Add key mapping support
This commit is contained in:
parent
77ac777526
commit
2c09da50be
|
@ -20,7 +20,8 @@ Buffer::Buffer(String name, Flags flags, std::vector<String> lines,
|
||||||
m_timestamp(0),
|
m_timestamp(0),
|
||||||
m_fs_timestamp(fs_timestamp),
|
m_fs_timestamp(fs_timestamp),
|
||||||
m_hooks(GlobalHooks::instance()),
|
m_hooks(GlobalHooks::instance()),
|
||||||
m_options(GlobalOptions::instance())
|
m_options(GlobalOptions::instance()),
|
||||||
|
m_keymaps(GlobalKeymaps::instance())
|
||||||
{
|
{
|
||||||
BufferManager::instance().register_buffer(*this);
|
BufferManager::instance().register_buffer(*this);
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#ifndef buffer_hh_INCLUDED
|
#ifndef buffer_hh_INCLUDED
|
||||||
#define buffer_hh_INCLUDED
|
#define buffer_hh_INCLUDED
|
||||||
|
|
||||||
#include "hook_manager.hh"
|
|
||||||
#include "line_and_column.hh"
|
#include "line_and_column.hh"
|
||||||
|
#include "hook_manager.hh"
|
||||||
#include "option_manager.hh"
|
#include "option_manager.hh"
|
||||||
|
#include "keymap_manager.hh"
|
||||||
#include "string.hh"
|
#include "string.hh"
|
||||||
#include "units.hh"
|
#include "units.hh"
|
||||||
|
|
||||||
|
@ -160,6 +161,8 @@ public:
|
||||||
const OptionManager& options() const { return m_options; }
|
const OptionManager& options() const { return m_options; }
|
||||||
HookManager& hooks() { return m_hooks; }
|
HookManager& hooks() { return m_hooks; }
|
||||||
const HookManager& hooks() const { return m_hooks; }
|
const HookManager& hooks() const { return m_hooks; }
|
||||||
|
KeymapManager& keymaps() { return m_keymaps; }
|
||||||
|
const KeymapManager& keymaps() const { return m_keymaps; }
|
||||||
|
|
||||||
std::unordered_set<BufferChangeListener*>& change_listeners() const { return m_change_listeners; }
|
std::unordered_set<BufferChangeListener*>& change_listeners() const { return m_change_listeners; }
|
||||||
|
|
||||||
|
@ -212,6 +215,7 @@ private:
|
||||||
|
|
||||||
OptionManager m_options;
|
OptionManager m_options;
|
||||||
HookManager m_hooks;
|
HookManager m_hooks;
|
||||||
|
KeymapManager m_keymaps;
|
||||||
|
|
||||||
friend constexpr Flags operator|(Flags lhs, Flags rhs)
|
friend constexpr Flags operator|(Flags lhs, Flags rhs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
|
|
||||||
virtual String description() const = 0;
|
virtual String description() const = 0;
|
||||||
|
|
||||||
|
virtual KeymapMode keymap_mode() const = 0;
|
||||||
|
|
||||||
using Insertion = Client::Insertion;
|
using Insertion = Client::Insertion;
|
||||||
Insertion& last_insert() { return m_client.m_last_insert; }
|
Insertion& last_insert() { return m_client.m_last_insert; }
|
||||||
|
|
||||||
|
@ -91,6 +93,8 @@ public:
|
||||||
(m_count != 0 ? " sel; param=" + to_string(m_count) : " sel");
|
(m_count != 0 ? " sel; param=" + to_string(m_count) : " sel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapMode keymap_mode() const override { return KeymapMode::Normal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_count = 0;
|
int m_count = 0;
|
||||||
Timer m_idle_timer;
|
Timer m_idle_timer;
|
||||||
|
@ -270,6 +274,7 @@ public:
|
||||||
return "menu";
|
return "menu";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapMode keymap_mode() const override { return KeymapMode::Menu; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuCallback m_callback;
|
MenuCallback m_callback;
|
||||||
|
@ -472,6 +477,7 @@ public:
|
||||||
return "prompt";
|
return "prompt";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapMode keymap_mode() const override { return KeymapMode::Prompt; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void display() const
|
void display() const
|
||||||
|
@ -513,6 +519,8 @@ public:
|
||||||
return "enter key";
|
return "enter key";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapMode keymap_mode() const override { return KeymapMode::None; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KeyCallback m_callback;
|
KeyCallback m_callback;
|
||||||
};
|
};
|
||||||
|
@ -933,6 +941,9 @@ public:
|
||||||
{
|
{
|
||||||
return "insert";
|
return "insert";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapMode keymap_mode() const override { return KeymapMode::Insert; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Mode { Default, Complete, InsertReg };
|
enum class Mode { Default, Complete, InsertReg };
|
||||||
Mode m_mode = Mode::Default;
|
Mode m_mode = Mode::Default;
|
||||||
|
@ -1010,7 +1021,7 @@ void Client::on_next_key(KeyCallback callback)
|
||||||
change_input_mode(new InputModes::NextKey(*this, callback));
|
change_input_mode(new InputModes::NextKey(*this, callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_valid(Key key)
|
static bool is_valid(Key key)
|
||||||
{
|
{
|
||||||
return key != Key::Invalid and key.key <= 0x10FFFF;
|
return key != Key::Invalid and key.key <= 0x10FFFF;
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1042,14 @@ void Client::handle_key(Key key)
|
||||||
{
|
{
|
||||||
const bool was_recording = is_recording();
|
const bool was_recording = is_recording();
|
||||||
|
|
||||||
|
auto keymap_mode = m_mode->keymap_mode();
|
||||||
|
KeymapManager& keymaps = m_context.keymaps();
|
||||||
|
if (keymaps.is_mapped(key, keymap_mode))
|
||||||
|
{
|
||||||
|
for (auto& k : keymaps.get_mapping(key, keymap_mode))
|
||||||
|
m_mode->on_key(k);
|
||||||
|
}
|
||||||
|
else
|
||||||
m_mode->on_key(key);
|
m_mode->on_key(key);
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
|
@ -530,6 +530,42 @@ void declare_option(CommandParameters params, Context& context)
|
||||||
opt->set_from_string(params[2]);
|
opt->set_from_string(params[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
KeymapManager& get_keymap_manager(const String& scope, Context& context)
|
||||||
|
{
|
||||||
|
if (prefix_match("global", scope))
|
||||||
|
return GlobalKeymaps::instance();
|
||||||
|
else if (prefix_match("buffer", scope))
|
||||||
|
return context.buffer().keymaps();
|
||||||
|
else if (prefix_match("window", scope))
|
||||||
|
return context.window().keymaps();
|
||||||
|
throw runtime_error("error: no such keymap container " + scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeymapMode parse_keymap_mode(const String& str)
|
||||||
|
{
|
||||||
|
if (prefix_match("normal", str)) return KeymapMode::Normal;
|
||||||
|
if (prefix_match("insert", str)) return KeymapMode::Insert;
|
||||||
|
if (prefix_match("menu", str)) return KeymapMode::Menu;
|
||||||
|
if (prefix_match("prompt", str)) return KeymapMode::Prompt;
|
||||||
|
throw runtime_error("unknown keymap mode '" + str + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
void map_key(CommandParameters params, Context& context)
|
||||||
|
{
|
||||||
|
ParametersParser parser(params, {}, ParametersParser::Flags::None, 4, 4);
|
||||||
|
|
||||||
|
KeymapManager& keymaps = get_keymap_manager(params[0], context);
|
||||||
|
KeymapMode keymap_mode = parse_keymap_mode(params[1]);
|
||||||
|
|
||||||
|
KeyList key = parse_keys(params[2]);
|
||||||
|
if (key.size() != 1)
|
||||||
|
throw runtime_error("only a single key can be mapped");
|
||||||
|
|
||||||
|
KeyList mapping = parse_keys(params[3]);
|
||||||
|
keymaps.map_key(key[0], keymap_mode, std::move(mapping));
|
||||||
|
}
|
||||||
|
|
||||||
class DraftUI : public UserInterface
|
class DraftUI : public UserInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -857,5 +893,6 @@ void register_commands()
|
||||||
cm.register_commands({"nc", "nameclient"}, set_client_name);
|
cm.register_commands({"nc", "nameclient"}, set_client_name);
|
||||||
|
|
||||||
cm.register_command("cd", change_working_directory, filename_completer);
|
cm.register_command("cd", change_working_directory, filename_completer);
|
||||||
|
cm.register_command("map", map_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,15 @@ HookManager& Context::hooks() const
|
||||||
return GlobalHooks::instance();
|
return GlobalHooks::instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeymapManager& Context::keymaps() const
|
||||||
|
{
|
||||||
|
if (has_window())
|
||||||
|
return window().keymaps();
|
||||||
|
if (has_buffer())
|
||||||
|
return buffer().keymaps();
|
||||||
|
return GlobalKeymaps::instance();
|
||||||
|
}
|
||||||
|
|
||||||
void Context::print_status(DisplayLine status) const
|
void Context::print_status(DisplayLine status) const
|
||||||
{
|
{
|
||||||
if (has_client())
|
if (has_client())
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Buffer;
|
||||||
class Client;
|
class Client;
|
||||||
class UserInterface;
|
class UserInterface;
|
||||||
class DisplayLine;
|
class DisplayLine;
|
||||||
|
class KeymapManager;
|
||||||
|
|
||||||
// A Context is used to access non singleton objects for various services
|
// A Context is used to access non singleton objects for various services
|
||||||
// in commands.
|
// in commands.
|
||||||
|
@ -48,6 +49,7 @@ struct Context
|
||||||
|
|
||||||
OptionManager& options() const;
|
OptionManager& options() const;
|
||||||
HookManager& hooks() const;
|
HookManager& hooks() const;
|
||||||
|
KeymapManager& keymaps() const;
|
||||||
|
|
||||||
void print_status(DisplayLine status) const;
|
void print_status(DisplayLine status) const;
|
||||||
|
|
||||||
|
|
45
src/keymap_manager.cc
Normal file
45
src/keymap_manager.cc
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "keymap_manager.hh"
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
|
||||||
|
template<> struct hash<Kakoune::KeymapMode>
|
||||||
|
{
|
||||||
|
size_t operator()(Kakoune::KeymapMode val) const
|
||||||
|
{
|
||||||
|
return hash<int>{}((int)val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
void KeymapManager::map_key(Key key, KeymapMode mode, std::vector<Key> mapping)
|
||||||
|
{
|
||||||
|
m_mapping[{key, mode}] = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeymapManager::unmap_key(Key key, KeymapMode mode)
|
||||||
|
{
|
||||||
|
m_mapping.erase({key, mode});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool KeymapManager::is_mapped(Key key, KeymapMode mode) const
|
||||||
|
{
|
||||||
|
return m_mapping.find({key, mode}) != m_mapping.end() or
|
||||||
|
(m_parent and m_parent->is_mapped(key, mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryview<Key> KeymapManager::get_mapping(Key key, KeymapMode mode) const
|
||||||
|
{
|
||||||
|
auto it = m_mapping.find({key, mode});
|
||||||
|
if (it != m_mapping.end())
|
||||||
|
return { it->second };
|
||||||
|
kak_assert(m_parent);
|
||||||
|
return m_parent->get_mapping(key, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
src/keymap_manager.hh
Normal file
52
src/keymap_manager.hh
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef keymap_manager_hh_INCLUDED
|
||||||
|
#define keymap_manager_hh_INCLUDED
|
||||||
|
|
||||||
|
#include "idvaluemap.hh"
|
||||||
|
#include "keys.hh"
|
||||||
|
#include "utils.hh"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class KeymapMode : int
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Normal,
|
||||||
|
Insert,
|
||||||
|
Prompt,
|
||||||
|
Menu
|
||||||
|
};
|
||||||
|
|
||||||
|
class KeymapManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KeymapManager(KeymapManager& parent) : m_parent(&parent) {}
|
||||||
|
|
||||||
|
void map_key(Key key, KeymapMode mode, std::vector<Key> mapping);
|
||||||
|
void unmap_key(Key key, KeymapMode mode);
|
||||||
|
|
||||||
|
bool is_mapped(Key key, KeymapMode mode) const;
|
||||||
|
memoryview<Key> get_mapping(Key key, KeymapMode mode) const;
|
||||||
|
private:
|
||||||
|
KeymapManager()
|
||||||
|
: m_parent(nullptr) {}
|
||||||
|
// the only one allowed to construct a root map manager
|
||||||
|
friend class GlobalKeymaps;
|
||||||
|
|
||||||
|
KeymapManager* m_parent;
|
||||||
|
|
||||||
|
using Keymap = std::unordered_map<std::pair<Key, KeymapMode>, std::vector<Key>>;
|
||||||
|
Keymap m_mapping;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GlobalKeymaps : public KeymapManager,
|
||||||
|
public Singleton<GlobalKeymaps>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // keymap_manager_hh_INCLUDED
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "hook_manager.hh"
|
#include "hook_manager.hh"
|
||||||
#include "ncurses.hh"
|
#include "ncurses.hh"
|
||||||
#include "option_manager.hh"
|
#include "option_manager.hh"
|
||||||
|
#include "keymap_manager.hh"
|
||||||
#include "parameters_parser.hh"
|
#include "parameters_parser.hh"
|
||||||
#include "register_manager.hh"
|
#include "register_manager.hh"
|
||||||
#include "remote.hh"
|
#include "remote.hh"
|
||||||
|
@ -264,6 +265,7 @@ int kakoune(memoryview<String> params)
|
||||||
EventManager event_manager;
|
EventManager event_manager;
|
||||||
GlobalOptions global_options;
|
GlobalOptions global_options;
|
||||||
GlobalHooks global_hooks;
|
GlobalHooks global_hooks;
|
||||||
|
GlobalKeymaps global_keymaps;
|
||||||
ShellManager shell_manager;
|
ShellManager shell_manager;
|
||||||
CommandManager command_manager;
|
CommandManager command_manager;
|
||||||
BufferManager buffer_manager;
|
BufferManager buffer_manager;
|
||||||
|
|
|
@ -19,7 +19,8 @@ void expand_unprintable(const Window& window, DisplayBuffer& display_buffer);
|
||||||
Window::Window(Buffer& buffer)
|
Window::Window(Buffer& buffer)
|
||||||
: Editor(buffer),
|
: Editor(buffer),
|
||||||
m_hooks(buffer.hooks()),
|
m_hooks(buffer.hooks()),
|
||||||
m_options(buffer.options())
|
m_options(buffer.options()),
|
||||||
|
m_keymaps(buffer.keymaps())
|
||||||
{
|
{
|
||||||
Context hook_context{*this};
|
Context hook_context{*this};
|
||||||
m_hooks.run_hook("WinCreate", buffer.name(), hook_context);
|
m_hooks.run_hook("WinCreate", buffer.name(), hook_context);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "highlighter.hh"
|
#include "highlighter.hh"
|
||||||
#include "hook_manager.hh"
|
#include "hook_manager.hh"
|
||||||
#include "option_manager.hh"
|
#include "option_manager.hh"
|
||||||
|
#include "keymap_manager.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
@ -46,6 +47,8 @@ public:
|
||||||
const OptionManager& options() const { return m_options; }
|
const OptionManager& options() const { return m_options; }
|
||||||
HookManager& hooks() { return m_hooks; }
|
HookManager& hooks() { return m_hooks; }
|
||||||
const HookManager& hooks() const { return m_hooks; }
|
const HookManager& hooks() const { return m_hooks; }
|
||||||
|
KeymapManager& keymaps() { return m_keymaps; }
|
||||||
|
const KeymapManager& keymaps() const { return m_keymaps; }
|
||||||
|
|
||||||
size_t timestamp() const { return m_timestamp; }
|
size_t timestamp() const { return m_timestamp; }
|
||||||
void forget_timestamp() { m_timestamp = -1; }
|
void forget_timestamp() { m_timestamp = -1; }
|
||||||
|
@ -65,6 +68,7 @@ private:
|
||||||
|
|
||||||
HookManager m_hooks;
|
HookManager m_hooks;
|
||||||
OptionManager m_options;
|
OptionManager m_options;
|
||||||
|
KeymapManager m_keymaps;
|
||||||
|
|
||||||
HighlighterGroup m_highlighters;
|
HighlighterGroup m_highlighters;
|
||||||
HighlighterGroup m_builtin_highlighters;
|
HighlighterGroup m_builtin_highlighters;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user