Skip to content

Commit 3a3d984

Browse files
committed
Merge pull request #103 from adamdriscoll/v3v4
PowerShell v3/v4 Support
2 parents 995b5b5 + a519228 commit 3a3d984

14 files changed

+239
-37
lines changed

src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ public void Stop()
7979
{
8080
// By disposing the thread we cancel all existing work
8181
// and cause the thread to be destroyed.
82-
this.messageLoopThread.Dispose();
82+
if (this.messageLoopThread != null)
83+
this.messageLoopThread.Dispose();
8384
}
8485

8586
public void SetRequestHandler<TParams, TResult>(

src/PowerShellEditorServices.Protocol/Server/ProtocolServer.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public void Stop()
7070
this.OnStop();
7171

7272
this.serverChannel.Stop();
73-
this.serverExitedTask.SetResult(true);
73+
if (this.serverExitedTask != null)
74+
this.serverExitedTask.SetResult(true);
7475
}
7576
}
7677

src/PowerShellEditorServices/Language/AstOperations.cs

+14-5
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,22 @@ static public SymbolReference FindDefinitionOfSymbol(
176176
/// Finds all symbols in a script
177177
/// </summary>
178178
/// <param name="scriptAst">The abstract syntax tree of the given script</param>
179+
/// <param name="powerShellVersion">The PowerShell version the Ast was generated from</param>
179180
/// <returns>A collection of SymbolReference objects</returns>
180-
static public IEnumerable<SymbolReference> FindSymbolsInDocument(Ast scriptAst)
181+
static public IEnumerable<SymbolReference> FindSymbolsInDocument(Ast scriptAst, Version powerShellVersion)
181182
{
182-
FindSymbolsVisitor findSymbolsVisitor = new FindSymbolsVisitor();
183-
scriptAst.Visit(findSymbolsVisitor);
184-
185-
return findSymbolsVisitor.SymbolReferences;
183+
if (powerShellVersion >= new Version(5,0))
184+
{
185+
FindSymbolsVisitor2 findSymbolsVisitor = new FindSymbolsVisitor2();
186+
scriptAst.Visit(findSymbolsVisitor);
187+
return findSymbolsVisitor.SymbolReferences;
188+
}
189+
else
190+
{
191+
FindSymbolsVisitor findSymbolsVisitor = new FindSymbolsVisitor();
192+
scriptAst.Visit(findSymbolsVisitor);
193+
return findSymbolsVisitor.SymbolReferences;
194+
}
186195
}
187196

188197
/// <summary>

src/PowerShellEditorServices/Language/CompletionResults.cs

+2
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,10 @@ private static CompletionType ConvertCompletionResultType(
275275
case CompletionResultType.Type:
276276
return CompletionType.Type;
277277

278+
#if !PowerShellv3
278279
case CompletionResultType.Keyword:
279280
return CompletionType.Keyword;
281+
#endif
280282

281283
case CompletionResultType.ProviderContainer:
282284
case CompletionResultType.ProviderItem:

src/PowerShellEditorServices/Language/FindSymbolVisitor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.EditorServices
1010
/// <summary>
1111
/// The visitor used to find the the symbol at a specfic location in the AST
1212
/// </summary>
13-
internal class FindSymbolVisitor : AstVisitor2
13+
internal class FindSymbolVisitor : AstVisitor
1414
{
1515
private int lineNumber;
1616
private int columnNumber;

src/PowerShellEditorServices/Language/FindSymbolsVisitor.cs

+5-20
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ namespace Microsoft.PowerShell.EditorServices
1111
/// <summary>
1212
/// The visitor used to find all the symbols (function and class defs) in the AST.
1313
/// </summary>
14-
internal class FindSymbolsVisitor : AstVisitor2
14+
/// <remarks>
15+
/// Requires PowerShell v3 or higher
16+
/// </remarks>
17+
internal class FindSymbolsVisitor : AstVisitor
1518
{
1619
public List<SymbolReference> SymbolReferences { get; private set; }
1720

@@ -68,25 +71,7 @@ public override AstVisitAction VisitVariableExpression(VariableExpressionAst var
6871

6972
return AstVisitAction.Continue;
7073
}
71-
72-
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
73-
{
74-
IScriptExtent nameExtent = new ScriptExtent() {
75-
Text = configurationDefinitionAst.InstanceName.Extent.Text,
76-
StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber,
77-
EndLineNumber = configurationDefinitionAst.Extent.EndLineNumber,
78-
StartColumnNumber = configurationDefinitionAst.Extent.StartColumnNumber,
79-
EndColumnNumber = configurationDefinitionAst.Extent.EndColumnNumber
80-
};
81-
82-
this.SymbolReferences.Add(
83-
new SymbolReference(
84-
SymbolType.Configuration,
85-
nameExtent));
86-
87-
return AstVisitAction.Continue;
88-
}
89-
74+
9075
private bool IsAssignedAtScriptScope(VariableExpressionAst variableExpressionAst)
9176
{
9277
Ast parent = variableExpressionAst.Parent;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System.Collections.Generic;
2+
using System.Management.Automation.Language;
3+
4+
namespace Microsoft.PowerShell.EditorServices
5+
{
6+
/// <summary>
7+
/// The visitor used to find all the symbols (function and class defs) in the AST.
8+
/// </summary>
9+
/// <remarks>
10+
/// Requires PowerShell v5 or higher
11+
/// </remarks>
12+
internal class FindSymbolsVisitor2 : AstVisitor2
13+
{
14+
private FindSymbolsVisitor findSymbolsVisitor;
15+
16+
public List<SymbolReference> SymbolReferences
17+
{
18+
get
19+
{
20+
return this.findSymbolsVisitor.SymbolReferences;
21+
}
22+
}
23+
24+
public FindSymbolsVisitor2()
25+
{
26+
this.findSymbolsVisitor = new FindSymbolsVisitor();
27+
}
28+
29+
/// <summary>
30+
/// Adds each function defintion as a
31+
/// </summary>
32+
/// <param name="functionDefinitionAst">A functionDefinitionAst object in the script's AST</param>
33+
/// <returns>A decision to stop searching if the right symbol was found,
34+
/// or a decision to continue if it wasn't found</returns>
35+
public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)
36+
{
37+
return this.findSymbolsVisitor.VisitFunctionDefinition(functionDefinitionAst);
38+
}
39+
40+
/// <summary>
41+
/// Checks to see if this variable expression is the symbol we are looking for.
42+
/// </summary>
43+
/// <param name="variableExpressionAst">A VariableExpressionAst object in the script's AST</param>
44+
/// <returns>A descion to stop searching if the right symbol was found,
45+
/// or a decision to continue if it wasn't found</returns>
46+
public override AstVisitAction VisitVariableExpression(VariableExpressionAst variableExpressionAst)
47+
{
48+
return this.findSymbolsVisitor.VisitVariableExpression(variableExpressionAst);
49+
}
50+
51+
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
52+
{
53+
IScriptExtent nameExtent = new ScriptExtent()
54+
{
55+
Text = configurationDefinitionAst.InstanceName.Extent.Text,
56+
StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber,
57+
EndLineNumber = configurationDefinitionAst.Extent.EndLineNumber,
58+
StartColumnNumber = configurationDefinitionAst.Extent.StartColumnNumber,
59+
EndColumnNumber = configurationDefinitionAst.Extent.EndColumnNumber
60+
};
61+
62+
this.findSymbolsVisitor.SymbolReferences.Add(
63+
new SymbolReference(
64+
SymbolType.Configuration,
65+
nameExtent));
66+
67+
return AstVisitAction.Continue;
68+
}
69+
}
70+
}

src/PowerShellEditorServices/Language/LanguageService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ public FindOccurrencesResult FindSymbolsInFile(ScriptFile scriptFile)
221221

222222
IEnumerable<SymbolReference> symbolReferencesinFile =
223223
AstOperations
224-
.FindSymbolsInDocument(scriptFile.ScriptAst)
224+
.FindSymbolsInDocument(scriptFile.ScriptAst, this.powerShellContext.PowerShellVersion)
225225
.Select(
226226
reference => {
227227
reference.SourceLine =

src/PowerShellEditorServices/PowerShellEditorServices.csproj

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
<FileAlignment>512</FileAlignment>
1414
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
1515
<RestorePackages>true</RestorePackages>
16+
<DefineConstants Condition=" '$(PowerShellVersion)' == 'v3'">PowerShellv3</DefineConstants>
17+
<DefineConstants Condition=" '$(PowerShellVersion)' == 'v4'">PowerShellv4</DefineConstants>
18+
<DefineConstants Condition=" '$(PowerShellVersion)' == '' Or '$(PowerShellVersion)' == 'v5'">PowerShellv5</DefineConstants>
1619
</PropertyGroup>
1720
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1821
<DebugSymbols>true</DebugSymbols>
1922
<DebugType>full</DebugType>
2023
<Optimize>false</Optimize>
2124
<OutputPath>bin\Debug\</OutputPath>
22-
<DefineConstants>DEBUG;TRACE</DefineConstants>
25+
<DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
2326
<ErrorReport>prompt</ErrorReport>
2427
<WarningLevel>4</WarningLevel>
2528
<DocumentationFile>bin\Debug\Microsoft.PowerShell.EditorServices.XML</DocumentationFile>
@@ -28,7 +31,7 @@
2831
<DebugType>pdbonly</DebugType>
2932
<Optimize>true</Optimize>
3033
<OutputPath>bin\Release\</OutputPath>
31-
<DefineConstants>TRACE</DefineConstants>
34+
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
3235
<ErrorReport>prompt</ErrorReport>
3336
<WarningLevel>4</WarningLevel>
3437
<WarningsAsErrors>1591,1573,1572</WarningsAsErrors>
@@ -49,12 +52,12 @@
4952
</Reference>
5053
<Reference Include="System" />
5154
<Reference Include="System.Core" />
55+
<Reference Include="System.Management.Automation" />
5256
<Reference Include="System.Xml.Linq" />
5357
<Reference Include="System.Data.DataSetExtensions" />
5458
<Reference Include="Microsoft.CSharp" />
5559
<Reference Include="System.Data" />
5660
<Reference Include="System.Xml" />
57-
<Reference Include="System.Management.Automation" />
5861
</ItemGroup>
5962
<ItemGroup>
6063
<Compile Include="Analysis\AnalysisOutputWriter.cs" />
@@ -76,6 +79,7 @@
7679
<Compile Include="Language\FindReferencesResult.cs" />
7780
<Compile Include="Language\FindReferencesVisitor.cs" />
7881
<Compile Include="Language\FindSymbolsVisitor.cs" />
82+
<Compile Include="Language\FindSymbolsVisitor2.cs" />
7983
<Compile Include="Language\FindSymbolVisitor.cs" />
8084
<Compile Include="Language\GetDefinitionResult.cs" />
8185
<Compile Include="Language\LanguageService.cs" />

src/PowerShellEditorServices/Session/PowerShellContext.cs

+46-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
namespace Microsoft.PowerShell.EditorServices
1717
{
18+
using System.Collections;
1819
using System.Management.Automation;
1920
using System.Management.Automation.Host;
2021
using System.Management.Automation.Runspaces;
@@ -68,6 +69,14 @@ public PowerShellContextState SessionState
6869
private set;
6970
}
7071

72+
/// <summary>
73+
/// PowerShell Version of the current runspace.
74+
/// </summary>
75+
public Version PowerShellVersion
76+
{
77+
get; private set;
78+
}
79+
7180
#endregion
7281

7382
#region Constructors
@@ -109,7 +118,7 @@ private void Initialize(Runspace initialRunspace)
109118

110119
this.initialRunspace = initialRunspace;
111120
this.currentRunspace = initialRunspace;
112-
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);
121+
113122
this.currentRunspace.Debugger.BreakpointUpdated += OnBreakpointUpdated;
114123
this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop;
115124

@@ -120,9 +129,38 @@ private void Initialize(Runspace initialRunspace)
120129
// TODO: Should this be configurable?
121130
this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned);
122131

132+
PowerShellVersion = GetPowerShellVersion();
133+
134+
#if !PowerShellv3
135+
if (PowerShellVersion > new Version(3,0))
136+
{
137+
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);
138+
}
139+
#endif
140+
123141
this.SessionState = PowerShellContextState.Ready;
124142
}
125143

144+
private Version GetPowerShellVersion()
145+
{
146+
try
147+
{
148+
var psVersionTable = this.currentRunspace.SessionStateProxy.GetVariable("PSVersionTable") as Hashtable;
149+
if (psVersionTable != null)
150+
{
151+
var version = psVersionTable["PSVersion"] as Version;
152+
if (version == null) return new Version(5, 0);
153+
return version;
154+
}
155+
}
156+
catch (Exception ex)
157+
{
158+
Logger.Write(LogLevel.Warning, "Failed to look up PowerShell version. Defaulting to version 5. " + ex.Message);
159+
}
160+
161+
return new Version(5, 0);
162+
}
163+
126164
#endregion
127165

128166
#region Public Methods
@@ -370,7 +408,12 @@ internal void BreakExecution()
370408
{
371409
Logger.Write(LogLevel.Verbose, "Debugger break requested...");
372410

373-
this.currentRunspace.Debugger.SetDebuggerStepMode(true);
411+
#if PowerShellv5
412+
if (PowerShellVersion >= new Version(5, 0))
413+
{
414+
this.currentRunspace.Debugger.SetDebuggerStepMode(true);
415+
}
416+
#endif
374417
}
375418

376419
internal void ResumeDebugger(DebuggerResumeAction resumeAction)
@@ -568,7 +611,7 @@ private static string GetStringForPSCommand(PSCommand psCommand)
568611

569612
return stringBuilder.ToString();
570613
}
571-
614+
572615
private void SetExecutionPolicy(ExecutionPolicy desiredExecutionPolicy)
573616
{
574617
var currentPolicy = ExecutionPolicy.Undefined;

test/PowerShellEditorServices.Test/Language/LanguageServiceTests.cs

+11
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@
1010
using Microsoft.PowerShell.EditorServices.Test.Shared.References;
1111
using Microsoft.PowerShell.EditorServices.Test.Shared.SymbolDetails;
1212
using Microsoft.PowerShell.EditorServices.Test.Shared.Symbols;
13+
using Microsoft.Win32;
1314
using System;
15+
using System.Diagnostics;
1416
using System.IO;
1517
using System.Linq;
1618
using System.Management.Automation.Runspaces;
1719
using System.Threading;
1820
using System.Threading.Tasks;
21+
using System.Xml.Linq;
1922
using Xunit;
2023

2124
namespace Microsoft.PowerShell.EditorServices.Test.Language
@@ -25,6 +28,7 @@ public class LanguageServiceTests : IDisposable
2528
private Workspace workspace;
2629
private LanguageService languageService;
2730
private PowerShellContext powerShellContext;
31+
2832

2933
public LanguageServiceTests()
3034
{
@@ -262,6 +266,7 @@ public void LanguageServiceFindsSymbolsInFile()
262266
Assert.Equal(4, symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Function).Count());
263267
Assert.Equal(3, symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Variable).Count());
264268
Assert.Equal(1, symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Workflow).Count());
269+
Assert.Equal(1, symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Configuration).Count());
265270

266271
SymbolReference firstFunctionSymbol = symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Function).First();
267272
Assert.Equal("AFunction", firstFunctionSymbol.SymbolName);
@@ -277,6 +282,11 @@ public void LanguageServiceFindsSymbolsInFile()
277282
Assert.Equal("AWorkflow", firstWorkflowSymbol.SymbolName);
278283
Assert.Equal(23, firstWorkflowSymbol.ScriptRegion.StartLineNumber);
279284
Assert.Equal(1, firstWorkflowSymbol.ScriptRegion.StartColumnNumber);
285+
286+
SymbolReference firstConfigurationSymbol = symbolsResult.FoundOccurrences.Where(r => r.SymbolType == SymbolType.Configuration).First();
287+
Assert.Equal("AConfiguration", firstConfigurationSymbol.SymbolName);
288+
Assert.Equal(25, firstConfigurationSymbol.ScriptRegion.StartLineNumber);
289+
Assert.Equal(1, firstConfigurationSymbol.ScriptRegion.StartColumnNumber);
280290
}
281291

282292
[Fact]
@@ -289,6 +299,7 @@ public void LanguageServiceFindsSymbolsInNoSymbolsFile()
289299
Assert.Equal(0, symbolsResult.FoundOccurrences.Count());
290300
}
291301

302+
292303
private ScriptFile GetScriptFile(ScriptRegion scriptRegion)
293304
{
294305
const string baseSharedScriptPath =

0 commit comments

Comments
 (0)