2013-09-13 01:47:23 +04:00
|
|
|
#include "client.hh"
|
2012-09-03 16:22:02 +04:00
|
|
|
|
2014-07-11 03:27:04 +04:00
|
|
|
#include "face_registry.hh"
|
2012-09-03 16:22:02 +04:00
|
|
|
#include "context.hh"
|
2013-05-20 16:10:53 +04:00
|
|
|
#include "buffer_manager.hh"
|
2013-04-09 21:39:03 +04:00
|
|
|
#include "user_interface.hh"
|
2013-08-02 20:58:37 +04:00
|
|
|
#include "file.hh"
|
2013-10-11 21:43:23 +04:00
|
|
|
#include "remote.hh"
|
2013-10-15 21:51:31 +04:00
|
|
|
#include "client_manager.hh"
|
2014-11-25 04:00:18 +03:00
|
|
|
#include "event_manager.hh"
|
2013-11-15 01:12:59 +04:00
|
|
|
#include "window.hh"
|
2012-09-03 16:22:02 +04:00
|
|
|
|
2014-11-25 04:00:18 +03:00
|
|
|
#include <signal.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2012-09-03 16:22:02 +04:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2013-12-21 00:10:08 +04:00
|
|
|
Client::Client(std::unique_ptr<UserInterface>&& ui,
|
|
|
|
std::unique_ptr<Window>&& window,
|
2014-04-08 00:25:44 +04:00
|
|
|
SelectionList selections,
|
|
|
|
EnvVarMap env_vars,
|
|
|
|
String name)
|
2013-12-21 00:10:08 +04:00
|
|
|
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
2014-12-19 02:12:58 +03:00
|
|
|
m_input_handler{std::move(selections), Context::Flags::None,
|
2014-04-08 00:25:44 +04:00
|
|
|
std::move(name)},
|
|
|
|
m_env_vars(env_vars)
|
2013-11-14 22:09:15 +04:00
|
|
|
{
|
|
|
|
context().set_client(*this);
|
2013-12-21 00:10:08 +04:00
|
|
|
context().set_window(*m_window);
|
2014-11-11 02:29:16 +03:00
|
|
|
|
|
|
|
m_window->options().register_watcher(*this);
|
|
|
|
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
|
2013-11-14 22:09:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
Client::~Client()
|
|
|
|
{
|
2014-11-11 02:29:16 +03:00
|
|
|
m_window->options().unregister_watcher(*this);
|
2013-11-14 22:09:15 +04:00
|
|
|
}
|
|
|
|
|
2014-11-29 23:14:52 +03:00
|
|
|
Optional<Key> Client::get_next_key(EventMode mode)
|
|
|
|
{
|
|
|
|
if (not m_pending_keys.empty())
|
|
|
|
{
|
|
|
|
Key key = m_pending_keys.front();
|
|
|
|
m_pending_keys.erase(m_pending_keys.begin());
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
if (mode != EventMode::Pending and m_ui->is_key_available())
|
|
|
|
return m_ui->get_key();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2014-11-25 04:00:18 +03:00
|
|
|
void Client::handle_available_input(EventMode mode)
|
2013-11-14 22:09:15 +04:00
|
|
|
{
|
2014-11-29 23:14:52 +03:00
|
|
|
if (mode == EventMode::Urgent)
|
|
|
|
{
|
|
|
|
Key key = m_ui->get_key();
|
|
|
|
if (key == ctrl('c'))
|
|
|
|
killpg(getpgrp(), SIGINT);
|
|
|
|
else
|
|
|
|
m_pending_keys.push_back(key);
|
|
|
|
}
|
|
|
|
else
|
2014-11-25 04:00:18 +03:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2014-11-29 23:14:52 +03:00
|
|
|
while (Optional<Key> key = get_next_key(mode))
|
2014-11-25 04:00:18 +03:00
|
|
|
{
|
2014-11-29 23:14:52 +03:00
|
|
|
if (*key == ctrl('c'))
|
2014-11-26 22:24:58 +03:00
|
|
|
killpg(getpgrp(), SIGINT);
|
|
|
|
else
|
|
|
|
{
|
2014-11-29 23:14:52 +03:00
|
|
|
m_input_handler.handle_key(*key);
|
2014-11-26 22:24:58 +03:00
|
|
|
m_input_handler.clear_mode_trash();
|
2014-11-29 23:14:52 +03:00
|
|
|
context().window().forget_timestamp();
|
2014-11-26 22:24:58 +03:00
|
|
|
}
|
2014-11-25 04:00:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
|
|
|
context().print_status({ error.what(), get_face("Error") });
|
|
|
|
context().hooks().run_hook("RuntimeError", error.what(), context());
|
|
|
|
}
|
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
|
|
|
ClientManager::instance().remove_client(*this);
|
|
|
|
}
|
|
|
|
}
|
2013-11-14 22:09:15 +04:00
|
|
|
}
|
|
|
|
|
2013-09-16 22:15:13 +04:00
|
|
|
void Client::print_status(DisplayLine status_line)
|
|
|
|
{
|
2014-08-12 22:19:46 +04:00
|
|
|
m_pending_status_line = std::move(status_line);
|
2013-09-16 22:15:13 +04:00
|
|
|
}
|
|
|
|
|
2013-10-11 00:34:19 +04:00
|
|
|
DisplayLine Client::generate_mode_line() const
|
2013-09-16 22:15:13 +04:00
|
|
|
{
|
2014-01-28 23:05:49 +04:00
|
|
|
auto pos = context().selections().main().cursor();
|
2013-10-11 00:34:19 +04:00
|
|
|
auto col = context().buffer()[pos.line].char_count_to(pos.column);
|
2013-09-16 22:15:13 +04:00
|
|
|
|
2014-07-07 22:57:03 +04:00
|
|
|
DisplayLine status;
|
2014-07-11 03:27:04 +04:00
|
|
|
Face info_face = get_face("Information");
|
|
|
|
Face status_face = get_face("StatusLine");
|
2014-07-07 22:57:03 +04:00
|
|
|
|
2014-07-11 03:27:04 +04:00
|
|
|
status.push_back({ context().buffer().display_name(), status_face });
|
|
|
|
status.push_back({ " " + to_string((int)pos.line+1) + ":" + to_string((int)col+1) + " ", status_face });
|
2013-10-11 00:34:19 +04:00
|
|
|
if (context().buffer().is_modified())
|
2014-07-11 03:27:04 +04:00
|
|
|
status.push_back({ "[+]", info_face });
|
2013-11-14 22:09:15 +04:00
|
|
|
if (m_input_handler.is_recording())
|
2014-12-08 16:59:29 +03:00
|
|
|
status.push_back({ "[recording ("_str + StringView{m_input_handler.recording_reg()} + ")]", info_face });
|
2013-10-11 00:34:19 +04:00
|
|
|
if (context().buffer().flags() & Buffer::Flags::New)
|
2014-07-11 03:27:04 +04:00
|
|
|
status.push_back({ "[new file]", info_face });
|
2014-12-05 16:47:09 +03:00
|
|
|
if (context().user_hooks_support().is_disabled())
|
2014-07-26 02:47:47 +04:00
|
|
|
status.push_back({ "[no-hooks]", info_face });
|
2014-07-07 22:57:03 +04:00
|
|
|
if (context().buffer().flags() & Buffer::Flags::Fifo)
|
2014-07-11 03:27:04 +04:00
|
|
|
status.push_back({ "[fifo]", info_face });
|
|
|
|
status.push_back({ " ", status_face });
|
2014-09-10 22:06:53 +04:00
|
|
|
for (auto& atom : m_input_handler.mode_line())
|
|
|
|
status.push_back(std::move(atom));
|
2014-07-11 03:27:04 +04:00
|
|
|
status.push_back({ " - " + context().name() + "@[" + Server::instance().session() + "]", status_face });
|
2014-07-07 22:57:03 +04:00
|
|
|
|
|
|
|
return status;
|
2013-09-16 22:15:13 +04:00
|
|
|
}
|
|
|
|
|
2013-12-21 00:10:08 +04:00
|
|
|
void Client::change_buffer(Buffer& buffer)
|
|
|
|
{
|
2015-02-12 17:55:02 +03:00
|
|
|
if (m_buffer_reload_dialog_opened)
|
|
|
|
close_buffer_reload_dialog();
|
|
|
|
|
2014-04-01 21:54:46 +04:00
|
|
|
auto& client_manager = ClientManager::instance();
|
2014-11-11 02:29:16 +03:00
|
|
|
m_window->options().unregister_watcher(*this);
|
2014-04-01 21:54:46 +04:00
|
|
|
client_manager.add_free_window(std::move(m_window),
|
|
|
|
std::move(context().selections()));
|
|
|
|
WindowAndSelections ws = client_manager.get_free_window(buffer);
|
2014-11-11 02:29:16 +03:00
|
|
|
|
2014-01-28 00:28:38 +04:00
|
|
|
m_window = std::move(ws.window);
|
2014-11-11 02:29:16 +03:00
|
|
|
m_window->options().register_watcher(*this);
|
|
|
|
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
|
|
|
|
|
2014-01-28 00:28:38 +04:00
|
|
|
context().m_selections = std::move(ws.selections);
|
2013-12-21 00:10:08 +04:00
|
|
|
context().set_window(*m_window);
|
|
|
|
m_window->set_dimensions(ui().dimensions());
|
2014-11-11 02:29:16 +03:00
|
|
|
|
2013-12-21 00:10:08 +04:00
|
|
|
m_window->hooks().run_hook("WinDisplay", buffer.name(), context());
|
|
|
|
}
|
|
|
|
|
2013-09-16 22:15:13 +04:00
|
|
|
void Client::redraw_ifn()
|
|
|
|
{
|
2014-07-07 23:13:08 +04:00
|
|
|
DisplayLine mode_line = generate_mode_line();
|
|
|
|
const bool buffer_changed = context().window().timestamp() != context().buffer().timestamp();
|
2014-08-12 22:19:46 +04:00
|
|
|
const bool mode_line_changed = mode_line.atoms() != m_mode_line.atoms();
|
|
|
|
const bool status_line_changed = m_status_line.atoms() != m_pending_status_line.atoms();
|
|
|
|
if (buffer_changed or status_line_changed or mode_line_changed)
|
2013-09-16 22:15:13 +04:00
|
|
|
{
|
2014-07-07 23:13:08 +04:00
|
|
|
if (buffer_changed)
|
|
|
|
{
|
|
|
|
CharCoord dimensions = context().ui().dimensions();
|
|
|
|
if (dimensions == CharCoord{0,0})
|
|
|
|
return;
|
|
|
|
context().window().set_dimensions(dimensions);
|
|
|
|
context().window().update_display_buffer(context());
|
|
|
|
}
|
|
|
|
m_mode_line = std::move(mode_line);
|
2014-08-12 22:19:46 +04:00
|
|
|
m_status_line = m_pending_status_line;
|
2013-11-14 22:09:15 +04:00
|
|
|
context().ui().draw(context().window().display_buffer(),
|
2014-07-07 23:13:08 +04:00
|
|
|
m_status_line, m_mode_line);
|
2013-09-16 22:15:13 +04:00
|
|
|
}
|
2014-04-15 22:19:44 +04:00
|
|
|
context().ui().refresh();
|
2013-09-16 22:15:13 +04:00
|
|
|
}
|
|
|
|
|
2015-02-12 17:55:02 +03:00
|
|
|
void Client::reload_buffer()
|
|
|
|
{
|
|
|
|
auto& buffer = context().buffer();
|
|
|
|
kak_assert(buffer.flags() & Buffer::Flags::File);
|
|
|
|
CharCoord view_pos = context().window().position();
|
|
|
|
ByteCoord cursor_pos = context().selections().main().cursor();
|
|
|
|
Buffer* buf = create_buffer_from_file(buffer.name());
|
|
|
|
kak_assert(buf == &buffer);
|
|
|
|
context().selections() = SelectionList{buffer, buffer.clamp(cursor_pos)};
|
|
|
|
context().window().set_position(view_pos);
|
|
|
|
context().print_status({ "'" + buffer.display_name() + "' reloaded",
|
|
|
|
get_face("Information") });
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::on_buffer_reload_key(Key key)
|
2013-10-21 21:58:11 +04:00
|
|
|
{
|
2015-02-12 17:55:02 +03:00
|
|
|
auto& buffer = context().buffer();
|
|
|
|
|
|
|
|
if (key == 'y' or key == ctrl('m'))
|
|
|
|
reload_buffer();
|
|
|
|
else if (key == 'n' or key == Key::Escape)
|
|
|
|
{
|
|
|
|
// reread timestamp in case the file was modified again
|
|
|
|
buffer.set_fs_timestamp(get_fs_timestamp(buffer.name()));
|
|
|
|
print_status({ "'" + buffer.display_name() + "' kept",
|
|
|
|
get_face("Information") });
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
print_status({ "'" + key_to_str(key) + "' is not a valid choice",
|
|
|
|
get_face("Error") });
|
|
|
|
m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
|
2013-10-21 21:58:11 +04:00
|
|
|
return;
|
2015-02-12 17:55:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& client : ClientManager::instance())
|
|
|
|
{
|
|
|
|
if (&client->context().buffer() == &buffer and
|
|
|
|
client->m_buffer_reload_dialog_opened)
|
|
|
|
client->close_buffer_reload_dialog();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::close_buffer_reload_dialog()
|
|
|
|
{
|
|
|
|
kak_assert(m_buffer_reload_dialog_opened);
|
|
|
|
m_buffer_reload_dialog_opened = false;
|
|
|
|
m_ui->info_hide();
|
|
|
|
m_input_handler.reset_normal_mode();
|
2013-10-21 21:58:11 +04:00
|
|
|
}
|
|
|
|
|
2015-02-12 17:55:02 +03:00
|
|
|
void Client::check_if_buffer_needs_reloading()
|
2013-10-15 21:51:31 +04:00
|
|
|
{
|
2015-03-04 23:46:26 +03:00
|
|
|
if (m_buffer_reload_dialog_opened)
|
|
|
|
return;
|
|
|
|
|
2013-11-14 22:09:15 +04:00
|
|
|
Buffer& buffer = context().buffer();
|
2013-10-21 21:58:11 +04:00
|
|
|
auto reload = context().options()["autoreload"].get<YesNoAsk>();
|
|
|
|
if (not (buffer.flags() & Buffer::Flags::File) or reload == No)
|
2013-10-15 21:51:31 +04:00
|
|
|
return;
|
2013-10-21 21:58:11 +04:00
|
|
|
|
|
|
|
const String& filename = buffer.name();
|
2013-10-15 21:51:31 +04:00
|
|
|
time_t ts = get_fs_timestamp(filename);
|
2014-06-22 14:09:44 +04:00
|
|
|
if (ts == InvalidTime or ts == buffer.fs_timestamp())
|
2013-10-21 21:58:11 +04:00
|
|
|
return;
|
|
|
|
if (reload == Ask)
|
2013-10-15 21:51:31 +04:00
|
|
|
{
|
2014-04-01 21:54:46 +04:00
|
|
|
m_ui->info_show(
|
|
|
|
"reload '" + buffer.display_name() + "' ?",
|
|
|
|
"'" + buffer.display_name() + "' was modified externally\n"
|
2015-02-12 17:55:02 +03:00
|
|
|
"press <ret> or y to reload, <esc> or n to keep",
|
2014-11-08 22:08:23 +03:00
|
|
|
CharCoord{}, get_face("Information"), InfoStyle::Prompt);
|
2014-03-24 23:31:40 +04:00
|
|
|
|
2015-02-12 17:55:02 +03:00
|
|
|
m_buffer_reload_dialog_opened = true;
|
|
|
|
m_input_handler.on_next_key(KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
|
2013-10-15 21:51:31 +04:00
|
|
|
}
|
2013-10-21 21:58:11 +04:00
|
|
|
else
|
2015-02-12 17:55:02 +03:00
|
|
|
reload_buffer();
|
2013-10-15 21:51:31 +04:00
|
|
|
}
|
|
|
|
|
2014-12-20 21:40:17 +03:00
|
|
|
StringView Client::get_env_var(const String& name) const
|
2014-04-08 00:25:44 +04:00
|
|
|
{
|
|
|
|
auto it = m_env_vars.find(name);
|
|
|
|
if (it == m_env_vars.end())
|
2014-12-20 21:40:17 +03:00
|
|
|
return {};
|
2014-04-08 00:25:44 +04:00
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
2014-11-11 02:29:16 +03:00
|
|
|
void Client::on_option_changed(const Option& option)
|
|
|
|
{
|
|
|
|
if (option.name() == "ui_options")
|
|
|
|
m_ui->set_ui_options(option.get<UserInterface::Options>());
|
|
|
|
}
|
|
|
|
|
2012-09-03 16:22:02 +04:00
|
|
|
}
|