'use strict' ; import { Logger , logger , LoggingDebugSession , InitializedEvent , TerminatedEvent , StoppedEvent , BreakpointEvent , OutputEvent , Thread , StackFrame , Scope , Source , Handles , Breakpoint } from 'vscode-debugadapter' ; import { DebugProtocol } from 'vscode-debugprotocol' ; import { basename } from 'path' ; import { PlanDebugRuntime , HappeningBreakpoint } from './PlanDebugRuntime' ; const { Subject } = require ( 'await-notify' ) ; /**
 * This interface describes the plan-debug specific launch attributes
 * (which are not part of the Debug Adapter Protocol).
 * The schema for these attributes lives in the package.json of the extension.
 * The interface should always match this schema.
 */ interface LaunchRequestArguments extends DebugProtocol . LaunchRequestArguments { /** An absolute path to the "plan" to debug. */ program : string ; /** Automatically stop target after launch. If not specified, target does not stop. */ stopOnEntry ? : boolean ; /** enable logging the Debug Adapter Protocol */ trace ? : boolean ; } export class PlanDebugSession extends LoggingDebugSession { private static THREAD_ID = 1 ; private _runtime : PlanDebugRuntime ; private _variableHandles = new Handles < string > ( ) ; private _configurationDone = new Subject ( ) ; /**
     * Creates a new debug adapter that is used for one debug session.
     * We configure the default implementation of a debug adapter here.
     */ public constructor ( ) { super ( "pddl-happenings-debug.txt" ) ; this . setDebuggerLinesStartAt1 ( false ) ; this . setDebuggerColumnsStartAt1 ( false ) ; this . _runtime = new PlanDebugRuntime ( ) ; this . _runtime . on ( 'stopOnEntry' , ( ) => { this . sendEvent ( new StoppedEvent ( 'entry' , PlanDebugSession . THREAD_ID ) ) ; } ) ; this . _runtime . on ( 'stopOnStep' , ( ) => { this . sendEvent ( new StoppedEvent ( 'step' , PlanDebugSession . THREAD_ID ) ) ; } ) ; this . _runtime . on ( 'stopOnBreakpoint' , ( ) => { this . sendEvent ( new StoppedEvent ( 'breakpoint' , PlanDebugSession . THREAD_ID ) ) ; } ) ; this . _runtime . on ( 'stopOnException' , ( ) => { this . sendEvent ( new StoppedEvent ( 'exception' , PlanDebugSession . THREAD_ID ) ) ; } ) ; this . _runtime . on ( 'breakpointValidated' , ( bp ) => { this . sendEvent ( new BreakpointEvent ( 'changed' , < DebugProtocol . Breakpoint > { verified : bp . verified , id : bp . id } ) ) ; } ) ; this . _runtime . on ( 'output' , ( text , filePath , line , column ) => { const e = new OutputEvent ( `${ text }\n` ) ; e . body . source = this . createSource ( filePath ) ; e . body . line = this . convertDebuggerLineToClient ( line ) ; e . body . column = this . convertDebuggerColumnToClient ( column ) ; this . sendEvent ( e ) ; } ) ; this . _runtime . on ( 'end' , ( ) => { this . sendEvent ( new TerminatedEvent ( ) ) ; } ) ; } /**
     * The 'initialize' request is the first request called by the frontend
     * to interrogate the features the debug adapter provides.
     */ protected initializeRequest ( response , _ ) { response . body = response . body || { } ; response . body . supportsConfigurationDoneRequest = true ; response . body . supportsEvaluateForHovers = true ; response . body . supportsStepBack = true ; this . sendResponse ( response ) ; this . sendEvent ( new InitializedEvent ( ) ) ; } /**
     * Called at the end of the configuration sequence.
     * Indicates that all breakpoints etc. have been sent to the DA and that the 'launch' can start.
     */ protected configurationDoneRequest ( response , args ) { super . configurationDoneRequest ( response , args ) ; this . _configurationDone . notify ( ) ; } protected async launchRequest ( response , args ) { logger . setup ( args . trace ? Logger . LogLevel . Verbose : Logger . LogLevel . Stop , false ) ; await this . _configurationDone . wait ( 1000 ) ; this . _runtime . start ( args . program , ! ! args . stopOnEntry ) ; this . sendResponse ( response ) ; } protected setBreakPointsRequest ( response , args ) { const path = args . source . path as string ; const clientLines = args . lines || [ ] ; this . _runtime . clearBreakpoints ( path ) ; const actualBreakpoints = clientLines . map ( l => { const { verified , line , id } = this . _runtime . setBreakPoint ( path , this . convertClientLineToDebugger ( l ) ) ; const bp = new Breakpoint ( verified , this . convertDebuggerLineToClient ( line ) ) as DebugProtocol . Breakpoint ; bp . id = id ; return bp ; } ) ; response . body = { breakpoints : actualBreakpoints } ; this . sendResponse ( response ) ; } protected threadsRequest ( response ) { response . body = { threads : [ new Thread ( PlanDebugSession . THREAD_ID , "thread 1" ) ] } ; this . sendResponse ( response ) ; } protected stackTraceRequest ( response , args ) { const startFrame = typeof args . startFrame === 'number' ? args . startFrame : 0 ; const maxLevels = typeof args . levels === 'number' ? args . levels : 1000 ; const endFrame = startFrame + maxLevels ; const stk = this . _runtime . stack ( startFrame , endFrame ) ; response . body = { stackFrames : stk . frames . map ( ( f ) => new StackFrame ( f . index , f . name , this . createSource ( f . file ) , this . convertDebuggerLineToClient ( f . line ) ) ) , totalFrames : stk . count } ; this . sendResponse ( response ) ; } protected scopesRequest ( response , args ) { const frameReference = args . frameId ; const scopes = new Array < Scope > ( ) ; scopes . push ( new Scope ( "Local" , this . _variableHandles . create ( "local_" + frameReference ) , false ) ) ; scopes . push ( new Scope ( "Global" , this . _variableHandles . create ( "global_" + frameReference ) , true ) ) ; response . body = { scopes : scopes } ; this . sendResponse ( response ) ; } protected variablesRequest ( response , args ) { const variables = new Array < DebugProtocol . Variable > ( ) ; const id = this . _variableHandles . get ( args . variablesReference ) ; if ( id !== null ) { variables . push ( { name : "predicate1" , type : "boolean" , value : Math . random ( ) > 0.5 ? "true" : "false" , variablesReference : 0 } ) ; variables . push ( { name : "function2" , type : "float" , value : `${ Math . random ( ) * 10 }` , variablesReference : 0 } ) ; variables . push ( { name : "object3" , type : "object" , value : "Object" , variablesReference : this . _variableHandles . create ( "object_" ) } ) ; } response . body = { variables : variables } ; this . sendResponse ( response ) ; } protected continueRequest ( response , _ ) { this . _runtime . continue ( ) ; this . sendResponse ( response ) ; } protected reverseContinueRequest ( response , _ ) { this . _runtime . continue ( true ) ; this . sendResponse ( response ) ; } protected nextRequest ( response , _ ) { this . _runtime . step ( ) ; this . sendResponse ( response ) ; } protected stepBackRequest ( response , _ ) { this . _runtime . step ( true ) ; this . sendResponse ( response ) ; } protected evaluateRequest ( response , args ) { let reply = undefined ; if ( args . context === 'repl' ) { const matches = /new +([0-9]+)/ . exec ( args . expression ) ; if ( matches && matches . length === 2 ) { const mbp = this . _runtime . setBreakPoint ( this . _runtime . sourceFile , this . convertClientLineToDebugger ( parseInt ( matches [ 1 ] ) ) ) ; const bp = < DebugProtocol . Breakpoint > new Breakpoint ( mbp . verified , this . convertDebuggerLineToClient ( mbp . line ) , undefined , this . createSource ( this . _runtime . sourceFile ) ) ; bp . id = mbp . id ; this . sendEvent ( new BreakpointEvent ( 'new' , bp ) ) ; reply = `breakpoint created` ; } else { const matches = /del +([0-9]+)/ . exec ( args . expression ) ; if ( matches && matches . length === 2 ) { const mbp = this . _runtime . clearBreakPoint ( this . _runtime . sourceFile , this . convertClientLineToDebugger ( parseInt ( matches [ 1 ] ) ) ) ; if ( mbp ) { const bp = < DebugProtocol . Breakpoint > new Breakpoint ( false ) ; bp . id = mbp . id ; this . sendEvent ( new BreakpointEvent ( 'removed' , bp ) ) ; reply = `breakpoint deleted` ; } } } } response . body = { result : reply ? reply : `evaluate(context: '${ args . context }', '${ args . expression }')` , variablesReference : 0 } ; this . sendResponse ( response ) ; } private createSource ( filePath ) { return new Source ( basename ( filePath ) , this . convertDebuggerPathToClient ( filePath ) , undefined , undefined , 'plan-debug-adapter-data' ) ; } }