Skip to content

Commit f9abe85

Browse files
committed
This is ConciseView
1 parent 9ace773 commit f9abe85

File tree

2 files changed

+193
-47
lines changed

2 files changed

+193
-47
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
filter GetConciseViewPositionMessage {
2+
[CmdletBinding()]
3+
param(
4+
[Parameter(ValueFromPipeline)]
5+
[System.Management.Automation.ErrorRecord]
6+
$InputObject
7+
)
8+
$err = $InputObject
9+
$posmsg = ''
10+
$headerWhitespace = ''
11+
$offsetWhitespace = ''
12+
$message = ''
13+
$prefix = ''
14+
15+
# Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
16+
# Note that in some versions, this is a Dictionary<,> and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
17+
# to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
18+
$useTargetObject = $null -ne $err.TargetObject -and
19+
$err.TargetObject -is [System.Collections.IDictionary] -and
20+
([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
21+
([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
22+
23+
# The checks here determine if we show line detailed error information:
24+
# - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
25+
$isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
26+
$err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
27+
28+
# - check if invocation is a script or multiple lines in the console
29+
$isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
30+
31+
# - check that it's not a script module as expectation is that users don't want to see the line of error within a module
32+
$shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
33+
$myinv.ScriptName -notmatch '\.psm1$'
34+
35+
if ($useTargetObject -or $shouldShowLineDetail) {
36+
37+
if ($useTargetObject) {
38+
$posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
39+
} elseif ($myinv.ScriptName) {
40+
if ($env:TERM_PROGRAM -eq 'vscode') {
41+
# If we are running in vscode, we know the file:line:col links are clickable so we use this format
42+
$posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
43+
} else {
44+
$posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
45+
}
46+
} else {
47+
$posmsg = "${newline}"
48+
}
49+
50+
if ($useTargetObject) {
51+
$scriptLineNumber = $err.TargetObject.Line
52+
$scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
53+
} else {
54+
$scriptLineNumber = $myinv.ScriptLineNumber
55+
$scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
56+
}
57+
58+
if ($scriptLineNumberLength -gt 4) {
59+
$headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
60+
}
61+
62+
$lineWhitespace = ''
63+
if ($scriptLineNumberLength -lt 4) {
64+
$lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
65+
}
66+
67+
$verticalBar = '|'
68+
$posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
69+
70+
$highlightLine = ''
71+
if ($useTargetObject) {
72+
$line = $_.TargetObject.LineText.Trim()
73+
$offsetLength = 0
74+
$offsetInLine = 0
75+
} else {
76+
$positionMessage = $myinv.PositionMessage.Split($newline)
77+
$line = $positionMessage[1].Substring(1) # skip the '+' at the start
78+
$highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
79+
$offsetLength = $highlightLine.Trim().Length
80+
$offsetInLine = $highlightLine.IndexOf('~')
81+
}
82+
83+
if (-not $line.EndsWith($newline)) {
84+
$line += $newline
85+
}
86+
87+
# don't color the whole line
88+
if ($offsetLength -lt $line.Length - 1) {
89+
$line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
90+
}
91+
92+
$posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
93+
$offsetWhitespace = ' ' * $offsetInLine
94+
$prefix = "${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"
95+
if ($highlightLine -ne '') {
96+
$posMsg += "${prefix}${highlightLine}${newline}"
97+
}
98+
$message = "${prefix}"
99+
}
100+
101+
if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
102+
if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
103+
# need to parse out the relevant part of the pre-rendered positionmessage
104+
$message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
105+
} elseif ($err.Exception) {
106+
$message += $err.Exception.Message
107+
} elseif ($err.Message) {
108+
$message += $err.Message
109+
} else {
110+
$message += $err.ToString()
111+
}
112+
} else {
113+
$message += $err.ErrorDetails.Message
114+
}
115+
116+
# if rendering line information, break up the message if it's wider than the console
117+
if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
118+
$prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
119+
$prefixVtLength = $prefix.Length - $prefixLength
120+
121+
# replace newlines in message so it lines up correct
122+
$message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
123+
124+
$windowWidth = 120
125+
if ($Host.UI.RawUI -ne $null) {
126+
$windowWidth = $Host.UI.RawUI.WindowSize.Width
127+
}
128+
129+
if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
130+
$sb = [Text.StringBuilder]::new()
131+
$substring = TruncateString -string $message -length ($windowWidth + $prefixVTLength)
132+
$null = $sb.Append($substring)
133+
$remainingMessage = $message.Substring($substring.Length).Trim()
134+
$null = $sb.Append($newline)
135+
while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
136+
$subMessage = $prefix + $remainingMessage
137+
$substring = TruncateString -string $subMessage -length ($windowWidth + $prefixVtLength)
138+
139+
if ($substring.Length - $prefix.Length -gt 0) {
140+
$null = $sb.Append($substring)
141+
$null = $sb.Append($newline)
142+
$remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
143+
} else {
144+
break
145+
}
146+
}
147+
$null = $sb.Append($prefix + $remainingMessage.Trim())
148+
$message = $sb.ToString()
149+
}
150+
151+
$message += $newline
152+
}
153+
154+
$posmsg += "${errorColor}" + $message
155+
156+
$reason = 'Error'
157+
if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
158+
$reason = 'Exception'
159+
# MyCommand can be the script block, so we don't want to show that so check if it's an actual command
160+
} elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore)) {
161+
$reason = $myinv.MyCommand
162+
} elseif ($err.CategoryInfo.Activity) {
163+
# If it's a scriptblock, better to show the command in the scriptblock that had the error
164+
$reason = $err.CategoryInfo.Activity
165+
} elseif ($myinv.MyCommand) {
166+
$reason = $myinv.MyCommand
167+
} elseif ($myinv.InvocationName) {
168+
$reason = $myinv.InvocationName
169+
} elseif ($err.CategoryInfo.Category) {
170+
$reason = $err.CategoryInfo.Category
171+
} elseif ($err.CategoryInfo.Reason) {
172+
$reason = $err.CategoryInfo.Reason
173+
}
174+
175+
"${errorColor}${reason}: ${posmsg}${resetcolor}"
176+
}
Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,32 @@
11
function ConvertTo-ConciseErrorView {
22
[CmdletBinding()]
33
param(
4+
[Parameter(ValueFromPipeline)]
45
[System.Management.Automation.ErrorRecord]
56
$InputObject
67
)
7-
if ("$accentColor".Length) {
8-
$local:accentColor = $script:errorAccentColor
9-
$local:resetColor = $script:resetColor
10-
} else {
11-
$local:accentColor = ">>>"
12-
$local:resetColor = "<<<"
13-
}
14-
15-
16-
if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
17-
$errorColor + $InputObject.Exception.Message + $resetColor
18-
} else {
19-
$myinv = $InputObject.InvocationInfo
20-
if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
21-
# rip off lines that say "At line:1 char:1" (hopefully, in a language agnostic way)
22-
$posmsg = $myinv.PositionMessage -replace "^At line:1 char:1[\r\n]+"
23-
24-
# rip off the underline and use the accentcolor instead
25-
$pattern = $posmsg -split "[\r\n]+" -match "\+( +~+)\s*" -replace '(~+)', '($1)' -replace '( +)','($1)' -replace '~| ','.'
26-
$posmsg = $posmsg -replace '[\r\n]+\+ +~+'
27-
if ($pattern) {
28-
$posmsg = $posmsg -replace "\+$pattern", "+`$1$accentColor`$2$resetColor"
29-
}
8+
begin { ResetColor }
9+
process {
10+
if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
11+
"${errorColor}$($InputObject.Exception.Message)${resetColor}"
3012
} else {
31-
$posmsg = ""
32-
}
33-
34-
if ($posmsg -ne "") {
35-
$posmsg = "`n" + $posmsg
36-
}
13+
if (!"$accentColor".Length) {
14+
$local:accentColor = ">>>"
15+
$local:resetColor = "<<<"
16+
}
3717

38-
if ( & { Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
39-
$posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
40-
}
18+
$message = GetConciseViewPositionMessage -InputObject $InputObject
4119

42-
$indent = 4
43-
$width = $host.UI.RawUI.BufferSize.Width - $indent - 2
20+
if ($InputObject.PSMessageDetails) {
21+
$message = $errorColor + ' : ' + $InputObject.PSMessageDetails + $message
22+
}
4423

45-
$originInfo = & { Set-StrictMode -Version 1; $InputObject.OriginInfo }
46-
if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
47-
$indentString = "+ PSComputerName : " + $originInfo.PSComputerName
48-
$posmsg += "`n"
49-
foreach ($line in @($indentString -split "(.{$width})")) {
50-
if ($line) {
51-
$posmsg += (" " * $indent + $line)
52-
}
24+
$recommendedAction = $InputObject.ErrorDetails.RecommendedAction
25+
if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
26+
$message = $message + $newline + ${errorColor} + ' Recommendation: ' + $recommendedAction + ${resetcolor}
5327
}
54-
}
5528

56-
if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
57-
$errorColor + $InputObject.Exception.Message + $resetColor + $posmsg + "`n "
58-
} else {
59-
$errorColor + $InputObject.ErrorDetails.Message + $resetColor + $posmsg
29+
$message
6030
}
6131
}
6232
}

0 commit comments

Comments
 (0)