debugger.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. import 'package:xterm/src/core/escape/handler.dart';
  2. import 'package:xterm/src/core/escape/parser.dart';
  3. import 'package:xterm/src/core/mouse/mode.dart';
  4. import 'package:xterm/src/utils/observable.dart';
  5. class TerminalCommand {
  6. TerminalCommand(
  7. this.start,
  8. this.end,
  9. this.chars,
  10. this.escapedChars,
  11. this.explanation,
  12. this.error,
  13. );
  14. final int start;
  15. final int end;
  16. final String chars;
  17. final String escapedChars;
  18. final List<String> explanation;
  19. final bool error;
  20. }
  21. class TerminalDebugger with Observable {
  22. late final _parser = EscapeParser(_handler);
  23. late final _handler = _TerminalDebuggerHandler(recordCommand);
  24. final recorded = <int>[];
  25. final commands = <TerminalCommand>[];
  26. void write(String chunk) {
  27. recorded.addAll(chunk.runes);
  28. _parser.write(chunk);
  29. notifyListeners();
  30. }
  31. void recordCommand(String explanation, {bool error = false}) {
  32. final start = _parser.tokenBegin;
  33. final end = _parser.tokenEnd;
  34. if (commands.isNotEmpty && commands.last.end == end) {
  35. commands.last.explanation.add(explanation);
  36. } else {
  37. final charCodes = recorded.sublist(start, end);
  38. final chars = String.fromCharCodes(charCodes);
  39. final escapedChars = _escape(chars);
  40. commands.add(
  41. TerminalCommand(start, end, chars, escapedChars, [explanation], error),
  42. );
  43. }
  44. }
  45. String getRecord(TerminalCommand command) {
  46. final charCodes = recorded.sublist(0, command.end);
  47. return String.fromCharCodes(charCodes);
  48. }
  49. static String _escape(String chars) {
  50. final escaped = StringBuffer();
  51. for (final char in chars.runes) {
  52. if (char == 0x1b) {
  53. escaped.write('ESC');
  54. } else if (char < 32) {
  55. escaped.write('^0x${char.toRadixString(16)}');
  56. } else if (char == 127) {
  57. escaped.write('^?');
  58. } else {
  59. escaped.writeCharCode(char);
  60. }
  61. }
  62. return escaped.toString();
  63. }
  64. }
  65. class _TerminalDebuggerHandler implements EscapeHandler {
  66. _TerminalDebuggerHandler(this.onCommand);
  67. final void Function(String explanation, {bool error}) onCommand;
  68. @override
  69. void writeChar(int char) {
  70. onCommand('writeChar(${String.fromCharCode(char)})');
  71. }
  72. /* SBC */
  73. @override
  74. void bell() {
  75. onCommand('bell');
  76. }
  77. @override
  78. void backspaceReturn() {
  79. onCommand('backspaceReturn');
  80. }
  81. @override
  82. void tab() {
  83. onCommand('tab');
  84. }
  85. @override
  86. void lineFeed() {
  87. onCommand('lineFeed');
  88. }
  89. @override
  90. void carriageReturn() {
  91. onCommand('carriageReturn');
  92. }
  93. @override
  94. void shiftOut() {
  95. onCommand('shiftOut');
  96. }
  97. @override
  98. void shiftIn() {
  99. onCommand('shiftIn');
  100. }
  101. @override
  102. void unknownSBC(int char) {
  103. onCommand('unkownSBC(${String.fromCharCode(char)})', error: true);
  104. }
  105. /* ANSI sequence */
  106. @override
  107. void saveCursor() {
  108. onCommand('saveCursor');
  109. }
  110. @override
  111. void restoreCursor() {
  112. onCommand('restoreCursor');
  113. }
  114. @override
  115. void index() {
  116. onCommand('index');
  117. }
  118. @override
  119. void nextLine() {
  120. onCommand('nextLine');
  121. }
  122. @override
  123. void setTapStop() {
  124. onCommand('setTapStop');
  125. }
  126. @override
  127. void reverseIndex() {
  128. onCommand('reverseIndex');
  129. }
  130. @override
  131. void designateCharset(int charset) {
  132. onCommand('designateCharset($charset)');
  133. }
  134. @override
  135. void unkownEscape(int char) {
  136. onCommand('unkownEscape(${String.fromCharCode(char)})', error: true);
  137. }
  138. /* CSI */
  139. @override
  140. void repeatPreviousCharacter(int count) {
  141. onCommand('repeatPreviousCharacter($count)');
  142. }
  143. @override
  144. void unknownCSI(int finalByte) {
  145. onCommand('unkownCSI(${String.fromCharCode(finalByte)})', error: true);
  146. }
  147. @override
  148. void setCursor(int x, int y) {
  149. onCommand('setCursor($x, $y)');
  150. }
  151. @override
  152. void setCursorX(int x) {
  153. onCommand('setCursorX($x)');
  154. }
  155. @override
  156. void setCursorY(int y) {
  157. onCommand('setCursorY($y)');
  158. }
  159. @override
  160. void sendPrimaryDeviceAttributes() {
  161. onCommand('sendPrimaryDeviceAttributes');
  162. }
  163. @override
  164. void clearTabStopUnderCursor() {
  165. onCommand('clearTabStopUnderCursor');
  166. }
  167. @override
  168. void clearAllTabStops() {
  169. onCommand('clearAllTabStops');
  170. }
  171. @override
  172. void moveCursorX(int offset) {
  173. onCommand('moveCursorX($offset)');
  174. }
  175. @override
  176. void moveCursorY(int n) {
  177. onCommand('moveCursorY($n)');
  178. }
  179. @override
  180. void sendSecondaryDeviceAttributes() {
  181. onCommand('sendSecondaryDeviceAttributes');
  182. }
  183. @override
  184. void sendTertiaryDeviceAttributes() {
  185. onCommand('sendTertiaryDeviceAttributes');
  186. }
  187. @override
  188. void sendOperatingStatus() {
  189. onCommand('sendOperatingStatus');
  190. }
  191. @override
  192. void sendCursorPosition() {
  193. onCommand('sendCursorPosition');
  194. }
  195. @override
  196. void setMargins(int i, [int? bottom]) {
  197. onCommand('setMargins($i, $bottom)');
  198. }
  199. @override
  200. void cursorNextLine(int amount) {
  201. onCommand('cursorNextLine($amount)');
  202. }
  203. @override
  204. void cursorPrecedingLine(int amount) {
  205. onCommand('cursorPrecedingLine($amount)');
  206. }
  207. @override
  208. void eraseDisplayBelow() {
  209. onCommand('eraseDisplayBelow');
  210. }
  211. @override
  212. void eraseDisplayAbove() {
  213. onCommand('eraseDisplayAbove');
  214. }
  215. @override
  216. void eraseDisplay() {
  217. onCommand('eraseDisplay');
  218. }
  219. @override
  220. void eraseScrollbackOnly() {
  221. onCommand('eraseScrollbackOnly');
  222. }
  223. @override
  224. void eraseLineRight() {
  225. onCommand('eraseLineRight');
  226. }
  227. @override
  228. void eraseLineLeft() {
  229. onCommand('eraseLineLeft');
  230. }
  231. @override
  232. void eraseLine() {
  233. onCommand('eraseLine');
  234. }
  235. @override
  236. void insertLines(int amount) {
  237. onCommand('insertLines($amount)');
  238. }
  239. @override
  240. void deleteLines(int amount) {
  241. onCommand('deleteLines($amount)');
  242. }
  243. @override
  244. void deleteChars(int amount) {
  245. onCommand('deleteChars($amount)');
  246. }
  247. @override
  248. void scrollUp(int amount) {
  249. onCommand('scrollUp($amount)');
  250. }
  251. @override
  252. void scrollDown(int amount) {
  253. onCommand('scrollDown($amount)');
  254. }
  255. @override
  256. void eraseChars(int amount) {
  257. onCommand('eraseChars($amount)');
  258. }
  259. @override
  260. void insertBlankChars(int amount) {
  261. onCommand('insertBlankChars($amount)');
  262. }
  263. @override
  264. void resize(int cols, int rows) {
  265. onCommand('resize($cols, $rows)');
  266. }
  267. @override
  268. void sendSize() {
  269. onCommand('sendSize');
  270. }
  271. /* Modes */
  272. @override
  273. void setInsertMode(bool enabled) {
  274. onCommand('setInsertMode($enabled)');
  275. }
  276. @override
  277. void setLineFeedMode(bool enabled) {
  278. onCommand('setLineFeedMode($enabled)');
  279. }
  280. @override
  281. void setUnknownMode(int mode, bool enabled) {
  282. onCommand('setUnknownMode($mode, $enabled)', error: true);
  283. }
  284. /* DEC Private modes */
  285. @override
  286. void setCursorKeysMode(bool enabled) {
  287. onCommand('setCursorKeysMode($enabled)');
  288. }
  289. @override
  290. void setReverseDisplayMode(bool enabled) {
  291. onCommand('setReverseDisplayMode($enabled)');
  292. }
  293. @override
  294. void setOriginMode(bool enabled) {
  295. onCommand('setOriginMode($enabled)');
  296. }
  297. @override
  298. void setColumnMode(bool enabled) {
  299. onCommand('setColumnMode($enabled)');
  300. }
  301. @override
  302. void setAutoWrapMode(bool enabled) {
  303. onCommand('setAutoWrapMode($enabled)');
  304. }
  305. @override
  306. void setMouseMode(MouseMode mode) {
  307. onCommand('setMouseMode($mode)');
  308. }
  309. @override
  310. void setCursorBlinkMode(bool enabled) {
  311. onCommand('setCursorBlinkMode($enabled)');
  312. }
  313. @override
  314. void setCursorVisibleMode(bool enabled) {
  315. onCommand('setCursorVisibleMode($enabled)');
  316. }
  317. @override
  318. void useAltBuffer() {
  319. onCommand('useAltBuffer');
  320. }
  321. @override
  322. void useMainBuffer() {
  323. onCommand('useMainBuffer');
  324. }
  325. @override
  326. void clearAltBuffer() {
  327. onCommand('clearAltBuffer');
  328. }
  329. @override
  330. void setAppKeypadMode(bool enabled) {
  331. onCommand('setAppKeypadMode($enabled)');
  332. }
  333. @override
  334. void setReportFocusMode(bool enabled) {
  335. onCommand('setReportFocusMode($enabled)');
  336. }
  337. @override
  338. void setMouseReportMode(MouseReportMode mode) {
  339. onCommand('setMouseReportMode($mode)');
  340. }
  341. @override
  342. void setAltBufferMouseScrollMode(bool enabled) {
  343. onCommand('setAltBufferMouseScrollMode($enabled)');
  344. }
  345. @override
  346. void setBracketedPasteMode(bool enabled) {
  347. onCommand('setBracketedPasteMode($enabled)');
  348. }
  349. @override
  350. void setUnknownDecMode(int mode, bool enabled) {
  351. onCommand('setUnknownDecMode($mode, $enabled)', error: true);
  352. }
  353. /* Select Graphic Rendition (SGR) */
  354. @override
  355. void resetCursorStyle() {
  356. onCommand('resetCursorStyle');
  357. }
  358. @override
  359. void setCursorBold() {
  360. onCommand('setCursorBold');
  361. }
  362. @override
  363. void setCursorFaint() {
  364. onCommand('setCursorFaint');
  365. }
  366. @override
  367. void setCursorItalic() {
  368. onCommand('setCursorItalic');
  369. }
  370. @override
  371. void setCursorUnderline() {
  372. onCommand('setCursorUnderline');
  373. }
  374. @override
  375. void setCursorBlink() {
  376. onCommand('setCursorBlink');
  377. }
  378. @override
  379. void setCursorInverse() {
  380. onCommand('setCursorInverse');
  381. }
  382. @override
  383. void setCursorInvisible() {
  384. onCommand('setCursorInvisible');
  385. }
  386. @override
  387. void setCursorStrikethrough() {
  388. onCommand('setCursorStrikethrough');
  389. }
  390. @override
  391. void unsetCursorBold() {
  392. onCommand('unsetCursorBold');
  393. }
  394. @override
  395. void unsetCursorFaint() {
  396. onCommand('unsetCursorFaint');
  397. }
  398. @override
  399. void unsetCursorItalic() {
  400. onCommand('unsetCursorItalic');
  401. }
  402. @override
  403. void unsetCursorUnderline() {
  404. onCommand('unsetCursorUnderline');
  405. }
  406. @override
  407. void unsetCursorBlink() {
  408. onCommand('unsetCursorBlink');
  409. }
  410. @override
  411. void unsetCursorInverse() {
  412. onCommand('unsetCursorInverse');
  413. }
  414. @override
  415. void unsetCursorInvisible() {
  416. onCommand('unsetCursorInvisible');
  417. }
  418. @override
  419. void unsetCursorStrikethrough() {
  420. onCommand('unsetCursorStrikethrough');
  421. }
  422. @override
  423. void setForegroundColor16(int color) {
  424. onCommand('setForegroundColor16($color)');
  425. }
  426. @override
  427. void setForegroundColor256(int index) {
  428. onCommand('setForegroundColor256($index)');
  429. }
  430. @override
  431. void setForegroundColorRgb(int r, int g, int b) {
  432. onCommand('setForegroundColorRgb($r, $g, $b)');
  433. }
  434. @override
  435. void resetForeground() {
  436. onCommand('resetForeground');
  437. }
  438. @override
  439. void setBackgroundColor16(int color) {
  440. onCommand('setBackgroundColor16($color)');
  441. }
  442. @override
  443. void setBackgroundColor256(int index) {
  444. onCommand('setBackgroundColor256($index)');
  445. }
  446. @override
  447. void setBackgroundColorRgb(int r, int g, int b) {
  448. onCommand('setBackgroundColorRgb($r, $g, $b)');
  449. }
  450. @override
  451. void resetBackground() {
  452. onCommand('resetBackground');
  453. }
  454. @override
  455. void unsupportedStyle(int param) {
  456. onCommand('unsupportedStyle($param)', error: true);
  457. }
  458. /* OSC */
  459. @override
  460. void setTitle(String name) {
  461. onCommand('setTitle($name)');
  462. }
  463. @override
  464. void setIconName(String name) {
  465. onCommand('setIconName($name)');
  466. }
  467. @override
  468. void unknownOSC(String ps) {
  469. onCommand('unknownOSC($ps)', error: true);
  470. }
  471. }