2013-11-14 22:12:59 +01:00
|
|
|
#ifndef input_handler_hh_INCLUDED
|
|
|
|
#define input_handler_hh_INCLUDED
|
|
|
|
|
|
|
|
#include "completion.hh"
|
2017-11-12 06:01:18 +01:00
|
|
|
#include "constexpr_utils.hh"
|
2013-11-14 22:12:59 +01:00
|
|
|
#include "context.hh"
|
2024-03-31 12:38:51 +02:00
|
|
|
#include "env_vars.hh"
|
2014-07-11 01:27:04 +02:00
|
|
|
#include "face.hh"
|
2013-12-15 20:41:32 +01:00
|
|
|
#include "normal.hh"
|
2024-04-01 02:33:51 +02:00
|
|
|
#include "optional.hh"
|
2013-11-14 22:12:59 +01:00
|
|
|
#include "keys.hh"
|
|
|
|
#include "string.hh"
|
|
|
|
#include "utils.hh"
|
2014-08-12 01:30:13 +02:00
|
|
|
#include "safe_ptr.hh"
|
2016-02-27 18:23:13 +01:00
|
|
|
#include "display_buffer.hh"
|
2020-02-27 16:36:15 +01:00
|
|
|
#include "event_manager.hh"
|
2013-11-14 22:12:59 +01:00
|
|
|
|
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
|
|
|
enum class PromptEvent
|
|
|
|
{
|
|
|
|
Change,
|
|
|
|
Abort,
|
|
|
|
Validate
|
|
|
|
};
|
2014-11-01 20:31:13 +01:00
|
|
|
using PromptCallback = std::function<void (StringView, PromptEvent, Context&)>;
|
2023-08-12 19:10:52 +02:00
|
|
|
|
2016-08-22 21:31:08 +02:00
|
|
|
enum class PromptFlags
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
Password = 1 << 0,
|
2017-10-28 04:07:28 +02:00
|
|
|
DropHistoryEntriesWithBlankPrefix = 1 << 1,
|
2018-06-03 04:06:29 +02:00
|
|
|
Search = 1 << 2,
|
2016-08-22 21:31:08 +02:00
|
|
|
};
|
2017-03-15 18:55:34 +01:00
|
|
|
constexpr bool with_bit_ops(Meta::Type<PromptFlags>) { return true; }
|
2016-08-22 21:31:08 +02:00
|
|
|
|
2013-11-14 22:12:59 +01:00
|
|
|
using KeyCallback = std::function<void (Key, Context&)>;
|
|
|
|
|
|
|
|
class InputMode;
|
2015-04-11 18:22:37 +02:00
|
|
|
enum class KeymapMode : char;
|
2017-04-12 11:39:17 +02:00
|
|
|
enum class CursorMode;
|
2013-11-14 22:12:59 +01:00
|
|
|
|
2019-01-24 11:02:07 +01:00
|
|
|
using PromptCompleter = std::function<Completions (const Context&, CompletionFlags,
|
2019-03-24 02:45:49 +01:00
|
|
|
StringView, ByteCount)>;
|
2021-10-10 01:28:34 +02:00
|
|
|
enum class InsertMode : unsigned
|
|
|
|
{
|
|
|
|
Insert,
|
|
|
|
Append,
|
|
|
|
Replace,
|
|
|
|
InsertAtLineBegin,
|
|
|
|
AppendAtLineEnd,
|
|
|
|
OpenLineBelow,
|
|
|
|
OpenLineAbove
|
|
|
|
};
|
2019-01-24 11:02:07 +01:00
|
|
|
|
2024-03-31 12:38:51 +02:00
|
|
|
struct ModeInfo
|
|
|
|
{
|
|
|
|
DisplayLine display_line;
|
2024-04-01 02:33:51 +02:00
|
|
|
Optional<NormalParams> normal_params;
|
2024-03-31 12:38:51 +02:00
|
|
|
};
|
|
|
|
|
2013-11-14 22:12:59 +01:00
|
|
|
class InputHandler : public SafeCountable
|
|
|
|
{
|
|
|
|
public:
|
2014-12-19 00:12:58 +01:00
|
|
|
InputHandler(SelectionList selections,
|
|
|
|
Context::Flags flags = Context::Flags::None,
|
|
|
|
String name = "");
|
2013-11-14 22:12:59 +01:00
|
|
|
~InputHandler();
|
|
|
|
|
|
|
|
// switch to insert mode
|
2016-10-24 20:41:05 +02:00
|
|
|
void insert(InsertMode mode, int count);
|
2013-11-14 22:12:59 +01:00
|
|
|
// repeat last insert mode key sequence
|
|
|
|
void repeat_last_insert();
|
2022-12-11 19:30:02 +01:00
|
|
|
// insert a string without affecting the mode stack
|
|
|
|
void paste(StringView content);
|
2013-11-14 22:12:59 +01:00
|
|
|
|
|
|
|
// enter prompt mode, callback is called on each change,
|
|
|
|
// abort or validation with corresponding PromptEvent value
|
|
|
|
// returns to normal mode after validation if callback does
|
|
|
|
// not change the mode itself
|
2017-10-31 02:54:21 +01:00
|
|
|
void prompt(StringView prompt, String initstr, String emptystr,
|
2019-06-05 15:19:27 +02:00
|
|
|
Face prompt_face, PromptFlags flags, char history_register,
|
2019-01-24 11:02:07 +01:00
|
|
|
PromptCompleter completer, PromptCallback callback);
|
2014-07-11 01:27:04 +02:00
|
|
|
void set_prompt_face(Face prompt_face);
|
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.
2022-08-29 08:00:53 +02:00
|
|
|
bool history_enabled() const;
|
2013-11-14 22:12:59 +01:00
|
|
|
|
|
|
|
// execute callback on next keypress and returns to normal mode
|
|
|
|
// if callback does not change the mode itself
|
2020-02-27 16:36:15 +01:00
|
|
|
void on_next_key(StringView mode_name, KeymapMode mode, KeyCallback callback,
|
|
|
|
Timer::Callback idle_callback = Timer::Callback{});
|
2013-11-14 22:12:59 +01:00
|
|
|
|
|
|
|
// process the given key
|
|
|
|
void handle_key(Key key);
|
|
|
|
|
2023-11-07 08:01:54 +01:00
|
|
|
void refresh_ifn();
|
|
|
|
|
2013-11-14 22:12:59 +01:00
|
|
|
void start_recording(char reg);
|
|
|
|
bool is_recording() const;
|
|
|
|
void stop_recording();
|
|
|
|
char recording_reg() const { return m_recording_reg; }
|
|
|
|
|
|
|
|
void reset_normal_mode();
|
|
|
|
|
|
|
|
Context& context() { return m_context; }
|
|
|
|
const Context& context() const { return m_context; }
|
|
|
|
|
2024-03-31 12:38:51 +02:00
|
|
|
ModeInfo mode_info() const;
|
2014-12-19 00:12:58 +01:00
|
|
|
|
2017-04-12 11:39:17 +02:00
|
|
|
std::pair<CursorMode, DisplayCoord> get_cursor_info() const;
|
|
|
|
|
2016-03-22 23:54:29 +01:00
|
|
|
// Force an input handler into normal mode temporarily
|
|
|
|
struct ScopedForceNormal
|
|
|
|
{
|
2016-11-03 00:12:57 +01:00
|
|
|
ScopedForceNormal(InputHandler& handler, NormalParams params);
|
2016-03-22 23:54:29 +01:00
|
|
|
~ScopedForceNormal();
|
|
|
|
|
|
|
|
private:
|
|
|
|
InputHandler& m_handler;
|
|
|
|
InputMode* m_mode;
|
|
|
|
};
|
|
|
|
|
2013-11-14 22:12:59 +01:00
|
|
|
private:
|
|
|
|
Context m_context;
|
|
|
|
|
|
|
|
friend class InputMode;
|
2016-11-28 14:59:55 +01:00
|
|
|
Vector<RefPtr<InputMode>, MemoryDomain::Client> m_mode_stack;
|
2013-11-14 22:12:59 +01:00
|
|
|
|
2015-06-02 21:56:57 +02:00
|
|
|
InputMode& current_mode() const { return *m_mode_stack.back(); }
|
|
|
|
|
|
|
|
void push_mode(InputMode* new_mode);
|
2015-12-13 00:51:51 +01:00
|
|
|
void pop_mode(InputMode* current_mode);
|
2013-11-14 22:12:59 +01:00
|
|
|
|
2017-11-08 07:39:52 +01:00
|
|
|
struct Insertion{
|
|
|
|
NestedBool recording;
|
|
|
|
InsertMode mode;
|
|
|
|
Vector<Key> keys;
|
|
|
|
bool disable_hooks;
|
|
|
|
int count;
|
|
|
|
} m_last_insert = { {}, InsertMode::Insert, {}, false, 1 };
|
2013-11-14 22:12:59 +01:00
|
|
|
|
|
|
|
char m_recording_reg = 0;
|
|
|
|
String m_recorded_keys;
|
2016-04-13 22:21:49 +02:00
|
|
|
int m_recording_level = -1;
|
2014-11-21 20:00:34 +01:00
|
|
|
|
|
|
|
int m_handle_key_level = 0;
|
2013-11-14 22:12:59 +01:00
|
|
|
};
|
|
|
|
|
2015-11-20 09:50:53 +01:00
|
|
|
enum class AutoInfo
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
Command = 1 << 0,
|
|
|
|
OnKey = 1 << 1,
|
|
|
|
Normal = 1 << 2
|
|
|
|
};
|
|
|
|
|
2017-03-15 18:55:34 +01:00
|
|
|
constexpr bool with_bit_ops(Meta::Type<AutoInfo>) { return true; }
|
2015-11-20 09:50:53 +01:00
|
|
|
|
2017-08-12 17:11:58 +02:00
|
|
|
constexpr auto enum_desc(Meta::Type<AutoInfo>)
|
2015-11-20 09:50:53 +01:00
|
|
|
{
|
2019-10-17 13:48:22 +02:00
|
|
|
return make_array<EnumDesc<AutoInfo>>({
|
2015-11-20 09:50:53 +01:00
|
|
|
{ AutoInfo::Command, "command"},
|
|
|
|
{ AutoInfo::OnKey, "onkey"},
|
|
|
|
{ AutoInfo::Normal, "normal" }
|
2017-08-12 17:11:58 +02:00
|
|
|
});
|
2015-11-20 09:50:53 +01:00
|
|
|
}
|
|
|
|
|
2018-07-15 01:45:17 +02:00
|
|
|
enum class AutoComplete
|
|
|
|
{
|
|
|
|
None = 0,
|
|
|
|
Insert = 0b01,
|
|
|
|
Prompt = 0b10
|
|
|
|
};
|
|
|
|
constexpr bool with_bit_ops(Meta::Type<AutoComplete>) { return true; }
|
|
|
|
|
|
|
|
constexpr auto enum_desc(Meta::Type<AutoComplete>)
|
|
|
|
{
|
2019-10-17 13:48:22 +02:00
|
|
|
return make_array<EnumDesc<AutoComplete>>({
|
2018-07-15 01:45:17 +02:00
|
|
|
{ AutoComplete::Insert, "insert"},
|
|
|
|
{ AutoComplete::Prompt, "prompt" }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-27 16:36:15 +01:00
|
|
|
bool should_show_info(AutoInfo mask, const Context& context);
|
2015-11-19 00:43:51 +01:00
|
|
|
bool show_auto_info_ifn(StringView title, StringView info, AutoInfo mask, const Context& context);
|
2016-02-27 18:23:13 +01:00
|
|
|
void hide_auto_info_ifn(const Context& context, bool hide);
|
2015-08-11 21:36:07 +02:00
|
|
|
|
|
|
|
template<typename Cmd>
|
2019-11-11 11:38:40 +01:00
|
|
|
void on_next_key_with_autoinfo(const Context& context, StringView mode_name,
|
|
|
|
KeymapMode keymap_mode, Cmd cmd,
|
2020-02-27 16:36:15 +01:00
|
|
|
String title, String info)
|
2015-08-11 21:36:07 +02:00
|
|
|
{
|
2019-11-11 11:38:40 +01:00
|
|
|
context.input_handler().on_next_key(mode_name,
|
2020-02-27 16:36:15 +01:00
|
|
|
keymap_mode, [cmd](Key key, Context& context) mutable {
|
|
|
|
bool hide = should_show_info(AutoInfo::OnKey, context);
|
2016-02-27 18:23:13 +01:00
|
|
|
hide_auto_info_ifn(context, hide);
|
2015-08-11 21:36:07 +02:00
|
|
|
cmd(key, context);
|
2023-08-12 19:10:52 +02:00
|
|
|
}, [&context, title=std::move(title), info=std::move(info)](Timer&) {
|
2020-02-27 16:36:15 +01:00
|
|
|
show_auto_info_ifn(title, info, AutoInfo::OnKey, context);
|
2023-08-12 19:10:52 +02:00
|
|
|
});
|
2015-08-11 21:36:07 +02:00
|
|
|
}
|
2016-02-27 18:23:13 +01:00
|
|
|
|
2023-08-27 00:05:46 +02:00
|
|
|
enum class OnHiddenCursor {
|
|
|
|
PreserveSelections,
|
|
|
|
MoveCursor,
|
|
|
|
MoveCursorAndAnchor,
|
|
|
|
};
|
|
|
|
|
|
|
|
void scroll_window(Context& context, LineCount offset, OnHiddenCursor on_hidden_cursor);
|
2016-07-28 10:51:49 +02:00
|
|
|
|
2013-11-14 22:12:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // input_handler_hh_INCLUDED
|