/// import type { DeviceInfo, RokuDeploy } from 'roku-deploy'; import { LoggingDebugSession } from '@vscode/debugadapter'; import type { DebugProtocol } from '@vscode/debugprotocol'; import { ProjectManager } from '../managers/ProjectManager'; import type { LaunchConfiguration, ComponentLibraryConfiguration } from '../LaunchConfiguration'; import { FileManager } from '../managers/FileManager'; import { SourceMapManager } from '../managers/SourceMapManager'; import { LocationManager } from '../managers/LocationManager'; import { BreakpointManager } from '../managers/BreakpointManager'; import { FileLoggingManager } from '../logging'; export declare class BrightScriptDebugSession extends LoggingDebugSession { constructor(); start(inStream: NodeJS.ReadableStream, outStream: NodeJS.WritableStream): void; /** * Once the client has disconnected, drop outgoing events instead of writing to the dead stream. * Writing to a broken pipe re-triggers the base 'error' -> shutdown() handler in a tight loop. */ sendEvent(event: DebugProtocol.Event): void; setupProcessErrorHandlers(): void; private handlingProcessError; /** * True when an error indicates the client (e.g. VS Code) has gone away, such as a broken stdout pipe * (EPIPE). Writing to a dead pipe just produces more EPIPEs, so we must not try to report over it. */ private isClientGoneError; /** * Tear down the process error handlers and forcibly exit, so we never leave an orphaned adapter * spinning in the background after the client is gone or a graceful shutdown has hung. */ private forceExit; private handleProcessError; teardownProcessErrorHandlers(): void; private onDeviceBreakpointsChanged; logger: import("@rokucommunity/logger").Logger; private readonly isWindowsPlatform; /** * A sequence used to help identify log statements for requests */ private idCounter; fileManager: FileManager; projectManager: ProjectManager; fileLoggingManager: FileLoggingManager; private processErrorHandlersRegistered; private isCrashed; /** Set once the client (e.g. VS Code) disconnects, so we stop writing to a now-dead stream */ private clientDisconnected; /** How long to wait for a graceful shutdown before forcibly exiting the process */ private shutdownForceExitTimeout; private _uncaughtExceptionHandler; private _unhandledRejectionHandler; breakpointManager: BreakpointManager; locationManager: LocationManager; sourceMapManager: SourceMapManager; rokuDeploy: RokuDeploy; private componentLibraryServer; private rokuAdapterDeferred; /** * A promise that is resolved whenever the app has started running for the first time */ private firstRunDeferred; /** * Resolved whenever we're finished copying all the files to staging for all projects */ private stagingDefered; private evaluateRefIdLookup; private evaluateRefIdCounter; private variables; /** * Caches the device lookups performed while resolving completion requests. Variables don't change * while the debugger is paused, so this avoids repeated round-trips for the same path. Cleared by * `clearState` whenever the debugger resumes or steps. */ private completionParentVariableCache; private rokuAdapter; private perfettoManager; private rendezvousTracker; tempVarPrefix: string; /** * The progressId of the active launch progress bar, if any. * Cleared once the ProgressEndEvent is sent. */ private launchProgressId; /** * The first encountered compile error, will be used to send to the client as a runtime error (nicer UI presentation) */ private compileError; /** * A magic number to represent a fake thread that will be used for showing compile errors in the UI as if they were runtime crashes */ private COMPILE_ERROR_THREAD_ID; private get enableDebugProtocol(); /** * Check if the Roku firmware supports Perfetto tracing (requires OS 15.2 or higher) */ private get supportsPerfettoTracing(); /** * Get a promise that resolves when the roku adapter is ready to be used */ private getRokuAdapter; private launchConfiguration; private initRequestArgs; private exceptionBreakpoints; /** * The 'initialize' request is the first request called by the frontend * to interrogate the features the debug adapter provides. */ initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void; protected setExceptionBreakPointsRequest(response: DebugProtocol.SetExceptionBreakpointsResponse, args: DebugProtocol.SetExceptionBreakpointsArguments): Promise; protected setTransientsToInvalid(): Promise; private showPopupMessage; private static requestIdSequence; private sendCustomRequest; /** * Get the cwd from the launchConfiguration, or default to process.cwd() */ private get cwd(); deviceInfo: DeviceInfo; /** * Set defaults and standardize values for all of the LaunchConfiguration values * @param config * @returns */ private normalizeLaunchConfig; launchRequest(response: DebugProtocol.LaunchResponse, config: LaunchConfiguration): Promise; protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): Promise; /** * Activate all required functionality for profiling */ private initializeProfiling; /** * If profiling was marked "connectOnStart", try connecting right away */ private tryProfilingConnectOnStart; /** * Clear certain properties that need reset whenever a debug session is restarted (via vscode or launched from the Roku home screen) */ private resetSessionState; /** * Activate rendezvous tracking (IF enabled in the LaunchConfig) */ initRendezvousTracking(): Promise; private _initRendezvousTracking; /** * Anytime a roku adapter emits diagnostics, this method is called to handle it. */ private handleDiagnostics; private publishTimeout; private publish; private pendingSendLogPromise; /** * Send log output to the "client" (i.e. vscode) * @param logOutput */ private sendLogOutput; /** * Extracts potential package paths from a given line of text. * * This method uses a regular expression to find matches in the provided line * and returns an array of objects containing details about each match. * * @param input - The line of text to search for potential package paths. * @returns An array of objects, each containing: * - `fullMatch`: The full matched string. * - `path`: The extracted path from the match. * - `lineNumber`: The line number extracted from the match. * - `columnNumber`: The column number extracted from the match, or `undefined` if not found. */ private getPotentialPkgPaths; /** * Converts the filename property in backtrace objects in the given input string to source paths if found */ private convertBacktracePaths; private runAutomaticSceneGraphCommands; /** * Stage, insert breakpoints, and package the main project */ prepareMainProject(): Promise; /** * Inject breakpoint STOP statements into the staged main project and create the zip package. * Runs after the DAP `InitializedEvent` so client-side `setBreakpoints` requests have landed * before any STOPs are written to the staged .brs files (telnet path) and before the zip is sealed. */ private packageMainProject; /** * Accepts custom events and requests from the extension * @param command name of the command to execute */ protected customRequest(command: string, response: DebugProtocol.Response, args: any): Promise; /** * Stores the path to the staging folder for each component library */ protected prepareComponentLibraries(componentLibraries: ComponentLibraryConfiguration[]): Promise; /** * Inject breakpoint STOPs into the staged complibs, seal their zips, install installable ones, * and start static file hosting. Runs in `configurationDoneRequest` (after `InitializedEvent`) * so client-side `setBreakpoints` requests have landed before the staged .brs files are written * and the zips are sealed. */ protected packageAndHostComponentLibraries(componentLibraries: ComponentLibraryConfiguration[], port: number): Promise; protected sourceRequest(response: DebugProtocol.SourceResponse, args: DebugProtocol.SourceArguments): void; /** * Called every time a breakpoint is created, modified, or deleted, for each file. This receives the entire list of breakpoints every time. */ setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): Promise; protected exceptionInfoRequest(response: DebugProtocol.ExceptionInfoResponse, args: DebugProtocol.ExceptionInfoArguments): void; protected threadsRequest(response: DebugProtocol.ThreadsResponse): Promise; /** * Get the thread name to display in the UI based on the thread info we have. * This is what displays in the `call stack` region in vscode * @param thread * @returns */ private getThreadName; protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): Promise; protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): Promise; /** * Get the locals scope container for a frame, creating an (unpopulated) one if it doesn't exist yet. * The child variables are filled in lazily by `populateScopeVariables`. */ private getOrCreateLocalsScope; protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): Promise; protected pauseRequest(response: DebugProtocol.PauseResponse, args: DebugProtocol.PauseArguments): Promise; protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments): void; /** * Clicked the "Step Over" button * @param response * @param args */ protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise; protected stepInRequest(response: DebugProtocol.StepInResponse, args: DebugProtocol.StepInArguments): Promise; protected stepOutRequest(response: DebugProtocol.StepOutResponse, args: DebugProtocol.StepOutArguments): Promise; protected stepBackRequest(response: DebugProtocol.StepBackResponse, args: DebugProtocol.StepBackArguments): void; variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments): Promise; private debounceSendInvalidatedEvent; private filterVariablesUpdates; /** * Takes a scope variable and populates its child variables based on the scope type and the current adapter type. * @param v scope variable to populate * @param args */ private populateScopeVariables; private evaluateRequestPromise; private evaluateVarIndexByFrameId; private getNextVarIndex; evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): Promise; private evaluateExpressionToTempVar; private bulkEvaluateExpressionToTempVar; protected completionsRequest(response: DebugProtocol.CompletionsResponse, args: DebugProtocol.CompletionsArguments, request?: DebugProtocol.Request): Promise; /** * Gets the closest completion details the incoming completion request. */ private getClosestCompletionDetails; /** * Compute the span of input text that a completion replaces: the run of identifier characters * immediately before the cursor. This lets the client filter the list correctly as the user keeps * typing past the first character. * * `start` is a 0-based offset into the line, NOT a client column. Per the Debug Adapter Protocol, * `CompletionItem.start` is measured in UTF-16 code units and the client maps it to a position * itself, so it must not be run through `toClientColumn` (unlike stack-frame, breakpoint, and scope * positions). Our debugger column base is already 0-based, so the internal offset is sent as-is. */ private getCompletionReplaceRange; /** * Scan backwards from `column` to find the nearest opening bracket (`(`, `[`, or `{`) that has not * been closed before the cursor. Returns the opener's index and character, or undefined if none. */ private findUnclosedOpener; /** * Resolve the parent variable for a completion request. Prefers the in-memory locals for the frame, * then falls back to a device lookup. Device lookups are cached for the duration of the paused state * (cleared by `clearState`) so repeated completion requests on the same path don't hammer the device. */ private resolveCompletionParentVariable; /** * Rebuild a valid BrightScript accessor expression from a resolved variable path. String-literal keys * arrive already quoted from `getVariablePath` and are emitted as `["key"]` so they stay case-sensitive * on the device (Roku AAs can be set case-sensitive); numeric segments use `[index]`, and identifiers * use dot access. This keeps array indices and string keys correct through the device lookup. */ private buildVariableExpression; /** * Normalize a variable path segment or variable name for matching: drop surrounding string-key quotes * and lower-case it. BrightScript variables and dotted access are case-insensitive, and the device * reports names lower-cased, so this lets the in-memory lookup find the parent regardless of the casing * the user typed (ex: `topRef` matching the cached `topref`). */ private normalizeVariableName; /** * Resolve a variable path against the current frame's local scope. The first path segment is matched * against the frame's locals (not the global pool of every materialized variable), then we walk down * the child variables. The empty path (`['']`) resolves to the locals scope container itself. */ private findFrameVariableByPath; private findVariableByPath; private debuggerVarTypeToRoType; /** * Called when the host stops debugging * @param response * @param args */ protected disconnectRequest(response: DebugProtocol.DisconnectResponse, args: DebugProtocol.DisconnectArguments, request?: DebugProtocol.Request): Promise; private createRokuAdapter; protected restartRequest(response: DebugProtocol.RestartResponse, args: DebugProtocol.RestartArguments, request?: DebugProtocol.Request): Promise; private exitAppTimeout; private ensureAppIsInactive; /** * Used to track whether the entry breakpoint has already been handled */ private entryBreakpointWasHandled; /** * Registers the main events for the RokuAdapter */ private connectRokuAdapter; private onSuspend; private setupSuspendedState; private getVariableFromResult; /** * Helper function to merge the results of an evaluate call into an existing EvaluateContainer * Used primarily for custom variables */ private mergeEvaluateContainers; private getEvaluateRefId; private clearState; /** * Sends a launch progress event to the client if the client supports progress reporting. * - `'start'`: begins a new progress bar with the given message. Assigns a new progressId. * - `'update'`: updates the message on the active progress bar. * - `'end'`: dismisses the active progress bar with an optional final message. */ private sendLaunchProgress; /** * Tells the client to re-request all variables because we've invalidated them * @param threadId * @param stackFrameId */ private sendInvalidatedEvent; /** * If `stopOnEntry` is enabled, register the entry breakpoint. */ handleEntryBreakpoint(): Promise; /** * Converts a debugger line number to a client line number. * * @param debuggerLine - The line number from the debugger as zero based. * @param defaultDebuggerLine - An optional default line number, as zero based, to use if `debuggerLine` is not provided. * @returns The corresponding client line number. */ private toClientLine; /** * Converts a debugger column number to a client column number. * * @param debuggerLine - The column number from the debugger as zero based. * @param defaultDebuggerLine - An optional default column number, as zero based, to use if `debuggerLine` is not provided. * @returns The corresponding client column number. */ private toClientColumn; /** * Converts a client line number to a debugger line number. * * @param clientLine - The line number from the client. * @param defaultDebuggerLine - An optional default line number, as zero based, to use if `clientLine` is not provided. * @returns The corresponding debugger line number as zero based. */ private toDebuggerLine; /** * Converts a client column number to a debugger column number. * * @param clientLine - The column number from the client. * @param defaultDebuggerLine - An optional default column number, as zero based, to use if `clientLine` is not provided. * @returns The corresponding debugger column number as zero based. */ private toDebuggerColumn; private shutdownPromise; /** * Called when the debugger is terminated. Feel free to call this as frequently as you want; we'll only run the shutdown process the first time, and return * the same promise on subsequent calls */ shutdown(errorMessage?: string, modal?: boolean): Promise; private _shutdown; } export interface AugmentedVariable extends DebugProtocol.Variable { childVariables?: AugmentedVariable[]; request_seq?: number; frameId?: number; /** * only used for lazy variables */ isResolved?: boolean; /** * used to indicate that this variable is a scope variable * and may require special handling */ isScope?: boolean; }