import { PythonEvaluator , ExecArgs } from "arepl-backend" ; import { PreviewContainer } from "./previewContainer" ; import pyGuiLibraryIsPresent from "./pyGuiLibraryIsPresent" ; import { settings } from "./settings" ; /**
 * formats text for passing into AREPL backend
 * Along the way decides whether backend needs restarting
 */ export class ToAREPLLogic { restartMode : boolean ; restartedLastTime = false ; lastSavedSection = "" ; lastCodeSection = "" ; lastEndSection = "" ; constructor ( private PythonEvaluator , private previewContainer ) { } public scanForUnsafeKeywords ( text , unsafeKeywords = [ ] ) { const commentChar = "#" ; if ( unsafeKeywords . length == 1 && unsafeKeywords [ 0 ] . trim ( ) == '' ) unsafeKeywords = [ ] ; if ( unsafeKeywords . length == 0 ) return false ; const unsafeKeywordsRe = new RegExp ( `^[^${ commentChar }]*${ unsafeKeywords . join ( '|' ) }` ) ; return unsafeKeywordsRe . test ( text ) ; } public onUserInput ( text , filePath , eol , showGlobalVars = true ) { const settingsCached = settings ( ) ; let codeLines = text . split ( eol ) ; let savedLines = [ ] ; let startLineNum = 0 ; let endLineNum = codeLines . length ; codeLines . forEach ( ( line , i ) => { if ( line . trimRight ( ) . endsWith ( "#$save" ) ) { savedLines = codeLines . slice ( 0 , i + 1 ) ; startLineNum = i + 1 ; } if ( line . trimRight ( ) . endsWith ( "#$end" ) ) { endLineNum = i + 1 ; return ; } } ) ; const endSection = codeLines . slice ( endLineNum ) . join ( eol ) ; codeLines = codeLines . slice ( startLineNum , endLineNum ) ; const unsafeKeywords = settingsCached . get < string [ ] > ( 'unsafeKeywords' ) ; const realTime = settingsCached . get < string > ( "whenToExecute" ) == "afterDelay" ; if ( realTime && this . scanForUnsafeKeywords ( codeLines . join ( eol ) , unsafeKeywords ) ) { throw Error ( "unsafeKeyword" ) ; } const data = { evalCode : codeLines . join ( eol ) , filePath , savedCode : savedLines . join ( eol ) , usePreviousVariables : settingsCached . get < boolean > ( 'keepPreviousVars' ) , show_global_vars : showGlobalVars , default_filter_vars : settingsCached . get < string [ ] > ( 'defaultFilterVars' ) , default_filter_types : settingsCached . get < string [ ] > ( 'defaultFilterTypes' ) } ; if ( endSection != this . lastEndSection && data . savedCode == this . lastSavedSection && data . evalCode == this . lastCodeSection ) { return false ; } this . lastCodeSection = data . evalCode ; this . lastSavedSection = data . savedCode ; this . lastEndSection = endSection ; this . restartMode = pyGuiLibraryIsPresent ( text ) ; if ( this . restartMode ) { this . checkSyntaxAndRestart ( data ) ; } else if ( this . restartedLastTime ) { this . restartPython ( data ) ; this . restartedLastTime = false ; } else { this . PythonEvaluator . execCode ( data ) ; } return true ; } /**
     * checks syntax before restarting - if syntax error it doesnt bother restarting but instead just shows syntax error
     * This is useful because we want to restart as little as possible
     */ private checkSyntaxAndRestart ( data ) { let syntaxPromise ; syntaxPromise = this . PythonEvaluator . checkSyntax ( data . savedCode + data . evalCode ) ; syntaxPromise . then ( ( ) => { this . restartPython ( data ) ; this . restartedLastTime = true ; } ) . catch ( ( error ) => { let internalErr = "" ; if ( typeof ( error ) != "string" ) { internalErr = error . message + '\n\n' + error . stack ; error = "" ; } this . previewContainer . handleResult ( { userVariables : { } , userError : null , userErrorMsg : < string > error , execTime : 0 , totalPyTime : 0 , totalTime : 0 , internalError : internalErr , caller : "" , lineno : - 1 , done : true , } ) ; } ) ; } private restartPython ( data ) { this . previewContainer . clearStoredData ( ) ; this . PythonEvaluator . restart ( this . PythonEvaluator . execCode . bind ( this . PythonEvaluator , data ) ) ; } }