@@ -165,7 +165,6 @@ public VariableDetailsBase[] GetVariables(int variableReferenceId)
165
165
if ( parentVariable . IsExpandable )
166
166
{
167
167
childVariables = parentVariable . GetChildren ( ) ;
168
-
169
168
foreach ( var child in childVariables )
170
169
{
171
170
// Only add child if it hasn't already been added.
@@ -270,11 +269,13 @@ public StackFrameDetails[] GetStackFrames()
270
269
/// <returns>The list of VariableScope instances which describe the available variable scopes.</returns>
271
270
public VariableScope [ ] GetVariableScopes ( int stackFrameId )
272
271
{
272
+ int localStackFrameVariableId = this . stackFrameDetails [ stackFrameId ] . LocalVariables . Id ;
273
+
273
274
return new VariableScope [ ]
274
275
{
275
- new VariableScope ( this . stackFrameDetails [ stackFrameId ] . LocalVariables . Id , "Local" ) ,
276
- new VariableScope ( this . scriptScopeVariables . Id , "Script" ) ,
277
- new VariableScope ( this . globalScopeVariables . Id , "Global" ) ,
276
+ new VariableScope ( localStackFrameVariableId , VariableContainerDetails . LocalScopeName ) ,
277
+ new VariableScope ( this . scriptScopeVariables . Id , VariableContainerDetails . ScriptScopeName ) ,
278
+ new VariableScope ( this . globalScopeVariables . Id , VariableContainerDetails . GlobalScopeName ) ,
278
279
} ;
279
280
}
280
281
@@ -311,15 +312,17 @@ private async Task FetchStackFramesAndVariables()
311
312
// Create a dummy variable for index 0, should never see this.
312
313
this . variables . Add ( new VariableDetails ( "Dummy" , null ) ) ;
313
314
315
+ // Must retrieve global/script variales before stack frame variables
316
+ // as we check stack frame variables against globals.
314
317
await FetchGlobalAndScriptVariables ( ) ;
315
318
await FetchStackFrames ( ) ;
316
-
317
319
}
318
320
319
321
private async Task FetchGlobalAndScriptVariables ( )
320
322
{
321
- this . scriptScopeVariables = await FetchVariableContainer ( "Script" ) ;
322
- this . globalScopeVariables = await FetchVariableContainer ( "Global" ) ;
323
+ // Retrieve globals first as script variable retrieval needs to search globals.
324
+ this . globalScopeVariables = await FetchVariableContainer ( VariableContainerDetails . GlobalScopeName ) ;
325
+ this . scriptScopeVariables = await FetchVariableContainer ( VariableContainerDetails . ScriptScopeName ) ;
323
326
}
324
327
325
328
private async Task < VariableContainerDetails > FetchVariableContainer ( string scope )
@@ -328,18 +331,89 @@ private async Task<VariableContainerDetails> FetchVariableContainer(string scope
328
331
psCommand . AddCommand ( "Get-Variable" ) ;
329
332
psCommand . AddParameter ( "Scope" , scope ) ;
330
333
331
- var variableContainerDetails = new VariableContainerDetails ( this . nextVariableId ++ , "Scope: " + scope ) ;
332
- this . variables . Add ( variableContainerDetails ) ;
334
+ var scopeVariableContainer =
335
+ new VariableContainerDetails ( this . nextVariableId ++ , "Scope: " + scope ) ;
336
+ this . variables . Add ( scopeVariableContainer ) ;
337
+
338
+
339
+ // Add a container node to this variable container that will "hide" the built-in,
340
+ // automatic variables that we usually aren't interested in. Do this for local and script scope.
341
+ VariableContainerDetails automaticVariableContainer = null ;
342
+ if ( scope != VariableContainerDetails . GlobalScopeName )
343
+ {
344
+ automaticVariableContainer =
345
+ new VariableContainerDetails ( this . nextVariableId ++ , "Automatic Variables" ) ;
346
+ this . variables . Add ( automaticVariableContainer ) ;
347
+ scopeVariableContainer . Children . Add ( automaticVariableContainer . Name , automaticVariableContainer ) ;
348
+ }
333
349
334
350
var results = await this . powerShellContext . ExecuteCommand < PSVariable > ( psCommand ) ;
335
- foreach ( PSVariable variable in results )
351
+ foreach ( PSVariable psvariable in results )
336
352
{
337
- var variableDetails = new VariableDetails ( variable ) { Id = this . nextVariableId ++ } ;
353
+ var variableDetails = new VariableDetails ( psvariable ) { Id = this . nextVariableId ++ } ;
338
354
this . variables . Add ( variableDetails ) ;
339
- variableContainerDetails . Children . Add ( variableDetails ) ;
355
+
356
+ if ( ( automaticVariableContainer == null ) || ShouldAlwaysDisplayVariable ( psvariable , scope ) )
357
+ {
358
+ scopeVariableContainer . Children . Add ( variableDetails . Name , variableDetails ) ;
359
+ }
360
+ else
361
+ {
362
+ automaticVariableContainer . Children . Add ( variableDetails . Name , variableDetails ) ;
363
+ }
364
+ }
365
+
366
+ return scopeVariableContainer ;
367
+ }
368
+
369
+ private bool ShouldAlwaysDisplayVariable ( PSVariable psvariable , string scope )
370
+ {
371
+ ScopedItemOptions constantAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . Constant ;
372
+ ScopedItemOptions readonlyAllScope = ScopedItemOptions . AllScope | ScopedItemOptions . ReadOnly ;
373
+
374
+ if ( scope == VariableContainerDetails . GlobalScopeName )
375
+ {
376
+ // We don't A) have a good way of distinguishing automatic from user created variabbles
377
+ // and B) globalScopeVariables.Children.ContainsKey() doesn't work for automatic variables
378
+ // stored in a child variable container within the globals variable container.
379
+ return true ;
380
+ }
381
+
382
+ // Some local variables, if they exist, should be displayed by default
383
+ if ( psvariable . GetType ( ) . Name == "LocalVariable" )
384
+ {
385
+ if ( psvariable . Name . Equals ( "_" ) )
386
+ {
387
+ return true ;
388
+ }
389
+ else if ( psvariable . Name . Equals ( "args" , StringComparison . OrdinalIgnoreCase ) )
390
+ {
391
+ var array = psvariable . Value as Array ;
392
+ return array != null ? array . Length > 0 : false ;
393
+ }
394
+
395
+ return false ;
396
+ }
397
+ else if ( psvariable . GetType ( ) != typeof ( PSVariable ) )
398
+ {
399
+ return false ;
400
+ }
401
+
402
+ if ( ( ( psvariable . Options | constantAllScope ) == constantAllScope ) ||
403
+ ( ( psvariable . Options | readonlyAllScope ) == readonlyAllScope ) )
404
+ {
405
+ if ( this . globalScopeVariables . Children . ContainsKey ( VariableDetails . DollarPrefix + psvariable . Name ) )
406
+ {
407
+ return false ;
408
+ }
409
+ }
410
+
411
+ if ( ( psvariable . Value != null ) && ( psvariable . Value . GetType ( ) == typeof ( PSDebugContext ) ) )
412
+ {
413
+ return false ;
340
414
}
341
415
342
- return variableContainerDetails ;
416
+ return true ;
343
417
}
344
418
345
419
private async Task FetchStackFrames ( )
@@ -354,7 +428,8 @@ private async Task FetchStackFrames()
354
428
355
429
for ( int i = 0 ; i < callStackFrames . Length ; i ++ )
356
430
{
357
- VariableContainerDetails localVariables = await FetchVariableContainer ( i . ToString ( ) ) ;
431
+ VariableContainerDetails localVariables =
432
+ await FetchVariableContainer ( i . ToString ( ) ) ;
358
433
this . stackFrameDetails [ i ] = StackFrameDetails . Create ( callStackFrames [ i ] , localVariables ) ;
359
434
}
360
435
}
0 commit comments