diff --git a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs index 4589b9a44..b2b75cfbc 100644 --- a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs +++ b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs @@ -351,6 +351,7 @@ private EditorServicesConfig CreateConfigObject() FeatureFlags = FeatureFlags, LogLevel = LogLevel, ConsoleRepl = GetReplKind(), + UseNullPSHostUI = Stdio, // If Stdio is used we can't write anything else out AdditionalModules = AdditionalModules, LanguageServiceTransport = GetLanguageServiceTransport(), DebugServiceTransport = GetDebugServiceTransport(), diff --git a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs index 3ec99db46..b2e683a2f 100644 --- a/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs +++ b/src/PowerShellEditorServices.Hosting/Configuration/EditorServicesConfig.cs @@ -89,6 +89,11 @@ public sealed class EditorServicesConfig /// public ConsoleReplKind ConsoleRepl { get; set; } = ConsoleReplKind.None; + /// + /// Will suppress messages to PSHost (to prevent Stdio clobbering) + /// + public bool UseNullPSHostUI { get; set; } + /// /// The minimum log level to log events with. /// diff --git a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs index b9dbb0b2e..8f868d393 100644 --- a/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs +++ b/src/PowerShellEditorServices.Hosting/Internal/EditorServicesRunner.cs @@ -289,6 +289,7 @@ private HostStartupInfo CreateHostStartupInfo() _config.LogPath, (int)_config.LogLevel, consoleReplEnabled: _config.ConsoleRepl != ConsoleReplKind.None, + useNullPSHostUI: _config.UseNullPSHostUI, usesLegacyReadLine: _config.ConsoleRepl == ConsoleReplKind.LegacyReadLine, bundledModulePath: _config.BundledModulePath); } diff --git a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs index ba241a0ac..964509626 100644 --- a/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs +++ b/src/PowerShellEditorServices/Hosting/HostStartupInfo.cs @@ -76,6 +76,11 @@ public sealed class HostStartupInfo /// public bool ConsoleReplEnabled { get; } + /// + /// True if we want to suppress messages to PSHost (to prevent Stdio clobbering) + /// + public bool UseNullPSHostUI { get; } + /// /// If true, the legacy PSES readline implementation will be used. Otherwise PSReadLine will be used. /// If the console REPL is not enabled, this setting will be ignored. @@ -139,6 +144,7 @@ public sealed class HostStartupInfo /// The path to log to. /// The minimum log event level. /// Enable console if true. + /// Whether or not to use the Null UI. /// Use PSReadLine if false, otherwise use the legacy readline implementation. /// A custom path to the expected bundled modules. public HostStartupInfo( @@ -153,6 +159,7 @@ public sealed class HostStartupInfo string logPath, int logLevel, bool consoleReplEnabled, + bool useNullPSHostUI, bool usesLegacyReadLine, string bundledModulePath) { @@ -167,6 +174,7 @@ public sealed class HostStartupInfo LogPath = logPath; LogLevel = logLevel; ConsoleReplEnabled = consoleReplEnabled; + UseNullPSHostUI = useNullPSHostUI; UsesLegacyReadLine = usesLegacyReadLine; // Respect a user provided bundled module path. diff --git a/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs b/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs index c460a3020..8875835a7 100644 --- a/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs +++ b/src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs @@ -292,10 +292,17 @@ private PowerShellResult InvokePowerShell(PSCommand command) catch (CmdletInvocationException ex) { // We do not want to crash EditorServices for exceptions caused by cmdlet invocation. - // Two main reasons that cause the exception are: + // The main reasons that cause the exception are: // * PSCmdlet.WriteOutput being called from another thread than Begin/Process // * CompositionContainer.ComposeParts complaining that "...Only one batch can be composed at a time" - _logger.LogError(ex.Message); + // * PSScriptAnalyzer not being able to find its PSScriptAnalyzer.psd1 because we are hosted by an Assembly other than pwsh.exe + string message = ex.Message; + if (!string.IsNullOrEmpty(ex.ErrorRecord.FullyQualifiedErrorId)) + { + // Microsoft.PowerShell.EditorServices.Services.Analysis.PssaCmdletAnalysisEngine: Exception of type 'System.Exception' was thrown. | + message += $" | {ex.ErrorRecord.FullyQualifiedErrorId}"; + } + _logger.LogError(message); } return result; diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs index 2b62595c8..c921de81b 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs @@ -193,9 +193,9 @@ static PsesInternalHost() Version = hostInfo.Version; DebugContext = new PowerShellDebugContext(loggerFactory, this); - UI = hostInfo.ConsoleReplEnabled - ? new EditorServicesConsolePSHostUserInterface(loggerFactory, hostInfo.PSHost.UI) - : new NullPSHostUI(); + UI = hostInfo.UseNullPSHostUI + ? new NullPSHostUI() + : new EditorServicesConsolePSHostUserInterface(loggerFactory, hostInfo.PSHost.UI); } public override CultureInfo CurrentCulture => _hostInfo.PSHost.CurrentCulture; diff --git a/test/PowerShellEditorServices.Test/PsesHostFactory.cs b/test/PowerShellEditorServices.Test/PsesHostFactory.cs index b2d4ceb7c..0e02b6389 100644 --- a/test/PowerShellEditorServices.Test/PsesHostFactory.cs +++ b/test/PowerShellEditorServices.Test/PsesHostFactory.cs @@ -57,6 +57,7 @@ public static PsesInternalHost Create(ILoggerFactory loggerFactory, bool loadPro logLevel: (int)LogLevel.None, consoleReplEnabled: false, usesLegacyReadLine: false, + useNullPSHostUI: true, bundledModulePath: BundledModulePath); PsesInternalHost psesHost = new(loggerFactory, null, testHostDetails); diff --git a/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs b/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs index b34bbe650..1155db102 100644 --- a/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs +++ b/test/PowerShellEditorServices.Test/Services/Symbols/PSScriptAnalyzerTests.cs @@ -36,6 +36,7 @@ public class PSScriptAnalyzerTests logPath: null, logLevel: 0, consoleReplEnabled: false, + useNullPSHostUI: true, usesLegacyReadLine: false, bundledModulePath: PsesHostFactory.BundledModulePath));