# encoding: ascii
# api: powershell
# title: Show-ColorizedContent.ps
# description: From Windows PowerShell Cookbook (O’Reilly) by Lee Holmes
# version: 0.1
# type: script
# author: Lee Holmes
# license: CC0
# x-poshcode-id: 2223
# x-archived: 2016-03-18T23:48:36
# x-published: 2011-09-09T21:42:00
#
#
##############################################################################
##
## Show-ColorizedContent
##
## From Windows PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Displays syntax highlighting, line numbering, and range highlighting for
PowerShell scripts.
.EXAMPLE
PS >Show-ColorizedContent Invoke-MyScript.ps1
001 | function Write-Greeting
002 | {
003 | param($greeting)
004 | Write-Host "$greeting World"
005 | }
006 |
007 | Write-Greeting "Hello"
.EXAMPLE
PS >Show-ColorizedContent Invoke-MyScript.ps1 -highlightRange (1..3+7)
001 > function Write-Greeting
002 > {
003 > param($greeting)
004 | Write-Host "$greeting World"
005 | }
006 |
007 > Write-Greeting "Hello"
#>
param(
## The path to colorize
[Parameter(Mandatory = $true)]
$Path,
## The range of lines to highlight
$HighlightRange = @(),
## Switch to exclude line numbers
[Switch] $ExcludeLineNumbers
)
Set-StrictMode -Version Latest
## Colors to use for the different script tokens.
## To pick your own colors:
## [Enum]::GetValues($host.UI.RawUI.ForegroundColor.GetType()) |
## Foreach-Object { Write-Host -Fore $_ "$_" }
$replacementColours = @{
'Attribute' = 'DarkCyan'
'Command' = 'Blue'
'CommandArgument' = 'Magenta'
'CommandParameter' = 'DarkBlue'
'Comment' = 'DarkGreen'
'GroupEnd' = 'Black'
'GroupStart' = 'Black'
'Keyword' = 'DarkBlue'
'LineContinuation' = 'Black'
'LoopLabel' = 'DarkBlue'
'Member' = 'Black'
'NewLine' = 'Black'
'Number' = 'Magenta'
'Operator' = 'DarkGray'
'Position' = 'Black'
'StatementSeparator' = 'Black'
'String' = 'DarkRed'
'Type' = 'DarkCyan'
'Unknown' = 'Black'
'Variable' = 'Red'
}
$highlightColor = "Red"
$highlightCharacter = ">"
$highlightWidth = 6
if($excludeLineNumbers) { $highlightWidth = 0 }
## Read the text of the file, and tokenize it
$file = (Resolve-Path $Path).Path
$content = [IO.File]::ReadAllText($file)
$parsed = [System.Management.Automation.PsParser]::Tokenize(
$content, [ref] $null) | Sort StartLine,StartColumn
## Write a formatted line -- in the format of:
## <Line Number> <Separator Character> <Text>
function WriteFormattedLine($formatString, [int] $line)
{
if($excludeLineNumbers) { return }
## By default, write the line number in gray, and use
## a simple pipe as the separator
$hColor = "DarkGray"
$separator = "|"
## If we need to highlight the line, use the highlight
## color and highlight separator as the separator
if($highlightRange -contains $line)
{
$hColor = $highlightColor
$separator = $highlightCharacter
}
## Write the formatted line
$text = $formatString -f $line,$separator
Write-Host -NoNewLine -Fore $hColor -Back White $text
}
## Complete the current line with filler cells
function CompleteLine($column)
{
## Figure how much space is remaining
$lineRemaining = $host.UI.RawUI.WindowSize.Width -
$column - $highlightWidth + 1
## If we have less than 0 remaining, we've wrapped onto the
## next line. Add another buffer width worth of filler
if($lineRemaining -lt 0)
{
$lineRemaining += $host.UI.RawUI.WindowSize.Width
}
Write-Host -NoNewLine -Back White (" " * $lineRemaining)
}
## Write the first line of context information (line number,
## highlight character.)
Write-Host
WriteFormattedLine "{0:D3} {1} " 1
## Now, go through each of the tokens in the input
## script
$column = 1
foreach($token in $parsed)
{
$color = "Gray"
## Determine the highlighting colour for that token by looking
## in the hashtable that maps token types to their color
$color = $replacementColours[[string]$token.Type]
if(-not $color) { $color = "Gray" }
## If it's a newline token, write the next line of context
## information
if(($token.Type -eq "NewLine") -or ($token.Type -eq "LineContinuation"))
{
CompleteLine $column
WriteFormattedLine "{0:D3} {1} " ($token.StartLine + 1)
$column = 1
}
else
{
## Do any indenting
if($column -lt $token.StartColumn)
{
$text = " " * ($token.StartColumn - $column)
Write-Host -Back White -NoNewLine $text
$column = $token.StartColumn
}
## See where the token ends
$tokenEnd = $token.Start + $token.Length - 1
## Handle the line numbering for multi-line strings and comments
if(
(($token.Type -eq "String") -or
($token.Type -eq "Comment")) -and
($token.EndLine -gt $token.StartLine))
{
## Store which line we've started at
$lineCounter = $token.StartLine
## Split the content of this token into its lines
## We use the start and end of the tokens to determine
## the position of the content, but use the content
## itself (rather than the token values) for manipulation.
$stringLines = $(
-join $content[$token.Start..$tokenEnd] -split "`n")
## Go through each of the lines in the content
foreach($stringLine in $stringLines)
{
$stringLine = $stringLine.Trim()
## If we're on a new line, fill the right hand
## side of the line with spaces, and write the header
## for the new line.
if($lineCounter -gt $token.StartLine)
{
CompleteLine $column
WriteFormattedLine "{0:D3} {1} " $lineCounter
$column = 1
}
## Now write the text of the current line
Write-Host -NoNewLine -Fore $color -Back White $stringLine
$column += $stringLine.Length
$lineCounter++
}
}
## Write out a regular token
else
{
## We use the start and end of the tokens to determine
## the position of the content, but use the content
## itself (rather than the token values) for manipulation.
$text = (-join $content[$token.Start..$tokenEnd])
Write-Host -NoNewLine -Fore $color -Back White $text
}
## Update our position in the column
$column = $token.EndColumn
}
}
CompleteLine $column
Write-Host