summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2013-10-25 00:01:17 +0100
committerMaxime Coste <frrrwww@gmail.com>2013-10-25 00:30:46 +0100
commit2c09da50be80dbaf484682689bfd1a685d601573 (patch)
tree6fe5efc4ef6ff1c379c388de48fdc0dd96b79981 /src
parent77ac777526a6a05c2d2027e30c0bbcf9720ad5b7 (diff)
Add key mapping support
Diffstat (limited to 'src')
-rw-r--r--src/buffer.cc3
-rw-r--r--src/buffer.hh6
-rw-r--r--src/client.cc23
-rw-r--r--src/commands.cc37
-rw-r--r--src/context.cc9
-rw-r--r--src/context.hh2
-rw-r--r--src/keymap_manager.cc45
-rw-r--r--src/keymap_manager.hh52
-rw-r--r--src/main.cc2
-rw-r--r--src/window.cc3
-rw-r--r--src/window.hh4
11 files changed, 181 insertions, 5 deletions
diff --git a/src/buffer.cc b/src/buffer.cc
index 368d530e..249064bb 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -20,7 +20,8 @@ Buffer::Buffer(String name, Flags flags, std::vector<String> lines,
m_timestamp(0),
m_fs_timestamp(fs_timestamp),
m_hooks(GlobalHooks::instance()),
- m_options(GlobalOptions::instance())
+ m_options(GlobalOptions::instance()),
+ m_keymaps(GlobalKeymaps::instance())
{
BufferManager::instance().register_buffer(*this);
diff --git a/src/buffer.hh b/src/buffer.hh
index 4b67ce48..8727d217 100644
--- a/src/buffer.hh
+++ b/src/buffer.hh
@@ -1,9 +1,10 @@
#ifndef buffer_hh_INCLUDED
#define buffer_hh_INCLUDED
-#include "hook_manager.hh"
#include "line_and_column.hh"
+#include "hook_manager.hh"
#include "option_manager.hh"
+#include "keymap_manager.hh"
#include "string.hh"
#include "units.hh"
@@ -160,6 +161,8 @@ public:
const OptionManager& options() const { return m_options; }
HookManager& hooks() { 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; }
@@ -212,6 +215,7 @@ private:
OptionManager m_options;
HookManager m_hooks;
+ KeymapManager m_keymaps;
friend constexpr Flags operator|(Flags lhs, Flags rhs)
{
diff --git a/src/client.cc b/src/client.cc
index c81eab56..3b9827d6 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -33,6 +33,8 @@ public:
virtual String description() const = 0;
+ virtual KeymapMode keymap_mode() const = 0;
+
using Insertion = Client::Insertion;
Insertion& last_insert() { return m_client.m_last_insert; }
@@ -91,6 +93,8 @@ public:
(m_count != 0 ? " sel; param=" + to_string(m_count) : " sel");
}
+ KeymapMode keymap_mode() const override { return KeymapMode::Normal; }
+
private:
int m_count = 0;
Timer m_idle_timer;
@@ -270,6 +274,7 @@ public:
return "menu";
}
+ KeymapMode keymap_mode() const override { return KeymapMode::Menu; }
private:
MenuCallback m_callback;
@@ -472,6 +477,7 @@ public:
return "prompt";
}
+ KeymapMode keymap_mode() const override { return KeymapMode::Prompt; }
private:
void display() const
@@ -513,6 +519,8 @@ public:
return "enter key";
}
+ KeymapMode keymap_mode() const override { return KeymapMode::None; }
+
private:
KeyCallback m_callback;
};
@@ -933,6 +941,9 @@ public:
{
return "insert";
}
+
+ KeymapMode keymap_mode() const override { return KeymapMode::Insert; }
+
private:
enum class Mode { Default, Complete, InsertReg };
Mode m_mode = Mode::Default;
@@ -1010,7 +1021,7 @@ void Client::on_next_key(KeyCallback 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;
}
@@ -1031,7 +1042,15 @@ void Client::handle_key(Key key)
{
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.
if (was_recording and is_recording())
diff --git a/src/commands.cc b/src/commands.cc
index eeb33e1a..4b015c16 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -530,6 +530,42 @@ void declare_option(CommandParameters params, Context& context)
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
{
public:
@@ -857,5 +893,6 @@ void register_commands()
cm.register_commands({"nc", "nameclient"}, set_client_name);
cm.register_command("cd", change_working_directory, filename_completer);
+ cm.register_command("map", map_key);
}
}
diff --git a/src/context.cc b/src/context.cc
index 513bd6cb..49a64f7d 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -75,6 +75,15 @@ HookManager& Context::hooks() const
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
{
if (has_client())
diff --git a/src/context.hh b/src/context.hh
index 07e4ffa1..d65e9d4e 100644
--- a/src/context.hh
+++ b/src/context.hh
@@ -12,6 +12,7 @@ class Buffer;
class Client;
class UserInterface;
class DisplayLine;
+class KeymapManager;
// A Context is used to access non singleton objects for various services
// in commands.
@@ -48,6 +49,7 @@ struct Context
OptionManager& options() const;
HookManager& hooks() const;
+ KeymapManager& keymaps() const;
void print_status(DisplayLine status) const;
diff --git a/src/keymap_manager.cc b/src/keymap_manager.cc
new file mode 100644
index 00000000..71f936fd
--- /dev/null
+++ b/src/keymap_manager.cc
@@ -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);
+}
+
+}
diff --git a/src/keymap_manager.hh b/src/keymap_manager.hh
new file mode 100644
index 00000000..c9887a6b
--- /dev/null
+++ b/src/keymap_manager.hh
@@ -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
+
diff --git a/src/main.cc b/src/main.cc
index cd078d6c..40829135 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -14,6 +14,7 @@
#include "hook_manager.hh"
#include "ncurses.hh"
#include "option_manager.hh"
+#include "keymap_manager.hh"
#include "parameters_parser.hh"
#include "register_manager.hh"
#include "remote.hh"
@@ -264,6 +265,7 @@ int kakoune(memoryview<String> params)
EventManager event_manager;
GlobalOptions global_options;
GlobalHooks global_hooks;
+ GlobalKeymaps global_keymaps;
ShellManager shell_manager;
CommandManager command_manager;
BufferManager buffer_manager;
diff --git a/src/window.cc b/src/window.cc
index c7ec5fff..2fdec0f8 100644
--- a/src/window.cc
+++ b/src/window.cc
@@ -19,7 +19,8 @@ void expand_unprintable(const Window& window, DisplayBuffer& display_buffer);
Window::Window(Buffer& buffer)
: Editor(buffer),
m_hooks(buffer.hooks()),
- m_options(buffer.options())
+ m_options(buffer.options()),
+ m_keymaps(buffer.keymaps())
{
Context hook_context{*this};
m_hooks.run_hook("WinCreate", buffer.name(), hook_context);
diff --git a/src/window.hh b/src/window.hh
index 534d18b3..49bbe39e 100644
--- a/src/window.hh
+++ b/src/window.hh
@@ -8,6 +8,7 @@
#include "highlighter.hh"
#include "hook_manager.hh"
#include "option_manager.hh"
+#include "keymap_manager.hh"
namespace Kakoune
{
@@ -46,6 +47,8 @@ public:
const OptionManager& options() const { return m_options; }
HookManager& hooks() { 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; }
void forget_timestamp() { m_timestamp = -1; }
@@ -65,6 +68,7 @@ private:
HookManager m_hooks;
OptionManager m_options;
+ KeymapManager m_keymaps;
HighlighterGroup m_highlighters;
HighlighterGroup m_builtin_highlighters;