Skip to content

Rkeithhill/variable filtering #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 95 additions & 17 deletions src/PowerShellEditorServices/Debugging/DebugService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Threading.Tasks;
using Microsoft.PowerShell.EditorServices.Utility;

namespace Microsoft.PowerShell.EditorServices
{
Expand Down Expand Up @@ -165,7 +165,6 @@ public VariableDetailsBase[] GetVariables(int variableReferenceId)
if (parentVariable.IsExpandable)
{
childVariables = parentVariable.GetChildren();

foreach (var child in childVariables)
{
// Only add child if it hasn't already been added.
Expand Down Expand Up @@ -270,11 +269,15 @@ public StackFrameDetails[] GetStackFrames()
/// <returns>The list of VariableScope instances which describe the available variable scopes.</returns>
public VariableScope[] GetVariableScopes(int stackFrameId)
{
int localStackFrameVariableId = this.stackFrameDetails[stackFrameId].LocalVariables.Id;
int autoVariablesId = this.stackFrameDetails[stackFrameId].AutoVariables.Id;

return new VariableScope[]
{
new VariableScope(this.stackFrameDetails[stackFrameId].LocalVariables.Id, "Local"),
new VariableScope(this.scriptScopeVariables.Id, "Script"),
new VariableScope(this.globalScopeVariables.Id, "Global"),
new VariableScope(autoVariablesId, VariableContainerDetails.AutoVariablesName),
new VariableScope(localStackFrameVariableId, VariableContainerDetails.LocalScopeName),
new VariableScope(this.scriptScopeVariables.Id, VariableContainerDetails.ScriptScopeName),
new VariableScope(this.globalScopeVariables.Id, VariableContainerDetails.GlobalScopeName),
};
}

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

// Must retrieve global/script variales before stack frame variables
// as we check stack frame variables against globals.
await FetchGlobalAndScriptVariables();
await FetchStackFrames();

}

private async Task FetchGlobalAndScriptVariables()
{
this.scriptScopeVariables = await FetchVariableContainer("Script");
this.globalScopeVariables = await FetchVariableContainer("Global");
// Retrieve globals first as script variable retrieval needs to search globals.
this.globalScopeVariables =
await FetchVariableContainer(VariableContainerDetails.GlobalScopeName, null);

this.scriptScopeVariables =
await FetchVariableContainer(VariableContainerDetails.ScriptScopeName, null);
}

private async Task<VariableContainerDetails> FetchVariableContainer(string scope)
private async Task<VariableContainerDetails> FetchVariableContainer(
string scope,
VariableContainerDetails autoVariables)
{
PSCommand psCommand = new PSCommand();
psCommand.AddCommand("Get-Variable");
psCommand.AddParameter("Scope", scope);

var variableContainerDetails = new VariableContainerDetails(this.nextVariableId++, "Scope: " + scope);
this.variables.Add(variableContainerDetails);
var scopeVariableContainer =
new VariableContainerDetails(this.nextVariableId++, "Scope: " + scope);
this.variables.Add(scopeVariableContainer);

var results = await this.powerShellContext.ExecuteCommand<PSVariable>(psCommand);
foreach (PSVariable variable in results)
foreach (PSVariable psvariable in results)
{
var variableDetails = new VariableDetails(variable) { Id = this.nextVariableId++ };
var variableDetails = new VariableDetails(psvariable) { Id = this.nextVariableId++ };
this.variables.Add(variableDetails);
variableContainerDetails.Children.Add(variableDetails);
scopeVariableContainer.Children.Add(variableDetails.Name, variableDetails);

if ((autoVariables != null) && AddToAutoVariables(psvariable, scope))
{
autoVariables.Children.Add(variableDetails.Name, variableDetails);
}
}

return variableContainerDetails;
return scopeVariableContainer;
}

private bool AddToAutoVariables(PSVariable psvariable, string scope)
{
if ((scope == VariableContainerDetails.GlobalScopeName) ||
(scope == VariableContainerDetails.ScriptScopeName))
{
// We don't A) have a good way of distinguishing built-in from user created variables
// and B) globalScopeVariables.Children.ContainsKey() doesn't work for built-in variables
// stored in a child variable container within the globals variable container.
return false;
}

var constantAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.Constant;
var readonlyAllScope = ScopedItemOptions.AllScope | ScopedItemOptions.ReadOnly;

// Some local variables, if they exist, should be displayed by default
if (psvariable.GetType().Name == "LocalVariable")
{
if (psvariable.Name.Equals("_"))
{
return true;
}
else if (psvariable.Name.Equals("args", StringComparison.OrdinalIgnoreCase))
{
var array = psvariable.Value as Array;
return array != null ? array.Length > 0 : false;
}

return false;
}
else if (psvariable.GetType() != typeof(PSVariable))
{
return false;
}

if (((psvariable.Options | constantAllScope) == constantAllScope) ||
((psvariable.Options | readonlyAllScope) == readonlyAllScope))
{
string prefixedVariableName = VariableDetails.DollarPrefix + psvariable.Name;
if (this.globalScopeVariables.Children.ContainsKey(prefixedVariableName))
{
return false;
}
}

if ((psvariable.Value != null) && (psvariable.Value.GetType() == typeof(PSDebugContext)))
{
return false;
}

return true;
}

private async Task FetchStackFrames()
Expand All @@ -354,8 +422,18 @@ private async Task FetchStackFrames()

for (int i = 0; i < callStackFrames.Length; i++)
{
VariableContainerDetails localVariables = await FetchVariableContainer(i.ToString());
this.stackFrameDetails[i] = StackFrameDetails.Create(callStackFrames[i], localVariables);
VariableContainerDetails autoVariables =
new VariableContainerDetails(
this.nextVariableId++,
VariableContainerDetails.AutoVariablesName);

this.variables.Add(autoVariables);

VariableContainerDetails localVariables =
await FetchVariableContainer(i.ToString(), autoVariables);

this.stackFrameDetails[i] =
StackFrameDetails.Create(callStackFrames[i], autoVariables, localVariables);
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/PowerShellEditorServices/Debugging/StackFrameDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class StackFrameDetails
/// </summary>
public int ColumnNumber { get; private set; }

/// <summary>
/// Gets or sets the VariableContainerDetails that contains the auto variables.
/// </summary>
public VariableContainerDetails AutoVariables { get; private set; }

/// <summary>
/// Gets or sets the VariableContainerDetails that contains the local variables.
/// </summary>
Expand All @@ -45,12 +50,16 @@ public class StackFrameDetails
/// <param name="callStackFrame">
/// The original CallStackFrame instance from which details will be obtained.
/// </param>
/// <param name="autoVariables">
/// A variable container with all the filtered, auto variables for this stack frame.
/// </param>
/// <param name="localVariables">
/// A variable container with all the local variables for this stack frame.
/// </param>
/// <returns>A new instance of the StackFrameDetails class.</returns>
static internal StackFrameDetails Create(
CallStackFrame callStackFrame,
VariableContainerDetails autoVariables,
VariableContainerDetails localVariables)
{
return new StackFrameDetails
Expand All @@ -59,6 +68,7 @@ static internal StackFrameDetails Create(
FunctionName = callStackFrame.FunctionName,
LineNumber = callStackFrame.Position.StartLineNumber,
ColumnNumber = callStackFrame.Position.StartColumnNumber,
AutoVariables = autoVariables,
LocalVariables = localVariables
};
}
Expand Down
30 changes: 26 additions & 4 deletions src/PowerShellEditorServices/Debugging/VariableContainerDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,27 @@ namespace Microsoft.PowerShell.EditorServices
[DebuggerDisplay("Name = {Name}, Id = {Id}, Count = {Children.Count}")]
public class VariableContainerDetails : VariableDetailsBase
{
private readonly List<VariableDetailsBase> children;
/// <summary>
/// Provides a constant for the name of the Global scope.
/// </summary>
public const string AutoVariablesName = "Auto";

/// <summary>
/// Provides a constant for the name of the Global scope.
/// </summary>
public const string GlobalScopeName = "Global";

/// <summary>
/// Provides a constant for the name of the Local scope.
/// </summary>
public const string LocalScopeName = "Local";

/// <summary>
/// Provides a constant for the name of the Script scope.
/// </summary>
public const string ScriptScopeName = "Script";

private readonly Dictionary<string, VariableDetailsBase> children;

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

this.children = new List<VariableDetailsBase>();
this.children = new Dictionary<string, VariableDetailsBase>();
}

/// <summary>
/// Gets the collection of child variables.
/// </summary>
public List<VariableDetailsBase> Children
public IDictionary<string, VariableDetailsBase> Children
{
get { return this.children; }
}
Expand All @@ -53,7 +73,9 @@ public List<VariableDetailsBase> Children
/// <returns></returns>
public override VariableDetailsBase[] GetChildren()
{
return this.children.ToArray();
var variablesArray = new VariableDetailsBase[this.children.Count];
this.children.Values.CopyTo(variablesArray, 0);
return variablesArray;
}
}
}