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>]::
|
||||
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>::
|
||||
|
|
|
@ -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"
|
||||
|
@ -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("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("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 = {
|
||||
|
@ -1189,7 +1195,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),
|
||||
|
@ -1427,11 +1433,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 {};
|
||||
};
|
||||
|
@ -1459,7 +1467,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)
|
||||
|
@ -1492,7 +1500,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)
|
||||
|
@ -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()
|
||||
|
@ -2175,6 +2241,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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
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