@@ -51,12 +51,7 @@ export class GenericEvaluator implements Evaluator {
51
51
// Use structural information about variables
52
52
// from the evaluation response if present.
53
53
if ( reply . variablesReference ) {
54
- let graph : GraphVisualizationData = {
55
- kind : { graph : true } ,
56
- nodes : [ ] ,
57
- edges : [ ]
58
- } ;
59
- await this . constructGraphFromVariablesReference ( reply . result , reply . variablesReference , graph ) ;
54
+ const graph = await this . constructGraphFromVariablesReference ( reply . result , reply . variablesReference ) ;
60
55
61
56
return {
62
57
kind : "data" ,
@@ -97,54 +92,85 @@ export class GenericEvaluator implements Evaluator {
97
92
}
98
93
}
99
94
95
+ /**
96
+ * Constructs GraphVisualizationData from a DAP variables
97
+ * reference by successively querying the debug adapter for
98
+ * variables. Objects are considered to be equivalent if
99
+ * they share the same variables reference (this is important
100
+ * for representing cyclic relationships).
101
+ *
102
+ * @param rootLabel - The root object's label
103
+ * @param rootVariablesReference - The root object's DAP variables reference
104
+ * @param maxDepth - The maximum depth to search at
105
+ * @param maxKnownNodes - The maximum number of nodes
106
+ */
100
107
private async constructGraphFromVariablesReference (
101
- label : string ,
102
- variablesReference : number ,
103
- graph : GraphVisualizationData ,
104
- isTopLevel : boolean = true ,
105
- knownNodeIds : { [ ref : number ] : string ; } = { } ,
106
- recursionDepth : number = 0 ,
107
- maxRecursionDepth : number = 30 ,
108
+ rootLabel : string ,
109
+ rootVariablesReference : number ,
110
+ maxDepth : number = 30 ,
108
111
maxKnownNodes : number = 100 ,
109
- ) : Promise < string > {
110
- const hasChilds = variablesReference > 0 ;
111
- const knownCount = Object . keys ( knownNodeIds ) . length + 1 ;
112
- const canRecurse = recursionDepth < maxRecursionDepth && knownCount < maxKnownNodes ;
113
- let result : GraphNode = {
114
- id : hasChilds ? `${ variablesReference } ` : `__${ label } @${ knownCount } __` ,
115
- label,
116
- color : isTopLevel ? "lightblue" : undefined ,
117
- shape : "box" ,
112
+ ) : Promise < GraphVisualizationData > {
113
+ // Perform a breadth-first search on the object to construct the graph
114
+
115
+ const graph : GraphVisualizationData = {
116
+ kind : { graph : true } ,
117
+ nodes : [ ] ,
118
+ edges : [ ]
118
119
} ;
119
- knownNodeIds [ variablesReference ] = result . id ;
120
-
121
- if ( hasChilds && canRecurse ) {
122
- for ( const variable of await this . session . getVariables ( { variablesReference } ) ) {
123
- let childId : string ;
124
-
125
- if ( variable . variablesReference > 0 && variable . variablesReference in knownNodeIds ) {
126
- // If the object is known, we have a (potentially cyclic) reference.
127
- childId = knownNodeIds [ variable . variablesReference ] ;
128
- } else {
129
- // Otherwise recurse
130
- childId = await this . constructGraphFromVariablesReference (
131
- variable . value ,
132
- variable . variablesReference ,
133
- graph ,
134
- false , // isTopLevel
135
- knownNodeIds ,
136
- recursionDepth + 1 ,
137
- maxRecursionDepth ,
138
- maxKnownNodes
139
- ) ;
120
+ const knownNodeIds : { [ ref : number ] : string ; } = { } ;
121
+ const bfsQueue : { source : { id : string , name : string } | undefined , label : string , variablesReference : number , depth : number } [ ] = [ {
122
+ source : undefined ,
123
+ label : rootLabel ,
124
+ variablesReference : rootVariablesReference ,
125
+ depth : 0 ,
126
+ } ] ;
127
+
128
+ let knownCount : number = 0 ;
129
+
130
+ do {
131
+ const variable = bfsQueue . shift ( ) ! ;
132
+ const hasChilds = variable . variablesReference > 0 ;
133
+
134
+ if ( variable . depth > maxDepth ) {
135
+ break ;
136
+ }
137
+
138
+ let nodeId : string ;
139
+
140
+ if ( ! hasChilds || ! ( variable . variablesReference in knownNodeIds ) ) {
141
+ // The variable is a leaf or an unvisited object: create the node.
142
+
143
+ const node : GraphNode = {
144
+ id : hasChilds ? `${ variable . variablesReference } ` : `__${ variable . label } @${ knownCount } __` ,
145
+ label : variable . label ,
146
+ color : variable . depth == 0 ? "lightblue" : undefined ,
147
+ shape : "box" ,
148
+ } ;
149
+
150
+ graph . nodes . push ( node ) ;
151
+ knownCount ++ ;
152
+
153
+ if ( hasChilds ) {
154
+ knownNodeIds [ variable . variablesReference ] = node . id ;
155
+
156
+ for ( const child of await this . session . getVariables ( { variablesReference : variable . variablesReference } ) ) {
157
+ bfsQueue . push ( { source : { id : node . id , name : child . name } , label : child . value , variablesReference : child . variablesReference , depth : variable . depth + 1 } ) ;
158
+ }
140
159
}
141
160
142
- graph . edges . push ( { from : result . id , to : childId , label : variable . name } ) ;
161
+ nodeId = node . id ;
162
+ } else {
163
+ // The variable is a visited object (e.g. due to a cyclic reference)
164
+
165
+ nodeId = knownNodeIds [ variable . variablesReference ] ;
143
166
}
144
- }
145
167
146
- graph . nodes . push ( result ) ;
147
- return result . id ;
168
+ if ( variable . source ) {
169
+ graph . edges . push ( { from : variable . source . id , to : nodeId , label : variable . source . name } ) ;
170
+ }
171
+ } while ( bfsQueue . length > 0 && knownCount <= maxKnownNodes ) ;
172
+
173
+ return graph ;
148
174
}
149
175
150
176
protected getFinalExpression ( args : {
0 commit comments