debugger.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. import 'package:xterm/core/escape/handler.dart';
  2. import 'package:xterm/core/escape/parser.dart';
  3. import 'package:xterm/core/mouse.dart';
  4. import 'package:xterm/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. /* Modes */
  264. @override
  265. void setInsertMode(bool enabled) {
  266. onCommand('setInsertMode($enabled)');
  267. }
  268. @override
  269. void setLineFeedMode(bool enabled) {
  270. onCommand('setLineFeedMode($enabled)');
  271. }
  272. @override
  273. void setUnknownMode(int mode, bool enabled) {
  274. onCommand('setUnknownMode($mode, $enabled)', error: true);
  275. }
  276. /* DEC Private modes */
  277. @override
  278. void setCursorKeysMode(bool enabled) {
  279. onCommand('setCursorKeysMode($enabled)');
  280. }
  281. @override
  282. void setReverseDisplayMode(bool enabled) {
  283. onCommand('setReverseDisplayMode($enabled)');
  284. }
  285. @override
  286. void setOriginMode(bool enabled) {
  287. onCommand('setOriginMode($enabled)');
  288. }
  289. @override
  290. void setColumnMode(bool enabled) {
  291. onCommand('setColumnMode($enabled)');
  292. }
  293. @override
  294. void setAutoWrapMode(bool enabled) {
  295. onCommand('setAutoWrapMode($enabled)');
  296. }
  297. @override
  298. void setMouseMode(MouseMode mode) {
  299. onCommand('setMouseMode($mode)');
  300. }
  301. @override
  302. void setCursorBlinkMode(bool enabled) {
  303. onCommand('setCursorBlinkMode($enabled)');
  304. }
  305. @override
  306. void setCursorVisibleMode(bool enabled) {
  307. onCommand('setCursorVisibleMode($enabled)');
  308. }
  309. @override
  310. void useAltBuffer() {
  311. onCommand('useAltBuffer');
  312. }
  313. @override
  314. void useMainBuffer() {
  315. onCommand('useMainBuffer');
  316. }
  317. @override
  318. void clearAltBuffer() {
  319. onCommand('clearAltBuffer');
  320. }
  321. @override
  322. void setAppKeypadMode(bool enabled) {
  323. onCommand('setAppKeypadMode($enabled)');
  324. }
  325. @override
  326. void setReportFocusMode(bool enabled) {
  327. onCommand('setReportFocusMode($enabled)');
  328. }
  329. @override
  330. void setMouseReportMode(MouseReportMode mode) {
  331. onCommand('setMouseReportMode($mode)');
  332. }
  333. @override
  334. void setAltBufferMouseScrollMode(bool enabled) {
  335. onCommand('setAltBufferMouseScrollMode($enabled)');
  336. }
  337. @override
  338. void setBracketedPasteMode(bool enabled) {
  339. onCommand('setBracketedPasteMode($enabled)');
  340. }
  341. @override
  342. void setUnknownDecMode(int mode, bool enabled) {
  343. onCommand('setUnknownDecMode($mode, $enabled)', error: true);
  344. }
  345. /* Select Graphic Rendition (SGR) */
  346. @override
  347. void resetCursorStyle() {
  348. onCommand('resetCursorStyle');
  349. }
  350. @override
  351. void setCursorBold() {
  352. onCommand('setCursorBold');
  353. }
  354. @override
  355. void setCursorFaint() {
  356. onCommand('setCursorFaint');
  357. }
  358. @override
  359. void setCursorItalic() {
  360. onCommand('setCursorItalic');
  361. }
  362. @override
  363. void setCursorUnderline() {
  364. onCommand('setCursorUnderline');
  365. }
  366. @override
  367. void setCursorBlink() {
  368. onCommand('setCursorBlink');
  369. }
  370. @override
  371. void setCursorInverse() {
  372. onCommand('setCursorInverse');
  373. }
  374. @override
  375. void setCursorInvisible() {
  376. onCommand('setCursorInvisible');
  377. }
  378. @override
  379. void setCursorStrikethrough() {
  380. onCommand('setCursorStrikethrough');
  381. }
  382. @override
  383. void unsetCursorBold() {
  384. onCommand('unsetCursorBold');
  385. }
  386. @override
  387. void unsetCursorFaint() {
  388. onCommand('unsetCursorFaint');
  389. }
  390. @override
  391. void unsetCursorItalic() {
  392. onCommand('unsetCursorItalic');
  393. }
  394. @override
  395. void unsetCursorUnderline() {
  396. onCommand('unsetCursorUnderline');
  397. }
  398. @override
  399. void unsetCursorBlink() {
  400. onCommand('unsetCursorBlink');
  401. }
  402. @override
  403. void unsetCursorInverse() {
  404. onCommand('unsetCursorInverse');
  405. }
  406. @override
  407. void unsetCursorInvisible() {
  408. onCommand('unsetCursorInvisible');
  409. }
  410. @override
  411. void unsetCursorStrikethrough() {
  412. onCommand('unsetCursorStrikethrough');
  413. }
  414. @override
  415. void setForegroundColor16(int color) {
  416. onCommand('setForegroundColor16($color)');
  417. }
  418. @override
  419. void setForegroundColor256(int index) {
  420. onCommand('setForegroundColor256($index)');
  421. }
  422. @override
  423. void setForegroundColorRgb(int r, int g, int b) {
  424. onCommand('setForegroundColorRgb($r, $g, $b)');
  425. }
  426. @override
  427. void resetForeground() {
  428. onCommand('resetForeground');
  429. }
  430. @override
  431. void setBackgroundColor16(int color) {
  432. onCommand('setBackgroundColor16($color)');
  433. }
  434. @override
  435. void setBackgroundColor256(int index) {
  436. onCommand('setBackgroundColor256($index)');
  437. }
  438. @override
  439. void setBackgroundColorRgb(int r, int g, int b) {
  440. onCommand('setBackgroundColorRgb($r, $g, $b)');
  441. }
  442. @override
  443. void resetBackground() {
  444. onCommand('resetBackground');
  445. }
  446. @override
  447. void unsupportedStyle(int param) {
  448. onCommand('unsupportedStyle($param)', error: true);
  449. }
  450. /* OSC */
  451. @override
  452. void setTitle(String name) {
  453. onCommand('setTitle($name)');
  454. }
  455. @override
  456. void setIconName(String name) {
  457. onCommand('setIconName($name)');
  458. }
  459. @override
  460. void unknownOSC(String ps) {
  461. onCommand('unknownOSC($ps)', error: true);
  462. }
  463. }