1
1
import * as vscode from 'vscode' ;
2
2
3
+ // Track sessions since vscode doesn't provide a list of them.
4
+ const sessions = new Map < string , vscode . DebugSession > ( ) ;
5
+ vscode . debug . onDidStartDebugSession ( ( s ) => sessions . set ( s . id , s ) ) ;
6
+ vscode . debug . onDidTerminateDebugSession ( ( s ) => sessions . delete ( s . id ) ) ;
7
+
3
8
export function registerGoDebugCommands ( ctx : vscode . ExtensionContext ) {
4
- ctx . subscriptions . push (
5
- vscode . commands . registerCommand (
6
- 'go.debug.openVariableAsDoc' ,
7
- async ( args : any ) => {
8
- const variable = args . variable ;
9
-
10
- let raw = variable . value . trim ( ) ;
11
- if (
12
- ( raw . startsWith ( '"' ) && raw . endsWith ( '"' ) ) ||
13
- ( raw . startsWith ( '`' ) && raw . endsWith ( '`' ) )
14
- ) {
15
- raw = raw . slice ( 1 , - 1 ) ;
16
- }
17
-
18
- let text : string ;
19
- try {
20
- text = JSON . parse ( `"${ raw . replace ( / " / g, '\\"' ) } "` ) ;
21
- } catch {
22
- text = raw
23
- . replace ( / \\ r / g, '\r' )
24
- . replace ( / \\ n / g, '\n' )
25
- . replace ( / \\ t / g, '\t' )
26
- . replace ( / \\ " / g, '"' )
27
- . replace ( / \\ \\ / g, '\\' ) ;
28
- }
29
-
30
- const doc = await vscode . workspace . openTextDocument ( {
31
- language : 'plaintext' ,
32
- content : text
33
- } ) ;
34
-
35
- const editor = await vscode . window . showTextDocument ( doc ) ;
36
-
37
- await vscode . commands . executeCommand ( 'workbench.action.editor.changeLanguageMode' ) ;
9
+ class VariableContentProvider implements vscode . TextDocumentContentProvider {
10
+ static uriForRef ( ref : VariableRef ) {
11
+ return vscode . Uri . from ( {
12
+ scheme : 'go-debug-variable' ,
13
+ authority : `${ ref . container . variablesReference } @${ ref . sessionId } ` ,
14
+ path : `/${ ref . variable . name } `
15
+ } ) ;
16
+ }
17
+
18
+ async provideTextDocumentContent ( uri : vscode . Uri ) : Promise < string > {
19
+ const name = uri . path . replace ( / ^ \/ / , '' ) ;
20
+ const [ container , sessionId ] = uri . authority . split ( '@' , 2 ) ;
21
+ if ( ! container || ! sessionId ) {
22
+ throw new Error ( 'Invalid URI' ) ;
38
23
}
39
- )
40
- )
41
- }
24
+
25
+ const session = sessions . get ( sessionId ) ;
26
+ if ( ! session ) return 'Debug session has been terminated' ;
27
+
28
+ const r : { variables : Variable [ ] } = await session . customRequest ( 'variables' , {
29
+ variablesReference : parseInt ( container , 10 )
30
+ } ) ;
31
+
32
+ const v = r . variables . find ( ( v ) => v . name === name ) ;
33
+ if ( ! v ) return `Cannot resolve variable ${ name } ` ;
34
+
35
+ const { result } = await session . customRequest ( 'evaluate' , {
36
+ expression : v . evaluateName ,
37
+ context : 'clipboard'
38
+ } ) ;
39
+
40
+ v . value = result ?? v . value ;
41
+
42
+ return parseVariable ( v ) ;
43
+ }
44
+ }
45
+
46
+ ctx . subscriptions . push (
47
+ vscode . workspace . registerTextDocumentContentProvider ( 'go-debug-variable' , new VariableContentProvider ( ) )
48
+ ) ;
49
+
50
+ ctx . subscriptions . push (
51
+ vscode . commands . registerCommand ( 'go.debug.openVariableAsDoc' , async ( ref : VariableRef ) => {
52
+ const uri = VariableContentProvider . uriForRef ( ref ) ;
53
+ const doc = await vscode . workspace . openTextDocument ( uri ) ;
54
+ await vscode . window . showTextDocument ( doc ) ;
55
+ } )
56
+ ) ;
57
+
58
+ interface VariableRef {
59
+ sessionId : string ;
60
+ container : Container ;
61
+ variable : Variable ;
62
+ }
63
+
64
+ interface Container {
65
+ name : string ;
66
+ variablesReference : number ;
67
+ expensive : boolean ;
68
+ }
69
+
70
+ interface Variable {
71
+ name : string ;
72
+ value : string ;
73
+ evaluateName : string ;
74
+ variablesReference : number ;
75
+ }
76
+
77
+ const escapeCodes : Record < string , string > = {
78
+ r : '\r' ,
79
+ n : '\n' ,
80
+ t : '\t'
81
+ } ;
82
+
83
+ function parseVariable ( variable : Variable ) {
84
+ let raw = variable . value . trim ( ) ;
85
+ try {
86
+ // Attempt to parse as JSON
87
+ return JSON . parse ( raw ) ;
88
+ } catch ( _ ) {
89
+ // Fall back to manual unescaping
90
+ raw = raw . slice ( 1 , - 1 ) ;
91
+ }
92
+
93
+ // Manually unescape
94
+ return raw . replace ( / \\ [ n r t \\ " ' ` ] / , ( _ , s ) => ( s in escapeCodes ? escapeCodes [ s ] : s ) ) ;
95
+ }
96
+ }
0 commit comments