2012-10-30 17:00:44 +04:00
|
|
|
#include "client_manager.hh"
|
|
|
|
|
2012-11-07 17:02:23 +04:00
|
|
|
#include "buffer_manager.hh"
|
2013-04-09 21:39:03 +04:00
|
|
|
#include "color_registry.hh"
|
2012-12-19 00:20:36 +04:00
|
|
|
#include "command_manager.hh"
|
2013-04-09 21:39:03 +04:00
|
|
|
#include "event_manager.hh"
|
2013-03-25 22:11:26 +04:00
|
|
|
#include "file.hh"
|
2013-04-09 21:39:03 +04:00
|
|
|
#include "user_interface.hh"
|
|
|
|
#include "window.hh"
|
2012-10-31 17:23:44 +04:00
|
|
|
|
2012-10-30 17:00:44 +04:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
Client::Client(std::unique_ptr<UserInterface>&& ui,
|
|
|
|
Window& window, String name)
|
|
|
|
: m_user_interface(std::move(ui)),
|
|
|
|
m_input_handler(*m_user_interface),
|
|
|
|
m_name(std::move(name))
|
2013-02-07 22:25:42 +04:00
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
kak_assert(not m_name.empty());
|
|
|
|
context().change_editor(window);
|
|
|
|
}
|
2013-02-07 22:25:42 +04:00
|
|
|
|
|
|
|
ClientManager::ClientManager() = default;
|
|
|
|
ClientManager::~ClientManager() = default;
|
|
|
|
|
2013-01-07 16:59:09 +04:00
|
|
|
String ClientManager::generate_name() const
|
|
|
|
{
|
|
|
|
for (int i = 0; true; ++i)
|
|
|
|
{
|
|
|
|
String name = "unnamed" + int_to_str(i);
|
|
|
|
bool found = false;
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
if (client->m_name == name)
|
2013-01-07 16:59:09 +04:00
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (not found)
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
|
|
|
|
const String& init_commands)
|
2012-10-30 17:00:44 +04:00
|
|
|
{
|
2012-12-28 16:51:14 +04:00
|
|
|
Buffer& buffer = **BufferManager::instance().begin();
|
2013-04-15 16:28:21 +04:00
|
|
|
Client* client = new Client{std::move(ui), get_unused_window_for_buffer(buffer),
|
|
|
|
generate_name()};
|
|
|
|
m_clients.emplace_back(client);
|
2012-12-19 21:56:47 +04:00
|
|
|
try
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
CommandManager::instance().execute(init_commands, client->context());
|
2012-12-19 21:56:47 +04:00
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
client->context().print_status({ error.what(), get_color("Error") });
|
|
|
|
client->context().hooks().run_hook("RuntimeError", error.what(),
|
|
|
|
client->context());
|
2012-12-19 21:56:47 +04:00
|
|
|
}
|
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
|
|
|
m_clients.pop_back();
|
2013-04-15 16:28:21 +04:00
|
|
|
return nullptr;
|
2012-12-19 21:56:47 +04:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
client->m_user_interface->set_input_callback([client, this]() {
|
2012-10-31 17:23:44 +04:00
|
|
|
try
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
client->m_input_handler.handle_available_inputs();
|
|
|
|
client->context().window().forget_timestamp();
|
2012-10-31 17:23:44 +04:00
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
client->context().print_status({ error.what(), get_color("Error") });
|
|
|
|
client->context().hooks().run_hook("RuntimeError", error.what(),
|
|
|
|
client->context());
|
2012-10-31 17:23:44 +04:00
|
|
|
}
|
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
ClientManager::instance().remove_client(*client);
|
2012-10-31 17:23:44 +04:00
|
|
|
}
|
2012-11-05 22:58:04 +04:00
|
|
|
ClientManager::instance().redraw_clients();
|
2012-10-31 17:23:44 +04:00
|
|
|
});
|
2012-11-05 22:58:04 +04:00
|
|
|
redraw_clients();
|
2013-04-15 16:28:21 +04:00
|
|
|
|
|
|
|
return client;
|
2012-10-30 17:00:44 +04:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
void ClientManager::remove_client(Client& client)
|
2012-10-30 17:00:44 +04:00
|
|
|
{
|
|
|
|
for (auto it = m_clients.begin(); it != m_clients.end(); ++it)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
if (it->get() == &client)
|
2012-10-30 17:00:44 +04:00
|
|
|
{
|
|
|
|
m_clients.erase(it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-04-09 22:04:11 +04:00
|
|
|
kak_assert(false);
|
2012-10-30 17:00:44 +04:00
|
|
|
}
|
|
|
|
|
2012-11-22 17:08:55 +04:00
|
|
|
Window& ClientManager::get_unused_window_for_buffer(Buffer& buffer)
|
2012-11-05 22:15:42 +04:00
|
|
|
{
|
2012-11-22 17:08:55 +04:00
|
|
|
for (auto& w : m_windows)
|
2012-11-05 22:15:42 +04:00
|
|
|
{
|
2012-11-22 17:08:55 +04:00
|
|
|
if (&w->buffer() != &buffer)
|
|
|
|
continue;
|
|
|
|
|
2012-11-05 22:15:42 +04:00
|
|
|
auto it = std::find_if(m_clients.begin(), m_clients.end(),
|
2012-11-22 17:17:46 +04:00
|
|
|
[&](const std::unique_ptr<Client>& client)
|
2013-01-28 16:48:34 +04:00
|
|
|
{ return &client->context().window() == w.get(); });
|
2012-11-05 22:15:42 +04:00
|
|
|
if (it == m_clients.end())
|
2012-11-05 22:58:04 +04:00
|
|
|
{
|
|
|
|
w->forget_timestamp();
|
2012-11-05 22:15:42 +04:00
|
|
|
return *w;
|
2012-11-05 22:58:04 +04:00
|
|
|
}
|
2012-11-05 22:15:42 +04:00
|
|
|
}
|
2012-11-22 17:08:55 +04:00
|
|
|
m_windows.emplace_back(new Window(buffer));
|
|
|
|
return *m_windows.back();
|
2012-11-05 22:15:42 +04:00
|
|
|
}
|
|
|
|
|
2012-11-07 17:02:23 +04:00
|
|
|
void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
|
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-01-28 16:48:34 +04:00
|
|
|
client->context().forget_jumps_to_buffer(buffer);
|
2012-11-12 22:59:25 +04:00
|
|
|
|
2013-01-28 16:48:34 +04:00
|
|
|
if (&client->context().buffer() != &buffer)
|
2012-11-07 17:02:23 +04:00
|
|
|
continue;
|
|
|
|
|
2013-04-10 20:54:01 +04:00
|
|
|
if (client->context().editor().is_editing())
|
2013-04-15 16:28:21 +04:00
|
|
|
throw runtime_error("client '" + client->m_name + "' is inserting in '" +
|
2013-04-10 20:54:01 +04:00
|
|
|
buffer.display_name() + '\'');
|
|
|
|
|
2012-11-07 17:02:23 +04: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)
|
|
|
|
{
|
|
|
|
Window& w = get_unused_window_for_buffer(*buf);
|
2013-01-28 16:48:34 +04:00
|
|
|
client->context().change_editor(w);
|
2012-11-07 17:02:23 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-22 17:08:55 +04:00
|
|
|
auto end = std::remove_if(m_windows.begin(), m_windows.end(),
|
2012-11-22 17:17:46 +04:00
|
|
|
[&buffer](const std::unique_ptr<Window>& w)
|
2012-11-22 17:08:55 +04:00
|
|
|
{ return &w->buffer() == &buffer; });
|
|
|
|
m_windows.erase(end, m_windows.end());
|
2012-11-07 17:02:23 +04:00
|
|
|
}
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
void ClientManager::set_client_name(Client& client, String name)
|
2012-12-03 21:56:53 +04:00
|
|
|
{
|
|
|
|
auto it = find_if(m_clients, [&name](std::unique_ptr<Client>& client)
|
2013-04-15 16:28:21 +04:00
|
|
|
{ return client->m_name == name; });
|
|
|
|
if (it != m_clients.end() and it->get() != &client)
|
2012-12-03 21:56:53 +04:00
|
|
|
throw runtime_error("name not unique: " + name);
|
2013-04-15 16:28:21 +04:00
|
|
|
client.m_name = std::move(name);
|
|
|
|
}
|
2012-12-03 21:56:53 +04:00
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
Client& ClientManager::get_client(const Context& context)
|
|
|
|
{
|
2012-12-03 21:56:53 +04:00
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-01-28 16:48:34 +04:00
|
|
|
if (&client->context() == &context)
|
2013-04-15 16:28:21 +04:00
|
|
|
return *client;
|
2012-12-03 21:56:53 +04:00
|
|
|
}
|
|
|
|
throw runtime_error("no client for current context");
|
|
|
|
}
|
|
|
|
|
2013-04-15 16:28:21 +04:00
|
|
|
Client& ClientManager::get_client(const String& name)
|
2013-01-07 16:59:09 +04:00
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-04-15 16:28:21 +04:00
|
|
|
if (client->m_name == name)
|
|
|
|
return *client;
|
2013-01-07 16:59:09 +04:00
|
|
|
}
|
2012-12-03 21:56:53 +04:00
|
|
|
throw runtime_error("no client named: " + name);
|
|
|
|
}
|
|
|
|
|
2013-04-04 20:50:00 +04:00
|
|
|
static DisplayLine generate_status_line(const Context& context)
|
2013-02-18 22:03:39 +04:00
|
|
|
{
|
2013-04-11 15:45:28 +04:00
|
|
|
auto pos = context.editor().main_selection().last();
|
|
|
|
auto col = utf8::distance(context.buffer().iterator_at_line_begin(pos), pos);
|
2013-03-25 22:11:26 +04:00
|
|
|
|
2013-04-11 15:45:28 +04:00
|
|
|
std::ostringstream oss;
|
2013-03-25 22:58:23 +04:00
|
|
|
oss << context.buffer().display_name()
|
2013-04-11 15:45:28 +04:00
|
|
|
<< " " << (int)pos.line()+1 << "," << (int)col+1;
|
2013-02-18 22:03:39 +04:00
|
|
|
if (context.buffer().is_modified())
|
|
|
|
oss << " [+]";
|
|
|
|
if (context.input_handler().is_recording())
|
|
|
|
oss << " [recording]";
|
|
|
|
if (context.buffer().flags() & Buffer::Flags::New)
|
|
|
|
oss << " [new file]";
|
|
|
|
oss << " [" << context.editor().selections().size() << " sel]";
|
|
|
|
if (context.editor().is_editing())
|
|
|
|
oss << " [insert]";
|
2013-04-04 20:50:00 +04:00
|
|
|
return { oss.str(), get_color("StatusLine") };
|
2013-02-18 22:03:39 +04:00
|
|
|
}
|
|
|
|
|
2012-11-05 22:58:04 +04:00
|
|
|
void ClientManager::redraw_clients() const
|
|
|
|
{
|
|
|
|
for (auto& client : m_clients)
|
|
|
|
{
|
2013-01-28 16:48:34 +04:00
|
|
|
Context& context = client->context();
|
2012-11-05 22:58:04 +04:00
|
|
|
if (context.window().timestamp() != context.buffer().timestamp())
|
|
|
|
{
|
|
|
|
DisplayCoord dimensions = context.ui().dimensions();
|
|
|
|
if (dimensions == DisplayCoord{0,0})
|
|
|
|
return;
|
|
|
|
context.window().set_dimensions(dimensions);
|
|
|
|
context.window().update_display_buffer();;
|
2013-02-18 22:03:39 +04:00
|
|
|
|
2012-11-05 22:58:04 +04:00
|
|
|
context.ui().draw(context.window().display_buffer(),
|
2013-02-18 22:03:39 +04:00
|
|
|
generate_status_line(context));
|
2012-11-05 22:58:04 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-30 17:00:44 +04:00
|
|
|
}
|