diff --git a/docs/Core.rst b/docs/Core.rst index 1c24fd825..2c5d1168f 100644 --- a/docs/Core.rst +++ b/docs/Core.rst @@ -106,6 +106,29 @@ of DFhack, rather than plugins or scripts. .. _cls: +alias +----- +The ``alias`` command allows configuring aliases to other DFHack commands. +Aliases are resolved immediately after built-in commands, which means that an +alias cannot override a built-in command, but can override a command implemented +by a plugin or script. + +Usage: + +:``alias list``: lists all configured aliases +:``alias add [arguments...]``: adds an alias +:``alias replace [arguments...]``: replaces an existing + alias with a new command, or adds the alias if it does not already exist +:``alias delete ``: removes the specified alias + +Aliases can be given additional arguments when created and invoked, which will +be passed to the underlying command in order. An example with `devel/print-args`:: + + [DFHack]# alias add pargs devel/print-args example + [DFHack]# pargs text + example + text + cls --- Clear the terminal. Does not delete command history. diff --git a/library/Core.cpp b/library/Core.cpp index 83b77f7d0..484301c3d 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -603,6 +603,7 @@ string getBuiltinCommand(std::string cmd) cmd == "disable" || cmd == "plug" || cmd == "keybinding" || + cmd == "alias" || cmd == "fpause" || cmd == "cls" || cmd == "die" || @@ -650,6 +651,7 @@ void ls_helper(color_ostream &con, const PluginCommand &pcmd) command_result Core::runCommand(color_ostream &con, const std::string &first_, vector &parts) { std::string first = first_; + command_result res; if (!first.empty()) { if(first.find('\\') != std::string::npos) @@ -787,8 +789,6 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v if(parts.size()) { - command_result res = CR_OK; - for (size_t i = 0; i < parts.size(); i++) { std::string part = parts[i]; @@ -994,6 +994,10 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v con << " (aliased to " << builtin_cmd << ")"; con << std::endl; } + else if (IsAlias(parts[0])) + { + con << " is an alias: " << GetAliasCommand(parts[0]) << std::endl; + } else if (plug) { con << " is a command implemented by the plugin " << plug->getName() << std::endl; @@ -1063,6 +1067,42 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v << Gui::getFocusString(Core::getTopViewscreen()) << endl; } } + else if (builtin == "alias") + { + if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace")) + { + const string &name = parts[1]; + vector cmd(parts.begin() + 2, parts.end()); + if (!AddAlias(name, cmd, parts[0] == "replace")) + { + con.printerr("Could not add alias %s - already exists\n", name.c_str()); + return CR_FAILURE; + } + } + else if (parts.size() >= 2 && (parts[0] == "delete" || parts[0] == "clear")) + { + if (!RemoveAlias(parts[1])) + { + con.printerr("Could not remove alias %s\n", parts[1].c_str()); + return CR_FAILURE; + } + } + else if (parts.size() >= 1 && (parts[0] == "list")) + { + auto aliases = ListAliases(); + for (auto p : aliases) + { + con << p.first << ": " << join_strings(" ", p.second) << endl; + } + } + else + { + con << "Usage: " << endl + << " alias add|replace " << endl + << " alias delete|clear " << endl + << " alias list" << endl; + } + } else if (builtin == "fpause") { World::SetPauseState(true); @@ -1218,9 +1258,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v return CR_WRONG_USAGE; } } + else if (RunAlias(con, first, parts, res)) + { + return res; + } else { - command_result res = plug_mgr->InvokeCommand(con, first, parts); + res = plug_mgr->InvokeCommand(con, first, parts); if(res == CR_NOT_IMPLEMENTED) { string completed; @@ -1405,7 +1449,8 @@ Core::Core() hotkey_set = false; HotkeyMutex = 0; HotkeyCond = 0; - misc_data_mutex=0; + alias_mutex = 0; + misc_data_mutex = 0; last_world_data_ptr = NULL; last_local_map_ptr = NULL; last_pause_state = false; @@ -1526,6 +1571,7 @@ bool Core::Init() // Init global object pointers df::global::InitGlobals(); + alias_mutex = new recursive_mutex(); cerr << "Initializing Console.\n"; // init the console. @@ -2576,6 +2622,64 @@ std::vector Core::ListKeyBindings(std::string keyspec) return rv; } +bool Core::AddAlias(const std::string &name, const std::vector &command, bool replace) +{ + tthread::lock_guard lock(*alias_mutex); + if (!IsAlias(name) || replace) + { + aliases[name] = command; + return true; + } + return false; +} + +bool Core::RemoveAlias(const std::string &name) +{ + tthread::lock_guard lock(*alias_mutex); + if (IsAlias(name)) + { + aliases.erase(name); + return true; + } + return false; +} + +bool Core::IsAlias(const std::string &name) +{ + tthread::lock_guard lock(*alias_mutex); + return aliases.find(name) != aliases.end(); +} + +bool Core::RunAlias(color_ostream &out, const std::string &name, + const std::vector ¶meters, command_result &result) +{ + tthread::lock_guard lock(*alias_mutex); + if (!IsAlias(name)) + { + return false; + } + + const string &first = aliases[name][0]; + vector parts(aliases[name].begin() + 1, aliases[name].end()); + parts.insert(parts.end(), parameters.begin(), parameters.end()); + result = runCommand(out, first, parts); + return true; +} + +std::map> Core::ListAliases() +{ + tthread::lock_guard lock(*alias_mutex); + return aliases; +} + +std::string Core::GetAliasCommand(const std::string &name, const std::string &default_) +{ + tthread::lock_guard lock(*alias_mutex); + if (IsAlias(name)) + return join_strings(" ", aliases[name]); + else + return default_; +} ///////////////// // ClassNameCheck diff --git a/library/include/Core.h b/library/include/Core.h index 6bcf9ee8e..fa65645a1 100644 --- a/library/include/Core.h +++ b/library/include/Core.h @@ -170,6 +170,14 @@ namespace DFHack std::vector ListKeyBindings(std::string keyspec); int8_t getModstate() { return modstate; } + bool AddAlias(const std::string &name, const std::vector &command, bool replace = false); + bool RemoveAlias(const std::string &name); + bool IsAlias(const std::string &name); + bool RunAlias(color_ostream &out, const std::string &name, + const std::vector ¶meters, command_result &result); + std::map> ListAliases(); + std::string GetAliasCommand(const std::string &name, const std::string &default_ = ""); + std::string getHackPath(); bool isWorldLoaded() { return (last_world_data_ptr != NULL); } @@ -256,6 +264,9 @@ namespace DFHack tthread::mutex * HotkeyMutex; tthread::condition_variable * HotkeyCond; + std::map> aliases; + tthread::recursive_mutex * alias_mutex; + bool SelectHotkey(int key, int modifiers); // for state change tracking