Fix input mode keep alive handling, use a refcount for input modes

Fixes #528
This commit is contained in:
Maxime Coste 2015-12-12 23:51:51 +00:00
parent 80b1d88bb0
commit 515231e824
3 changed files with 38 additions and 28 deletions

View File

@ -17,7 +17,7 @@
namespace Kakoune namespace Kakoune
{ {
class InputMode class InputMode : public RefCountable
{ {
public: public:
InputMode(InputHandler& input_handler) : m_input_handler(input_handler) {} InputMode(InputHandler& input_handler) : m_input_handler(input_handler) {}
@ -25,7 +25,7 @@ public:
InputMode(const InputMode&) = delete; InputMode(const InputMode&) = delete;
InputMode& operator=(const InputMode&) = delete; InputMode& operator=(const InputMode&) = delete;
void handle_key(Key key) { on_key(key, {}); } void handle_key(Key key) { RefPtr<InputMode> keep_alive{this}; on_key(key); }
virtual void on_enabled() {} virtual void on_enabled() {}
virtual void on_disabled() {} virtual void on_disabled() {}
@ -39,21 +39,16 @@ public:
Insertion& last_insert() { return m_input_handler.m_last_insert; } Insertion& last_insert() { return m_input_handler.m_last_insert; }
protected: protected:
// KeepAlive is usedto make sure the current InputMode virtual void on_key(Key key) = 0;
// stays alive until the end of the on_key method
struct KeepAlive { std::shared_ptr<InputMode> ptr; };
virtual void on_key(Key key, KeepAlive keep_alive) = 0;
void push_mode(InputMode* new_mode) void push_mode(InputMode* new_mode)
{ {
m_input_handler.push_mode(new_mode); m_input_handler.push_mode(new_mode);
} }
void pop_mode(KeepAlive& keep_alive) void pop_mode()
{ {
kak_assert(not keep_alive.ptr); m_input_handler.pop_mode(this);
keep_alive.ptr = m_input_handler.pop_mode(this);
} }
private: private:
InputHandler& m_input_handler; InputHandler& m_input_handler;
@ -195,7 +190,7 @@ public:
context().hooks().run_hook("NormalEnd", "", context()); context().hooks().run_hook("NormalEnd", "", context());
} }
void on_key(Key key, KeepAlive keep_alive) override void on_key(Key key) override
{ {
bool do_restore_hooks = false; bool do_restore_hooks = false;
auto restore_hooks = on_scope_end([&, this]{ auto restore_hooks = on_scope_end([&, this]{
@ -246,7 +241,7 @@ public:
else else
{ {
if (m_single_command) if (m_single_command)
pop_mode(keep_alive); pop_mode();
context().print_status({}); context().print_status({});
if (context().has_ui()) if (context().has_ui())
@ -508,7 +503,7 @@ public:
context().ui().menu_select(0); context().ui().menu_select(0);
} }
void on_key(Key key, KeepAlive keep_alive) override void on_key(Key key) override
{ {
auto match_filter = [this](const DisplayLine& choice) { auto match_filter = [this](const DisplayLine& choice) {
for (auto& atom : choice) for (auto& atom : choice)
@ -525,7 +520,7 @@ public:
if (context().has_ui()) if (context().has_ui())
context().ui().menu_hide(); context().ui().menu_hide();
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
pop_mode(keep_alive); pop_mode();
int selected = m_selected - m_choices.begin(); int selected = m_selected - m_choices.begin();
m_callback(selected, MenuEvent::Validate, context()); m_callback(selected, MenuEvent::Validate, context());
return; return;
@ -543,7 +538,7 @@ public:
{ {
if (context().has_ui()) if (context().has_ui())
context().ui().menu_hide(); context().ui().menu_hide();
pop_mode(keep_alive); pop_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());
} }
@ -665,7 +660,7 @@ public:
display(); display();
} }
void on_key(Key key, KeepAlive keep_alive) override void on_key(Key key) override
{ {
History& history = ms_history[m_prompt]; History& history = ms_history[m_prompt];
const String& line = m_line_editor.line(); const String& line = m_line_editor.line();
@ -678,7 +673,7 @@ public:
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
if (context().has_ui()) if (context().has_ui())
context().ui().menu_hide(); context().ui().menu_hide();
pop_mode(keep_alive); pop_mode();
// call callback after pop_mode so that callback // call callback after pop_mode so that callback
// may change the mode // may change the mode
m_callback(line, PromptEvent::Validate, context()); m_callback(line, PromptEvent::Validate, context());
@ -691,7 +686,7 @@ public:
context().print_status(DisplayLine{}); context().print_status(DisplayLine{});
if (context().has_ui()) if (context().has_ui())
context().ui().menu_hide(); context().ui().menu_hide();
pop_mode(keep_alive); pop_mode();
m_callback(line, PromptEvent::Abort, context()); m_callback(line, PromptEvent::Abort, context());
return; return;
} }
@ -943,9 +938,9 @@ public:
NextKey(InputHandler& input_handler, KeymapMode keymap_mode, KeyCallback callback) NextKey(InputHandler& input_handler, KeymapMode keymap_mode, KeyCallback callback)
: InputMode(input_handler), m_keymap_mode(keymap_mode), m_callback(std::move(callback)) {} : InputMode(input_handler), m_keymap_mode(keymap_mode), m_callback(std::move(callback)) {}
void on_key(Key key, KeepAlive keep_alive) override void on_key(Key key) override
{ {
pop_mode(keep_alive); pop_mode();
m_callback(key, context()); m_callback(key, context());
} }
@ -1013,7 +1008,7 @@ public:
m_idle_timer.set_next_date(TimePoint::max()); m_idle_timer.set_next_date(TimePoint::max());
} }
void on_key(Key key, KeepAlive keep_alive) override void on_key(Key key) override
{ {
auto& buffer = context().buffer(); auto& buffer = context().buffer();
last_insert().keys.push_back(key); last_insert().keys.push_back(key);
@ -1028,7 +1023,7 @@ public:
context().hooks().run_hook("InsertEnd", "", context()); context().hooks().run_hook("InsertEnd", "", context());
m_completer.reset(); m_completer.reset();
pop_mode(keep_alive); pop_mode();
} }
else if (key == Key::Backspace) else if (key == Key::Backspace)
{ {
@ -1296,16 +1291,14 @@ void InputHandler::push_mode(InputMode* new_mode)
new_mode->on_enabled(); new_mode->on_enabled();
} }
std::unique_ptr<InputMode> InputHandler::pop_mode(InputMode* mode) void InputHandler::pop_mode(InputMode* mode)
{ {
kak_assert(m_mode_stack.back() == mode); kak_assert(m_mode_stack.back().get() == mode);
kak_assert(m_mode_stack.size() > 1); kak_assert(m_mode_stack.size() > 1);
current_mode().on_disabled(); current_mode().on_disabled();
std::unique_ptr<InputMode> res = std::move(m_mode_stack.back());
m_mode_stack.pop_back(); m_mode_stack.pop_back();
current_mode().on_enabled(); current_mode().on_enabled();
return res;
} }
void InputHandler::reset_normal_mode() void InputHandler::reset_normal_mode()

View File

@ -87,12 +87,12 @@ private:
Context m_context; Context m_context;
friend class InputMode; friend class InputMode;
Vector<std::unique_ptr<InputMode>> m_mode_stack; Vector<RefPtr<InputMode>> m_mode_stack;
InputMode& current_mode() const { return *m_mode_stack.back(); } InputMode& current_mode() const { return *m_mode_stack.back(); }
void push_mode(InputMode* new_mode); void push_mode(InputMode* new_mode);
std::unique_ptr<InputMode> pop_mode(InputMode* current_mode); void pop_mode(InputMode* current_mode);
struct Insertion{ InsertMode mode; Vector<Key> keys; bool disable_hooks; }; struct Insertion{ InsertMode mode; Vector<Key> keys; bool disable_hooks; };
Insertion m_last_insert = { InsertMode::Insert, {}, false }; Insertion m_last_insert = { InsertMode::Insert, {}, false };

View File

@ -103,6 +103,23 @@ private:
} }
}; };
struct RefCountable
{
int refcount = 0;
virtual ~RefCountable() = default;
friend void inc_ref_count(RefCountable* r, void*)
{
++r->refcount;
}
friend void dec_ref_count(RefCountable* r, void*)
{
if (--r->refcount == 0)
delete r;
}
};
} }
#endif // ref_ptr_hh_INCLUDED #endif // ref_ptr_hh_INCLUDED