Tracing HostVersion parameter to Start-EditorServices
This is my trace of the HostVersion parameter. The purpose of this log is to identify all uses for the HostVersion parameter. These uses will be documented in my unofficial documentation on the Start-EditorServices command
- Unofficial Docs - Syntax
[-HostVersion < string, mandatory, ="" not="" null="" or="" empty="" >]
- PSES/module/PSES/Start-EditorServices.ps1
[Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $HostVersion
- PSES/module/PSES/Start-EditorServices.ps1
Import-Module -Name "$PSScriptRoot/PowerShellEditorServices.psd1" Start-EditorServices @PSBoundParameters
- PSES/module/PSES/PowerShellEditorServices.psd1
# Script module or binary module file associated with this manifest. RootModule = if ($PSEdition -eq 'Core') { 'bin/Core/Microsoft.PowerShell.EditorServices.Hosting.dll' } else { 'bin/Desktop/Microsoft.PowerShell.EditorServices.Hosting.dll' }
- I believe this is a call to the Microsoft.PowerShell.EditorServices.Hosting namespace - still uncertain about this.
- PSES/src/PSES.Hosting/Commands/StartEditorServicesCommand.cs
/// <summary> /// The Start-EditorServices command, the conventional entrypoint for PowerShell Editor Services. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Cmdlet parameters can be arrays")] [Cmdlet(VerbsLifecycle.Start, "EditorServices", DefaultParameterSetName = "NamedPipe")] public sealed class StartEditorServicesCommand : PSCmdlet { ... }
- The parameter from the NamedPipe parameter set:
/// <summary> /// The version to report for the host. /// </summary> [Parameter(Mandatory = true)] [ValidateNotNullOrEmpty] public Version HostVersion { get; set; }
- The only use for HostVersion in the entire CMDLet - hopefully this will be an easy argument trace :D
HostInfo hostInfo = new(HostName, HostProfileId, HostVersion);
- HostInfo is just a data structure that stores strings and semvers - no need to trace into this. will need to trace the hostinfo object tho :(
Tracing HostInfo/EditorServicesConfig
From my previous experiences with these objects, this is gonna be a long trace :(
- PSES/src/PSES.Hosting/Commands/StartEditorServicesCommand.cs (CreateConfigObject Private Method)
- The only use of HostVersion from above is in the private method CreateConfigObject from the StartEditorServicesCommand (The C# object representing the Start-EditorServices Cmdlet)
- This is a longer function, so I won't list the source code here, but I'll summarize it:
- Logs diagnostic message "Creating host configuration"
- Expands BundledModulesPath (cmdlet parameter) if it is a relative path
- Gets the $profile object from PowerShell
- Creates the HostInfo object
- Pulls in some kind of session state stuff from the runspace object - I have no idea what this is or if it is even useful :/
- Instantiates an EditorServicesConfig object with
- the HostInfo object,
- the PSHost object from the PWSH runtime the CMDlet will run in
- the SessionDetailsPath cmdlet parameter
- the Expanded BundledModulesPath path-string
- the LogPath cmdlet parameter
- Sets these properties on the new EditorServicesConfig object:
- FeatureFlags with the FeatureFlags cmdlet parameter
- LogLevel with the LogLevel cmdlet parameter
- ConsoleRepl with GetReplKind private method
- REPL is short for Read-Eval-Print-Loop
- This is the function that determines which readline Powershell is using by pulling from an Enum<out int>
- The 3 options are:
- None - no interactive console
- LeagacyReadLine - the readline implementation before MicroSoft ported in the PSReadLine repository
- PSReadLine - the latest readline implementation
- AdditionalModules with the AdditionalModules cmdlet parameter
- Service Transport Configs with:
- these private methods:
- GetLanguageServiceTransport
- GetDebugServiceTransport
- these return a config representing the type of pipe used for each service (named-pipe/split-named-pipes/stdio-pipe)
- these private methods:
- InitialSessionState with the session state stuff from earlier
- ProfilePaths struct (ProfilePathConfig enum) with the $profile object from earlier - note for devs: the enums and the internal methods do nothing, but bloat the code.
- StartupBanner with the StartupBanner cmdlet parameter
- default can be found here - I can use this later when I trace the StartupBanner object. Actually, any mention of "cmdlet parameter" in this log can be used for tracing.
- Returns the EditorServicesConfig object
- This object is effectively just a struct that stores the above values. It does not have any method members
- PSES/src/PSES.Hosting/Commands/StartEditorServicesCommand.cs (EndProcessing Protected Method)
- This is an override to Cmdlet.EndProcessing Method
- The call to CreateConfigObject()
EditorServicesConfig editorServicesConfig = CreateConfigObject();
- Pass to EditorServicesLoader.Create() - only use for editorServicesConfig
using EditorServicesLoader psesLoader = EditorServicesLoader.Create( _logger, editorServicesConfig, sessionFileWriter, _loggerUnsubscribers );
- Only call to the psesLoader object
// Synchronously start editor services and wait here until it shuts down. #pragma warning disable VSTHRD002 psesLoader.LoadAndRunEditorServicesAsync().GetAwaiter().GetResult(); #pragma warning restore VSTHRD002
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (Create() - public static method)
- editorServicesConfig from above gets passed in as hostConfig
- this function is just a named constructor. The actual constructor does nothing except assign it's parameters in the private variable stack
- this function just loads in some additional c# assemblies then returns the new EditorServicesConfig object
- note: the stored EditorServicesConfig (_hostConfig) is stored in the "private" stack
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (List of all uses of _hostConfig in this class)
- used within:
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (UpdatePSModulePath - public method)
- This private method is relatively short:
private void UpdatePSModulePath() { if (string.IsNullOrEmpty(_hostConfig.BundledModulePath)) { _logger.Log(PsesLogLevel.Diagnostic, "BundledModulePath not set, skipping"); return; } string psModulePath = Environment.GetEnvironmentVariable("PSModulePath").TrimEnd(Path.PathSeparator); if ($"{psModulePath}{Path.PathSeparator}".Contains($"{_hostConfig.BundledModulePath}{Path.PathSeparator}")) { _logger.Log(PsesLogLevel.Diagnostic, "BundledModulePath already set, skipping"); return; } psModulePath = $ "{psModulePath}{Path.PathSeparator}{_hostConfig.BundledModulePath}"; Environment.SetEnvironmentVariable("PSModulePath", psModulePath); _logger.Log(PsesLogLevel.Verbose, $ "Updated PSModulePath to: '{psModulePath}'"); }
- This doesn't manipulate HostInfo.Version at all, but it does show that BundledModulePath is a sub path of $env:PSModulePath, specifically "${$env:PSModulePath}\${$HostVersion}"
- This private method is relatively short:
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (LogHostInformation - private method)
- This method is lengthy, but doesn't seem to manipulate HostVersion
- Simply dumps HostVersion to the logger.
- It is important to note that Host Version and PowerShell Version are 2 separate log entries here
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (ValidateConfiguration - private method)
- This method is also short
private void ValidateConfiguration() { _logger.Log(PsesLogLevel.Diagnostic, "Validating configuration"); bool lspUsesStdio = _hostConfig.LanguageServiceTransport is StdioTransportConfig; bool debugUsesStdio = _hostConfig.DebugServiceTransport is StdioTransportConfig; // Ensure LSP and Debug are not both Stdio if (lspUsesStdio && debugUsesStdio) { throw new ArgumentException("LSP and Debug transports cannot both use Stdio"); } if (_hostConfig.ConsoleRepl != ConsoleReplKind.None && (lspUsesStdio || debugUsesStdio)) { throw new ArgumentException("Cannot use the REPL with a Stdio protocol transport"); } if (_hostConfig.PSHost == null) { throw new ArgumentNullException(nameof(_hostConfig.PSHost)); } }
- No manipulations to HostVersion
- This method is also short
- LoadAndRunEditorServicesAsync - public task
- This is task that gets called with the "psesLoader.LoadAndRunEditorServicesAsync" function
public Task LoadAndRunEditorServicesAsync() { LogHostInformation(); #if!CoreCLR // Make sure the .NET Framework version supports .NET Standard 2.0 CheckNetFxVersion(); #endif UpdatePSModulePath(); ValidateConfiguration(); // Method with no implementation that forces the PSES assembly to load, triggering an AssemblyResolve event _logger.Log(PsesLogLevel.Verbose, "Loading PowerShell Editor Services"); LoadEditorServices(); _logger.Log(PsesLogLevel.Verbose, "Starting EditorServices"); _editorServicesRunner = new EditorServicesRunner(_logger, _hostConfig, _sessionFileWriter, _loggersToUnsubscribe); // The trigger method for Editor Services return Task.Run(_editorServicesRunner.RunUntilShutdown); }
- The only significant calls here are the construction of the EditorServicesRunner object and the Task.Run(_editorServicesRunner.RunUntilShutdown)
- This is task that gets called with the "psesLoader.LoadAndRunEditorServicesAsync" function
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (EditorServicesRunner - internal class constructor)
- Regular setter-constructor (private variables are prefixed with "_"
- Signature
public EditorServicesRunner( HostLogger logger, EditorServicesConfig config, ISessionFileWriter sessionFileWriter, IReadOnlyCollection<IDisposable> loggersToUnsubscribe)
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (RunUntilShutdown - public task)
- wrapper for CreateEditorServicesAndRunUntilShutdown (private async task)
- Makes a few log entries to logger, and writes the "session started" state to the session.json file
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (CreateEditorServicesAndRunUntilShutdown - private async task)
- This is the C# Task factory wrapped by RunUntilShutdown
- It is lengthy, so I will break it up:
- determines which servers it will be creating (debug, server, or both):
bool creatingLanguageServer = _config.LanguageServiceTransport != null; bool creatingDebugServer = _config.DebugServiceTransport != null; bool isTempDebugSession = creatingDebugServer && !creatingLanguageServer;
- !!! - creates a HostStartupInfo object - this object is a wrapper for HostInfo, which wraps the HostVersion variable
- If it is only running a debug server, run it then return out of the task
- Unsubs from the host logger before the session's server is run
- Writes the startup banner - but only if there is a Read-Eval-Print Loop running
- Creates a PsesLanguageServer using the CreateLanguageServerAsync method (declaration)
- !!! - the HostStartupInfo object from above is passed to this method
- this task is halted until the language server pipe is connected to. This halts the entire CreateEditorServicesAndRunUntilShutdown Task
- Serves as a wrapper for EditorServicesServerFactory.CreateLanguageServer()
- !!! - Only significant calls to this new object:
- Creates the debug server and passes the PsesLanguageServer to it's constructor
- Awaits language server shutdown
- Resubs the host logger
- determines which servers it will be creating (debug, server, or both):
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (CreateHostStartupInfo - private method)
- This is the factory used to create the HostStartupInfo from 11.b.ii of this log (link is to the source code)
- This is just a wrapper for the HostStartupInfo constructor in order to for some reason pointlessly clone _config.ProfilePaths??
- HostStartupInfo's constructor for the most part is just a setter-constructor (Parameter -> UpperCamelCase Properties)
- Only deviation is that it again adds parsing to the bundledmodulepath variable. - Why do we keep doing this?? We can't just do this in one spot guys?
- This is the factory used to create the HostStartupInfo from 11.b.ii of this log (link is to the source code)
- PSES/src/PSES.Hosting/EditorServicesLoader.cs (CreateLanguageServerAsync - private async method)
see 11.b.vi
- PSES/src/PSES/Hosting/EditorServicesServerFactory (CreateLanguageServer - public method)
- see 11.b.vi.iii
- wraps PSESLanguageServer constructor
- PSES/src/PSES/Server/PsesLanguageServer.cs (PsesLanguageServer - public constructor)
- see above (14.b)
- just a setter-constructor
- PSES/src/PSES/Server/PsesLanguageServer.cs (StartAsync - public async task)
- see 11.b.vi.iv.i
- wraps OmniSharp LanguageServer.From constructor
- only usage of the HostStartupInfo object is in the AddPsesLanguageServices call
- PSES/src/PSES/Server/PsesLanguageServer.cs (WaitForShutdown - public task)
- see 11.b.vi.iv.ii
- wraps Omnisharp LanguageServer.WaitForExit task and shutsdown _psesHost (this is created as a service in the From constructor)
- PSES/src/PSES/Server/PsesServerCollectionExtensions.cs (AddPsesLanguageServices - extension method for IServiceCollection)
- Just adds the object as a service. According to the search below, there is no further reference to this object's version parameters.
- Services check (this should be through PsesLanguageServer.LanguageServer.Services)
- according to this search, the only instance of PsesLanguageServer is the ones I've already documented.
- EditorServicesServerFactory.cs has 1 ref to LanguageServer.Services, and this is to create the debug server
- this is also the only instance of it in EditorServicesRunner.cs
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.