Refactor prompt history handling
Share incremental regex logic, pass the synthetized nature of keys through to input handlers.
This commit is contained in:
parent
ec79864559
commit
e3122ab2c1
|
@ -31,7 +31,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) { RefPtr<InputMode> keep_alive{this}; on_key(key); }
|
void handle_key(Key key, bool synthesized) { RefPtr<InputMode> keep_alive{this}; on_key(key, synthesized); }
|
||||||
virtual void paste(StringView content);
|
virtual void paste(StringView content);
|
||||||
|
|
||||||
virtual void on_enabled() {}
|
virtual void on_enabled() {}
|
||||||
|
@ -57,7 +57,7 @@ public:
|
||||||
Insertion& last_insert() { return m_input_handler.m_last_insert; }
|
Insertion& last_insert() { return m_input_handler.m_last_insert; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void on_key(Key key) = 0;
|
virtual void on_key(Key key, bool synthesized) = 0;
|
||||||
|
|
||||||
void push_mode(InputMode* new_mode)
|
void push_mode(InputMode* new_mode)
|
||||||
{
|
{
|
||||||
|
@ -260,7 +260,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key, bool) override
|
||||||
{
|
{
|
||||||
kak_assert(m_state != State::PopOnEnabled);
|
kak_assert(m_state != State::PopOnEnabled);
|
||||||
ScopedSetBool set_in_on_key{m_in_on_key};
|
ScopedSetBool set_in_on_key{m_in_on_key};
|
||||||
|
@ -646,7 +646,7 @@ public:
|
||||||
context().client().menu_select(0);
|
context().client().menu_select(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key, bool) override
|
||||||
{
|
{
|
||||||
auto match_filter = [this](const DisplayLine& choice) {
|
auto match_filter = [this](const DisplayLine& choice) {
|
||||||
for (auto& atom : choice)
|
for (auto& atom : choice)
|
||||||
|
@ -808,9 +808,11 @@ public:
|
||||||
m_line_editor.reset(std::move(initstr), m_empty_text);
|
m_line_editor.reset(std::move(initstr), m_empty_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key, bool synthesized) override
|
||||||
{
|
{
|
||||||
const String& line = m_line_editor.line();
|
const String& line = m_line_editor.line();
|
||||||
|
if (not synthesized)
|
||||||
|
m_was_interactive = true;
|
||||||
|
|
||||||
auto can_auto_insert_completion = [&] {
|
auto can_auto_insert_completion = [&] {
|
||||||
const bool has_completions = not m_completions.candidates.empty();
|
const bool has_completions = not m_completions.candidates.empty();
|
||||||
|
@ -1064,16 +1066,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool was_interactive()
|
|
||||||
{
|
|
||||||
return m_was_interactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_was_interactive()
|
|
||||||
{
|
|
||||||
m_was_interactive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayLine mode_line() const override
|
DisplayLine mode_line() const override
|
||||||
{
|
{
|
||||||
return { "prompt", context().faces()["StatusLineMode"] };
|
return { "prompt", context().faces()["StatusLineMode"] };
|
||||||
|
@ -1210,7 +1202,7 @@ private:
|
||||||
|
|
||||||
void history_push(StringView entry)
|
void history_push(StringView entry)
|
||||||
{
|
{
|
||||||
if (entry.empty() or not was_interactive() or
|
if (entry.empty() or not m_was_interactive or
|
||||||
(m_flags & PromptFlags::DropHistoryEntriesWithBlankPrefix and
|
(m_flags & PromptFlags::DropHistoryEntriesWithBlankPrefix and
|
||||||
is_horizontal_blank(entry[0_byte])))
|
is_horizontal_blank(entry[0_byte])))
|
||||||
return;
|
return;
|
||||||
|
@ -1227,7 +1219,7 @@ public:
|
||||||
: InputMode(input_handler), m_name{std::move(name)}, m_callback(std::move(callback)), m_keymap_mode(keymap_mode),
|
: InputMode(input_handler), m_name{std::move(name)}, m_callback(std::move(callback)), m_keymap_mode(keymap_mode),
|
||||||
m_idle_timer{Clock::now() + get_idle_timeout(context()), std::move(idle_callback)} {}
|
m_idle_timer{Clock::now() + get_idle_timeout(context()), std::move(idle_callback)} {}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key, bool) override
|
||||||
{
|
{
|
||||||
// maintain hooks disabled in the callback if they were before pop_mode
|
// maintain hooks disabled in the callback if they were before pop_mode
|
||||||
ScopedSetBool disable_hooks(context().hooks_disabled(),
|
ScopedSetBool disable_hooks(context().hooks_disabled(),
|
||||||
|
@ -1306,7 +1298,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_key(Key key) override
|
void on_key(Key key, bool) override
|
||||||
{
|
{
|
||||||
auto& buffer = context().buffer();
|
auto& buffer = context().buffer();
|
||||||
|
|
||||||
|
@ -1697,7 +1689,7 @@ void InputHandler::repeat_last_insert()
|
||||||
// refill last_insert, this is very inefficient, but necessary at the moment
|
// refill last_insert, this is very inefficient, but necessary at the moment
|
||||||
// to properly handle insert completion
|
// to properly handle insert completion
|
||||||
m_last_insert.keys.push_back(key);
|
m_last_insert.keys.push_back(key);
|
||||||
current_mode().handle_key(key);
|
current_mode().handle_key(key, true);
|
||||||
}
|
}
|
||||||
kak_assert(dynamic_cast<InputModes::Normal*>(¤t_mode()) != nullptr);
|
kak_assert(dynamic_cast<InputModes::Normal*>(¤t_mode()) != nullptr);
|
||||||
}
|
}
|
||||||
|
@ -1718,17 +1710,10 @@ void InputHandler::prompt(StringView prompt, String initstr, String emptystr,
|
||||||
|
|
||||||
void InputHandler::set_prompt_face(Face prompt_face)
|
void InputHandler::set_prompt_face(Face prompt_face)
|
||||||
{
|
{
|
||||||
auto* prompt = dynamic_cast<InputModes::Prompt*>(¤t_mode());
|
if (auto* prompt = dynamic_cast<InputModes::Prompt*>(¤t_mode()))
|
||||||
if (prompt)
|
|
||||||
prompt->set_prompt_face(prompt_face);
|
prompt->set_prompt_face(prompt_face);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputHandler::history_enabled() const
|
|
||||||
{
|
|
||||||
auto* prompt = dynamic_cast<InputModes::Prompt*>(¤t_mode());
|
|
||||||
return prompt and prompt->was_interactive();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputHandler::menu(Vector<DisplayLine> choices, MenuCallback callback)
|
void InputHandler::menu(Vector<DisplayLine> choices, MenuCallback callback)
|
||||||
{
|
{
|
||||||
push_mode(new InputModes::Menu(*this, std::move(choices), std::move(callback)));
|
push_mode(new InputModes::Menu(*this, std::move(choices), std::move(callback)));
|
||||||
|
@ -1779,18 +1764,12 @@ void InputHandler::handle_key(Key key)
|
||||||
|
|
||||||
const bool was_recording = is_recording();
|
const bool was_recording = is_recording();
|
||||||
++m_handle_key_level;
|
++m_handle_key_level;
|
||||||
auto dec = on_scope_end([this]{
|
auto dec = on_scope_end([this]{ --m_handle_key_level;} );
|
||||||
--m_handle_key_level;
|
|
||||||
if (m_handle_key_level == 0)
|
|
||||||
for (auto& mode : m_mode_stack)
|
|
||||||
if (auto* prompt = dynamic_cast<InputModes::Prompt*>(&*mode))
|
|
||||||
prompt->set_was_interactive();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto process_key = [&](Key key) {
|
auto process_key = [&](Key key, bool synthesized) {
|
||||||
if (m_last_insert.recording)
|
if (m_last_insert.recording)
|
||||||
m_last_insert.keys.push_back(key);
|
m_last_insert.keys.push_back(key);
|
||||||
current_mode().handle_key(key);
|
current_mode().handle_key(key, synthesized);
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto keymap_mode = current_mode().keymap_mode();
|
const auto keymap_mode = current_mode().keymap_mode();
|
||||||
|
@ -1800,10 +1779,10 @@ void InputHandler::handle_key(Key key)
|
||||||
ScopedSetBool noninteractive{context().noninteractive()};
|
ScopedSetBool noninteractive{context().noninteractive()};
|
||||||
|
|
||||||
for (auto& k : keymaps.get_mapping_keys(key, keymap_mode))
|
for (auto& k : keymaps.get_mapping_keys(key, keymap_mode))
|
||||||
process_key(k);
|
process_key(k, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
process_key(key);
|
process_key(key, m_handle_key_level > 1);
|
||||||
|
|
||||||
// do not record the key that made us enter or leave recording mode,
|
// do not record the key that made us enter or leave recording mode,
|
||||||
// and the ones that are triggered recursively by previous keys.
|
// and the ones that are triggered recursively by previous keys.
|
||||||
|
|
|
@ -834,7 +834,8 @@ void regex_prompt(Context& context, String prompt, char reg, T func)
|
||||||
[&](auto&& m) { candidates.push_back(m.candidate().str()); return true; });
|
[&](auto&& m) { candidates.push_back(m.candidate().str()); return true; });
|
||||||
return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) };
|
return {(int)(word.begin() - regex.begin()), pos, std::move(candidates) };
|
||||||
},
|
},
|
||||||
[=, func=T(std::move(func)), selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
|
[=, func=T(std::move(func)), saved_reg=RegisterManager::instance()[reg].save(context),
|
||||||
|
selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
|
||||||
(StringView str, PromptEvent event, Context& context) mutable {
|
(StringView str, PromptEvent event, Context& context) mutable {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -857,6 +858,11 @@ void regex_prompt(Context& context, String prompt, char reg, T func)
|
||||||
|
|
||||||
if (event == PromptEvent::Validate)
|
if (event == PromptEvent::Validate)
|
||||||
context.push_jump();
|
context.push_jump();
|
||||||
|
else
|
||||||
|
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||||
|
|
||||||
|
if (not str.empty() and event == PromptEvent::Change) // Ensure search register based highlighters work incrementally
|
||||||
|
RegisterManager::instance()[reg].set(context, str.str());
|
||||||
|
|
||||||
if (not str.empty() or event == PromptEvent::Validate)
|
if (not str.empty() or event == PromptEvent::Validate)
|
||||||
func(Regex{str.empty() ? default_regex : str, direction_flags(mode)}, event, context);
|
func(Regex{str.empty() ? default_regex : str, direction_flags(mode)}, event, context);
|
||||||
|
@ -930,13 +936,8 @@ void search(Context& context, NormalParams params)
|
||||||
const int count = params.count;
|
const int count = params.count;
|
||||||
|
|
||||||
regex_prompt<regex_mode>(context, prompt.str(), reg,
|
regex_prompt<regex_mode>(context, prompt.str(), reg,
|
||||||
[reg, count, saved_reg = RegisterManager::instance()[reg].save(context)]
|
[reg, count]
|
||||||
(const Regex& regex, PromptEvent event, Context& context) {
|
(const Regex& regex, PromptEvent event, Context& context) {
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
|
||||||
if (event == PromptEvent::Abort)
|
|
||||||
return;
|
|
||||||
RegisterManager::instance()[reg].set(context, regex.str());
|
|
||||||
|
|
||||||
if (regex.empty() or regex.str().empty())
|
if (regex.empty() or regex.str().empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1017,14 +1018,8 @@ void select_regex(Context& context, NormalParams params)
|
||||||
auto prompt = capture ? format("select (capture {}):", capture) : "select:"_str;
|
auto prompt = capture ? format("select (capture {}):", capture) : "select:"_str;
|
||||||
|
|
||||||
regex_prompt(context, std::move(prompt), reg,
|
regex_prompt(context, std::move(prompt), reg,
|
||||||
[reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
|
[reg, capture]
|
||||||
(Regex ex, PromptEvent event, Context& context) {
|
(Regex ex, PromptEvent event, Context& context) {
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
|
||||||
if (event == PromptEvent::Abort)
|
|
||||||
return;
|
|
||||||
if (context.input_handler().history_enabled())
|
|
||||||
RegisterManager::instance()[reg].set(context, ex.str());
|
|
||||||
|
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
auto& buffer = selections.buffer();
|
auto& buffer = selections.buffer();
|
||||||
if (not ex.empty() and not ex.str().empty())
|
if (not ex.empty() and not ex.str().empty())
|
||||||
|
@ -1039,14 +1034,8 @@ void split_regex(Context& context, NormalParams params)
|
||||||
auto prompt = capture ? format("split (on capture {}):", (int)capture) : "split:"_str;
|
auto prompt = capture ? format("split (on capture {}):", (int)capture) : "split:"_str;
|
||||||
|
|
||||||
regex_prompt(context, std::move(prompt), reg,
|
regex_prompt(context, std::move(prompt), reg,
|
||||||
[reg, capture, saved_reg = RegisterManager::instance()[reg].save(context)]
|
[reg, capture]
|
||||||
(Regex ex, PromptEvent event, Context& context) {
|
(Regex ex, PromptEvent event, Context& context) {
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
|
||||||
if (event == PromptEvent::Abort)
|
|
||||||
return;
|
|
||||||
if (context.input_handler().history_enabled())
|
|
||||||
RegisterManager::instance()[reg].set(context, ex.str());
|
|
||||||
|
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
auto& buffer = selections.buffer();
|
auto& buffer = selections.buffer();
|
||||||
if (not ex.empty() and not ex.str().empty())
|
if (not ex.empty() and not ex.str().empty())
|
||||||
|
@ -1142,15 +1131,8 @@ void keep(Context& context, NormalParams params)
|
||||||
const char reg = to_lower(params.reg ? params.reg : '/');
|
const char reg = to_lower(params.reg ? params.reg : '/');
|
||||||
|
|
||||||
regex_prompt(context, prompt.str(), reg,
|
regex_prompt(context, prompt.str(), reg,
|
||||||
[reg, saved_reg = RegisterManager::instance()[reg].save(context),
|
[reg, selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
|
||||||
selection_edition=std::make_shared<ScopedSelectionEdition>(context)]
|
|
||||||
(const Regex& regex, PromptEvent event, Context& context) {
|
(const Regex& regex, PromptEvent event, Context& context) {
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
|
||||||
if (event == PromptEvent::Abort)
|
|
||||||
return;
|
|
||||||
if (context.input_handler().history_enabled())
|
|
||||||
RegisterManager::instance()[reg].set(context, regex.str());
|
|
||||||
|
|
||||||
if (regex.empty() or regex.str().empty())
|
if (regex.empty() or regex.str().empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user