Add declare-user-mode / enter-user-mode commands

This commit is contained in:
Delapouite 2018-01-18 09:57:14 +01:00
parent 9c25e955df
commit 6b447a0ecb
9 changed files with 122 additions and 13 deletions

View File

@ -129,6 +129,12 @@ command *q!* has to be used). Aliases are mentionned below each commands.
*unmap* <scope> <mode> <key> [<expected>]::
unbind a key combination (See <<mapping#,`:doc mapping`>>)
*declare-user-mode* <scope> <name>::
declare a new user keymap mode within the context of a scope
*enter-user-mode* <scope> <name>::
enable <name> keymap mode for next key
== Hooks
*hook* [-group <group>] <scope> <hook_name> <filtering_regex> <command>::

View File

@ -15,6 +15,7 @@
#include "highlighter.hh"
#include "highlighters.hh"
#include "insert_completer.hh"
#include "normal.hh"
#include "option_manager.hh"
#include "option_types.hh"
#include "parameters_parser.hh"
@ -1096,7 +1097,7 @@ const CommandDesc echo_cmd = {
}
};
KeymapMode parse_keymap_mode(const String& str)
KeymapMode parse_keymap_mode(const String& str, const KeymapManager::UserModeList& user_modes)
{
if (prefix_match("normal", str)) return KeymapMode::Normal;
if (prefix_match("insert", str)) return KeymapMode::Insert;
@ -1107,7 +1108,12 @@ KeymapMode parse_keymap_mode(const String& str)
if (prefix_match("user", str)) return KeymapMode::User;
if (prefix_match("object", str)) return KeymapMode::Object;
throw runtime_error(format("unknown keymap mode '{}'", str));
auto it = find(user_modes, str);
if (it == user_modes.end())
throw runtime_error(format("unknown keymap mode '{}'", str));
char offset = static_cast<char>(KeymapMode::FirstUserMode);
return (KeymapMode)(std::distance(user_modes.begin(), it) + offset);
}
const CommandDesc debug_cmd = {
@ -1186,7 +1192,7 @@ const CommandDesc debug_cmd = {
write_to_debug_buffer("Mappings:");
for (auto& mode : modes)
{
KeymapMode m = parse_keymap_mode(mode);
KeymapMode m = parse_keymap_mode(mode, keymaps.user_modes());
for (auto& key : keymaps.get_mapped_keys(m))
write_to_debug_buffer(format(" * {} {}: {}",
mode, key_to_str(key),
@ -1415,11 +1421,13 @@ auto map_key_completer =
if (token_to_complete == 0)
return { 0_byte, params[0].length(),
complete(params[0], pos_in_token, scopes) };
if (token_to_complete == 1)
{
constexpr const char* modes[] = { "normal", "insert", "menu", "prompt", "goto", "view", "user", "object" };
static constexpr auto modes = { "normal", "insert", "menu", "prompt", "goto", "view", "user", "object" };
auto& user_modes = get_scope(params[0], context).keymaps().user_modes();
return { 0_byte, params[1].length(),
complete(params[1], pos_in_token, modes) };
complete(params[1], pos_in_token, concatenated(modes, user_modes) | gather<Vector<String>>()) };
}
return {};
};
@ -1447,7 +1455,7 @@ const CommandDesc map_key_cmd = {
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
KeymapMode keymap_mode = parse_keymap_mode(parser[1]);
KeymapMode keymap_mode = parse_keymap_mode(parser[1], keymaps.user_modes());
KeyList key = parse_keys(parser[2]);
if (key.size() != 1)
@ -1480,7 +1488,7 @@ const CommandDesc unmap_key_cmd = {
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
KeymapMode keymap_mode = parse_keymap_mode(parser[1]);
KeymapMode keymap_mode = parse_keymap_mode(parser[1], keymaps.user_modes());
KeyList key = parse_keys(parser[2]);
if (key.size() != 1)
@ -2101,6 +2109,64 @@ const CommandDesc fail_cmd = {
}
};
const CommandDesc declare_user_mode_cmd = {
"declare-user-mode",
nullptr,
"declare-user-mode <scope> <name>: add a new user keymap mode in given <scope>",
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None,
CommandHelper{},
make_completer(complete_scope),
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
keymaps.add_user_mode(std::move(parser[1]));
}
};
const CommandDesc enter_user_mode_cmd = {
"enter-user-mode",
nullptr,
"enter-user-mode <scope> <name>: enable <name> keymap mode for next key",
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None,
CommandHelper{},
[](const Context& context, CompletionFlags flags,
CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token) -> Completions
{
if (token_to_complete == 0)
return { 0_byte, params[0].length(),
complete(params[0], pos_in_token, scopes) };
if (token_to_complete == 1)
{
KeymapManager& keymaps = get_scope(params[0], context).keymaps();
return { 0_byte, params[1].length(),
complete(params[1], pos_in_token, keymaps.user_modes()) };
}
return {};
},
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
KeymapMode mode = parse_keymap_mode(parser[1], keymaps.user_modes());
on_next_key_with_autoinfo(context, mode,
[mode](Key key, Context& context) mutable {
if (not context.keymaps().is_mapped(key, mode))
return;
auto& mapping = context.keymaps().get_mapping(key, mode);
ScopedSetBool disable_keymaps(context.keymaps_disabled());
InputHandler::ScopedForceNormal force_normal{context.input_handler(), {}};
ScopedEdition edition(context);
for (auto& key : mapping.keys)
context.input_handler().handle_key(key);
}, parser[1], build_autoinfo_for_mapping(context, mode, {}));
}
};
}
void register_commands()
@ -2163,6 +2229,8 @@ void register_commands()
register_command(change_directory_cmd);
register_command(rename_session_cmd);
register_command(fail_cmd);
register_command(declare_user_mode_cmd);
register_command(enter_user_mode_cmd);
}
}

View File

@ -2,6 +2,8 @@
#include "array_view.hh"
#include "assert.hh"
#include "exception.hh"
#include "string_utils.hh"
#include <algorithm>
@ -49,4 +51,19 @@ KeymapManager::KeyList KeymapManager::get_mapped_keys(KeymapMode mode) const
return res;
}
void KeymapManager::add_user_mode(const String user_mode_name)
{
auto modes = {"normal", "insert", "prompt", "menu", "goto", "view", "user", "object"};
if (contains(modes, user_mode_name))
throw runtime_error(format("'{}' is already a regular mode", user_mode_name));
if (contains(m_user_modes, user_mode_name))
throw runtime_error(format("user mode '{}' already defined", user_mode_name));
if (contains_that(user_mode_name, [](char c){ return not isalnum(c); }))
throw runtime_error(format("invalid mode name: '{}'", user_mode_name));
m_user_modes.push_back(user_mode_name);
}
}

View File

@ -22,6 +22,7 @@ enum class KeymapMode : char
View,
User,
Object,
FirstUserMode,
};
class KeymapManager
@ -43,6 +44,10 @@ public:
};
const KeymapInfo& get_mapping(Key key, KeymapMode mode) const;
using UserModeList = Vector<String>;
const UserModeList& user_modes() const { return m_user_modes; }
void add_user_mode(const String user_mode_name);
private:
KeymapManager()
: m_parent(nullptr) {}
@ -52,6 +57,8 @@ private:
KeymapManager* m_parent;
using KeyAndMode = std::pair<Key, KeymapMode>;
HashMap<KeyAndMode, KeymapInfo, MemoryDomain::Mapping> m_mapping;
UserModeList m_user_modes;
};
}

View File

@ -127,12 +127,6 @@ void repeat_last_select(Context& context, NormalParams)
context.repeat_last_select();
}
struct KeyInfo
{
ConstArrayView<Key> keys;
StringView docstring;
};
String build_autoinfo_for_mapping(Context& context, KeymapMode mode,
ConstArrayView<KeyInfo> built_ins)
{

View File

@ -1,8 +1,10 @@
#ifndef normal_hh_INCLUDED
#define normal_hh_INCLUDED
#include "context.hh"
#include "optional.hh"
#include "keys.hh"
#include "keymap_manager.hh"
#include "string.hh"
namespace Kakoune
@ -24,6 +26,15 @@ struct NormalCmd
Optional<NormalCmd> get_normal_command(Key key);
struct KeyInfo
{
ConstArrayView<Key> keys;
StringView docstring;
};
String build_autoinfo_for_mapping(Context& context, KeymapMode mode,
ConstArrayView<KeyInfo> built_ins);
}
#endif // normal_hh_INCLUDED

View File

@ -0,0 +1,4 @@
:declare-user-mode global foo<ret>
:map global foo f '<esc>wchello from foo<esc>'
:enter-user-mode global foo
f

View File

@ -0,0 +1 @@
bar

View File

@ -0,0 +1 @@
hello from foo