2013-09-12 23:47:23 +02:00
|
|
|
#include "client.hh"
|
2012-09-03 14:22:02 +02:00
|
|
|
|
2014-07-11 01:27:04 +02:00
|
|
|
#include "face_registry.hh"
|
2012-09-03 14:22:02 +02:00
|
|
|
#include "context.hh"
|
2013-05-20 14:10:53 +02:00
|
|
|
#include "buffer_manager.hh"
|
2015-09-19 13:43:39 +02:00
|
|
|
#include "buffer_utils.hh"
|
2013-08-02 18:58:37 +02:00
|
|
|
#include "file.hh"
|
2013-10-11 19:43:23 +02:00
|
|
|
#include "remote.hh"
|
2017-03-16 10:57:39 +01:00
|
|
|
#include "option.hh"
|
2018-05-26 02:01:26 +02:00
|
|
|
#include "option_types.hh"
|
2013-10-15 19:51:31 +02:00
|
|
|
#include "client_manager.hh"
|
2015-09-19 13:43:39 +02:00
|
|
|
#include "command_manager.hh"
|
2014-11-25 02:00:18 +01:00
|
|
|
#include "event_manager.hh"
|
2016-02-27 18:23:13 +01:00
|
|
|
#include "user_interface.hh"
|
2013-11-14 22:12:59 +01:00
|
|
|
#include "window.hh"
|
2017-03-10 10:06:37 +01:00
|
|
|
#include "hash_map.hh"
|
2012-09-03 14:22:02 +02:00
|
|
|
|
2017-01-08 23:30:15 +01:00
|
|
|
#include <csignal>
|
2014-11-25 02:00:18 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2017-01-08 23:30:15 +01:00
|
|
|
#include <utility>
|
|
|
|
|
2012-09-03 14:22:02 +02:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
2013-12-20 21:10:08 +01:00
|
|
|
Client::Client(std::unique_ptr<UserInterface>&& ui,
|
|
|
|
std::unique_ptr<Window>&& window,
|
2017-08-28 08:12:15 +02:00
|
|
|
SelectionList selections, int pid,
|
2014-04-07 22:25:44 +02:00
|
|
|
EnvVarMap env_vars,
|
2017-08-23 08:22:23 +02:00
|
|
|
String name,
|
|
|
|
OnExitCallback on_exit)
|
2013-12-20 21:10:08 +01:00
|
|
|
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
2017-08-28 08:12:15 +02:00
|
|
|
m_pid{pid},
|
2017-08-23 08:22:23 +02:00
|
|
|
m_on_exit{std::move(on_exit)},
|
2019-02-09 05:41:09 +01:00
|
|
|
m_env_vars(std::move(env_vars)),
|
2014-12-19 00:12:58 +01:00
|
|
|
m_input_handler{std::move(selections), Context::Flags::None,
|
2019-02-09 05:41:09 +01:00
|
|
|
std::move(name)}
|
2013-11-14 19:09:15 +01:00
|
|
|
{
|
2016-05-09 14:48:48 +02:00
|
|
|
m_window->set_client(this);
|
|
|
|
|
2013-11-14 19:09:15 +01:00
|
|
|
context().set_client(*this);
|
2013-12-20 21:10:08 +01:00
|
|
|
context().set_window(*m_window);
|
2014-11-11 00:29:16 +01:00
|
|
|
|
2016-02-27 18:23:13 +01:00
|
|
|
m_window->set_dimensions(m_ui->dimensions());
|
2014-11-11 00:29:16 +01:00
|
|
|
m_window->options().register_watcher(*this);
|
2016-02-27 18:23:13 +01:00
|
|
|
|
2014-11-11 00:29:16 +01:00
|
|
|
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
|
2016-11-29 22:35:53 +01:00
|
|
|
m_ui->set_on_key([this](Key key) {
|
2023-03-13 10:55:31 +01:00
|
|
|
kak_assert(key != Key::Invalid);
|
2016-11-29 22:35:53 +01:00
|
|
|
if (key == ctrl('c'))
|
2021-07-12 02:08:17 +02:00
|
|
|
{
|
|
|
|
auto prev_handler = set_signal_handler(SIGINT, SIG_IGN);
|
2016-11-29 22:35:53 +01:00
|
|
|
killpg(getpgrp(), SIGINT);
|
2021-07-12 02:08:17 +02:00
|
|
|
set_signal_handler(SIGINT, prev_handler);
|
|
|
|
}
|
2023-05-15 13:10:02 +02:00
|
|
|
else if (key == ctrl('g'))
|
|
|
|
{
|
|
|
|
m_pending_keys.clear();
|
|
|
|
print_status({"operation cancelled", context().faces()["Error"]});
|
|
|
|
throw cancel{};
|
|
|
|
}
|
2018-06-08 10:54:11 +02:00
|
|
|
else if (key.modifiers & Key::Modifiers::Resize)
|
2018-03-30 00:58:18 +02:00
|
|
|
{
|
2018-06-19 12:20:38 +02:00
|
|
|
m_window->set_dimensions(key.coord());
|
2018-03-30 00:58:18 +02:00
|
|
|
force_redraw();
|
|
|
|
}
|
2016-11-29 22:35:53 +01:00
|
|
|
else
|
|
|
|
m_pending_keys.push_back(key);
|
|
|
|
});
|
2022-12-11 19:30:02 +01:00
|
|
|
m_ui->set_on_paste([this](StringView content) {
|
|
|
|
context().input_handler().paste(content);
|
|
|
|
});
|
2016-11-03 20:09:52 +01:00
|
|
|
|
2018-10-22 23:15:53 +02:00
|
|
|
m_window->hooks().run_hook(Hook::WinDisplay, m_window->buffer().name(), context());
|
2016-11-03 20:09:52 +01:00
|
|
|
|
2016-03-08 00:11:59 +01:00
|
|
|
force_redraw();
|
2013-11-14 19:09:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Client::~Client()
|
|
|
|
{
|
2014-11-11 00:29:16 +01:00
|
|
|
m_window->options().unregister_watcher(*this);
|
2016-05-09 14:48:48 +02:00
|
|
|
m_window->set_client(nullptr);
|
2017-01-22 13:19:23 +01:00
|
|
|
// Do not move the selections here, as we need them to be valid
|
|
|
|
// in order to correctly destroy the input handler
|
2017-01-19 19:44:26 +01:00
|
|
|
ClientManager::instance().add_free_window(std::move(m_window),
|
2017-01-22 13:19:23 +01:00
|
|
|
context().selections());
|
2013-11-14 19:09:15 +01:00
|
|
|
}
|
|
|
|
|
2018-04-29 14:27:28 +02:00
|
|
|
bool Client::is_ui_ok() const
|
|
|
|
{
|
|
|
|
return m_ui->is_ok();
|
|
|
|
}
|
|
|
|
|
2016-11-30 10:47:38 +01:00
|
|
|
bool Client::process_pending_inputs()
|
2013-11-14 19:09:15 +01:00
|
|
|
{
|
2016-11-30 10:47:38 +01:00
|
|
|
const bool debug_keys = (bool)(context().options()["debug"].get<DebugFlags>() & DebugFlags::Keys);
|
2019-12-16 04:07:22 +01:00
|
|
|
m_window->run_resize_hook_ifn();
|
2016-11-30 10:47:38 +01:00
|
|
|
// steal keys as we might receive new keys while handling them.
|
|
|
|
Vector<Key, MemoryDomain::Client> keys = std::move(m_pending_keys);
|
|
|
|
for (auto& key : keys)
|
2014-11-25 02:00:18 +01:00
|
|
|
{
|
2016-11-30 10:47:38 +01:00
|
|
|
try
|
2014-11-25 02:00:18 +01:00
|
|
|
{
|
2016-11-26 14:29:17 +01:00
|
|
|
if (debug_keys)
|
2022-08-04 10:51:10 +02:00
|
|
|
write_to_debug_buffer(format("Client '{}' got key '{}'", context().name(), key));
|
2016-11-26 14:29:17 +01:00
|
|
|
|
2016-11-29 22:35:53 +01:00
|
|
|
if (key == Key::FocusIn)
|
2018-10-22 23:15:53 +02:00
|
|
|
context().hooks().run_hook(Hook::FocusIn, context().name(), context());
|
2016-11-29 22:35:53 +01:00
|
|
|
else if (key == Key::FocusOut)
|
2018-10-22 23:15:53 +02:00
|
|
|
context().hooks().run_hook(Hook::FocusOut, context().name(), context());
|
2016-09-04 18:54:07 +02:00
|
|
|
else
|
2016-11-29 22:35:53 +01:00
|
|
|
m_input_handler.handle_key(key);
|
2017-01-25 00:57:36 +01:00
|
|
|
|
2022-08-04 10:51:10 +02:00
|
|
|
context().hooks().run_hook(Hook::RawKey, to_string(key), context());
|
2014-11-25 02:00:18 +01:00
|
|
|
}
|
2016-11-30 10:47:38 +01:00
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
2017-06-04 09:48:23 +02:00
|
|
|
write_to_debug_buffer(format("Error: {}", error.what()));
|
2023-05-15 13:10:02 +02:00
|
|
|
context().print_status({error.what().str(), context().faces()["Error"]});
|
2018-10-22 23:15:53 +02:00
|
|
|
context().hooks().run_hook(Hook::RuntimeError, error.what(), context());
|
2016-11-30 10:47:38 +01:00
|
|
|
}
|
2015-07-22 14:30:03 +02:00
|
|
|
}
|
2016-11-30 10:47:38 +01:00
|
|
|
return not keys.empty();
|
2013-11-14 19:09:15 +01:00
|
|
|
}
|
|
|
|
|
2018-03-30 00:58:18 +02:00
|
|
|
void Client::print_status(DisplayLine status_line)
|
2013-09-16 20:15:13 +02:00
|
|
|
{
|
2016-03-08 00:11:59 +01:00
|
|
|
m_status_line = std::move(status_line);
|
2018-03-30 00:58:18 +02:00
|
|
|
m_ui_pending |= StatusLine;
|
2013-09-16 20:15:13 +02:00
|
|
|
}
|
|
|
|
|
2016-11-29 20:53:11 +01:00
|
|
|
|
|
|
|
DisplayCoord Client::dimensions() const
|
|
|
|
{
|
|
|
|
return m_ui->dimensions();
|
|
|
|
}
|
|
|
|
|
2017-03-10 10:06:37 +01:00
|
|
|
String generate_context_info(const Context& context)
|
|
|
|
{
|
|
|
|
String s = "";
|
|
|
|
if (context.buffer().is_modified())
|
|
|
|
s += "[+]";
|
|
|
|
if (context.client().input_handler().is_recording())
|
|
|
|
s += format("[recording ({})]", context.client().input_handler().recording_reg());
|
|
|
|
if (context.hooks_disabled())
|
|
|
|
s += "[no-hooks]";
|
2018-01-26 12:03:20 +01:00
|
|
|
if (not(context.buffer().flags() & (Buffer::Flags::File | Buffer::Flags::Debug)))
|
|
|
|
s += "[scratch]";
|
|
|
|
if (context.buffer().flags() & Buffer::Flags::New)
|
|
|
|
s += "[new file]";
|
2017-03-10 10:06:37 +01:00
|
|
|
if (context.buffer().flags() & Buffer::Flags::Fifo)
|
|
|
|
s += "[fifo]";
|
2017-09-19 18:16:11 +02:00
|
|
|
if (context.buffer().flags() & Buffer::Flags::Debug)
|
|
|
|
s += "[debug]";
|
2021-09-21 11:47:20 +02:00
|
|
|
if (context.buffer().flags() & Buffer::Flags::ReadOnly)
|
|
|
|
s += "[readonly]";
|
2017-03-10 10:06:37 +01:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2013-10-10 22:34:19 +02:00
|
|
|
DisplayLine Client::generate_mode_line() const
|
2013-09-16 20:15:13 +02:00
|
|
|
{
|
2015-09-19 13:43:39 +02:00
|
|
|
DisplayLine modeline;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const String& modelinefmt = context().options()["modelinefmt"].get<String>();
|
2017-03-10 10:06:37 +01:00
|
|
|
HashMap<String, DisplayLine> atoms{{ "mode_info", context().client().input_handler().mode_line() },
|
2018-04-07 07:36:39 +02:00
|
|
|
{ "context_info", {generate_context_info(context()),
|
|
|
|
context().faces()["Information"]}}};
|
2017-03-10 10:06:37 +01:00
|
|
|
auto expanded = expand(modelinefmt, context(), ShellContext{},
|
|
|
|
[](String s) { return escape(s, '{', '\\'); });
|
2018-04-07 07:36:39 +02:00
|
|
|
modeline = parse_display_line(expanded, context().faces(), atoms);
|
2015-09-19 13:43:39 +02:00
|
|
|
}
|
|
|
|
catch (runtime_error& err)
|
|
|
|
{
|
|
|
|
write_to_debug_buffer(format("Error while parsing modelinefmt: {}", err.what()));
|
2018-04-07 07:36:39 +02:00
|
|
|
modeline.push_back({ "modelinefmt error, see *debug* buffer", context().faces()["Error"] });
|
2015-09-19 13:43:39 +02:00
|
|
|
}
|
2013-09-16 20:15:13 +02:00
|
|
|
|
2015-09-19 13:43:39 +02:00
|
|
|
return modeline;
|
2013-09-16 20:15:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-15 22:21:53 +02:00
|
|
|
void Client::change_buffer(Buffer& buffer, Optional<FunctionRef<void()>> set_selections)
|
2013-12-20 21:10:08 +01:00
|
|
|
{
|
2015-02-12 15:55:02 +01:00
|
|
|
if (m_buffer_reload_dialog_opened)
|
|
|
|
close_buffer_reload_dialog();
|
|
|
|
|
2014-04-01 19:54:46 +02:00
|
|
|
auto& client_manager = ClientManager::instance();
|
2019-12-28 01:27:04 +01:00
|
|
|
WindowAndSelections ws = client_manager.get_free_window(buffer);
|
|
|
|
|
2014-11-11 00:29:16 +01:00
|
|
|
m_window->options().unregister_watcher(*this);
|
2016-05-09 14:48:48 +02:00
|
|
|
m_window->set_client(nullptr);
|
2014-04-01 19:54:46 +02:00
|
|
|
client_manager.add_free_window(std::move(m_window),
|
2022-08-15 22:21:53 +02:00
|
|
|
context().selections());
|
2019-12-28 01:27:04 +01:00
|
|
|
|
2014-01-27 21:28:38 +01:00
|
|
|
m_window = std::move(ws.window);
|
2016-05-09 14:48:48 +02:00
|
|
|
m_window->set_client(this);
|
2014-11-11 00:29:16 +01:00
|
|
|
m_window->options().register_watcher(*this);
|
2022-08-15 22:21:53 +02:00
|
|
|
|
|
|
|
if (set_selections)
|
|
|
|
(*set_selections)();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScopedSelectionEdition selection_edition{context()};
|
|
|
|
context().selections_write_only() = std::move(ws.selections);
|
|
|
|
}
|
|
|
|
|
2013-12-20 21:10:08 +01:00
|
|
|
context().set_window(*m_window);
|
2019-12-28 01:27:04 +01:00
|
|
|
|
2016-02-27 18:23:13 +01:00
|
|
|
m_window->set_dimensions(m_ui->dimensions());
|
2019-12-28 01:27:04 +01:00
|
|
|
m_ui->set_ui_options(m_window->options()["ui_options"].get<UserInterface::Options>());
|
2014-11-11 00:29:16 +01:00
|
|
|
|
2018-10-22 23:15:53 +02:00
|
|
|
m_window->hooks().run_hook(Hook::WinDisplay, buffer.name(), context());
|
2017-05-13 12:18:21 +02:00
|
|
|
force_redraw();
|
2013-12-20 21:10:08 +01:00
|
|
|
}
|
|
|
|
|
2016-02-27 18:23:13 +01:00
|
|
|
static bool is_inline(InfoStyle style)
|
|
|
|
{
|
|
|
|
return style == InfoStyle::Inline or
|
|
|
|
style == InfoStyle::InlineAbove or
|
|
|
|
style == InfoStyle::InlineBelow;
|
|
|
|
}
|
|
|
|
|
2016-03-08 00:11:59 +01:00
|
|
|
void Client::redraw_ifn()
|
2013-09-16 20:15:13 +02:00
|
|
|
{
|
2015-06-22 14:34:22 +02:00
|
|
|
Window& window = context().window();
|
2016-03-08 00:11:59 +01:00
|
|
|
if (window.needs_redraw(context()))
|
|
|
|
m_ui_pending |= Draw;
|
2015-06-17 22:28:02 +02:00
|
|
|
|
2016-03-08 00:11:59 +01:00
|
|
|
DisplayLine mode_line = generate_mode_line();
|
|
|
|
if (mode_line.atoms() != m_mode_line.atoms())
|
|
|
|
{
|
|
|
|
m_ui_pending |= StatusLine;
|
|
|
|
m_mode_line = std::move(mode_line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ui_pending == 0)
|
|
|
|
return;
|
|
|
|
|
2019-11-02 21:49:26 +01:00
|
|
|
const auto& faces = context().faces();
|
2018-04-07 07:36:39 +02:00
|
|
|
|
2016-03-08 00:11:59 +01:00
|
|
|
if (m_ui_pending & Draw)
|
2016-04-11 14:44:10 +02:00
|
|
|
m_ui->draw(window.update_display_buffer(context()),
|
2018-04-07 07:36:39 +02:00
|
|
|
faces["Default"], faces["BufferPadding"]);
|
2016-02-27 18:23:13 +01:00
|
|
|
|
2017-06-16 11:19:08 +02:00
|
|
|
const bool update_menu_anchor = (m_ui_pending & Draw) and not (m_ui_pending & MenuHide) and
|
|
|
|
not m_menu.items.empty() and m_menu.style == MenuStyle::Inline;
|
|
|
|
if ((m_ui_pending & MenuShow) or update_menu_anchor)
|
2013-09-16 20:15:13 +02:00
|
|
|
{
|
2017-06-16 11:19:08 +02:00
|
|
|
auto anchor = m_menu.style == MenuStyle::Inline ?
|
2016-09-22 21:36:26 +02:00
|
|
|
window.display_position(m_menu.anchor) : DisplayCoord{};
|
2017-06-16 11:19:08 +02:00
|
|
|
if (not (m_ui_pending & MenuShow) and m_menu.ui_anchor != anchor)
|
|
|
|
m_ui_pending |= anchor ? (MenuShow | MenuSelect) : MenuHide;
|
|
|
|
m_menu.ui_anchor = anchor;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_ui_pending & MenuShow and m_menu.ui_anchor)
|
|
|
|
m_ui->menu_show(m_menu.items, *m_menu.ui_anchor,
|
2018-04-07 07:36:39 +02:00
|
|
|
faces["MenuForeground"], faces["MenuBackground"],
|
2016-03-08 00:11:59 +01:00
|
|
|
m_menu.style);
|
2017-06-16 11:19:08 +02:00
|
|
|
if (m_ui_pending & MenuSelect and m_menu.ui_anchor)
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui->menu_select(m_menu.selected);
|
|
|
|
if (m_ui_pending & MenuHide)
|
|
|
|
m_ui->menu_hide();
|
2015-06-17 22:28:02 +02:00
|
|
|
|
2017-06-16 11:19:08 +02:00
|
|
|
const bool update_info_anchor = (m_ui_pending & Draw) and not (m_ui_pending & InfoHide) and
|
|
|
|
not m_info.content.empty() and is_inline(m_info.style);
|
|
|
|
if ((m_ui_pending & InfoShow) or update_info_anchor)
|
2016-03-05 14:53:21 +01:00
|
|
|
{
|
2017-06-16 11:19:08 +02:00
|
|
|
auto anchor = is_inline(m_info.style) ?
|
|
|
|
window.display_position(m_info.anchor) : DisplayCoord{};
|
|
|
|
if (not (m_ui_pending & MenuShow) and m_info.ui_anchor != anchor)
|
|
|
|
m_ui_pending |= anchor ? InfoShow : InfoHide;
|
|
|
|
m_info.ui_anchor = anchor;
|
2016-03-05 14:53:21 +01:00
|
|
|
}
|
2017-06-16 11:19:08 +02:00
|
|
|
|
|
|
|
if (m_ui_pending & InfoShow and m_info.ui_anchor)
|
|
|
|
m_ui->info_show(m_info.title, m_info.content, *m_info.ui_anchor,
|
2018-04-07 07:36:39 +02:00
|
|
|
faces["Information"], m_info.style);
|
2016-03-08 00:11:59 +01:00
|
|
|
if (m_ui_pending & InfoHide)
|
|
|
|
m_ui->info_hide();
|
|
|
|
|
|
|
|
if (m_ui_pending & StatusLine)
|
2018-04-07 07:36:39 +02:00
|
|
|
m_ui->draw_status(m_status_line, m_mode_line, faces["StatusLine"]);
|
2016-03-08 00:11:59 +01:00
|
|
|
|
2017-04-12 11:39:17 +02:00
|
|
|
auto cursor = m_input_handler.get_cursor_info();
|
|
|
|
m_ui->set_cursor(cursor.first, cursor.second);
|
|
|
|
|
2019-09-26 12:14:08 +02:00
|
|
|
m_ui->refresh(m_ui_pending & Refresh);
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending = 0;
|
2013-09-16 20:15:13 +02:00
|
|
|
}
|
|
|
|
|
2015-08-23 14:29:24 +02:00
|
|
|
void Client::force_redraw()
|
|
|
|
{
|
2016-03-08 22:35:56 +01:00
|
|
|
m_ui_pending |= Refresh | Draw | StatusLine |
|
2016-03-08 14:42:00 +01:00
|
|
|
(m_menu.items.empty() ? MenuHide : MenuShow | MenuSelect) |
|
|
|
|
(m_info.content.empty() ? InfoHide : InfoShow);
|
2015-08-23 14:29:24 +02:00
|
|
|
}
|
|
|
|
|
2015-02-12 15:55:02 +01:00
|
|
|
void Client::reload_buffer()
|
|
|
|
{
|
2015-10-16 02:33:17 +02:00
|
|
|
Buffer& buffer = context().buffer();
|
2018-02-05 10:26:44 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
reload_file_buffer(buffer);
|
|
|
|
context().print_status({ format("'{}' reloaded", buffer.display_name()),
|
2018-04-07 07:36:39 +02:00
|
|
|
context().faces()["Information"] });
|
2018-06-12 18:31:23 +02:00
|
|
|
|
2018-10-22 23:15:53 +02:00
|
|
|
m_window->hooks().run_hook(Hook::BufReload, buffer.name(), context());
|
2018-02-05 10:26:44 +01:00
|
|
|
}
|
|
|
|
catch (runtime_error& error)
|
|
|
|
{
|
|
|
|
context().print_status({ format("error while reloading buffer: '{}'", error.what()),
|
2018-04-07 07:36:39 +02:00
|
|
|
context().faces()["Error"] });
|
2019-11-23 15:55:46 +01:00
|
|
|
buffer.set_fs_status(get_fs_status(buffer.name()));
|
2018-02-05 10:26:44 +01:00
|
|
|
}
|
2015-02-12 15:55:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Client::on_buffer_reload_key(Key key)
|
2013-10-21 19:58:11 +02:00
|
|
|
{
|
2015-02-12 15:55:02 +01:00
|
|
|
auto& buffer = context().buffer();
|
|
|
|
|
2018-03-05 01:17:57 +01:00
|
|
|
auto set_autoreload = [this](Autoreload autoreload) {
|
|
|
|
auto* option = &context().options()["autoreload"];
|
|
|
|
// Do not touch global autoreload, set it at least at buffer level
|
|
|
|
if (&option->manager() == &GlobalScope::instance().options())
|
|
|
|
option = &context().buffer().options().get_local_option("autoreload");
|
|
|
|
option->set(autoreload);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (key == 'y' or key == 'Y' or key == Key::Return)
|
|
|
|
{
|
2015-02-12 15:55:02 +01:00
|
|
|
reload_buffer();
|
2018-03-05 01:17:57 +01:00
|
|
|
if (key == 'Y')
|
|
|
|
set_autoreload(Autoreload::Yes);
|
|
|
|
}
|
|
|
|
else if (key == 'n' or key == 'N' or key == Key::Escape)
|
2015-02-12 15:55:02 +01:00
|
|
|
{
|
|
|
|
// reread timestamp in case the file was modified again
|
2019-11-23 15:55:46 +01:00
|
|
|
buffer.set_fs_status(get_fs_status(buffer.name()));
|
2015-06-01 22:15:59 +02:00
|
|
|
print_status({ format("'{}' kept", buffer.display_name()),
|
2018-04-07 07:36:39 +02:00
|
|
|
context().faces()["Information"] });
|
2018-03-05 01:17:57 +01:00
|
|
|
if (key == 'N')
|
|
|
|
set_autoreload(Autoreload::No);
|
2015-02-12 15:55:02 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-08-04 10:51:10 +02:00
|
|
|
print_status({ format("'{}' is not a valid choice", key),
|
2018-04-07 07:36:39 +02:00
|
|
|
context().faces()["Error"] });
|
2019-11-11 11:38:40 +01:00
|
|
|
m_input_handler.on_next_key("buffer-reload", KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
|
2013-10-21 19:58:11 +02:00
|
|
|
return;
|
2015-02-12 15:55:02 +01: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);
|
2018-01-26 03:45:36 +01:00
|
|
|
// Reset first as this might check for reloading.
|
|
|
|
m_input_handler.reset_normal_mode();
|
2015-02-12 15:55:02 +01:00
|
|
|
m_buffer_reload_dialog_opened = false;
|
2017-01-04 12:24:07 +01:00
|
|
|
info_hide(true);
|
2013-10-21 19:58:11 +02:00
|
|
|
}
|
|
|
|
|
2015-02-12 15:55:02 +01:00
|
|
|
void Client::check_if_buffer_needs_reloading()
|
2013-10-15 19:51:31 +02:00
|
|
|
{
|
2015-03-04 21:46:26 +01:00
|
|
|
if (m_buffer_reload_dialog_opened)
|
|
|
|
return;
|
|
|
|
|
2013-11-14 19:09:15 +01:00
|
|
|
Buffer& buffer = context().buffer();
|
2015-11-20 09:50:53 +01:00
|
|
|
auto reload = context().options()["autoreload"].get<Autoreload>();
|
|
|
|
if (not (buffer.flags() & Buffer::Flags::File) or reload == Autoreload::No)
|
2013-10-15 19:51:31 +02:00
|
|
|
return;
|
2013-10-21 19:58:11 +02:00
|
|
|
|
2023-02-21 06:59:16 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
const String& filename = buffer.name();
|
|
|
|
const timespec ts = get_fs_timestamp(filename);
|
|
|
|
const auto status = buffer.fs_status();
|
2019-11-23 15:55:46 +01:00
|
|
|
|
2023-02-21 06:59:16 +01:00
|
|
|
if (ts == InvalidTime or ts == status.timestamp)
|
|
|
|
return;
|
2019-11-23 15:55:46 +01:00
|
|
|
|
2023-02-21 06:59:16 +01:00
|
|
|
if (MappedFile fd{filename};
|
|
|
|
fd.st.st_size == status.file_size and hash_data(fd.data, fd.st.st_size) == status.hash)
|
|
|
|
return;
|
2019-11-23 15:55:46 +01:00
|
|
|
|
2023-02-21 06:59:16 +01:00
|
|
|
if (reload == Autoreload::Ask)
|
|
|
|
{
|
|
|
|
StringView bufname = buffer.display_name();
|
|
|
|
info_show(format("reload '{}' ?", bufname),
|
|
|
|
format("'{}' was modified externally\n"
|
|
|
|
" y, <ret>: reload | n, <esc>: keep\n"
|
|
|
|
" Y: always reload | N: always keep\n",
|
|
|
|
bufname), {}, InfoStyle::Modal);
|
|
|
|
|
|
|
|
m_buffer_reload_dialog_opened = true;
|
|
|
|
m_input_handler.on_next_key("buffer-reload", KeymapMode::None, [this](Key key, Context&){ on_buffer_reload_key(key); });
|
|
|
|
}
|
|
|
|
else
|
|
|
|
reload_buffer();
|
|
|
|
}
|
|
|
|
catch (Kakoune::runtime_error& error)
|
2013-10-15 19:51:31 +02:00
|
|
|
{
|
2023-02-21 06:59:16 +01:00
|
|
|
write_to_debug_buffer(format("Error while checking if buffer {} changed: {}", buffer.name(), error.what()));
|
2013-10-15 19:51:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-16 20:57:57 +02:00
|
|
|
StringView Client::get_env_var(StringView name) const
|
2014-04-07 22:25:44 +02:00
|
|
|
{
|
|
|
|
auto it = m_env_vars.find(name);
|
|
|
|
if (it == m_env_vars.end())
|
2014-12-20 19:40:17 +01:00
|
|
|
return {};
|
2015-09-16 20:57:57 +02:00
|
|
|
return it->value;
|
2015-03-10 20:33:46 +01:00
|
|
|
}
|
|
|
|
|
2014-11-11 00:29:16 +01:00
|
|
|
void Client::on_option_changed(const Option& option)
|
|
|
|
{
|
|
|
|
if (option.name() == "ui_options")
|
2016-03-05 14:53:21 +01:00
|
|
|
{
|
2014-11-11 00:29:16 +01:00
|
|
|
m_ui->set_ui_options(option.get<UserInterface::Options>());
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= Draw;
|
2016-03-05 14:53:21 +01:00
|
|
|
}
|
2014-11-11 00:29:16 +01:00
|
|
|
}
|
|
|
|
|
2016-09-22 21:36:26 +02:00
|
|
|
void Client::menu_show(Vector<DisplayLine> choices, BufferCoord anchor, MenuStyle style)
|
2016-02-27 18:23:13 +01:00
|
|
|
{
|
2016-03-30 20:46:43 +02:00
|
|
|
m_menu = Menu{ std::move(choices), anchor, {}, style, -1 };
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= MenuShow;
|
|
|
|
m_ui_pending &= ~MenuHide;
|
2016-02-27 18:23:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Client::menu_select(int selected)
|
|
|
|
{
|
|
|
|
m_menu.selected = selected;
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= MenuSelect;
|
|
|
|
m_ui_pending &= ~MenuHide;
|
2016-02-27 18:23:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Client::menu_hide()
|
|
|
|
{
|
|
|
|
m_menu = Menu{};
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= MenuHide;
|
|
|
|
m_ui_pending &= ~(MenuShow | MenuSelect);
|
2016-02-27 18:23:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-22 11:48:26 +01:00
|
|
|
void Client::info_show(DisplayLine title, DisplayLineList content, BufferCoord anchor, InfoStyle style)
|
2016-02-27 18:23:13 +01:00
|
|
|
{
|
2017-01-04 12:24:07 +01:00
|
|
|
if (m_info.style == InfoStyle::Modal) // We already have a modal info opened, do not touch it.
|
|
|
|
return;
|
|
|
|
|
2016-03-30 20:46:43 +02:00
|
|
|
m_info = Info{ std::move(title), std::move(content), anchor, {}, style };
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= InfoShow;
|
|
|
|
m_ui_pending &= ~InfoHide;
|
2016-02-27 18:23:13 +01:00
|
|
|
}
|
|
|
|
|
2019-11-22 11:48:26 +01:00
|
|
|
void Client::info_show(StringView title, StringView content, BufferCoord anchor, InfoStyle style)
|
|
|
|
{
|
2019-11-24 01:39:33 +01:00
|
|
|
if (not content.empty() and content.back() == '\n')
|
|
|
|
content = content.substr(0, content.length() - 1);
|
2019-11-24 07:25:14 +01:00
|
|
|
info_show(title.empty() ? DisplayLine{} : DisplayLine{title.str(), Face{}},
|
2019-11-22 11:48:26 +01:00
|
|
|
content | split<StringView>('\n')
|
2019-11-24 07:25:14 +01:00
|
|
|
| transform([](StringView s) { return DisplayLine{replace(s, '\t', ' '), Face{}}; })
|
2019-11-22 11:48:26 +01:00
|
|
|
| gather<DisplayLineList>(),
|
|
|
|
anchor, style);
|
|
|
|
}
|
|
|
|
|
2017-01-04 12:24:07 +01:00
|
|
|
void Client::info_hide(bool even_modal)
|
2016-02-27 18:23:13 +01:00
|
|
|
{
|
2017-01-04 12:24:07 +01:00
|
|
|
if (not even_modal and m_info.style == InfoStyle::Modal)
|
|
|
|
return;
|
|
|
|
|
2016-02-27 18:23:13 +01:00
|
|
|
m_info = Info{};
|
2016-03-08 00:11:59 +01:00
|
|
|
m_ui_pending |= InfoHide;
|
|
|
|
m_ui_pending &= ~InfoShow;
|
2016-02-27 18:23:13 +01:00
|
|
|
}
|
|
|
|
|
2012-09-03 14:22:02 +02:00
|
|
|
}
|