Skip to content

Commit

Permalink
ipmo modules correctly and don't sync while debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerLeonhardt committed Jun 26, 2019
1 parent f4fbcf1 commit 1cbeb60
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 39 deletions.
8 changes: 5 additions & 3 deletions src/PowerShellEditorServices/Language/AstOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@
using System.Threading;
using System.Threading.Tasks;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;

namespace Microsoft.PowerShell.EditorServices
{
using System.Management.Automation;
using System.Management.Automation.Language;

/// <summary>
/// Provides common operations for the syntax tree of a parsed script.
Expand Down Expand Up @@ -73,6 +71,7 @@ static public async Task<CommandCompletion> GetCompletionsAsync(

if (!RunspaceSynchronizer.IsReadyForEvents)
{
pwsh.Runspace.Name = "RunspaceSynchronizerTargetRunspace";
RunspaceSynchronizer.InitializeRunspaces(powerShellContext.CurrentRunspace.Runspace, pwsh.Runspace);
}

Expand All @@ -97,7 +96,10 @@ static public async Task<CommandCompletion> GetCompletionsAsync(

var stopwatch = new Stopwatch();

if (powerShellContext.IsPSReadLineEnabled)
// Static class members in Windows PowerShell had a thread synchronization issue.
// This issue was fixed in PowerShell 6+ so we only use the new completions if PSReadLine is enabled
// and we're running in .NET Core.
if (powerShellContext.IsPSReadLineEnabled && Utils.IsNetCore)
{
stopwatch.Start();

Expand Down
82 changes: 46 additions & 36 deletions src/PowerShellEditorServices/Language/RunspaceSynchronizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.Reflection;
using Microsoft.PowerShell.Commands;

namespace Microsoft.PowerShell.EditorServices
{
Expand All @@ -23,6 +25,7 @@ namespace Microsoft.PowerShell.EditorServices
/// </summary>
public class RunspaceSynchronizer
{
private static readonly Version versionZero = new Version(0, 0);
// Determines whether the HandleRunspaceStateChange event should attempt to sync the runspaces.
private static bool SourceActionEnabled = false;

Expand Down Expand Up @@ -71,9 +74,9 @@ public class RunspaceSynchronizer
public static void InitializeRunspaces(Runspace runspaceSource, Runspace runspaceTarget)
{
sourceRunspace = runspaceSource;
sourceEngineIntrinsics = ReflectionUtils.GetEngineIntrinsics(sourceRunspace);
sourceEngineIntrinsics = sourceRunspace.GetEngineIntrinsics();
targetRunspace = runspaceTarget;
targetEngineIntrinsics = ReflectionUtils.GetEngineIntrinsics(runspaceTarget);
targetEngineIntrinsics = runspaceTarget.GetEngineIntrinsics();
IsReadyForEvents = true;

sourceEngineIntrinsics.Events.SubscribeEvent(
Expand Down Expand Up @@ -104,15 +107,15 @@ public static void Activate()

private static void HandleRunspaceStateChange(object sender, PSEventArgs args)
{
if (!SourceActionEnabled)
if (!SourceActionEnabled || sourceRunspace.Debugger.IsActive)
{
return;
}

SourceActionEnabled = false;

var newOrChangedModules = new List<PSModuleInfo>();
List<PSModuleInfo> modules = ReflectionUtils.GetModules(sourceRunspace);
List<PSModuleInfo> modules = sourceRunspace.GetModules();
foreach (PSModuleInfo module in modules)
{
if (moduleCache.Add(module))
Expand Down Expand Up @@ -150,8 +153,16 @@ private static void HandleRunspaceStateChange(object sender, PSEventArgs args)
{
if(moduleInfo.Path != null)
{
string nameParameterValue = moduleInfo.Path;
// If the version is greater than zero, the module info was probably imported by the psd1 or module base.
// If so, we can just import from the module base which is the root of the module folder.
if (moduleInfo.Version > versionZero)
{
nameParameterValue = moduleInfo.ModuleBase;
}

pwsh.AddCommand("Import-Module")
.AddParameter("Name", moduleInfo.Path)
.AddParameter("Name", nameParameterValue)
.AddParameter("Force")
.AddStatement();
}
Expand All @@ -172,40 +183,39 @@ private static void HandleRunspaceStateChange(object sender, PSEventArgs args)
}

#endregion
}

// A collection of helper methods that use Reflection in some form.
private class ReflectionUtils
{
private static BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default;
internal static class RunspaceExtensions
{
private static BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default;

// Gets the modules loaded in a runspace.
// This exists in runspace.ExecutionContext.Modules.GetModule(string[] patterns, bool all)
internal static List<PSModuleInfo> GetModules(Runspace runspace)
{
var executionContext = typeof(Runspace)
.GetProperty("ExecutionContext", bindingFlags)
.GetValue(runspace);
var ModuleIntrinsics = executionContext.GetType()
.GetProperty("Modules", bindingFlags)
.GetValue(executionContext);
var modules = ModuleIntrinsics.GetType()
.GetMethod("GetModules", bindingFlags, null, new Type[] { typeof(string[]), typeof(bool) }, null)
.Invoke(ModuleIntrinsics, new object[] { new string[] { "*" }, false }) as List<PSModuleInfo>;
return modules;
}
// Gets the modules loaded in a runspace.
// This exists in runspace.ExecutionContext.Modules.GetModule(string[] patterns, bool all)
internal static List<PSModuleInfo> GetModules(this Runspace runspace)
{
var executionContext = typeof(Runspace)
.GetProperty("ExecutionContext", bindingFlags)
.GetValue(runspace);
var ModuleIntrinsics = executionContext.GetType()
.GetProperty("Modules", bindingFlags)
.GetValue(executionContext);
var modules = ModuleIntrinsics.GetType()
.GetMethod("GetModules", bindingFlags, null, new Type[] { typeof(string[]), typeof(bool) }, null)
.Invoke(ModuleIntrinsics, new object[] { new string[] { "*" }, false }) as List<PSModuleInfo>;
return modules;
}

// Gets the engine intrinsics object on a Runspace.
// This exists in runspace.ExecutionContext.EngineIntrinsics.
internal static EngineIntrinsics GetEngineIntrinsics(Runspace runspace)
{
var executionContext = typeof(Runspace)
.GetProperty("ExecutionContext", bindingFlags)
.GetValue(runspace);
var engineIntrinsics = executionContext.GetType()
.GetProperty("EngineIntrinsics", bindingFlags)
.GetValue(executionContext) as EngineIntrinsics;
return engineIntrinsics;
}
// Gets the engine intrinsics object on a Runspace.
// This exists in runspace.ExecutionContext.EngineIntrinsics.
internal static EngineIntrinsics GetEngineIntrinsics(this Runspace runspace)
{
var executionContext = typeof(Runspace)
.GetProperty("ExecutionContext", bindingFlags)
.GetValue(runspace);
var engineIntrinsics = executionContext.GetType()
.GetProperty("EngineIntrinsics", bindingFlags)
.GetValue(executionContext) as EngineIntrinsics;
return engineIntrinsics;
}
}

Expand Down

0 comments on commit 1cbeb60

Please sign in to comment.