99 - Ctrl+arrows
1010 - Ctrl+L
1111 - Ctrl+C
12- - code cleanup
12+ - code cleanup, greatly simplify terminal handling
1313 - bundle Scapy
1414-->
1515
@@ -47,7 +47,6 @@ var pythonCodeY = 0;
4747var historyCodeList: string [] = [];
4848var lastPythonCodeLine = " " ;
4949var renderingCode = true ;
50- var stdout_codes: Array <number > = [];
5150
5251// pyodide
5352var pyodide: any = null ;
@@ -56,6 +55,12 @@ var pyodide: any = null;
5655const terminal: Ref <HTMLElement | null > = ref (null ); // container
5756const term = new Terminal (); // xterm.js object
5857
58+ // Python terminal
59+ var pythonCode = ' ' ;
60+ var historyIndex = 0 ;
61+ var historyCode = " " ;
62+ var lastCRIndex = 0 ;
63+
5964export function mountFile(file : File ) {
6065 /*
6166 * Mount a File
@@ -71,13 +76,6 @@ export function mountFile(file: File) {
7176 freader .readAsArrayBuffer (file );
7277}
7378
74- function rawstdout(code : number ) {
75- /*
76- * Add string to terminal output
77- */
78- stdout_codes .push (code );
79- }
80-
8179function recalcFromPythonCode() {
8280 /*
8381 * Recalculate cursor position based on
@@ -104,19 +102,6 @@ function prompt() {
104102 term .write (' \r\x1b [34m>>> ' );
105103};
106104
107- var pythonCode = ' ' ;
108- var blockFlag = " " ;
109- var blockMap: { [type : string ]: string } = {
110- " :" : " \r " ,
111- " \\ " : " \r " ,
112- " {" : " }" ,
113- " [" : " ]" ,
114- " (" : " )" ,
115- }
116- var historyIndex = 0 ;
117- var historyCode = " " ;
118- var lastCRIndex = 0 ;
119-
120105function setCursorPosition(x : Number , y : Number ) {
121106 /*
122107 * Set the cursor position in the terminal
@@ -141,6 +126,17 @@ async function writeHighlightPythonCode(x: Number, y: Number, pythonCode: string
141126 });
142127}
143128
129+ function writeByteOnTerm(data : number ) {
130+ /*
131+ * Write to terminal output. Called directly by pyodide
132+ */
133+ if (data == 10 ) { // \n
134+ term .write (" \r\n " );
135+ } else {
136+ term .write (new Uint8Array ([data ]));
137+ }
138+ }
139+
144140function displayCurrentPrompt() {
145141 /*
146142 * Display the prompt string based on the state
@@ -237,47 +233,33 @@ term.onData(e => {
237233 let pythonCodeList = pythonCode .split (' \r ' );
238234 let lastLine = pythonCodeList [pythonCodeList .length - 1 ];
239235 if (lastLine .length > 0 ) {
240- historyCodeList = historyCodeList .concat (lastLine )
241- }
242- if (((pythonCode [pythonCode .length - 1 ] in blockMap )) && (blockFlag === " " )) {
243- blockFlag = pythonCode [pythonCode .length - 1 ];
244- pythonCode += e ;
245- term .writeln (" \r " );
246- term .write (' ... ' );
247- break ;
248- }
249- if (blockFlag != " " ) {
250- if (pythonCode [pythonCode .length - 1 ] === blockMap [blockFlag ]) {
251- blockFlag = " " ;
252- } else {
253- pythonCode += e ;
254- term .writeln (" \r " );
255- term .write (' ... ' );
256- break ;
257- }
236+ historyCodeList = historyCodeList .concat (lastLine );
258237 }
259238 term .writeln (' \x1b [0m' );
260-
261- stdout_codes = []
262- pyodide .runPythonAsync (pythonCode ).then ((output : string ) => {
263- let result = new TextDecoder ().decode (new Uint8Array (stdout_codes ));
264- if (result .length > 0 ) {
265- term .write (result .replaceAll (" \n " , " \r\n " ));
266- } else if (output != undefined ) {
267- term .write (output .toString ().replaceAll (" \n " , " \r\n " ) + ' \r\n ' );
239+ // We use code.InteractiveInterpreter to take detect incomplete inputs
240+ pyodide .runPythonAsync (`
241+ _PY_code = """
242+ ${pythonCode .replaceAll (" \\ " , " \\\\ " )}
243+ """
244+ _PY_EVAL.runsource(_PY_code)
245+ ` ).then ((incomplete : boolean ) => {
246+ if (incomplete ) {
247+ pythonCode += " \r " ;
248+ term .write (" \r " );
249+ term .write (' ... ' );
250+ } else {
251+ pythonCode = ' ' ;
252+ prompt ();
268253 }
269- prompt ();
270254 }).catch ((err : any ) => {
271255 term .write (' \x1b [01;31m' + err .message .replaceAll (' \n ' , ' \r\n ' ) + ' \x1b [0m' );
272256 prompt ();
273- pythonCode = " "
257+ pythonCode = ' ' ;
274258 });
275-
276259 } else {
277260 term .writeln (' \x1b [0m' );
278261 prompt ();
279262 }
280- pythonCode = ' ' ;
281263 break ;
282264 case VK_CANCEL :
283265 term .write (' ^C\r\n ' );
@@ -313,7 +295,7 @@ term.onData(e => {
313295 break ;
314296 default :
315297 // debug
316- for (let i = 0 ; i < e .length ; i ++ ) console .log (e .charCodeAt (i ));
298+ // for (let i = 0; i < e.length; i++) console.log(e.charCodeAt(i));
317299 // other key pressed
318300 if (e >= String .fromCharCode (0x20 ) && e <= String .fromCharCode (0x7E ) || e >= ' \u00a0 ' ) {
319301 // printable
@@ -368,41 +350,45 @@ async function startPyodide() {
368350 await pyodide .loadPackage (" micropip" )
369351 await pyodide .loadPackage (" ssl" )
370352
353+ // TODO: move output handlers here... when all Scapy loading issues are fixes
354+
371355 term .write (' \r Loading Scapy... ' );
372356 await pyodide .loadPackage (scapyWheelURL );
373357 // await scapyInstall();
374358 await pyodide .runPythonAsync (`
375359 from scapy.all import *
376360 conf.color_theme = DefaultTheme()
361+ import code
362+ _PY_EVAL = code.InteractiveInterpreter(locals=globals())
377363 ` );
378364
379- pyodide .setStdout ({ raw: rawstdout , isatty: true });
365+ // Register output stdout / stderr
366+ pyodide .setStdout ({ raw: writeByteOnTerm , isatty: true });
367+ pyodide .setStderr ({ raw: writeByteOnTerm , isatty: true });
380368
381369 let mini = " False" ;
382370 if (smAndDown .value ) {
383371 mini = " True" ;
384372 }
385373
374+ term .write (' \r ' );
386375 await pyodide .runPythonAsync (`
387376 import sys
388377 from pygments import highlight
389378 from pygments.lexers import PythonLexer
390379 from pygments.formatters import TerminalTrueColorFormatter
391380 print(get_fancy_banner( ` + mini + ` ))
392381 ` ).then (() => {
393- let result = new TextDecoder ().decode (new Uint8Array (stdout_codes ));
394- term .write (' \r ' + result .replaceAll (" \n " , " \r\n " ) + ' \r\n ' );
395382 prompt ();
396383 renderingCode = false ;
397- }
398- );
384+ });
399385}
400386
401387// Startup hook
402388onMounted (async () => {
403389 // reset existing
390+ term .clear ();
404391 term .reset ();
405- stdout_codes = [];
406392 historyCodeList = [];
407393 // start xterm.js and pyodide
408394 startXterm (term , terminal ).then (startPyodide ).catch ((ex ) => {
@@ -417,11 +403,6 @@ onMounted(async () => {
417403 } catch { }
418404 });
419405});
420-
421- // Exit hook
422- onUnmounted (() => {
423- term.dispose();
424- });
425406 </script >
426407
427408<style >
0 commit comments