2012-10-30 14:00:44 +01:00
|
|
|
#include "client_manager.hh"
|
|
|
|
|
2012-11-07 14:02:23 +01:00
|
|
|
#include "buffer_manager.hh"
|
2012-12-18 21:20:36 +01:00
|
|
|
#include "command_manager.hh"
|
2013-04-09 19:39:03 +02:00
|
|
|
#include "event_manager.hh"
|
2014-07-11 01:27:04 +02:00
|
|
|
#include "face_registry.hh"
|
2013-03-25 19:11:26 +01:00
|
|
|
#include "file.hh"
|
2017-08-29 10:23:03 +02:00
|
|
|
#include "ranges.hh"
|
2013-04-09 19:39:03 +02:00
|
|
|
#include "window.hh"
|
2012-10-31 14:23:44 +01:00
|
|
|
|
2012-10-30 14:00:44 +01:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2013-02-07 19:25:42 +01:00
|
|
|
ClientManager::ClientManager() = default;
|
2015-08-26 20:34:19 +02:00
|
|
|
ClientManager::~ClientManager()
|
|
|
|
{
|
2019-04-12 01:02:59 +02:00
|
|
|
clear(true);
|
2016-03-03 14:55:35 +01:00
|
|
|
}
|
|
|
|
|
2019-04-12 01:02:59 +02:00
|
|
|
void ClientManager::clear(bool disconnect_clients)
|
2016-03-03 14:55:35 +01:00
|
|
|
{
|
2019-04-12 01:02:59 +02:00
|
|
|
if (disconnect_clients)
|
|
|
|
{
|
|
|
|
while (not m_clients.empty())
|
|
|
|
remove_client(*m_clients.front(), true, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_clients.clear();
|
2016-09-04 18:54:07 +02:00
|
|
|
m_client_trash.clear();
|
2019-04-08 14:32:30 +02:00
|
|
|
|
2019-04-04 13:05:44 +02:00
|
|
|
for (auto& window : m_free_windows)
|
|
|
|
window.window->run_hook_in_own_context(Hook::WinClose,
|
|
|
|
window.window->buffer().name());
|
2017-01-19 19:44:26 +01:00
|
|
|
m_free_windows.clear();
|
2017-01-21 13:14:44 +01:00
|
|
|
m_window_trash.clear();
|
2015-08-26 20:34:19 +02:00
|
|
|
}
|
2013-02-07 19:25:42 +01:00
|
|
|
|
2013-01-07 13:59:09 +01:00
|
|
|
String ClientManager::generate_name() const
|
|
|
|
{
|
|
|
|
for (int i = 0; true; ++i)
|
|
|
|
{
|
2018-04-23 11:42:11 +02:00
|
|
|
String name = format("client{}", i);
|
2018-03-25 07:53:27 +02:00
|
|
|
if (not client_name_exists(name))
|
2013-01-07 13:59:09 +01:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-28 08:12:15 +02:00
|
|
|
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, int pid,
|
2018-03-22 21:36:18 +01:00
|
|
|
String name, EnvVarMap env_vars, StringView init_cmds,
|
2022-12-06 07:48:42 +01:00
|
|
|
StringView init_buffer, Optional<BufferCoord> init_coord,
|
2017-08-23 08:22:23 +02:00
|
|
|
Client::OnExitCallback on_exit)
|
2012-10-30 14:00:44 +01:00
|
|
|
{
|
2023-01-06 08:36:57 +01:00
|
|
|
Buffer* buffer = nullptr;
|
|
|
|
if (not init_buffer.empty())
|
|
|
|
buffer = BufferManager::instance().get_buffer_ifp(init_buffer);
|
|
|
|
if (buffer == nullptr)
|
|
|
|
buffer = &BufferManager::instance().get_first_buffer();
|
2022-12-06 07:48:42 +01:00
|
|
|
|
2023-01-06 08:36:57 +01:00
|
|
|
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),
|
2017-08-28 08:12:15 +02:00
|
|
|
std::move(ws.selections), pid,
|
2018-03-22 21:36:18 +01:00
|
|
|
std::move(env_vars),
|
|
|
|
name.empty() ? generate_name() : std::move(name),
|
2017-08-28 08:12:15 +02:00
|
|
|
std::move(on_exit)};
|
2013-04-15 14:28:21 +02:00
|
|
|
m_clients.emplace_back(client);
|
2016-12-01 21:40:50 +01:00
|
|
|
|
2017-01-19 19:44:26 +01:00
|
|
|
if (init_coord)
|
|
|
|
{
|
|
|
|
auto& selections = client->context().selections_write_only();
|
2023-01-06 08:36:57 +01:00
|
|
|
selections = SelectionList(*buffer, buffer->clamp(*init_coord));
|
2017-01-19 19:44:26 +01:00
|
|
|
client->context().window().center_line(init_coord->line);
|
|
|
|
}
|
2016-12-01 21:40:50 +01:00
|
|
|
|
2012-12-19 18:56:47 +01:00
|
|
|
try
|
|
|
|
{
|
2019-04-08 13:58:12 +02:00
|
|
|
auto& context = client->context();
|
|
|
|
context.hooks().run_hook(Hook::ClientCreate, context.name(), context);
|
|
|
|
CommandManager::instance().execute(init_cmds, context);
|
2012-12-19 18:56:47 +01:00
|
|
|
}
|
2016-09-04 18:54:07 +02:00
|
|
|
catch (Kakoune::runtime_error& error)
|
2012-12-19 18:56:47 +01:00
|
|
|
{
|
2021-08-17 12:40:30 +02:00
|
|
|
client->context().print_status({error.what().str(), client->context().faces()["Error"]});
|
2018-10-22 23:15:53 +02:00
|
|
|
client->context().hooks().run_hook(Hook::RuntimeError, error.what(),
|
2016-09-04 18:54:07 +02:00
|
|
|
client->context());
|
2012-12-19 18:56:47 +01:00
|
|
|
}
|
|
|
|
|
2016-09-04 18:54:07 +02:00
|
|
|
// Do not return the client if it already got moved to the trash
|
|
|
|
return contains(m_clients, client) ? client : nullptr;
|
2012-10-30 14:00:44 +01:00
|
|
|
}
|
|
|
|
|
2021-03-10 23:08:35 +01:00
|
|
|
bool ClientManager::process_pending_inputs()
|
2014-11-25 02:00:18 +01:00
|
|
|
{
|
2021-03-10 23:08:35 +01:00
|
|
|
bool processed_some_input = false;
|
2016-11-30 10:47:38 +01:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
bool had_input = false;
|
2016-12-01 21:55:28 +01:00
|
|
|
// 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)
|
2019-04-04 04:10:38 +02:00
|
|
|
for (int i = 0; i < m_clients.size(); )
|
|
|
|
{
|
|
|
|
if (not m_clients[i]->is_ui_ok())
|
|
|
|
{
|
|
|
|
remove_client(*m_clients[i], false, -1);
|
|
|
|
continue;
|
|
|
|
}
|
2016-12-01 21:55:28 +01:00
|
|
|
had_input = m_clients[i]->process_pending_inputs() or had_input;
|
2021-03-10 23:08:35 +01:00
|
|
|
processed_some_input |= had_input;
|
2019-04-04 04:10:38 +02:00
|
|
|
++i;
|
|
|
|
}
|
2016-11-30 10:47:38 +01:00
|
|
|
|
|
|
|
if (not had_input)
|
|
|
|
break;
|
|
|
|
}
|
2021-03-10 23:08:35 +01:00
|
|
|
return processed_some_input;
|
2014-11-25 02:00:18 +01:00
|
|
|
}
|
|
|
|
|
2018-01-21 02:00:40 +01:00
|
|
|
bool ClientManager::has_pending_inputs() const
|
|
|
|
{
|
2018-03-25 07:47:19 +02:00
|
|
|
return any_of(m_clients, [](auto&& c) { return c->has_pending_inputs(); });
|
2018-01-21 02:00:40 +01:00
|
|
|
}
|
|
|
|
|
2017-08-23 08:22:23 +02:00
|
|
|
void ClientManager::remove_client(Client& client, bool graceful, int status)
|
2012-10-30 14:00:44 +01:00
|
|
|
{
|
2016-09-04 18:54:07 +02:00
|
|
|
auto it = find(m_clients, &client);
|
|
|
|
if (it == m_clients.end())
|
|
|
|
{
|
|
|
|
kak_assert(contains(m_client_trash, &client));
|
|
|
|
return;
|
|
|
|
}
|
2019-04-08 13:58:12 +02:00
|
|
|
|
2016-09-04 18:54:07 +02:00
|
|
|
m_client_trash.push_back(std::move(*it));
|
2015-08-26 20:33:52 +02:00
|
|
|
m_clients.erase(it);
|
2015-10-08 14:43:39 +02:00
|
|
|
|
2019-04-08 13:58:12 +02:00
|
|
|
auto& context = client.context();
|
|
|
|
context.hooks().run_hook(Hook::ClientClose, context.name(), context);
|
|
|
|
|
2019-04-13 07:40:43 +02:00
|
|
|
client.exit(status);
|
|
|
|
|
2015-10-08 14:43:39 +02:00
|
|
|
if (not graceful and m_clients.empty())
|
|
|
|
BufferManager::instance().backup_modified_buffers();
|
2012-10-30 14:00:44 +01:00
|
|
|
}
|
|
|
|
|
2013-12-20 21:10:08 +01:00
|
|
|
WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
|
2012-11-05 19:15:42 +01:00
|
|
|
{
|
2019-06-16 11:04:06 +02:00
|
|
|
kak_assert(contains(BufferManager::instance(), &buffer));
|
2016-03-08 22:35:56 +01:00
|
|
|
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
|
|
|
|
2015-06-21 20:56:23 +02: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;
|
2013-12-20 21:10:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
|
|
|
|
{
|
2019-06-16 11:04:06 +02:00
|
|
|
if (not contains(BufferManager::instance(), &window->buffer()))
|
2019-06-18 13:49:41 +02:00
|
|
|
{
|
|
|
|
m_window_trash.push_back(std::move(window));
|
2019-06-16 11:04:06 +02:00
|
|
|
return;
|
2019-06-18 13:49:41 +02:00
|
|
|
}
|
2019-06-16 11:04:06 +02:00
|
|
|
|
2015-01-26 20:41:10 +01:00
|
|
|
window->clear_display_buffer();
|
2017-05-27 07:17:23 +02:00
|
|
|
m_free_windows.push_back({std::move(window), std::move(selections)});
|
2012-11-05 19:15:42 +01:00
|
|
|
}
|
|
|
|
|
2012-11-07 14:02:23 +01:00
|
|
|
void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
|
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
2019-07-22 12:05:42 +02:00
|
|
|
client->context().forget_buffer(buffer);
|
2012-11-07 14:02:23 +01:00
|
|
|
|
2019-02-16 12:58:00 +01:00
|
|
|
Vector<std::unique_ptr<Window>> removed_windows;
|
2020-05-29 03:59:03 +02:00
|
|
|
m_free_windows.erase(remove_if(m_free_windows,
|
|
|
|
[&buffer, &removed_windows](WindowAndSelections& ws) {
|
|
|
|
if (&ws.window->buffer() != &buffer)
|
|
|
|
return false;
|
|
|
|
removed_windows.push_back(std::move(ws.window));
|
|
|
|
return true;
|
|
|
|
}),
|
2019-02-16 12:58:00 +01:00
|
|
|
m_free_windows.end());
|
|
|
|
|
|
|
|
for (auto&& removed_window : removed_windows)
|
2019-02-16 10:02:46 +01:00
|
|
|
{
|
2019-02-16 12:58:00 +01:00
|
|
|
removed_window->run_hook_in_own_context(Hook::WinClose,
|
|
|
|
removed_window->buffer().name());
|
|
|
|
m_window_trash.push_back(std::move(removed_window));
|
2019-02-16 10:02:46 +01:00
|
|
|
}
|
2012-11-07 14:02:23 +01:00
|
|
|
}
|
|
|
|
|
2016-05-13 21:32:53 +02:00
|
|
|
void ClientManager::clear_window_trash()
|
|
|
|
{
|
|
|
|
m_window_trash.clear();
|
2016-09-04 18:54:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientManager::clear_client_trash()
|
|
|
|
{
|
|
|
|
m_client_trash.clear();
|
2016-05-13 21:32:53 +02:00
|
|
|
}
|
|
|
|
|
2018-03-25 07:53:27 +02:00
|
|
|
bool ClientManager::client_name_exists(StringView name) const
|
2012-12-03 18:56:53 +01:00
|
|
|
{
|
2018-03-25 07:53:27 +02:00
|
|
|
return const_cast<ClientManager*>(this)->get_client_ifp(name) != nullptr;
|
2012-12-03 18:56:53 +01:00
|
|
|
}
|
|
|
|
|
2014-10-20 20:18:38 +02:00
|
|
|
Client* ClientManager::get_client_ifp(StringView name)
|
2013-01-07 13:59:09 +01:00
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-11-14 21:51:25 +01:00
|
|
|
if (client->context().name() == name)
|
2013-12-07 14:43:48 +01:00
|
|
|
return client.get();
|
2013-01-07 13:59:09 +01:00
|
|
|
}
|
2013-12-07 14:43:48 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2014-10-20 20:18:38 +02:00
|
|
|
Client& ClientManager::get_client(StringView name)
|
2013-12-07 14:43:48 +01:00
|
|
|
{
|
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));
|
2012-12-03 18:56:53 +01:00
|
|
|
}
|
|
|
|
|
2012-11-05 19:58:04 +01:00
|
|
|
void ClientManager::redraw_clients() const
|
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
2013-09-16 20:15:13 +02:00
|
|
|
client->redraw_ifn();
|
2012-11-05 19:58:04 +01:00
|
|
|
}
|
|
|
|
|
2014-04-18 15:02:14 +02:00
|
|
|
CandidateList ClientManager::complete_client_name(StringView prefix,
|
2014-04-07 22:43:55 +02:00
|
|
|
ByteCount cursor_pos) const
|
|
|
|
{
|
2016-03-08 22:35:56 +01:00
|
|
|
auto c = m_clients | transform([](const std::unique_ptr<Client>& c) -> const String&
|
|
|
|
{ return c->context().name(); });
|
2016-02-09 23:50:10 +01:00
|
|
|
return complete(prefix, cursor_pos, c);
|
2014-04-07 22:43:55 +02:00
|
|
|
}
|
|
|
|
|
2012-10-30 14:00:44 +01:00
|
|
|
}
|