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