Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions src/PowerShellEditorServices/Language/CompletionResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,10 @@ private static CompletionType ConvertCompletionResultType(
case CompletionResultType.Type:
return CompletionType.Type;

#if !PowerShellv3
case CompletionResultType.Keyword:
return CompletionType.Keyword;
#endif

case CompletionResultType.ProviderContainer:
case CompletionResultType.ProviderItem:
Expand Down
4 changes: 4 additions & 0 deletions src/PowerShellEditorServices/Language/FindSymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ namespace Microsoft.PowerShell.EditorServices
/// <summary>
/// The visitor used to find the the symbol at a specfic location in the AST
/// </summary>
#if PowerShellv5
internal class FindSymbolVisitor : AstVisitor2
#else
internal class FindSymbolVisitor : AstVisitor
#endif
{
private int lineNumber;
private int columnNumber;
Expand Down
10 changes: 9 additions & 1 deletion src/PowerShellEditorServices/Language/FindSymbolsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ namespace Microsoft.PowerShell.EditorServices
/// <summary>
/// The visitor used to find all the symbols (function and class defs) in the AST.
/// </summary>
#if PowerShellv5
internal class FindSymbolsVisitor : AstVisitor2
#else
internal class FindSymbolsVisitor : AstVisitor
#endif
{
public List<SymbolReference> SymbolReferences { get; private set; }

Expand Down Expand Up @@ -69,9 +73,11 @@ public override AstVisitAction VisitVariableExpression(VariableExpressionAst var
return AstVisitAction.Continue;
}

#if PowerShell5
public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinitionAst configurationDefinitionAst)
{
IScriptExtent nameExtent = new ScriptExtent() {
IScriptExtent nameExtent = new ScriptExtent()
{
Text = configurationDefinitionAst.InstanceName.Extent.Text,
StartLineNumber = configurationDefinitionAst.Extent.StartLineNumber,
EndLineNumber = configurationDefinitionAst.Extent.EndLineNumber,
Expand All @@ -86,6 +92,8 @@ public override AstVisitAction VisitConfigurationDefinition(ConfigurationDefinit

return AstVisitAction.Continue;
}
#endif


private bool IsAssignedAtScriptScope(VariableExpressionAst variableExpressionAst)
{
Expand Down
12 changes: 9 additions & 3 deletions src/PowerShellEditorServices/PowerShellEditorServices.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<DefineConstants Condition=" '$(PowerShellVersion)' == 'v3'">PowerShellv3</DefineConstants>
<DefineConstants Condition=" '$(PowerShellVersion)' == 'v4'">PowerShellv4</DefineConstants>
<DefineConstants Condition=" '$(PowerShellVersion)' == '' Or '$(PowerShellVersion)' == 'v5'">PowerShellv5</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Microsoft.PowerShell.EditorServices.XML</DocumentationFile>
Expand All @@ -28,7 +31,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;$(DefineConstants)</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<WarningsAsErrors>1591,1573,1572</WarningsAsErrors>
Expand All @@ -49,12 +52,15 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll</HintPath>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we take out the HintPath? I dunno if it might be differnet somehow on other people's machines

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I'll fix that. I'm using it for the tests but it should be put in dynamically.

</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="System.Management.Automation" />
</ItemGroup>
<ItemGroup>
<Compile Include="Analysis\AnalysisOutputWriter.cs" />
Expand Down
35 changes: 32 additions & 3 deletions src/PowerShellEditorServices/Session/PowerShellContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

namespace Microsoft.PowerShell.EditorServices
{
using System.Collections;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
Expand Down Expand Up @@ -68,6 +69,14 @@ public PowerShellContextState SessionState
private set;
}

/// <summary>
/// PowerShell Version of the current runspace.
/// </summary>
public Version PowerShellVersion
{
get; private set;
}

#endregion

#region Constructors
Expand Down Expand Up @@ -109,7 +118,7 @@ private void Initialize(Runspace initialRunspace)

this.initialRunspace = initialRunspace;
this.currentRunspace = initialRunspace;
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);

this.currentRunspace.Debugger.BreakpointUpdated += OnBreakpointUpdated;
this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop;

Expand All @@ -120,6 +129,21 @@ private void Initialize(Runspace initialRunspace)
// TODO: Should this be configurable?
this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned);

var psVersionTable = this.currentRunspace.SessionStateProxy.GetVariable("PSVersionTable") as Hashtable;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance this could fail? Should we put a try/catch around it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so I'd say make it into its own private function, something like GetPowerShellVersion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Will do.

if (psVersionTable != null)
{
Version version;
Version.TryParse(psVersionTable["PSVersion"] as string, out version);
PowerShellVersion = version;
}

#if !PowerShellv3
if (PowerShellVersion > new Version(3,0))
{
this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);
}
#endif

this.SessionState = PowerShellContextState.Ready;
}

Expand Down Expand Up @@ -370,7 +394,12 @@ internal void BreakExecution()
{
Logger.Write(LogLevel.Verbose, "Debugger break requested...");

this.currentRunspace.Debugger.SetDebuggerStepMode(true);
#if PowerShellv5
if (PowerShellVersion >= new Version(5, 0))
{
this.currentRunspace.Debugger.SetDebuggerStepMode(true);
}
#endif
}

internal void ResumeDebugger(DebuggerResumeAction resumeAction)
Expand Down Expand Up @@ -568,7 +597,7 @@ private static string GetStringForPSCommand(PSCommand psCommand)

return stringBuilder.ToString();
}

private void SetExecutionPolicy(ExecutionPolicy desiredExecutionPolicy)
{
var currentPolicy = ExecutionPolicy.Undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
using Microsoft.PowerShell.EditorServices.Test.Shared.References;
using Microsoft.PowerShell.EditorServices.Test.Shared.SymbolDetails;
using Microsoft.PowerShell.EditorServices.Test.Shared.Symbols;
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Xunit;

namespace Microsoft.PowerShell.EditorServices.Test.Language
Expand All @@ -25,6 +28,7 @@ public class LanguageServiceTests : IDisposable
private Workspace workspace;
private LanguageService languageService;
private PowerShellContext powerShellContext;
private DirectoryInfo packageDirectory;

public LanguageServiceTests()
{
Expand All @@ -37,6 +41,11 @@ public LanguageServiceTests()
public void Dispose()
{
this.powerShellContext.Dispose();

if (packageDirectory != null && packageDirectory.Exists)
{
packageDirectory.Delete(true);
}
}

[Fact]
Expand Down Expand Up @@ -289,6 +298,96 @@ public void LanguageServiceFindsSymbolsInNoSymbolsFile()
Assert.Equal(0, symbolsResult.FoundOccurrences.Count());
}

[Theory]
[InlineData("3")]
[InlineData("4")]
[InlineData("5")]
public void CompilesWithPowerShellVersion(string version)
{
var assemblyPath = InstallPackage(string.Format("Microsoft.PowerShell.{0}.ReferenceAssemblies", version), "1.0.0");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! This is a neat way to do it. Two things I'd recommend:

  1. Maybe we can just make the reference assemblies a NuGet dependency of the test project so that we don't need to call NuGet from within the test code? That way you can just reference the already-pulled dependencies directly. Might make things easier for AppVeyor
  2. It'd be nice to move these tests into their own test class/file, maybe something like PowerShellVersionTests.cs

var projectPath = @"..\..\..\..\src\PowerShellEditorServices\PowerShellEditorServices.csproj";
FileInfo fi = new FileInfo(projectPath);
var projectVersion = Path.Combine(fi.DirectoryName, version + ".PowerShellEditorServices.csproj");

var doc = XDocument.Load(projectPath);
var references = doc.Root.Descendants().Where(m => m.Name.LocalName == "Reference");
var reference = references.First(m => m.Attribute("Include").Value.StartsWith("System.Management.Automation"));
var hintPath = reference.Descendants().First(m => m.Name.LocalName == "HintPath");
hintPath.Value = assemblyPath;

doc.Save(projectVersion);

try
{
Compile(projectVersion, version);
}
finally
{
File.Delete(projectVersion);
}
}

private void Compile(string project, string version)
{
string msbuild;
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"))
{
var root = key.GetValue("MSBuildToolsPath") as string;
msbuild = Path.Combine(root, "MSBuild.exe");
}

FileInfo fi = new FileInfo(project);

var p = new Process();
p.StartInfo.FileName = msbuild;
p.StartInfo.Arguments = string.Format(@" {0} /p:Configuration=Debug /t:Build /fileLogger /flp1:logfile=errors.txt;errorsonly /p:SolutionDir={1} /p:SolutionName=PowerShellEditorServices /p:DefineConstants=PowerShellv{2}", project, fi.Directory.Parent.Parent.FullName, version);
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.WaitForExit(60000);
if (!p.HasExited)
{
p.Kill();
throw new Exception("Compilation didn't complete in 60 seconds.");
}

if (p.ExitCode != 0)
{
var errors = File.ReadAllText("errors.txt");
throw new Exception(errors);
}
}

public string InstallPackage(string packageName, string packageVersion)
{
var packageDir = Path.Combine(Path.GetTempPath(), "PowerShellPackages");
packageDirectory = new DirectoryInfo(packageDir);
packageDirectory.Create();

var nuget = Path.Combine(Environment.CurrentDirectory, "NuGet.exe");
ProcessStartInfo si = new ProcessStartInfo();

var p = new Process();
p.StartInfo.FileName = nuget;
p.StartInfo.Arguments = string.Format("install {0} -o {1} -Version {2}", packageName, packageDir, packageVersion);
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.WaitForExit(10000);
if (!p.HasExited)
{
p.Kill();
throw new Exception("Failed to download PowerShell NuGet packages required for this test.");
}

var packageFolder = packageName + "." + packageVersion;

var assemblyPath = Path.Combine(packageDir, packageFolder);
return Path.Combine(assemblyPath, @"lib\net4\System.Management.Automation.dll");
}

private ScriptFile GetScriptFile(ScriptRegion scriptRegion)
{
const string baseSharedScriptPath =
Expand Down
Binary file added test/PowerShellEditorServices.Test/NuGet.exe
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="NuGet.exe">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution already has NuGet.exe in the root .nuget folder so you could probably just use that directly rather than including another one here. Might not be necessary at all if we pull in the reference assemblies as NuGet dependencies for the test project

<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
Expand Down