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.find(chr) != string::npos) {
282static string addEscaping(
const string& str,
bool quote,
bool finished)
284 if (str.empty() && finished) {
287 string result = escapeChars(str,
"$[]");
289 result.insert(result.begin(),
'"');
294 result = escapeChars(result,
" ");
308 return interpreter.
execute(command);
318 "While executing ", script,
": ", e.getMessage());
331 string_view pre = command.substr(0, last);
332 string_view post = command.substr(last);
335 vector<string> originalTokens = split(post,
' ');
336 if (originalTokens.empty()) {
337 originalTokens.emplace_back();
341 auto tokens = removeEscaping(originalTokens,
true);
342 auto oldNum = tokens.size();
344 auto newNum = tokens.size();
345 bool tokenFinished = oldNum != newNum;
348 string& original = originalTokens.back();
349 if (
const string& completed = tokens[oldNum - 1]; !completed.empty()) {
350 bool quote = !original.empty() && (original[0] ==
'"');
351 original = addEscaping(completed, quote, tokenFinished);
354 assert(newNum == (oldNum + 1));
355 assert(tokens.back().empty());
356 originalTokens.emplace_back();
360 return strCat(pre,
join(originalTokens,
' '));
365 if (tokens.empty()) {
369 if (tokens.size() == 1) {
370 string_view cmd = tokens[0];
371 string_view leadingNs;
373 if (cmd.starts_with(
"::")) {
374 cmd.remove_prefix(2);
378 auto p1 = cmd.rfind(
"::");
379 string_view ns = (p1 == string_view::npos) ? cmd : cmd.
substr(0, p1 + 2);
383 vector<string> names2;
384 names2.reserve(names.size());
385 for (string_view n1 : names) {
387 if (n1.starts_with(
"::")) n1.remove_prefix(2);
389 if (!n1.starts_with(ns))
continue;
391 string_view n2 = n1.substr(ns.size());
393 auto p2 = n2.find(
"::");
394 auto n3 = (p2 == string_view::npos) ? n1 : n1.
substr(0, ns.
size() + p2 + 2);
396 names2.push_back(
strCat(leadingNs,
n3));
400 string_view cmd = tokens.front();
401 if (cmd.starts_with(
"::")) cmd.remove_prefix(2);
404 (*v)->tabCompletion(tokens);
406 TclObject command =
makeTclList(
"openmsx::tabcompletion");
407 command.addListElements(tokens);
409 TclObject list = command.executeCommand(interpreter);
410 bool sensitive =
true;
411 auto begin = list.begin();
412 auto end = list.end();
414 auto it2 =
end; --it2;
416 if (back ==
"false") {
419 }
else if (back ==
"true") {
425 }
catch (CommandException& e) {
427 "Error while executing tab-completion "
428 "proc: ",
e.getMessage());
437GlobalCommandController::HelpCmd::HelpCmd(GlobalCommandController& controller_)
438 : Command(controller_,
"help")
442void GlobalCommandController::HelpCmd::execute(
443 std::span<const TclObject> tokens, TclObject& result)
445 auto& controller =
OUTER(GlobalCommandController, helpCmd);
446 switch (tokens.size()) {
449 "Use 'help [command]' to get help for a specific command\n"
450 "The following commands exist:\n";
451 auto cmds = concat<string_view>(
453 getInterpreter().execute(
"openmsx::all_command_names_with_help"));
455 return c.find(
"::") != std::string_view::npos; }),
458 for (
auto& line : formatListInColumns(cmds)) {
465 if (
const auto* v =
lookup(controller.commandCompleters, tokens[1].getString())) {
466 result = (*v)->help(tokens.subspan(1));
469 command.addListElements(
view::drop(tokens, 1));
470 result = command.executeCommand(getInterpreter());
477string GlobalCommandController::HelpCmd::help(std::span<const TclObject> )
const
479 return "prints help information for commands\n";
482void GlobalCommandController::HelpCmd::tabCompletion(vector<string>& tokens)
const
484 string front = std::move(tokens.front());
485 tokens.erase(
begin(tokens));
486 auto& controller =
OUTER(GlobalCommandController, helpCmd);
487 controller.tabCompletion(tokens);
488 tokens.insert(
begin(tokens), std::move(front));
494GlobalCommandController::TabCompletionCmd::TabCompletionCmd(
495 GlobalCommandController& controller_)
496 : Command(controller_,
"tabcompletion")
500void GlobalCommandController::TabCompletionCmd::execute(
501 std::span<const TclObject> tokens, TclObject& result)
503 checkNumArgs(tokens, 2,
"commandstring");
505 auto& controller =
OUTER(GlobalCommandController, tabCompletionCmd);
506 result = controller.tabCompletion(tokens[1].getString());
509string GlobalCommandController::TabCompletionCmd::help(std::span<const TclObject> )
const
511 return "!!! This command will change in the future !!!\n"
512 "Tries to completes the given argument as if it were typed in "
513 "the console. This command is only useful to provide "
514 "tabcompletion to external console interfaces.";
520GlobalCommandController::UpdateCmd::UpdateCmd(CommandController& commandController_)
521 : Command(commandController_,
"openmsx_update")
528 auto i :
xrange(updateStr.size())) {
529 if (updateStr[i] == name) {
533 throw CommandException(
"No such update type: ", name.getString());
536CliConnection& GlobalCommandController::UpdateCmd::getConnection()
538 const auto& controller =
OUTER(GlobalCommandController, updateCmd);
539 if (
auto* c = controller.getConnection()) {
542 throw CommandException(
"This command only makes sense when "
543 "it's used from an external application.");
546void GlobalCommandController::UpdateCmd::execute(
547 std::span<const TclObject> tokens, TclObject& )
549 checkNumArgs(tokens, 3, Prefix{1},
"enable|disable type");
550 if (tokens[1] ==
"enable") {
551 getConnection().setUpdateEnable(getType(tokens[2]),
true);
552 }
else if (tokens[1] ==
"disable") {
553 getConnection().setUpdateEnable(getType(tokens[2]),
false);
559string GlobalCommandController::UpdateCmd::help(std::span<const TclObject> )
const
561 return "Enable or disable update events for external applications. See doc/openmsx-control-xml.txt.";
564void GlobalCommandController::UpdateCmd::tabCompletion(vector<string>& tokens)
const
566 switch (tokens.size()) {
568 using namespace std::literals;
569 static constexpr std::array ops = {
"enable"sv,
"disable"sv};
570 completeString(tokens, ops);
582GlobalCommandController::PlatformInfo::PlatformInfo(InfoCommand& openMSXInfoCommand_)
583 : InfoTopic(openMSXInfoCommand_,
"platform")
587void GlobalCommandController::PlatformInfo::execute(
588 std::span<const TclObject> , TclObject& result)
const
590 result = TARGET_PLATFORM;
593string GlobalCommandController::PlatformInfo::help(std::span<const TclObject> )
const
595 return "Prints openMSX platform.";
600GlobalCommandController::VersionInfo::VersionInfo(InfoCommand& openMSXInfoCommand_)
601 : InfoTopic(openMSXInfoCommand_,
"version")
605void GlobalCommandController::VersionInfo::execute(
606 std::span<const TclObject> , TclObject& result)
const
611string GlobalCommandController::VersionInfo::help(std::span<const TclObject> )
const
613 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 remove_if(ForwardRange &&range, UnaryPredicate pred)
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)