import { DebugSession , ErrorDestination , OutputEvent , InitializedEvent , StackFrame , Source , Scope , Thread } from "vscode-debugadapter" ; import { DebugProtocol } from "vscode-debugprotocol" ; import { ITraceCmds , LaunchRequestArguments , PrologDebugger } from "./prologDebugger" ; import * as path from "path" ; import { spawn , SpawnOptions } from "process-promises" ; export class PrologDebugSession extends DebugSession { private static SCOPEREF = 1 ; public static THREAD_ID = 100 ; private _prologDebugger : PrologDebugger ; private _runtimeExecutable : string ; private _startupQuery : string ; private _startFile : string ; private _cwd : string ; private _stopOnEntry : boolean ; private _traceCmds : ITraceCmds ; private _currentVariables : DebugProtocol . Variable [ ] = [ ] ; private _stackFrames : DebugProtocol . StackFrame [ ] = [ ] ; private _debugging : boolean ; public constructor ( ) { super ( ) ; this . setDebuggerColumnsStartAt1 ( true ) ; this . setDebuggerLinesStartAt1 ( true ) ; this . setDebuggerPathFormat ( "native" ) ; } protected initializeRequest ( response , args ) { response . body = { supportsConfigurationDoneRequest : true , supportTerminateDebuggee : true , supportsConditionalBreakpoints : true , supportsHitConditionalBreakpoints : true , supportsFunctionBreakpoints : true , supportsEvaluateForHovers : true , supportsExceptionOptions : true , supportsExceptionInfoRequest : true , exceptionBreakpointFilters : [ { filter : "Notice" , label : "Notices" } , { filter : "Warning" , label : "Warnings" } , { filter : "Error" , label : "Errors" } , { filter : "Exception" , label : "Exceptions" } , { filter : "*" , label : "Everything" , default : true } ] } ; this . sendResponse ( response ) ; } protected attachRequest ( response , args ) { this . sendErrorResponse ( response , new Error ( "Attach requests are not supported" ) ) ; this . shutdown ( ) ; } public addStackFrame ( frame ) { this . _stackFrames . unshift ( new StackFrame ( frame . id , `(${ frame . level })${ frame . name }` , new Source ( path . basename ( frame . file ) , this . convertDebuggerPathToClient ( frame . file ) ) , this . convertDebuggerLineToClient ( frame . line ) , this . convertDebuggerColumnToClient ( frame . column ) ) ) ; } public setCurrentVariables ( vars ) { this . _currentVariables = [ ] ; while ( vars . length > 0 ) { this . _currentVariables . push ( vars . pop ( ) ) ; } } protected async launchRequest ( response , args ) { this . _startupQuery = args . startupQuery || "start" ; this . _startFile = path . resolve ( args . program ) ; this . _cwd = args . cwd ; this . _runtimeExecutable = args . runtimeExecutable || "swipl" ; this . _stopOnEntry = typeof args . stopOnEntry ? args . stopOnEntry : true ; this . _traceCmds = args . traceCmds ; this . _prologDebugger = await new PrologDebugger ( args , this ) ; this . _prologDebugger . addListener ( "responseBreakpoints" , ( bps ) => { this . sendResponse ( bps ) ; } ) ; this . _prologDebugger . addListener ( "responseFunctionBreakpoints" , ( fbps ) => { this . sendResponse ( fbps ) ; } ) ; this . sendResponse ( response ) ; this . sendEvent ( new InitializedEvent ( ) ) ; } protected threadsRequest ( response ) { response . body = { threads : [ new Thread ( PrologDebugSession . THREAD_ID , "thread 1" ) ] } ; this . sendResponse ( response ) ; } protected setBreakPointsRequest ( response , args ) { if ( this . _debugging ) { this . debugOutput ( "Breakpoints set during debugging would take effect in next debugging process." ) ; return ; } this . _prologDebugger . setBreakpoints ( args , response ) ; } protected setExceptionBreakPointsRequest ( response , args ) { this . sendResponse ( response ) ; } protected setFunctionBreakPointsRequest ( response , args ) { this . _prologDebugger . setFunctionBreakpoints ( args , response ) ; } protected configurationDoneRequest ( response , args ) { this . sendResponse ( response ) ; this . _prologDebugger . startup ( `${ this . _startupQuery }` ) ; if ( ! this . _stopOnEntry ) { this . _prologDebugger . query ( `cmd:${ this . _traceCmds . continue [ 1 ] }\n` ) ; } this . _debugging = true ; } private evaluateExpression ( exp ) { const vars = this . _currentVariables ; for ( let i = 0 ; i < vars . length ; i ++ ) { if ( vars [ i ] . name === exp ) { return vars [ i ] . value ; } } return null ; } protected evaluateRequest ( response , args ) { let val = this . evaluateExpression ( args . expression ) ; if ( val ) { response . body = { result : val , variablesReference : 0 } ; this . sendResponse ( response ) ; return ; } else { if ( args . context === "repl" ) { const vars = this . _currentVariables ; let exp = args . expression . trim ( ) ; if ( exp . startsWith ( ":" ) ) { let input = "input" + args . expression ; this . _prologDebugger . query ( input + "\n" ) ; } else { for ( let i = 0 ; i < vars . length ; i ++ ) { let re = new RegExp ( "\\b" + vars [ i ] . name + "\\b" , "g" ) ; exp = exp . replace ( re , vars [ i ] . value ) ; } this . debugOutput ( args . expression ) ; this . evaluate ( exp ) ; } response . body = { result : "" , variablesReference : 0 } ; } this . sendResponse ( response ) ; return ; } } protected stackTraceRequest ( response , args ) { response . body = { stackFrames : this . _stackFrames } ; this . sendResponse ( response ) ; } protected variablesRequest ( response , args ) { const variables = new Array < DebugProtocol . Variable > ( ) ; for ( let i = 0 ; i < this . _currentVariables . length ; i ++ ) { variables . push ( this . _currentVariables [ i ] ) ; } response . body = { variables : variables } ; this . sendResponse ( response ) ; } protected scopesRequest ( response , args ) { const scopes = new Array < Scope > ( ) ; scopes . push ( new Scope ( "Local" , PrologDebugSession . SCOPEREF ++ , false ) ) ; response . body = { scopes : scopes } ; this . sendResponse ( response ) ; } protected continueRequest ( response , args ) { this . _prologDebugger . query ( `cmd:${ this . _traceCmds . continue [ 1 ] }\n` ) ; this . sendResponse ( response ) ; } protected nextRequest ( response , args ) { this . _prologDebugger . query ( `cmd:${ this . _traceCmds . stepover [ 1 ] }\n` ) ; this . sendResponse ( response ) ; } protected stepInRequest ( response , args ) { this . _prologDebugger . query ( `cmd:${ this . _traceCmds . stepinto [ 1 ] }\n` ) ; this . sendResponse ( response ) ; } protected stepOutRequest ( response , args ) { this . _prologDebugger . query ( `cmd:${ this . _traceCmds . stepout [ 1 ] }\n` ) ; this . sendResponse ( response ) ; } protected disconnectRequest ( response , args ) { this . _debugging = false ; this . _prologDebugger . dispose ( ) ; this . shutdown ( ) ; this . sendResponse ( response ) ; } protected restartRequest ( response , args ) { this . _debugging = false ; this . _prologDebugger . dispose ( ) ; this . shutdown ( ) ; this . sendResponse ( response ) ; } protected sendErrorResponse ( response , error , dest ? ) ; protected sendErrorResponse ( response , codeOrMessage , format ? , variables ? , dest ? ) ; protected sendErrorResponse ( response ) { if ( arguments [ 1 ] instanceof Error ) { const error = arguments [ 1 ] as Error & { code ? : number | string ; errno ? : number ; } ; const dest = arguments [ 2 ] as ErrorDestination ; let code ; if ( typeof error . code === "number" ) { code = error . code as number ; } else if ( typeof error . errno === "number" ) { code = error . errno ; } else { code = 0 ; } super . sendErrorResponse ( response , code , error . message , dest ) ; } else { super . sendErrorResponse ( response , arguments [ 1 ] , arguments [ 2 ] , arguments [ 3 ] , arguments [ 4 ] ) ; } } public debugOutput ( msg ) { this . sendEvent ( new OutputEvent ( msg ) ) ; } private evaluate ( expression ) { let exec = this . _runtimeExecutable ; let args = [ "-q" , `${ this . _startFile }` ] ; let input = `
    call((${ expression })).
    halt.
    ` ; let spawnOptions = { cwd : this . _cwd } ; spawn ( exec , args , spawnOptions ) . on ( "process" , proc => { if ( proc . pid ) { proc . stdin . write ( input ) ; proc . stdin . end ( ) ; } } ) . on ( "stdout" , data => { this . debugOutput ( "\n" + data ) ; } ) . on ( "stderr" , err => { this . debugOutput ( "\n" + err ) ; } ) . catch ( err => { this . debugOutput ( err . message ) ; } ) ; } } DebugSession . run ( PrologDebugSession ) ;