Skip to content

Improve debugging support #30

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 2 commits into from
Nov 13, 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
25 changes: 10 additions & 15 deletions src/PowerShellEditorServices.Host/MessageLoop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,22 +183,17 @@ await this.messageWriter.WriteMessage(
}, null);
}

void PowerShellSession_OutputWritten(object sender, OutputWrittenEventArgs e)
async void PowerShellSession_OutputWritten(object sender, OutputWrittenEventArgs e)
{
// TODO: change this to use the OutputEvent!

//await this.messageWriter.WriteMessage(
// new ReplWriteOutputEvent
// {
// Body = new ReplWriteOutputEventBody
// {
// LineContents = e.OutputText,
// LineType = e.OutputType,
// IncludeNewLine = e.IncludeNewLine,
// ForegroundColor = e.ForegroundColor,
// BackgroundColor = e.BackgroundColor
// }
// });
await this.messageWriter.WriteMessage(
new OutputEvent
{
Body = new OutputEventBody
{
Output = e.OutputText + (e.IncludeNewLine ? "\r\n" : string.Empty),
Category = (e.OutputType == OutputType.Error) ? "stderr" : "stdout"
}
});
}

#endregion
Expand Down
27 changes: 27 additions & 0 deletions src/PowerShellEditorServices.Transport.Stdio/Event/OutputEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

using Microsoft.PowerShell.EditorServices.Transport.Stdio.Message;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.PowerShell.EditorServices.Transport.Stdio.Event
{
[MessageTypeName("output")]
public class OutputEvent : EventBase<OutputEventBody>
{
}

public class OutputEventBody
{
public string Category { get; set; }

public string Output { get; set; }
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<Compile Include="Event\EventBase.cs" />
<Compile Include="Event\ExitedEvent.cs" />
<Compile Include="Event\InitializedEvent.cs" />
<Compile Include="Event\OutputEvent.cs" />
<Compile Include="Event\ReplPromptChoiceEvent.cs">
<SubType>Code</SubType>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public override async Task ProcessMessage(
MessageWriter messageWriter)
{
VariableDetails result =
editorSession.DebugService.EvaluateExpression(
await editorSession.DebugService.EvaluateExpression(
this.Arguments.Expression,
this.Arguments.FrameId);

Expand Down
42 changes: 32 additions & 10 deletions src/PowerShellEditorServices/Debugging/DebugService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,17 @@ public VariableDetails[] GetVariables(int variableReferenceId)
}

/// <summary>
/// Evaluates an expression in the context of the stopped
/// debugger. For now, this just does simple evaluation of
/// a variable in the session. In the future it will execute
/// commands in the PowerShellSession.
/// Evaluates a variable expression in the context of the stopped
/// debugger. This method decomposes the variable expression to
/// walk the cached variable data for the specified stack frame.
/// </summary>
/// <param name="expressionString">The expression string to execute.</param>
/// <param name="stackFrameId">The ID of the stack frame in which the expression should be executed.</param>
/// <param name="variableExpression">The variable expression string to evaluate.</param>
/// <param name="stackFrameId">The ID of the stack frame in which the expression should be evaluated.</param>
/// <returns>A VariableDetails object containing the result.</returns>
public VariableDetails EvaluateExpression(string expressionString, int stackFrameId)
public VariableDetails GetVariableFromExpression(string variableExpression, int stackFrameId)
{
// Break up the variable path
string[] variablePathParts = expressionString.Split('.');
string[] variablePathParts = variableExpression.Split('.');

VariableDetails resolvedVariable = null;
IEnumerable<VariableDetails> variableList = this.currentVariables;
Expand All @@ -222,10 +221,10 @@ public VariableDetails EvaluateExpression(string expressionString, int stackFram
v =>
string.Equals(
v.Name,
expressionString,
variableExpression,
StringComparison.InvariantCultureIgnoreCase));

if (resolvedVariable != null &&
if (resolvedVariable != null &&
resolvedVariable.IsExpandable)
{
// Continue by searching in this variable's children
Expand All @@ -236,6 +235,29 @@ public VariableDetails EvaluateExpression(string expressionString, int stackFram
return resolvedVariable;
}

/// <summary>
/// Evaluates an expression in the context of the stopped
/// debugger. This method will execute the specified expression
/// PowerShellSession.
/// </summary>
/// <param name="expressionString">The expression string to execute.</param>
/// <param name="stackFrameId">The ID of the stack frame in which the expression should be executed.</param>
/// <returns>A VariableDetails object containing the result.</returns>
public async Task<VariableDetails> EvaluateExpression(string expressionString, int stackFrameId)
{
var results =
await this.powerShellSession.ExecuteScriptString(
expressionString);

// Since this method should only be getting invoked in the debugger,
// we can assume that Out-String will be getting used to format results
// of command executions into string output.

return new VariableDetails(
expressionString,
string.Join(Environment.NewLine, results));
}

/// <summary>
/// Gets the list of stack frames at the point where the
/// debugger sf stopped.
Expand Down
9 changes: 7 additions & 2 deletions src/PowerShellEditorServices/Debugging/VariableDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public class VariableDetails
{
#region Fields

/// <summary>
/// Provides a constant for the dollar sign variable prefix string.
/// </summary>
public const string DollarPrefix = "$";

/// <summary>
/// Provides a constant for the variable ID of the local variable scope.
/// </summary>
Expand Down Expand Up @@ -79,7 +84,7 @@ public class VariableDetails
/// The PSVariable instance from which variable details will be obtained.
/// </param>
public VariableDetails(PSVariable psVariable)
: this(psVariable.Name, psVariable.Value)
: this(DollarPrefix + psVariable.Name, psVariable.Value)
{
}

Expand All @@ -105,8 +110,8 @@ public VariableDetails(string name, object value)
{
this.valueObject = value;

this.IsExpandable = GetIsExpandable(value);
this.Name = name;
this.IsExpandable = GetIsExpandable(value);
this.ValueString =
this.IsExpandable == false ?
GetValueString(value) :
Expand Down
6 changes: 3 additions & 3 deletions src/PowerShellEditorServices/Session/PowerShellSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,12 @@ public Task ExecuteCommand(PSCommand psCommand)
/// </summary>
/// <param name="scriptString">The script string to execute.</param>
/// <returns>A Task that can be awaited for the script completion.</returns>
public async Task ExecuteScriptString(string scriptString)
public async Task<IEnumerable<object>> ExecuteScriptString(string scriptString)
{
PSCommand psCommand = new PSCommand();
psCommand.AddScript(scriptString);

await this.ExecuteCommand<object>(psCommand, true);
return await this.ExecuteCommand<object>(psCommand, true);
}

/// <summary>
Expand All @@ -328,7 +328,7 @@ public async Task ExecuteScriptAtPath(string scriptPath)
PSCommand command = new PSCommand();
command.AddCommand(scriptPath);

await this.ExecuteCommand<object>(command);
await this.ExecuteCommand<object>(command, true);
}

/// <summary>
Expand Down
13 changes: 7 additions & 6 deletions test/PowerShellEditorServices.Test.Host/ScenarioTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,20 +371,21 @@ await this.MessageWriter.WriteMessage(
Assert.Equal(sigHelp.Body.ArgumentCount, 1);
}

[Fact(Skip = "Console output events are disabled until we migrate to the updated debug protocol.")]
[Fact]
public async Task ServiceExecutesReplCommandAndReceivesOutput()
{
await this.MessageWriter.WriteMessage(
new ReplExecuteRequest
new EvaluateRequest
{
Arguments = new ReplExecuteArgs
Arguments = new EvaluateRequestArguments
{
CommandString = "1 + 2"
Expression = "1 + 2"
}
});

ReplWriteOutputEvent replWriteLineEvent = this.WaitForMessage<ReplWriteOutputEvent>();
Assert.Equal("3", replWriteLineEvent.Body.LineContents);
OutputEvent outputEvent = this.WaitForMessage<OutputEvent>();
Assert.Equal("3\r\n", outputEvent.Body.Output);
Assert.Equal("stdout", outputEvent.Body.Category);
}

[Fact(Skip = "Choice prompt functionality is currently in transition to a new model.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,25 +177,25 @@ await this.debugService.SetBreakpoints(

// TODO: Add checks for correct value strings as well

var strVar = variables.FirstOrDefault(v => v.Name == "strVar");
var strVar = variables.FirstOrDefault(v => v.Name == "$strVar");
Assert.NotNull(strVar);
Assert.False(strVar.IsExpandable);

var objVar = variables.FirstOrDefault(v => v.Name == "objVar");
var objVar = variables.FirstOrDefault(v => v.Name == "$objVar");
Assert.NotNull(objVar);
Assert.True(objVar.IsExpandable);

var objChildren = debugService.GetVariables(objVar.Id);
Assert.Equal(2, objChildren.Length);

var arrVar = variables.FirstOrDefault(v => v.Name == "arrVar");
var arrVar = variables.FirstOrDefault(v => v.Name == "$arrVar");
Assert.NotNull(arrVar);
Assert.True(arrVar.IsExpandable);

var arrChildren = debugService.GetVariables(arrVar.Id);
Assert.Equal(4, arrChildren.Length);

var classVar = variables.FirstOrDefault(v => v.Name == "classVar");
var classVar = variables.FirstOrDefault(v => v.Name == "$classVar");
Assert.NotNull(classVar);
Assert.True(classVar.IsExpandable);

Expand Down