Disable history only for prompts that are never shown in the UI
My terminal allows to map <c-[> and <esc> independently. I like to use <c-[> as escape key so I have this mapping: map global prompt <c-[> <esc> Unfortunately, this is not equivalent to <esc>. Since mappings are run with history disabled, <c-[> will not add the command to the prompt history. So disabling command history inside mappings is wrong in case the command prompt was created before mapping execution. The behavior should be: "a prompt that is both created and closed inside a noninteractive context does not add to prompt history", where "noninteractive" means inside a mapping, hook, command, execute-keys or evaluate-commands. Implement this behavior, it should better meet user expectations. Scripts can always use "set-register" to add to history. Here are my test cases: 1. Basic regression test (needs above mapping): :nop should be added to history<c-[> --- 2. Create the prompt in a noninteractive context: :exec %{:} now we're back in the interactive context, so we can type: nop should be added to history<ret> --- 3. To check if it works for nested prompts, first set up this mapping. map global prompt <c-j> '<a-semicolon>:nop should NOT be added to history<ret>' map global prompt <c-h> '<a-semicolon>:nop should be added to history first' Then type :nop should be added to history second<c-j><c-h><ret><ret> the inner command run by <c-j> should not be added to history because it only existed in a noninteractive context. --- See also the discussion https://github.com/mawww/kakoune/pull/4692 We could automate the tests if we had a test setup that allowed feeding interactive key input into Kakoune instead of using "execute-commands". Some projects use tmux, or maybe we can mock the terminal.
This commit is contained in:
parent
6563b82092
commit
163eb6dbc6
|
@ -788,6 +788,7 @@ public:
|
||||||
m_prompt(prompt.str()), m_prompt_face(face),
|
m_prompt(prompt.str()), m_prompt_face(face),
|
||||||
m_empty_text{std::move(emptystr)},
|
m_empty_text{std::move(emptystr)},
|
||||||
m_line_editor{context().faces()}, m_flags(flags),
|
m_line_editor{context().faces()}, m_flags(flags),
|
||||||
|
m_was_interactive{not context().noninteractive()},
|
||||||
m_history{RegisterManager::instance()[history_register]},
|
m_history{RegisterManager::instance()[history_register]},
|
||||||
m_current_history{-1},
|
m_current_history{-1},
|
||||||
m_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Prompt},
|
m_auto_complete{context().options()["autocomplete"].get<AutoComplete>() & AutoComplete::Prompt},
|
||||||
|
@ -1062,6 +1063,16 @@ 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"] };
|
||||||
|
@ -1189,6 +1200,7 @@ private:
|
||||||
LineEditor m_line_editor;
|
LineEditor m_line_editor;
|
||||||
bool m_line_changed = false;
|
bool m_line_changed = false;
|
||||||
PromptFlags m_flags;
|
PromptFlags m_flags;
|
||||||
|
bool m_was_interactive;
|
||||||
Register& m_history;
|
Register& m_history;
|
||||||
int m_current_history;
|
int m_current_history;
|
||||||
bool m_auto_complete;
|
bool m_auto_complete;
|
||||||
|
@ -1197,7 +1209,7 @@ private:
|
||||||
|
|
||||||
void history_push(StringView entry)
|
void history_push(StringView entry)
|
||||||
{
|
{
|
||||||
if (entry.empty() or context().noninteractive() or
|
if (entry.empty() or not 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;
|
||||||
|
@ -1710,6 +1722,12 @@ void InputHandler::set_prompt_face(Face prompt_face)
|
||||||
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)));
|
||||||
|
@ -1760,7 +1778,13 @@ 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]{ --m_handle_key_level; });
|
auto dec = on_scope_end([this]{
|
||||||
|
--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) {
|
||||||
if (m_last_insert.recording)
|
if (m_last_insert.recording)
|
||||||
|
|
|
@ -83,6 +83,7 @@ public:
|
||||||
Face prompt_face, PromptFlags flags, char history_register,
|
Face prompt_face, PromptFlags flags, char history_register,
|
||||||
PromptCompleter completer, PromptCallback callback);
|
PromptCompleter completer, PromptCallback callback);
|
||||||
void set_prompt_face(Face prompt_face);
|
void set_prompt_face(Face prompt_face);
|
||||||
|
bool history_enabled() const;
|
||||||
|
|
||||||
// enter menu mode, callback is called on each selection change,
|
// enter menu mode, callback is called on each selection change,
|
||||||
// abort or validation with corresponding MenuEvent value
|
// abort or validation with corresponding MenuEvent value
|
||||||
|
|
|
@ -1016,7 +1016,7 @@ void select_regex(Context& context, NormalParams params)
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||||
if (event == PromptEvent::Abort)
|
if (event == PromptEvent::Abort)
|
||||||
return;
|
return;
|
||||||
if (not context.noninteractive())
|
if (context.input_handler().history_enabled())
|
||||||
RegisterManager::instance()[reg].set(context, ex.str());
|
RegisterManager::instance()[reg].set(context, ex.str());
|
||||||
|
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
|
@ -1038,7 +1038,7 @@ void split_regex(Context& context, NormalParams params)
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||||
if (event == PromptEvent::Abort)
|
if (event == PromptEvent::Abort)
|
||||||
return;
|
return;
|
||||||
if (not context.noninteractive())
|
if (context.input_handler().history_enabled())
|
||||||
RegisterManager::instance()[reg].set(context, ex.str());
|
RegisterManager::instance()[reg].set(context, ex.str());
|
||||||
|
|
||||||
auto& selections = context.selections();
|
auto& selections = context.selections();
|
||||||
|
@ -1142,7 +1142,7 @@ void keep(Context& context, NormalParams params)
|
||||||
RegisterManager::instance()[reg].restore(context, saved_reg);
|
RegisterManager::instance()[reg].restore(context, saved_reg);
|
||||||
if (event == PromptEvent::Abort)
|
if (event == PromptEvent::Abort)
|
||||||
return;
|
return;
|
||||||
if (not context.noninteractive())
|
if (context.input_handler().history_enabled())
|
||||||
RegisterManager::instance()[reg].set(context, regex.str());
|
RegisterManager::instance()[reg].set(context, regex.str());
|
||||||
|
|
||||||
if (regex.empty() or regex.str().empty())
|
if (regex.empty() or regex.str().empty())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user