Add scoped aliases
aliases are now stored in window, buffer, or globally.
This commit is contained in:
parent
502de87697
commit
185b980718
|
@ -988,6 +988,27 @@ Some helper commands can be used to define composite commands:
|
|||
Note that these commands are available in interactive command mode, but are
|
||||
not that useful in this context.
|
||||
|
||||
Aliases
|
||||
-------
|
||||
|
||||
With +:alias+ commands can be given additional names. aliases are scoped, so
|
||||
that an alias can refer to one command for a buffer, and to another for another
|
||||
buffer.
|
||||
|
||||
--------------------------------
|
||||
:alias <scope> <alias> <command>
|
||||
--------------------------------
|
||||
|
||||
with +<scope>+ being +global+, +buffer+ or +window+, will define +<alias>+ as
|
||||
an alias for +<command>+
|
||||
|
||||
-------------------------------------
|
||||
:unalias <scope> <alias> [<expected>]
|
||||
-------------------------------------
|
||||
|
||||
Will remove the given alias in the given scope. If +<expected>+ is specified
|
||||
the alias will only be removed if its current value is +<expected>+.
|
||||
|
||||
FIFO Buffer
|
||||
-----------
|
||||
|
||||
|
|
48
src/alias_registry.cc
Normal file
48
src/alias_registry.cc
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include "alias_registry.hh"
|
||||
|
||||
#include "command_manager.hh"
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
void AliasRegistry::add_alias(String alias, String command)
|
||||
{
|
||||
kak_assert(not alias.empty());
|
||||
kak_assert(CommandManager::instance().command_defined(command));
|
||||
m_aliases[alias] = std::move(command);
|
||||
}
|
||||
|
||||
void AliasRegistry::remove_alias(const String& alias)
|
||||
{
|
||||
auto it = m_aliases.find(alias);
|
||||
if (it != m_aliases.end())
|
||||
m_aliases.erase(it);
|
||||
}
|
||||
|
||||
StringView AliasRegistry::operator[](const String& alias) const
|
||||
{
|
||||
auto it = m_aliases.find(alias);
|
||||
if (it != m_aliases.end())
|
||||
return it->second;
|
||||
else if (m_parent)
|
||||
return (*m_parent)[alias];
|
||||
else
|
||||
return StringView{};
|
||||
}
|
||||
|
||||
std::vector<StringView> AliasRegistry::aliases_for(StringView command) const
|
||||
{
|
||||
std::vector<StringView> res;
|
||||
if (m_parent)
|
||||
res = m_parent->aliases_for(command);
|
||||
|
||||
for (auto& alias : m_aliases)
|
||||
{
|
||||
if (alias.second == command)
|
||||
res.push_back(alias.first);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
37
src/alias_registry.hh
Normal file
37
src/alias_registry.hh
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef alias_registry_hh_INCLUDED
|
||||
#define alias_registry_hh_INCLUDED
|
||||
|
||||
#include "utils.hh"
|
||||
#include "safe_ptr.hh"
|
||||
#include "string.hh"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
|
||||
class AliasRegistry : public SafeCountable
|
||||
{
|
||||
public:
|
||||
AliasRegistry(AliasRegistry& parent) : m_parent(&parent) {}
|
||||
void add_alias(String alias, String command);
|
||||
void remove_alias(const String& alias);
|
||||
StringView operator[](const String& name) const;
|
||||
|
||||
std::vector<StringView> aliases_for(StringView command) const;
|
||||
|
||||
private:
|
||||
friend class GlobalAliases;
|
||||
AliasRegistry() {}
|
||||
|
||||
safe_ptr<AliasRegistry> m_parent;
|
||||
std::unordered_map<String, String> m_aliases;
|
||||
};
|
||||
|
||||
class GlobalAliases : public AliasRegistry, public Singleton<GlobalAliases>
|
||||
{};
|
||||
|
||||
}
|
||||
|
||||
#endif // alias_registry_hh_INCLUDED
|
||||
|
|
@ -23,7 +23,8 @@ Buffer::Buffer(String name, Flags flags, std::vector<String> lines,
|
|||
m_fs_timestamp(fs_timestamp),
|
||||
m_hooks(GlobalHooks::instance()),
|
||||
m_options(GlobalOptions::instance()),
|
||||
m_keymaps(GlobalKeymaps::instance())
|
||||
m_keymaps(GlobalKeymaps::instance()),
|
||||
m_aliases(GlobalAliases::instance())
|
||||
{
|
||||
BufferManager::instance().register_buffer(*this);
|
||||
m_options.register_watcher(*this);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef buffer_hh_INCLUDED
|
||||
#define buffer_hh_INCLUDED
|
||||
|
||||
#include "alias_registry.hh"
|
||||
#include "coord.hh"
|
||||
#include "flags.hh"
|
||||
#include "hook_manager.hh"
|
||||
|
@ -155,6 +156,8 @@ public:
|
|||
const HookManager& hooks() const { return m_hooks; }
|
||||
KeymapManager& keymaps() { return m_keymaps; }
|
||||
const KeymapManager& keymaps() const { return m_keymaps; }
|
||||
AliasRegistry& aliases() { return m_aliases; }
|
||||
const AliasRegistry& aliases() const { return m_aliases; }
|
||||
|
||||
ValueMap& values() const { return m_values; }
|
||||
|
||||
|
@ -217,6 +220,7 @@ private:
|
|||
OptionManager m_options;
|
||||
HookManager m_hooks;
|
||||
KeymapManager m_keymaps;
|
||||
AliasRegistry m_aliases;
|
||||
|
||||
// Values are just data holding by the buffer, so it is part of its
|
||||
// observable state
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "command_manager.hh"
|
||||
|
||||
#include "alias_registry.hh"
|
||||
#include "assert.hh"
|
||||
#include "context.hh"
|
||||
#include "register_manager.hh"
|
||||
|
@ -13,7 +14,7 @@ namespace Kakoune
|
|||
|
||||
bool CommandManager::command_defined(const String& command_name) const
|
||||
{
|
||||
return find_command(command_name) != m_commands.end();
|
||||
return m_commands.find(command_name) != m_commands.end();
|
||||
}
|
||||
|
||||
void CommandManager::register_command(String command_name,
|
||||
|
@ -30,13 +31,6 @@ void CommandManager::register_command(String command_name,
|
|||
std::move(completer) };
|
||||
}
|
||||
|
||||
void CommandManager::register_alias(String alias, String command)
|
||||
{
|
||||
kak_assert(not alias.empty());
|
||||
kak_assert(command_defined(command));
|
||||
m_aliases[alias] = std::move(command);
|
||||
}
|
||||
|
||||
struct parse_error : runtime_error
|
||||
{
|
||||
parse_error(const String& error)
|
||||
|
@ -347,10 +341,10 @@ struct command_not_found : runtime_error
|
|||
};
|
||||
|
||||
CommandManager::CommandMap::const_iterator
|
||||
CommandManager::find_command(const String& name) const
|
||||
CommandManager::find_command(const Context& context, const String& name) const
|
||||
{
|
||||
auto it = m_aliases.find(name);
|
||||
const String& cmd_name = it == m_aliases.end() ? name : it->second;
|
||||
auto alias = context.aliases()[name];
|
||||
const String& cmd_name = alias.empty() ? name : alias.str();
|
||||
|
||||
return m_commands.find(cmd_name);
|
||||
}
|
||||
|
@ -363,7 +357,7 @@ void CommandManager::execute_single_command(CommandParameters params,
|
|||
return;
|
||||
|
||||
memoryview<String> param_view(params.begin()+1, params.end());
|
||||
auto command_it = find_command(params[0]);
|
||||
auto command_it = find_command(context, params[0]);
|
||||
if (command_it == m_commands.end())
|
||||
throw command_not_found(params[0]);
|
||||
|
||||
|
@ -443,7 +437,7 @@ void CommandManager::execute(StringView command_line,
|
|||
execute_single_command(params, context, command_coord);
|
||||
}
|
||||
|
||||
CommandInfo CommandManager::command_info(StringView command_line) const
|
||||
CommandInfo CommandManager::command_info(const Context& context, StringView command_line) const
|
||||
{
|
||||
TokenList tokens = parse<false>(command_line);
|
||||
size_t cmd_idx = 0;
|
||||
|
@ -458,7 +452,7 @@ CommandInfo CommandManager::command_info(StringView command_line) const
|
|||
tokens[cmd_idx].type() != Token::Type::Raw)
|
||||
return res;
|
||||
|
||||
auto cmd = find_command(tokens[cmd_idx].content());
|
||||
auto cmd = find_command(context, tokens[cmd_idx].content());
|
||||
if (cmd == m_commands.end())
|
||||
return res;
|
||||
|
||||
|
@ -467,11 +461,8 @@ CommandInfo CommandManager::command_info(StringView command_line) const
|
|||
res.second += cmd->second.docstring + "\n";
|
||||
|
||||
String aliases;
|
||||
for (auto& alias : m_aliases)
|
||||
{
|
||||
if (alias.second == cmd->first)
|
||||
aliases += " " + alias.first;
|
||||
}
|
||||
for (auto& alias : context.aliases().aliases_for(cmd->first))
|
||||
aliases += " " + alias;
|
||||
if (not aliases.empty())
|
||||
res.second += "Aliases:" + aliases + "\n";
|
||||
|
||||
|
@ -562,7 +553,7 @@ Completions CommandManager::complete(const Context& context,
|
|||
|
||||
const String& command_name = tokens[cmd_idx].content();
|
||||
|
||||
auto command_it = find_command(command_name);
|
||||
auto command_it = find_command(context, command_name);
|
||||
if (command_it == m_commands.end() or
|
||||
not command_it->second.completer)
|
||||
return Completions();
|
||||
|
|
|
@ -65,7 +65,8 @@ public:
|
|||
Completions complete(const Context& context, CompletionFlags flags,
|
||||
StringView command_line, ByteCount cursor_pos);
|
||||
|
||||
CommandInfo command_info(StringView command_line) const;
|
||||
CommandInfo command_info(const Context& context,
|
||||
StringView command_line) const;
|
||||
|
||||
bool command_defined(const String& command_name) const;
|
||||
|
||||
|
@ -74,7 +75,6 @@ public:
|
|||
ParameterDesc param_desc,
|
||||
CommandFlags flags = CommandFlags::None,
|
||||
CommandCompleter completer = CommandCompleter());
|
||||
void register_alias(String alias, String command);
|
||||
|
||||
private:
|
||||
void execute_single_command(CommandParameters params,
|
||||
|
@ -90,9 +90,9 @@ private:
|
|||
};
|
||||
using CommandMap = std::unordered_map<String, CommandDescriptor>;
|
||||
CommandMap m_commands;
|
||||
std::unordered_map<String, String> m_aliases;
|
||||
|
||||
CommandMap::const_iterator find_command(const String& name) const;
|
||||
CommandMap::const_iterator find_command(const Context& context,
|
||||
const String& name) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ const ParameterDesc single_optional_name_param{
|
|||
SwitchMap{}, ParameterDesc::Flags::None, 0, 1
|
||||
};
|
||||
|
||||
static constexpr auto scopes = { "global", "buffer", "window" };
|
||||
|
||||
struct CommandDesc
|
||||
{
|
||||
const char* name;
|
||||
|
@ -511,8 +513,6 @@ HookManager& get_hook_manager(const String& scope, const Context& context)
|
|||
throw runtime_error("error: no such hook container " + scope);
|
||||
}
|
||||
|
||||
static constexpr auto scopes = { "global", "buffer", "window" };
|
||||
|
||||
const CommandDesc add_hook_cmd = {
|
||||
"hook",
|
||||
nullptr,
|
||||
|
@ -613,14 +613,6 @@ void define_command(const ParametersParser& parser, Context& context)
|
|||
if (parser.has_option("docstring"))
|
||||
docstring = parser.option_value("docstring");
|
||||
|
||||
String alias;
|
||||
if (parser.has_option("alias"))
|
||||
{
|
||||
alias = parser.option_value("alias");
|
||||
if (alias.empty())
|
||||
throw runtime_error("alias should not be an empty string");
|
||||
}
|
||||
|
||||
String commands = parser[1];
|
||||
Command cmd;
|
||||
ParameterDesc desc;
|
||||
|
@ -697,8 +689,6 @@ void define_command(const ParametersParser& parser, Context& context)
|
|||
|
||||
auto& cm = CommandManager::instance();
|
||||
cm.register_command(cmd_name, cmd, std::move(docstring), desc, flags, completer);
|
||||
if (not alias.empty())
|
||||
cm.register_alias(std::move(alias), cmd_name);
|
||||
}
|
||||
|
||||
const CommandDesc define_command_cmd = {
|
||||
|
@ -709,7 +699,6 @@ const CommandDesc define_command_cmd = {
|
|||
SwitchMap{ { "shell-params", { false, "pass parameters to each shell escape as $0..$N" } },
|
||||
{ "allow-override", { false, "allow overriding an existing command" } },
|
||||
{ "hidden", { false, "do not display the command in completion candidates" } },
|
||||
{ "alias", { true, "define an alias for this command" } },
|
||||
{ "docstring", { true, "define the documentation string for command" } },
|
||||
{ "file-completion", { false, "complete parameters using filename completion" } },
|
||||
{ "client-completion", { false, "complete parameters using client name completion" } },
|
||||
|
@ -723,6 +712,49 @@ const CommandDesc define_command_cmd = {
|
|||
define_command
|
||||
};
|
||||
|
||||
AliasRegistry& get_aliases(const String& scope, const Context& context)
|
||||
{
|
||||
if (prefix_match("global", scope))
|
||||
return GlobalAliases::instance();
|
||||
else if (prefix_match("buffer", scope))
|
||||
return context.buffer().aliases();
|
||||
else if (prefix_match("window", scope))
|
||||
return context.window().aliases();
|
||||
throw runtime_error("error: no such scope " + scope);
|
||||
}
|
||||
|
||||
const CommandDesc alias_cmd = {
|
||||
"alias",
|
||||
nullptr,
|
||||
"alias <scope> <alias> <command>: define a command alias in given scope",
|
||||
ParameterDesc{SwitchMap{}, ParameterDesc::Flags::None, 3, 3},
|
||||
CommandFlags::None,
|
||||
CommandCompleter{},
|
||||
[](const ParametersParser& parser, Context& context)
|
||||
{
|
||||
AliasRegistry& aliases = get_aliases(parser[0], context);
|
||||
aliases.add_alias(parser[1], parser[2]);
|
||||
}
|
||||
};
|
||||
|
||||
const CommandDesc unalias_cmd = {
|
||||
"unalias",
|
||||
nullptr,
|
||||
"unalias <scope> <alias> [<expected>]: remove a command alias in given scope\n"
|
||||
"if <expected> is specified, the alias is removed only if its value is <expected>",
|
||||
ParameterDesc{SwitchMap{}, ParameterDesc::Flags::None, 2, 3},
|
||||
CommandFlags::None,
|
||||
CommandCompleter{},
|
||||
[](const ParametersParser& parser, Context& context)
|
||||
{
|
||||
AliasRegistry& aliases = get_aliases(parser[0], context);
|
||||
if (parser.positional_count() == 3 and
|
||||
aliases[parser[1]] != parser[2])
|
||||
return;
|
||||
aliases.remove_alias(parser[1]);
|
||||
}
|
||||
};
|
||||
|
||||
const CommandDesc echo_cmd = {
|
||||
"echo",
|
||||
nullptr,
|
||||
|
@ -1387,7 +1419,7 @@ static void register_command(CommandManager& cm, const CommandDesc& c)
|
|||
{
|
||||
cm.register_command(c.name, c.func, c.docstring, c.params, c.flags, c.completer);
|
||||
if (c.alias)
|
||||
cm.register_alias(c.alias, c.name);
|
||||
GlobalAliases::instance().add_alias(c.alias, c.name);
|
||||
}
|
||||
|
||||
void register_commands()
|
||||
|
@ -1414,6 +1446,8 @@ void register_commands()
|
|||
register_command(cm, add_hook_cmd);
|
||||
register_command(cm, rm_hook_cmd);
|
||||
register_command(cm, define_command_cmd);
|
||||
register_command(cm, alias_cmd);
|
||||
register_command(cm, unalias_cmd);
|
||||
register_command(cm, echo_cmd);
|
||||
register_command(cm, debug_cmd);
|
||||
register_command(cm, source_cmd);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "context.hh"
|
||||
|
||||
#include "alias_registry.hh"
|
||||
#include "client.hh"
|
||||
#include "user_interface.hh"
|
||||
#include "register_manager.hh"
|
||||
|
@ -80,6 +81,15 @@ KeymapManager& Context::keymaps() const
|
|||
return GlobalKeymaps::instance();
|
||||
}
|
||||
|
||||
AliasRegistry& Context::aliases() const
|
||||
{
|
||||
if (has_window())
|
||||
return window().aliases();
|
||||
if (has_buffer())
|
||||
return buffer().aliases();
|
||||
return GlobalAliases::instance();
|
||||
}
|
||||
|
||||
void Context::set_client(Client& client)
|
||||
{
|
||||
kak_assert(not has_client());
|
||||
|
|
|
@ -14,6 +14,7 @@ class InputHandler;
|
|||
class UserInterface;
|
||||
class DisplayLine;
|
||||
class KeymapManager;
|
||||
class AliasRegistry;
|
||||
|
||||
// A Context is used to access non singleton objects for various services
|
||||
// in commands.
|
||||
|
@ -60,6 +61,7 @@ public:
|
|||
OptionManager& options() const;
|
||||
HookManager& hooks() const;
|
||||
KeymapManager& keymaps() const;
|
||||
AliasRegistry& aliases() const;
|
||||
|
||||
void print_status(DisplayLine status) const;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "assert.hh"
|
||||
#include "alias_registry.hh"
|
||||
#include "buffer.hh"
|
||||
#include "buffer_manager.hh"
|
||||
#include "buffer_utils.hh"
|
||||
|
@ -317,6 +318,7 @@ int run_server(StringView session, StringView init_command,
|
|||
GlobalOptions global_options;
|
||||
GlobalHooks global_hooks;
|
||||
GlobalKeymaps global_keymaps;
|
||||
GlobalAliases global_aliases;
|
||||
ShellManager shell_manager;
|
||||
CommandManager command_manager;
|
||||
BufferManager buffer_manager;
|
||||
|
@ -399,6 +401,7 @@ int run_filter(StringView keystr, memoryview<StringView> files)
|
|||
GlobalOptions global_options;
|
||||
GlobalHooks global_hooks;
|
||||
GlobalKeymaps global_keymaps;
|
||||
GlobalAliases global_aliases;
|
||||
ShellManager shell_manager;
|
||||
BufferManager buffer_manager;
|
||||
RegisterManager register_manager;
|
||||
|
|
|
@ -361,7 +361,7 @@ void command(Context& context, int)
|
|||
context.ui().info_hide();
|
||||
if (event == PromptEvent::Change and context.options()["autoinfo"].get<int>() > 0)
|
||||
{
|
||||
auto info = CommandManager::instance().command_info(cmdline);
|
||||
auto info = CommandManager::instance().command_info(context, cmdline);
|
||||
Face col = get_face("Information");
|
||||
CharCoord pos = context.window().dimensions();
|
||||
pos.column -= 1;
|
||||
|
|
|
@ -21,7 +21,8 @@ Window::Window(Buffer& buffer)
|
|||
: m_buffer(&buffer),
|
||||
m_hooks(buffer.hooks()),
|
||||
m_options(buffer.options()),
|
||||
m_keymaps(buffer.keymaps())
|
||||
m_keymaps(buffer.keymaps()),
|
||||
m_aliases(buffer.aliases())
|
||||
{
|
||||
InputHandler hook_handler{{ *m_buffer, Selection{} }};
|
||||
hook_handler.context().set_window(*this);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef window_hh_INCLUDED
|
||||
#define window_hh_INCLUDED
|
||||
|
||||
#include "alias_registry.hh"
|
||||
#include "completion.hh"
|
||||
#include "display_buffer.hh"
|
||||
#include "highlighter_group.hh"
|
||||
|
@ -44,6 +45,8 @@ public:
|
|||
const HookManager& hooks() const { return m_hooks; }
|
||||
KeymapManager& keymaps() { return m_keymaps; }
|
||||
const KeymapManager& keymaps() const { return m_keymaps; }
|
||||
AliasRegistry& aliases() { return m_aliases; }
|
||||
const AliasRegistry& aliases() const { return m_aliases; }
|
||||
|
||||
Buffer& buffer() const { return *m_buffer; }
|
||||
|
||||
|
@ -67,6 +70,7 @@ private:
|
|||
HookManager m_hooks;
|
||||
OptionManager m_options;
|
||||
KeymapManager m_keymaps;
|
||||
AliasRegistry m_aliases;
|
||||
|
||||
HighlighterGroup m_highlighters;
|
||||
HighlighterGroup m_builtin_highlighters;
|
||||
|
|
Loading…
Reference in New Issue
Block a user