openMSX
GlobalCommandController.cc
Go to the documentation of this file.
2 #include "Reactor.hh"
3 #include "Setting.hh"
4 #include "ProxyCommand.hh"
5 #include "ProxySetting.hh"
6 #include "LocalFileReference.hh"
7 #include "GlobalCliComm.hh"
8 #include "CliConnection.hh"
9 #include "CommandException.hh"
10 #include "SettingsManager.hh"
11 #include "TclObject.hh"
12 #include "Version.hh"
13 #include "ScopedAssign.hh"
14 #include "join.hh"
15 #include "outer.hh"
16 #include "ranges.hh"
17 #include "stl.hh"
18 #include "StringOp.hh"
19 #include "view.hh"
20 #include "xrange.hh"
21 #include "build-info.hh"
22 #include <cassert>
23 #include <memory>
24 
25 using std::string;
26 using std::string_view;
27 using std::vector;
28 
29 namespace openmsx {
30 
32  EventDistributor& eventDistributor,
33  GlobalCliComm& cliComm_, Reactor& reactor_)
34  : cliComm(cliComm_)
35  , connection(nullptr)
36  , reactor(reactor_)
37  , openMSXInfoCommand(*this, "openmsx_info")
38  , hotKey(reactor.getRTScheduler(), *this, eventDistributor)
39  , settingsConfig(*this, hotKey)
40  , helpCmd(*this)
41  , tabCompletionCmd(*this)
42  , updateCmd(*this)
43  , platformInfo(getOpenMSXInfoCommand())
44  , versionInfo (getOpenMSXInfoCommand())
45  , romInfoTopic(getOpenMSXInfoCommand())
46 {
47 }
48 
50 
52 {
53  // GlobalCommandController destructor must have run before
54  // we can check this.
55  assert(commands.empty());
56  assert(commandCompleters.empty());
57 }
58 
60 {
61  auto it = proxyCommandMap.find(name);
62  if (it == end(proxyCommandMap)) {
63  it = proxyCommandMap.emplace_noDuplicateCheck(
64  0, std::make_unique<ProxyCmd>(reactor, name));
65  }
66  ++it->first;
67 }
68 
70 {
71  auto it = proxyCommandMap.find(name);
72  assert(it != end(proxyCommandMap));
73  assert(it->first > 0);
74  --it->first;
75  if (it->first == 0) {
76  proxyCommandMap.erase(it);
77  }
78 }
79 
80 GlobalCommandController::ProxySettings::iterator
81 GlobalCommandController::findProxySetting(string_view name)
82 {
83  return ranges::find_if(proxySettings, [&](auto& v) {
84  return v.first->getFullName() == name;
85  });
86 }
87 
89 {
90  const auto& name = setting.getBaseNameObj();
91  auto it = findProxySetting(name.getString());
92  if (it == end(proxySettings)) {
93  // first occurrence
94  auto proxy = std::make_unique<ProxySetting>(reactor, name);
97  proxySettings.emplace_back(std::move(proxy), 1);
98  } else {
99  // was already registered
100  ++(it->second);
101  }
102 }
103 
105 {
106  auto it = findProxySetting(setting.getBaseName());
107  assert(it != end(proxySettings));
108  assert(it->second);
109  --(it->second);
110  if (it->second == 0) {
111  auto& proxy = *it->first;
114  move_pop_back(proxySettings, it);
115  }
116 }
117 
119 {
120  return cliComm;
121 }
122 
124 {
125  return interpreter;
126 }
127 
129  Command& command, const string& str)
130 {
131  assert(!commands.contains(str));
132  commands.emplace_noDuplicateCheck(str, &command);
133  interpreter.registerCommand(str, command);
134 }
135 
137  Command& command, string_view str)
138 {
139  assert(commands.contains(str));
140  assert(commands[str] == &command);
141  commands.erase(str);
142  interpreter.unregisterCommand(command);
143 }
144 
146  CommandCompleter& completer, string_view str)
147 {
148  if (StringOp::startsWith(str, "::")) str.remove_prefix(2); // drop leading ::
149  assert(!commandCompleters.contains(str));
150  commandCompleters.emplace_noDuplicateCheck(str, &completer);
151 }
152 
154  CommandCompleter& completer, string_view str)
155 {
156  if (StringOp::startsWith(str, "::")) str.remove_prefix(2); // drop leading ::
157  assert(commandCompleters.contains(str));
158  assert(commandCompleters[str] == &completer); (void)completer;
159  commandCompleters.erase(str);
160 }
161 
163 {
165  interpreter.registerSetting(setting);
166 }
167 
169 {
170  interpreter.unregisterSetting(setting);
172 }
173 
174 bool GlobalCommandController::hasCommand(string_view command) const
175 {
176  return commands.contains(command);
177 }
178 
179 void GlobalCommandController::split(string_view str, vector<string>& tokens,
180  const char delimiter)
181 {
182  enum ParseState {Alpha, BackSlash, Quote};
183  ParseState state = Alpha;
184 
185  for (auto chr : str) {
186  switch (state) {
187  case Alpha:
188  if (tokens.empty()) {
189  tokens.emplace_back();
190  }
191  if (chr == delimiter) {
192  // token done, start new token
193  tokens.emplace_back();
194  } else {
195  tokens.back() += chr;
196  if (chr == '\\') {
197  state = BackSlash;
198  } else if (chr == '"') {
199  state = Quote;
200  }
201  }
202  break;
203  case Quote:
204  tokens.back() += chr;
205  if (chr == '"') {
206  state = Alpha;
207  }
208  break;
209  case BackSlash:
210  tokens.back() += chr;
211  state = Alpha;
212  break;
213  }
214  }
215 }
216 
217 string GlobalCommandController::removeEscaping(const string& str)
218 {
219  enum ParseState {Alpha, BackSlash, Quote};
220  ParseState state = Alpha;
221 
222  string result;
223  for (auto chr : str) {
224  switch (state) {
225  case Alpha:
226  if (chr == '\\') {
227  state = BackSlash;
228  } else if (chr == '"') {
229  state = Quote;
230  } else {
231  result += chr;
232  }
233  break;
234  case Quote:
235  if (chr == '"') {
236  state = Alpha;
237  } else {
238  result += chr;
239  }
240  break;
241  case BackSlash:
242  result += chr;
243  state = Alpha;
244  break;
245  }
246  }
247  return result;
248 }
249 
250 vector<string> GlobalCommandController::removeEscaping(
251  const vector<string>& input, bool keepLastIfEmpty)
252 {
253  vector<string> result;
254  for (auto& s : input) {
255  if (!s.empty()) {
256  result.push_back(removeEscaping(s));
257  }
258  }
259  if (keepLastIfEmpty && (input.empty() || input.back().empty())) {
260  result.emplace_back();
261  }
262  return result;
263 }
264 
265 static string escapeChars(const string& str, const string& chars)
266 {
267  string result;
268  for (auto chr : str) {
269  if (chars.find(chr) != string::npos) {
270  result += '\\';
271  }
272  result += chr;
273 
274  }
275  return result;
276 }
277 
278 string GlobalCommandController::addEscaping(const string& str, bool quote,
279  bool finished)
280 {
281  if (str.empty() && finished) {
282  quote = true;
283  }
284  string result = escapeChars(str, "$[]");
285  if (quote) {
286  result.insert(result.begin(), '"');
287  if (finished) {
288  result += '"';
289  }
290  } else {
291  result = escapeChars(result, " ");
292  }
293  return result;
294 }
295 
296 bool GlobalCommandController::isComplete(const string& command)
297 {
298  return interpreter.isComplete(command);
299 }
300 
302  const string& command, CliConnection* connection_)
303 {
304  ScopedAssign sa(connection, connection_);
305  return interpreter.execute(command);
306 }
307 
308 void GlobalCommandController::source(const string& script)
309 {
310  try {
311  LocalFileReference file(script);
312  interpreter.executeFile(file.getFilename());
313  } catch (CommandException& e) {
315  "While executing ", script, ": ", e.getMessage());
316  }
317 }
318 
319 string GlobalCommandController::tabCompletion(string_view command)
320 {
321  // split on 'active' command (the command that should actually be
322  // completed). Some examples:
323  // if {[debug rea<tab> <-- should complete the 'debug' command
324  // instead of the 'if' command
325  // bind F6 { cycl<tab> <-- should complete 'cycle' instead of 'bind'
326  TclParser parser = interpreter.parse(command);
327  int last = parser.getLast();
328  string_view pre = command.substr(0, last);
329  string_view post = command.substr(last);
330 
331  // split command string in tokens
332  vector<string> originalTokens;
333  split(post, originalTokens, ' ');
334  if (originalTokens.empty()) {
335  originalTokens.emplace_back();
336  }
337 
338  // complete last token
339  auto tokens = removeEscaping(originalTokens, true);
340  auto oldNum = tokens.size();
341  tabCompletion(tokens);
342  auto newNum = tokens.size();
343  bool tokenFinished = oldNum != newNum;
344 
345  // replace last token
346  string& original = originalTokens.back();
347  string& completed = tokens[oldNum - 1];
348  if (!completed.empty()) {
349  bool quote = !original.empty() && (original[0] == '"');
350  original = addEscaping(completed, quote, tokenFinished);
351  }
352  if (tokenFinished) {
353  assert(newNum == (oldNum + 1));
354  assert(tokens.back().empty());
355  originalTokens.emplace_back();
356  }
357 
358  // rebuild command string
359  return strCat(pre, join(originalTokens, ' '));
360 }
361 
362 void GlobalCommandController::tabCompletion(vector<string>& tokens)
363 {
364  if (tokens.empty()) {
365  // nothing typed yet
366  return;
367  }
368  if (tokens.size() == 1) {
369  string_view cmd = tokens[0];
370  string_view leadingNs;
371  // remove leading ::
372  if (StringOp::startsWith(cmd, "::")) {
373  cmd.remove_prefix(2);
374  leadingNs = "::";
375  }
376  // get current (typed) namespace
377  auto p1 = cmd.rfind("::");
378  string_view ns = (p1 == string_view::npos) ? cmd : cmd.substr(0, p1 + 2);
379 
380  // build a list of all command strings
381  TclObject names = interpreter.getCommandNames();
382  vector<string> names2;
383  names2.reserve(names.size());
384  for (string_view n1 : names) {
385  // remove leading ::
386  if (StringOp::startsWith(n1, "::")) n1.remove_prefix(2);
387  // initial namespace part must match
388  if (!StringOp::startsWith(n1, ns)) continue;
389  // the part following the initial namespace
390  string_view n2 = n1.substr(ns.size());
391  // only keep upto the next namespace portion,
392  auto p2 = n2.find("::");
393  auto n3 = (p2 == string_view::npos) ? n1 : n1.substr(0, ns.size() + p2 + 2);
394  // don't care about adding the same string multiple times
395  names2.push_back(strCat(leadingNs, n3));
396  }
397  Completer::completeString(tokens, names2);
398  } else {
399  string_view cmd = tokens.front();
400  if (StringOp::startsWith(cmd, "::")) cmd.remove_prefix(2); // drop leading ::
401 
402  if (auto v = lookup(commandCompleters, cmd)) {
403  (*v)->tabCompletion(tokens);
404  } else {
405  TclObject command = makeTclList("openmsx::tabcompletion");
406  command.addListElements(tokens);
407  try {
408  TclObject list = command.executeCommand(interpreter);
409  bool sensitive = true;
410  auto begin = list.begin();
411  auto end = list.end();
412  if (begin != end) {
413  auto it2 = end; --it2;
414  auto back = *it2;
415  if (back == "false") {
416  end = it2;
417  sensitive = false;
418  } else if (back == "true") {
419  end = it2;
420  sensitive = true;
421  }
422  }
423  Completer::completeString(tokens, begin, end, sensitive);
424  } catch (CommandException& e) {
425  cliComm.printWarning(
426  "Error while executing tab-completion "
427  "proc: ", e.getMessage());
428  }
429  }
430  }
431 }
432 
433 
434 // Help Command
435 
436 GlobalCommandController::HelpCmd::HelpCmd(GlobalCommandController& controller_)
437  : Command(controller_, "help")
438 {
439 }
440 
441 void GlobalCommandController::HelpCmd::execute(
442  span<const TclObject> tokens, TclObject& result)
443 {
444  auto& controller = OUTER(GlobalCommandController, helpCmd);
445  switch (tokens.size()) {
446  case 1: {
447  string text =
448  "Use 'help [command]' to get help for a specific command\n"
449  "The following commands exist:\n";
450  auto cmds = concat<string_view>(
451  view::keys(controller.commandCompleters),
452  getInterpreter().execute("openmsx::all_command_names_with_help"));
453  cmds.erase(ranges::remove_if(cmds, [](const auto& c) {
454  return c.find("::") != std::string_view::npos; }),
455  cmds.end());
456  ranges::sort(cmds);
457  for (auto& line : formatListInColumns(cmds)) {
458  strAppend(text, line, '\n');
459  }
460  result = text;
461  break;
462  }
463  default: {
464  if (auto v = lookup(controller.commandCompleters, tokens[1].getString())) {
465  auto tokens2 = to_vector(view::transform(
466  view::drop(tokens, 1),
467  [](auto& t) { return string(t.getString()); }));
468  result = (*v)->help(tokens2);
469  } else {
470  TclObject command = makeTclList("openmsx::help");
471  command.addListElements(view::drop(tokens, 1));
472  result = command.executeCommand(getInterpreter());
473  }
474  break;
475  }
476  }
477 }
478 
479 string GlobalCommandController::HelpCmd::help(const vector<string>& /*tokens*/) const
480 {
481  return "prints help information for commands\n";
482 }
483 
484 void GlobalCommandController::HelpCmd::tabCompletion(vector<string>& tokens) const
485 {
486  string front = std::move(tokens.front());
487  tokens.erase(begin(tokens));
488  auto& controller = OUTER(GlobalCommandController, helpCmd);
489  controller.tabCompletion(tokens);
490  tokens.insert(begin(tokens), std::move(front));
491 }
492 
493 
494 // TabCompletionCmd Command
495 
496 GlobalCommandController::TabCompletionCmd::TabCompletionCmd(
497  GlobalCommandController& controller_)
498  : Command(controller_, "tabcompletion")
499 {
500 }
501 
502 void GlobalCommandController::TabCompletionCmd::execute(
503  span<const TclObject> tokens, TclObject& result)
504 {
505  checkNumArgs(tokens, 2, "commandstring");
506  // TODO this prints list of possible completions in the console
507  auto& controller = OUTER(GlobalCommandController, tabCompletionCmd);
508  result = controller.tabCompletion(tokens[1].getString());
509 }
510 
511 string GlobalCommandController::TabCompletionCmd::help(const vector<string>& /*tokens*/) const
512 {
513  return "!!! This command will change in the future !!!\n"
514  "Tries to completes the given argument as if it were typed in "
515  "the console. This command is only useful to provide "
516  "tabcompletion to external console interfaces.";
517 }
518 
519 
520 // class UpdateCmd
521 
522 GlobalCommandController::UpdateCmd::UpdateCmd(CommandController& commandController_)
523  : Command(commandController_, "openmsx_update")
524 {
525 }
526 
527 static GlobalCliComm::UpdateType getType(const TclObject& name)
528 {
529  auto updateStr = CliComm::getUpdateStrings();
530  for (auto i : xrange(updateStr.size())) {
531  if (updateStr[i] == name) {
532  return static_cast<CliComm::UpdateType>(i);
533  }
534  }
535  throw CommandException("No such update type: ", name.getString());
536 }
537 
538 CliConnection& GlobalCommandController::UpdateCmd::getConnection()
539 {
540  auto& controller = OUTER(GlobalCommandController, updateCmd);
541  if (auto* c = controller.getConnection()) {
542  return *c;
543  }
544  throw CommandException("This command only makes sense when "
545  "it's used from an external application.");
546 }
547 
548 void GlobalCommandController::UpdateCmd::execute(
549  span<const TclObject> tokens, TclObject& /*result*/)
550 {
551  checkNumArgs(tokens, 3, "enable|disable type");
552  if (tokens[1] == "enable") {
553  getConnection().setUpdateEnable(getType(tokens[2]), true);
554  } else if (tokens[1] == "disable") {
555  getConnection().setUpdateEnable(getType(tokens[2]), false);
556  } else {
557  throw SyntaxError();
558  }
559 }
560 
561 string GlobalCommandController::UpdateCmd::help(const vector<string>& /*tokens*/) const
562 {
563  static const string helpText = "Enable or disable update events for external applications. See doc/openmsx-control-xml.txt.";
564  return helpText;
565 }
566 
567 void GlobalCommandController::UpdateCmd::tabCompletion(vector<string>& tokens) const
568 {
569  switch (tokens.size()) {
570  case 2: {
571  static constexpr const char* const ops[] = { "enable", "disable" };
572  completeString(tokens, ops);
573  break;
574  }
575  case 3:
576  completeString(tokens, CliComm::getUpdateStrings());
577  break;
578  }
579 }
580 
581 
582 // Platform info
583 
584 GlobalCommandController::PlatformInfo::PlatformInfo(InfoCommand& openMSXInfoCommand_)
585  : InfoTopic(openMSXInfoCommand_, "platform")
586 {
587 }
588 
589 void GlobalCommandController::PlatformInfo::execute(
590  span<const TclObject> /*tokens*/, TclObject& result) const
591 {
592  result = TARGET_PLATFORM;
593 }
594 
595 string GlobalCommandController::PlatformInfo::help(const vector<string>& /*tokens*/) const
596 {
597  return "Prints openMSX platform.";
598 }
599 
600 // Version info
601 
602 GlobalCommandController::VersionInfo::VersionInfo(InfoCommand& openMSXInfoCommand_)
603  : InfoTopic(openMSXInfoCommand_, "version")
604 {
605 }
606 
607 void GlobalCommandController::VersionInfo::execute(
608  span<const TclObject> /*tokens*/, TclObject& result) const
609 {
610  result = Version::full();
611 }
612 
613 string GlobalCommandController::VersionInfo::help(const vector<string>& /*tokens*/) const
614 {
615  return "Prints openMSX version.";
616 }
617 
618 } // namespace openmsx
openmsx::CommandException
Definition: CommandException.hh:8
openmsx::GlobalCommandController::unregisterCompleter
void unregisterCompleter(CommandCompleter &completer, std::string_view str) override
Definition: GlobalCommandController.cc:153
n3
mat3 n3(vec3(1, 0, 3), vec3(4, 5, 6), vec3(7, 8, 9))
openmsx::GlobalCommandController::unregisterProxyCommand
void unregisterProxyCommand(std::string_view name)
Definition: GlobalCommandController.cc:69
TclParser
Definition: TclParser.hh:11
openmsx::GlobalCommandController::getSettingsManager
SettingsManager & getSettingsManager()
Definition: GlobalCommandController.hh:89
openmsx::GlobalCommandControllerBase::commands
hash_map< std::string, Command *, XXHasher > commands
Definition: GlobalCommandController.hh:31
openmsx::GlobalCommandController::getInterpreter
Interpreter & getInterpreter() override
Definition: GlobalCommandController.cc:123
xrange
auto xrange(T e)
Definition: xrange.hh:170
StringOp::startsWith
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:71
openmsx::LocalFileReference
Helper class to use files in APIs other than openmsx::File.
Definition: LocalFileReference.hh:30
openmsx::Interpreter::parse
TclParser parse(std::string_view command)
Definition: Interpreter.cc:447
lookup
const Value * lookup(const hash_map< Key, Value, Hasher, Equal > &map, const Key2 &key)
Definition: hash_map.hh:91
openmsx::GlobalCommandController::unregisterCommand
void unregisterCommand(Command &command, std::string_view str) override
Definition: GlobalCommandController.cc:136
ScopedAssign
Assign new value to some variable and restore the original value when this object goes out of scope.
Definition: ScopedAssign.hh:7
ranges::sort
void sort(RandomAccessRange &&range)
Definition: ranges.hh:35
openmsx::SettingsManager::registerSetting
void registerSetting(BaseSetting &setting)
Definition: SettingsManager.cc:34
Setting.hh
openmsx::GlobalCommandController::getCliComm
CliComm & getCliComm() override
Definition: GlobalCommandController.cc:118
TclObject.hh
openmsx::GlobalCommandController::registerCommand
void registerCommand(Command &command, const std::string &str) override
(Un)register a command
Definition: GlobalCommandController.cc:128
openmsx::CliComm::UpdateType
UpdateType
Definition: CliComm.hh:20
openmsx::BaseSetting::getBaseName
std::string_view getBaseName() const
Definition: Setting.hh:36
openmsx::LocalFileReference::getFilename
const std::string & getFilename() const
Returns path to a local uncompressed version of this file.
Definition: LocalFileReference.cc:74
openmsx::GlobalCommandController::~GlobalCommandController
~GlobalCommandController()
openmsx::GlobalCommandControllerBase::~GlobalCommandControllerBase
~GlobalCommandControllerBase()
Definition: GlobalCommandController.cc:51
openmsx::Interpreter::getCommandNames
TclObject getCommandNames()
Definition: Interpreter.cc:196
openmsx::Interpreter::execute
TclObject execute(const std::string &command)
Definition: Interpreter.cc:206
openmsx::GlobalCommandController::hasCommand
bool hasCommand(std::string_view command) const override
Does a command with this name already exist?
Definition: GlobalCommandController.cc:174
TclParser::getLast
int getLast() const
Get Start of the last subcommand.
Definition: TclParser.hh:32
openmsx::GlobalCommandController::executeCommand
TclObject executeCommand(const std::string &command, CliConnection *connection=nullptr) override
Execute the given command.
Definition: GlobalCommandController.cc:301
openmsx::GlobalCommandController::source
void source(const std::string &script)
Executes all defined auto commands.
Definition: GlobalCommandController.cc:308
t
TclObject t
Definition: TclObject_test.cc:264
openmsx::SettingsManager::unregisterSetting
void unregisterSetting(BaseSetting &setting)
Definition: SettingsManager.cc:40
openmsx::Interpreter::unregisterCommand
void unregisterCommand(Command &command)
Definition: Interpreter.cc:153
ranges.hh
join.hh
GlobalCliComm.hh
openmsx::Interpreter::isComplete
bool isComplete(const std::string &command) const
Definition: Interpreter.cc:201
LocalFileReference.hh
strAppend
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:644
openmsx::Setting
Definition: Setting.hh:119
openmsx::EventDistributor
Definition: EventDistributor.hh:16
openmsx::CliConnection
Definition: CliConnection.hh:19
openmsx::GlobalCommandController::unregisterSetting
void unregisterSetting(Setting &setting) override
Definition: GlobalCommandController.cc:168
openmsx::Interpreter::unregisterSetting
void unregisterSetting(BaseSetting &variable)
Definition: Interpreter.cc:313
ScopedAssign.hh
openmsx::GlobalCliComm
Definition: GlobalCliComm.hh:15
openmsx::GlobalCommandController::GlobalCommandController
GlobalCommandController(const GlobalCommandController &)=delete
Version.hh
span
Definition: span.hh:34
openmsx::Reactor
Contains the main loop of openMSX.
Definition: Reactor.hh:66
openmsx::Completer::completeString
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Definition: Completer.hh:125
Reactor.hh
OUTER
#define OUTER(type, member)
Definition: outer.hh:38
move_pop_back
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
Definition: stl.hh:177
view::drop
auto drop(Range &&range, size_t n)
Definition: view.hh:288
view::transform
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:306
openmsx::CliComm::printWarning
void printWarning(std::string_view message)
Definition: CliComm.cc:10
ProxyCommand.hh
openmsx::GlobalCommandController::tabCompletion
std::string tabCompletion(std::string_view command)
Complete the given command.
openmsx::CommandCompleter
Definition: Command.hh:19
openmsx::Interpreter::registerSetting
void registerSetting(BaseSetting &variable)
Definition: Interpreter.cc:261
openmsx::Interpreter::executeFile
TclObject executeFile(const std::string &filename)
Definition: Interpreter.cc:215
openmsx::GlobalCommandController::registerSetting
void registerSetting(Setting &setting) override
TODO.
Definition: GlobalCommandController.cc:162
openmsx::CliComm::getUpdateStrings
static span< const char *const > getUpdateStrings()
Definition: CliComm.hh:84
view::keys
auto keys(Map &&map)
Definition: view.hh:311
build-info.hh
view.hh
outer.hh
StringOp.hh
GlobalCommandController.hh
openmsx::TclObject::getString
std::string_view getString() const
Definition: TclObject.cc:102
openmsx::GlobalCommandController::isComplete
bool isComplete(const std::string &command)
Returns true iff the command is complete (all braces, quotes etc.
Definition: GlobalCommandController.cc:296
openmsx::makeTclList
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:280
openmsx::GlobalCommandController::registerProxyCommand
void registerProxyCommand(const std::string &name)
Definition: GlobalCommandController.cc:59
openmsx::Command
Definition: Command.hh:40
openmsx::CliComm
Definition: CliComm.hh:10
openmsx::Version::full
static std::string full()
Definition: Version.cc:8
openmsx::Interpreter::registerCommand
void registerCommand(const std::string &name, Command &command)
Definition: Interpreter.cc:145
stl.hh
SettingsManager.hh
openmsx::TclObject
Definition: TclObject.hh:21
openmsx::GlobalCommandControllerBase::commandCompleters
hash_map< std::string, CommandCompleter *, XXHasher > commandCompleters
Definition: GlobalCommandController.hh:32
join
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
Definition: join.hh:60
ranges::find_if
auto find_if(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:113
openmsx::BaseSetting::getBaseNameObj
const TclObject & getBaseNameObj() const
Definition: Setting.hh:34
openmsx::GlobalCommandController::unregisterProxySetting
void unregisterProxySetting(Setting &setting)
Definition: GlobalCommandController.cc:104
CliConnection.hh
ProxySetting.hh
openmsx::MSXException::getMessage
const std::string & getMessage() const &
Definition: MSXException.hh:23
ranges::remove_if
auto remove_if(ForwardRange &&range, UnaryPredicate pred)
Definition: ranges.hh:173
strCat
std::string strCat(Ts &&...ts)
Definition: strCat.hh:573
openmsx::Interpreter
Definition: Interpreter.hh:16
CommandException.hh
openmsx::GlobalCommandController::registerCompleter
void registerCompleter(CommandCompleter &completer, std::string_view str) override
(Un)register a command completer, used to complete build-in Tcl cmds
Definition: GlobalCommandController.cc:145
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
to_vector
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
Definition: stl.hh:311
xrange.hh
openmsx::GlobalCommandController::registerProxySetting
void registerProxySetting(Setting &setting)
Definition: GlobalCommandController.cc:88