openMSX
CommandConsole.cc
Go to the documentation of this file.
1 #include "CommandConsole.hh"
2 #include "CommandException.hh"
4 #include "Completer.hh"
5 #include "Interpreter.hh"
6 #include "Keys.hh"
7 #include "FileContext.hh"
8 #include "FileException.hh"
9 #include "FileOperations.hh"
10 #include "CliComm.hh"
11 #include "InputEvents.hh"
12 #include "Display.hh"
13 #include "VideoSystem.hh"
14 #include "EventDistributor.hh"
15 #include "Version.hh"
16 #include "checked_cast.hh"
17 #include "utf8_unchecked.hh"
18 #include "StringOp.hh"
19 #include "ScopedAssign.hh"
20 #include "view.hh"
21 #include "xrange.hh"
22 #include <algorithm>
23 #include <fstream>
24 #include <cassert>
25 
26 using std::min;
27 using std::max;
28 using std::string;
29 using std::string_view;
30 
31 namespace openmsx {
32 
33 // class ConsoleLine
34 
35 ConsoleLine::ConsoleLine(string line_, uint32_t rgb)
36  : line(std::move(line_))
37  , chunks(1, {rgb, 0})
38 {
39 }
40 
41 void ConsoleLine::addChunk(string_view text, uint32_t rgb)
42 {
43  chunks.emplace_back(rgb, line.size());
44  line.append(text.data(), text.size());
45 }
46 
47 size_t ConsoleLine::numChars() const
48 {
49  return utf8::unchecked::size(line);
50 }
51 
52 uint32_t ConsoleLine::chunkColor(size_t i) const
53 {
54  assert(i < chunks.size());
55  return chunks[i].first;
56 }
57 
58 string_view ConsoleLine::chunkText(size_t i) const
59 {
60  assert(i < chunks.size());
61  auto pos = chunks[i].second;
62  auto len = ((i + 1) == chunks.size())
63  ? string_view::npos
64  : chunks[i + 1].second - pos;
65  return string_view(line).substr(pos, len);
66 }
67 
68 // class CommandConsole
69 
70 constexpr const char* const PROMPT_NEW = "> ";
71 constexpr const char* const PROMPT_CONT = "| ";
72 constexpr const char* const PROMPT_BUSY = "*busy*";
73 
75  GlobalCommandController& commandController_,
76  EventDistributor& eventDistributor_,
77  Display& display_)
78  : commandController(commandController_)
79  , eventDistributor(eventDistributor_)
80  , display(display_)
81  , consoleSetting(
82  commandController, "console",
83  "turns console display on/off", false, Setting::DONT_SAVE)
84  , historySizeSetting(
85  commandController, "console_history_size",
86  "amount of commands kept in console history", 100, 0, 10000)
87  , removeDoublesSetting(
88  commandController, "console_remove_doubles",
89  "don't add the command to history if it's the same as the previous one",
90  true)
91  , history(std::max(1, historySizeSetting.getInt()))
92  , executingCommand(false)
93 {
94  resetScrollBack();
95  prompt = PROMPT_NEW;
96  newLineConsole(prompt);
97  loadHistory();
98  putPrompt();
100 
101  const auto& fullVersion = Version::full();
102  print(fullVersion);
103  print(string(fullVersion.size(), '-'));
104  print("\n"
105  "General information about openMSX is available at "
106  "http://openmsx.org.\n"
107  "\n"
108  "Type 'help' to see a list of available commands "
109  "(use <PgUp>/<PgDn> to scroll).\n"
110  "Or read the Console Command Reference in the manual.\n"
111  "\n");
112 
113  commandController.getInterpreter().setOutput(this);
114  eventDistributor.registerEventListener(
116  // also listen to KEY_UP events, so that we can consume them
117  eventDistributor.registerEventListener(
119 }
120 
122 {
123  eventDistributor.unregisterEventListener(OPENMSX_KEY_DOWN_EVENT, *this);
124  eventDistributor.unregisterEventListener(OPENMSX_KEY_UP_EVENT, *this);
125  commandController.getInterpreter().setOutput(nullptr);
126  Completer::setOutput(nullptr);
127 }
128 
129 void CommandConsole::saveHistory()
130 {
131  try {
132  std::ofstream outputfile;
133  FileOperations::openofstream(outputfile,
134  userFileContext("console").resolveCreate("history.txt"));
135  if (!outputfile) {
136  throw FileException(
137  "Error while saving the console history.");
138  }
139  for (auto& s : history) {
140  outputfile << s << '\n';
141  }
142  } catch (FileException& e) {
143  commandController.getCliComm().printWarning(e.getMessage());
144  }
145 }
146 
147 void CommandConsole::loadHistory()
148 {
149  try {
150  std::ifstream inputfile(
151  userFileContext("console").
152  resolveCreate("history.txt").c_str());
153  string line;
154  while (inputfile) {
155  getline(inputfile, line);
156  putCommandHistory(line);
157  }
158  } catch (FileException&) {
159  // Error while loading the console history, ignore
160  }
161 }
162 
164 {
165  int xPosition = cursorPosition % getColumns();
166  auto num = lines[0].numChars() / getColumns();
167  int yPosition = unsigned(num - (cursorPosition / getColumns()));
168  return {xPosition, yPosition};
169 }
170 
171 int CommandConsole::signalEvent(const std::shared_ptr<const Event>& event) noexcept
172 {
173  if (!consoleSetting.getBoolean()) return 0;
174  const auto& keyEvent = checked_cast<const KeyEvent&>(*event);
175 
176  // If the console is open then don't pass the event to the MSX
177  // (whetever the (keyboard) event is). If the event has a meaning for
178  // the console, then also don't pass the event to the hotkey system.
179  // For example PgUp, PgDown are keys that have both a meaning in the
180  // console and are used by standard key bindings.
181  if (event->getType() == OPENMSX_KEY_DOWN_EVENT) {
182  if (!executingCommand) {
183  if (handleEvent(keyEvent)) {
184  // event was used
185  display.repaintDelayed(40000); // 25fps
186  return EventDistributor::HOTKEY; // block HOTKEY and MSX
187  }
188  } else {
189  // For commands that take a long time to execute (e.g.
190  // a loadstate that needs to create a filepool index),
191  // we also send events during the execution (so that
192  // we can show progress on the OSD). In that case
193  // ignore extra input events.
194  }
195  } else {
196  assert(event->getType() == OPENMSX_KEY_UP_EVENT);
197  }
198  return EventDistributor::MSX; // block MSX
199 }
200 
201 bool CommandConsole::handleEvent(const KeyEvent& keyEvent)
202 {
203  auto keyCode = keyEvent.getKeyCode();
204  int key = keyCode & Keys::K_MASK;
205  int mod = keyCode & ~Keys::K_MASK;
206 
207  switch (mod) {
208  case Keys::KM_CTRL:
209  switch (key) {
210  case Keys::K_H:
211  backspace();
212  return true;
213  case Keys::K_A:
214  cursorPosition = unsigned(prompt.size());
215  return true;
216  case Keys::K_E:
217  cursorPosition = unsigned(lines[0].numChars());
218  return true;
219  case Keys::K_C:
220  clearCommand();
221  return true;
222  case Keys::K_L:
223  clearHistory();
224  return true;
225 #ifndef __APPLE__
226  case Keys::K_V:
227  paste();
228  return true;
229 #endif
230  }
231  break;
232  case Keys::KM_SHIFT:
233  switch (key) {
234  case Keys::K_PAGEUP:
235  scroll(max<int>(getRows() - 1, 1));
236  return true;
237  case Keys::K_PAGEDOWN:
238  scroll(-max<int>(getRows() - 1, 1));
239  return true;
240  }
241  break;
242  case Keys::KM_META:
243 #ifdef __APPLE__
244  switch (key) {
245  case Keys::K_V:
246  paste();
247  return true;
248  case Keys::K_K:
249  clearHistory();
250  return true;
251  case Keys::K_LEFT:
252  cursorPosition = unsigned(prompt.size());
253  return true;
254  case Keys::K_RIGHT:
255  cursorPosition = unsigned(lines[0].numChars());
256  return true;
257  }
258 #endif
259  break;
260  case Keys::KM_ALT:
261  switch (key) {
262  case Keys::K_BACKSPACE:
263  deleteToStartOfWord();
264  return true;
265 #ifdef __APPLE__
266  case Keys::K_DELETE:
267  deleteToEndOfWord();
268  return true;
269 #else
270  case Keys::K_D:
271  deleteToEndOfWord();
272  return true;
273 #endif
274  case Keys::K_LEFT:
275  gotoStartOfWord();
276  return true;
277  case Keys::K_RIGHT:
278  gotoEndOfWord();
279  return true;
280  }
281  break;
282  case 0: // no modifier
283  switch (key) {
284  case Keys::K_PAGEUP:
285  scroll(1);
286  return true;
287  case Keys::K_PAGEDOWN:
288  scroll(-1);
289  return true;
290  case Keys::K_UP:
291  prevCommand();
292  return true;
293  case Keys::K_DOWN:
294  nextCommand();
295  return true;
296  case Keys::K_BACKSPACE:
297  backspace();
298  return true;
299  case Keys::K_DELETE:
300  delete_key();
301  return true;
302  case Keys::K_TAB:
303  tabCompletion();
304  return true;
305  case Keys::K_RETURN:
306  case Keys::K_KP_ENTER:
307  commandExecute();
308  cursorPosition = unsigned(prompt.size());
309  return true;
310  case Keys::K_LEFT:
311  if (cursorPosition > prompt.size()) {
312  --cursorPosition;
313  }
314  return true;
315  case Keys::K_RIGHT:
316  if (cursorPosition < lines[0].numChars()) {
317  ++cursorPosition;
318  }
319  return true;
320  case Keys::K_HOME:
321 #ifdef __APPLE__
322  scroll(lines.size());
323 #else
324  cursorPosition = unsigned(prompt.size());
325 #endif
326  return true;
327  case Keys::K_END:
328 #ifdef __APPLE__
329  scroll(-lines.size());
330 #else
331  cursorPosition = unsigned(lines[0].numChars());
332 #endif
333  return true;
334  }
335  break;
336  }
337 
338  auto unicode = keyEvent.getUnicode();
339  if (!unicode || (mod & Keys::KM_META)) {
340  // Disallow META modifer for 'normal' key presses because on
341  // MacOSX Cmd+L is used as a hotkey to toggle the console.
342  // Hopefully there are no systems that require META to type
343  // normal keys. However there _are_ systems that require the
344  // following modifiers, some examples:
345  // MODE: to type '1-9' on a N900
346  // ALT: to type | [ ] on a azerty keyboard layout
347  // CTRL+ALT: to type '#' on a spanish keyboard layout (windows)
348  //
349  // Event was not used by the console, allow the other
350  // subsystems to process it. E.g. F10, or Cmd+L to close the
351  // console.
352  return false;
353  }
354 
355  // Apparently on macOS keyboard events for keys like F1 have a non-zero
356  // unicode field. This confuses the console code (it thinks it's a
357  // printable character) and it prevents those keys from triggering
358  // hotkey bindings. See this bug report:
359  // https://github.com/openMSX/openMSX/issues/1095
360  // As a workaround we ignore chars in the 'Private Use Area' (PUA).
361  // https://en.wikipedia.org/wiki/Private_Use_Areas
362  if (utf8::is_pua(unicode)) {
363  return false;
364  }
365 
366  if (unicode >= 0x20) {
367  normalKey(unicode);
368  } else {
369  // Skip CTRL-<X> combinations, but still return true.
370  }
371  return true;
372 }
373 
374 void CommandConsole::output(string_view text)
375 {
376  print(text);
377 }
378 
379 unsigned CommandConsole::getOutputColumns() const
380 {
381  return getColumns();
382 }
383 
384 void CommandConsole::print(string_view text, unsigned rgb)
385 {
386  while (true) {
387  auto pos = text.find('\n');
388  newLineConsole(ConsoleLine(string(text.substr(0, pos)), rgb));
389  if (pos == string_view::npos) return;
390  text = text.substr(pos + 1); // skip newline
391  if (text.empty()) return;
392  }
393 }
394 
395 void CommandConsole::newLineConsole(string line)
396 {
397  newLineConsole(ConsoleLine(std::move(line)));
398 }
399 
400 void CommandConsole::newLineConsole(ConsoleLine line)
401 {
402  if (lines.isFull()) {
403  lines.removeBack();
404  }
405  ConsoleLine tmp = std::move(lines[0]);
406  lines[0] = std::move(line);
407  lines.addFront(std::move(tmp));
408 }
409 
410 void CommandConsole::putCommandHistory(const string& command)
411 {
412  if (command.empty()) return;
413  if (removeDoublesSetting.getBoolean() && !history.empty() &&
414  (history.back() == command)) {
415  return;
416  }
417  if (history.full()) history.pop_front();
418  history.push_back(command);
419 }
420 
421 void CommandConsole::commandExecute()
422 {
423  resetScrollBack();
424  string cmd0 = lines[0].str().substr(prompt.size());
425  putCommandHistory(cmd0);
426  saveHistory(); // save at this point already, so that we don't lose history in case of a crash
427 
428  strAppend(commandBuffer, cmd0, '\n');
429  newLineConsole(lines[0]);
430  if (commandController.isComplete(commandBuffer)) {
431  // Normally the busy prompt is NOT shown (not even very briefly
432  // because the screen is not redrawn), though for some commands
433  // that potentially take a long time to execute, we explicitly
434  // send events, see also comment in signalEvent().
435  prompt = PROMPT_BUSY;
436  putPrompt();
437 
438  try {
439  ScopedAssign sa(executingCommand, true);
440  auto resultObj = commandController.executeCommand(
441  commandBuffer);
442  auto result = resultObj.getString();
443  if (!result.empty()) {
444  print(result);
445  }
446  } catch (CommandException& e) {
447  print(e.getMessage(), 0xff0000);
448  }
449  commandBuffer.clear();
450  prompt = PROMPT_NEW;
451  } else {
452  prompt = PROMPT_CONT;
453  }
454  putPrompt();
455 }
456 
457 ConsoleLine CommandConsole::highLight(string_view line)
458 {
459  ConsoleLine result;
460  result.addChunk(prompt, 0xffffff);
461 
462  TclParser parser = commandController.getInterpreter().parse(line);
463  string colors = parser.getColors();
464  assert(colors.size() == line.size());
465 
466  unsigned pos = 0;
467  while (pos != colors.size()) {
468  char col = colors[pos];
469  unsigned pos2 = pos++;
470  while ((pos != colors.size()) && (colors[pos] == col)) {
471  ++pos;
472  }
473  // TODO make these color configurable?
474  unsigned rgb = [&] {
475  switch (col) {
476  case 'E': return 0xff0000; // error
477  case 'c': return 0x5c5cff; // comment
478  case 'v': return 0x00ffff; // variable
479  case 'l': return 0xff00ff; // literal
480  case 'p': return 0xcdcd00; // proc
481  case 'o': return 0x00cdcd; // operator
482  default: return 0xffffff; // other
483  }
484  }();
485  result.addChunk(line.substr(pos2, pos - pos2), rgb);
486  }
487  return result;
488 }
489 
490 void CommandConsole::putPrompt()
491 {
492  commandScrollBack = unsigned(history.size());
493  currentLine.clear();
494  lines[0] = highLight(currentLine);
495  cursorPosition = unsigned(prompt.size());
496 }
497 
498 void CommandConsole::tabCompletion()
499 {
500  resetScrollBack();
501  auto pl = unsigned(prompt.size());
502  string front(utf8::unchecked::substr(lines[0].str(), pl, cursorPosition - pl));
503  string back (utf8::unchecked::substr(lines[0].str(), cursorPosition));
504  string newFront = commandController.tabCompletion(front);
505  cursorPosition = pl + unsigned(utf8::unchecked::size(newFront));
506  currentLine = newFront + back;
507  lines[0] = highLight(currentLine);
508 }
509 
510 void CommandConsole::scroll(int delta)
511 {
512  consoleScrollBack = max(min(consoleScrollBack + delta, int(lines.size()) - int(rows)), 0);
513 }
514 
515 // Returns the position of the start of the word relative to the current cursor position.
516 // If the cursor is already located at the start of a word then return the start of the previous word.
517 // If there is no previous word, then return the start of the line (position of the prompt).
518 //
519 // One complication is that the line is utf8 encoded and the positions are
520 // given in character-counts instead of byte-counts. To solve this this
521 // function returns a tuple:
522 // * an iterator to the start of the word (likely the new cursor position).
523 // * an iterator to the current cursor position.
524 // * the distance (in characters, not bytes) between these two iterators.
525 // The first item in this tuple is the actual result. The other two are only
526 // returned for efficiency (this function calculates them anyway, and it's
527 // likely the caller will need them as well).
528 static std::tuple<std::string::const_iterator, std::string::const_iterator, unsigned>
529  getStartOfWord(const std::string& line, unsigned cursorPos, size_t promptSize)
530 {
531  auto begin = std::begin(line);
532  auto prompt = begin + promptSize; // assumes prompt only contains single-byte utf8 chars
533  auto cursor = begin; utf8::unchecked::advance(cursor, cursorPos);
534  auto pos = cursor;
535 
536  // search (backwards) for first non-white-space
537  unsigned distance = 0;
538  while (pos > prompt) {
539  auto pos2 = pos;
541  if (*pos2 != one_of(' ', '\t')) break;
542  pos = pos2;
543  ++distance;
544  }
545  // search (backwards) for first white-space
546  while (pos > prompt) {
547  auto pos2 = pos;
549  if (*pos2 == one_of(' ', '\t')) break;
550  pos = pos2;
551  ++distance;
552  }
553  return {pos, cursor, distance};
554 }
555 
556 // Similar to getStartOfWord() but locates the end of the word instead.
557 // The end of the word is the 2nd (instead of the 1st) element in the tuple.
558 // This is done so that both getStartOfWord() and getEndOfWord() have the invariant:
559 // result = [begin, end, distance]
560 // -> begin <= end
561 // -> end - begin == distance
562 static std::tuple<std::string::const_iterator, std::string::const_iterator, unsigned>
563  getEndOfWord(const std::string& line, unsigned cursorPos)
564 {
565  auto begin = std::begin(line);
566  auto end = std::end (line);
567  auto cursor = begin; utf8::unchecked::advance(cursor, cursorPos);
568  auto pos = cursor;
569 
570  // search (forwards) for first non-white-space
571  unsigned distance = 0;
572  while ((pos < end) && (*pos == one_of(' ', '\t'))) {
573  ++pos; // single-byte utf8 character
574  ++distance;
575  }
576  // search (forwards) for first white-space
577  while ((pos < end) && (*pos != one_of(' ', '\t'))) {
579  ++distance;
580  }
581  return {cursor, pos, distance};
582 }
583 
584 void CommandConsole::gotoStartOfWord()
585 {
586  auto [begin, end, distance] = getStartOfWord(lines[0].str(), cursorPosition, prompt.size());
587  cursorPosition -= distance;
588 }
589 
590 void CommandConsole::deleteToStartOfWord()
591 {
592  resetScrollBack();
593  currentLine = lines[0].str();
594  auto [begin, end, distance] = getStartOfWord(currentLine, cursorPosition, prompt.size());
595  currentLine.erase(begin, end);
596  currentLine.erase(0, prompt.size());
597  lines[0] = highLight(currentLine);
598  cursorPosition -= distance;
599 }
600 
601 void CommandConsole::gotoEndOfWord()
602 {
603  auto [begin, end, distance] = getEndOfWord(lines[0].str(), cursorPosition);
604  cursorPosition += distance;
605 }
606 
607 void CommandConsole::deleteToEndOfWord()
608 {
609  resetScrollBack();
610  currentLine = lines[0].str();
611  auto [begin, end, distance] = getEndOfWord(currentLine, cursorPosition);
612  currentLine.erase(begin, end);
613  currentLine.erase(0, prompt.size());
614  lines[0] = highLight(currentLine);
615 }
616 
617 void CommandConsole::prevCommand()
618 {
619  resetScrollBack();
620  if (history.empty()) {
621  return; // no elements
622  }
623  bool match = false;
624  unsigned tmp = commandScrollBack;
625  while ((tmp != 0) && !match) {
626  --tmp;
627  match = StringOp::startsWith(history[tmp], currentLine);
628  }
629  if (match) {
630  commandScrollBack = tmp;
631  lines[0] = highLight(history[commandScrollBack]);
632  cursorPosition = unsigned(lines[0].numChars());
633  }
634 }
635 
636 void CommandConsole::nextCommand()
637 {
638  resetScrollBack();
639  if (commandScrollBack == history.size()) {
640  return; // don't loop !
641  }
642  bool match = false;
643  auto tmp = commandScrollBack;
644  while ((++tmp != history.size()) && !match) {
645  match = StringOp::startsWith(history[tmp], currentLine);
646  }
647  if (match) {
648  --tmp; // one time to many
649  commandScrollBack = tmp;
650  lines[0] = highLight(history[commandScrollBack]);
651  } else {
652  commandScrollBack = unsigned(history.size());
653  lines[0] = highLight(currentLine);
654  }
655  cursorPosition = unsigned(lines[0].numChars());
656 }
657 
658 void CommandConsole::clearCommand()
659 {
660  resetScrollBack();
661  commandBuffer.clear();
662  prompt = PROMPT_NEW;
663  currentLine.clear();
664  lines[0] = highLight(currentLine);
665  cursorPosition = unsigned(prompt.size());
666 }
667 
668 void CommandConsole::clearHistory()
669 {
670  resetScrollBack();
671  while (lines.size() > 1) {
672  lines.removeBack();
673  }
674 }
675 
676 void CommandConsole::backspace()
677 {
678  resetScrollBack();
679  if (cursorPosition > prompt.size()) {
680  currentLine = lines[0].str();
681  auto b = begin(currentLine);
682  utf8::unchecked::advance(b, cursorPosition - 1);
683  auto e = b;
685  currentLine.erase(b, e);
686  currentLine.erase(0, prompt.size());
687  lines[0] = highLight(currentLine);
688  --cursorPosition;
689  }
690 }
691 
692 void CommandConsole::delete_key()
693 {
694  resetScrollBack();
695  if (lines[0].numChars() > cursorPosition) {
696  currentLine = lines[0].str();
697  auto b = begin(currentLine);
698  utf8::unchecked::advance(b, cursorPosition);
699  auto e = b;
701  currentLine.erase(b, e);
702  currentLine.erase(0, prompt.size());
703  lines[0] = highLight(currentLine);
704  }
705 }
706 
707 void CommandConsole::normalKey(uint32_t chr)
708 {
709  assert(chr);
710  resetScrollBack();
711  currentLine = lines[0].str();
712  auto pos = begin(currentLine);
713  utf8::unchecked::advance(pos, cursorPosition);
714  utf8::unchecked::append(chr, inserter(currentLine, pos));
715  currentLine.erase(0, prompt.size());
716  lines[0] = highLight(currentLine);
717  ++cursorPosition;
718 }
719 
720 void CommandConsole::resetScrollBack()
721 {
722  consoleScrollBack = 0;
723 }
724 
725 static std::vector<std::string_view> splitLines(std::string_view str)
726 {
727  // This differs from StringOp::split() in two ways:
728  // - If the input is an empty string, then the resulting vector
729  // contains 1 element which is the empty string (StringOp::split()
730  // would return an empty vector).
731  // - If the input ends on a newline character, then the final item in
732  // the result vector is an empty string (StringOp::split() would not
733  // include that last empty string).
734  // TODO can we come up with a good name for this function and move it
735  // to StringOp?
736  std::vector<std::string_view> result;
737  while (true) {
738  auto pos = str.find_first_of('\n');
739  if (pos == std::string_view::npos) break;
740  result.push_back(str.substr(0, pos));
741  str = str.substr(pos + 1);
742  }
743  result.push_back(str);
744  return result;
745 }
746 
747 void CommandConsole::paste()
748 {
749  auto text = display.getVideoSystem().getClipboardText();
750  if (text.empty()) return;
751  auto pastedLines = splitLines(text);
752  assert(!pastedLines.empty());
753 
754  // helper function 'append()' to append to the existing text
755  std::string_view prefix = lines[0].str();
756  prefix.remove_prefix(prompt.size());
757  auto append = [&](std::string_view suffix) {
758  if (prefix.empty()) {
759  lines[0] = highLight(suffix);
760  } else {
761  lines[0] = highLight(tmpStrCat(prefix, suffix));
762  prefix = "";
763  }
764  };
765  // execute all but the last line
766  for (const auto& line : view::drop_back(pastedLines, 1)) {
767  append(line);
768  commandExecute();
769  }
770  // only enter (not execute) the last line
771  append(pastedLines.back());
772  cursorPosition = unsigned(lines[0].numChars());
773 }
774 
775 } // namespace openmsx
Assign new value to some variable and restore the original value when this object goes out of scope.
Definition: ScopedAssign.hh:8
std::string getColors() const
Ouput: a string of equal length of the input command where each character indicates the type of the c...
Definition: TclParser.hh:28
size_t size() const
bool full() const
bool empty() const
void push_back(const T &t)
Definition: one_of.hh:7
bool getBoolean() const noexcept
void printWarning(std::string_view message)
Definition: CliComm.cc:10
CommandConsole(GlobalCommandController &commandController, EventDistributor &eventDistributor, Display &display)
gl::ivec2 getCursorPosition() const
unsigned getColumns() const
unsigned getRows() const
static void setOutput(InterpreterOutput *output_)
Definition: Completer.hh:65
std::string_view chunkText(size_t i) const
Get the text for the i-th chunk.
ConsoleLine()=default
Construct empty line.
size_t numChars() const
Get the number of UTF8 characters in this line.
uint32_t chunkColor(size_t i) const
Get the color for the i-th chunk.
void addChunk(std::string_view text, uint32_t rgb)
Append a chunk with a (different) color.
Represents the output window/screen of openMSX.
Definition: Display.hh:33
VideoSystem & getVideoSystem()
Definition: Display.cc:113
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
std::string tabCompletion(std::string_view command)
Complete the given command.
TclObject executeCommand(zstring_view command, CliConnection *connection=nullptr) override
Execute the given command.
bool isComplete(zstring_view command)
Returns true iff the command is complete (all braces, quotes etc.
void setOutput(InterpreterOutput *output_)
Definition: Interpreter.hh:27
TclParser parse(std::string_view command)
Definition: Interpreter.cc:453
zstring_view getString() const
Definition: TclObject.cc:111
static std::string full()
Definition: Version.cc:8
virtual std::string getClipboardText()=0
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:33
void append(Result &)
Definition: stl.hh:340
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:287
void openofstream(std::ofstream &stream, zstring_view filename)
Open an ofstream in a platform-independent manner.
@ K_BACKSPACE
Definition: Keys.hh:31
@ K_KP_ENTER
Definition: Keys.hh:120
@ K_RETURN
Definition: Keys.hh:34
@ K_PAGEDOWN
Definition: Keys.hh:132
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr const char *const PROMPT_BUSY
@ OPENMSX_KEY_UP_EVENT
Definition: Event.hh:12
@ OPENMSX_KEY_DOWN_EVENT
Definition: Event.hh:13
constexpr const char *const PROMPT_CONT
constexpr const char *const PROMPT_NEW
FileContext userFileContext(string_view savePath)
Definition: FileContext.cc:172
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)
void advance(octet_iterator &it, distance_type n)
size_t size(std::string_view utf8)
uint32_t next(octet_iterator &it)
octet_iterator append(uint32_t cp, octet_iterator result)
uint32_t prior(octet_iterator &it)
auto distance(octet_iterator first, octet_iterator last)
constexpr bool is_pua(uint32_t cp)
Definition: utf8_core.hh:254
constexpr auto drop_back(Range &&range, size_t n)
Definition: view.hh:294
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
void strAppend(std::string &result, Ts &&...ts)
Definition: strCat.hh:669
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:83
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84