Skip to content

CodeCompletion exposes cmdlets not available to the parent runspace #1281

Closed
@dkattan

Description

@dkattan

Summary

When calling PSES programmatically (from C# but using the supported script) in a ConstrainedRunspace in ConstrainedLanguage mode, information about all cmdlets and the underlying filesystem is exposed via CodeCompletion.

Steps to Reproduce

StartLanguageServerViaScript()
    {
      InitialSessionState iss = InitialSessionState.Create();
      iss.LanguageMode = PSLanguageMode.ConstrainedLanguage;
      iss.ImportPSModule("Microsoft.Powershell.Utility", "Microsoft.Powershell.Core", "Microsoft.PowerShell.Security");
      iss.Commands.Add(new SessionStateCmdletEntry("Get-Command", typeof(GetCommandCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Where-Object", typeof(WhereObjectCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Select-Object", typeof(SelectObjectCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Format-List", typeof(FormatListCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Format-Table", typeof(FormatTableCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Set-ExecutionPolicy", typeof(SetExecutionPolicyCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Format-Hex", typeof(FormatHex), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), null));
      iss.Commands.Add(new SessionStateCmdletEntry("Start-EditorServices", typeof(PSCommand), null));

      iss.Commands.Add(new SessionStateFunctionEntry("TabExpansion2", PowershellCommands.TabExpansion));
iss.ImportPSModule(_modulePath);
      Runspace myRunSpace = RunspaceFactory.CreateRunspace(_host, iss);
      myRunSpace.Open();
      PowerShell powershell = PowerShell.Create();
      powershell.Runspace = myRunSpace;
      powershell.AddCommand("Set-ExecutionPolicy")
                   .AddParameter("-ExecutionPolicy", "Bypass")
                   .AddParameter("-Scope", "Process")
                   .Invoke();
      PSCommand[] profileCommands = PSHostUtilities.GetProfileCommands("Microsoft.PowerShell");
      foreach (PSCommand command in profileCommands)
      {
        powershell.Commands = command;
        powershell.Invoke();
      }
      powershell.AddScript(PowershellCommands.TabExpansion);
      powershell.Runspace.SessionStateProxy.InvokeCommand.GetCommand("TabExpansion2", CommandTypes.Function);
      
      var scriptInvocation = powershell.Runspace.SessionStateProxy.InvokeCommand
                    .GetCommand(_StartEditorServicesScriptPath, CommandTypes.ExternalScript);
powershell.AddCommand(scriptInvocation)
      //"-FeatureFlags", "@()"
      .AddParameter("HostName", "monaco")
      .AddParameter("HostProfileId", "0")
      .AddParameter("HostVersion", "1.0.0")
      .AddParameter("LogPath", _logPath)
      .AddParameter("LogLevel", "Diagnostic")
      .AddParameter("BundledModulesPath", _bundledModulesPath)
      .AddParameter("SessionDetailsPath", _sessionInfoFilePath)
      .AddParameter("EnableConsoleRepl");
      
      try
      {
        foreach (var command in powershell.Commands.Commands)
       {
command.MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
          command.MergeMyResults(PipelineResultTypes.Information, PipelineResultTypes.Output);
        }
        _psInvocationResult = powershell.BeginInvoke();
}
      catch (Exception ex)
      {
Console.WriteLine(ex.Message);
      }
}

Expected Behavior

CodeCompletion should only return information about the specific set of modules I loaded into my runspace via InitialSessionState

What actually happens

All available modules are available via CodeCompletion and my custom (and likely unnecessary) TabExpansion2 function never gets called.

Possible Fixes

Instead of always creating a new runspace in PowerShellContext.Create() use the existing one.

Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost, hostStartupInfo.LanguageMode);

Create a new runspace but allow the user to specify a custom initialsessionstate. Perhaps do this instead of the recent change that simply passes in LanguageMode as this would give the user more granular control of the runspace. This would naturally require Start-EditorServices to be modified to accept an initiationsessionstate object.

I feel pretty confident I can implement the required changes, I’d like some feedback as to which option is most feasible or if there are perhaps intentional reasons a new runspace is being created.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions