29using std::string_view;
39 , openMSXInfoCommand(*this,
"openmsx_info")
40 , hotKey(reactor.getRTScheduler(), *this, eventDistributor)
41 , settingsConfig(*this, hotKey, reactor.getShortcuts())
43 , tabCompletionCmd(*this)
45 , platformInfo(getOpenMSXInfoCommand())
46 , versionInfo (getOpenMSXInfoCommand())
47 , romInfoTopic(getOpenMSXInfoCommand())
58 assert(commands.empty());
65 auto it = proxyCommandMap.find(name);
66 if (it ==
end(proxyCommandMap)) {
67 it = proxyCommandMap.emplace_noDuplicateCheck(
68 0, std::make_unique<ProxyCmd>(reactor, name));
75 auto it = proxyCommandMap.find(name);
76 assert(it !=
end(proxyCommandMap));
77 assert(it->first > 0);
80 proxyCommandMap.erase(it);
84GlobalCommandController::ProxySettings::iterator
85GlobalCommandController::findProxySetting(string_view name)
88 [](
auto& v) {
return v.first->getFullName(); });
93 const auto& name =
setting.getBaseNameObj();
94 auto it = findProxySetting(name.getString());
95 if (it ==
end(proxySettings)) {
97 auto proxy = std::make_unique<ProxySetting>(reactor, name);
100 proxySettings.emplace_back(std::move(proxy), 1);
109 auto it = findProxySetting(
setting.getBaseName());
110 assert(it !=
end(proxySettings));
113 if (it->second == 0) {
114 auto& proxy = *it->first;
135 assert(!commands.contains(str));
136 commands.emplace_noDuplicateCheck(str, &command);
142 Command& command, string_view str)
146 assert(commands.contains(str));
147 assert(commands[str] == &command);
156 if (str.starts_with(
"::")) str.remove_prefix(2);
164 if (str.starts_with(
"::")) str.remove_prefix(2);
182static vector<string> split(string_view str,
const char delimiter)
184 vector<string> tokens;
186 enum ParseState {Alpha, BackSlash, Quote};
187 ParseState state = Alpha;
189 for (
auto chr : str) {
192 if (tokens.empty()) {
193 tokens.emplace_back();
195 if (chr == delimiter) {
197 tokens.emplace_back();
199 tokens.back() += chr;
202 }
else if (chr ==
'"') {
208 tokens.back() += chr;
214 tokens.back() += chr;
222static string removeEscaping(
const string& str)
224 enum ParseState {Alpha, BackSlash, Quote};
225 ParseState state = Alpha;
228 for (
auto chr : str) {
233 }
else if (chr ==
'"') {
255static vector<string> removeEscaping(std::span<const string> input,
bool keepLastIfEmpty)
257 vector<string> result;
258 for (
const auto& s : input) {
260 result.push_back(removeEscaping(s));
263 if (keepLastIfEmpty && (input.empty() || input.back().empty())) {
264 result.emplace_back();
269static string escapeChars(
const string& str, string_view chars)
272 for (
auto chr : str) {
273 if (chars.contains(chr)) {
281static string addEscaping(
const string& str,
bool quote,
bool finished)
283 if (str.empty() && finished) {
286 string result = escapeChars(str,
"$[]");
288 result.insert(result.begin(),
'"');
293 result = escapeChars(result,
" ");
307 return interpreter.
execute(command);
317 "While executing ", script,
": ", e.getMessage());
330 string_view pre = command.substr(0, last);
331 string_view post = command.substr(last);
334 vector<string> originalTokens = split(post,
' ');
335 if (originalTokens.empty()) {
336 originalTokens.emplace_back();
340 auto tokens = removeEscaping(originalTokens,
true);
341 auto oldNum = tokens.size();
343 auto newNum = tokens.size();
344 bool tokenFinished = oldNum != newNum;
347 string& original = originalTokens.back();
348 if (
const string& completed = tokens[oldNum - 1]; !completed.empty()) {
349 bool quote = !original.empty() && (original[0] ==
'"');
350 original = addEscaping(completed, quote, tokenFinished);
353 assert(newNum == (oldNum + 1));
354 assert(tokens.back().empty());
355 originalTokens.emplace_back();
359 return strCat(pre,
join(originalTokens,
' '));
364 if (tokens.empty()) {
368 if (tokens.size() == 1) {
369 string_view cmd = tokens[0];
370 string_view leadingNs;
372 if (cmd.starts_with(
"::")) {
373 cmd.remove_prefix(2);
377 auto p1 = cmd.rfind(
"::");
378 string_view ns = (p1 == string_view::npos) ? cmd : cmd.
substr(0, p1 + 2);
382 vector<string> names2;
383 names2.reserve(names.size());
384 for (string_view n1 : names) {
386 if (n1.starts_with(
"::")) n1.remove_prefix(2);
388 if (!n1.starts_with(ns))
continue;
390 string_view n2 = n1.substr(ns.size());
392 auto p2 = n2.find(
"::");
393 auto n3 = (p2 == string_view::npos) ? n1 : n1.
substr(0, ns.
size() + p2 + 2);
395 names2.push_back(
strCat(leadingNs,
n3));
399 string_view cmd = tokens.front();
400 if (cmd.starts_with(
"::")) cmd.remove_prefix(2);
403 (*v)->tabCompletion(tokens);
405 TclObject command =
makeTclList(
"openmsx::tabcompletion");
406 command.addListElements(tokens);
408 TclObject list = command.executeCommand(interpreter);
409 bool sensitive =
true;
410 auto begin = list.begin();
411 auto end = list.end();
413 auto it2 =
end; --it2;
415 if (back ==
"false") {
418 }
else if (back ==
"true") {
424 }
catch (CommandException& e) {
426 "Error while executing tab-completion "
427 "proc: ",
e.getMessage());
436GlobalCommandController::HelpCmd::HelpCmd(GlobalCommandController& controller_)
437 : Command(controller_,
"help")
441void GlobalCommandController::HelpCmd::execute(
442 std::span<const TclObject> tokens, TclObject& result)
444 auto& controller =
OUTER(GlobalCommandController, helpCmd);
445 switch (tokens.size()) {
448 "Use 'help [command]' to get help for a specific command\n"
449 "The following commands exist:\n";
450 auto cmds = concat<string_view>(
452 getInterpreter().execute(
"openmsx::all_command_names_with_help"));
453 std::erase_if(cmds, [](
const auto& c) {
return c.contains(
"::"); });
455 for (
auto& line : formatListInColumns(cmds)) {
462 if (
const auto* v =
lookup(controller.commandCompleters, tokens[1].getString())) {
463 result = (*v)->help(tokens.subspan(1));
466 command.addListElements(
view::drop(tokens, 1));
467 result = command.executeCommand(getInterpreter());
474string GlobalCommandController::HelpCmd::help(std::span<const TclObject> )
const
476 return "prints help information for commands\n";
479void GlobalCommandController::HelpCmd::tabCompletion(vector<string>& tokens)
const
481 string front = std::move(tokens.front());
482 tokens.erase(
begin(tokens));
483 auto& controller =
OUTER(GlobalCommandController, helpCmd);
484 controller.tabCompletion(tokens);
485 tokens.insert(
begin(tokens), std::move(front));
491GlobalCommandController::TabCompletionCmd::TabCompletionCmd(
492 GlobalCommandController& controller_)
493 : Command(controller_,
"tabcompletion")
497void GlobalCommandController::TabCompletionCmd::execute(
498 std::span<const TclObject> tokens, TclObject& result)
500 checkNumArgs(tokens, 2,
"commandstring");
502 auto& controller =
OUTER(GlobalCommandController, tabCompletionCmd);
503 result = controller.tabCompletion(tokens[1].getString());
506string GlobalCommandController::TabCompletionCmd::help(std::span<const TclObject> )
const
508 return "!!! This command will change in the future !!!\n"
509 "Tries to completes the given argument as if it were typed in "
510 "the console. This command is only useful to provide "
511 "tabcompletion to external console interfaces.";
517GlobalCommandController::UpdateCmd::UpdateCmd(CommandController& commandController_)
518 : Command(commandController_,
"openmsx_update")
525 auto i :
xrange(updateStr.size())) {
526 if (updateStr[i] == name) {
530 throw CommandException(
"No such update type: ", name.getString());
533CliConnection& GlobalCommandController::UpdateCmd::getConnection()
535 const auto& controller =
OUTER(GlobalCommandController, updateCmd);
536 if (
auto* c = controller.getConnection()) {
539 throw CommandException(
"This command only makes sense when "
540 "it's used from an external application.");
543void GlobalCommandController::UpdateCmd::execute(
544 std::span<const TclObject> tokens, TclObject& )
546 checkNumArgs(tokens, 3, Prefix{1},
"enable|disable type");
547 if (tokens[1] ==
"enable") {
548 getConnection().setUpdateEnable(getType(tokens[2]),
true);
549 }
else if (tokens[1] ==
"disable") {
550 getConnection().setUpdateEnable(getType(tokens[2]),
false);
556string GlobalCommandController::UpdateCmd::help(std::span<const TclObject> )
const
558 return "Enable or disable update events for external applications. See doc/openmsx-control-xml.txt.";
561void GlobalCommandController::UpdateCmd::tabCompletion(vector<string>& tokens)
const
563 switch (tokens.size()) {
565 using namespace std::literals;
566 static constexpr std::array ops = {
"enable"sv,
"disable"sv};
567 completeString(tokens, ops);
579GlobalCommandController::PlatformInfo::PlatformInfo(InfoCommand& openMSXInfoCommand_)
580 : InfoTopic(openMSXInfoCommand_,
"platform")
584void GlobalCommandController::PlatformInfo::execute(
585 std::span<const TclObject> , TclObject& result)
const
587 result = TARGET_PLATFORM;
590string GlobalCommandController::PlatformInfo::help(std::span<const TclObject> )
const
592 return "Prints openMSX platform.";
597GlobalCommandController::VersionInfo::VersionInfo(InfoCommand& openMSXInfoCommand_)
598 : InfoTopic(openMSXInfoCommand_,
"version")
602void GlobalCommandController::VersionInfo::execute(
603 std::span<const TclObject> , TclObject& result)
const
608string GlobalCommandController::VersionInfo::help(std::span<const TclObject> )
const
610 return "Prints openMSX version.";
Assign new value to some variable and restore the original value when this object goes out of scope.
int getLast() const
Get Start of the last subcommand.
void printWarning(std::string_view message)
static auto getUpdateStrings()
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
~GlobalCommandControllerBase()
hash_map< std::string, CommandCompleter *, XXHasher > commandCompleters
void registerProxySetting(const Setting &setting)
Interpreter & getInterpreter() override
void unregisterProxySetting(const Setting &setting)
GlobalCommandController(EventDistributor &eventDistributor, GlobalCliComm &cliComm, Reactor &reactor)
void registerSetting(Setting &setting) override
TODO.
std::string tabCompletion(std::string_view command)
Complete the given command.
void registerProxyCommand(std::string_view name)
CliComm & getCliComm() override
void unregisterProxyCommand(std::string_view name)
void registerCommand(Command &command, zstring_view str) override
(Un)register a command
void registerCompleter(CommandCompleter &completer, std::string_view str) override
(Un)register a command completer, used to complete build-in Tcl cmds
TclObject executeCommand(zstring_view command, CliConnection *connection=nullptr) override
Execute the given command.
void unregisterSetting(Setting &setting) override
void unregisterCompleter(CommandCompleter &completer, std::string_view str) override
void unregisterCommand(Command &command, std::string_view str) override
bool isComplete(zstring_view command)
Returns true iff the command is complete (all braces, quotes etc.
SettingsManager & getSettingsManager()
void source(const std::string &script)
Executes all defined auto commands.
~GlobalCommandController()
TclObject execute(zstring_view command)
TclObject getCommandNames()
void registerSetting(BaseSetting &variable)
void unregisterCommand(Command &command)
TclParser parse(std::string_view command)
TclObject executeFile(zstring_view filename)
bool isComplete(zstring_view command) const
void unregisterSetting(BaseSetting &variable)
void registerCommand(zstring_view name, Command &command)
Helper class to use files in APIs other than openmsx::File.
const std::string & getFilename() const
Returns path to a local uncompressed version of this file.
Contains the main loop of openMSX.
void unregisterSetting(BaseSetting &setting)
void registerSetting(BaseSetting &setting)
static std::string full()
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
mat3 n3(vec3(1, 0, 3), vec3(4, 5, 6), vec3(7, 8, 9))
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
This file implemented 3 utility functions:
TclObject makeTclList(Args &&... args)
auto find(InputRange &&range, const T &value)
constexpr void sort(RandomAccessRange &&range)
std::string_view substr(std::string_view utf8, std::string_view::size_type first=0, std::string_view::size_type len=std::string_view::npos)
size_t size(std::string_view utf8)
constexpr auto drop(Range &&range, size_t n)
constexpr auto keys(Map &&map)
#define OUTER(type, member)
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
void strAppend(std::string &result, Ts &&...ts)
constexpr auto xrange(T e)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)