2012-10-30 14:00:44 +01:00
|
|
|
#include "client_manager.hh"
|
|
|
|
|
2012-11-07 14:02:23 +01:00
|
|
|
#include "buffer_manager.hh"
|
2013-04-09 19:39:03 +02:00
|
|
|
#include "color_registry.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"
|
2013-03-25 19:11:26 +01:00
|
|
|
#include "file.hh"
|
2013-04-09 19:39:03 +02:00
|
|
|
#include "user_interface.hh"
|
|
|
|
#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;
|
|
|
|
ClientManager::~ClientManager() = default;
|
|
|
|
|
2013-01-07 13:59:09 +01:00
|
|
|
String ClientManager::generate_name() const
|
|
|
|
{
|
|
|
|
for (int i = 0; true; ++i)
|
|
|
|
{
|
2013-05-13 14:23:07 +02:00
|
|
|
String name = "unnamed" + to_string(i);
|
2013-09-13 00:01:47 +02:00
|
|
|
if (validate_client_name(name))
|
2013-01-07 13:59:09 +01:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:47:23 +02:00
|
|
|
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
|
2014-04-07 22:25:44 +02:00
|
|
|
EnvVarMap env_vars,
|
2013-09-12 23:47:23 +02:00
|
|
|
const String& init_commands)
|
2012-10-30 14:00:44 +01:00
|
|
|
{
|
2012-12-28 13:51:14 +01:00
|
|
|
Buffer& buffer = **BufferManager::instance().begin();
|
2013-12-20 21:10:08 +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),
|
2014-04-07 22:25:44 +02:00
|
|
|
std::move(ws.selections), std::move(env_vars),
|
|
|
|
generate_name()};
|
2013-04-15 14:28:21 +02:00
|
|
|
m_clients.emplace_back(client);
|
2012-12-19 18:56:47 +01:00
|
|
|
try
|
|
|
|
{
|
2013-04-15 14:28:21 +02:00
|
|
|
CommandManager::instance().execute(init_commands, client->context());
|
2012-12-19 18:56:47 +01:00
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
2013-04-15 14:28:21 +02:00
|
|
|
client->context().print_status({ error.what(), get_color("Error") });
|
|
|
|
client->context().hooks().run_hook("RuntimeError", error.what(),
|
|
|
|
client->context());
|
2012-12-19 18:56:47 +01:00
|
|
|
}
|
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
|
|
|
m_clients.pop_back();
|
2013-04-15 14:28:21 +02:00
|
|
|
return nullptr;
|
2012-12-19 18:56:47 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 23:39:34 +02:00
|
|
|
client->ui().set_input_callback([client, this]() {
|
2012-10-31 14:23:44 +01:00
|
|
|
try
|
|
|
|
{
|
2013-09-16 19:48:41 +02:00
|
|
|
client->handle_available_input();
|
2012-10-31 14:23:44 +01:00
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
2013-04-15 14:28:21 +02:00
|
|
|
client->context().print_status({ error.what(), get_color("Error") });
|
|
|
|
client->context().hooks().run_hook("RuntimeError", error.what(),
|
|
|
|
client->context());
|
2012-10-31 14:23:44 +01:00
|
|
|
}
|
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
2013-04-15 14:28:21 +02:00
|
|
|
ClientManager::instance().remove_client(*client);
|
2012-10-31 14:23:44 +01:00
|
|
|
}
|
|
|
|
});
|
2013-04-15 14:28:21 +02:00
|
|
|
|
|
|
|
return client;
|
2012-10-30 14:00:44 +01:00
|
|
|
}
|
|
|
|
|
2013-09-12 23:47:23 +02:00
|
|
|
void ClientManager::remove_client(Client& client)
|
2012-10-30 14:00:44 +01:00
|
|
|
{
|
|
|
|
for (auto it = m_clients.begin(); it != m_clients.end(); ++it)
|
|
|
|
{
|
2013-04-15 14:28:21 +02:00
|
|
|
if (it->get() == &client)
|
2012-10-30 14:00:44 +01:00
|
|
|
{
|
|
|
|
m_clients.erase(it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-04-09 20:04:11 +02:00
|
|
|
kak_assert(false);
|
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
|
|
|
{
|
2014-01-29 20:15:50 +01:00
|
|
|
for (auto it = m_free_windows.rbegin(), end = m_free_windows.rend();
|
2013-12-20 21:10:08 +01:00
|
|
|
it != end; ++it)
|
2012-11-05 19:15:42 +01:00
|
|
|
{
|
2014-01-27 21:28:38 +01:00
|
|
|
auto& w = it->window;
|
2013-12-20 21:10:08 +01:00
|
|
|
if (&w->buffer() == &buffer)
|
2012-11-05 19:58:04 +01:00
|
|
|
{
|
|
|
|
w->forget_timestamp();
|
2013-12-20 21:10:08 +01:00
|
|
|
WindowAndSelections res = std::move(*it);
|
2014-01-29 20:15:50 +01:00
|
|
|
m_free_windows.erase(it.base()-1);
|
2013-12-20 21:10:08 +01:00
|
|
|
return res;
|
2012-11-05 19:58:04 +01:00
|
|
|
}
|
2012-11-05 19:15:42 +01:00
|
|
|
}
|
2014-04-08 21:09:54 +02:00
|
|
|
return WindowAndSelections{ std::unique_ptr<Window>{new Window{buffer}},
|
|
|
|
DynamicSelectionList{buffer,
|
|
|
|
{ Selection{ {}, {} } } } };
|
2013-12-20 21:10:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
|
|
|
|
{
|
|
|
|
Buffer& buffer = window->buffer();
|
2014-01-27 21:28:38 +01:00
|
|
|
m_free_windows.push_back({ std::move(window), DynamicSelectionList{ buffer, 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)
|
|
|
|
{
|
2013-01-28 13:48:34 +01:00
|
|
|
client->context().forget_jumps_to_buffer(buffer);
|
2012-11-12 19:59:25 +01:00
|
|
|
|
2013-01-28 13:48:34 +01:00
|
|
|
if (&client->context().buffer() != &buffer)
|
2012-11-07 14:02:23 +01:00
|
|
|
continue;
|
|
|
|
|
2013-12-15 19:07:51 +01:00
|
|
|
if (client->context().is_editing())
|
2013-11-14 21:51:25 +01:00
|
|
|
throw runtime_error("client '" + client->context().name() + "' is inserting in '" +
|
2013-04-10 18:54:01 +02:00
|
|
|
buffer.display_name() + '\'');
|
|
|
|
|
2012-11-07 14:02:23 +01:00
|
|
|
// change client context to edit the first buffer which is not the
|
|
|
|
// specified one. As BufferManager stores buffer according to last
|
|
|
|
// access, this selects a sensible buffer to display.
|
|
|
|
for (auto& buf : BufferManager::instance())
|
|
|
|
{
|
|
|
|
if (buf != &buffer)
|
|
|
|
{
|
2013-12-20 21:10:08 +01:00
|
|
|
client->context().change_buffer(*buf);
|
2012-11-07 14:02:23 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-20 21:10:08 +01:00
|
|
|
auto end = std::remove_if(m_free_windows.begin(), m_free_windows.end(),
|
|
|
|
[&buffer](const WindowAndSelections& ws)
|
2014-01-27 21:28:38 +01:00
|
|
|
{ return &ws.window->buffer() == &buffer; });
|
2013-12-20 21:10:08 +01:00
|
|
|
m_free_windows.erase(end, m_free_windows.end());
|
2012-11-07 14:02:23 +01:00
|
|
|
}
|
|
|
|
|
2013-09-13 00:01:47 +02:00
|
|
|
bool ClientManager::validate_client_name(const String& name) const
|
2012-12-03 18:56:53 +01:00
|
|
|
{
|
2013-09-13 00:01:47 +02:00
|
|
|
auto it = find_if(m_clients, [&](const std::unique_ptr<Client>& client)
|
2013-11-14 21:51:25 +01:00
|
|
|
{ return client->context().name() == name; });
|
2013-09-13 00:01:47 +02:00
|
|
|
return it == m_clients.end();
|
2012-12-03 18:56:53 +01:00
|
|
|
}
|
|
|
|
|
2013-12-07 14:43:48 +01:00
|
|
|
Client* ClientManager::get_client_ifp(const String& 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Client& ClientManager::get_client(const String& name)
|
|
|
|
{
|
|
|
|
Client* client = get_client_ifp(name);
|
|
|
|
if (not client)
|
|
|
|
throw runtime_error("no client named: " + name);
|
|
|
|
return *client;
|
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
|
|
|
|
{
|
2014-04-18 15:02:14 +02:00
|
|
|
auto real_prefix = prefix.substr(0, cursor_pos);
|
2014-04-07 22:43:55 +02:00
|
|
|
CandidateList result;
|
|
|
|
CandidateList subsequence_result;
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
|
|
|
const String& name = client->context().name();
|
|
|
|
|
|
|
|
if (prefix_match(name, real_prefix))
|
|
|
|
result.push_back(escape(name));
|
|
|
|
if (subsequence_match(name, real_prefix))
|
|
|
|
subsequence_result.push_back(escape(name));
|
|
|
|
}
|
|
|
|
return result.empty() ? subsequence_result : result;
|
|
|
|
}
|
|
|
|
|
2012-10-30 14:00:44 +01:00
|
|
|
}
|