Skip to content

Instantly share code, notes, and snippets.

@trackd
Last active June 25, 2024 22:20
Show Gist options
  • Save trackd/2c670e3c1a127ab32d885a68168c06a9 to your computer and use it in GitHub Desktop.
Save trackd/2c670e3c1a127ab32d885a68168c06a9 to your computer and use it in GitHub Desktop.
ft as markdowntable
function ConvertTo-MarkdownTable {
<#
.DESCRIPTION
Convert an object to a markdown table.
.PARAMETER InputObject
The object to convert to a markdown table.
.PARAMETER Property
The properties to display in the table.
Passthru to Format-Table.
.PARAMETER Autosize
Autosize the columns.
.PARAMETER Grid
Add a grid to the table.
.PARAMETER AllowVT
Allow VT codes to be included in the output.
.PARAMETER Padding
The padding to add to each cell (before and after value).
defaults to a space.
.PARAMETER View
The view to use for the table.
.PARAMETER Delimiter
The delimiter to use for the table.
Defaults to '|'.
.EXAMPLE
ls | ConvertTo-MarkdownTable
ls | ConvertTo-MarkdownTable -Autosize
ls | ft | ConvertTo-MarkdownTable
for fun,
gps | ConvertTo-MarkdownTable -Delimiter '😄'
.NOTES
saw https://gist.github.com/JustinGrote/595973381d6438d987552d6a33851515
and wanted to try my own version.
#>
[CmdletBinding()]
[Alias('ctmt')]
[OutputType([string])]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[AllowNull()]
[AllowEmptyString()]
[object[]] $InputObject,
[object[]] $Property,
[switch] $Autosize,
[switch] $Grid,
[ValidateLength(1,2)]
[string] $Padding = ' ',
[switch] $AllowVT,
[string] $View,
[ValidateLength(1,2)]
[string] $Delimiter = '|',
[ValidateLength(1,1)]
[String] $VerticalDelimiter = '-'
)
begin {
$Objects = [System.Collections.Generic.List[object]]::new()
$sb = [System.Text.StringBuilder]::new()
$e = [char]27
$VTCodes = @(
# color
[Regex]::new("$e\[\d*(;\d+)*m")
# CSI
[Regex]::new("$e\[\?\d+[hl]")
# link
[Regex]::new("$e\]8;;.*?$e\\")
) -join '|'
$len = @{}
# for formatting the output.
$Cell = $Padding + '{0}' + $Padding + $Delimiter
$CellStart = $Delimiter + $Cell
function GetColumnWidths {
<#
for autosize, get the width of each column.
#>
param(
[string[]] $Strings,
[int] $Count
)
if ([String]::IsNullOrEmpty($Strings)) {
return @{}
}
$widths = @{}
for ($i = 0; $i -lt $Strings.Length; $i++) {
$columnIndex = $i % $Count
$string = $Strings[$i] -replace $VTCodes
if ($widths.ContainsKey($columnIndex)) {
if ($string.Length -gt $widths[$columnIndex]) {
$widths[$columnIndex] = $string.Length
}
}
else {
$widths[$columnIndex] = $string.Length
}
}
$widths
}
}
process {
if ($null -eq $InputObject) {
return
}
if ($MyInvocation.ExpectingInput) {
return $Objects.Add($InputObject)
}
foreach ($entry in $InputObject) {
if ([String]::IsNullOrEmpty($entry)) {
continue
}
$Objects.Add($entry)
}
}
end {
if ($Objects.Count -eq 0) {
return
}
if ($Objects[0].GetType().Name -ne 'FormatStartData') {
$ft = @{}
if ($Property) {
$ft.Property = $Property
}
if ($View) {
$ft.View = $View
}
$Objects = $Objects | Format-Table @ft
}
$null = & {
# suppress all stringbuilder output in one go.
if ($Objects[0].Gettype().Name -eq 'FormatStartData') {
$ColumnIndex = 0
$ColumnCount = $Objects[0].shapeinfo.tablecolumninfolist.count
if ($Autosize) {
$widthSplat = @{
Strings = $Objects.FormatEntryInfo.formatPropertyFieldList.propertyValue
Count = $ColumnCount
}
$WidthCheck = GetColumnWidths @widthSplat
}
foreach ($header in $Objects[0].shapeinfo.tablecolumninfolist) {
# property header
if ($null -ne $header.label) {
$Name = $header.label
}
else {
$Name = $header.propertyname
}
# column width
if ($Autosize) {
$header.Width = $WidthCheck[$ColumnIndex]
if ($Name.Length -gt $header.Width) {
$header.Width = $Name.Length
}
}
elseif ($header.width -eq 0) {
$header.width = ($host.UI.RawUI.WindowSize.Width - 10) / $ColumnCount
}
# add to the string builder
if ($ColumnIndex -eq 0) {
$sb.AppendFormat($CellStart, $Name.PadRight($header.Width))
}
else {
$sb.AppendFormat($Cell, $Name.PadRight($header.Width))
}
$len[$ColumnIndex] = $header.Width
$ColumnIndex++
}
$bar = $sb.ToString() -replace $VTCodes -replace "[^$Delimiter]",$VerticalDelimiter
$sb.AppendLine().Append($bar)
if ($Grid) {
$sb.Insert(0, $bar + "`n")
}
}
if ($Objects.FormatEntryInfo) {
foreach ($row in $Objects.FormatEntryInfo) {
if ($row.formatPropertyFieldList) {
$ColumnIndex = 0
$sb.AppendLine().Append($Delimiter)
foreach ($value in $row.formatPropertyFieldList) {
$pad = $len[$ColumnIndex]
# need both to get the length of the string without VT codes for padding.
$valWithVT = $value.propertyValue.ToString()
$valWithoutVT = $valWithVT -replace $VTCodes
if ($AllowVT) {
$sb.AppendFormat(
$Cell,
$valWithVT.PadRight(
$pad + ($valWithVT.Length - $valWithoutVT.Length)
)
)
}
else {
$sb.AppendFormat($Cell, $valWithoutVT.PadRight($pad))
}
$ColumnIndex++
}
if ($Grid) {
$sb.AppendLine().Append($bar)
}
}
else {
# string array, no properties or header.
if ($sb.Length -eq 0) {
$sb.AppendFormat($CellStart, $row.text)
}
else {
$sb.AppendLine().AppendFormat($CellStart, $row.text)
}
}
}
}
}
$sb.Insert(0,"`n").AppendLine().ToString()
}
}
Register-ArgumentCompleter -CommandName ConvertTo-MarkdownTable -ParameterName View -ScriptBlock {
param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
$commandPipeline = $commandAst.Parent
$pipelineIndex = if ($commandPipeline.PipelineElements) {
$commandPipeline.PipelineElements.IndexOf($commandAst)
}
else {
-1
}
if ($pipelineIndex) {
$priorPipeline = $commandPipeline.PipelineElements[0..($pipelineIndex - 1)].Extent -join ' | '
$tabtrick = '{0} | Format-Table -View "{1}' -f $priorPipeline, $wordToComplete
return (TabExpansion2 -inputScript $tabtrick -cursorColumn $tabtrick.Length).CompletionMatches
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment