Skip to content

Commit edbebbf

Browse files
committed
Merge pull request #67 from rkeithhill/rkeithhill/variable-filtering
Add 'auto' variable filtering
2 parents c0b5a02 + 707ca33 commit edbebbf

File tree

3 files changed

+131
-21
lines changed

3 files changed

+131
-21
lines changed

Diff for: src/PowerShellEditorServices/Debugging/DebugService.cs

+95-17
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
44
//
55

6-
using Microsoft.PowerShell.EditorServices.Utility;
76
using System;
87
using System.Collections.Generic;
98
using System.Linq;
109
using System.Management.Automation;
1110
using System.Threading.Tasks;
11+
using Microsoft.PowerShell.EditorServices.Utility;
1212

1313
namespace Microsoft.PowerShell.EditorServices
1414
{
@@ -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,15 @@ 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+
int autoVariablesId = this.stackFrameDetails[stackFrameId].AutoVariables.Id;
274+
273275
return new VariableScope[]
274276
{
275-
new VariableScope(this.stackFrameDetails[stackFrameId].LocalVariables.Id, "Local"),
276-
new VariableScope(this.scriptScopeVariables.Id, "Script"),
277-
new VariableScope(this.globalScopeVariables.Id, "Global"),
277+
new VariableScope(autoVariablesId, VariableContainerDetails.AutoVariablesName),
278+
new VariableScope(localStackFrameVariableId, VariableContainerDetails.LocalScopeName),
279+
new VariableScope(this.scriptScopeVariables.Id, VariableContainerDetails.ScriptScopeName),
280+
new VariableScope(this.globalScopeVariables.Id, VariableContainerDetails.GlobalScopeName),
278281
};
279282
}
280283

@@ -311,35 +314,100 @@ private async Task FetchStackFramesAndVariables()
311314
// Create a dummy variable for index 0, should never see this.
312315
this.variables.Add(new VariableDetails("Dummy", null));
313316

317+
// Must retrieve global/script variales before stack frame variables
318+
// as we check stack frame variables against globals.
314319
await FetchGlobalAndScriptVariables();
315320
await FetchStackFrames();
316-
317321
}
318322

319323
private async Task FetchGlobalAndScriptVariables()
320324
{
321-
this.scriptScopeVariables = await FetchVariableContainer("Script");
322-
this.globalScopeVariables = await FetchVariableContainer("Global");
325+
// Retrieve globals first as script variable retrieval needs to search globals.
326+
this.globalScopeVariables =
327+
await FetchVariableContainer(VariableContainerDetails.GlobalScopeName, null);
328+
329+
this.scriptScopeVariables =
330+
await FetchVariableContainer(VariableContainerDetails.ScriptScopeName, null);
323331
}
324332

325-
private async Task<VariableContainerDetails> FetchVariableContainer(string scope)
333+
private async Task<VariableContainerDetails> FetchVariableContainer(
334+
string scope,
335+
VariableContainerDetails autoVariables)
326336
{
327337
PSCommand psCommand = new PSCommand();
328338
psCommand.AddCommand("Get-Variable");
329339
psCommand.AddParameter("Scope", scope);
330340

331-
var variableContainerDetails = new VariableContainerDetails(this.nextVariableId++, "Scope: " + scope);
332-
this.variables.Add(variableContainerDetails);
341+
var scopeVariableContainer =
342+
new VariableContainerDetails(this.nextVariableId++, "Scope: " + scope);
343+
this.variables.Add(scopeVariableContainer);
333344

334345
var results = await this.powerShellContext.ExecuteCommand<PSVariable>(psCommand);
335-
foreach (PSVariable variable in results)
346+
foreach (PSVariable psvariable in results)
336347
{
337-
var variableDetails = new VariableDetails(variable) { Id = this.nextVariableId++ };
348+
var variableDetails = new VariableDetails(psvariable) { Id = this.nextVariableId++ };
338349
this.variables.Add(variableDetails);
339-
variableContainerDetails.Children.Add(variableDetails);
350+
scopeVariableContainer.Children.Add(variableDetails.Name, variableDetails);
351+
352+
if ((autoVariables != null) && AddToAutoVariables(psvariable, scope))
353+
{
354+
autoVariables.Children.Add(variableDetails.Name, variableDetails);
355+
}
340356
}
341357

342-
return variableContainerDetails;
358+
return scopeVariableContainer;
359+
}
360+
361+
private bool AddToAutoVariables(PSVariable psvariable, string scope)
362+
{
363+
if ((scope == VariableContainerDetails.GlobalScopeName) ||
364+
(scope == VariableContainerDetails.ScriptScopeName))
365+
{
366+
// We don't A) have a good way of distinguishing built-in from user created variables
367+
// and B) globalScopeVariables.Children.ContainsKey() doesn't work for built-in variables
368+
// stored in a child variable container within the globals variable container.
369+
return false;
370+
}
371+
372+
var constantAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.Constant;
373+
var readonlyAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.ReadOnly;
374+
375+
// Some local variables, if they exist, should be displayed by default
376+
if (psvariable.GetType().Name == "LocalVariable")
377+
{
378+
if (psvariable.Name.Equals("_"))
379+
{
380+
return true;
381+
}
382+
else if (psvariable.Name.Equals("args", StringComparison.OrdinalIgnoreCase))
383+
{
384+
var array = psvariable.Value as Array;
385+
return array != null ? array.Length > 0 : false;
386+
}
387+
388+
return false;
389+
}
390+
else if (psvariable.GetType() != typeof(PSVariable))
391+
{
392+
return false;
393+
}
394+
395+
if (((psvariable.Options | constantAllScope) == constantAllScope) ||
396+
((psvariable.Options | readonlyAllScope) == readonlyAllScope))
397+
{
398+
string prefixedVariableName = VariableDetails.DollarPrefix + psvariable.Name;
399+
if (this.globalScopeVariables.Children.ContainsKey(prefixedVariableName))
400+
{
401+
return false;
402+
}
403+
}
404+
405+
if ((psvariable.Value != null) && (psvariable.Value.GetType() == typeof(PSDebugContext)))
406+
{
407+
return false;
408+
}
409+
410+
return true;
343411
}
344412

345413
private async Task FetchStackFrames()
@@ -354,8 +422,18 @@ private async Task FetchStackFrames()
354422

355423
for (int i = 0; i < callStackFrames.Length; i++)
356424
{
357-
VariableContainerDetails localVariables = await FetchVariableContainer(i.ToString());
358-
this.stackFrameDetails[i] = StackFrameDetails.Create(callStackFrames[i], localVariables);
425+
VariableContainerDetails autoVariables =
426+
new VariableContainerDetails(
427+
this.nextVariableId++,
428+
VariableContainerDetails.AutoVariablesName);
429+
430+
this.variables.Add(autoVariables);
431+
432+
VariableContainerDetails localVariables =
433+
await FetchVariableContainer(i.ToString(), autoVariables);
434+
435+
this.stackFrameDetails[i] =
436+
StackFrameDetails.Create(callStackFrames[i], autoVariables, localVariables);
359437
}
360438
}
361439

Diff for: src/PowerShellEditorServices/Debugging/StackFrameDetails.cs

+10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ public class StackFrameDetails
3333
/// </summary>
3434
public int ColumnNumber { get; private set; }
3535

36+
/// <summary>
37+
/// Gets or sets the VariableContainerDetails that contains the auto variables.
38+
/// </summary>
39+
public VariableContainerDetails AutoVariables { get; private set; }
40+
3641
/// <summary>
3742
/// Gets or sets the VariableContainerDetails that contains the local variables.
3843
/// </summary>
@@ -45,12 +50,16 @@ public class StackFrameDetails
4550
/// <param name="callStackFrame">
4651
/// The original CallStackFrame instance from which details will be obtained.
4752
/// </param>
53+
/// <param name="autoVariables">
54+
/// A variable container with all the filtered, auto variables for this stack frame.
55+
/// </param>
4856
/// <param name="localVariables">
4957
/// A variable container with all the local variables for this stack frame.
5058
/// </param>
5159
/// <returns>A new instance of the StackFrameDetails class.</returns>
5260
static internal StackFrameDetails Create(
5361
CallStackFrame callStackFrame,
62+
VariableContainerDetails autoVariables,
5463
VariableContainerDetails localVariables)
5564
{
5665
return new StackFrameDetails
@@ -59,6 +68,7 @@ static internal StackFrameDetails Create(
5968
FunctionName = callStackFrame.FunctionName,
6069
LineNumber = callStackFrame.Position.StartLineNumber,
6170
ColumnNumber = callStackFrame.Position.StartColumnNumber,
71+
AutoVariables = autoVariables,
6272
LocalVariables = localVariables
6373
};
6474
}

Diff for: src/PowerShellEditorServices/Debugging/VariableContainerDetails.cs

+26-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,27 @@ 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 AutoVariablesName = "Auto";
27+
28+
/// <summary>
29+
/// Provides a constant for the name of the Global scope.
30+
/// </summary>
31+
public const string GlobalScopeName = "Global";
32+
33+
/// <summary>
34+
/// Provides a constant for the name of the Local scope.
35+
/// </summary>
36+
public const string LocalScopeName = "Local";
37+
38+
/// <summary>
39+
/// Provides a constant for the name of the Script scope.
40+
/// </summary>
41+
public const string ScriptScopeName = "Script";
42+
43+
private readonly Dictionary<string, VariableDetailsBase> children;
2444

2545
/// <summary>
2646
/// Instantiates an instance of VariableScopeDetails.
@@ -36,13 +56,13 @@ public VariableContainerDetails(int id, string name)
3656
this.IsExpandable = true;
3757
this.ValueString = " "; // An empty string isn't enough due to a temporary bug in VS Code.
3858

39-
this.children = new List<VariableDetailsBase>();
59+
this.children = new Dictionary<string, VariableDetailsBase>();
4060
}
4161

4262
/// <summary>
4363
/// Gets the collection of child variables.
4464
/// </summary>
45-
public List<VariableDetailsBase> Children
65+
public IDictionary<string, VariableDetailsBase> Children
4666
{
4767
get { return this.children; }
4868
}
@@ -53,7 +73,9 @@ public List<VariableDetailsBase> Children
5373
/// <returns></returns>
5474
public override VariableDetailsBase[] GetChildren()
5575
{
56-
return this.children.ToArray();
76+
var variablesArray = new VariableDetailsBase[this.children.Count];
77+
this.children.Values.CopyTo(variablesArray, 0);
78+
return variablesArray;
5779
}
5880
}
5981
}

0 commit comments

Comments
 (0)