Add key mapping support

This commit is contained in:
Maxime Coste 2013-10-25 00:01:17 +01:00
parent 77ac777526
commit 2c09da50be
11 changed files with 181 additions and 5 deletions

View File

@ -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);

View File

@ -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)
{ {

View File

@ -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,7 +1042,15 @@ void Client::handle_key(Key key)
{ {
const bool was_recording = is_recording(); const bool was_recording = is_recording();
m_mode->on_key(key); 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);
// 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.
if (was_recording and is_recording()) if (was_recording and is_recording())

View File

@ -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);
} }
} }

View File

@ -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())

View File

@ -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
View 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
View 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

View File

@ -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;

View File

@ -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);

View File

@ -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;