Skip to content

Commit 31042aa

Browse files
committed
Initial checkin for issue PowerShell#56 - filter variables into a way to make the user's variables more front-and-center.
1 parent 0d4486d commit 31042aa

File tree

2 files changed

+110
-18
lines changed

2 files changed

+110
-18
lines changed

src/PowerShellEditorServices/Debugging/DebugService.cs

+89-14
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ public VariableDetailsBase[] GetVariables(int variableReferenceId)
165165
if (parentVariable.IsExpandable)
166166
{
167167
childVariables = parentVariable.GetChildren();
168-
169168
foreach (var child in childVariables)
170169
{
171170
// Only add child if it hasn't already been added.
@@ -270,11 +269,13 @@ public StackFrameDetails[] GetStackFrames()
270269
/// <returns>The list of VariableScope instances which describe the available variable scopes.</returns>
271270
public VariableScope[] GetVariableScopes(int stackFrameId)
272271
{
272+
int localStackFrameVariableId = this.stackFrameDetails[stackFrameId].LocalVariables.Id;
273+
273274
return new VariableScope[]
274275
{
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),
278279
};
279280
}
280281

@@ -311,15 +312,17 @@ private async Task FetchStackFramesAndVariables()
311312
// Create a dummy variable for index 0, should never see this.
312313
this.variables.Add(new VariableDetails("Dummy", null));
313314

315+
// Must retrieve global/script variales before stack frame variables
316+
// as we check stack frame variables against globals.
314317
await FetchGlobalAndScriptVariables();
315318
await FetchStackFrames();
316-
317319
}
318320

319321
private async Task FetchGlobalAndScriptVariables()
320322
{
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);
323326
}
324327

325328
private async Task<VariableContainerDetails> FetchVariableContainer(string scope)
@@ -328,18 +331,89 @@ private async Task<VariableContainerDetails> FetchVariableContainer(string scope
328331
psCommand.AddCommand("Get-Variable");
329332
psCommand.AddParameter("Scope", scope);
330333

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+
}
333349

334350
var results = await this.powerShellContext.ExecuteCommand<PSVariable>(psCommand);
335-
foreach (PSVariable variable in results)
351+
foreach (PSVariable psvariable in results)
336352
{
337-
var variableDetails = new VariableDetails(variable) { Id = this.nextVariableId++ };
353+
var variableDetails = new VariableDetails(psvariable) { Id = this.nextVariableId++ };
338354
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;
340414
}
341415

342-
return variableContainerDetails;
416+
return true;
343417
}
344418

345419
private async Task FetchStackFrames()
@@ -354,7 +428,8 @@ private async Task FetchStackFrames()
354428

355429
for (int i = 0; i < callStackFrames.Length; i++)
356430
{
357-
VariableContainerDetails localVariables = await FetchVariableContainer(i.ToString());
431+
VariableContainerDetails localVariables =
432+
await FetchVariableContainer(i.ToString());
358433
this.stackFrameDetails[i] = StackFrameDetails.Create(callStackFrames[i], localVariables);
359434
}
360435
}

src/PowerShellEditorServices/Debugging/VariableContainerDetails.cs

+21-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,22 @@ namespace Microsoft.PowerShell.EditorServices
2020
[DebuggerDisplay("Name = {Name}, Id = {Id}, Count = {Children.Count}")]
2121
public class VariableContainerDetails : VariableDetailsBase
2222
{
23-
private readonly List<VariableDetailsBase> children;
23+
/// <summary>
24+
/// Provides a constant for the name of the Global scope.
25+
/// </summary>
26+
public const string GlobalScopeName = "Global";
27+
28+
/// <summary>
29+
/// Provides a constant for the name of the Script scope.
30+
/// </summary>
31+
public const string ScriptScopeName = "Script";
32+
33+
/// <summary>
34+
/// Provides a constant for the name of the Local scope.
35+
/// </summary>
36+
public const string LocalScopeName = "Local";
37+
38+
private readonly Dictionary<string, VariableDetailsBase> children;
2439

2540
/// <summary>
2641
/// Instantiates an instance of VariableScopeDetails.
@@ -36,13 +51,13 @@ public VariableContainerDetails(int id, string name)
3651
this.IsExpandable = true;
3752
this.ValueString = " "; // An empty string isn't enough due to a temporary bug in VS Code.
3853

39-
this.children = new List<VariableDetailsBase>();
54+
this.children = new Dictionary<string, VariableDetailsBase>();
4055
}
4156

4257
/// <summary>
4358
/// Gets the collection of child variables.
4459
/// </summary>
45-
public List<VariableDetailsBase> Children
60+
public IDictionary<string, VariableDetailsBase> Children
4661
{
4762
get { return this.children; }
4863
}
@@ -53,7 +68,9 @@ public List<VariableDetailsBase> Children
5368
/// <returns></returns>
5469
public override VariableDetailsBase[] GetChildren()
5570
{
56-
return this.children.ToArray();
71+
var variablesArray = new VariableDetailsBase[this.children.Count];
72+
this.children.Values.CopyTo(variablesArray, 0);
73+
return variablesArray;
5774
}
5875
}
5976
}

0 commit comments

Comments
 (0)