Extract InputHandler from Client

This commit is contained in:
Maxime Coste 2013-11-14 18:09:15 +00:00
parent fe55d51e9f
commit ad275d1d1c
9 changed files with 198 additions and 134 deletions

View File

@ -6,6 +6,7 @@
#include "file.hh" #include "file.hh"
#include "utils.hh" #include "utils.hh"
#include "window.hh" #include "window.hh"
#include "client.hh"
#include <algorithm> #include <algorithm>
@ -40,19 +41,19 @@ Buffer::Buffer(String name, Flags flags, std::vector<String> lines,
} }
Editor editor_for_hooks(*this); Editor editor_for_hooks(*this);
Context context(editor_for_hooks); InputHandler hook_handler(editor_for_hooks);
if (flags & Flags::File) if (flags & Flags::File)
{ {
if (flags & Flags::New) if (flags & Flags::New)
m_hooks.run_hook("BufNew", m_name, context); m_hooks.run_hook("BufNew", m_name, hook_handler.context());
else else
{ {
kak_assert(m_fs_timestamp != InvalidTime); kak_assert(m_fs_timestamp != InvalidTime);
m_hooks.run_hook("BufOpen", m_name, context); m_hooks.run_hook("BufOpen", m_name, hook_handler.context());
} }
} }
m_hooks.run_hook("BufCreate", m_name, context); m_hooks.run_hook("BufCreate", m_name, hook_handler.context());
// now we may begin to record undo data // now we may begin to record undo data
m_flags = flags; m_flags = flags;
@ -65,8 +66,8 @@ Buffer::~Buffer()
{ {
{ {
Editor hook_editor{*this}; Editor hook_editor{*this};
Context hook_context{hook_editor}; InputHandler hook_handler(hook_editor);
m_hooks.run_hook("BufClose", m_name, hook_context); m_hooks.run_hook("BufClose", m_name, hook_handler.context());
} }
m_options.unregister_watcher(*this); m_options.unregister_watcher(*this);
@ -763,7 +764,7 @@ void Buffer::on_option_changed(const Option& option)
{ {
String desc = option.name() + "=" + option.get_as_string(); String desc = option.name() + "=" + option.get_as_string();
Editor hook_editor{*this}; Editor hook_editor{*this};
Context hook_context{hook_editor}; InputHandler hook_handler(hook_editor);
m_hooks.run_hook("BufSetOption", desc, hook_context); m_hooks.run_hook("BufSetOption", desc, hook_handler.context());
} }
} }

View File

@ -22,26 +22,26 @@ namespace Kakoune
class InputMode class InputMode
{ {
public: public:
InputMode(Client& client) : m_client(client) {} InputMode(InputHandler& input_handler) : m_input_handler(input_handler) {}
virtual ~InputMode() {} virtual ~InputMode() {}
InputMode(const InputMode&) = delete; InputMode(const InputMode&) = delete;
InputMode& operator=(const InputMode&) = delete; InputMode& operator=(const InputMode&) = delete;
virtual void on_key(Key key) = 0; virtual void on_key(Key key) = 0;
virtual void on_replaced() {} virtual void on_replaced() {}
Context& context() const { return m_client.context(); } Context& context() const { return m_input_handler.context(); }
virtual String description() const = 0; virtual String description() const = 0;
virtual KeymapMode keymap_mode() const = 0; virtual KeymapMode keymap_mode() const = 0;
using Insertion = Client::Insertion; using Insertion = InputHandler::Insertion;
Insertion& last_insert() { return m_client.m_last_insert; } Insertion& last_insert() { return m_input_handler.m_last_insert; }
protected: protected:
void reset_normal_mode(); void reset_normal_mode();
private: private:
Client& m_client; InputHandler& m_input_handler;
}; };
namespace InputModes namespace InputModes
@ -53,17 +53,18 @@ static constexpr std::chrono::milliseconds fs_check_timeout{500};
class Normal : public InputMode class Normal : public InputMode
{ {
public: public:
Normal(Client& client) Normal(InputHandler& input_handler)
: InputMode(client), : InputMode(input_handler),
m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) { m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) {
context().hooks().run_hook("NormalIdle", "", context()); context().hooks().run_hook("NormalIdle", "", context());
}}, }},
m_fs_check_timer{Clock::now() + fs_check_timeout, [this](Timer& timer) { m_fs_check_timer{Clock::now() + fs_check_timeout, [this](Timer& timer) {
if (not context().has_client())
return;
context().client().check_buffer_fs_timestamp(); context().client().check_buffer_fs_timestamp();
timer.set_next_date(Clock::now() + fs_check_timeout); timer.set_next_date(Clock::now() + fs_check_timeout);
}} }}
{ {
context().client().check_buffer_fs_timestamp();
context().hooks().run_hook("NormalBegin", "", context()); context().hooks().run_hook("NormalBegin", "", context());
} }
@ -179,12 +180,14 @@ private:
class Menu : public InputMode class Menu : public InputMode
{ {
public: public:
Menu(Client& client, memoryview<String> choices, Menu(InputHandler& input_handler, memoryview<String> choices,
MenuCallback callback) MenuCallback callback)
: InputMode(client), : InputMode(input_handler),
m_callback(callback), m_choices(choices.begin(), choices.end()), m_callback(callback), m_choices(choices.begin(), choices.end()),
m_selected(m_choices.begin()) m_selected(m_choices.begin())
{ {
if (not context().has_ui())
return;
DisplayCoord menu_pos{ context().ui().dimensions().line, 0_char }; DisplayCoord menu_pos{ context().ui().dimensions().line, 0_char };
context().ui().menu_show(choices, menu_pos, get_color("MenuForeground"), context().ui().menu_show(choices, menu_pos, get_color("MenuForeground"),
get_color("MenuBackground"), MenuStyle::Prompt); get_color("MenuBackground"), MenuStyle::Prompt);
@ -198,7 +201,8 @@ public:
if (key == ctrl('m')) if (key == ctrl('m'))
{ {
context().ui().menu_hide(); if (context().has_ui())
context().ui().menu_hide();
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
reset_normal_mode(); reset_normal_mode();
int selected = m_selected - m_choices.begin(); int selected = m_selected - m_choices.begin();
@ -216,7 +220,8 @@ public:
} }
else else
{ {
context().ui().menu_hide(); if (context().has_ui())
context().ui().menu_hide();
reset_normal_mode(); reset_normal_mode();
int selected = m_selected - m_choices.begin(); int selected = m_selected - m_choices.begin();
m_callback(selected, MenuEvent::Abort, context()); m_callback(selected, MenuEvent::Abort, context());
@ -281,7 +286,8 @@ private:
{ {
m_selected = it; m_selected = it;
int selected = m_selected - m_choices.begin(); int selected = m_selected - m_choices.begin();
context().ui().menu_select(selected); if (context().has_ui())
context().ui().menu_select(selected);
m_callback(selected, MenuEvent::Select, context()); m_callback(selected, MenuEvent::Select, context());
} }
@ -311,9 +317,9 @@ String common_prefix(memoryview<String> strings)
class Prompt : public InputMode class Prompt : public InputMode
{ {
public: public:
Prompt(Client& client, const String& prompt, Prompt(InputHandler& input_handler, const String& prompt,
ColorPair colors, Completer completer, PromptCallback callback) ColorPair colors, Completer completer, PromptCallback callback)
: InputMode(client), m_prompt(prompt), m_prompt_colors(colors), : InputMode(input_handler), m_prompt(prompt), m_prompt_colors(colors),
m_completer(completer), m_callback(callback) m_completer(completer), m_callback(callback)
{ {
m_history_it = ms_history[m_prompt].end(); m_history_it = ms_history[m_prompt].end();
@ -344,7 +350,8 @@ public:
history.push_back(line); history.push_back(line);
} }
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
context().ui().menu_hide(); if (context().has_ui())
context().ui().menu_hide();
reset_normal_mode(); reset_normal_mode();
// call callback after reset_normal_mode so that callback // call callback after reset_normal_mode so that callback
// may change the mode // may change the mode
@ -354,7 +361,8 @@ public:
else if (key == Key::Escape or key == ctrl('c')) else if (key == Key::Escape or key == ctrl('c'))
{ {
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
context().ui().menu_hide(); if (context().has_ui())
context().ui().menu_hide();
reset_normal_mode(); reset_normal_mode();
m_callback(line, PromptEvent::Abort, context()); m_callback(line, PromptEvent::Abort, context());
return; return;
@ -432,7 +440,8 @@ public:
m_current_completion = candidates.size()-1; m_current_completion = candidates.size()-1;
const String& completion = candidates[m_current_completion]; const String& completion = candidates[m_current_completion];
context().ui().menu_select(m_current_completion); if (context().has_ui())
context().ui().menu_select(m_current_completion);
m_line_editor.insert_from(line.char_count_to(m_completions.start), m_line_editor.insert_from(line.char_count_to(m_completions.start),
completion); completion);
@ -450,7 +459,8 @@ public:
{ {
m_line_editor.handle_key(key); m_line_editor.handle_key(key);
m_current_completion = -1; m_current_completion = -1;
context().ui().menu_hide(); if (context().has_ui())
context().ui().menu_hide();
showcompl = true; showcompl = true;
} }
@ -486,7 +496,7 @@ private:
m_completions = m_completer(context(), CompletionFlags::Fast, line, m_completions = m_completer(context(), CompletionFlags::Fast, line,
line.byte_count_to(m_line_editor.cursor_pos())); line.byte_count_to(m_line_editor.cursor_pos()));
CandidateList& candidates = m_completions.candidates; CandidateList& candidates = m_completions.candidates;
if (not candidates.empty()) if (context().has_ui() and not candidates.empty())
{ {
DisplayCoord menu_pos{ context().ui().dimensions().line, 0_char }; DisplayCoord menu_pos{ context().ui().dimensions().line, 0_char };
context().ui().menu_show(candidates, menu_pos, get_color("MenuForeground"), context().ui().menu_show(candidates, menu_pos, get_color("MenuForeground"),
@ -522,8 +532,8 @@ std::unordered_map<String, std::vector<String>> Prompt::ms_history;
class NextKey : public InputMode class NextKey : public InputMode
{ {
public: public:
NextKey(Client& client, KeyCallback callback) NextKey(InputHandler& input_handler, KeyCallback callback)
: InputMode(client), m_callback(callback) {} : InputMode(input_handler), m_callback(callback) {}
void on_key(Key key) override void on_key(Key key) override
{ {
@ -592,7 +602,8 @@ public:
m_completions.end = cursor_pos; m_completions.end = cursor_pos;
m_completions.begin = buffer.advance(m_completions.end, -candidate.length()); m_completions.begin = buffer.advance(m_completions.end, -candidate.length());
m_completions.timestamp = m_context.buffer().timestamp(); m_completions.timestamp = m_context.buffer().timestamp();
m_context.ui().menu_select(m_current_candidate); if (m_context.has_ui())
m_context.ui().menu_select(m_current_candidate);
// when we select a match, remove non displayed matches from the candidates // when we select a match, remove non displayed matches from the candidates
// which are considered as invalid with the new completion timestamp // which are considered as invalid with the new completion timestamp
@ -630,7 +641,6 @@ public:
} }
if (not m_matching_candidates.empty()) if (not m_matching_candidates.empty())
{ {
m_context.ui().menu_hide();
m_current_candidate = m_matching_candidates.size(); m_current_candidate = m_matching_candidates.size();
m_completions.end = cursor; m_completions.end = cursor;
menu_show(); menu_show();
@ -646,7 +656,8 @@ public:
void reset() void reset()
{ {
m_completions = BufferCompletion{}; m_completions = BufferCompletion{};
m_context.ui().menu_hide(); if (m_context.has_ui())
m_context.ui().menu_hide();
} }
template<BufferCompletion (BufferCompleter::*complete_func)(const Buffer&, BufferCoord)> template<BufferCompletion (BufferCompleter::*complete_func)(const Buffer&, BufferCoord)>
@ -661,7 +672,6 @@ public:
kak_assert(cursor_pos >= m_completions.begin); kak_assert(cursor_pos >= m_completions.begin);
m_matching_candidates = m_completions.candidates; m_matching_candidates = m_completions.candidates;
m_current_candidate = m_matching_candidates.size(); m_current_candidate = m_matching_candidates.size();
m_context.ui().menu_hide();
menu_show(); menu_show();
m_matching_candidates.push_back(buffer.string(m_completions.begin, m_completions.end)); m_matching_candidates.push_back(buffer.string(m_completions.begin, m_completions.end));
return true; return true;
@ -814,6 +824,8 @@ private:
void menu_show() void menu_show()
{ {
if (m_context.has_ui())
return;
DisplayCoord menu_pos = m_context.window().display_position(m_completions.begin); DisplayCoord menu_pos = m_context.window().display_position(m_completions.begin);
m_context.ui().menu_show(m_matching_candidates, menu_pos, m_context.ui().menu_show(m_matching_candidates, menu_pos,
get_color("MenuForeground"), get_color("MenuForeground"),
@ -850,8 +862,8 @@ private:
class Insert : public InputMode class Insert : public InputMode
{ {
public: public:
Insert(Client& client, InsertMode mode) Insert(InputHandler& input_handler, InsertMode mode)
: InputMode(client), : InputMode(input_handler),
m_insert_mode(mode), m_insert_mode(mode),
m_edition(context().editor()), m_edition(context().editor()),
m_completer(context()), m_completer(context()),
@ -1088,31 +1100,31 @@ private:
void InputMode::reset_normal_mode() void InputMode::reset_normal_mode()
{ {
m_client.reset_normal_mode(); m_input_handler.reset_normal_mode();
} }
Client::Client(std::unique_ptr<UserInterface>&& ui, Editor& editor, String name) InputHandler::InputHandler(Editor& editor)
: m_ui(std::move(ui)), m_context(*this, editor), m_mode(new InputModes::Normal(*this)), m_name(name) : m_mode(new InputModes::Normal(*this)),
m_context(*this, editor)
{ {
} }
Client::~Client() InputHandler::~InputHandler()
{ {}
}
void Client::change_input_mode(InputMode* new_mode) void InputHandler::change_input_mode(InputMode* new_mode)
{ {
m_mode->on_replaced(); m_mode->on_replaced();
m_mode_trash.emplace_back(std::move(m_mode)); m_mode_trash.emplace_back(std::move(m_mode));
m_mode.reset(new_mode); m_mode.reset(new_mode);
} }
void Client::insert(InsertMode mode) void InputHandler::insert(InsertMode mode)
{ {
change_input_mode(new InputModes::Insert(*this, mode)); change_input_mode(new InputModes::Insert(*this, mode));
} }
void Client::repeat_last_insert() void InputHandler::repeat_last_insert()
{ {
if (m_last_insert.second.empty()) if (m_last_insert.second.empty())
return; return;
@ -1127,27 +1139,27 @@ void Client::repeat_last_insert()
kak_assert(dynamic_cast<InputModes::Normal*>(m_mode.get()) != nullptr); kak_assert(dynamic_cast<InputModes::Normal*>(m_mode.get()) != nullptr);
} }
void Client::prompt(const String& prompt, ColorPair prompt_colors, void InputHandler::prompt(const String& prompt, ColorPair prompt_colors,
Completer completer, PromptCallback callback) Completer completer, PromptCallback callback)
{ {
change_input_mode(new InputModes::Prompt(*this, prompt, prompt_colors, change_input_mode(new InputModes::Prompt(*this, prompt, prompt_colors,
completer, callback)); completer, callback));
} }
void Client::set_prompt_colors(ColorPair prompt_colors) void InputHandler::set_prompt_colors(ColorPair prompt_colors)
{ {
InputModes::Prompt* prompt = dynamic_cast<InputModes::Prompt*>(m_mode.get()); InputModes::Prompt* prompt = dynamic_cast<InputModes::Prompt*>(m_mode.get());
if (prompt) if (prompt)
prompt->set_prompt_colors(prompt_colors); prompt->set_prompt_colors(prompt_colors);
} }
void Client::menu(memoryview<String> choices, void InputHandler::menu(memoryview<String> choices,
MenuCallback callback) MenuCallback callback)
{ {
change_input_mode(new InputModes::Menu(*this, choices, callback)); change_input_mode(new InputModes::Menu(*this, choices, callback));
} }
void Client::on_next_key(KeyCallback callback) void InputHandler::on_next_key(KeyCallback callback)
{ {
change_input_mode(new InputModes::NextKey(*this, callback)); change_input_mode(new InputModes::NextKey(*this, callback));
} }
@ -1157,17 +1169,7 @@ static bool is_valid(Key key)
return key != Key::Invalid and key.key <= 0x10FFFF; return key != Key::Invalid and key.key <= 0x10FFFF;
} }
void Client::handle_available_input() void InputHandler::handle_key(Key key)
{
while (m_ui->is_key_available())
{
handle_key(m_ui->get_key());
m_mode_trash.clear();
}
m_context.window().forget_timestamp();
}
void Client::handle_key(Key key)
{ {
if (is_valid(key)) if (is_valid(key))
{ {
@ -1189,29 +1191,59 @@ void Client::handle_key(Key key)
} }
} }
void Client::start_recording(char reg) void InputHandler::start_recording(char reg)
{ {
kak_assert(m_recording_reg == 0); kak_assert(m_recording_reg == 0);
m_recorded_keys = ""; m_recorded_keys = "";
m_recording_reg = reg; m_recording_reg = reg;
} }
bool Client::is_recording() const bool InputHandler::is_recording() const
{ {
return m_recording_reg != 0; return m_recording_reg != 0;
} }
void Client::stop_recording() void InputHandler::stop_recording()
{ {
kak_assert(m_recording_reg != 0); kak_assert(m_recording_reg != 0);
RegisterManager::instance()[m_recording_reg] = memoryview<String>(m_recorded_keys); RegisterManager::instance()[m_recording_reg] = memoryview<String>(m_recorded_keys);
m_recording_reg = 0; m_recording_reg = 0;
} }
void InputHandler::reset_normal_mode()
{
change_input_mode(new InputModes::Normal(*this));
}
void InputHandler::clear_mode_trash()
{
m_mode_trash.clear();
}
Client::Client(std::unique_ptr<UserInterface>&& ui, Editor& editor, String name)
: m_input_handler(editor), m_ui(std::move(ui)), m_name(name)
{
context().set_client(*this);
}
Client::~Client()
{
}
void Client::handle_available_input()
{
while (m_ui->is_key_available())
{
m_input_handler.handle_key(m_ui->get_key());
m_input_handler.clear_mode_trash();
}
context().window().forget_timestamp();
}
void Client::print_status(DisplayLine status_line) void Client::print_status(DisplayLine status_line)
{ {
m_status_line = std::move(status_line); m_status_line = std::move(status_line);
m_context.window().forget_timestamp(); context().window().forget_timestamp();
} }
DisplayLine Client::generate_mode_line() const DisplayLine Client::generate_mode_line() const
@ -1224,35 +1256,30 @@ DisplayLine Client::generate_mode_line() const
<< " " << (int)pos.line+1 << ":" << (int)col+1; << " " << (int)pos.line+1 << ":" << (int)col+1;
if (context().buffer().is_modified()) if (context().buffer().is_modified())
oss << " [+]"; oss << " [+]";
if (is_recording()) if (m_input_handler.is_recording())
oss << " [recording (" << m_recording_reg << ")]"; oss << " [recording (" << m_input_handler.recording_reg() << ")]";
if (context().buffer().flags() & Buffer::Flags::New) if (context().buffer().flags() & Buffer::Flags::New)
oss << " [new file]"; oss << " [new file]";
oss << " [" << m_mode->description() << "]" << " - " << name() oss << " [" << m_input_handler.mode().description() << "]" << " - " << name()
<< "@[" << Server::instance().session() << "]"; << "@[" << Server::instance().session() << "]";
return { oss.str(), get_color("StatusLine") }; return { oss.str(), get_color("StatusLine") };
} }
void Client::redraw_ifn() void Client::redraw_ifn()
{ {
if (m_context.window().timestamp() != m_context.buffer().timestamp()) if (context().window().timestamp() != context().buffer().timestamp())
{ {
DisplayCoord dimensions = m_context.ui().dimensions(); DisplayCoord dimensions = context().ui().dimensions();
if (dimensions == DisplayCoord{0,0}) if (dimensions == DisplayCoord{0,0})
return; return;
m_context.window().set_dimensions(dimensions); context().window().set_dimensions(dimensions);
m_context.window().update_display_buffer();; context().window().update_display_buffer();;
m_context.ui().draw(m_context.window().display_buffer(), context().ui().draw(context().window().display_buffer(),
m_status_line, generate_mode_line()); m_status_line, generate_mode_line());
} }
} }
void Client::reset_normal_mode()
{
change_input_mode(new InputModes::Normal(*this));
}
static void reload_buffer(Context& context, const String& filename) static void reload_buffer(Context& context, const String& filename)
{ {
DisplayCoord view_pos = context.window().position(); DisplayCoord view_pos = context.window().position();
@ -1270,7 +1297,7 @@ static void reload_buffer(Context& context, const String& filename)
void Client::check_buffer_fs_timestamp() void Client::check_buffer_fs_timestamp()
{ {
Buffer& buffer = m_context.buffer(); Buffer& buffer = context().buffer();
auto reload = context().options()["autoreload"].get<YesNoAsk>(); auto reload = context().options()["autoreload"].get<YesNoAsk>();
if (not (buffer.flags() & Buffer::Flags::File) or reload == No) if (not (buffer.flags() & Buffer::Flags::File) or reload == No)
return; return;
@ -1282,7 +1309,7 @@ void Client::check_buffer_fs_timestamp()
if (reload == Ask) if (reload == Ask)
{ {
print_status({"'" + buffer.display_name() + "' was modified externally, press r or y to reload, k or n to keep", get_color("Prompt")}); print_status({"'" + buffer.display_name() + "' was modified externally, press r or y to reload, k or n to keep", get_color("Prompt")});
on_next_key([this, ts, filename](Key key, Context& context) { m_input_handler.on_next_key([this, ts, filename](Key key, Context& context) {
Buffer* buf = BufferManager::instance().get_buffer_ifp(filename); Buffer* buf = BufferManager::instance().get_buffer_ifp(filename);
// buffer got deleted while waiting for the key, do nothing // buffer got deleted while waiting for the key, do nothing
if (not buf) if (not buf)

View File

@ -35,11 +35,11 @@ using KeyCallback = std::function<void (Key, Context&)>;
class InputMode; class InputMode;
enum class InsertMode : unsigned; enum class InsertMode : unsigned;
class Client : public SafeCountable class InputHandler : public SafeCountable
{ {
public: public:
Client(std::unique_ptr<UserInterface>&& ui, Editor& editor, String name); InputHandler(Editor& editor);
~Client(); ~InputHandler();
// switch to insert mode // switch to insert mode
void insert(InsertMode mode); void insert(InsertMode mode);
@ -65,18 +65,46 @@ public:
// if callback does not change the mode itself // if callback does not change the mode itself
void on_next_key(KeyCallback callback); void on_next_key(KeyCallback callback);
// handle all the keys currently available in the user interface
void handle_available_input();
// process the given key // process the given key
void handle_key(Key key); void handle_key(Key key);
void start_recording(char reg); void start_recording(char reg);
bool is_recording() const; bool is_recording() const;
void stop_recording(); void stop_recording();
char recording_reg() const { return m_recording_reg; }
void reset_normal_mode();
Context& context() { return m_context; } Context& context() { return m_context; }
const Context& context() const { return m_context; } const Context& context() const { return m_context; }
const InputMode& mode() const { return *m_mode; }
void clear_mode_trash();
private:
Context m_context;
friend class InputMode;
std::unique_ptr<InputMode> m_mode;
std::vector<std::unique_ptr<InputMode>> m_mode_trash;
void change_input_mode(InputMode* new_mode);
using Insertion = std::pair<InsertMode, std::vector<Key>>;
Insertion m_last_insert = {InsertMode::Insert, {}};
char m_recording_reg = 0;
String m_recorded_keys;
};
class Client : public SafeCountable
{
public:
Client(std::unique_ptr<UserInterface>&& ui, Editor& editor, String name);
~Client();
// handle all the keys currently available in the user interface
void handle_available_input();
const String& name() const { return m_name; } const String& name() const { return m_name; }
void set_name(String name) { m_name = std::move(name); } void set_name(String name) { m_name = std::move(name); }
@ -88,26 +116,18 @@ public:
void check_buffer_fs_timestamp(); void check_buffer_fs_timestamp();
void reset_normal_mode(); Context& context() { return m_input_handler.context(); }
const Context& context() const { return m_input_handler.context(); }
private: private:
void change_input_mode(InputMode* new_mode); InputHandler m_input_handler;
DisplayLine generate_mode_line() const; DisplayLine generate_mode_line() const;
Context m_context;
friend class InputMode;
std::unique_ptr<UserInterface> m_ui; std::unique_ptr<UserInterface> m_ui;
std::unique_ptr<InputMode> m_mode;
std::vector<std::unique_ptr<InputMode>> m_mode_trash;
String m_name; String m_name;
DisplayLine m_status_line; DisplayLine m_status_line;
using Insertion = std::pair<InsertMode, std::vector<Key>>;
Insertion m_last_insert = {InsertMode::Insert, {}};
char m_recording_reg = 0;
String m_recorded_keys;
}; };
} }

View File

@ -1,7 +1,6 @@
#ifndef client_manager_hh_INCLUDED #ifndef client_manager_hh_INCLUDED
#define client_manager_hh_INCLUDED #define client_manager_hh_INCLUDED
#include "context.hh"
#include "client.hh" #include "client.hh"
namespace Kakoune namespace Kakoune

View File

@ -642,7 +642,7 @@ void menu(CommandParameters params, Context& context)
select_cmds.push_back(parser[i+2]); select_cmds.push_back(parser[i+2]);
} }
context.client().menu(choices, context.input_handler().menu(choices,
[=](int choice, MenuEvent event, Context& context) { [=](int choice, MenuEvent event, Context& context) {
if (event == MenuEvent::Validate and choice >= 0 and choice < commands.size()) if (event == MenuEvent::Validate and choice >= 0 and choice < commands.size())
CommandManager::instance().execute(commands[choice], context); CommandManager::instance().execute(commands[choice], context);
@ -801,7 +801,7 @@ void exec_keys(const KeyList& keys, Context& context)
scoped_edition edition(context.editor()); scoped_edition edition(context.editor());
for (auto& key : keys) for (auto& key : keys)
context.client().handle_key(key); context.input_handler().handle_key(key);
} }
void register_commands() void register_commands()

View File

@ -9,11 +9,8 @@ namespace Kakoune
Context::Context() = default; Context::Context() = default;
Context::Context(Editor& editor) Context::Context(InputHandler& input_handler, Editor& editor)
: m_editor(&editor) {} : m_input_handler(&input_handler), m_editor(&editor) {}
Context::Context(Client& client, Editor& editor)
: m_client(&client), m_editor(&editor) {}
Context::~Context() = default; Context::~Context() = default;
@ -43,10 +40,17 @@ bool Context::has_window() const
return (bool)m_editor and dynamic_cast<Window*>(m_editor.get()); return (bool)m_editor and dynamic_cast<Window*>(m_editor.get());
} }
InputHandler& Context::input_handler() const
{
if (not has_input_handler())
throw runtime_error("no input handler in context");
return *m_input_handler;
}
Client& Context::client() const Client& Context::client() const
{ {
if (not has_client()) if (not has_client())
throw runtime_error("no input handler in context"); throw runtime_error("no client in context");
return *m_client; return *m_client;
} }
@ -54,7 +58,7 @@ UserInterface& Context::ui() const
{ {
if (not has_ui()) if (not has_ui())
throw runtime_error("no user interface in context"); throw runtime_error("no user interface in context");
return m_client->ui(); return client().ui();
} }
OptionManager& Context::options() const OptionManager& Context::options() const
@ -84,6 +88,12 @@ KeymapManager& Context::keymaps() const
return GlobalKeymaps::instance(); return GlobalKeymaps::instance();
} }
void Context::set_client(Client& client)
{
kak_assert(not has_client());
m_client.reset(&client);
}
void Context::print_status(DisplayLine status) const void Context::print_status(DisplayLine status) const
{ {
if (has_client()) if (has_client())
@ -162,8 +172,8 @@ void Context::change_editor(Editor& editor)
window().set_dimensions(ui().dimensions()); window().set_dimensions(ui().dimensions());
window().hooks().run_hook("WinDisplay", buffer().name(), *this); window().hooks().run_hook("WinDisplay", buffer().name(), *this);
} }
if (has_client()) if (has_input_handler())
client().reset_normal_mode(); input_handler().reset_normal_mode();
} }
} }

View File

@ -10,6 +10,7 @@ class Editor;
class Window; class Window;
class Buffer; class Buffer;
class Client; class Client;
class InputHandler;
class UserInterface; class UserInterface;
class DisplayLine; class DisplayLine;
class KeymapManager; class KeymapManager;
@ -24,8 +25,7 @@ class Context
{ {
public: public:
Context(); Context();
explicit Context(Editor& editor); Context(InputHandler& input_handler, Editor& editor);
Context(Client& client, Editor& editor);
~Context(); ~Context();
Context(const Context&) = delete; Context(const Context&) = delete;
@ -43,11 +43,16 @@ public:
Client& client() const; Client& client() const;
bool has_client() const { return (bool)m_client; } bool has_client() const { return (bool)m_client; }
InputHandler& input_handler() const;
bool has_input_handler() const { return (bool)m_input_handler; }
UserInterface& ui() const; UserInterface& ui() const;
bool has_ui() const { return (bool)m_client; } bool has_ui() const { return has_client(); }
void change_editor(Editor& editor); void change_editor(Editor& editor);
void set_client(Client& client);
OptionManager& options() const; OptionManager& options() const;
HookManager& hooks() const; HookManager& hooks() const;
KeymapManager& keymaps() const; KeymapManager& keymaps() const;
@ -60,8 +65,9 @@ public:
void forget_jumps_to_buffer(Buffer& buffer); void forget_jumps_to_buffer(Buffer& buffer);
private: private:
safe_ptr<Editor> m_editor; safe_ptr<Editor> m_editor;
safe_ptr<Client> m_client; safe_ptr<InputHandler> m_input_handler;
safe_ptr<Client> m_client;
using JumpList = std::vector<DynamicSelectionList>; using JumpList = std::vector<DynamicSelectionList>;
JumpList m_jump_list; JumpList m_jump_list;

View File

@ -25,12 +25,12 @@ using namespace std::placeholders;
template<InsertMode mode> template<InsertMode mode>
void insert(Context& context, int) void insert(Context& context, int)
{ {
context.client().insert(mode); context.input_handler().insert(mode);
} }
void repeat_insert(Context& context, int) void repeat_insert(Context& context, int)
{ {
context.client().repeat_last_insert(); context.input_handler().repeat_last_insert();
} }
bool show_auto_info_ifn(const String& title, const String& info, bool show_auto_info_ifn(const String& title, const String& info,
@ -50,7 +50,7 @@ void on_next_key_with_autoinfo(const Context& context, Cmd cmd,
const String& title, const String& info) const String& title, const String& info)
{ {
const bool hide = show_auto_info_ifn(title, info, context); const bool hide = show_auto_info_ifn(title, info, context);
context.client().on_next_key([hide,cmd](Key key, Context& context) mutable { context.input_handler().on_next_key([hide,cmd](Key key, Context& context) mutable {
if (hide) if (hide)
context.ui().info_hide(); context.ui().info_hide();
cmd(key, context); cmd(key, context);
@ -247,7 +247,7 @@ void for_each_char(Context& context, int)
void command(Context& context, int) void command(Context& context, int)
{ {
context.client().prompt( context.input_handler().prompt(
":", get_color("Prompt"), ":", get_color("Prompt"),
std::bind(&CommandManager::complete, &CommandManager::instance(), _1, _2, _3, _4), std::bind(&CommandManager::complete, &CommandManager::instance(), _1, _2, _3, _4),
[](const String& cmdline, PromptEvent event, Context& context) { [](const String& cmdline, PromptEvent event, Context& context) {
@ -258,7 +258,7 @@ void command(Context& context, int)
void pipe(Context& context, int) void pipe(Context& context, int)
{ {
context.client().prompt("pipe:", get_color("Prompt"), complete_nothing, context.input_handler().prompt("pipe:", get_color("Prompt"), complete_nothing,
[](const String& cmdline, PromptEvent event, Context& context) [](const String& cmdline, PromptEvent event, Context& context)
{ {
if (event != PromptEvent::Validate) if (event != PromptEvent::Validate)
@ -299,7 +299,7 @@ void search(Context& context, int)
{ {
const char* prompt = direction == Forward ? "search:" : "reverse search:"; const char* prompt = direction == Forward ? "search:" : "reverse search:";
DynamicSelectionList selections{context.buffer(), context.editor().selections()}; DynamicSelectionList selections{context.buffer(), context.editor().selections()};
context.client().prompt(prompt, get_color("Prompt"), complete_nothing, context.input_handler().prompt(prompt, get_color("Prompt"), complete_nothing,
[selections](const String& str, PromptEvent event, Context& context) { [selections](const String& str, PromptEvent event, Context& context) {
try try
{ {
@ -309,7 +309,7 @@ void search(Context& context, int)
return; return;
Regex ex{str}; Regex ex{str};
context.client().set_prompt_colors(get_color("Prompt")); context.input_handler().set_prompt_colors(get_color("Prompt"));
if (event == PromptEvent::Validate) if (event == PromptEvent::Validate)
{ {
if (str.empty()) if (str.empty())
@ -328,7 +328,7 @@ void search(Context& context, int)
if (event == PromptEvent::Validate) if (event == PromptEvent::Validate)
throw runtime_error("regex error: "_str + err.what()); throw runtime_error("regex error: "_str + err.what());
else else
context.client().set_prompt_colors(get_color("Error")); context.input_handler().set_prompt_colors(get_color("Error"));
} }
catch (runtime_error&) catch (runtime_error&)
{ {
@ -449,7 +449,7 @@ void paste(Context& context, int)
template<typename T> template<typename T>
void regex_prompt(Context& context, const String prompt, T on_validate) void regex_prompt(Context& context, const String prompt, T on_validate)
{ {
context.client().prompt(prompt, get_color("Prompt"), complete_nothing, context.input_handler().prompt(prompt, get_color("Prompt"), complete_nothing,
[=](const String& str, PromptEvent event, Context& context) { [=](const String& str, PromptEvent event, Context& context) {
if (event == PromptEvent::Validate) if (event == PromptEvent::Validate)
{ {
@ -465,7 +465,7 @@ void regex_prompt(Context& context, const String prompt, T on_validate)
else if (event == PromptEvent::Change) else if (event == PromptEvent::Change)
{ {
const bool ok = Regex{str, boost::regex_constants::no_except}.status() == 0; const bool ok = Regex{str, boost::regex_constants::no_except}.status() == 0;
context.client().set_prompt_colors(get_color(ok ? "Prompt" : "Error")); context.input_handler().set_prompt_colors(get_color(ok ? "Prompt" : "Error"));
} }
}); });
} }
@ -755,13 +755,13 @@ void select_to_next_char(Context& context, int param)
void start_or_end_macro_recording(Context& context, int) void start_or_end_macro_recording(Context& context, int)
{ {
if (context.client().is_recording()) if (context.input_handler().is_recording())
context.client().stop_recording(); context.input_handler().stop_recording();
else else
on_next_key_with_autoinfo(context, [](Key key, Context& context) { on_next_key_with_autoinfo(context, [](Key key, Context& context) {
if (key.modifiers == Key::Modifiers::None and if (key.modifiers == Key::Modifiers::None and
key.key >= 'a' and key.key <= 'z') key.key >= 'a' and key.key <= 'z')
context.client().start_recording(key.key); context.input_handler().start_recording(key.key);
}, "record macro", "enter macro name "); }, "record macro", "enter macro name ");
} }

View File

@ -4,6 +4,7 @@
#include "context.hh" #include "context.hh"
#include "highlighter.hh" #include "highlighter.hh"
#include "hook_manager.hh" #include "hook_manager.hh"
#include "client.hh"
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
@ -22,8 +23,8 @@ Window::Window(Buffer& buffer)
m_options(buffer.options()), m_options(buffer.options()),
m_keymaps(buffer.keymaps()) m_keymaps(buffer.keymaps())
{ {
Context hook_context{*this}; InputHandler hook_handler{*this};
m_hooks.run_hook("WinCreate", buffer.name(), hook_context); m_hooks.run_hook("WinCreate", buffer.name(), hook_handler.context());
m_options.register_watcher(*this); m_options.register_watcher(*this);
m_builtin_highlighters.append({"tabulations", expand_tabulations}); m_builtin_highlighters.append({"tabulations", expand_tabulations});
@ -36,8 +37,8 @@ Window::Window(Buffer& buffer)
Window::~Window() Window::~Window()
{ {
Context hook_context{*this}; InputHandler hook_handler{*this};
m_hooks.run_hook("WinClose", buffer().name(), hook_context); m_hooks.run_hook("WinClose", buffer().name(), hook_handler.context());
m_options.unregister_watcher(*this); m_options.unregister_watcher(*this);
} }
@ -250,8 +251,8 @@ BufferCoord Window::offset_coord(BufferCoord coord, LineCount offset)
void Window::on_option_changed(const Option& option) void Window::on_option_changed(const Option& option)
{ {
String desc = option.name() + "=" + option.get_as_string(); String desc = option.name() + "=" + option.get_as_string();
Context hook_context{*this}; InputHandler hook_handler{*this};
m_hooks.run_hook("WinSetOption", desc, hook_context); m_hooks.run_hook("WinSetOption", desc, hook_handler.context());
} }
} }