Merge remote-tracking branch 'Delapouite/user-mode'
This commit is contained in:
commit
f88195d2d9
|
@ -129,6 +129,12 @@ command *q!* has to be used). Aliases are mentionned below each commands.
|
||||||
*unmap* <scope> <mode> <key> [<expected>]::
|
*unmap* <scope> <mode> <key> [<expected>]::
|
||||||
unbind a key combination (See <<mapping#,`:doc mapping`>>)
|
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
|
== Hooks
|
||||||
|
|
||||||
*hook* [-group <group>] <scope> <hook_name> <filtering_regex> <command>::
|
*hook* [-group <group>] <scope> <hook_name> <filtering_regex> <command>::
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "highlighter.hh"
|
#include "highlighter.hh"
|
||||||
#include "highlighters.hh"
|
#include "highlighters.hh"
|
||||||
#include "insert_completer.hh"
|
#include "insert_completer.hh"
|
||||||
|
#include "normal.hh"
|
||||||
#include "option_manager.hh"
|
#include "option_manager.hh"
|
||||||
#include "option_types.hh"
|
#include "option_types.hh"
|
||||||
#include "parameters_parser.hh"
|
#include "parameters_parser.hh"
|
||||||
|
@ -1099,7 +1100,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("normal", str)) return KeymapMode::Normal;
|
||||||
if (prefix_match("insert", str)) return KeymapMode::Insert;
|
if (prefix_match("insert", str)) return KeymapMode::Insert;
|
||||||
|
@ -1110,7 +1111,12 @@ KeymapMode parse_keymap_mode(const String& str)
|
||||||
if (prefix_match("user", str)) return KeymapMode::User;
|
if (prefix_match("user", str)) return KeymapMode::User;
|
||||||
if (prefix_match("object", str)) return KeymapMode::Object;
|
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 = {
|
const CommandDesc debug_cmd = {
|
||||||
|
@ -1189,7 +1195,7 @@ const CommandDesc debug_cmd = {
|
||||||
write_to_debug_buffer("Mappings:");
|
write_to_debug_buffer("Mappings:");
|
||||||
for (auto& mode : modes)
|
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))
|
for (auto& key : keymaps.get_mapped_keys(m))
|
||||||
write_to_debug_buffer(format(" * {} {}: {}",
|
write_to_debug_buffer(format(" * {} {}: {}",
|
||||||
mode, key_to_str(key),
|
mode, key_to_str(key),
|
||||||
|
@ -1427,11 +1433,13 @@ auto map_key_completer =
|
||||||
if (token_to_complete == 0)
|
if (token_to_complete == 0)
|
||||||
return { 0_byte, params[0].length(),
|
return { 0_byte, params[0].length(),
|
||||||
complete(params[0], pos_in_token, scopes) };
|
complete(params[0], pos_in_token, scopes) };
|
||||||
|
|
||||||
if (token_to_complete == 1)
|
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(),
|
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 {};
|
return {};
|
||||||
};
|
};
|
||||||
|
@ -1459,7 +1467,7 @@ const CommandDesc map_key_cmd = {
|
||||||
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
||||||
{
|
{
|
||||||
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
|
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]);
|
KeyList key = parse_keys(parser[2]);
|
||||||
if (key.size() != 1)
|
if (key.size() != 1)
|
||||||
|
@ -1492,7 +1500,7 @@ const CommandDesc unmap_key_cmd = {
|
||||||
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
[](const ParametersParser& parser, Context& context, const ShellContext&)
|
||||||
{
|
{
|
||||||
KeymapManager& keymaps = get_scope(parser[0], context).keymaps();
|
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]);
|
KeyList key = parse_keys(parser[2]);
|
||||||
if (key.size() != 1)
|
if (key.size() != 1)
|
||||||
|
@ -2113,6 +2121,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()
|
void register_commands()
|
||||||
|
@ -2175,6 +2241,8 @@ void register_commands()
|
||||||
register_command(change_directory_cmd);
|
register_command(change_directory_cmd);
|
||||||
register_command(rename_session_cmd);
|
register_command(rename_session_cmd);
|
||||||
register_command(fail_cmd);
|
register_command(fail_cmd);
|
||||||
|
register_command(declare_user_mode_cmd);
|
||||||
|
register_command(enter_user_mode_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "array_view.hh"
|
#include "array_view.hh"
|
||||||
#include "assert.hh"
|
#include "assert.hh"
|
||||||
|
#include "exception.hh"
|
||||||
|
#include "string_utils.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -49,4 +51,19 @@ KeymapManager::KeyList KeymapManager::get_mapped_keys(KeymapMode mode) const
|
||||||
return res;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ enum class KeymapMode : char
|
||||||
View,
|
View,
|
||||||
User,
|
User,
|
||||||
Object,
|
Object,
|
||||||
|
FirstUserMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
class KeymapManager
|
class KeymapManager
|
||||||
|
@ -43,6 +44,10 @@ public:
|
||||||
};
|
};
|
||||||
const KeymapInfo& get_mapping(Key key, KeymapMode mode) const;
|
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:
|
private:
|
||||||
KeymapManager()
|
KeymapManager()
|
||||||
: m_parent(nullptr) {}
|
: m_parent(nullptr) {}
|
||||||
|
@ -52,6 +57,8 @@ private:
|
||||||
KeymapManager* m_parent;
|
KeymapManager* m_parent;
|
||||||
using KeyAndMode = std::pair<Key, KeymapMode>;
|
using KeyAndMode = std::pair<Key, KeymapMode>;
|
||||||
HashMap<KeyAndMode, KeymapInfo, MemoryDomain::Mapping> m_mapping;
|
HashMap<KeyAndMode, KeymapInfo, MemoryDomain::Mapping> m_mapping;
|
||||||
|
|
||||||
|
UserModeList m_user_modes;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,12 +127,6 @@ void repeat_last_select(Context& context, NormalParams)
|
||||||
context.repeat_last_select();
|
context.repeat_last_select();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KeyInfo
|
|
||||||
{
|
|
||||||
ConstArrayView<Key> keys;
|
|
||||||
StringView docstring;
|
|
||||||
};
|
|
||||||
|
|
||||||
String build_autoinfo_for_mapping(Context& context, KeymapMode mode,
|
String build_autoinfo_for_mapping(Context& context, KeymapMode mode,
|
||||||
ConstArrayView<KeyInfo> built_ins)
|
ConstArrayView<KeyInfo> built_ins)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#ifndef normal_hh_INCLUDED
|
#ifndef normal_hh_INCLUDED
|
||||||
#define normal_hh_INCLUDED
|
#define normal_hh_INCLUDED
|
||||||
|
|
||||||
|
#include "context.hh"
|
||||||
#include "optional.hh"
|
#include "optional.hh"
|
||||||
#include "keys.hh"
|
#include "keys.hh"
|
||||||
|
#include "keymap_manager.hh"
|
||||||
#include "string.hh"
|
#include "string.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
|
@ -24,6 +26,15 @@ struct NormalCmd
|
||||||
|
|
||||||
Optional<NormalCmd> get_normal_command(Key key);
|
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
|
#endif // normal_hh_INCLUDED
|
||||||
|
|
4
test/normal/user-modes/cmd
Normal file
4
test/normal/user-modes/cmd
Normal 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
|
1
test/normal/user-modes/in
Normal file
1
test/normal/user-modes/in
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bar
|
1
test/normal/user-modes/out
Normal file
1
test/normal/user-modes/out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hello from foo
|
Loading…
Reference in New Issue
Block a user