replace more Core cpp code with calls to helpdb

also document devel/dump-rpc builtin
develop
myk002 2022-07-14 13:19:30 -07:00
parent 185f49976c
commit e926e1116e
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
5 changed files with 547 additions and 608 deletions

@ -255,6 +255,17 @@ type
---- ----
``type command`` shows where ``command`` is implemented. ``type command`` shows where ``command`` is implemented.
.. _devel/dump-rpc:
devel/dump-rpc
--------------
Writes RPC endpoint information to the specified file.
Usage::
devel/dump-rpc FILENAME
Other Commands Other Commands
-------------- --------------
The following commands are *not* built-in, but offer similarly useful functions. The following commands are *not* built-in, but offer similarly useful functions.

@ -277,65 +277,6 @@ static string dfhack_version_desc()
return s.str(); return s.str();
} }
static std::string getScriptHelp(std::string path, std::string helpprefix)
{
ifstream script(path.c_str());
if (script.good())
{
std::string help;
if (getline(script, help) &&
help.substr(0,helpprefix.length()) == helpprefix)
{
help = help.substr(helpprefix.length());
while (help.size() && help[0] == ' ')
help = help.substr(1);
return help;
}
}
return "No help available.";
}
static void listScripts(PluginManager *plug_mgr, std::map<string,string> &pset, std::string path, bool all, std::string prefix = "")
{
std::vector<string> files;
Filesystem::listdir(path, files);
path += '/';
for (size_t i = 0; i < files.size(); i++)
{
if (hasEnding(files[i], ".lua"))
{
string help = getScriptHelp(path + files[i], "--");
string key = prefix + files[i].substr(0, files[i].size()-4);
if (pset.find(key) == pset.end()) {
pset[key] = help;
}
}
else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && hasEnding(files[i], ".rb"))
{
string help = getScriptHelp(path + files[i], "#");
string key = prefix + files[i].substr(0, files[i].size()-3);
if (pset.find(key) == pset.end()) {
pset[key] = help;
}
}
else if (all && !files[i].empty() && files[i][0] != '.' && files[i] != "internal" && files[i] != "test")
{
listScripts(plug_mgr, pset, path+files[i]+"/", all, prefix+files[i]+"/");
}
}
}
static void listAllScripts(map<string, string> &pset, bool all)
{
vector<string> paths;
Core::getInstance().getScriptPaths(&paths);
for (string path : paths)
listScripts(Core::getInstance().getPluginManager(), pset, path, all);
}
namespace { namespace {
struct ScriptArgs { struct ScriptArgs {
const string *pcmd; const string *pcmd;
@ -442,60 +383,52 @@ command_result Core::runCommand(color_ostream &out, const std::string &command)
return CR_NOT_IMPLEMENTED; return CR_NOT_IMPLEMENTED;
} }
// List of built in commands bool is_builtin(color_ostream &con, const string &command) {
static const std::set<std::string> built_in_commands = { CoreSuspender suspend;
"ls" , auto L = Lua::Core::State;
"help" , Lua::StackUnwinder top(L);
"tags" ,
"type" ,
"load" ,
"unload" ,
"reload" ,
"enable" ,
"disable" ,
"plug" ,
"keybinding" ,
"alias" ,
"fpause" ,
"cls" ,
"die" ,
"kill-lua" ,
"script" ,
"hide" ,
"show" ,
"sc-script"
};
static bool try_autocomplete(color_ostream &con, const std::string &first, std::string &completed) if (!lua_checkstack(L, 1) ||
{ !Lua::PushModulePublic(con, L, "helpdb", "is_builtin")) {
std::vector<std::string> possible; con.printerr("Failed to load helpdb Lua code\n");
return false;
}
// Check for possible built in commands to autocomplete first Lua::Push(L, command);
for (auto const &command : built_in_commands)
if (command.substr(0, first.size()) == first)
possible.push_back(command);
auto plug_mgr = Core::getInstance().getPluginManager(); if (!Lua::SafeCall(con, L, 1, 1)) {
for (auto it = plug_mgr->begin(); it != plug_mgr->end(); ++it) con.printerr("Failed Lua call to helpdb.is_builtin.\n");
{ return false;
const Plugin * plug = it->second; }
for (size_t j = 0; j < plug->size(); j++)
{ return lua_toboolean(L, -1);
const PluginCommand &pcmd = (*plug)[j]; }
if (pcmd.isHotkeyCommand())
continue; void get_commands(color_ostream &con, vector<string> &commands) {
if (pcmd.name.substr(0, first.size()) == first) CoreSuspender suspend;
possible.push_back(pcmd.name); auto L = Lua::Core::State;
Lua::StackUnwinder top(L);
if (!lua_checkstack(L, 1) ||
!Lua::PushModulePublic(con, L, "helpdb", "get_commands")) {
con.printerr("Failed to load helpdb Lua code\n");
return;
} }
if (!Lua::SafeCall(con, L, 0, 1)) {
con.printerr("Failed Lua call to helpdb.get_commands.\n");
} }
bool all = (first.find('/') != std::string::npos); Lua::GetVector(L, commands);
}
std::map<string, string> scripts; static bool try_autocomplete(color_ostream &con, const std::string &first, std::string &completed)
listAllScripts(scripts, all); {
for (auto iter = scripts.begin(); iter != scripts.end(); ++iter) std::vector<std::string> commands, possible;
if (iter->first.substr(0, first.size()) == first)
possible.push_back(iter->first); for (auto &command : commands)
if (command.substr(0, first.size()) == first)
possible.push_back(command);
if (possible.size() == 1) if (possible.size() == 1)
{ {
@ -651,30 +584,6 @@ static std::string sc_event_name (state_change_event id) {
return "SC_UNKNOWN"; return "SC_UNKNOWN";
} }
string getBuiltinCommand(std::string cmd)
{
std::string builtin = "";
// Check our list of builtin commands from the header
if (built_in_commands.count(cmd))
builtin = cmd;
// Check for some common aliases for built in commands
else if (cmd == "?" || cmd == "man")
builtin = "help";
else if (cmd == "dir")
builtin = "ls";
else if (cmd == "clear")
builtin = "cls";
else if (cmd == "devel/dump-rpc")
builtin = "devel/dump-rpc";
return builtin;
}
void help_helper(color_ostream &con, const string &entry_name) { void help_helper(color_ostream &con, const string &entry_name) {
CoreSuspender suspend; CoreSuspender suspend;
auto L = Lua::Core::State; auto L = Lua::Core::State;
@ -753,10 +662,10 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_FAILURE; return CR_FAILURE;
} }
command_result res; if (first.empty())
if (!first.empty()) return CR_NOT_IMPLEMENTED;
{
if(first.find('\\') != std::string::npos) if (first.find('\\') != std::string::npos)
{ {
con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", first.c_str()); con.printerr("Replacing backslashes with forward slashes in \"%s\"\n", first.c_str());
for (size_t i = 0; i < first.size(); i++) for (size_t i = 0; i < first.size(); i++)
@ -767,8 +676,8 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
// let's see what we actually got // let's see what we actually got
string builtin = getBuiltinCommand(first); command_result res;
if (builtin == "help") if (first == "help" || first == "man" || first == "?")
{ {
if(!parts.size()) if(!parts.size())
{ {
@ -803,15 +712,15 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
help_helper(con, parts[0]); help_helper(con, parts[0]);
} }
} }
else if (builtin == "tags") else if (first == "tags")
{ {
tags_helper(con); tags_helper(con);
} }
else if (builtin == "load" || builtin == "unload" || builtin == "reload") else if (first == "load" || first == "unload" || first == "reload")
{ {
bool all = false; bool all = false;
bool load = (builtin == "load"); bool load = (first == "load");
bool unload = (builtin == "unload"); bool unload = (first == "unload");
if (parts.size()) if (parts.size())
{ {
for (auto p = parts.begin(); p != parts.end(); p++) for (auto p = parts.begin(); p != parts.end(); p++)
@ -845,12 +754,12 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
} }
else else
con.printerr("%s: no arguments\n", builtin.c_str()); con.printerr("%s: no arguments\n", first.c_str());
} }
else if( builtin == "enable" || builtin == "disable" ) else if( first == "enable" || first == "disable" )
{ {
CoreSuspender suspend; CoreSuspender suspend;
bool enable = (builtin == "enable"); bool enable = (first == "enable");
if(parts.size()) if(parts.size())
{ {
@ -885,14 +794,14 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
else if (!plug->can_set_enabled()) else if (!plug->can_set_enabled())
{ {
res = CR_NOT_IMPLEMENTED; res = CR_NOT_IMPLEMENTED;
con.printerr("Cannot %s plugin: %s\n", builtin.c_str(), part.c_str()); con.printerr("Cannot %s plugin: %s\n", first.c_str(), part.c_str());
} }
else else
{ {
res = plug->set_enabled(con, enable); res = plug->set_enabled(con, enable);
if (res != CR_OK || plug->is_enabled() != enable) if (res != CR_OK || plug->is_enabled() != enable)
con.printerr("Could not %s plugin: %s\n", builtin.c_str(), part.c_str()); con.printerr("Could not %s plugin: %s\n", first.c_str(), part.c_str());
} }
} }
@ -914,11 +823,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
} }
} }
else if (builtin == "ls" || builtin == "dir") else if (first == "ls" || first == "dir")
{ {
ls_helper(con, parts); ls_helper(con, parts);
} }
else if (builtin == "plug") else if (first == "plug")
{ {
const char *header_format = "%30s %10s %4s %8s\n"; const char *header_format = "%30s %10s %4s %8s\n";
const char *row_format = "%30s %10s %4i %8s\n"; const char *row_format = "%30s %10s %4i %8s\n";
@ -964,7 +873,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
con.color(COLOR_RESET); con.color(COLOR_RESET);
} }
} }
else if (builtin == "type") else if (first == "type")
{ {
if (!parts.size()) if (!parts.size())
{ {
@ -972,15 +881,13 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
} }
con << parts[0]; con << parts[0];
string builtin_cmd = getBuiltinCommand(parts[0]); bool builtin = is_builtin(con, parts[0]);
string lua_path = findScript(parts[0] + ".lua"); string lua_path = findScript(parts[0] + ".lua");
string ruby_path = findScript(parts[0] + ".rb"); string ruby_path = findScript(parts[0] + ".rb");
Plugin *plug = plug_mgr->getPluginByCommand(parts[0]); Plugin *plug = plug_mgr->getPluginByCommand(parts[0]);
if (builtin_cmd.size()) if (builtin)
{ {
con << " is a built-in command"; con << " is a built-in command";
if (builtin_cmd != parts[0])
con << " (aliased to " << builtin_cmd << ")";
con << std::endl; con << std::endl;
} }
else if (IsAlias(parts[0])) else if (IsAlias(parts[0]))
@ -1008,7 +915,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_FAILURE; return CR_FAILURE;
} }
} }
else if (builtin == "keybinding") else if (first == "keybinding")
{ {
if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add")) if (parts.size() >= 3 && (parts[0] == "set" || parts[0] == "add"))
{ {
@ -1056,7 +963,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
<< Gui::getFocusString(Core::getTopViewscreen()) << endl; << Gui::getFocusString(Core::getTopViewscreen()) << endl;
} }
} }
else if (builtin == "alias") else if (first == "alias")
{ {
if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace")) if (parts.size() >= 3 && (parts[0] == "add" || parts[0] == "replace"))
{ {
@ -1092,7 +999,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
<< " alias list" << endl; << " alias list" << endl;
} }
} }
else if (builtin == "fpause") else if (first == "fpause")
{ {
World::SetPauseState(true); World::SetPauseState(true);
if (auto scr = Gui::getViewscreenByType<df::viewscreen_new_regionst>()) if (auto scr = Gui::getViewscreenByType<df::viewscreen_new_regionst>())
@ -1101,7 +1008,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
con.print("The game was forced to pause!\n"); con.print("The game was forced to pause!\n");
} }
else if (builtin == "cls") else if (first == "cls" || first == "clear")
{ {
if (con.is_console()) if (con.is_console())
((Console&)con).clear(); ((Console&)con).clear();
@ -1111,11 +1018,11 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_NEEDS_CONSOLE; return CR_NEEDS_CONSOLE;
} }
} }
else if (builtin == "die") else if (first == "die")
{ {
std::_Exit(666); std::_Exit(666);
} }
else if (builtin == "kill-lua") else if (first == "kill-lua")
{ {
bool force = false; bool force = false;
for (auto it = parts.begin(); it != parts.end(); ++it) for (auto it = parts.begin(); it != parts.end(); ++it)
@ -1132,7 +1039,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
" profiling and coverage monitoring.\n"); " profiling and coverage monitoring.\n");
} }
} }
else if (builtin == "script") else if (first == "script")
{ {
if(parts.size() == 1) if(parts.size() == 1)
{ {
@ -1145,7 +1052,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
} }
} }
else if (builtin=="hide") else if (first == "hide")
{ {
if (!getConsole().hide()) if (!getConsole().hide())
{ {
@ -1154,7 +1061,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
return CR_OK; return CR_OK;
} }
else if (builtin=="show") else if (first == "show")
{ {
if (!getConsole().show()) if (!getConsole().show())
{ {
@ -1163,7 +1070,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
return CR_OK; return CR_OK;
} }
else if (builtin == "sc-script") else if (first == "sc-script")
{ {
if (parts.empty() || parts[0] == "help" || parts[0] == "?") if (parts.empty() || parts[0] == "help" || parts[0] == "?")
{ {
@ -1257,7 +1164,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_WRONG_USAGE; return CR_WRONG_USAGE;
} }
} }
else if (builtin == "devel/dump-rpc") else if (first == "devel/dump-rpc")
{ {
if (parts.size() == 1) if (parts.size() == 1)
{ {
@ -1331,9 +1238,6 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
} }
return CR_OK; return CR_OK;
}
return CR_NOT_IMPLEMENTED;
} }
bool Core::loadScriptFile(color_ostream &out, string fname, bool silent) bool Core::loadScriptFile(color_ostream &out, string fname, bool silent)

@ -148,6 +148,16 @@ void Lua::Push(lua_State *state, df::coord2d pos)
lua_setfield(state, -2, "y"); lua_setfield(state, -2, "y");
} }
void GetVector(lua_State *state, std::vector<std::string> &pvec)
{
lua_pushnil(state); // first key
while (lua_next(state, 1) != 0)
{
pvec.push_back(lua_tostring(state, -1));
lua_pop(state, 1); // remove value, leave key
}
}
int Lua::PushPosXYZ(lua_State *state, df::coord pos) int Lua::PushPosXYZ(lua_State *state, df::coord pos)
{ {
if (!pos.isValid()) if (!pos.isValid())

@ -339,6 +339,8 @@ namespace DFHack {namespace Lua {
} }
} }
DFHACK_EXPORT void GetVector(lua_State *state, std::vector<std::string> &pvec);
DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos); DFHACK_EXPORT int PushPosXYZ(lua_State *state, df::coord pos);
DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos); DFHACK_EXPORT int PushPosXY(lua_State *state, df::coord2d pos);

@ -250,6 +250,7 @@ local BUILTINS = {
alias='Configure helper aliases for other DFHack commands.', alias='Configure helper aliases for other DFHack commands.',
cls='Clear the console screen.', cls='Clear the console screen.',
clear='Clear the console screen.', clear='Clear the console screen.',
['devel/dump-rpc']='Write RPC endpoint information to a file.',
die='Force DF to close immediately, without saving.', die='Force DF to close immediately, without saving.',
enable='Enable a plugin or persistent script.', enable='Enable a plugin or persistent script.',
disable='Disable a plugin or persistent script.', disable='Disable a plugin or persistent script.',
@ -575,15 +576,26 @@ function search_entries(include, exclude)
ensure_db() ensure_db()
include = normalize_filter(include) include = normalize_filter(include)
exclude = normalize_filter(exclude) exclude = normalize_filter(exclude)
local commands = {} local entries = {}
for command in pairs(db) do for entry in pairs(db) do
if (not include or matches(command, include)) and if (not include or matches(entry, include)) and
(not exclude or not matches(command, exclude)) then (not exclude or not matches(entry, exclude)) then
table.insert(commands, command) table.insert(entries, entry)
end end
end end
table.sort(commands, sort_by_basename) table.sort(entries, sort_by_basename)
return commands return entries
end
-- returns a list of all commands. used by Core's autocomplete functionality.
function get_commands()
local include = {types={ENTRY_TYPES.COMMAND}}
return search_entries(include)
end
function is_builtin(command)
ensure_db()
return db[command] and db[command].entry_types[ENTRY_TYPES.BUILTIN]
end end
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
@ -633,7 +645,7 @@ function list_entries(skip_tags, include, exclude)
end end
end end
if #entries == 0 then if #entries == 0 then
print('no entries found.') print('No matches.')
end end
end end