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_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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
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 "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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user