kakoune/src/client_manager.cc

229 lines
7.0 KiB
C++
Raw Normal View History

#include "client_manager.hh"
#include "buffer_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "ranges.hh"
#include "window.hh"
2012-10-31 14:23:44 +01:00
namespace Kakoune
{
ClientManager::ClientManager() = default;
ClientManager::~ClientManager()
{
clear();
}
void ClientManager::clear()
{
// So that clients destructor find the client manager empty
// so that local UI does not fork.
ClientList clients = std::move(m_clients);
clients.clear();
m_client_trash.clear();
m_free_windows.clear();
m_window_trash.clear();
}
String ClientManager::generate_name() const
{
for (int i = 0; true; ++i)
{
String name = format("client{}", i);
2018-03-25 07:53:27 +02:00
if (not client_name_exists(name))
return name;
}
}
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, int pid,
String name, EnvVarMap env_vars, StringView init_cmds,
Optional<BufferCoord> init_coord,
Client::OnExitCallback on_exit)
{
Buffer& buffer = BufferManager::instance().get_first_buffer();
WindowAndSelections ws = get_free_window(buffer);
2014-01-27 21:28:38 +01:00
Client* client = new Client{std::move(ui), std::move(ws.window),
std::move(ws.selections), pid,
std::move(env_vars),
name.empty() ? generate_name() : std::move(name),
std::move(on_exit)};
m_clients.emplace_back(client);
if (init_coord)
{
auto& selections = client->context().selections_write_only();
selections = SelectionList(buffer, buffer.clamp(*init_coord));
client->context().window().center_line(init_coord->line);
}
try
{
CommandManager::instance().execute(init_cmds, client->context());
}
catch (Kakoune::runtime_error& error)
{
client->context().print_status({ fix_atom_text(error.what().str()),
client->context().faces()["Error"] });
client->context().hooks().run_hook(Hook::RuntimeError, error.what(),
client->context());
}
// Do not return the client if it already got moved to the trash
return contains(m_clients, client) ? client : nullptr;
}
void ClientManager::process_pending_inputs()
{
while (true)
{
bool had_input = false;
// Use index based iteration as a m_clients might get mutated during
// client input processing, which would break iterator based iteration.
// (its fine to skip a client if that happens as had_input will be true
// if a client triggers client removal)
for (int i = 0; i < m_clients.size(); )
{
if (not m_clients[i]->is_ui_ok())
{
remove_client(*m_clients[i], false, -1);
continue;
}
had_input = m_clients[i]->process_pending_inputs() or had_input;
++i;
}
if (not had_input)
break;
}
}
bool ClientManager::has_pending_inputs() const
{
return any_of(m_clients, [](auto&& c) { return c->has_pending_inputs(); });
}
void ClientManager::remove_client(Client& client, bool graceful, int status)
{
auto it = find(m_clients, &client);
if (it == m_clients.end())
{
kak_assert(contains(m_client_trash, &client));
return;
}
client.exit(status);
m_client_trash.push_back(std::move(*it));
m_clients.erase(it);
if (not graceful and m_clients.empty())
BufferManager::instance().backup_modified_buffers();
}
WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
{
auto it = find_if(m_free_windows | reverse(),
2015-03-14 12:27:01 +01:00
[&](const WindowAndSelections& ws)
{ return &ws.window->buffer() == &buffer; });
if (it == m_free_windows.rend())
2016-03-12 16:27:54 +01:00
return { std::make_unique<Window>(buffer), { buffer, Selection{} } };
2015-03-14 12:27:01 +01:00
it->window->force_redraw();
2015-03-14 12:27:01 +01:00
WindowAndSelections res = std::move(*it);
m_free_windows.erase(it.base()-1);
res.selections.update();
return res;
}
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
{
window->clear_display_buffer();
2017-05-27 07:17:23 +02:00
m_free_windows.push_back({std::move(window), std::move(selections)});
}
void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
{
for (auto& client : m_clients)
{
auto& context = client->context();
context.jump_list().forget_buffer(buffer);
if (client->last_buffer() == &buffer)
client->set_last_buffer(nullptr);
if (&context.buffer() != &buffer)
continue;
if (context.is_editing())
context.input_handler().reset_normal_mode();
Buffer* last = client->last_buffer();
context.change_buffer(last ? *last : BufferManager::instance().get_first_buffer());
}
Vector<std::unique_ptr<Window>> removed_windows;
m_free_windows.erase(std::remove_if(m_free_windows.begin(), m_free_windows.end(),
[&buffer, &removed_windows](WindowAndSelections& ws) {
if (&ws.window->buffer() != &buffer)
return false;
removed_windows.push_back(std::move(ws.window));
return true;
}),
m_free_windows.end());
for (auto&& removed_window : removed_windows)
{
removed_window->run_hook_in_own_context(Hook::WinClose,
removed_window->buffer().name());
m_window_trash.push_back(std::move(removed_window));
}
}
void ClientManager::clear_window_trash()
{
m_window_trash.clear();
}
void ClientManager::clear_client_trash()
{
m_client_trash.clear();
}
2018-03-25 07:53:27 +02:00
bool ClientManager::client_name_exists(StringView name) const
{
2018-03-25 07:53:27 +02:00
return const_cast<ClientManager*>(this)->get_client_ifp(name) != nullptr;
}
Client* ClientManager::get_client_ifp(StringView name)
{
for (auto& client : m_clients)
{
if (client->context().name() == name)
return client.get();
}
return nullptr;
}
Client& ClientManager::get_client(StringView name)
{
2015-01-04 23:34:36 +01:00
if (Client* client = get_client_ifp(name))
return *client;
2018-04-06 16:56:53 +02:00
throw runtime_error(format("no such client: '{}'", name));
}
void ClientManager::redraw_clients() const
{
for (auto& client : m_clients)
client->redraw_ifn();
}
CandidateList ClientManager::complete_client_name(StringView prefix,
ByteCount cursor_pos) const
{
auto c = m_clients | transform([](const std::unique_ptr<Client>& c) -> const String&
{ return c->context().name(); });
return complete(prefix, cursor_pos, c);
}
}