From fa8b2f7de29edf259e85383ab3fd8bf22da5aad5 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 25 Sep 2019 11:25:23 -0700 Subject: [PATCH 01/26] Initial changes to new error view --- .../PowerShellCore_format_ps1xml.cs | 52 +++++++++++++++++ .../ExperimentalFeature.cs | 5 +- .../engine/parser/Position.cs | 58 ++++++++++++++++--- .../resources/ParserStrings.resx | 5 ++ 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 35d34c60670..d8370b64a7e 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -794,6 +794,32 @@ private static IEnumerable ViewsOf_System_Management_Autom } ") .AddScriptBlockExpressionBinding(@" + $newErrorView = (Get-ExperimentalFeature -Name PSErrorView).Enabled + + $resetColor = ""`e[0m"" + + function Get-VT100Color([ConsoleColor] $color) { + switch ($color) { + [ConsoleColor]::Black { ""`e[2;30m"" } + [ConsoleColor]::DarkRed { ""`e[2;31m"" } + [ConsoleColor]::DarkGreen { ""`e[2;32m"" } + [ConsoleColor]::DarkYellow { ""`e[2;33m"" } + [ConsoleColor]::DarkBlue { ""`e[2;34m"" } + [ConsoleColor]::DarkMagenta { ""`e[2;35m"" } + [ConsoleColor]::DarkCyan { ""`e[2;36m"" } + [ConsoleColor]::Gray { ""`e[2;37m"" } + [ConsoleColor]::DarkGray { ""`e[1;30m"" } + [ConsoleColor]::Red { ""`e[1;31m"" } + [ConsoleColor]::Green { ""`e[1;32m"" } + [ConsoleColor]::Yellow { ""`e[1;33m"" } + [ConsoleColor]::Blue { ""`e[1;34m"" } + [ConsoleColor]::Magenta { ""`e[1;35m"" } + [ConsoleColor]::Cyan { ""`e[1;36m"" } + [ConsoleColor]::White { ""`e[1;37m"" } + default { ""`e[1;31m"" } + } + } + if ($_.FullyQualifiedErrorId -eq ""NativeCommandErrorMessage"" -or $_.FullyQualifiedErrorId -eq ""NativeCommandError"") { $_.Exception.Message } @@ -815,6 +841,32 @@ private static IEnumerable ViewsOf_System_Management_Autom $posmsg = "" : "" + $_.PSMessageDetails + $posmsg } + if ($newErrorView) { + $errorColor = """" + if ($Host.PrivateData) { + $errorColor = Get-VT100Color -color $Host.PrivateData.ErrorForegroundColor + } + + if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { + if ($_.Exception -is [System.Management.Automation.ParseException]) { + $posmsg = $_.Exception.Message + $posmsg + } + else { + $posmsg += $errorColor + $_.Exception.Message + } + } + else { + $posmsg += $errorColor + $_.ErrorDetails.Message + } + + $category = """" + if ($_.CategoryInfo.Category) { + $category = ""[$($_.CategoryInfo.Category)]"" + } + + return ""${errorColor}Error ${category}${resetColor}: $($_.CategoryInfo.Reason)`n${posmsg}${resetcolor}"" + } + $indent = 4 $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 71b0d0f86b9..24994bc3e3d 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -114,7 +114,10 @@ static ExperimentalFeature() description: "New parameter set for ForEach-Object to run script blocks in parallel"), new ExperimentalFeature( name: "PSTernaryOperator", - description: "Support the ternary operator in PowerShell language") + description: "Support the ternary operator in PowerShell language"), + new ExperimentalFeature( + name: "PSErrorView", + description: "New formatting for ErrorRecord") }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/parser/Position.cs b/src/System.Management.Automation/engine/parser/Position.cs index 5d033191633..dd0f1994ec7 100644 --- a/src/System.Management.Automation/engine/parser/Position.cs +++ b/src/System.Management.Automation/engine/parser/Position.cs @@ -141,6 +141,7 @@ internal static string VerboseMessage(IScriptExtent position) string sourceLine = position.StartScriptPosition.Line.TrimEnd(); + string whitespaceFill = string.Empty; string message = string.Empty; if (!string.IsNullOrEmpty(sourceLine)) { @@ -230,20 +231,61 @@ internal static string VerboseMessage(IScriptExtent position) } if (needsPrefixDots) - sb.Append("... "); + { + sb.Append("\u2026 "); + } + sb.Append(sourceLine); + if (needsSuffixDots) - sb.Append(" ..."); + { + sb.Append(" \u2026"); + } + sb.Append(Environment.NewLine); - sb.Append("+ "); - sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 4 : 0)); - // errorLength of 0 happens at EOF - always write out 1. - sb.Append('~', errorLength > 0 ? errorLength : 1); + if (ExperimentalFeature.IsEnabled("PSErrorView")) + { + whitespaceFill = " ".Substring(0, position.StartLineNumber.ToString().Length); + sb.Append(whitespaceFill); + sb.Append("\x1b[1;36m | "); + sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 2 : 0)); + // errorLength of 0 happens at EOF - always write out 1. + sb.Append("\x1b[1;31m"); + sb.Append('^', errorLength > 0 ? errorLength : 1); + sb.Append("\x1b[0m"); + } + else + { + sb.Append("+ "); + sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 2 : 0)); + // errorLength of 0 happens at EOF - always write out 1. + sb.Append('~', errorLength > 0 ? errorLength : 1); + } + message = sb.ToString(); } - return StringUtil.Format(ParserStrings.TextForPositionMessage, fileName, position.StartLineNumber, - position.StartColumnNumber, message); + if (ExperimentalFeature.IsEnabled("PSErrorView")) + { + return StringUtil.Format( + ParserStrings.TextForPositionMessageNew, + fileName, + position.StartLineNumber, + position.StartColumnNumber, + message, + whitespaceFill).Replace('#','\x1b'); + + } + else + { + return StringUtil.Format( + ParserStrings.TextForPositionMessage, + fileName, + position.StartLineNumber, + position.StartColumnNumber, + message); + + } } /// diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index 7d68e49d66b..be90cdf58fb 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -610,6 +610,11 @@ The correct form is: foreach ($a in $b) {...} At {0}:{1} char:{2} + {3} + + + #[0mAt {0}:{1} char:{2} +#[1;36m{4} | +#[1;36m{1} | #[0m{3} At char:{0} From ae20c6f72acd51dd7a50a78df7c2aadecb8bfa0e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 26 Sep 2019 19:42:27 -0700 Subject: [PATCH 02/26] make $ErrorView an enum. Move Analytic view entirely to formatxml. Leave existing NormalView and CategoryView. --- .../host/msh/ConsoleHost.cs | 11 + .../host/msh/ConsoleHostUserInterface.cs | 1 + .../PowerShellCore_format_ps1xml.cs | 216 +++++++++++------- .../engine/CommandBase.cs | 17 ++ .../engine/InitialSessionState.cs | 8 +- .../engine/parser/Position.cs | 49 +--- .../resources/ParserStrings.resx | 5 - 7 files changed, 181 insertions(+), 126 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 1a727133309..bc6467f815d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -732,6 +732,17 @@ public ConsoleColorProxy(ConsoleHostUserInterface ui) _ui = ui; } + public ConsoleColor ErrorAccentColor + { + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + get + { return _ui.ErrorAccentColor; } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + set + { _ui.ErrorAccentColor = value; } + } + public ConsoleColor ErrorForegroundColor { [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index 0fd679b3c10..5dd59cb770a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -1356,6 +1356,7 @@ public override void WriteErrorLine(string value) } // Error colors + public ConsoleColor ErrorAccentColor { get; set; } = ConsoleColor.Cyan; public ConsoleColor ErrorForegroundColor { get; set; } = ConsoleColor.Red; public ConsoleColor ErrorBackgroundColor { get; set; } = Console.BackgroundColor; diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index d8370b64a7e..08ea8bd34b0 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -743,7 +743,7 @@ private static IEnumerable ViewsOf_System_Management_Autom CustomControl.Create(outOfBand: true) .StartEntry() .AddScriptBlockExpressionBinding(@" - if (($_.FullyQualifiedErrorId -ne ""NativeCommandErrorMessage"" -and $_.FullyQualifiedErrorId -ne ""NativeCommandError"") -and $ErrorView -ne ""CategoryView"") + if (($_.FullyQualifiedErrorId -ne ""NativeCommandErrorMessage"" -and $_.FullyQualifiedErrorId -ne ""NativeCommandError"") -and $ErrorView -ne ""Category"") { $myinv = $_.InvocationInfo if ($myinv -and $myinv.MyCommand) @@ -771,6 +771,10 @@ private static IEnumerable ViewsOf_System_Management_Autom } default { + if ($ErrorView -eq 'Analytic') { + break + } + if ($myinv.InvocationName -match '^[&\.]?$') { if ($myinv.MyCommand.Name) @@ -794,114 +798,166 @@ private static IEnumerable ViewsOf_System_Management_Autom } ") .AddScriptBlockExpressionBinding(@" - $newErrorView = (Get-ExperimentalFeature -Name PSErrorView).Enabled - $resetColor = ""`e[0m"" + $resetColor = '' + if ($Host.UI.SupportsVirtualTerminal) { + $resetColor = ""`e[0m"" + } - function Get-VT100Color([ConsoleColor] $color) { + function Get-VT100Color([ConsoleColor] $color) { switch ($color) { - [ConsoleColor]::Black { ""`e[2;30m"" } - [ConsoleColor]::DarkRed { ""`e[2;31m"" } - [ConsoleColor]::DarkGreen { ""`e[2;32m"" } - [ConsoleColor]::DarkYellow { ""`e[2;33m"" } - [ConsoleColor]::DarkBlue { ""`e[2;34m"" } - [ConsoleColor]::DarkMagenta { ""`e[2;35m"" } - [ConsoleColor]::DarkCyan { ""`e[2;36m"" } - [ConsoleColor]::Gray { ""`e[2;37m"" } - [ConsoleColor]::DarkGray { ""`e[1;30m"" } - [ConsoleColor]::Red { ""`e[1;31m"" } - [ConsoleColor]::Green { ""`e[1;32m"" } - [ConsoleColor]::Yellow { ""`e[1;33m"" } - [ConsoleColor]::Blue { ""`e[1;34m"" } - [ConsoleColor]::Magenta { ""`e[1;35m"" } - [ConsoleColor]::Cyan { ""`e[1;36m"" } - [ConsoleColor]::White { ""`e[1;37m"" } + 'Black' { ""`e[2;30m"" } + 'DarkRed' { ""`e[2;31m"" } + 'DarkGreen' { ""`e[2;32m"" } + 'DarkYellow' { ""`e[2;33m"" } + 'DarkBlue' { ""`e[2;34m"" } + 'DarkMagenta' { ""`e[2;35m"" } + 'DarkCyan' { ""`e[2;36m"" } + 'Gray' { ""`e[2;37m"" } + 'DarkGray' { ""`e[1;30m"" } + 'Red' { ""`e[1;31m"" } + 'Green' { ""`e[1;32m"" } + 'Yellow' { ""`e[1;33m"" } + 'Blue' { ""`e[1;34m"" } + 'Magenta' { ""`e[1;35m"" } + 'Cyan' { ""`e[1;36m"" } + 'White' { ""`e[1;37m"" } default { ""`e[1;31m"" } } - } - - if ($_.FullyQualifiedErrorId -eq ""NativeCommandErrorMessage"" -or $_.FullyQualifiedErrorId -eq ""NativeCommandError"") { - $_.Exception.Message - } - else - { - $myinv = $_.InvocationInfo - if ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) { - $posmsg = $myinv.PositionMessage - } else { - $posmsg = """" - } - - if ($posmsg -ne """") - { - $posmsg = ""`n"" + $posmsg - } + } - if ( & { Set-StrictMode -Version 1; $_.PSMessageDetails } ) { - $posmsg = "" : "" + $_.PSMessageDetails + $posmsg + function Get-AnalyticPositionMessage { + $errorColor = '' + $accentColor = '' + if ($Host.PrivateData -and $Host.UI.SupportsVirtualTerminal) { + $errorColor = Get-VT100Color -color $Host.PrivateData.ErrorForegroundColor + $accentColor = Get-VT100Color -color $Host.PrivateData.ErrorAccentColor } - if ($newErrorView) { - $errorColor = """" - if ($Host.PrivateData) { - $errorColor = Get-VT100Color -color $Host.PrivateData.ErrorForegroundColor - } + $posmsg = '' - if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { - if ($_.Exception -is [System.Management.Automation.ParseException]) { - $posmsg = $_.Exception.Message + $posmsg - } - else { - $posmsg += $errorColor + $_.Exception.Message - } + if ($myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { + if ($myinv.ScriptName) { + $posmsg = ""${resetColor}At $($myinv.ScriptName)`n"" } else { - $posmsg += $errorColor + $_.ErrorDetails.Message + $posmsg = ""`n"" } - $category = """" - if ($_.CategoryInfo.Category) { - $category = ""[$($_.CategoryInfo.Category)]"" + $headerWhitespace = '' + $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length + if ($scriptLineNumberLength -gt 4) { + $headerWhitespace = ' ' * ($scriptLineNumberLength - 4) } - return ""${errorColor}Error ${category}${resetColor}: $($_.CategoryInfo.Reason)`n${posmsg}${resetcolor}"" - } + $lineWhitespace = '' + if ($scriptLineNumberLength -lt 4) { + $lineWhitespace = ' ' * (4 - $scriptLineNumberLength) + } - $indent = 4 + $posmsg += ""${accentColor}${headerWhitespace}Line |`n"" + $posmsg += ""${accentColor}${lineWhitespace}$($myinv.ScriptLineNumber) | ${resetcolor}$($myinv.Line)`n"" - $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } + $offsetWhitespace = ' ' * $myinv.OffsetInLine - if ($null -ne $errorCategoryMsg) - { - $indentString = ""+ CategoryInfo : "" + $_.ErrorCategory_Message + $posmsg += ""${accentColor}${headerWhitespace} |${offsetWhitespace}${errorColor}^ "" } - else - { - $indentString = ""+ CategoryInfo : "" + $_.CategoryInfo + + if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { + if ($_.CategoryInfo.Category -eq 'ParserError') { + $posmsg += $errorColor + $_.Exception.message.split(""~`n"")[1].split(""`n`n"")[0] + } + else { + $posmsg += $errorColor + $_.Exception.Message + } + } + else { + $posmsg += $errorColor + $_.ErrorDetails.Message } - $posmsg += ""`n"" + $indentString + $reason = 'Error' + if ($_.Exception -and $_.Exception.WasThrownFromThrowStatement) { + $reason = 'Exception' + } + elseif ($_.CategoryInfo.Category) { + $reason = $_.CategoryInfo.Category + } + elseif ($_CategoryInfo.Reason) { + $reason = $_.CategoryInfo.Reason + } - $indentString = ""+ FullyQualifiedErrorId : "" + $_.FullyQualifiedErrorId - $posmsg += ""`n"" + $indentString + $errorMsg = 'Error' - $originInfo = & { Set-StrictMode -Version 1; $_.OriginInfo } + ""${errorColor}${reason}:${resetColor} ${posmsg}${resetcolor}"" + } - if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) + if ($_.FullyQualifiedErrorId -eq ""NativeCommandErrorMessage"" -or $_.FullyQualifiedErrorId -eq ""NativeCommandError"" -or $ErrorView -eq ""Message"") { + $_.Exception.Message + } + else + { + $myinv = $_.InvocationInfo + if ($myinv -and $ErrorView -eq 'Analytic') { + $posmsg = Get-AnalyticPositionMessage + } + elseif ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) { + $posmsg = $myinv.PositionMessage + } else { + $posmsg = '' + } + + if ($posmsg -ne '') { - $indentString = ""+ PSComputerName : "" + $originInfo.PSComputerName - $posmsg += ""`n"" + $indentString + $posmsg = ""`n"" + $posmsg } - if ($ErrorView -eq ""CategoryView"") { - $_.CategoryInfo.GetMessage() + if ( & { Set-StrictMode -Version 1; $_.PSMessageDetails } ) { + $posmsg = ' : ' + $_.PSMessageDetails + $posmsg } - elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { - $_.Exception.Message + $posmsg + ""`n "" - } else { - $_.ErrorDetails.Message + $posmsg + + switch($ErrorView) { + 'Normal' { + $indent = 4 + + $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } + + if ($null -ne $errorCategoryMsg) + { + $indentString = '+ CategoryInfo : ' + $_.ErrorCategory_Message + } + else + { + $indentString = '+ CategoryInfo : ' + $_.CategoryInfo + } + + $posmsg += ""`n"" + $indentString + + $indentString = '+ FullyQualifiedErrorId : ' + $_.FullyQualifiedErrorId + $posmsg += ""`n"" + $indentString + + $originInfo = & { Set-StrictMode -Version 1; $_.OriginInfo } + + if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) + { + $indentString = '+ PSComputerName : ' + $originInfo.PSComputerName + $posmsg += ""`n"" + $indentString + } + + if ($ErrorView -eq 'CategoryView') { + $_.CategoryInfo.GetMessage() + } + elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { + $_.Exception.Message + $posmsg + '`n ' + } else { + $_.ErrorDetails.Message + $posmsg + } + } + + 'Analytic' { + $posmsg + } } - } + } ") .EndEntry() .EndControl()); diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index b2351059cc9..257189c4c35 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -266,6 +266,23 @@ internal void InternalDispose(bool isDisposing) namespace System.Management.Automation { + #region ErrorView + /// + /// Defines the potential ErrorView options. + /// + public enum ErrorView + { + /// Existing all red multi-line output. + Normal = 0, + + /// Only show category information. + Category = 1, + + /// Analytic shows more information on the context of the error or just the message if not a script + Analytic = 2, + } + #endregion ErrorView + #region ActionPreference /// /// Defines the Action Preference options. These options determine diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index ebf1f768185..9e704e95723 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4326,6 +4326,8 @@ .ForwardHelpCategory Cmdlet internal const ActionPreference defaultVerbosePreference = ActionPreference.SilentlyContinue; internal const ActionPreference defaultWarningPreference = ActionPreference.Continue; internal const ActionPreference defaultInformationPreference = ActionPreference.SilentlyContinue; + + internal const ErrorView defaultErrorView = ErrorView.Normal; internal const bool defaultWhatIfPreference = false; internal const ConfirmImpact defaultConfirmPreference = ConfirmImpact.High; @@ -4408,8 +4410,10 @@ .ForwardHelpCategory Cmdlet ), new SessionStateVariableEntry( SpecialVariables.ErrorView, - "NormalView", - RunspaceInit.ErrorViewDescription + ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.Analytic : defaultErrorView, + RunspaceInit.ErrorViewDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ErrorView)) ), new SessionStateVariableEntry( SpecialVariables.NestedPromptLevel, diff --git a/src/System.Management.Automation/engine/parser/Position.cs b/src/System.Management.Automation/engine/parser/Position.cs index dd0f1994ec7..22b264dcb20 100644 --- a/src/System.Management.Automation/engine/parser/Position.cs +++ b/src/System.Management.Automation/engine/parser/Position.cs @@ -243,49 +243,20 @@ internal static string VerboseMessage(IScriptExtent position) } sb.Append(Environment.NewLine); - if (ExperimentalFeature.IsEnabled("PSErrorView")) - { - whitespaceFill = " ".Substring(0, position.StartLineNumber.ToString().Length); - sb.Append(whitespaceFill); - sb.Append("\x1b[1;36m | "); - sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 2 : 0)); - // errorLength of 0 happens at EOF - always write out 1. - sb.Append("\x1b[1;31m"); - sb.Append('^', errorLength > 0 ? errorLength : 1); - sb.Append("\x1b[0m"); - } - else - { - sb.Append("+ "); - sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 2 : 0)); - // errorLength of 0 happens at EOF - always write out 1. - sb.Append('~', errorLength > 0 ? errorLength : 1); - } + sb.Append("+ "); + sb.Append(' ', spacesBeforeError + (needsPrefixDots ? 2 : 0)); + // errorLength of 0 happens at EOF - always write out 1. + sb.Append('~', errorLength > 0 ? errorLength : 1); message = sb.ToString(); } - if (ExperimentalFeature.IsEnabled("PSErrorView")) - { - return StringUtil.Format( - ParserStrings.TextForPositionMessageNew, - fileName, - position.StartLineNumber, - position.StartColumnNumber, - message, - whitespaceFill).Replace('#','\x1b'); - - } - else - { - return StringUtil.Format( - ParserStrings.TextForPositionMessage, - fileName, - position.StartLineNumber, - position.StartColumnNumber, - message); - - } + return StringUtil.Format( + ParserStrings.TextForPositionMessage, + fileName, + position.StartLineNumber, + position.StartColumnNumber, + message); } /// diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index be90cdf58fb..7d68e49d66b 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -610,11 +610,6 @@ The correct form is: foreach ($a in $b) {...} At {0}:{1} char:{2} + {3} - - - #[0mAt {0}:{1} char:{2} -#[1;36m{4} | -#[1;36m{1} | #[0m{3} At char:{0} From 71e48936564cf43dc6666c7a141ccf8aa5e5695b Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 26 Sep 2019 20:20:57 -0700 Subject: [PATCH 03/26] rename Analytic to Concise and fix it so invocation name doesn't show for Concise --- .../PowerShellCore_format_ps1xml.cs | 24 ++++++++----------- .../engine/CommandBase.cs | 4 ++-- .../engine/InitialSessionState.cs | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 08ea8bd34b0..3aff3ed5249 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -743,7 +743,7 @@ private static IEnumerable ViewsOf_System_Management_Autom CustomControl.Create(outOfBand: true) .StartEntry() .AddScriptBlockExpressionBinding(@" - if (($_.FullyQualifiedErrorId -ne ""NativeCommandErrorMessage"" -and $_.FullyQualifiedErrorId -ne ""NativeCommandError"") -and $ErrorView -ne ""Category"") + if (($_.FullyQualifiedErrorId -ne 'NativeCommandErrorMessage' -and $_.FullyQualifiedErrorId -ne 'NativeCommandError') -and $ErrorView -ne 'Category' -and $ErrorView -ne 'Concise') { $myinv = $_.InvocationInfo if ($myinv -and $myinv.MyCommand) @@ -754,7 +754,7 @@ private static IEnumerable ViewsOf_System_Management_Autom { if ($myinv.MyCommand.Path) { - $myinv.MyCommand.Path + "" : "" + $myinv.MyCommand.Path + ' : ' } break @@ -764,27 +764,23 @@ private static IEnumerable ViewsOf_System_Management_Autom { if ($myinv.MyCommand.ScriptBlock) { - $myinv.MyCommand.ScriptBlock.ToString() + "" : "" + $myinv.MyCommand.ScriptBlock.ToString() + ' : ' } break } default { - if ($ErrorView -eq 'Analytic') { - break - } - if ($myinv.InvocationName -match '^[&\.]?$') { if ($myinv.MyCommand.Name) { - $myinv.MyCommand.Name + "" : "" + $myinv.MyCommand.Name + ' : ' } } else { - $myinv.InvocationName + "" : "" + $myinv.InvocationName + ' : ' } break @@ -793,7 +789,7 @@ private static IEnumerable ViewsOf_System_Management_Autom } elseif ($myinv -and $myinv.InvocationName) { - $myinv.InvocationName + "" : "" + $myinv.InvocationName + ' : ' } } ") @@ -826,7 +822,7 @@ private static IEnumerable ViewsOf_System_Management_Autom } } - function Get-AnalyticPositionMessage { + function Get-ConcisePositionMessage { $errorColor = '' $accentColor = '' if ($Host.PrivateData -and $Host.UI.SupportsVirtualTerminal) { @@ -897,8 +893,8 @@ function Get-AnalyticPositionMessage { else { $myinv = $_.InvocationInfo - if ($myinv -and $ErrorView -eq 'Analytic') { - $posmsg = Get-AnalyticPositionMessage + if ($myinv -and $ErrorView -eq 'Concise') { + $posmsg = Get-ConcisePositionMessage } elseif ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) { $posmsg = $myinv.PositionMessage @@ -953,7 +949,7 @@ function Get-AnalyticPositionMessage { } } - 'Analytic' { + 'Concise' { $posmsg } } diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 257189c4c35..f0399a0d292 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -278,8 +278,8 @@ public enum ErrorView /// Only show category information. Category = 1, - /// Analytic shows more information on the context of the error or just the message if not a script - Analytic = 2, + /// Concise shows more information on the context of the error or just the message if not a script or parser error. + Concise = 2, } #endregion ErrorView diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 9e704e95723..6379e26e1e3 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4410,7 +4410,7 @@ .ForwardHelpCategory Cmdlet ), new SessionStateVariableEntry( SpecialVariables.ErrorView, - ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.Analytic : defaultErrorView, + ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.Concise : defaultErrorView, RunspaceInit.ErrorViewDescription, ScopedItemOptions.None, new ArgumentTypeConverterAttribute(typeof(ErrorView)) From 8471e8e7c2c2fdf229d4f46dbe9c9c1096598fcd Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 26 Sep 2019 20:50:28 -0700 Subject: [PATCH 04/26] use command name as reason if it exists to different multiple cmdlets in a single line --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 3aff3ed5249..ecc52e9f623 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -875,6 +875,12 @@ function Get-ConcisePositionMessage { if ($_.Exception -and $_.Exception.WasThrownFromThrowStatement) { $reason = 'Exception' } + elseif ($myinv.MyCommand) { + $reason = $myinv.MyCommand + } + elseif ($myinv.InvocationName) { + $reason = $myinv.InvocationName + } elseif ($_.CategoryInfo.Category) { $reason = $_.CategoryInfo.Category } From e88a02d9304dde0dfe17f54c4a705bd1591f3efd Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 08:50:04 -0700 Subject: [PATCH 05/26] rename enum to include View suffix --- .../PowerShellCore_format_ps1xml.cs | 14 +++++++------- .../engine/CommandBase.cs | 6 +++--- .../engine/InitialSessionState.cs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index ecc52e9f623..aa12ea15b17 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -743,7 +743,7 @@ private static IEnumerable ViewsOf_System_Management_Autom CustomControl.Create(outOfBand: true) .StartEntry() .AddScriptBlockExpressionBinding(@" - if (($_.FullyQualifiedErrorId -ne 'NativeCommandErrorMessage' -and $_.FullyQualifiedErrorId -ne 'NativeCommandError') -and $ErrorView -ne 'Category' -and $ErrorView -ne 'Concise') + if (($_.FullyQualifiedErrorId -ne 'NativeCommandErrorMessage' -and $_.FullyQualifiedErrorId -ne 'NativeCommandError') -and $ErrorView -ne 'CategoryView' -and $ErrorView -ne 'ConciseView') { $myinv = $_.InvocationInfo if ($myinv -and $myinv.MyCommand) @@ -822,7 +822,7 @@ private static IEnumerable ViewsOf_System_Management_Autom } } - function Get-ConcisePositionMessage { + function Get-ConciseViewPositionMessage { $errorColor = '' $accentColor = '' if ($Host.PrivateData -and $Host.UI.SupportsVirtualTerminal) { @@ -893,14 +893,14 @@ function Get-ConcisePositionMessage { ""${errorColor}${reason}:${resetColor} ${posmsg}${resetcolor}"" } - if ($_.FullyQualifiedErrorId -eq ""NativeCommandErrorMessage"" -or $_.FullyQualifiedErrorId -eq ""NativeCommandError"" -or $ErrorView -eq ""Message"") { + if ($_.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $_.FullyQualifiedErrorId -eq 'NativeCommandError') { $_.Exception.Message } else { $myinv = $_.InvocationInfo - if ($myinv -and $ErrorView -eq 'Concise') { - $posmsg = Get-ConcisePositionMessage + if ($myinv -and $ErrorView -eq 'ConciseView') { + $posmsg = Get-ConciseViewPositionMessage } elseif ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) { $posmsg = $myinv.PositionMessage @@ -918,7 +918,7 @@ function Get-ConcisePositionMessage { } switch($ErrorView) { - 'Normal' { + 'NormalView' { $indent = 4 $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } @@ -955,7 +955,7 @@ function Get-ConcisePositionMessage { } } - 'Concise' { + 'ConciseView' { $posmsg } } diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index f0399a0d292..c192ac3ffbf 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -273,13 +273,13 @@ namespace System.Management.Automation public enum ErrorView { /// Existing all red multi-line output. - Normal = 0, + NormalView = 0, /// Only show category information. - Category = 1, + CategoryView = 1, /// Concise shows more information on the context of the error or just the message if not a script or parser error. - Concise = 2, + ConciseView = 2, } #endregion ErrorView diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 6379e26e1e3..91d84032f9a 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4327,7 +4327,7 @@ .ForwardHelpCategory Cmdlet internal const ActionPreference defaultWarningPreference = ActionPreference.Continue; internal const ActionPreference defaultInformationPreference = ActionPreference.SilentlyContinue; - internal const ErrorView defaultErrorView = ErrorView.Normal; + internal const ErrorView defaultErrorView = ErrorView.NormalView; internal const bool defaultWhatIfPreference = false; internal const ConfirmImpact defaultConfirmPreference = ConfirmImpact.High; @@ -4410,7 +4410,7 @@ .ForwardHelpCategory Cmdlet ), new SessionStateVariableEntry( SpecialVariables.ErrorView, - ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.Concise : defaultErrorView, + ExperimentalFeature.IsEnabled("PSErrorView") ? ErrorView.ConciseView : defaultErrorView, RunspaceInit.ErrorViewDescription, ScopedItemOptions.None, new ArgumentTypeConverterAttribute(typeof(ErrorView)) From c38c601e7502ff4df2b0eb77080c253972ce4e2b Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 13:13:35 -0700 Subject: [PATCH 06/26] move vt100 helpers as private function to Get-ConciseViewPositionMessage, fix display of remote errors for Concise and Normal views --- .../PowerShellCore_format_ps1xml.cs | 119 +++++++++--------- .../engine/Formatting/ErrorView.Tests.ps1 | 39 ++++++ 2 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 test/powershell/engine/Formatting/ErrorView.Tests.ps1 diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index aa12ea15b17..b177379349c 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -795,34 +795,35 @@ private static IEnumerable ViewsOf_System_Management_Autom ") .AddScriptBlockExpressionBinding(@" - $resetColor = '' - if ($Host.UI.SupportsVirtualTerminal) { - $resetColor = ""`e[0m"" - } + function Get-ConciseViewPositionMessage { - function Get-VT100Color([ConsoleColor] $color) { - switch ($color) { - 'Black' { ""`e[2;30m"" } - 'DarkRed' { ""`e[2;31m"" } - 'DarkGreen' { ""`e[2;32m"" } - 'DarkYellow' { ""`e[2;33m"" } - 'DarkBlue' { ""`e[2;34m"" } - 'DarkMagenta' { ""`e[2;35m"" } - 'DarkCyan' { ""`e[2;36m"" } - 'Gray' { ""`e[2;37m"" } - 'DarkGray' { ""`e[1;30m"" } - 'Red' { ""`e[1;31m"" } - 'Green' { ""`e[1;32m"" } - 'Yellow' { ""`e[1;33m"" } - 'Blue' { ""`e[1;34m"" } - 'Magenta' { ""`e[1;35m"" } - 'Cyan' { ""`e[1;36m"" } - 'White' { ""`e[1;37m"" } - default { ""`e[1;31m"" } - } - } + $resetColor = '' + if ($Host.UI.SupportsVirtualTerminal) { + $resetColor = ""`e[0m"" + } + + function Get-VT100Color([ConsoleColor] $color) { + switch ($color) { + 'Black' { ""`e[2;30m"" } + 'DarkRed' { ""`e[2;31m"" } + 'DarkGreen' { ""`e[2;32m"" } + 'DarkYellow' { ""`e[2;33m"" } + 'DarkBlue' { ""`e[2;34m"" } + 'DarkMagenta' { ""`e[2;35m"" } + 'DarkCyan' { ""`e[2;36m"" } + 'Gray' { ""`e[2;37m"" } + 'DarkGray' { ""`e[1;30m"" } + 'Red' { ""`e[1;31m"" } + 'Green' { ""`e[1;32m"" } + 'Yellow' { ""`e[1;33m"" } + 'Blue' { ""`e[1;34m"" } + 'Magenta' { ""`e[1;35m"" } + 'Cyan' { ""`e[1;36m"" } + 'White' { ""`e[1;37m"" } + default { ""`e[1;31m"" } + } + } - function Get-ConciseViewPositionMessage { $errorColor = '' $accentColor = '' if ($Host.PrivateData -and $Host.UI.SupportsVirtualTerminal) { @@ -832,7 +833,7 @@ function Get-ConciseViewPositionMessage { $posmsg = '' - if ($myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { + if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { $posmsg = ""${resetColor}At $($myinv.ScriptName)`n"" } @@ -899,7 +900,7 @@ function Get-ConciseViewPositionMessage { else { $myinv = $_.InvocationInfo - if ($myinv -and $ErrorView -eq 'ConciseView') { + if ($ErrorView -eq 'ConciseView') { $posmsg = Get-ConciseViewPositionMessage } elseif ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) { @@ -917,47 +918,43 @@ function Get-ConciseViewPositionMessage { $posmsg = ' : ' + $_.PSMessageDetails + $posmsg } - switch($ErrorView) { - 'NormalView' { - $indent = 4 + if ($ErrorView -eq 'ConciseView') { + return $posmsg + } - $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } + $indent = 4 - if ($null -ne $errorCategoryMsg) - { - $indentString = '+ CategoryInfo : ' + $_.ErrorCategory_Message - } - else - { - $indentString = '+ CategoryInfo : ' + $_.CategoryInfo - } + $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message } - $posmsg += ""`n"" + $indentString + if ($null -ne $errorCategoryMsg) + { + $indentString = '+ CategoryInfo : ' + $_.ErrorCategory_Message + } + else + { + $indentString = '+ CategoryInfo : ' + $_.CategoryInfo + } - $indentString = '+ FullyQualifiedErrorId : ' + $_.FullyQualifiedErrorId - $posmsg += ""`n"" + $indentString + $posmsg += ""`n"" + $indentString - $originInfo = & { Set-StrictMode -Version 1; $_.OriginInfo } + $indentString = ""+ FullyQualifiedErrorId : "" + $_.FullyQualifiedErrorId + $posmsg += ""`n"" + $indentString - if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) - { - $indentString = '+ PSComputerName : ' + $originInfo.PSComputerName - $posmsg += ""`n"" + $indentString - } + $originInfo = & { Set-StrictMode -Version 1; $_.OriginInfo } - if ($ErrorView -eq 'CategoryView') { - $_.CategoryInfo.GetMessage() - } - elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { - $_.Exception.Message + $posmsg + '`n ' - } else { - $_.ErrorDetails.Message + $posmsg - } - } + if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) + { + $indentString = ""+ PSComputerName : "" + $originInfo.PSComputerName + $posmsg += ""`n"" + $indentString + } - 'ConciseView' { - $posmsg - } + if ($ErrorView -eq 'CategoryView') { + $_.CategoryInfo.GetMessage() + } + elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { + $_.Exception.Message + $posmsg + ""`n"" + } else { + $_.ErrorDetails.Message + $posmsg } } ") diff --git a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 new file mode 100644 index 00000000000..a2cc8ad2f06 --- /dev/null +++ b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 @@ -0,0 +1,39 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe "Tests for `$ErrorView" -Tag CI { + + It "`$ErrorView is an enum" { + $ErrorView | Should -BeOfType [System.Management.Automation.ErrorView] + } + + It "`$ErrorView should have correct default value" { + $expectedDefault = "NormalView" + + if ((Get-ExperimentalFeature -Name PSErrorView).IsEnabled) { + $expectedDefault = "ConciseView" + } + + $ErrorView | Should -BeExactly $expectedDefault + } + + Context "ConciseView tests" { + + It "Cmdlet error should be one line of text" { + Get-Item (New-Guid) -ErrorVariable e + ($e | Out-String).Trim().Count | Should -Be 1 + } + + It "Script error should contain path to script and line for error" { + $testScript = @' + $a = 1 + 123) + $b = 2 +'@ + + $testScriptPath = Join-Path -Path $TestDrive -ChildPath "test.ps1" + Set-Content -Path $testScriptPath -Value $testScriptPath + & $testScriptPath | Out-String | Should -Contain $testScriptPath + } + } +} From 63421fb93581f8c3f99a2926ef41daab1752540f Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 13:41:34 -0700 Subject: [PATCH 07/26] add tests --- .../engine/Formatting/ErrorView.Tests.ps1 | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 index a2cc8ad2f06..e3376d35860 100644 --- a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 +++ b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 @@ -10,7 +10,7 @@ Describe "Tests for `$ErrorView" -Tag CI { It "`$ErrorView should have correct default value" { $expectedDefault = "NormalView" - if ((Get-ExperimentalFeature -Name PSErrorView).IsEnabled) { + if ((Get-ExperimentalFeature -Name PSErrorView).Enabled) { $expectedDefault = "ConciseView" } @@ -20,20 +20,28 @@ Describe "Tests for `$ErrorView" -Tag CI { Context "ConciseView tests" { It "Cmdlet error should be one line of text" { - Get-Item (New-Guid) -ErrorVariable e + Get-Item (New-Guid) -ErrorVariable e -ErrorAction SilentlyContinue ($e | Out-String).Trim().Count | Should -Be 1 } It "Script error should contain path to script and line for error" { $testScript = @' + [cmdletbinding()] + param() $a = 1 123) $b = 2 '@ $testScriptPath = Join-Path -Path $TestDrive -ChildPath "test.ps1" - Set-Content -Path $testScriptPath -Value $testScriptPath - & $testScriptPath | Out-String | Should -Contain $testScriptPath + Set-Content -Path $testScriptPath -Value $testScript + $e = { & $testScriptPath } | Should -Throw -ErrorId "UnexpectedToken" -PassThru + $e | Out-String | Should -Match $testScriptPath + } + + It "Remote errors show up correctly" { + Start-Job -ScriptBlock { get-item (new-guid) } | Wait-Job | Receive-Job -ErrorVariable e -ErrorAction SilentlyContinue + ($e | Out-String).Trim().Count | Should -Be 1 } } } From 2dfad59b6e788b96d8ca4cd61787e530fedf3c99 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 16:30:41 -0700 Subject: [PATCH 08/26] remove unused variable --- src/System.Management.Automation/engine/parser/Position.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Management.Automation/engine/parser/Position.cs b/src/System.Management.Automation/engine/parser/Position.cs index 22b264dcb20..03f80ed95a4 100644 --- a/src/System.Management.Automation/engine/parser/Position.cs +++ b/src/System.Management.Automation/engine/parser/Position.cs @@ -141,7 +141,6 @@ internal static string VerboseMessage(IScriptExtent position) string sourceLine = position.StartScriptPosition.Line.TrimEnd(); - string whitespaceFill = string.Empty; string message = string.Empty; if (!string.IsNullOrEmpty(sourceLine)) { From 9c0411e7548e2dacedd47c7a10d1c7f75c9d1a34 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 16:49:30 -0700 Subject: [PATCH 09/26] fix redirection test --- .../Language/Parser/RedirectionOperator.Tests.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 b/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 index 6516b6ee416..be81c381cde 100644 --- a/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 +++ b/test/powershell/Language/Parser/RedirectionOperator.Tests.ps1 @@ -96,6 +96,15 @@ Describe "File redirection mixed with Out-Null" -Tags CI { } Describe "File redirection should have 'DoComplete' called on the underlying pipeline processor" -Tags CI { + BeforeAll { + $originalErrorView = $ErrorView + $ErrorView = "NormalView" + } + + AfterAll { + $ErrorView = $originalErrorView + } + It "File redirection should result in the same file as Out-File" { $object = [pscustomobject] @{ one = 1 } $redirectFile = Join-Path $TestDrive fileRedirect.txt From 313d37a780e0bb59877580ab853a054fb547617e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 17:50:36 -0700 Subject: [PATCH 10/26] fix test failures --- .../Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 | 3 +++ .../Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 | 4 ++-- test/powershell/engine/Formatting/ErrorView.Tests.ps1 | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 index 900102db0b3..cc335afc8de 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Implicit.Remoting.Tests.ps1 @@ -193,11 +193,14 @@ try BeforeAll { if ($skipTest) { return } $module = Import-PSSession -Session $session -Name Get-Variable -Prefix My -AllowClobber + $oldErrorView = $ErrorView + $ErrorView = "NormalView" } AfterAll { if ($skipTest) { return } if ($null -ne $module) { Remove-Module $module -Force -ErrorAction SilentlyContinue } + $ErrorView = $oldErrorView } It "Test non-terminating error" { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 index 22734cc2a0c..bc22e73746a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 @@ -102,8 +102,8 @@ Describe "Write-Error Tests" -Tags "CI" { $longtext += $longtext } $pwsh = $pshome + "/pwsh" - $result = & $pwsh -c Write-Error -Message $longtext 2>&1 - $result.Count | Should -BeExactly 4 + $result = & $pwsh -c "`$ErrorView = 'Normal'; Write-Error -Message $longtext 2>&1" + $result.Count | Should -BeExactly 3 $result[0] | Should -Match $longtext } } diff --git a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 index e3376d35860..153adcde8de 100644 --- a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 +++ b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 @@ -36,7 +36,7 @@ Describe "Tests for `$ErrorView" -Tag CI { $testScriptPath = Join-Path -Path $TestDrive -ChildPath "test.ps1" Set-Content -Path $testScriptPath -Value $testScript $e = { & $testScriptPath } | Should -Throw -ErrorId "UnexpectedToken" -PassThru - $e | Out-String | Should -Match $testScriptPath + $e | Out-String | Should -BeLike "*$testScriptPath*" } It "Remote errors show up correctly" { From 2c7372340134a8276c7d7a1ca0a18bf4c2bbd52f Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 27 Sep 2019 21:14:05 -0700 Subject: [PATCH 11/26] another attempt to fix write-error test that fails in CI but not locally --- .../Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 index bc22e73746a..02e52841d93 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 @@ -101,8 +101,7 @@ Describe "Write-Error Tests" -Tags "CI" { while ($longtext.Length -lt [console]::WindowWidth) { $longtext += $longtext } - $pwsh = $pshome + "/pwsh" - $result = & $pwsh -c "`$ErrorView = 'Normal'; Write-Error -Message $longtext 2>&1" + $result = pwsh -noprofile -command "`$ErrorView = 'NormalView'; Write-Error -Message '$longtext' 2>&1" $result.Count | Should -BeExactly 3 $result[0] | Should -Match $longtext } From e2c9ee8777b3929333d364b420ae0c122a7f3ce5 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 06:20:37 -0700 Subject: [PATCH 12/26] fix redirection in write-error test --- .../Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 index 02e52841d93..2a680fb03e4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Error.Tests.ps1 @@ -101,7 +101,7 @@ Describe "Write-Error Tests" -Tags "CI" { while ($longtext.Length -lt [console]::WindowWidth) { $longtext += $longtext } - $result = pwsh -noprofile -command "`$ErrorView = 'NormalView'; Write-Error -Message '$longtext' 2>&1" + $result = pwsh -noprofile -command "`$ErrorView = 'NormalView'; Write-Error -Message '$longtext'" 2>&1 $result.Count | Should -BeExactly 3 $result[0] | Should -Match $longtext } From fde04e282a01b30a9a8feb5a8ebce3b03d6cab8a Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 06:31:31 -0700 Subject: [PATCH 13/26] address Ilya's comments --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 6 +++++- src/System.Management.Automation/engine/parser/Position.cs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index b177379349c..35d4c6c37aa 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -803,6 +803,10 @@ function Get-ConciseViewPositionMessage { } function Get-VT100Color([ConsoleColor] $color) { + if (! $Host.UI.SupportsVirtualTerminal) { + return '' + } + switch ($color) { 'Black' { ""`e[2;30m"" } 'DarkRed' { ""`e[2;31m"" } @@ -826,7 +830,7 @@ function Get-ConciseViewPositionMessage { $errorColor = '' $accentColor = '' - if ($Host.PrivateData -and $Host.UI.SupportsVirtualTerminal) { + if ($Host.PrivateData) { $errorColor = Get-VT100Color -color $Host.PrivateData.ErrorForegroundColor $accentColor = Get-VT100Color -color $Host.PrivateData.ErrorAccentColor } diff --git a/src/System.Management.Automation/engine/parser/Position.cs b/src/System.Management.Automation/engine/parser/Position.cs index 03f80ed95a4..f26054174e4 100644 --- a/src/System.Management.Automation/engine/parser/Position.cs +++ b/src/System.Management.Automation/engine/parser/Position.cs @@ -231,14 +231,14 @@ internal static string VerboseMessage(IScriptExtent position) if (needsPrefixDots) { - sb.Append("\u2026 "); + sb.Append("\u2026 "); // Unicode ellipsis character } sb.Append(sourceLine); if (needsSuffixDots) { - sb.Append(" \u2026"); + sb.Append(" \u2026"); // Unicode ellipsis character } sb.Append(Environment.NewLine); From a9e5b46ab836bfd5f9e7539c8612e858ca156ee0 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 06:55:51 -0700 Subject: [PATCH 14/26] fix consolehost test --- test/powershell/Host/ConsoleHost.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 12ced856831..39864e4d3d6 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -393,7 +393,7 @@ export $envVarName='$guid' It "errors are in text if error is redirected, encoded command, non-interactive, and outputformat specified" { $p = [Diagnostics.Process]::new() $p.StartInfo.FileName = "pwsh" - $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("throw 'boom'")) + $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("`$ErrorView='NormalView';throw 'boom'")) $p.StartInfo.Arguments = "-EncodedCommand $encoded -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -OutputFormat text" $p.StartInfo.UseShellExecute = $false $p.StartInfo.RedirectStandardError = $true From b7ec14b32196a996ec194f7313b437367d1236c5 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 08:02:59 -0700 Subject: [PATCH 15/26] Change `At` to `In`. Add highlight to where error is in line --- .../PowerShellCore_format_ps1xml.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 35d4c6c37aa..ac5a35f3564 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -798,8 +798,10 @@ private static IEnumerable ViewsOf_System_Management_Autom function Get-ConciseViewPositionMessage { $resetColor = '' + $inverseMode = '' if ($Host.UI.SupportsVirtualTerminal) { $resetColor = ""`e[0m"" + $inverseMode = ""`e[7m"" } function Get-VT100Color([ConsoleColor] $color) { @@ -839,7 +841,7 @@ function Get-ConciseViewPositionMessage { if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { - $posmsg = ""${resetColor}At $($myinv.ScriptName)`n"" + $posmsg = ""${resetColor}In $($myinv.ScriptName)`n"" } else { $posmsg = ""`n"" @@ -856,12 +858,16 @@ function Get-ConciseViewPositionMessage { $lineWhitespace = ' ' * (4 - $scriptLineNumberLength) } - $posmsg += ""${accentColor}${headerWhitespace}Line |`n"" - $posmsg += ""${accentColor}${lineWhitespace}$($myinv.ScriptLineNumber) | ${resetcolor}$($myinv.Line)`n"" - + $verticalBar = '|' + $posmsg += ""${accentColor}${headerWhitespace}Line ${verticalBar}`n"" + $line = $myinv.Line + $highlightLine = $myinv.PositionMessage.Split('+').Count - 1 + $offsetLength = $myinv.PositionMessage.split('+')[$highlightLine].Trim().Length + $line = $line.Insert($myinv.OffsetInLine - 1 + $offsetLength, $resetColor) + $line = $line.Insert($myinv.OffsetInLine - 1, $errorColor) + $posmsg += ""${accentColor}${lineWhitespace}$($myinv.ScriptLineNumber) ${verticalBar} ${resetcolor}${line}`n"" $offsetWhitespace = ' ' * $myinv.OffsetInLine - - $posmsg += ""${accentColor}${headerWhitespace} |${offsetWhitespace}${errorColor}^ "" + $posmsg += ""${accentColor}${headerWhitespace} ${verticalBar}${offsetWhitespace}${errorColor}^ "" } if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { From deba3affd4cdeee7aeeec1b9dc5f0c60247aff8b Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 08:10:43 -0700 Subject: [PATCH 16/26] change `In` to lowercase --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index ac5a35f3564..456894e9150 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -841,7 +841,7 @@ function Get-ConciseViewPositionMessage { if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { - $posmsg = ""${resetColor}In $($myinv.ScriptName)`n"" + $posmsg = ""${resetColor}in $($myinv.ScriptName)`n"" } else { $posmsg = ""`n"" From f5738eb45cf6a0fd00654cd11a5fdb2fe86c8572 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 28 Sep 2019 08:13:53 -0700 Subject: [PATCH 17/26] add verification of line number --- test/powershell/engine/Formatting/ErrorView.Tests.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 index 153adcde8de..ff457a75525 100644 --- a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 +++ b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 @@ -37,6 +37,8 @@ Describe "Tests for `$ErrorView" -Tag CI { Set-Content -Path $testScriptPath -Value $testScript $e = { & $testScriptPath } | Should -Throw -ErrorId "UnexpectedToken" -PassThru $e | Out-String | Should -BeLike "*$testScriptPath*" + # validate line number is shown + $e | Out-String | Should -BeLike "* 4 *" } It "Remote errors show up correctly" { From fe59da57a05b3b9dda73e575b1aee6941fef09f3 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 30 Sep 2019 15:53:19 -0700 Subject: [PATCH 18/26] make the error message flow better in concise mode --- .../PowerShellCore_format_ps1xml.cs | 73 +++++++++++++++++-- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 456894e9150..52517bcdb34 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -798,10 +798,8 @@ private static IEnumerable ViewsOf_System_Management_Autom function Get-ConciseViewPositionMessage { $resetColor = '' - $inverseMode = '' if ($Host.UI.SupportsVirtualTerminal) { $resetColor = ""`e[0m"" - $inverseMode = ""`e[7m"" } function Get-VT100Color([ConsoleColor] $color) { @@ -830,6 +828,39 @@ function Get-ConciseViewPositionMessage { } } + # return length of string sans VT100 codes + function Get-RawStringLength($string) { + $vtCodes = ""`e[0m"", ""`e[2;30m"", ""`e[2;31m"", ""`e[2;32m"", ""`e[2;33m"", ""`e[2;34m"", + ""`e[2;35m"", ""`e[2;36m"", ""`e[2;37m"", ""`e[1;30m"", ""`e[1;31m"", ""`e[1;32m"", + ""`e[1;33m"", ""`e[1;34m"", ""`e[1;35m"", ""`e[1;36m"", ""`e[1;37m"" + + $newString = $string + foreach ($vtCode in $vtCodes) { + $newString = $newString.Replace($vtCode, '') + } + + return $newString.Length + } + + # returns a string cut to last whitespace + function Get-TruncatedString($string, $length) { + if ($string.Length -le $length) { + return $string + } + + if (-not $string.Contains(' ')) { + return $string.Substring(0, $length) + } + + $split = $string.Substring(0, $length).Split(' ') + if ($split.Count -gt 1) { + return [string]::Join(' ', $split, 0, $split.Count - 1) + } + else { + return $split[0] + } + } + $errorColor = '' $accentColor = '' if ($Host.PrivateData) { @@ -838,6 +869,10 @@ function Get-ConciseViewPositionMessage { } $posmsg = '' + $headerWhitespace = '' + $offsetWhitespace = '' + $message = '' + $prefix = '' if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { @@ -847,7 +882,6 @@ function Get-ConciseViewPositionMessage { $posmsg = ""`n"" } - $headerWhitespace = '' $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length if ($scriptLineNumberLength -gt 4) { $headerWhitespace = ' ' * ($scriptLineNumberLength - 4) @@ -867,21 +901,46 @@ function Get-ConciseViewPositionMessage { $line = $line.Insert($myinv.OffsetInLine - 1, $errorColor) $posmsg += ""${accentColor}${lineWhitespace}$($myinv.ScriptLineNumber) ${verticalBar} ${resetcolor}${line}`n"" $offsetWhitespace = ' ' * $myinv.OffsetInLine - $posmsg += ""${accentColor}${headerWhitespace} ${verticalBar}${offsetWhitespace}${errorColor}^ "" + $prefix = ""${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"" + $message = ""${prefix}${offsetWhitespace}^ "" } if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { if ($_.CategoryInfo.Category -eq 'ParserError') { - $posmsg += $errorColor + $_.Exception.message.split(""~`n"")[1].split(""`n`n"")[0] + $message += $_.Exception.message.split(""~`n"")[1].split(""`n`n"")[0] } else { - $posmsg += $errorColor + $_.Exception.Message + $message += $_.Exception.Message } } else { - $posmsg += $errorColor + $_.ErrorDetails.Message + $message += $_.ErrorDetails.Message + } + + # if rendering line information, break up the message if it's wider than the console + if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { + $prefixLength = Get-RawStringLength -string $prefix + $prefixVtLength = $prefix.Length - $prefixLength + if ([Console]::WindowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt [Console]::WindowWidth) { + $sb = [Text.StringBuilder]::new() + $substring = Get-TruncatedString -string $message -length ([Console]::WindowWidth + $prefixVTLength) + $null = $sb.Append($substring) + $remainingMessage = $message.Substring($substring.Length) + $null = $sb.Append([Environment]::Newline) + while (($remainingMessage.Length + $prefixLength) -gt [Console]::WindowWidth) { + $subMessage = $prefix + $remainingMessage.Substring(0, [Console]::WindowWidth - $prefixLength) + $substring = Get-TruncatedString -string $subMessage -length [Console]::WindowWidth + $null = $sb.Append($substring) + $null = $sb.Append([Environment]::Newline) + $remainingMessage = $remainingMessage.Substring($remainingMessage.Length - $substring.Length) + } + $null = $sb.Append($prefix + $remainingMessage) + $message = $sb.ToString() + } } + $posmsg += $message + $reason = 'Error' if ($_.Exception -and $_.Exception.WasThrownFromThrowStatement) { $reason = 'Exception' From 0284146d326bf7038887e49f013a1f0c09727537 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 30 Sep 2019 16:05:31 -0700 Subject: [PATCH 19/26] fix case where message is multiple lines --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 52517bcdb34..441976a5cd3 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -843,7 +843,7 @@ function Get-ConciseViewPositionMessage { } # returns a string cut to last whitespace - function Get-TruncatedString($string, $length) { + function Get-TruncatedString($string, [int]$length) { if ($string.Length -le $length) { return $string } @@ -929,7 +929,7 @@ function Get-ConciseViewPositionMessage { $null = $sb.Append([Environment]::Newline) while (($remainingMessage.Length + $prefixLength) -gt [Console]::WindowWidth) { $subMessage = $prefix + $remainingMessage.Substring(0, [Console]::WindowWidth - $prefixLength) - $substring = Get-TruncatedString -string $subMessage -length [Console]::WindowWidth + $substring = Get-TruncatedString -string $subMessage -length ([Console]::WindowWidth) $null = $sb.Append($substring) $null = $sb.Append([Environment]::Newline) $remainingMessage = $remainingMessage.Substring($remainingMessage.Length - $substring.Length) From 3500ff311b41e13a225564d3288a0d6081d31ffb Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 1 Oct 2019 08:59:50 -0700 Subject: [PATCH 20/26] fix so message is already error color --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 441976a5cd3..5c41140eee0 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -939,7 +939,7 @@ function Get-ConciseViewPositionMessage { } } - $posmsg += $message + $posmsg += ""${errorColor}"" + $message $reason = 'Error' if ($_.Exception -and $_.Exception.WasThrownFromThrowStatement) { From adee296a5a52d88edda7624cc8c7b7a97491dc84 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 1 Oct 2019 12:55:58 -0700 Subject: [PATCH 21/26] when showing error with line, replace newlines so the message lines up nicely --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 5c41140eee0..0e43f60d28a 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -921,6 +921,10 @@ function Get-ConciseViewPositionMessage { if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { $prefixLength = Get-RawStringLength -string $prefix $prefixVtLength = $prefix.Length - $prefixLength + + # replace newlines in message so it lines up correct + $message = $message.Replace([Environment]::Newline, ' ') + if ([Console]::WindowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt [Console]::WindowWidth) { $sb = [Text.StringBuilder]::new() $substring = Get-TruncatedString -string $message -length ([Console]::WindowWidth + $prefixVTLength) From 30e1bc8f66be7d776b0657bd3c0712b00e819344 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Tue, 1 Oct 2019 14:44:55 -0700 Subject: [PATCH 22/26] fix small alignment issue --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 0e43f60d28a..ac66ede7d9d 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -900,7 +900,7 @@ function Get-ConciseViewPositionMessage { $line = $line.Insert($myinv.OffsetInLine - 1 + $offsetLength, $resetColor) $line = $line.Insert($myinv.OffsetInLine - 1, $errorColor) $posmsg += ""${accentColor}${lineWhitespace}$($myinv.ScriptLineNumber) ${verticalBar} ${resetcolor}${line}`n"" - $offsetWhitespace = ' ' * $myinv.OffsetInLine + $offsetWhitespace = ' ' * ($myinv.OffsetInLine - 1) $prefix = ""${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"" $message = ""${prefix}${offsetWhitespace}^ "" } @@ -929,7 +929,7 @@ function Get-ConciseViewPositionMessage { $sb = [Text.StringBuilder]::new() $substring = Get-TruncatedString -string $message -length ([Console]::WindowWidth + $prefixVTLength) $null = $sb.Append($substring) - $remainingMessage = $message.Substring($substring.Length) + $remainingMessage = $message.Substring($substring.Length).Trim() $null = $sb.Append([Environment]::Newline) while (($remainingMessage.Length + $prefixLength) -gt [Console]::WindowWidth) { $subMessage = $prefix + $remainingMessage.Substring(0, [Console]::WindowWidth - $prefixLength) From 4066273eb83401a6facafe7057adc8632cbc2c09 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Tue, 1 Oct 2019 16:48:48 -0700 Subject: [PATCH 23/26] fix some rendering of parser messages --- .../PowerShellCore_format_ps1xml.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index ac66ede7d9d..9c63567fd59 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -873,13 +873,14 @@ function Get-ConciseViewPositionMessage { $offsetWhitespace = '' $message = '' $prefix = '' + $newline = [Environment]::Newline if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { - $posmsg = ""${resetColor}in $($myinv.ScriptName)`n"" + $posmsg = ""${resetColor}error in $($myinv.ScriptName)${newline}"" } else { - $posmsg = ""`n"" + $posmsg = ""${newline}"" } $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length @@ -893,7 +894,7 @@ function Get-ConciseViewPositionMessage { } $verticalBar = '|' - $posmsg += ""${accentColor}${headerWhitespace}Line ${verticalBar}`n"" + $posmsg += ""${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"" $line = $myinv.Line $highlightLine = $myinv.PositionMessage.Split('+').Count - 1 $offsetLength = $myinv.PositionMessage.split('+')[$highlightLine].Trim().Length @@ -906,8 +907,10 @@ function Get-ConciseViewPositionMessage { } if (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) { - if ($_.CategoryInfo.Category -eq 'ParserError') { - $message += $_.Exception.message.split(""~`n"")[1].split(""`n`n"")[0] + # we use `n instead of $newline here because that's what is in the message + if ($_.CategoryInfo.Category -eq 'ParserError' -and $_.Exception.Message.Contains(""~`n"")) { + # need to parse out the relevant part of the pre-rendered positionmessage + $message += $_.Exception.Message.split(""~`n"")[1].split(""${newline}${newline}"")[0] } else { $message += $_.Exception.Message @@ -923,22 +926,21 @@ function Get-ConciseViewPositionMessage { $prefixVtLength = $prefix.Length - $prefixLength # replace newlines in message so it lines up correct - $message = $message.Replace([Environment]::Newline, ' ') - + $message = $message.Replace($newline, ' ') if ([Console]::WindowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt [Console]::WindowWidth) { $sb = [Text.StringBuilder]::new() $substring = Get-TruncatedString -string $message -length ([Console]::WindowWidth + $prefixVTLength) $null = $sb.Append($substring) $remainingMessage = $message.Substring($substring.Length).Trim() - $null = $sb.Append([Environment]::Newline) + $null = $sb.Append($newline) while (($remainingMessage.Length + $prefixLength) -gt [Console]::WindowWidth) { $subMessage = $prefix + $remainingMessage.Substring(0, [Console]::WindowWidth - $prefixLength) $substring = Get-TruncatedString -string $subMessage -length ([Console]::WindowWidth) $null = $sb.Append($substring) - $null = $sb.Append([Environment]::Newline) - $remainingMessage = $remainingMessage.Substring($remainingMessage.Length - $substring.Length) + $null = $sb.Append($newline) + $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length) } - $null = $sb.Append($prefix + $remainingMessage) + $null = $sb.Append($prefix + $remainingMessage.Trim()) $message = $sb.ToString() } } From ba92741ff6d0c6b225045f99d3364016d455cb05 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Tue, 1 Oct 2019 17:53:58 -0700 Subject: [PATCH 24/26] make `error in` text in error color, fix rendering of long error messages where the width didn't take into account vt100 code characters --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 9c63567fd59..c02b079feac 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -877,7 +877,7 @@ function Get-ConciseViewPositionMessage { if ($myinv -and $myinv.ScriptName -or $_.CategoryInfo.Category -eq 'ParserError') { if ($myinv.ScriptName) { - $posmsg = ""${resetColor}error in $($myinv.ScriptName)${newline}"" + $posmsg = ""error in${resetcolor} $($myinv.ScriptName)${newline}"" } else { $posmsg = ""${newline}"" @@ -934,11 +934,11 @@ function Get-ConciseViewPositionMessage { $remainingMessage = $message.Substring($substring.Length).Trim() $null = $sb.Append($newline) while (($remainingMessage.Length + $prefixLength) -gt [Console]::WindowWidth) { - $subMessage = $prefix + $remainingMessage.Substring(0, [Console]::WindowWidth - $prefixLength) - $substring = Get-TruncatedString -string $subMessage -length ([Console]::WindowWidth) + $subMessage = $prefix + $remainingMessage + $substring = Get-TruncatedString -string $subMessage -length ([Console]::WindowWidth + $prefixVtLength) $null = $sb.Append($substring) $null = $sb.Append($newline) - $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length) + $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim() } $null = $sb.Append($prefix + $remainingMessage.Trim()) $message = $sb.ToString() @@ -966,7 +966,7 @@ function Get-ConciseViewPositionMessage { $errorMsg = 'Error' - ""${errorColor}${reason}:${resetColor} ${posmsg}${resetcolor}"" + ""${errorColor}${reason}: ${posmsg}${resetcolor}"" } if ($_.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $_.FullyQualifiedErrorId -eq 'NativeCommandError') { From 49518063577fbcd8fa54950071f0c31cb0a81b17 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 4 Oct 2019 13:33:06 -0700 Subject: [PATCH 25/26] Update test/powershell/Host/ConsoleHost.Tests.ps1 Co-Authored-By: James Truher [MSFT] --- test/powershell/Host/ConsoleHost.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 39864e4d3d6..d1b83f12907 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -393,7 +393,7 @@ export $envVarName='$guid' It "errors are in text if error is redirected, encoded command, non-interactive, and outputformat specified" { $p = [Diagnostics.Process]::new() $p.StartInfo.FileName = "pwsh" - $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("`$ErrorView='NormalView';throw 'boom'")) + $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('$ErrorView="NormalView";throw "boom"')) $p.StartInfo.Arguments = "-EncodedCommand $encoded -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -OutputFormat text" $p.StartInfo.UseShellExecute = $false $p.StartInfo.RedirectStandardError = $true From f03a0a1d7cc3e1ba9365c25d5cb391291f7204af Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL)" Date: Fri, 4 Oct 2019 15:51:34 -0700 Subject: [PATCH 26/26] address Jim's comments --- .../PowerShellCore_format_ps1xml.cs | 68 ++++++++++--------- .../engine/Formatting/ErrorView.Tests.ps1 | 22 +++--- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index c02b079feac..f00d1871e9e 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -743,7 +743,7 @@ private static IEnumerable ViewsOf_System_Management_Autom CustomControl.Create(outOfBand: true) .StartEntry() .AddScriptBlockExpressionBinding(@" - if (($_.FullyQualifiedErrorId -ne 'NativeCommandErrorMessage' -and $_.FullyQualifiedErrorId -ne 'NativeCommandError') -and $ErrorView -ne 'CategoryView' -and $ErrorView -ne 'ConciseView') + if (@('NativeCommandErrorMessage'.'NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView') -notcontains $ErrorView) { $myinv = $_.InvocationInfo if ($myinv -and $myinv.MyCommand) @@ -802,30 +802,31 @@ function Get-ConciseViewPositionMessage { $resetColor = ""`e[0m"" } - function Get-VT100Color([ConsoleColor] $color) { + function Get-VT100Color([string] $color) { if (! $Host.UI.SupportsVirtualTerminal) { return '' } - switch ($color) { - 'Black' { ""`e[2;30m"" } - 'DarkRed' { ""`e[2;31m"" } - 'DarkGreen' { ""`e[2;32m"" } - 'DarkYellow' { ""`e[2;33m"" } - 'DarkBlue' { ""`e[2;34m"" } - 'DarkMagenta' { ""`e[2;35m"" } - 'DarkCyan' { ""`e[2;36m"" } - 'Gray' { ""`e[2;37m"" } - 'DarkGray' { ""`e[1;30m"" } - 'Red' { ""`e[1;31m"" } - 'Green' { ""`e[1;32m"" } - 'Yellow' { ""`e[1;33m"" } - 'Blue' { ""`e[1;34m"" } - 'Magenta' { ""`e[1;35m"" } - 'Cyan' { ""`e[1;36m"" } - 'White' { ""`e[1;37m"" } - default { ""`e[1;31m"" } + $colors = @{ + 'Black' = ""`e[2;30m"" + 'DarkRed' = ""`e[2;31m"" + 'DarkGreen' = ""`e[2;32m"" + 'DarkYellow' = ""`e[2;33m"" + 'DarkBlue' = ""`e[2;34m"" + 'DarkMagenta' = ""`e[2;35m"" + 'DarkCyan' = ""`e[2;36m"" + 'Gray' = ""`e[2;37m"" + 'DarkGray' = ""`e[1;30m"" + 'Red' = ""`e[1;31m"" + 'Green' = ""`e[1;32m"" + 'Yellow' = ""`e[1;33m"" + 'Blue' = ""`e[1;34m"" + 'Magenta' = ""`e[1;35m"" + 'Cyan' = ""`e[1;36m"" + 'White' = ""`e[1;37m"" } + + return $colors[$color] } # return length of string sans VT100 codes @@ -844,21 +845,24 @@ function Get-ConciseViewPositionMessage { # returns a string cut to last whitespace function Get-TruncatedString($string, [int]$length) { + if ($string.Length -le $length) { return $string } - if (-not $string.Contains(' ')) { - return $string.Substring(0, $length) - } + return ($string.Substring(0,$length) -split '\s',-2)[0] - $split = $string.Substring(0, $length).Split(' ') - if ($split.Count -gt 1) { - return [string]::Join(' ', $split, 0, $split.Count - 1) - } - else { - return $split[0] - } + #if (-not $string.Contains(' ')) { + # return $string.Substring(0, $length) + #} + + #$split = $string.Substring(0, $length).Split(' ') + #if ($split.Count -gt 1) { + # return [string]::Join(' ', $split, 0, $split.Count - 1) + #} + #else { + # return $split[0] + #} } $errorColor = '' @@ -926,7 +930,7 @@ function Get-ConciseViewPositionMessage { $prefixVtLength = $prefix.Length - $prefixLength # replace newlines in message so it lines up correct - $message = $message.Replace($newline, ' ') + $message = $message.Replace($newline, ' ').Replace(""`t"", ' ') if ([Console]::WindowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt [Console]::WindowWidth) { $sb = [Text.StringBuilder]::new() $substring = Get-TruncatedString -string $message -length ([Console]::WindowWidth + $prefixVTLength) @@ -943,6 +947,8 @@ function Get-ConciseViewPositionMessage { $null = $sb.Append($prefix + $remainingMessage.Trim()) $message = $sb.ToString() } + + $message += $newline } $posmsg += ""${errorColor}"" + $message diff --git a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 index ff457a75525..2865757f072 100644 --- a/test/powershell/engine/Formatting/ErrorView.Tests.ps1 +++ b/test/powershell/engine/Formatting/ErrorView.Tests.ps1 @@ -1,30 +1,30 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -Describe "Tests for `$ErrorView" -Tag CI { +Describe 'Tests for $ErrorView' -Tag CI { - It "`$ErrorView is an enum" { + It '$ErrorView is an enum' { $ErrorView | Should -BeOfType [System.Management.Automation.ErrorView] } - It "`$ErrorView should have correct default value" { - $expectedDefault = "NormalView" + It '$ErrorView should have correct default value' { + $expectedDefault = 'NormalView' if ((Get-ExperimentalFeature -Name PSErrorView).Enabled) { - $expectedDefault = "ConciseView" + $expectedDefault = 'ConciseView' } $ErrorView | Should -BeExactly $expectedDefault } - Context "ConciseView tests" { + Context 'ConciseView tests' { - It "Cmdlet error should be one line of text" { + It 'Cmdlet error should be one line of text' { Get-Item (New-Guid) -ErrorVariable e -ErrorAction SilentlyContinue ($e | Out-String).Trim().Count | Should -Be 1 } - It "Script error should contain path to script and line for error" { + It 'Script error should contain path to script and line for error' { $testScript = @' [cmdletbinding()] param() @@ -33,12 +33,12 @@ Describe "Tests for `$ErrorView" -Tag CI { $b = 2 '@ - $testScriptPath = Join-Path -Path $TestDrive -ChildPath "test.ps1" + $testScriptPath = Join-Path -Path $TestDrive -ChildPath 'test.ps1' Set-Content -Path $testScriptPath -Value $testScript - $e = { & $testScriptPath } | Should -Throw -ErrorId "UnexpectedToken" -PassThru + $e = { & $testScriptPath } | Should -Throw -ErrorId 'UnexpectedToken' -PassThru $e | Out-String | Should -BeLike "*$testScriptPath*" # validate line number is shown - $e | Out-String | Should -BeLike "* 4 *" + $e | Out-String | Should -BeLike '* 4 *' } It "Remote errors show up correctly" {