Add scoped aliases

aliases are now stored in window, buffer, or globally.
This commit is contained in:
Maxime Coste 2014-10-29 23:22:54 +00:00
parent 502de87697
commit 185b980718
14 changed files with 197 additions and 41 deletions

View File

@ -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
View 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
View 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

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;