diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 94cb4da..f7b7d82 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -20,6 +20,10 @@ jobs:
submodules: true
fetch-depth: 0
+ - name: earthly +build
+ if: github.ref != 'refs/heads/main'
+ run: earthly --strict +build
+
- name: earthly +test
if: github.ref != 'refs/heads/main'
run: earthly --strict +test
@@ -34,18 +38,18 @@ jobs:
- uses: actions/upload-artifact@v4
with:
- name: ModuleBuilder
- path: Modules/ModuleBuilder
+ name: ErrorView
+ path: Modules/ErrorView
- uses: actions/upload-artifact@v4
with:
name: TestResults
- path: Modules/ModuleBuilder-TestResults
+ path: Modules/ErrorView-TestResults
- uses: actions/upload-artifact@v4
with:
name: Packages
- path: Modules/ModuleBuilder-Packages
+ path: Modules/ErrorView-Packages
- name: Upload Tests
uses: actions/upload-artifact@v4
@@ -68,8 +72,8 @@ jobs:
- name: Download Build Output
uses: actions/download-artifact@v4
with:
- name: ModuleBuilder
- path: Modules/ModuleBuilder
+ name: ErrorView
+ path: Modules/ErrorView
- name: Download Pester Tests
uses: actions/download-artifact@v4
with:
@@ -83,15 +87,15 @@ jobs:
- uses: PoshCode/Actions/install-requiredmodules@v1
- uses: PoshCode/Actions/pester@v1
with:
- codeCoveragePath: Modules/ModuleBuilder
- moduleUnderTest: ModuleBuilder
+ codeCoveragePath: Modules/ErrorView
+ moduleUnderTest: ErrorView
additionalModulePaths: ${{github.workspace}}/Modules
- name: Publish Test Results
uses: zyborg/dotnet-tests-report@v1
with:
test_results_path: results.xml
- name: Upload Results
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: Pester Results
path: ${{github.workspace}}/*.xml
diff --git a/ErrorView.code-workspace b/ErrorView.code-workspace
new file mode 100644
index 0000000..0f707bf
--- /dev/null
+++ b/ErrorView.code-workspace
@@ -0,0 +1,14 @@
+{
+ "folders": [
+ {
+ "name": "ErrorView",
+ "path": "."
+ },
+ {
+ "path": "../../Tasks"
+ }
+ ],
+ "settings": {
+ "powershell.cwd": "."
+ }
+}
\ No newline at end of file
diff --git a/Reference/FileInfo.format.ps1xml b/Reference/FileInfo.format.ps1xml
new file mode 100644
index 0000000..745a045
--- /dev/null
+++ b/Reference/FileInfo.format.ps1xml
@@ -0,0 +1,206 @@
+
+
+
+
+ children
+
+ System.IO.DirectoryInfo
+
+
+ PSParentPath
+
+
+
+
+
+ 7
+ Left
+
+
+
+ 26
+ Right
+
+
+
+ 14
+ Right
+
+
+
+ Left
+
+
+
+
+
+
+
+ ModeWithoutHardLink
+
+
+ LastWriteTimeString
+
+
+ LengthString
+
+
+ NameString
+
+
+
+
+
+
+
+ childrenWithHardlink
+
+ System.IO.DirectoryInfo
+
+
+ PSParentPath
+
+
+
+
+
+ 7
+ Left
+
+
+
+ 26
+ Right
+
+
+
+ 14
+ Right
+
+
+
+ Left
+
+
+
+
+
+
+
+ Mode
+
+
+ LastWriteTimeString
+
+
+ LengthString
+
+
+ NameString
+
+
+
+
+
+
+
+ children
+
+ System.IO.DirectoryInfo
+
+
+ PSParentPath
+
+
+
+
+
+
+ Name
+
+
+ CreationTime
+
+
+ LastWriteTime
+
+
+ LastAccessTime
+
+
+ Mode
+
+
+ LinkType
+
+
+ Target
+
+
+
+
+
+ System.IO.FileInfo
+
+
+
+ Name
+
+
+
+ LengthString
+
+
+ CreationTime
+
+
+ LastWriteTime
+
+
+ LastAccessTime
+
+
+ Mode
+
+
+ LinkType
+
+
+ Target
+
+
+ VersionInfo
+
+
+
+
+
+
+
+ children
+
+ System.IO.DirectoryInfo
+
+
+ PSParentPath
+
+
+
+
+
+ Name
+
+
+
+
+ System.IO.DirectoryInfo
+
+
+ Name
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Reference/LegacyErrorView.format.ps1xml b/Reference/LegacyErrorView.format.ps1xml
new file mode 100644
index 0000000..fde1bd3
--- /dev/null
+++ b/Reference/LegacyErrorView.format.ps1xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Reference/NativeCommandError.ps1 b/Reference/NativeCommandError.ps1
new file mode 100644
index 0000000..48f7007
--- /dev/null
+++ b/Reference/NativeCommandError.ps1
@@ -0,0 +1,56 @@
+$errorColor = ''
+$commandPrefix = ''
+if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
+{
+ $myinv = $_.InvocationInfo
+ if ($Host.UI.SupportsVirtualTerminal) {
+ $errorColor = $PSStyle.Formatting.Error
+ }
+
+ $commandPrefix = if ($myinv -and $myinv.MyCommand) {
+ switch -regex ( $myinv.MyCommand.CommandType )
+ {
+ ([System.Management.Automation.CommandTypes]::ExternalScript)
+ {
+ if ($myinv.MyCommand.Path)
+ {
+ $myinv.MyCommand.Path + ' : '
+ }
+
+ break
+ }
+
+ ([System.Management.Automation.CommandTypes]::Script)
+ {
+ if ($myinv.MyCommand.ScriptBlock)
+ {
+ $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+ }
+
+ break
+ }
+ default
+ {
+ if ($myinv.InvocationName -match '^[&\.]?$')
+ {
+ if ($myinv.MyCommand.Name)
+ {
+ $myinv.MyCommand.Name + ' : '
+ }
+ }
+ else
+ {
+ $myinv.InvocationName + ' : '
+ }
+
+ break
+ }
+ }
+ }
+ elseif ($myinv -and $myinv.InvocationName)
+ {
+ $myinv.InvocationName + ' : '
+ }
+}
+
+$errorColor + $commandPrefix
\ No newline at end of file
diff --git a/Reference/OriginalErrorView.format.ps1xml b/Reference/OriginalErrorView.format.ps1xml
new file mode 100644
index 0000000..835a083
--- /dev/null
+++ b/Reference/OriginalErrorView.format.ps1xml
@@ -0,0 +1,600 @@
+
+
+
+
+ GetErrorInstance
+
+ System.Management.Automation.ErrorRecord#PSExtendedError
+
+
+ PSErrorIndex
+
+
+
+
+
+
+
+
+ Set-StrictMode -Off
+
+ $maxDepth = 10
+ $ellipsis = "`u{2026}"
+ $resetColor = ''
+ $errorColor = ''
+ $accentColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = $PSStyle.Reset
+ $errorColor = $psstyle.Formatting.Error
+ $accentColor = $PSStyle.Formatting.FormatAccent
+ }
+
+ function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+ $newline = [Environment]::Newline
+ $output = [System.Text.StringBuilder]::new()
+ $prefix = ' ' * $indent
+
+ $expandTypes = @(
+ 'Microsoft.Rest.HttpRequestMessageWrapper'
+ 'Microsoft.Rest.HttpResponseMessageWrapper'
+ 'System.Management.Automation.InvocationInfo'
+ )
+
+ # if object is an Exception, add an ExceptionType property
+ if ($obj -is [Exception]) {
+ $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
+ }
+
+ # first find the longest property so we can indent properly
+ $propLength = 0
+ foreach ($prop in $obj.PSObject.Properties) {
+ if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+ $propLength = $prop.Name.Length
+ }
+ }
+
+ $addedProperty = $false
+ foreach ($prop in $obj.PSObject.Properties) {
+
+ # don't show empty properties or our added property for $error[index]
+ if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+ $addedProperty = $true
+ $null = $output.Append($prefix)
+ $null = $output.Append($accentColor)
+ $null = $output.Append($prop.Name)
+ $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
+ $null = $output.Append($propNameIndent)
+ $null = $output.Append(' : ')
+ $null = $output.Append($resetColor)
+
+ $newIndent = $indent + 4
+
+ # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+ if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+ $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $null = $output.Append($newline)
+ $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
+ }
+ }
+ # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+ elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $targetSite = [PSCustomObject]@{
+ Name = $prop.Value.Name
+ DeclaringType = $prop.Value.DeclaringType
+ MemberType = $prop.Value.MemberType
+ Module = $prop.Value.Module
+ }
+
+ $null = $output.Append($newline)
+ $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
+ }
+ }
+ # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+ elseif ($prop.Name -eq 'StackTrace') {
+ # for a stacktrace which is usually quite wide with info, we left justify it
+ $null = $output.Append($newline)
+ $null = $output.Append($prop.Value)
+ }
+ # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+ elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+ $isFirstElement = $true
+ foreach ($key in $prop.Value.Keys) {
+ if ($isFirstElement) {
+ $null = $output.Append($newline)
+ }
+
+ if ($key -eq 'Authorization') {
+ $null = $output.Append("${prefix} ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
+ }
+ else {
+ $null = $output.Append("${prefix} ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
+ }
+
+ $isFirstElement = $false
+ }
+ }
+ # if the object implements IEnumerable and not a string, we try to show each object
+ # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+ elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $isFirstElement = $true
+ foreach ($value in $prop.Value) {
+ $null = $output.Append($newline)
+ $valueIndent = ' ' * ($newIndent + 2)
+
+ if ($value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("${prefix}${valueIndent}[$($value.ToString())]")
+ }
+ elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+ $null = $output.Append("${prefix}${valueIndent}${value}")
+ }
+ else {
+ if (!$isFirstElement) {
+ $null = $output.Append($newline)
+ }
+ $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
+ }
+ $isFirstElement = $false
+ }
+ }
+ }
+ elseif ($prop.Value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("[$($prop.Value.ToString())]")
+ }
+ # Anything else, we convert to string.
+ # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+ else {
+ $value = $null
+ if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
+ {
+ if ($prop.Name -eq 'PositionMessage') {
+ $value = $value.Insert($value.IndexOf('~'), $errorColor)
+ }
+ elseif ($prop.Name -eq 'Message') {
+ $value = $errorColor + $value
+ }
+
+ $isFirstLine = $true
+ if ($value.Contains($newline)) {
+ # the 3 is to account for ' : '
+ $valueIndent = ' ' * ($propLength + 3)
+ # need to trim any extra whitespace already in the text
+ foreach ($line in $value.Split($newline)) {
+ if (!$isFirstLine) {
+ $null = $output.Append("${newline}${prefix}${valueIndent}")
+ }
+ $null = $output.Append($line.Trim())
+ $isFirstLine = $false
+ }
+ }
+ else {
+ $null = $output.Append($value)
+ }
+ }
+ }
+
+ $null = $output.Append($newline)
+ }
+ }
+
+ # if we had added nested properties, we need to remove the last newline
+ if ($addedProperty) {
+ $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+ }
+
+ $output.ToString()
+ }
+
+ # Add back original typename and remove PSExtendedError
+ if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
+ $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
+ $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
+ }
+ elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
+ $_.PSObject.TypeNames.Add('System.Exception')
+ $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
+ }
+
+ Show-ErrorRecord $_
+
+
+
+
+
+
+
+
+ ErrorInstance
+
+ System.Management.Automation.ErrorRecord
+
+
+
+
+
+
+
+ $errorColor = ''
+ $commandPrefix = ''
+ if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
+ {
+ $myinv = $_.InvocationInfo
+ if ($Host.UI.SupportsVirtualTerminal) {
+ $errorColor = $PSStyle.Formatting.Error
+ }
+
+ $commandPrefix = if ($myinv -and $myinv.MyCommand) {
+ switch -regex ( $myinv.MyCommand.CommandType )
+ {
+ ([System.Management.Automation.CommandTypes]::ExternalScript)
+ {
+ if ($myinv.MyCommand.Path)
+ {
+ $myinv.MyCommand.Path + ' : '
+ }
+
+ break
+ }
+
+ ([System.Management.Automation.CommandTypes]::Script)
+ {
+ if ($myinv.MyCommand.ScriptBlock)
+ {
+ $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+ }
+
+ break
+ }
+ default
+ {
+ if ($myinv.InvocationName -match '^[&\.]?$')
+ {
+ if ($myinv.MyCommand.Name)
+ {
+ $myinv.MyCommand.Name + ' : '
+ }
+ }
+ else
+ {
+ $myinv.InvocationName + ' : '
+ }
+
+ break
+ }
+ }
+ }
+ elseif ($myinv -and $myinv.InvocationName)
+ {
+ $myinv.InvocationName + ' : '
+ }
+ }
+
+ $errorColor + $commandPrefix
+
+
+ Set-StrictMode -Off
+ $ErrorActionPreference = 'Stop'
+ trap { 'Error found in error view definition: ' + $_.Exception.Message }
+ $newline = [Environment]::Newline
+
+ $resetColor = ''
+ $errorColor = ''
+ $accentColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = $PSStyle.Reset
+ $errorColor = $PSStyle.Formatting.Error
+ $accentColor = $PSStyle.Formatting.ErrorAccent
+ }
+
+ function Get-ConciseViewPositionMessage {
+
+ # returns a string cut to last whitespace
+ function Get-TruncatedString($string, [int]$length) {
+
+ if ($string.Length -le $length) {
+ return $string
+ }
+
+ return ($string.Substring(0,$length) -split '\s',-2)[0]
+ }
+
+ $posmsg = ''
+ $headerWhitespace = ''
+ $offsetWhitespace = ''
+ $message = ''
+ $prefix = ''
+
+ # 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
+ # 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
+ # 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.
+ $useTargetObject = $null -ne $err.TargetObject -and
+ $err.TargetObject -is [System.Collections.IDictionary] -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
+ # The checks here determine if we show line detailed error information:
+ # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+ $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+ $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
+ # - check if invocation is a script or multiple lines in the console
+ $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
+ # - 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
+ $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+ $myinv.ScriptName -notmatch '\.psm1$'
+
+ if ($useTargetObject -or $shouldShowLineDetail) {
+
+ if ($useTargetObject) {
+ $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
+ }
+ elseif ($myinv.ScriptName) {
+ if ($env:TERM_PROGRAM -eq 'vscode') {
+ # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+ }
+ else {
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+ }
+ }
+ else {
+ $posmsg = "${newline}"
+ }
+
+ if ($useTargetObject) {
+ $scriptLineNumber = $err.TargetObject.Line
+ $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
+ }
+ else {
+ $scriptLineNumber = $myinv.ScriptLineNumber
+ $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+ }
+
+ if ($scriptLineNumberLength -gt 4) {
+ $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+ }
+
+ $lineWhitespace = ''
+ if ($scriptLineNumberLength -lt 4) {
+ $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+ }
+
+ $verticalBar = '|'
+ $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+ $highlightLine = ''
+ if ($useTargetObject) {
+ $line = $_.TargetObject.LineText.Trim()
+ $offsetLength = 0
+ $offsetInLine = 0
+ }
+ else {
+ $positionMessage = $myinv.PositionMessage.Split($newline)
+ $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+ $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+ $offsetLength = $highlightLine.Trim().Length
+ $offsetInLine = $highlightLine.IndexOf('~')
+ }
+
+ if (-not $line.EndsWith($newline)) {
+ $line += $newline
+ }
+
+ # don't color the whole line
+ if ($offsetLength -lt $line.Length - 1) {
+ $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+ }
+
+ $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+ $offsetWhitespace = ' ' * $offsetInLine
+ $prefix = "${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"
+ if ($highlightLine -ne '') {
+ $posMsg += "${prefix}${highlightLine}${newline}"
+ }
+ $message = "${prefix}"
+ }
+
+ if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+ if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+ # need to parse out the relevant part of the pre-rendered positionmessage
+ $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+ }
+ elseif ($err.Exception) {
+ $message += $err.Exception.Message
+ }
+ elseif ($err.Message) {
+ $message += $err.Message
+ }
+ else {
+ $message += $err.ToString()
+ }
+ }
+ else {
+ $message += $err.ErrorDetails.Message
+ }
+
+ # if rendering line information, break up the message if it's wider than the console
+ if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+ $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
+ $prefixVtLength = $prefix.Length - $prefixLength
+
+ # replace newlines in message so it lines up correct
+ $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+ $windowWidth = 120
+ if ($Host.UI.RawUI -ne $null) {
+ $windowWidth = $Host.UI.RawUI.WindowSize.Width
+ }
+
+ if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+ $sb = [Text.StringBuilder]::new()
+ $substring = Get-TruncatedString -string $message -length ($windowWidth + $prefixVTLength)
+ $null = $sb.Append($substring)
+ $remainingMessage = $message.Substring($substring.Length).Trim()
+ $null = $sb.Append($newline)
+ while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+ $subMessage = $prefix + $remainingMessage
+ $substring = Get-TruncatedString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+ if ($substring.Length - $prefix.Length -gt 0)
+ {
+ $null = $sb.Append($substring)
+ $null = $sb.Append($newline)
+ $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+ }
+ else
+ {
+ break
+ }
+ }
+ $null = $sb.Append($prefix + $remainingMessage.Trim())
+ $message = $sb.ToString()
+ }
+
+ $message += $newline
+ }
+
+ $posmsg += "${errorColor}" + $message
+
+ $reason = 'Error'
+ if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+ $reason = 'Exception'
+ }
+ # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+ elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore))
+ {
+ $reason = $myinv.MyCommand
+ }
+ # If it's a scriptblock, better to show the command in the scriptblock that had the error
+ elseif ($err.CategoryInfo.Activity) {
+ $reason = $err.CategoryInfo.Activity
+ }
+ elseif ($myinv.MyCommand) {
+ $reason = $myinv.MyCommand
+ }
+ elseif ($myinv.InvocationName) {
+ $reason = $myinv.InvocationName
+ }
+ elseif ($err.CategoryInfo.Category) {
+ $reason = $err.CategoryInfo.Category
+ }
+ elseif ($err.CategoryInfo.Reason) {
+ $reason = $err.CategoryInfo.Reason
+ }
+
+ $errorMsg = 'Error'
+
+ "${errorColor}${reason}: ${posmsg}${resetcolor}"
+ }
+
+ $myinv = $_.InvocationInfo
+ $err = $_
+ if (!$myinv -and $_.ErrorRecord -and $_.ErrorRecord.InvocationInfo) {
+ $err = $_.ErrorRecord
+ $myinv = $err.InvocationInfo
+ }
+
+ if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') {
+ return "${errorColor}$($err.Exception.Message)${resetcolor}"
+ }
+
+ if ($ErrorView -eq 'DetailedView') {
+ $message = Get-Error | Out-String
+ return "${errorColor}${message}${resetcolor}"
+ }
+
+ if ($ErrorView -eq 'CategoryView') {
+ $message = $err.CategoryInfo.GetMessage()
+ return "${errorColor}${message}${resetcolor}"
+ }
+
+ $posmsg = ''
+ if ($ErrorView -eq 'ConciseView') {
+ $posmsg = Get-ConciseViewPositionMessage
+ }
+ elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) {
+ $posmsg = $myinv.PositionMessage
+ if ($posmsg -ne '') {
+ $posmsg = $newline + $posmsg
+ }
+ }
+
+ if ($err.PSMessageDetails) {
+ $posmsg = ' : ' + $err.PSMessageDetails + $posmsg
+ }
+
+ if ($ErrorView -eq 'ConciseView') {
+ $recommendedAction = $_.ErrorDetails.RecommendedAction
+ if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+ $recommendedAction = $newline +
+ ${errorColor} +
+ ' Recommendation: ' +
+ $recommendedAction +
+ ${resetcolor}
+ }
+
+ if ($err.PSMessageDetails) {
+ $posmsg = "${errorColor}${posmsg}"
+ }
+ return $posmsg + $recommendedAction
+ }
+
+ $indent = 4
+
+ $errorCategoryMsg = $err.ErrorCategory_Message
+
+ if ($null -ne $errorCategoryMsg)
+ {
+ $indentString = '+ CategoryInfo : ' + $err.ErrorCategory_Message
+ }
+ else
+ {
+ $indentString = '+ CategoryInfo : ' + $err.CategoryInfo
+ }
+
+ $posmsg += $newline + $indentString
+
+ $indentString = "+ FullyQualifiedErrorId : " + $err.FullyQualifiedErrorId
+ $posmsg += $newline + $indentString
+
+ $originInfo = $err.OriginInfo
+
+ if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName))
+ {
+ $indentString = "+ PSComputerName : " + $originInfo.PSComputerName
+ $posmsg += $newline + $indentString
+ }
+
+ $finalMsg = if ($err.ErrorDetails.Message) {
+ $err.ErrorDetails.Message + $posmsg
+ } else {
+ $err.Exception.Message + $posmsg
+ }
+
+ "${errorColor}${finalMsg}${resetcolor}"
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Reference/OriginalExceptionView.format.ps1xml b/Reference/OriginalExceptionView.format.ps1xml
new file mode 100644
index 0000000..d6e176d
--- /dev/null
+++ b/Reference/OriginalExceptionView.format.ps1xml
@@ -0,0 +1,45 @@
+
+
+
+
+ GetErrorInstance
+
+ System.Management.Automation.ErrorRecord#PSExtendedError
+
+
+ PSErrorIndex
+
+
+
+
+
+
+
+
+ Set-StrictMode -Off
+
+ $maxDepth = 10
+ $ellipsis = "`u{2026}"
+ $resetColor = ''
+ $errorColor = ''
+ $accentColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = $PSStyle.Reset
+ $errorColor = $psstyle.Formatting.Error
+ $accentColor = $PSStyle.Formatting.FormatAccent
+ }
+
+ function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+ $newline = [Environment]::Newline
+ $output = [System.Text.StringBuilder]::new()
+ $prefix = ' ' * $indent
+
+ $expandTypes = @(
+ 'Microsoft.Rest.HttpRequestMessageWrapper'
+ 'Microsoft.Rest.HttpResponseMessageWrapper'
+ 'System.Management.Automation.InvocationInfo'
+ )
+
+ # if object is an Exception, add an ExceptionType property
+$
\ No newline at end of file
diff --git a/Reference/default.ps1 b/Reference/default.ps1
new file mode 100644
index 0000000..cae60b6
--- /dev/null
+++ b/Reference/default.ps1
@@ -0,0 +1,302 @@
+Set-StrictMode -Off
+$ErrorActionPreference = 'Stop'
+trap { 'Error found in error view definition: ' + $_.Exception.Message }
+$newline = [Environment]::Newline
+
+$resetColor = ''
+$errorColor = ''
+$accentColor = ''
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = $PSStyle.Reset
+ $errorColor = $PSStyle.Formatting.Error
+ $accentColor = $PSStyle.Formatting.ErrorAccent
+}
+
+function Get-ConciseViewPositionMessage {
+
+ # returns a string cut to last whitespace
+ function Get-TruncatedString($string, [int]$length) {
+
+ if ($string.Length -le $length) {
+ return $string
+ }
+
+ return ($string.Substring(0,$length) -split '\s',-2)[0]
+ }
+
+ $posmsg = ''
+ $headerWhitespace = ''
+ $offsetWhitespace = ''
+ $message = ''
+ $prefix = ''
+
+ # 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
+ # 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
+ # 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.
+ $useTargetObject = $null -ne $err.TargetObject -and
+ $err.TargetObject -is [System.Collections.IDictionary] -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
+ # The checks here determine if we show line detailed error information:
+ # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+ $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+ $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
+ # - check if invocation is a script or multiple lines in the console
+ $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
+ # - 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
+ $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+ $myinv.ScriptName -notmatch '\.psm1$'
+
+ if ($useTargetObject -or $shouldShowLineDetail) {
+
+ if ($useTargetObject) {
+ $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
+ }
+ elseif ($myinv.ScriptName) {
+ if ($env:TERM_PROGRAM -eq 'vscode') {
+ # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+ }
+ else {
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+ }
+ }
+ else {
+ $posmsg = "${newline}"
+ }
+
+ if ($useTargetObject) {
+ $scriptLineNumber = $err.TargetObject.Line
+ $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
+ }
+ else {
+ $scriptLineNumber = $myinv.ScriptLineNumber
+ $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+ }
+
+ if ($scriptLineNumberLength -gt 4) {
+ $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+ }
+
+ $lineWhitespace = ''
+ if ($scriptLineNumberLength -lt 4) {
+ $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+ }
+
+ $verticalBar = '|'
+ $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+ $highlightLine = ''
+ if ($useTargetObject) {
+ $line = $_.TargetObject.LineText.Trim()
+ $offsetLength = 0
+ $offsetInLine = 0
+ }
+ else {
+ $positionMessage = $myinv.PositionMessage.Split($newline)
+ $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+ $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+ $offsetLength = $highlightLine.Trim().Length
+ $offsetInLine = $highlightLine.IndexOf('~')
+ }
+
+ if (-not $line.EndsWith($newline)) {
+ $line += $newline
+ }
+
+ # don't color the whole line
+ if ($offsetLength -lt $line.Length - 1) {
+ $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+ }
+
+ $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+ $offsetWhitespace = ' ' * $offsetInLine
+ $prefix = "${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"
+ if ($highlightLine -ne '') {
+ $posMsg += "${prefix}${highlightLine}${newline}"
+ }
+ $message = "${prefix}"
+ }
+
+ if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+ if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+ # need to parse out the relevant part of the pre-rendered positionmessage
+ $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+ }
+ elseif ($err.Exception) {
+ $message += $err.Exception.Message
+ }
+ elseif ($err.Message) {
+ $message += $err.Message
+ }
+ else {
+ $message += $err.ToString()
+ }
+ }
+ else {
+ $message += $err.ErrorDetails.Message
+ }
+
+ # if rendering line information, break up the message if it's wider than the console
+ if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+ $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
+ $prefixVtLength = $prefix.Length - $prefixLength
+
+ # replace newlines in message so it lines up correct
+ $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+ $windowWidth = 120
+ if ($null -ne $Host.UI.RawUI) {
+ $windowWidth = $Host.UI.RawUI.WindowSize.Width
+ }
+
+ if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+ $sb = [Text.StringBuilder]::new()
+ $substring = Get-TruncatedString -string $message -length ($windowWidth + $prefixVTLength)
+ $null = $sb.Append($substring)
+ $remainingMessage = $message.Substring($substring.Length).Trim()
+ $null = $sb.Append($newline)
+ while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+ $subMessage = $prefix + $remainingMessage
+ $substring = Get-TruncatedString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+ if ($substring.Length - $prefix.Length -gt 0)
+ {
+ $null = $sb.Append($substring)
+ $null = $sb.Append($newline)
+ $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+ }
+ else
+ {
+ break
+ }
+ }
+ $null = $sb.Append($prefix + $remainingMessage.Trim())
+ $message = $sb.ToString()
+ }
+
+ $message += $newline
+ }
+
+ $posmsg += "${errorColor}" + $message
+
+ $reason = 'Error'
+ if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+ $reason = 'Exception'
+ }
+ # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+ elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore))
+ {
+ $reason = $myinv.MyCommand
+ }
+ # If it's a scriptblock, better to show the command in the scriptblock that had the error
+ elseif ($err.CategoryInfo.Activity) {
+ $reason = $err.CategoryInfo.Activity
+ }
+ elseif ($myinv.MyCommand) {
+ $reason = $myinv.MyCommand
+ }
+ elseif ($myinv.InvocationName) {
+ $reason = $myinv.InvocationName
+ }
+ elseif ($err.CategoryInfo.Category) {
+ $reason = $err.CategoryInfo.Category
+ }
+ elseif ($err.CategoryInfo.Reason) {
+ $reason = $err.CategoryInfo.Reason
+ }
+
+ $errorMsg = 'Error'
+
+ "${errorColor}${reason}: ${posmsg}${resetcolor}"
+}
+
+$myinv = $_.InvocationInfo
+$err = $_
+if (!$myinv -and $_.ErrorRecord -and $_.ErrorRecord.InvocationInfo) {
+ $err = $_.ErrorRecord
+ $myinv = $err.InvocationInfo
+}
+
+if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') {
+ return "${errorColor}$($err.Exception.Message)${resetcolor}"
+}
+
+if ($ErrorView -eq 'DetailedView') {
+ $message = Get-Error | Out-String
+ return "${errorColor}${message}${resetcolor}"
+}
+
+if ($ErrorView -eq 'CategoryView') {
+ $message = $err.CategoryInfo.GetMessage()
+ return "${errorColor}${message}${resetcolor}"
+}
+
+$posmsg = ''
+if ($ErrorView -eq 'ConciseView') {
+ $posmsg = Get-ConciseViewPositionMessage
+}
+elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) {
+ $posmsg = $myinv.PositionMessage
+ if ($posmsg -ne '') {
+ $posmsg = $newline + $posmsg
+ }
+}
+
+if ($err.PSMessageDetails) {
+ $posmsg = ' : ' + $err.PSMessageDetails + $posmsg
+}
+
+if ($ErrorView -eq 'ConciseView') {
+ $recommendedAction = $_.ErrorDetails.RecommendedAction
+ if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+ $recommendedAction = $newline +
+ ${errorColor} +
+ ' Recommendation: ' +
+ $recommendedAction +
+ ${resetcolor}
+ }
+
+ if ($err.PSMessageDetails) {
+ $posmsg = "${errorColor}${posmsg}"
+ }
+ return $posmsg + $recommendedAction
+}
+
+$indent = 4
+
+$errorCategoryMsg = $err.ErrorCategory_Message
+
+if ($null -ne $errorCategoryMsg)
+{
+ $indentString = '+ CategoryInfo : ' + $err.ErrorCategory_Message
+}
+else
+{
+ $indentString = '+ CategoryInfo : ' + $err.CategoryInfo
+}
+
+$posmsg += $newline + $indentString
+
+$indentString = "+ FullyQualifiedErrorId : " + $err.FullyQualifiedErrorId
+$posmsg += $newline + $indentString
+
+$originInfo = $err.OriginInfo
+
+if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName))
+{
+ $indentString = "+ PSComputerName : " + $originInfo.PSComputerName
+ $posmsg += $newline + $indentString
+}
+
+$finalMsg = if ($err.ErrorDetails.Message) {
+ $err.ErrorDetails.Message + $posmsg
+} else {
+ $err.Exception.Message + $posmsg
+}
+
+"${errorColor}${finalMsg}${resetcolor}"
\ No newline at end of file
diff --git a/Reference/detail.ps1 b/Reference/detail.ps1
new file mode 100644
index 0000000..f4f1ac8
--- /dev/null
+++ b/Reference/detail.ps1
@@ -0,0 +1,196 @@
+Set-StrictMode -Off
+
+$maxDepth = 10
+$ellipsis = "`u{2026}"
+$resetColor = ''
+$errorColor = ''
+$accentColor = ''
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = $PSStyle.Reset
+ $errorColor = $psstyle.Formatting.Error
+ $accentColor = $PSStyle.Formatting.FormatAccent
+}
+
+function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+ $newline = [Environment]::Newline
+ $output = [System.Text.StringBuilder]::new()
+ $prefix = ' ' * $indent
+
+ $expandTypes = @(
+ 'Microsoft.Rest.HttpRequestMessageWrapper'
+ 'Microsoft.Rest.HttpResponseMessageWrapper'
+ 'System.Management.Automation.InvocationInfo'
+ )
+
+ # if object is an Exception, add an ExceptionType property
+ if ($obj -is [Exception]) {
+ $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
+ }
+
+ # first find the longest property so we can indent properly
+ $propLength = 0
+ foreach ($prop in $obj.PSObject.Properties) {
+ if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+ $propLength = $prop.Name.Length
+ }
+ }
+
+ $addedProperty = $false
+ foreach ($prop in $obj.PSObject.Properties) {
+
+ # don't show empty properties or our added property for $error[index]
+ if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+ $addedProperty = $true
+ $null = $output.Append($prefix)
+ $null = $output.Append($accentColor)
+ $null = $output.Append($prop.Name)
+ $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
+ $null = $output.Append($propNameIndent)
+ $null = $output.Append(' : ')
+ $null = $output.Append($resetColor)
+
+ $newIndent = $indent + 4
+
+ # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+ if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+ $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $null = $output.Append($newline)
+ $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
+ }
+ }
+ # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+ elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $targetSite = [PSCustomObject]@{
+ Name = $prop.Value.Name
+ DeclaringType = $prop.Value.DeclaringType
+ MemberType = $prop.Value.MemberType
+ Module = $prop.Value.Module
+ }
+
+ $null = $output.Append($newline)
+ $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
+ }
+ }
+ # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+ elseif ($prop.Name -eq 'StackTrace') {
+ # for a stacktrace which is usually quite wide with info, we left justify it
+ $null = $output.Append($newline)
+ $null = $output.Append($prop.Value)
+ }
+ # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+ elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+ $isFirstElement = $true
+ foreach ($key in $prop.Value.Keys) {
+ if ($isFirstElement) {
+ $null = $output.Append($newline)
+ }
+
+ if ($key -eq 'Authorization') {
+ $null = $output.Append("${prefix} ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
+ }
+ else {
+ $null = $output.Append("${prefix} ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
+ }
+
+ $isFirstElement = $false
+ }
+ }
+ # if the object implements IEnumerable and not a string, we try to show each object
+ # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+ elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ else {
+ $isFirstElement = $true
+ foreach ($value in $prop.Value) {
+ $null = $output.Append($newline)
+ $valueIndent = ' ' * ($newIndent + 2)
+
+ if ($value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("${prefix}${valueIndent}[$($value.ToString())]")
+ }
+ elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+ $null = $output.Append("${prefix}${valueIndent}${value}")
+ }
+ else {
+ if (!$isFirstElement) {
+ $null = $output.Append($newline)
+ }
+ $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
+ }
+ $isFirstElement = $false
+ }
+ }
+ }
+ elseif ($prop.Value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("[$($prop.Value.ToString())]")
+ }
+ # Anything else, we convert to string.
+ # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+ else {
+ $value = $null
+ if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
+ {
+ if ($prop.Name -eq 'PositionMessage') {
+ $value = $value.Insert($value.IndexOf('~'), $errorColor)
+ }
+ elseif ($prop.Name -eq 'Message') {
+ $value = $errorColor + $value
+ }
+
+ $isFirstLine = $true
+ if ($value.Contains($newline)) {
+ # the 3 is to account for ' : '
+ $valueIndent = ' ' * ($propLength + 3)
+ # need to trim any extra whitespace already in the text
+ foreach ($line in $value.Split($newline)) {
+ if (!$isFirstLine) {
+ $null = $output.Append("${newline}${prefix}${valueIndent}")
+ }
+ $null = $output.Append($line.Trim())
+ $isFirstLine = $false
+ }
+ }
+ else {
+ $null = $output.Append($value)
+ }
+ }
+ }
+
+ $null = $output.Append($newline)
+ }
+ }
+
+ # if we had added nested properties, we need to remove the last newline
+ if ($addedProperty) {
+ $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+ }
+
+ $output.ToString()
+}
+
+# Add back original typename and remove PSExtendedError
+if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
+ $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
+ $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
+}
+elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
+ $_.PSObject.TypeNames.Add('System.Exception')
+ $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
+}
+
+Show-ErrorRecord $_
\ No newline at end of file
diff --git a/Reference/legacy.ps1 b/Reference/legacy.ps1
new file mode 100644
index 0000000..8ab5ee2
--- /dev/null
+++ b/Reference/legacy.ps1
@@ -0,0 +1,57 @@
+if ($_.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
+ $_.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
+ }
+
+ $indent = 4
+ $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+
+ $errorCategoryMsg = & { Set-StrictMode -Version 1; $_.ErrorCategory_Message }
+ if ($errorCategoryMsg -ne $null)
+ {
+ $indentString = "+ CategoryInfo : " + $_.ErrorCategory_Message
+ }
+ else
+ {
+ $indentString = "+ CategoryInfo : " + $_.CategoryInfo
+ }
+ $posmsg += "`n"
+ foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+
+ $indentString = "+ FullyQualifiedErrorId : " + $_.FullyQualifiedErrorId
+ $posmsg += "`n"
+ foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+
+ $originInfo = & { Set-StrictMode -Version 1; $_.OriginInfo }
+ if (($originInfo -ne $null) -and ($originInfo.PSComputerName -ne $null))
+ {
+ $indentString = "+ PSComputerName : " + $originInfo.PSComputerName
+ $posmsg += "`n"
+ foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+ }
+
+ if ($ErrorView -eq "CategoryView") {
+ $_.CategoryInfo.GetMessage()
+ }
+ elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) {
+ $_.Exception.Message + $posmsg + "`n "
+ } else {
+ $_.ErrorDetails.Message + $posmsg
+ }
+}
\ No newline at end of file
diff --git a/ScriptAnalyzerSettings.psd1 b/ScriptAnalyzerSettings.psd1
new file mode 100644
index 0000000..caca8da
--- /dev/null
+++ b/ScriptAnalyzerSettings.psd1
@@ -0,0 +1,4 @@
+@{
+ Severity = @('Error', 'Warning')
+ ExcludeRules = @('PSAvoidGlobalVars')
+}
diff --git a/build.psd1 b/build.psd1
index a3d17d5..bc39a65 100644
--- a/build.psd1
+++ b/build.psd1
@@ -2,6 +2,7 @@
ModuleManifest = "./source/ErrorView.psd1"
CopyPaths = 'ErrorView.format.ps1xml'
Prefix = 'prefix.ps1'
+ Postfix = 'postfix.ps1'
# The rest of the paths are relative to the manifest
OutputDirectory = ".."
VersionedOutputDirectory = $true
diff --git a/source/ErrorView.format.ps1xml b/source/ErrorView.format.ps1xml
index 4b3a2c7..582f7f0 100644
--- a/source/ErrorView.format.ps1xml
+++ b/source/ErrorView.format.ps1xml
@@ -11,12 +11,13 @@
-
- Write-NativeCommandError $_
-
+
+ ExceptionInstance
+
+
+ System.Exception
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ InformationRecord
+
+
+ System.Management.Automation.InformationRecord
+
+
+
+
+
+
+
+ $_.MessageData | Format-List * | Out-String
+
+
+
+
+ "Tags: " + @($_.Tags -join ", ")
+
+
+
+
+ $_ | Select-Object * -ExcludeProperty Tags, MessageData | Format-List * -Force | Out-String
+
+
+
+
+
+
+
diff --git a/source/postfix.ps1 b/source/postfix.ps1
new file mode 100644
index 0000000..bac8866
--- /dev/null
+++ b/source/postfix.ps1
@@ -0,0 +1,7 @@
+if ($script:ErrorView) {
+ Set-ErrorView $ErrorView
+} elseif ($Env:GITHUB_ACTIONS -or $Env:TF_BUILD) {
+ Set-ErrorView "DetailedErrorView"
+} else {
+ Set-ErrorView "ConciseView"
+}
\ No newline at end of file
diff --git a/source/prefix.ps1 b/source/prefix.ps1
index 9f73337..a116ae8 100644
--- a/source/prefix.ps1
+++ b/source/prefix.ps1
@@ -1,8 +1,17 @@
-[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'ErrorView is all about the ErrorView global variable')]
param(
- $global:ErrorView = "Simple"
+ $ErrorView
)
-# We need to _overwrite_ the ErrorView
-# So -PrependPath, instead of FormatsToProcess
-Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
\ No newline at end of file
+# We need to _overwrite_ the ErrorView, so we must use -PrependPath
+Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
+
+Set-StrictMode -Off
+$ErrorActionPreference = 'Stop'
+trap { 'Error found in error view definition: ' + $_.Exception.Message }
+
+$script:ellipsis = [char]0x2026
+$script:newline = [Environment]::Newline
+$script:LineColors = @(
+ "`e[38;2;255;255;255m"
+ "`e[38;2;179;179;179m"
+)
\ No newline at end of file
diff --git a/source/private/GetAzurePipelinePositionMessage.ps1 b/source/private/GetAzurePipelinePositionMessage.ps1
new file mode 100644
index 0000000..a32031e
--- /dev/null
+++ b/source/private/GetAzurePipelinePositionMessage.ps1
@@ -0,0 +1,36 @@
+filter GetGooglePositionMessage {
+ [CmdletBinding()]
+ [OUtputType([string])]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+ $InvocationInfo = $InputObject.InvocationInfo
+ # 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
+ # 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
+ # 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.
+ $useTargetObject = $null -ne $InputObject.TargetObject -and
+ $InputObject.TargetObject -is [System.Collections.IDictionary] -and
+ ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('Line') -and
+ ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('LineText')
+
+ $file = if ($useTargetObject) {
+ "$($InputObject.TargetObject.File)"
+ } elseif (.ScriptName) {
+ "$($InvocationInfo.ScriptName)"
+ }
+
+ $line = if ($useTargetObject) {
+ $InputObject.TargetObject.Line
+ } else {
+ $InvocationInfo.ScriptLineNumber
+ }
+
+ if ($useTargetObject) {
+ "sourcepath=$file;linenumber=$line"
+ } else {
+ $column = $InvocationInfo.OffsetInLine
+ "sourcepath=$file;linenumber=$line,columnnumber=$column"
+ }
+}
diff --git a/source/private/GetConciseMessage.ps1 b/source/private/GetConciseMessage.ps1
new file mode 100644
index 0000000..707194f
--- /dev/null
+++ b/source/private/GetConciseMessage.ps1
@@ -0,0 +1,174 @@
+filter GetConciseMessage {
+ [CmdletBinding()]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+ $err = $InputObject
+ $posmsg = ''
+ $headerWhitespace = ''
+ $message = ''
+ $prefix = ''
+
+ # 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
+ # 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
+ # 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.
+ $useTargetObject = $null -ne $err.TargetObject -and
+ $err.TargetObject -is [System.Collections.IDictionary] -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+ ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
+ # The checks here determine if we show line detailed error information:
+ # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+ $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+ $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
+ # - check if invocation is a script or multiple lines in the console
+ $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
+ # - 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
+ $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+ $myinv.ScriptName -notmatch '\.psm1$'
+
+ if ($useTargetObject -or $shouldShowLineDetail) {
+
+ if ($useTargetObject) {
+ $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
+ } elseif ($myinv.ScriptName) {
+ if ($env:TERM_PROGRAM -eq 'vscode') {
+ # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+ } else {
+ $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+ }
+ } else {
+ $posmsg = "${newline}"
+ }
+
+ if ($useTargetObject) {
+ $scriptLineNumber = $err.TargetObject.Line
+ $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
+ } else {
+ $scriptLineNumber = $myinv.ScriptLineNumber
+ $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+ }
+
+ if ($scriptLineNumberLength -gt 4) {
+ $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+ }
+
+ $lineWhitespace = ''
+ if ($scriptLineNumberLength -lt 4) {
+ $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+ }
+
+ $verticalBar = '|'
+ $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+ $highlightLine = ''
+ if ($useTargetObject) {
+ $line = $_.TargetObject.LineText.Trim()
+ $offsetLength = 0
+ $offsetInLine = 0
+ } else {
+ $positionMessage = $myinv.PositionMessage.Split($newline)
+ $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+ $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+ $offsetLength = $highlightLine.Trim().Length
+ $offsetInLine = $highlightLine.IndexOf('~')
+ }
+
+ if (-not $line.EndsWith($newline)) {
+ $line += $newline
+ }
+
+ # don't color the whole line
+ if ($offsetLength -lt $line.Length - 1) {
+ $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+ }
+
+ $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+ $prefix = "${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"
+ if ($highlightLine -ne '') {
+ $posMsg += "${prefix}${highlightLine}${newline}"
+ }
+ $message = "${prefix}"
+ }
+
+ if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+ if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+ # need to parse out the relevant part of the pre-rendered positionmessage
+ $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+ } elseif ($err.Exception) {
+ $message += $err.Exception.Message
+ } elseif ($err.Message) {
+ $message += $err.Message
+ } else {
+ $message += $err.ToString()
+ }
+ } else {
+ $message += $err.ErrorDetails.Message
+ }
+
+ # if rendering line information, break up the message if it's wider than the console
+ if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+ $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
+ $prefixVtLength = $prefix.Length - $prefixLength
+
+ # replace newlines in message so it lines up correct
+ $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+ $windowWidth = 120
+ if ($null -ne $Host.UI.RawUI) {
+ $windowWidth = $Host.UI.RawUI.WindowSize.Width
+ }
+
+ if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+ $sb = [Text.StringBuilder]::new()
+ $substring = TruncateString -string $message -length ($windowWidth + $prefixVTLength)
+ $null = $sb.Append($substring)
+ $remainingMessage = $message.Substring($substring.Length).Trim()
+ $null = $sb.Append($newline)
+ while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+ $subMessage = $prefix + $remainingMessage
+ $substring = TruncateString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+ if ($substring.Length - $prefix.Length -gt 0) {
+ $null = $sb.Append($substring)
+ $null = $sb.Append($newline)
+ $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+ } else {
+ break
+ }
+ }
+ $null = $sb.Append($prefix + $remainingMessage.Trim())
+ $message = $sb.ToString()
+ }
+
+ $message += $newline
+ }
+
+ $posmsg += "${errorColor}" + $message
+
+ $reason = 'Error'
+ if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+ $reason = 'Exception'
+ # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+ } elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore)) {
+ $reason = $myinv.MyCommand
+ } elseif ($err.CategoryInfo.Activity) {
+ # If it's a scriptblock, better to show the command in the scriptblock that had the error
+ $reason = $err.CategoryInfo.Activity
+ } elseif ($myinv.MyCommand) {
+ $reason = $myinv.MyCommand
+ } elseif ($myinv.InvocationName) {
+ $reason = $myinv.InvocationName
+ } elseif ($err.CategoryInfo.Category) {
+ $reason = $err.CategoryInfo.Category
+ } elseif ($err.CategoryInfo.Reason) {
+ $reason = $err.CategoryInfo.Reason
+ }
+
+ "${errorColor}${reason}: ${posmsg}${resetcolor}"
+}
diff --git a/source/private/GetErrorMessage.ps1 b/source/private/GetErrorMessage.ps1
new file mode 100644
index 0000000..515abfe
--- /dev/null
+++ b/source/private/GetErrorMessage.ps1
@@ -0,0 +1,23 @@
+filter GetErrorTitle {
+ [CmdletBinding()]
+ [OUtputType([string])]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+ if ($InputObject.ErrorDetails -and $InputObject.ErrorDetails.Message) {
+ $InputObject.ErrorDetails.Message
+ } else {
+ if ($InputObject.CategoryInfo.Category -eq 'ParserError' -and $InputObject.Exception.Message.Contains("~$newline")) {
+ # need to parse out the relevant part of the pre-rendered positionmessage
+ $InputObject.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+ } elseif ($InputObject.Exception) {
+ $InputObject.Exception.Message
+ } elseif ($InputObject.Message) {
+ $InputObject.Message
+ } else {
+ $InputObject.ToString()
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/private/GetErrorTitle.ps1 b/source/private/GetErrorTitle.ps1
new file mode 100644
index 0000000..f1f779f
--- /dev/null
+++ b/source/private/GetErrorTitle.ps1
@@ -0,0 +1,29 @@
+filter GetErrorTitle {
+ [CmdletBinding()]
+ [OUtputType([string])]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+
+ if ($InputObject.Exception -and $InputObject.Exception.WasThrownFromThrowStatement) {
+ 'Exception'
+ # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+ } elseif ($InputObject.InvocationInfo.MyCommand -and $InputObject.InvocationInfo.MyCommand.Name -and (Get-Command -Name $InputObject.InvocationInfo.MyCommand -ErrorAction Ignore)) {
+ $InputObject.InvocationInfo.MyCommand
+ } elseif ($InputObject.CategoryInfo.Activity) {
+ # If it's a scriptblock, better to show the command in the scriptblock that had the error
+ $InputObject.CategoryInfo.Activity
+ } elseif ($InputObject.InvocationInfo.MyCommand) {
+ $InputObject.InvocationInfo.MyCommand
+ } elseif ($InputObject.InvocationInfo.InvocationName) {
+ $InputObject.InvocationInfo.InvocationName
+ } elseif ($InputObject.CategoryInfo.Category) {
+ $InputObject.CategoryInfo.Category
+ } elseif ($InputObject.CategoryInfo.Reason) {
+ $InputObject.CategoryInfo.Reason
+ } else {
+ 'Error'
+ }
+}
\ No newline at end of file
diff --git a/source/private/GetGoogleWorkflowPositionMessage.ps1 b/source/private/GetGoogleWorkflowPositionMessage.ps1
new file mode 100644
index 0000000..6faac67
--- /dev/null
+++ b/source/private/GetGoogleWorkflowPositionMessage.ps1
@@ -0,0 +1,39 @@
+filter GetGoogleWorkflowPositionMessage {
+ [CmdletBinding()]
+ [OUtputType([string])]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+ $InvocationInfo = $InputObject.InvocationInfo
+ # 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
+ # 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
+ # 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.
+ $useTargetObject = $null -ne $InputObject.TargetObject -and
+ $InputObject.TargetObject -is [System.Collections.IDictionary] -and
+ ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('Line') -and
+ ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('LineText')
+
+ $file = if ($useTargetObject) {
+ "$($InputObject.TargetObject.File)"
+ } elseif (.ScriptName) {
+ "$($InvocationInfo.ScriptName)"
+ }
+
+ $line = if ($useTargetObject) {
+ $InputObject.TargetObject.Line
+ } else {
+ $InvocationInfo.ScriptLineNumber
+ }
+
+ if ($useTargetObject) {
+ "file=$file,line=$line"
+ } else {
+ $column = $InvocationInfo.OffsetInLine
+
+ $Length = $InvocationInfo.PositionMessage.Split($newline)[-1].Substring(1).Trim().Length
+ $endColumn = $column + $Length
+ "file=$file,line=$line,col=$column,endColumn=$endColumn"
+ }
+}
diff --git a/source/private/GetListRecursive.ps1 b/source/private/GetListRecursive.ps1
new file mode 100644
index 0000000..ef943ea
--- /dev/null
+++ b/source/private/GetListRecursive.ps1
@@ -0,0 +1,167 @@
+function GetListRecursive {
+ <#
+ .SYNOPSIS
+ Internal implementation of the Detailed error view to support recursion and indentation
+ #>
+ [CmdletBinding()]
+ param(
+ $InputObject,
+ [int]$indent = 0,
+ [int]$depth = 1
+ )
+ $output = [System.Text.StringBuilder]::new()
+ $padding = ' ' * $indent
+
+ $expandTypes = @(
+ 'Microsoft.Rest.HttpRequestMessageWrapper'
+ 'Microsoft.Rest.HttpResponseMessageWrapper'
+ 'System.Management.Automation.InvocationInfo'
+ )
+
+ # The built-in DetailedView aligns all the ":" characters, but it's awful
+
+ $addedProperty = $false
+ foreach ($prop in $InputObject.PSObject.Properties) {
+ # PowerShell creates an ErrorRecord property on Exceptions that points back to the parent ErrorRecord.
+ # This is basically a circular reference that causes repeated informtion, so we're going to skip them
+ if ($prop.Value -is [System.Management.Automation.ErrorRecord] -and $depth -ge 2) {
+ continue
+ }
+ # don't show empty properties or our added property for $error[index]
+ if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+ $addedProperty = $true
+ $null = $output.Append($padding)
+ $null = $output.Append($accentColor)
+ $null = $output.Append($prop.Name)
+ $null = $output.Append(': ')
+ $null = $output.Append($resetColor)
+
+ [int]$nextIndent = $indent + 2
+ [int]$nextDepth = $depth + 1
+ $nextPadding = ' ' * $nextIndent
+
+ # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes
+ if ($prop.Value -is [Exception] -or
+ $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+ $expandTypes -contains $prop.TypeNameOfValue -or
+ ($null -ne $prop.TypeNames -and $expandTypes -contains $prop.TypeNames[0])) {
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ } else {
+ # For Exceptions, add a fake "Type" property
+ if ($prop.Value -is [Exception]) {
+ $null = $output.Append(( $accentColor + "[" + $prop.Value.GetType().FullName + "]" + $resetColor))
+ }
+ $null = $output.Append($newline)
+ $null = $output.Append((GetListRecursive $prop.Value $nextIndent $nextDepth))
+ }
+ } elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+ # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ } else {
+ $targetSite = [PSCustomObject]@{
+ Name = $prop.Value.Name
+ DeclaringType = $prop.Value.DeclaringType
+ MemberType = $prop.Value.MemberType
+ Module = $prop.Value.Module
+ }
+
+ $null = $output.Append($newline)
+ $null = $output.Append((GetListRecursive $targetSite $nextIndent $nextDepth))
+ }
+ } elseif ($prop.Name -eq 'StackTrace') {
+ # StackTrace is handled specifically because the lines are typically long but we can't trucate them, so we don't indent it any more
+ $null = $output.Append($newline)
+ # $null = $output.Append($prop.Value)
+ $Wrap = @{
+ Width = $Host.UI.RawUI.BufferSize.Width - 2
+ IndentPadding = ""
+ WrappedIndent = " "
+ Colors = $LineColors
+ }
+ $null = $output.Append(($prop.Value | WrapString @Wrap))
+ } elseif ($prop.Name -eq 'HResult') {
+ # `HResult` is handled specifically so we can format it in hex
+ # $null = $output.Append($newline)
+ $null = $output.Append("0x{0:x} ({0})" -f $prop.Value)
+ } elseif ($prop.Name -eq 'PipelineIterationInfo') {
+ # I literally have no idea what use this is
+ $null = $output.Append($prop.Value -join ', ')
+ } elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+ # Dictionary and Hashtable we want to show as Key/Value pairs
+ $null = $output.Append($newline)
+ foreach ($key in $prop.Value.Keys) {
+ if ($key -eq 'Authorization') {
+ $null = $output.Append("${nextPadding}${accentColor}${key}: ${resetColor}${ellipsis}${newline}")
+ } else {
+ $null = $output.Append("${nextPadding}${accentColor}${key}: ${resetColor}$($prop.Value[$key])${newline}")
+ }
+ }
+ } elseif (!($prop.Value -is [System.String]) -and $null -ne $prop.Value.GetType().GetInterface('IEnumerable') -and $prop.Name -ne 'Data') {
+ # if the object implements IEnumerable and not a string, we try to show each object
+ # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ } else {
+ $isFirstElement = $true
+ foreach ($value in $prop.Value) {
+ $null = $output.Append($newline)
+
+ if ($value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("[$($value.ToString())]")
+ } elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+ $null = $output.Append("${value}")
+ } else {
+ if (!$isFirstElement) {
+ $null = $output.Append($newline)
+ }
+ $null = $output.Append((GetListRecursive $value $nextIndent $nextDepth))
+ }
+ $isFirstElement = $false
+ }
+ }
+ } elseif ($prop.Value -is [Type]) {
+ # Just show the typename instead of it as an object
+ $null = $output.Append("[$($prop.Value.ToString())]")
+ } else {
+ # Anything else, we convert to string.
+ # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+ $value = $null
+ if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $null -ne $value) {
+ $value = $value.Trim()
+ if ($InputObject -is [System.Management.Automation.InvocationInfo] -and $prop.Name -eq 'PositionMessage') {
+ # Make the underline red
+ $value = $value.Insert($value.IndexOf('~'), $errorColor)
+ } elseif ( ($InputObject -is [System.Management.Automation.ErrorRecord] -or
+ $InputObject -is [System.Exception]) -and $prop.Name -in 'Message', 'FullyQualifiedErrorId', 'CategoryInfo') {
+ $value = $errorColor + $value
+ }
+ $Wrap = @{
+ Width = $Host.UI.RawUI.BufferSize.Width - 2
+ # Because the first line contains the property name, we don't want to indent it
+ FirstLineIndent = ''
+ # But all other lines (including wrapped lines) should be indented to align
+ IndentPadding = " " * ($nextIndent + $prop.Name.Length)
+ Colors = $LineColors
+ }
+
+ $null = $output.Append(($value | WrapString @Wrap))
+ }
+ }
+
+ $null = $output.Append($newline)
+ }
+ }
+
+ # if we had added nested properties, we need to remove the last newline
+ if ($addedProperty) {
+ $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+ }
+
+ $output.ToString()
+ Write-Information "EXIT GetListRecursive END $($InputObject.GetType().FullName) $indent $depth (of $maxDepth)" -Tags 'Trace', 'Enter', 'GetListRecursive'
+}
diff --git a/source/private/GetYamlRecursive.ps1 b/source/private/GetYamlRecursive.ps1
new file mode 100644
index 0000000..9862626
--- /dev/null
+++ b/source/private/GetYamlRecursive.ps1
@@ -0,0 +1,177 @@
+function GetYamlRecursive {
+ <#
+ .SYNOPSIS
+ Creates a description of an ErrorRecord that looks like valid Yaml
+ .DESCRIPTION
+ This produces valid Yaml output from ErrorRecord you pass to it, recursively.
+ #>
+ [CmdletBinding()]
+ param(
+ # The object that you want to convert to YAML
+ [Parameter(Mandatory, ValueFromPipeline)]
+ $InputObject,
+
+ # Optionally, a limit on the depth to recurse properties (defaults to 16)
+ [parameter()]
+ [int]$depth = 1,
+
+ # If set, include empty and null properties in the output
+ [switch]$IncludeEmpty,
+
+ # Recursive use only. Handles indentation for formatting
+ [parameter(DontShow)]
+ [int]$NestingLevel = 0,
+
+ # use OuterXml instead of treating XmlDocuments like objects
+ [parameter(DontShow)]
+ [switch]$XmlAsXml
+ )
+ process {
+ $Width = $Host.UI.RawUI.BufferSize.Width - 1 - ($NestingLevel * 2)
+ $__hasoutput = $true
+ $padding = ' ' * $NestingLevel # # lets just create our left-padding for the block
+ $Recurse = @{
+ 'Depth' = $depth + 1
+ 'NestingLevel' = $NestingLevel + 1
+ 'XmlAsXml' = $XmlAsXml
+ }
+ $Wrap = @{
+ Width = $Host.UI.RawUI.BufferSize.Width - 2
+ IndentPadding = $padding
+ Colors = $LineColors
+ }
+
+ @(
+ if ($Null -eq $InputObject) { return 'null' } # if it is null return null
+ if ($NestingLevel -eq 0 -and $local:__hasoutput) { '---' } # if we have output before, add a yaml separator
+
+ try {
+ switch ($InputObject) {
+ # prevent these values being expanded
+ <# if ($Type -in @( 'guid',
+ , 'datatable', 'List`1','SqlDataReader', 'datarow', 'type',
+ 'MemberTypes', 'RuntimeModule', 'RuntimeType', 'ErrorCategoryInfo', 'CommandInfo', 'CmdletInfo' )) {
+ #>
+ { $InputObject -is [scriptblock] } {
+ "{$($InputObject.ToString())}"
+ break
+ }
+ { $InputObject -is [type] } {
+ "'[$($InputObject.FullName)]'"
+ break
+ }
+ { $InputObject -is [System.Xml.XmlDocument] -or $InputObject -is [System.Xml.XmlElement] } {
+ "|"
+ $InputObject.OuterXml | WrapString @Wrap
+ break
+ }
+ { $InputObject -is [datetime] -or $InputObject -is [datetimeoffset] } {
+ # s=SortableDateTimePattern (based on ISO 8601) using local time
+ $InputObject.ToString('s')
+ break
+ }
+ { $InputObject -is [timespan] -or $InputObject -is [version] -or $InputObject -is [uri] } {
+ # s=SortableDateTimePattern (based on ISO 8601) using local time
+ "'$InputObject'"
+ break
+ }
+ # yaml case for booleans
+ { $InputObject -is [bool] } {
+ if ($InputObject) { 'true' } else { 'false' }
+ break
+ }
+ # If we're going to go over our depth, just output like it's a value type
+ # ValueTypes are just output with no possibility of wrapping or recursion
+ { $InputObject -is [Enum] -or $InputObject.GetType().BaseType -eq [ValueType] -or $depth -gt $maxDepth } {
+ "$InputObject"
+ break
+ }
+ # 'PSNoteProperty' {
+ # # Write-Verbose "$($padding)Show $($property.Name)"
+ # GetYamlRecursive -InputObject $InputObject.Value @Recurse }
+ { $InputObject -is [System.Collections.IDictionary] } {
+ foreach ($kvp in $InputObject.GetEnumerator()) {
+ # Write-Verbose "$($padding)Enumerate $($property.Name)"
+ "$newline$padding$accentColor$($kvp.Name):$resetColor " +
+ (GetYamlRecursive -InputObject $kvp.Value @Recurse)
+ }
+ break
+ }
+
+ { $InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string] } {
+ foreach ($item in $InputObject) {
+ # Write-Verbose "$($padding)Enumerate $($property.Name)"
+ $Value = GetYamlRecursive -InputObject $item @Recurse
+ # if ($Value -ne 'null' -or $IncludeEmpty) {
+ "$newline$accentColor$padding$resetColor- $Value"
+ # }
+ }
+ break
+ }
+
+ # Limit recursive enumeration to specific types:
+ { $InputObject -is [Exception] -or ($InputObject -is [System.Management.Automation.ErrorRecord] -and $depth -lt 2) -or
+ $InputObject.PSTypeNames[0] -in @(
+ # 'System.Exception'
+ # 'System.Management.Automation.ErrorRecord'
+ 'Microsoft.Rest.HttpRequestMessageWrapper'
+ 'Microsoft.Rest.HttpResponseMessageWrapper'
+ 'System.Management.Automation.InvocationInfo'
+ ) } {
+ if ($depth -ge $maxDepth) {
+ $null = $output.Append($ellipsis)
+ }
+ # For exceptions, output a fake property for the exception type
+ if ($InputObject -is [Exception]) {
+ "$newline$padding${accentColor}#Type:$resetColor ${accentColor}" + $InputObject.GetType().FullName + $resetColor
+ }
+ foreach ($property in $InputObject.PSObject.Properties) {
+ if ($property.Value) {
+ $Value = GetYamlRecursive -InputObject $property.Value @Recurse
+ # For special cases, add some color:
+ if ($property.Name -eq "PositionMessage") {
+ $Value = $Value -replace "(\+\s+)(~+)", "`$1$errorColor`$2$resetColor"
+ }
+ if ($InputObject -is [Exception] -and $property.Name -eq "Message") {
+ $Value = "$errorColor$Value$resetColor"
+ }
+ if ((-not [string]::IsNullOrEmpty($Value) -and $Value -ne 'null' -and $Value.Count -gt 0) -or $IncludeEmpty) {
+ "$newline$padding$accentColor$($property.Name):$resetColor " + $Value
+ }
+ }
+ }
+ break
+ }
+ # 'generic' {
+ # foreach($key in $InputObject.Keys) {
+ # # Write-Verbose "$($padding)Enumerate $($key)"
+ # $Value = GetYamlRecursive -InputObject $InputObject.$key @Recurse
+ # if ((-not [string]::IsNullOrEmpty($Value) -and $Value -ne 'null') -or $IncludeEmpty) {
+ # "$padding$accentColor$($key):$resetColor " + $Value
+ # }
+ # }
+ # }
+ default {
+ # Treat anything else as a string
+ $StringValue = $null
+ if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($InputObject, [string], [ref]$StringValue) -and $null -ne $StringValue) {
+ $StringValue = $StringValue.Trim()
+ if ($StringValue -match '[\r\n]' -or $StringValue.Length -gt $Width) {
+ ">$newline" # signal that we are going to use the readable 'newlines-folded' format
+ $StringValue | WrapString @Wrap -EmphasizeOriginalNewlines
+ } elseif ($StringValue.Contains(":")) {
+ "'$($StringValue -replace '''', '''''')'" # single quote it
+ } else {
+ "$($StringValue -replace '''', '''''')"
+ }
+ } else {
+ Write-Warning "Unable to convert $($InputObject.GetType().FullName) to string"
+ }
+ }
+ }
+ } catch {
+ "Error formatting error ($($_)) in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($InputObject)' Class: $($InputObject.GetType().Name) BaseClass: $($InputObject.GetType().BaseType.Name) "
+ }
+ ) -join ""
+ }
+}
\ No newline at end of file
diff --git a/source/private/Recolor.ps1 b/source/private/Recolor.ps1
new file mode 100644
index 0000000..4a831a5
--- /dev/null
+++ b/source/private/Recolor.ps1
@@ -0,0 +1,11 @@
+function ResetColor {
+ $script:resetColor = ''
+ $script:errorColor = ''
+ $script:accentColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $script:resetColor = "$([char]27)[0m"
+ $script:errorColor = if ($null -ne $PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+ $script:accentColor = if ($null -ne $PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+ }
+}
\ No newline at end of file
diff --git a/source/private/TruncateString.ps1 b/source/private/TruncateString.ps1
new file mode 100644
index 0000000..86ffab6
--- /dev/null
+++ b/source/private/TruncateString.ps1
@@ -0,0 +1,21 @@
+filter TruncateString {
+ [CmdletBinding()]
+ param(
+ # The input string will be wrapped to a certain length, with optional padding on the front
+ [Parameter(ValueFromPipeline)]
+ [string]$InputObject,
+
+ [Parameter(Position = 0)]
+ [Alias('Length')]
+ [int]$Width = ($Host.UI.RawUI.BufferSize.Width)
+ )
+ # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
+ # $maxLength = $width - $IndentPadding.Length -1
+ $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
+
+ if ($InputObject.Length -le $Width) {
+ return $InputObject
+ }
+
+ ($InputObject.Substring(0,$length) -split $wrapper,-2)[0]
+}
\ No newline at end of file
diff --git a/source/private/WrapString.ps1 b/source/private/WrapString.ps1
new file mode 100644
index 0000000..e825dd1
--- /dev/null
+++ b/source/private/WrapString.ps1
@@ -0,0 +1,89 @@
+
+filter WrapString {
+ [CmdletBinding()]
+ param(
+ # The input string will be wrapped to a certain length, with optional padding on the front
+ [Parameter(ValueFromPipeline)]
+ [string]$InputObject,
+
+ # The maximum length of a line. Defaults to [Console]::BufferWidth - 1
+ [Parameter(Position=0)]
+ [int]$Width = ($Host.UI.RawUI.BufferSize.Width),
+
+ # The padding for each line defaults to an empty string.
+ # If set, whitespace on the front of each line is replaced with this string.
+ [string]$IndentPadding = ([string]::Empty),
+
+ # If set, this will be used only for the first line (defaults to IndentPadding)
+ [string]$FirstLineIndent = $IndentPadding,
+
+ # If set, wrapped lines use this instead of IndentPadding to create a hanging indent
+ [string]$WrappedIndent = $IndentPadding,
+
+
+ # If set, colors to use for alternating lines
+ [string[]]$Colors = @(''),
+
+ # If set, will output empty lines for each original new line
+ [switch]$EmphasizeOriginalNewlines
+ )
+ begin {
+ $FirstLine = $true
+ $color = 0;
+ Write-Debug "Colors: $($Colors -replace "`e(.+)", "`e`$1``e`$1")"
+ # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
+ # $maxLength = $width - $IndentPadding.Length -1
+ $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
+ $output = [System.Text.StringBuilder]::new()
+ $buffer = [System.Text.StringBuilder]::new()
+ $lineLength = 0
+ if ($Width -lt $IndentPadding.Length) {
+ Write-Warning "Width $Width is less than IndentPadding length $($IndentPadding.Length). Setting Width to BufferWidth ($($Host.UI.RawUI.BufferSize.Width))"
+ }
+ }
+ process {
+ foreach($line in $InputObject -split "(\r?\n)") {
+ if ($FirstLine -and $PSBoundParameters.ContainsKey('FirstLineIndent')) {
+ $IndentPadding, $FirstLineIndent = $FirstLineIndent, $IndentPadding
+ }
+ # Don't bother trying to split empty lines
+ if ([String]::IsNullOrWhiteSpace($AnsiRegex.Replace($line, ''))) {
+ Write-Debug "Empty String ($($line.Length))"
+ if ($EmphasizeOriginalNewlines) {
+ $null = $output.Append($newline)
+ }
+ continue
+ }
+
+ $slices = $line -split $wrapper | ForEach-Object { @{ Text = $_; Length = MeasureString $_ } }
+ Write-Debug "$($line.Length) words in line. $($AnsiRegex.Replace($line, ''))"
+ foreach($slice in $slices) {
+ $lineLength += $slice.Length
+ if ($lineLength -le $Width) {
+ Write-Verbose "+ $($slice.Length) = $lineLength < $Width"
+ $null = $buffer.Append($slice.Text)
+ } else {
+ Write-Verbose "Output $($lineLength - $slice.Length)"
+ Write-Verbose "+ $($slice.Length) = $($slice.Length)"
+ #$null = $output.Append($buffer.ToString())
+ $null = $buffer.Append($newline).Append($WrappedIndent).Append($slice.Text)
+ $lineLength = $IndentPadding.Length + $slice.Length
+ }
+ }
+ if (!$FirstLine) {
+ $null = $output.Append($newline)
+ }
+ if ($PSBoundParameters.ContainsKey("IndentPadding")) {
+ $null = $output.Append($Colors[$color] + $IndentPadding + $buffer.ToString().TrimStart())
+ } else {
+ $null = $output.Append($Colors[$color] + $buffer.ToString())
+ }
+ $color = ($color + 1) % $Colors.Length
+ $null = $buffer.Clear() #.Append($Colors[$color]).Append($IndentPadding)
+ $lineLength = $IndentPadding.Length
+ $FirstLine = $false
+ $IndentPadding = $FirstLineIndent
+ }
+ $output.ToString()
+ }
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-CategoryErrorView.ps1 b/source/public/ConvertTo-CategoryErrorView.ps1
index ccb619a..c5fb25f 100644
--- a/source/public/ConvertTo-CategoryErrorView.ps1
+++ b/source/public/ConvertTo-CategoryErrorView.ps1
@@ -15,5 +15,12 @@ filter ConvertTo-CategoryErrorView {
[System.Management.Automation.ErrorRecord]
$InputObject
)
- $InputObject.CategoryInfo.GetMessage()
+ $resetColor = ''
+ $errorColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = "$([char]0x1b)[0m"
+ $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+ }
+ $errorColor + $InputObject.CategoryInfo.GetMessage() + $resetColor
}
\ No newline at end of file
diff --git a/source/public/ConvertTo-ConciseErrorView.ps1 b/source/public/ConvertTo-ConciseErrorView.ps1
new file mode 100644
index 0000000..390e6b4
--- /dev/null
+++ b/source/public/ConvertTo-ConciseErrorView.ps1
@@ -0,0 +1,32 @@
+function ConvertTo-ConciseErrorView {
+ [CmdletBinding()]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject
+ )
+ begin { ResetColor }
+ process {
+ if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
+ "${errorColor}$($InputObject.Exception.Message)${resetColor}"
+ } else {
+ if (!"$accentColor".Length) {
+ $local:accentColor = ">>>"
+ $local:resetColor = "<<<"
+ }
+
+ $message = GetConciseMessage -InputObject $InputObject
+
+ if ($InputObject.PSMessageDetails) {
+ $message = $errorColor + ' : ' + $InputObject.PSMessageDetails + $message
+ }
+
+ $recommendedAction = $InputObject.ErrorDetails.RecommendedAction
+ if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+ $message = $message + $newline + ${errorColor} + ' Recommendation: ' + $recommendedAction + ${resetcolor}
+ }
+
+ $message
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index ed52bcb..539b43f 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -1,10 +1,10 @@
-function ConvertTo-DetailedErrorView {
+filter ConvertTo-DetailedErrorView {
<#
.SYNOPSIS
Converts an ErrorRecord to a detailed error string
.DESCRIPTION
- The default PowerShell "DetailedView" ErrorView
- Copied from the PowerShellCore.format.ps1xml
+ An "improved" version of the PowerShell "DetailedView" ErrorView
+ Originally copied from the PowerShellCore.format.ps1xml
.LINK
https://github.com/PowerShell/PowerShell/blob/c444645b0941d73dc769f0bba6ab70d317bd51a9/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs#L903
#>
@@ -19,188 +19,13 @@ function ConvertTo-DetailedErrorView {
# The maximum depth to recurse into the object
[int]$maxDepth = 10
)
-
- begin {
-
- Set-StrictMode -Off
-
- $ellipsis = "`u{2026}"
- $resetColor = ''
- $errorColor = ''
- $accentColor = ''
- $newline = [Environment]::Newline
- $OutputRoot = [System.Text.StringBuilder]::new()
-
- if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
- $resetColor = $PSStyle.Reset
- $errorColor = $PSStyle.Formatting.Error
- $accentColor = $PSStyle.Formatting.FormatAccent
- }
-
- function DetailedErrorView {
- <#
- .SYNOPSIS
- Internal implementation of the Detailed error view to support recursion and indentation
- #>
- [CmdletBinding()]
- param(
- $InputObject,
- [int]$indent = 0,
- [int]$depth = 1
- )
- $prefix = ' ' * $indent
-
- $expandTypes = @(
- 'Microsoft.Rest.HttpRequestMessageWrapper'
- 'Microsoft.Rest.HttpResponseMessageWrapper'
- 'System.Management.Automation.InvocationInfo'
- )
-
- # if object is an Exception, add an ExceptionType property
- if ($InputObject -is [Exception]) {
- $InputObject | Add-Member -NotePropertyName Type -NotePropertyValue $InputObject.GetType().FullName -ErrorAction Ignore
- }
-
- # first find the longest property so we can indent properly
- $propLength = 0
- foreach ($prop in $InputObject.PSObject.Properties) {
- if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
- $propLength = $prop.Name.Length
- }
- }
-
- $addedProperty = $false
- foreach ($prop in $InputObject.PSObject.Properties) {
-
- # don't show empty properties or our added property for $error[index]
- if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
- $addedProperty = $true
- $null = $OutputRoot.Append($prefix)
- $null = $OutputRoot.Append($accentColor)
- $null = $OutputRoot.Append($prop.Name)
- $null = $OutputRoot.Append(' ',($propLength - $prop.Name.Length))
- $null = $OutputRoot.Append(' : ')
- $null = $OutputRoot.Append($resetColor)
-
- $newIndent = $indent + 4
-
- # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
- if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
- $expandTypes -contains $prop.TypeNameOfValue -or ($null -ne $prop.TypeNames -and $expandTypes -contains $prop.TypeNames[0])) {
-
- if ($depth -ge $maxDepth) {
- $null = $OutputRoot.Append($ellipsis)
- }
- else {
- $null = $OutputRoot.Append($newline)
- $null = $OutputRoot.Append((DetailedErrorView $prop.Value $newIndent ($depth + 1)))
- }
- }
- # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
- elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
- if ($depth -ge $maxDepth) {
- $null = $OutputRoot.Append($ellipsis)
- }
- else {
- $targetSite = [PSCustomObject]@{
- Name = $prop.Value.Name
- DeclaringType = $prop.Value.DeclaringType
- MemberType = $prop.Value.MemberType
- Module = $prop.Value.Module
- }
-
- $null = $OutputRoot.Append($newline)
- $null = $OutputRoot.Append((DetailedErrorView $targetSite $newIndent ($depth + 1)))
- }
- }
- # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
- elseif ($prop.Name -eq 'StackTrace') {
- # for a stacktrace which is usually quite wide with info, we left justify it
- $null = $OutputRoot.Append($newline)
- $null = $OutputRoot.Append($prop.Value)
- }
- # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
- elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
- $isFirstElement = $true
- foreach ($key in $prop.Value.Keys) {
- if ($isFirstElement) {
- $null = $OutputRoot.Append($newline)
- }
-
- if ($key -eq 'Authorization') {
- $null = $OutputRoot.Append("${prefix} ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
- }
- else {
- $null = $OutputRoot.Append("${prefix} ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
- }
-
- $isFirstElement = $false
- }
- }
- # if the object implements IEnumerable and not a string, we try to show each object
- # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
- elseif (!($prop.Value -is [System.String]) -and $null -ne $prop.Value.GetType().GetInterface('IEnumerable') -and $prop.Name -ne 'Data') {
-
- if ($depth -ge $maxDepth) {
- $null = $OutputRoot.Append($ellipsis)
- }
- else {
- $isFirstElement = $true
- foreach ($value in $prop.Value) {
- $null = $OutputRoot.Append($newline)
- if (!$isFirstElement) {
- $null = $OutputRoot.Append($newline)
- }
- $null = $OutputRoot.Append((DetailedErrorView $value $newIndent ($depth + 1)))
- $isFirstElement = $false
- }
- }
- }
- # Anything else, we convert to string.
- # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
- else {
- $value = $null
- if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $null -ne $value)
- {
- if ($prop.Name -eq 'PositionMessage') {
- $value = $value.Insert($value.IndexOf('~'), $errorColor)
- }
- elseif ($prop.Name -eq 'Message') {
- $value = $errorColor + $value
- }
-
- $isFirstLine = $true
- if ($value.Contains($newline)) {
- # the 3 is to account for ' : '
- $valueIndent = ' ' * ($propLength + 3)
- # need to trim any extra whitespace already in the text
- foreach ($line in $value.Split($newline)) {
- if (!$isFirstLine) {
- $null = $OutputRoot.Append("${newline}${prefix}${valueIndent}")
- }
- $null = $OutputRoot.Append($line.Trim())
- $isFirstLine = $false
- }
- }
- else {
- $null = $OutputRoot.Append($value)
- }
- }
- }
-
- $null = $OutputRoot.Append($newline)
- }
- }
-
- # if we had added nested properties, we need to remove the last newline
- if ($addedProperty) {
- $null = $OutputRoot.Remove($OutputRoot.Length - $newline.Length, $newline.Length)
- }
-
- $OutputRoot.ToString()
- }
- }
+ begin { ResetColor }
process {
- DetailedErrorView $InputObject
+ $newline + (GetListRecursive $InputObject) + $newline
+ if ($Env:GITHUB_ACTIONS) {
+ "::error $(GetGoogleWorkflowPositionMesage),title=$(GetErrorTitle $InputObject)::$(GetErrorMessage $InputObject)"
+ } elseif ($Env:TF_BUILD) {
+ "##vso[task.logissue type=error;$(GetAzurePipelinesPositionMesage)]$(GetErrorTitle $InputObject): $(GetErrorMessage $InputObject)"
+ }
}
-}
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-FullErrorView.ps1 b/source/public/ConvertTo-FullErrorView.ps1
index dbe4ce5..c13705e 100644
--- a/source/public/ConvertTo-FullErrorView.ps1
+++ b/source/public/ConvertTo-FullErrorView.ps1
@@ -3,7 +3,7 @@ filter ConvertTo-FullErrorView {
.SYNOPSIS
Converts an ErrorRecord to a full error view
.DESCRIPTION
- The most verbose error view I've got, it shows everything, recursing forever.
+ A simple, verbose error view that just shows everything, recursing forever.
#>
[CmdletBinding()]
param(
@@ -12,19 +12,32 @@ filter ConvertTo-FullErrorView {
[System.Management.Automation.ErrorRecord]
$InputObject
)
- $PSStyle.OutputRendering, $Rendering = "Ansi", $PSStyle.OutputRendering
- $Detail = $InputObject | Format-List * -Force | Out-String
- $PSStyle.OutputRendering = $Rendering
+ $resetColor = ''
+ $errorColor = ''
+ #$accentColor = ''
+
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ # For Format-List to use color when piped to Out-String, OutputRendering needs to be Ansi
+ $PSStyle.OutputRendering, $Rendering = "Ansi", $PSStyle.OutputRendering
+
+ $resetColor = "$([char]0x1b)[0m"
+ $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+ }
+
+ $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
# NOTE: ErrorViewRecurse is normally false, and only set temporarily by Format-Error -Recurse
if ($ErrorViewRecurse) {
$Count = 1
$Exception = $InputObject.Exception
while ($Exception = $Exception.InnerException) {
- $Detail += "`nINNER EXCEPTION $($Count): $($Exception.GetType().FullName)`n`n"
- $Detail += $Exception | Format-List * -Force | Out-String
+ $Detail += $errorColor + "`nINNER EXCEPTION $($Count): $resetColor$($Exception.GetType().FullName)`n`n"
+ $Detail += $Exception | Format-List * -Force | Out-String -Width 120
$Count++
}
}
+ if ($resetColor) {
+ $PSStyle.OutputRendering = $Rendering
+ }
$Detail
}
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalErrorView.ps1 b/source/public/ConvertTo-NormalErrorView.ps1
index 182d84e..9ee9113 100644
--- a/source/public/ConvertTo-NormalErrorView.ps1
+++ b/source/public/ConvertTo-NormalErrorView.ps1
@@ -11,64 +11,48 @@ filter ConvertTo-NormalErrorView {
[System.Management.Automation.ErrorRecord]
$InputObject
)
-
- if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
- $InputObject.Exception.Message
- } else {
- $myinv = $InputObject.InvocationInfo
- if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
- $posmsg = $myinv.PositionMessage
+ begin { ResetColor }
+ process {
+ if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
+ "${errorColor}$($InputObject.Exception.Message)${resetColor}"
} else {
- $posmsg = ""
- }
-
- if ($posmsg -ne "") {
- $posmsg = "`n" + $posmsg
- }
-
- if ( &{ Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
- $posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
- }
+ $myinv = $InputObject.InvocationInfo
+ $posmsg = ''
+ if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError')) -and $myinv.PositionMessage) {
+ $posmsg = $newline + $myinv.PositionMessage
+ }
- $indent = 4
- $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+ if ($err.PSMessageDetails) {
+ $posmsg = ' : ' + $err.PSMessageDetails + $posmsg
+ }
- $errorCategoryMsg = &{ Set-StrictMode -Version 1; $InputObject.ErrorCategory_Message }
- if ($null -ne $errorCategoryMsg) {
- $indentString = "+ CategoryInfo : " + $InputObject.ErrorCategory_Message
- } else {
- $indentString = "+ CategoryInfo : " + $InputObject.CategoryInfo
- }
- $posmsg += "`n"
- foreach ($line in @($indentString -split "(.{$width})")) {
- if ($line) {
- $posmsg += (" " * $indent + $line)
+ $Wrap = @{
+ Width = $host.UI.RawUI.BufferSize.Width - 2
+ IndentPadding = " "
}
- }
- $indentString = "+ FullyQualifiedErrorId : " + $InputObject.FullyQualifiedErrorId
- $posmsg += "`n"
- foreach ($line in @($indentString -split "(.{$width})")) {
- if ($line) {
- $posmsg += (" " * $indent + $line)
+ $errorCategoryMsg = $InputObject.ErrorCategory_Message
+ [string]$line = if ($null -ne $errorCategoryMsg) {
+ $accentColor + "+ CategoryInfo : " + $errorColor + $InputObject.ErrorCategory_Message | WrapString @Wrap
+ } else {
+ $accentColor + "+ CategoryInfo : " + $errorColor + $InputObject.CategoryInfo | WrapString @Wrap
}
- }
+ $posmsg += $newline + $line
- $originInfo = &{ Set-StrictMode -Version 1; $InputObject.OriginInfo }
- if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
- $indentString = "+ PSComputerName : " + $originInfo.PSComputerName
- $posmsg += "`n"
- foreach ($line in @($indentString -split "(.{$width})")) {
- if ($line) {
- $posmsg += (" " * $indent + $line)
- }
+ $line = $accentColor + "+ FullyQualifiedErrorId: " + $errorColor + $InputObject.FullyQualifiedErrorId | WrapString @Wrap
+ $posmsg += $newline + $line
+
+ $originInfo = $InputObject.OriginInfo
+ if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
+ $line = $accentColor + "+ PSComputerName : " + $errorColor + $originInfo.PSComputerName | WrapString @Wrap
+ $posmsg += $newline + $line
}
- }
- if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
- $InputObject.Exception.Message + $posmsg + "`n "
- } else {
- $InputObject.ErrorDetails.Message + $posmsg
+ if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
+ $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor
+ } else {
+ $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
+ }
}
}
}
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalExceptionView.ps1 b/source/public/ConvertTo-NormalExceptionView.ps1
new file mode 100644
index 0000000..31de80f
--- /dev/null
+++ b/source/public/ConvertTo-NormalExceptionView.ps1
@@ -0,0 +1,18 @@
+filter ConvertTo-NormalExceptionView {
+ <#
+ .SYNOPSIS
+ Converts an Exception to a NormalView message string
+ .DESCRIPTION
+ The original default PowerShell ErrorView, updated for VT100
+ #>
+ [CmdletBinding()]
+ param(
+ [Parameter(ValueFromPipeline)]
+ [System.Exception]
+ $InputObject
+ )
+ begin { ResetColor }
+ process {
+ $errorColor + $InputObject.Message + $resetColor
+ }
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-SimpleErrorView.ps1 b/source/public/ConvertTo-SimpleErrorView.ps1
index 20e4849..c8b836b 100644
--- a/source/public/ConvertTo-SimpleErrorView.ps1
+++ b/source/public/ConvertTo-SimpleErrorView.ps1
@@ -4,29 +4,27 @@ function ConvertTo-SimpleErrorView {
[System.Management.Automation.ErrorRecord]
$InputObject
)
+ $resetColor = ''
+ $errorColor = ''
+ #$accentColor = ''
- if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
+ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+ $resetColor = "$([char]0x1b)[0m"
+ $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+ #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+ }
+
+ if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
$InputObject.Exception.Message
} else {
$myinv = $InputObject.InvocationInfo
if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
# rip off lines that say "At line:1 char:1" (hopefully, in a language agnostic way)
- $posmsg = $myinv.PositionMessage -replace "^At line:1 .*[\r\n]+"
- # rip off the underline and instead, put >>>markers<<< around the important bit
- # we could, instead, set the background to a highlight color?
- $pattern = $posmsg -split "[\r\n]+" -match "\+( +~+)\s*" -replace '(~+)', '($1)' -replace '( +)','($1)' -replace '~| ','.'
- $posmsg = $posmsg -replace '[\r\n]+\+ +~+'
- if ($pattern) {
- $posmsg = $posmsg -replace "\+$pattern", '+ $1>>>$2<<<'
- }
+ $posmsg = "`n" + $myinv.PositionMessage -replace "^At line:1 .*[\r\n]+"
} else {
$posmsg = ""
}
- if ($posmsg -ne "") {
- $posmsg = "`n" + $posmsg
- }
-
if ( & { Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
$posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
}
@@ -46,9 +44,9 @@ function ConvertTo-SimpleErrorView {
}
if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
- $InputObject.Exception.Message + $posmsg + "`n "
+ $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor + "`n "
} else {
- $InputObject.ErrorDetails.Message + $posmsg
+ $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
}
}
}
\ No newline at end of file
diff --git a/source/public/ConvertTo-YamlErrorView.ps1 b/source/public/ConvertTo-YamlErrorView.ps1
new file mode 100644
index 0000000..089e314
--- /dev/null
+++ b/source/public/ConvertTo-YamlErrorView.ps1
@@ -0,0 +1,26 @@
+function ConvertTo-YamlErrorView {
+ <#
+ .SYNOPSIS
+ Creates a description of an ErrorRecord that looks like valid Yaml
+ .DESCRIPTION
+ This produces valid Yaml output from ErrorRecord you pass to it, recursively.
+ #>
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'maxDepth')]
+ [CmdletBinding()]
+ param(
+ # The object that you want to convert to YAML
+ [Parameter(Mandatory, ValueFromPipeline)]
+ [System.Management.Automation.ErrorRecord]
+ $InputObject,
+
+ # The maximum depth to recurse into the object
+ [int]$maxDepth = 10,
+
+ # If set, include empty and null properties in the output
+ [switch]$IncludeEmpty
+ )
+ begin { ResetColor }
+ process {
+ GetYamlRecursive -InputObject $InputObject -IncludeEmpty:$IncludeEmpty
+ }
+}
\ No newline at end of file
diff --git a/source/public/Format-Error.ps1 b/source/public/Format-Error.ps1
index 59a80c8..6a28a37 100644
--- a/source/public/Format-Error.ps1
+++ b/source/public/Format-Error.ps1
@@ -1,13 +1,13 @@
function Format-Error {
<#
.SYNOPSIS
- Formats an error for the screen using a specified error view
+ Formats an error (or exception) for the screen using a specified error view
.DESCRIPTION
Temporarily switches the error view and outputs the errors
.EXAMPLE
Format-Error
- Shows the Normal error view for the most recent error
+ Shows the Detailed error view for the most recent error (changed to be compatible with Get-Error)
.EXAMPLE
$error[0..4] | Format-Error Full
@@ -17,8 +17,8 @@ function Format-Error {
Shows the full error view of the specific error, recursing into the inner exceptions (if that's supported by the view)
#>
- [CmdletBinding(DefaultParameterSetName="Count")]
- [Alias("fe", "Get-Error")]
+ [CmdletBinding(DefaultParameterSetName = "InputObject")]
+ [Alias("fe"<#, "Get-Error"#>)]
[OutputType([System.Management.Automation.ErrorRecord])]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'The ArgumentCompleter parameters are the required method signature')]
@@ -28,37 +28,41 @@ function Format-Error {
[ArgumentCompleter({
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
[System.Management.Automation.CompletionResult[]]((
- Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord]
+ Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord], [System.Exception]
).Name -replace "ConvertTo-(.*)ErrorView",'$1' -like "*$($wordToComplete)*")
})]
$View = "Detailed",
- [Parameter(ParameterSetName="Count", Mandatory)]
+ [Parameter(ParameterSetName="Count")]
[int]$Newest = 1,
# Error records (e.g. from $Error). Defaults to the most recent error: $Error[0]
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName="InputObject", Mandatory)]
[Alias("ErrorRecord")]
- [System.Management.Automation.ErrorRecord]$InputObject = $(
- $e = $Error[0..($Newest-1)]
- if ($e -is ([System.Management.Automation.ErrorRecord])) { $e }
- elseif ($e.ErrorRecord -is ([System.Management.Automation.ErrorRecord])) { $e.ErrorRecord }
- elseif ($Error.Count -eq 0) { Write-Warning "The global `$Error collection is empty" }
+ [PSObject]$InputObject = $(
+ if ($global:Error.Count -eq 0) {
+ Write-Warning "The global `$Error collection is empty"
+ } else {
+ $global:Error[0..($Newest-1)]
+ }
),
- # Allows ErrorView functions to recurse to InnerException
+ # Encourages ErrorView functions to recurse InnerException properties
[switch]$Recurse
)
begin {
$ErrorActionPreference = "Continue"
- $View, $ErrorView = $ErrorView, $View
- [bool]$Recurse, [bool]$ErrorViewRecurse = [bool]$ErrorViewRecurse, $Recurse
+
+ $local:_ErrorView, $global:ErrorView = $global:ErrorView, $View
+ $local:_ErrorViewRecurse, [bool]$global:ErrorViewRecurse = [bool]$global:ErrorViewRecurse, $Recurse
}
process {
$InputObject
}
end {
- [bool]$ErrorViewRecurse = $Recurse
- $ErrorView = $View
+ $global:ErrorView = $local:_ErrorView
+ if ($null -ne $local:_ErrorViewRecurse) {
+ [bool]$global:ErrorViewRecurse = $local:_ErrorViewRecurse
+ }
}
}
diff --git a/source/public/Get-ErrorPrefix.ps1 b/source/public/Get-ErrorPrefix.ps1
new file mode 100644
index 0000000..cce9461
--- /dev/null
+++ b/source/public/Get-ErrorPrefix.ps1
@@ -0,0 +1,46 @@
+filter Get-ErrorPrefix {
+ [CmdletBinding()]
+ param(
+ [Parameter(ValueFromPipeline)]
+ $InputObject
+ )
+ if (@('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId) {
+ if ($InputObject -is [System.Exception]) {
+ $InputObject.GetType().FullName + " : "
+ } else {
+ $myinv = $InputObject.InvocationInfo
+ if ($myinv -and $myinv.MyCommand) {
+ switch -regex ( $myinv.MyCommand.CommandType ) {
+ ([System.Management.Automation.CommandTypes]::ExternalScript) {
+ if ($myinv.MyCommand.Path) {
+ $myinv.MyCommand.Path + ' : '
+ }
+
+ break
+ }
+
+ ([System.Management.Automation.CommandTypes]::Script) {
+ if ($myinv.MyCommand.ScriptBlock) {
+ $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+ }
+
+ break
+ }
+ default {
+ if ($myinv.InvocationName -match '^[&\.]?$') {
+ if ($myinv.MyCommand.Name) {
+ $myinv.MyCommand.Name + ' : '
+ }
+ } else {
+ $myinv.InvocationName + ' : '
+ }
+
+ break
+ }
+ }
+ } elseif ($myinv -and $myinv.InvocationName) {
+ $myinv.InvocationName + ' : '
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/public/Set-ErrorView.ps1 b/source/public/Set-ErrorView.ps1
index cd52ea6..7a0d5e6 100644
--- a/source/public/Set-ErrorView.ps1
+++ b/source/public/Set-ErrorView.ps1
@@ -21,13 +21,14 @@ filter Set-ErrorView {
})]
$View = "Normal"
)
- # Update the enum every time, because how often do you change the error view?
+
+ # Re-create an update the enum every time, because how often do you change the error view?
$Names = [System.Management.Automation.ErrorView].GetEnumNames() + @(
Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord]
).Name -replace "ConvertTo-(\w+)ErrorView", '$1View' | Select-Object -Unique
$ofs = ';'
- [ScriptBlock]::Create("enum ErrorView { $Names }").Invoke()
+ . ([ScriptBlock]::Create("enum ErrorView { $Names }"))
[ErrorView]$global:ErrorView = $View
}
diff --git a/source/public/Write-NativeCommandError.ps1 b/source/public/Write-NativeCommandError.ps1
deleted file mode 100644
index 08b699f..0000000
--- a/source/public/Write-NativeCommandError.ps1
+++ /dev/null
@@ -1,39 +0,0 @@
-function Write-NativeCommandError {
- [CmdletBinding()]
- param(
- [System.Management.Automation.ErrorRecord]
- $InputObject
- )
-
- if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") { return }
-
- $myinv = $InputObject.InvocationInfo
- if ($myinv -and $myinv.MyCommand) {
- switch -regex ( $myinv.MyCommand.CommandType ) {
- ([System.Management.Automation.CommandTypes]::ExternalScript) {
- if ($myinv.MyCommand.Path) {
- $myinv.MyCommand.Path + " : "
- }
- break
- }
- ([System.Management.Automation.CommandTypes]::Script) {
- if ($myinv.MyCommand.ScriptBlock) {
- $myinv.MyCommand.ScriptBlock.ToString() + " : "
- }
- break
- }
- default {
- if ($myinv.InvocationName -match '^[&\.]?$') {
- if ($myinv.MyCommand.Name) {
- $myinv.MyCommand.Name + " : "
- }
- } else {
- $myinv.InvocationName + " : "
- }
- break
- }
- }
- } elseif ($myinv -and $myinv.InvocationName) {
- $myinv.InvocationName + " : "
- }
-}
\ No newline at end of file