@@ -11,12 +11,96 @@ import {
1111 Handles ,
1212} from '@vscode/debugadapter' ;
1313import { DebugProtocol } from '@vscode/debugprotocol' ;
14+ import { execFileSync as nodeExecFileSync } from 'child_process' ;
15+ import * as fs from 'fs' ;
1416import * as vscode from 'vscode' ;
1517import * as path from 'path' ;
1618
1719import { outputChannel } from './extension' ;
1820import { LogDebugger } from './logDebugger' ;
1921
22+ export interface ProcessRunner {
23+ execFileSync ( file : string , args : string [ ] ) : Buffer ;
24+ readFile ( path : string ) : Buffer ;
25+ }
26+
27+ const defaultProcessRunner : ProcessRunner = {
28+ execFileSync : ( file : string , args : string [ ] ) : Buffer =>
29+ nodeExecFileSync ( file , args ) as Buffer ,
30+ readFile : ( path : string ) : Buffer =>
31+ fs . readFileSync ( path )
32+ } ;
33+
34+ export interface EditorEffects {
35+ openAndFocus ( log : string , line : number ) : void ;
36+ highlightLine ( log : string , line : number ) : void ;
37+ clearHighlights ( ) : void ;
38+ }
39+
40+ class DefaultEditorEffects implements EditorEffects {
41+ private readonly _highlightDecoration : vscode . TextEditorDecorationType ;
42+
43+ constructor ( ) {
44+ const focusColor = new vscode . ThemeColor ( 'editor.focusedStackFrameHighlightBackground' ) ;
45+ this . _highlightDecoration = vscode . window . createTextEditorDecorationType ( {
46+ backgroundColor : focusColor
47+ } ) ;
48+ }
49+
50+ public openAndFocus ( log : string , line : number ) : void {
51+ const editors = this . findEditors ( log ) ;
52+ if ( editors . length >= 1 ) {
53+ this . focusEditor ( editors [ 0 ] , line ) ;
54+ } else {
55+ Promise . resolve ( vscode . workspace . openTextDocument ( log ) )
56+ . then ( doc => {
57+ return vscode . window . showTextDocument ( doc , {
58+ viewColumn : vscode . ViewColumn . Beside ,
59+ preserveFocus : false
60+ } ) ;
61+ } )
62+ . then ( editor => {
63+ this . focusEditor ( editor , line ) ;
64+ return editor ;
65+ } )
66+ . catch ( error => {
67+ const message = `Failed to open log file: ${ error . message } ` ;
68+ outputChannel . appendLine ( message ) ;
69+ console . error ( message ) ;
70+ } ) ;
71+ }
72+ }
73+
74+ public highlightLine ( log : string , line : number ) : void {
75+ const editor = this . findEditors ( log ) ;
76+ if ( editor . length > 0 ) {
77+ this . focusEditor ( editor [ 0 ] , line ) ;
78+ }
79+ }
80+
81+ public clearHighlights ( ) : void {
82+ vscode . window . visibleTextEditors . forEach ( ( editor ) => editor . setDecorations ( this . _highlightDecoration , [ ] ) ) ;
83+ }
84+
85+ private findEditors ( log : string ) : vscode . TextEditor [ ] {
86+ const target = path . resolve ( log ) ;
87+ return vscode . window . visibleTextEditors . filter ( ( editor ) => path . resolve ( editor . document . fileName ) === target ) ;
88+ }
89+
90+ private focusEditor ( editor : vscode . TextEditor , line : number ) : void {
91+ const start = Math . max ( 0 , line - 1 ) ;
92+ let range = new vscode . Range (
93+ new vscode . Position ( start , 0 ) ,
94+ new vscode . Position ( start , Number . MAX_VALUE )
95+ ) ;
96+ editor . setDecorations ( this . _highlightDecoration , [ range ] ) ;
97+ editor . revealRange (
98+ range ,
99+ vscode . TextEditorRevealType . InCenter
100+ ) ;
101+ }
102+ }
103+
20104interface CallSite {
21105 name : string ,
22106 sourcePath : string ,
@@ -77,20 +161,29 @@ export class BinaryNotFoundError extends Error {
77161
78162export class DebugSession extends LoggingDebugSession {
79163
80- private static _threadID = 1 ;
164+ // prefer constant to be all caps
165+ // eslint-disable-next-line
166+ private static readonly NEWLINE = '\n' . charCodeAt ( 0 ) ;
167+
168+ private static readonly _threadID = 1 ;
81169 private _binaryPath : string ;
82170 private readonly _variableHandles = new Handles < 'locals' > ( ) ;
83171 private _launchArgs : ILaunchRequestArguments = { source : "" , log : "" , log_format : "" } ;
84- private readonly _highlightDecoration : vscode . TextEditorDecorationType ;
85172 private _mapping ?: LogMapping = undefined ;
86173 private readonly _logDebugger : LogDebugger ;
174+ private readonly _processRunner : ProcessRunner ;
175+ private readonly _editorEffects : EditorEffects ;
87176
88177 /**
89178 * Create a new debug adapter to use with a debug session.
90179 */
91- public constructor ( logDebugger : LogDebugger ) {
180+ public constructor (
181+ logDebugger : LogDebugger ,
182+ processRunner : ProcessRunner = defaultProcessRunner ,
183+ editorEffects : EditorEffects = new DefaultEditorEffects ( )
184+ ) {
92185 super ( "log2src-dap.txt" ) ;
93-
186+ this . _editorEffects = editorEffects ;
94187 this . _binaryPath = PLATFORM_TO_BINARY . get ( `${ process . platform } -${ process . arch } ` ) ! ;
95188
96189 if ( ! this . _binaryPath ) {
@@ -100,17 +193,15 @@ export class DebugSession extends LoggingDebugSession {
100193 }
101194
102195 this . _logDebugger = logDebugger ;
196+ this . _processRunner = processRunner ;
103197 this . setDebuggerLinesStartAt1 ( true ) ;
104198 this . setDebuggerColumnsStartAt1 ( true ) ;
105-
106- const focusColor = new vscode . ThemeColor ( 'editor.focusedStackFrameHighlightBackground' ) ;
107- this . _highlightDecoration = vscode . window . createTextEditorDecorationType ( { "backgroundColor" : focusColor } ) ;
108199 outputChannel . appendLine ( "Starting up..." ) ;
109200 }
110201
111202 protected disconnectRequest ( response : DebugProtocol . DisconnectResponse , args : DebugProtocol . DisconnectArguments , request ?: DebugProtocol . Request ) : void {
112203 console . log ( `disconnectRequest suspend: ${ args . suspendDebuggee } , terminate: ${ args . terminateDebuggee } ` ) ;
113- vscode . window . visibleTextEditors . forEach ( ( editor ) => editor . setDecorations ( this . _highlightDecoration , [ ] ) ) ;
204+ this . _editorEffects . clearHighlights ( ) ;
114205 this . sendResponse ( response ) ;
115206 }
116207
@@ -133,7 +224,12 @@ export class DebugSession extends LoggingDebugSession {
133224 protected setBreakPointsRequest ( response : DebugProtocol . SetBreakpointsResponse , args : DebugProtocol . SetBreakpointsArguments ) {
134225 console . log ( `setBreakPointsRequest ${ JSON . stringify ( args ) } ` ) ;
135226
136- const source = args . source . path as string ;
227+ const source = args . source . path ;
228+ if ( ! source ) {
229+ response . body = { breakpoints : [ ] } ;
230+ this . sendResponse ( response ) ;
231+ return ;
232+ }
137233 const bps = args . breakpoints || [ ] ;
138234 const breakpoints = this . _logDebugger . setBreakpoints ( source , bps ) ;
139235
@@ -159,42 +255,18 @@ export class DebugSession extends LoggingDebugSession {
159255 logger . setup ( args . trace ? Logger . LogLevel . Verbose : Logger . LogLevel . Error , false ) ;
160256
161257 this . _launchArgs = args ;
162- this . openLogAndFocus ( ) ;
163- const execFile = require ( 'child_process' ) . execFileSync ;
164- let stdout = execFile ( 'wc' , [ '-l' , this . _launchArgs . log ] ) ;
165- const logLines = + stdout . toString ( ) . trim ( ) . split ( " " ) [ 0 ] || Number . MAX_VALUE
166- this . _logDebugger . setToLog ( this . _launchArgs . log , logLines ) ;
258+ const log = this . _launchArgs . log ;
259+ const logContent = this . _processRunner . readFile ( log ) ;
260+ const logLines = logContent . reduce ( ( count , byte ) => byte === DebugSession . NEWLINE ? count + 1 : count , 0 ) || Number . MAX_VALUE ;
261+ this . _logDebugger . setToLog ( log , logLines ) ;
262+ this . _editorEffects . openAndFocus ( log , this . _logDebugger . linenum ( ) ) ;
167263
168264 if ( ! this . _logDebugger . hasBreakpoints ( ) ) {
169265 this . sendEvent ( new StoppedEvent ( 'entry' , DebugSession . _threadID ) ) ;
170266 }
171267 this . sendResponse ( response ) ;
172268 }
173269
174- private openLogAndFocus ( ) {
175- const editors = this . findEditors ( ) ;
176- if ( editors . length >= 1 ) {
177- this . focusEditor ( editors [ 0 ] ) ;
178- } else {
179- Promise . resolve ( vscode . workspace . openTextDocument ( this . _launchArgs . log ) )
180- . then ( doc => {
181- return vscode . window . showTextDocument ( doc , {
182- viewColumn : vscode . ViewColumn . Beside ,
183- preserveFocus : false
184- } ) ;
185- } )
186- . then ( editor => {
187- this . focusEditor ( editor ) ;
188- return editor ;
189- } )
190- . catch ( error => {
191- const message = `Failed to open log file: ${ error . message } ` ;
192- outputChannel . appendLine ( message ) ;
193- console . error ( message ) ;
194- } ) ;
195- }
196- }
197-
198270 protected threadsRequest ( response : DebugProtocol . ThreadsResponse ) : void {
199271 console . log ( `threadsRequest` ) ;
200272
@@ -241,24 +313,20 @@ export class DebugSession extends LoggingDebugSession {
241313 console . log ( `stackTraceRequest ${ JSON . stringify ( args ) } ` ) ;
242314
243315 const log2srcPath = path . resolve ( __dirname , this . _binaryPath ) ;
244- const execFile = require ( 'child_process' ) . execFileSync ;
245316 const start = this . _logDebugger . linenum ( ) - 1 ;
246317
247- const editors = this . findEditors ( ) ;
248- if ( editors . length > 0 ) {
249- this . focusEditor ( editors [ 0 ] ) ;
250- }
318+ this . _editorEffects . openAndFocus ( this . _launchArgs . log , this . _logDebugger . linenum ( ) ) ;
251319
252- let l2sArgs = [ '-d' , this . _launchArgs . source ,
320+ const l2sArgs : string [ ] = [ '-d' , this . _launchArgs . source ,
253321 '--log' , this . _launchArgs . log ,
254- '--start' , start ,
255- '--count' , 1 ]
322+ '--start' , String ( start ) ,
323+ '--count' , '1' ] ;
256324 if ( this . _launchArgs . log_format !== undefined && this . _launchArgs . log_format !== "" ) {
257325 l2sArgs . push ( "-f" ) ;
258326 l2sArgs . push ( this . _launchArgs . log_format ) ;
259327 }
260328 outputChannel . appendLine ( `args ${ l2sArgs . join ( " " ) } ` ) ;
261- let stdout = execFile ( log2srcPath , l2sArgs ) ;
329+ const stdout = this . _processRunner . execFileSync ( log2srcPath , l2sArgs ) ;
262330 this . _mapping = JSON . parse ( stdout . toString ( 'utf8' ) ) ;
263331 outputChannel . appendLine ( `mapped ${ JSON . stringify ( this . _mapping ) } ` ) ;
264332
@@ -275,24 +343,6 @@ export class DebugSession extends LoggingDebugSession {
275343 this . sendResponse ( response ) ;
276344 }
277345
278- private findEditors ( ) : vscode . TextEditor [ ] {
279- const target = path . resolve ( this . _launchArgs . log ) ;
280- return vscode . window . visibleTextEditors . filter ( ( editor ) => editor . document . fileName === target ) ;
281- }
282-
283- private focusEditor ( editor : vscode . TextEditor ) {
284- const start = this . _logDebugger . linenum ( ) - 1 ;
285- let range = new vscode . Range (
286- new vscode . Position ( start , 0 ) ,
287- new vscode . Position ( start , Number . MAX_VALUE )
288- ) ;
289- editor . setDecorations ( this . _highlightDecoration , [ range ] ) ;
290- editor . revealRange (
291- range ,
292- vscode . TextEditorRevealType . InCenter
293- ) ;
294- }
295-
296346 private buildStackFrame ( index : number , srcRef ?: SourceRef ) : StackFrame {
297347 let name = "???" ;
298348 let lineNumber = - 1 ;
0 commit comments