Skip to content
Closed
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
4 changes: 2 additions & 2 deletions PowerShellEditorServices.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ task Clean BinClean,{
exec { & $script:dotnetExe clean }
Get-ChildItem -Recurse $PSScriptRoot\src\*.nupkg | Remove-Item -Force -ErrorAction Ignore
Get-ChildItem $PSScriptRoot\PowerShellEditorServices*.zip | Remove-Item -Force -ErrorAction Ignore
Get-ChildItem $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US\*-help.xml | Remove-Item -Force -ErrorAction Ignore
Get-ChildItem $PSScriptRoot\module\PowerShellEditorServices.Commands\en-US\*-help.xml | Remove-Item -Force -ErrorAction Ignore

# Remove bundled component modules
$moduleJsonPath = "$PSScriptRoot\modules.json"
Expand Down Expand Up @@ -406,7 +406,7 @@ task RestorePsesModules -After Build {
}

task BuildCmdletHelp {
New-ExternalHelp -Path $PSScriptRoot\module\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices\Commands\en-US -Force
New-ExternalHelp -Path $PSScriptRoot\module\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices.Commands\en-US -Force
New-ExternalHelp -Path $PSScriptRoot\module\PowerShellEditorServices.VSCode\docs -OutputPath $PSScriptRoot\module\PowerShellEditorServices.VSCode\en-US -Force
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
</MemberSet>
</Members>
</Type>
</Types>
</Types>
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ private delegate string ReadLineInvoker(
Type type = Type.GetType("Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2");
MethodInfo method = type?.GetMethod(
"ReadLine",
new[] { typeof(Runspace), typeof(EngineIntrinsics), typeof(CancellationToken) });
new [] { typeof(Runspace), typeof(EngineIntrinsics), typeof(CancellationToken) });

// TODO: Handle method being null here. This shouldn't ever happen.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ internal class PowerShellContextService : IHostSupportsInteractiveSession

private static string s_commandsModulePath => Path.GetFullPath(Path.Combine(
s_bundledModulePath,
"PowerShellEditorServices",
"Commands",
"PowerShellEditorServices.Commands.psd1"));
"PowerShellEditorServices.Commands"));

private static string s_psReadLineModulePath => Path.GetFullPath(Path.Combine(
s_bundledModulePath,
"PSReadLine"));

private static readonly Action<Runspace, ApartmentState> s_runspaceApartmentStateSetter;
private static readonly PropertyInfo s_writeStreamProperty;
Expand Down Expand Up @@ -190,7 +192,6 @@ public PowerShellContextService(
RunspaceChanged += PowerShellContext_RunspaceChangedAsync;
ExecutionStatusChanged += PowerShellContext_ExecutionStatusChangedAsync;
}

[SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Checked by Validate call")]
public static PowerShellContextService Create(
ILoggerFactory factory,
Expand All @@ -201,13 +202,6 @@ public static PowerShellContextService Create(

Validate.IsNotNull(nameof(hostStartupInfo), hostStartupInfo);

// Respect a user provided bundled module path.
if (Directory.Exists(hostStartupInfo.BundledModulePath))
{
logger.LogTrace($"Using new bundled module path: {hostStartupInfo.BundledModulePath}");
s_bundledModulePath = hostStartupInfo.BundledModulePath;
}

bool shouldUsePSReadLine = hostStartupInfo.ConsoleReplEnabled
&& !hostStartupInfo.UsesLegacyReadLine;

Expand All @@ -218,7 +212,7 @@ public static PowerShellContextService Create(

EditorServicesPSHostUserInterface hostUserInterface =
hostStartupInfo.ConsoleReplEnabled
? (EditorServicesPSHostUserInterface) new TerminalPSHostUserInterface(powerShellContext, hostStartupInfo.PSHost, logger)
? (EditorServicesPSHostUserInterface)new TerminalPSHostUserInterface(powerShellContext, hostStartupInfo.PSHost, logger)
: new ProtocolPSHostUserInterface(languageServer, powerShellContext, logger);

EditorServicesPSHost psHost =
Expand All @@ -230,26 +224,7 @@ public static PowerShellContextService Create(

logger.LogTrace("Creating initial PowerShell runspace");
Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost, hostStartupInfo.InitialSessionState);
powerShellContext.Initialize(hostStartupInfo.ProfilePaths, initialRunspace, true, hostUserInterface);
powerShellContext.ImportCommandsModuleAsync();

// TODO: This can be moved to the point after the $psEditor object
// gets initialized when that is done earlier than LanguageServer.Initialize
foreach (string module in hostStartupInfo.AdditionalModules)
{
var command =
new PSCommand()
.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
.AddParameter("Name", module);

#pragma warning disable CS4014
// This call queues the loading on the pipeline thread, so no need to await
powerShellContext.ExecuteCommandAsync<PSObject>(
command,
sendOutputToHost: false,
sendErrorToHost: true);
#pragma warning restore CS4014
}
powerShellContext.Initialize(hostStartupInfo, initialRunspace, true, hostUserInterface);

return powerShellContext;
}
Expand Down Expand Up @@ -311,7 +286,7 @@ public static Runspace CreateRunspace(PSHost psHost, InitialSessionState initial
/// <param name="ownsInitialRunspace">If true, the PowerShellContext owns this runspace.</param>
/// <param name="consoleHost">An IHostOutput implementation. Optional.</param>
public void Initialize(
ProfilePathInfo profilePaths,
HostStartupInfo hostStartupInfo,
Runspace initialRunspace,
bool ownsInitialRunspace,
IHostOutput consoleHost)
Expand Down Expand Up @@ -342,7 +317,12 @@ public void Initialize(
RunspaceContext.Original,
connectionString: null);
this.CurrentRunspace = this.initialRunspace;

// Respect a user provided bundled module path.
if (Directory.Exists(hostStartupInfo.BundledModulePath))
{
logger.LogTrace($"Using new bundled module path: {hostStartupInfo.BundledModulePath}");
s_bundledModulePath = hostStartupInfo.BundledModulePath;
}
// Write out the PowerShell version for tracking purposes
this.logger.LogInformation($"PowerShell Version: {this.LocalPowerShellVersion.Version}, Edition: {this.LocalPowerShellVersion.Edition}");

Expand All @@ -365,7 +345,7 @@ public void Initialize(
this.ConfigureRunspaceCapabilities(this.CurrentRunspace);

// Set the $profile variable in the runspace
this.profilePaths = profilePaths;
this.profilePaths = hostStartupInfo.ProfilePaths;
if (profilePaths != null)
{
this.SetProfileVariableInCurrentRunspace(profilePaths);
Expand Down Expand Up @@ -400,9 +380,23 @@ public void Initialize(
this.versionSpecificOperations);
this.InvocationEventQueue = InvocationEventQueue.Create(this, this.PromptNest);

// Import the PowerShellEditorServices.Commands module into the runspace.
this.ImportModule(s_commandsModulePath);
if (isPSReadLineEnabled)
{
// Imports the PSReadLine2 module into the runspace.
ImportModule(s_psReadLineModulePath);
}
// TODO: This can be moved to the point after the $psEditor object
// gets initialized when that is done earlier than LanguageServer.Initialize
foreach (string module in hostStartupInfo.AdditionalModules)
{
this.ImportModule(module);
}

if (powerShellVersion.Major >= 5 &&
this.isPSReadLineEnabled &&
PSReadLinePromptContext.TryGetPSReadLineProxy(logger, initialRunspace, s_bundledModulePath, out PSReadLineProxy proxy))
PSReadLinePromptContext.TryGetPSReadLineProxy(logger, out PSReadLineProxy proxy))
{
this.PromptContext = new PSReadLinePromptContext(
this,
Expand All @@ -414,26 +408,20 @@ public void Initialize(
{
this.PromptContext = new LegacyReadLineContext(this);
}

// Finally, restore the runspace's execution policy to the user's policy instead of
// Bypass.
this.RestoreExecutionPolicy();
}

/// <summary>
/// Imports the PowerShellEditorServices.Commands module into
/// the runspace. This method will be moved somewhere else soon.
/// </summary>
/// <returns></returns>
public Task ImportCommandsModuleAsync()
public void ImportModule(string path)
{
this.logger.LogTrace($"Importing PowershellEditorServices commands from {s_commandsModulePath}");
this.logger.LogTrace($"Importing module {path}");

PSCommand importCommand = new PSCommand()
.AddCommand("Import-Module")
.AddArgument(s_commandsModulePath);
.AddArgument(path);

return this.ExecuteCommandAsync<PSObject>(importCommand, sendOutputToHost: false, sendErrorToHost: false);
this.ExecuteCommandAsync<PSObject>(importCommand, sendOutputToHost: false, sendErrorToHost: false).ConfigureAwait(false).GetAwaiter().GetResult();
}

private static bool CheckIfRunspaceNeedsEventHandlers(RunspaceDetails runspaceDetails)
Expand Down Expand Up @@ -592,7 +580,7 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
// cancelled prompt when it's called again.
if (executionOptions.AddToHistory)
{
this.PromptContext.AddToHistory(executionOptions.InputString ?? psCommand.Commands[0].CommandText);
this.PromptContext.AddToHistory(executionOptions.InputString ?? psCommand.Commands [0].CommandText);
}

bool hadErrors = false;
Expand Down Expand Up @@ -646,7 +634,7 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
// Instruct PowerShell to send output and errors to the host
if (executionOptions.WriteOutputToHost)
{
psCommand.Commands[0].MergeMyResults(
psCommand.Commands [0].MergeMyResults(
PipelineResultTypes.Error,
PipelineResultTypes.Output);

Expand Down Expand Up @@ -681,7 +669,7 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
if (executionOptions.WriteInputToHost)
{
this.WriteOutput(
executionOptions.InputString ?? psCommand.Commands[0].CommandText,
executionOptions.InputString ?? psCommand.Commands [0].CommandText,
includeNewLine: true);
}

Expand Down Expand Up @@ -994,7 +982,7 @@ public Task<IEnumerable<object>> ExecuteScriptStringAsync(
Validate.IsNotNull(nameof(scriptString), scriptString);

PSCommand command = null;
if(CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
if (CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
{
try
{
Expand All @@ -1009,7 +997,7 @@ public Task<IEnumerable<object>> ExecuteScriptStringAsync(
}

// fall back to old behavior
if(command == null)
if (command == null)
{
command = new PSCommand().AddScript(scriptString.Trim());
}
Expand Down Expand Up @@ -1445,7 +1433,7 @@ private ExecutionTarget GetExecutionTarget(ExecutionOptions options = null)

// We can't take the pipeline from PSReadLine if it's in a remote session, so we need to
// invoke locally in that case.
if (IsDebuggerStopped && PromptNest.IsInDebugger && !(options.IsReadLine && PromptNest.IsRemote))
if (PromptNest.IsInDebugger && IsDebuggerStopped && !(options.IsReadLine && PromptNest.IsRemote))
{
return ExecutionTarget.Debugger;
}
Expand Down Expand Up @@ -1709,7 +1697,7 @@ internal static string WildcardEscapePath(string path, bool escapeSpaces = false
var sb = new StringBuilder();
for (int i = 0; i < path.Length; i++)
{
char curr = path[i];
char curr = path [i];
switch (curr)
{
// Escape '[', ']', '?' and '*' with '`'
Expand Down Expand Up @@ -1763,11 +1751,11 @@ internal static string UnescapeWildcardEscapedPath(string wildcardEscapedPath)
for (int i = 0; i < wildcardEscapedPath.Length; i++)
{
// If we see a backtick perform a lookahead
char curr = wildcardEscapedPath[i];
char curr = wildcardEscapedPath [i];
if (curr == '`' && i + 1 < wildcardEscapedPath.Length)
{
// If the next char is an escapable one, don't add this backtick to the new string
char next = wildcardEscapedPath[i + 1];
char next = wildcardEscapedPath [i + 1];
switch (next)
{
case '[':
Expand Down Expand Up @@ -2169,14 +2157,14 @@ internal void RestoreExecutionPolicy()
// set to expected values, so we must sift through those.

ExecutionPolicy policyToSet = ExecutionPolicy.Bypass;
var currentUserPolicy = (ExecutionPolicy)policies[policies.Count - 2].Members["ExecutionPolicy"].Value;
var currentUserPolicy = (ExecutionPolicy)policies [policies.Count - 2].Members ["ExecutionPolicy"].Value;
if (currentUserPolicy != ExecutionPolicy.Undefined)
{
policyToSet = currentUserPolicy;
}
else
{
var localMachinePolicy = (ExecutionPolicy)policies[policies.Count - 1].Members["ExecutionPolicy"].Value;
var localMachinePolicy = (ExecutionPolicy)policies [policies.Count - 1].Members ["ExecutionPolicy"].Value;
if (localMachinePolicy != ExecutionPolicy.Undefined)
{
policyToSet = localMachinePolicy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,59 +64,46 @@ internal PSReadLinePromptContext(

internal static bool TryGetPSReadLineProxy(
ILogger logger,
Runspace runspace,
string bundledModulePath,
out PSReadLineProxy readLineProxy)
{
readLineProxy = null;
logger.LogTrace("Attempting to load PSReadLine");
using (var pwsh = PowerShell.Create())
{
pwsh.Runspace = runspace;
pwsh.AddCommand("Microsoft.PowerShell.Core\\Import-Module")
.AddParameter("Name", Path.Combine(bundledModulePath, "PSReadLine"))
.Invoke();

if (pwsh.HadErrors)
{
logger.LogWarning("PSConsoleReadline type not found: {Reason}", pwsh.Streams.Error[0].ToString());
return false;
}

var psReadLineType = Type.GetType("Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2");
var psReadLineType = Type.GetType("Microsoft.PowerShell.PSConsoleReadLine, Microsoft.PowerShell.PSReadLine2");

if (psReadLineType == null)
{
// NOTE: For some reason `Type.GetType(...)` can fail to find the type,
// and in that case, this search through the `AppDomain` for some reason will succeed.
// It's slower, but only happens when needed.
logger.LogTrace("PSConsoleReadline type not found using Type.GetType(), searching all loaded assemblies...");
psReadLineType = AppDomain.CurrentDomain
.GetAssemblies()
.FirstOrDefault(asm => asm.GetName().Name.Equals("Microsoft.PowerShell.PSReadLine2"))
?.ExportedTypes
?.FirstOrDefault(type => type.FullName.Equals("Microsoft.PowerShell.PSConsoleReadLine"));
if (psReadLineType == null)
{
// NOTE: For some reason `Type.GetType(...)` can fail to find the type,
// and in that case, this search through the `AppDomain` for some reason will succeed.
// It's slower, but only happens when needed.
logger.LogTrace("PSConsoleReadline type not found using Type.GetType(), searching all loaded assemblies...");
psReadLineType = AppDomain.CurrentDomain
.GetAssemblies()
.FirstOrDefault(asm => asm.GetName().Name.Equals("Microsoft.PowerShell.PSReadLine2"))
?.ExportedTypes
?.FirstOrDefault(type => type.FullName.Equals("Microsoft.PowerShell.PSConsoleReadLine"));
if (psReadLineType == null)
{
logger.LogWarning("PSConsoleReadLine type not found anywhere!");
return false;
}
}

try
{
readLineProxy = new PSReadLineProxy(psReadLineType, logger);
}
catch (InvalidOperationException e)
{
// The Type we got back from PowerShell doesn't have the members we expected.
// Could be an older version, a custom build, or something a newer version with
// breaking changes.
logger.LogWarning("PSReadLineProxy unable to be initialized: {Reason}", e);
logger.LogWarning("PSConsoleReadLine type not found anywhere!");
return false;
}
}

try
{
readLineProxy = new PSReadLineProxy(psReadLineType, logger);
}
catch (InvalidOperationException e)
{
// The Type we got back from PowerShell doesn't have the members we expected.
// Could be an older version, a custom build, or something a newer version with
// breaking changes.
logger.LogWarning("PSReadLineProxy unable to be initialized: {Reason}", e);
return false;
}
catch (CommandNotFoundException e)
{
logger.LogWarning("PSReadLineProxy unable to be initialized: {Reason}", e);
return false;
}
return true;
}

Expand Down Expand Up @@ -164,7 +151,8 @@ public void AbortReadLine()
WaitForReadLineExit();
}

public async Task AbortReadLineAsync() {
public async Task AbortReadLineAsync()
{
if (_readLineCancellationSource == null)
{
return;
Expand All @@ -181,7 +169,8 @@ public void WaitForReadLineExit()
{ }
}

public async Task WaitForReadLineExitAsync() {
public async Task WaitForReadLineExitAsync()
{
using (await _promptNest.GetRunspaceHandleAsync(isReadLine: true, CancellationToken.None).ConfigureAwait(false))
{ }
}
Expand Down
Loading