@@ -6,35 +6,42 @@ import * as gv from "ts-graphviz";
6
6
export class CodeMapProvider implements vs . DebugAdapterTracker , vs . TextDocumentContentProvider {
7
7
dotDocument ?: vs . TextDocument ;
8
8
graph : gv . Digraph ;
9
+ active = false ;
9
10
10
- constructor ( ) {
11
- this . graph = gv . digraph ( 'Call Graph' , { splines : true } ) ;
12
- }
13
-
14
- /** @override */
15
- provideTextDocumentContent ( uri : vs . Uri , token : vs . CancellationToken ) : vs . ProviderResult < string > {
16
- // here we update the source .dot document
17
- // TODO: actually check uri
18
- return gv . toDot ( this . graph ) ;
19
- }
20
-
21
- // https://code.visualstudio.com/api/extension-guides/virtual-documents#update-virtual-documents
22
- onDidChangeEmitter = new vs . EventEmitter < vs . Uri > ( ) ;
23
- onDidChange = this . onDidChangeEmitter . event ; // without this the emitter silently doesn't work
24
-
25
- async onWillStartSession ( ) {
26
- this . dotDocument = await vs . workspace . openTextDocument ( vs . Uri . parse ( 'dot:1.dot' , true ) ) ;
11
+ async activate ( ) : Promise < void > {
27
12
this . graph = gv . digraph ( 'Call Graph' , { splines : true } ) ; // reset the graph
28
13
29
- // used for debugging
30
- vs . window . showTextDocument ( this . dotDocument , { preview : false } ) ;
14
+ this . dotDocument = await vs . workspace . openTextDocument ( vs . Uri . parse ( 'dot:callgraph.dot' , true ) ) ;
15
+ this . active = true ;
16
+
17
+ // save the current editor
18
+ const activeEditor = vs . window . activeTextEditor ;
19
+ // show the `dot` source
20
+ vs . window . showTextDocument ( this . dotDocument , vs . ViewColumn . Beside , true ) ;
31
21
32
22
const args = {
33
23
document : this . dotDocument ,
34
- callback : ( webpanel : any ) => {
35
- // The callback function receives the newly created webPanel.
36
- // Overload webPanel.handleMessage(message) to receive message events like onClick and onDblClick
37
- //console.log(JSON.stringify(webpanel, undefined, 2));
24
+ callback : ( panel : any /* PreviewPanel */ ) => {
25
+ // we have to switch back to the original editor group to prevent issues
26
+ const webpanel : vs . WebviewPanel = panel . panel ;
27
+ const disposable = webpanel . onDidChangeViewState ( e => {
28
+ if ( activeEditor )
29
+ vs . window . showTextDocument ( activeEditor . document , activeEditor . viewColumn , false ) ;
30
+ disposable . dispose ( ) ;
31
+ } ) ;
32
+ // handle user closing the graphviz preview
33
+ webpanel . onDidDispose ( e => {
34
+ this . active = false ;
35
+ // there's no way to close a document, only this
36
+ // FIXME: if the editor is not in the active view column, this opens a new one and closes it
37
+ vs . window . showTextDocument ( this . dotDocument ! , vs . ViewColumn . Active , false )
38
+ . then ( ( ) => {
39
+ return vs . commands . executeCommand ( 'workbench.action.closeActiveEditor' ) ;
40
+ } ) ;
41
+ this . graph . clear ( ) ;
42
+ this . onDidChangeEmitter . fire ( this . dotDocument ! . uri ) ;
43
+ this . dotDocument = undefined ; // FIXME: does not delete the document
44
+ } ) ;
38
45
} ,
39
46
allowMultiplePanels : false ,
40
47
title : 'Call Graph' ,
@@ -43,11 +50,24 @@ export class CodeMapProvider implements vs.DebugAdapterTracker, vs.TextDocumentC
43
50
vs . commands . executeCommand ( "graphviz-interactive-preview.preview.beside" , args ) ;
44
51
}
45
52
46
- onWillStopSession ( ) {
47
- this . dotDocument = undefined ;
48
- this . graph . clear ( ) ;
53
+ /** @override TextDocumentContentProvider */
54
+ provideTextDocumentContent ( uri : vs . Uri , token : vs . CancellationToken ) : vs . ProviderResult < string > {
55
+ // here we update the source .dot document
56
+ if ( uri . path != 'callgraph.dot' )
57
+ return ;
58
+ return gv . toDot ( this . graph ) ;
49
59
}
50
60
61
+ // https://code.visualstudio.com/api/extension-guides/virtual-documents#update-virtual-documents
62
+ onDidChangeEmitter = new vs . EventEmitter < vs . Uri > ( ) ;
63
+ onDidChange = this . onDidChangeEmitter . event ; // without this the emitter silently doesn't work
64
+
65
+ /** @override DebugAdapterTracker */
66
+ // onWillStartSession() {}
67
+
68
+ /** @override DebugAdapterTracker */
69
+ // onWillStopSession() {}
70
+
51
71
private getOrCreateNode ( name : string ) {
52
72
return this . graph . getNode ( name ) ?? this . graph . createNode ( name , { shape : "box" } ) ;
53
73
}
@@ -64,11 +84,17 @@ export class CodeMapProvider implements vs.DebugAdapterTracker, vs.TextDocumentC
64
84
if ( ! r . success || r . body . stackFrames . length < 1 )
65
85
return ;
66
86
87
+ let lastNode = this . getOrCreateNode ( CodeMapProvider . wordwrap ( r . body . stackFrames [ 0 ] . name , 64 ) ) ;
88
+ // prevent re-rendering if we're still in the same function
89
+ if ( lastNode . attributes . get ( "color" ) )
90
+ return ;
91
+
92
+ // mark the current function with a red border
67
93
for ( const f of this . graph . nodes )
68
94
f . attributes . delete ( "color" ) ;
69
-
70
- let lastNode = this . getOrCreateNode ( CodeMapProvider . wordwrap ( r . body . stackFrames [ 0 ] . name , 64 ) ) ;
71
95
lastNode . attributes . set ( "color" , "red" ) ;
96
+
97
+ // walk up the stack and create nodes/edges
72
98
for ( let i = 1 ; i < r . body . stackFrames . length ; ++ i ) {
73
99
const nodeName = CodeMapProvider . wordwrap ( r . body . stackFrames [ i ] . name , 64 ) ;
74
100
const node = this . getOrCreateNode ( nodeName ) ;
@@ -82,9 +108,11 @@ export class CodeMapProvider implements vs.DebugAdapterTracker, vs.TextDocumentC
82
108
this . onDidChangeEmitter . fire ( this . dotDocument ! . uri ) ;
83
109
}
84
110
85
- /** @override */
111
+ /** @override DebugAdapterTracker */
86
112
onDidSendMessage ( msg : dap . ProtocolMessage ) {
87
- console . log ( `< ${ typeof msg } ${ JSON . stringify ( msg , undefined , 2 ) } ` ) ;
113
+ if ( ! this . active )
114
+ return ;
115
+
88
116
if ( msg . type !== "response" || ( msg as dap . Response ) . command !== "stackTrace" )
89
117
return ;
90
118
0 commit comments