Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ dotnet_diagnostic.IDE0029.severity = warning
dotnet_diagnostic.IDE0030.severity = warning
dotnet_diagnostic.IDE0270.severity = warning
dotnet_diagnostic.IDE0019.severity = warning
dotnet_diagnostic.IDE0010.severity = none

# Prefer var when the type is apparent (modern and concise)
# how does this work with IDE0007?
Expand Down Expand Up @@ -156,6 +157,7 @@ csharp_style_unused_value_expression_statement_preference = unused_local_variabl
# Nullable reference types - enabled as suggestions; project opt-in controls runtime enforcement
nullable = enable
csharp_style_prefer_primary_constructors = false
dotnet_diagnostic.IDE0072.severity = none

# Formatting / newline preferences
# prefer Stroustrup
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ src/.vs/*
Module/lib
debug.md
[Oo]utput/
*/obj/*
*/bin/*
**/obj/**
**/bin/**
.github/chatmodes/*
.github/instructions/*
.github/prompts/*
ref/**
Copilot-Processing.md
tools/**
src/Utilities/TMConsole.cs
19 changes: 19 additions & 0 deletions Module/TextMate.format.ps1xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,24 @@
</TableRowEntries>
</TableControl>
</View>
<View>
<Name>HighlightedText</Name>
<ViewSelectedBy>
<TypeName>PSTextMate.Core.HighlightedText</TypeName>
</ViewSelectedBy>
<CustomControl>
<CustomEntries>
<CustomEntry>
<CustomItem>
<ExpressionBinding>
<ScriptBlock>
[PSTextMate.Utilities.Writer]::Write($_, $false, $true)
</ScriptBlock>
</ExpressionBinding>
</CustomItem>
</CustomEntry>
</CustomEntries>
</CustomControl>
</View>
</ViewDefinitions>
</Configuration>
10 changes: 6 additions & 4 deletions Module/TextMate.psd1
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
@{
RootModule = 'lib/PSTextMate.dll'
ModuleVersion = '0.1.0'
RootModule = 'TextMate.psm1'
ModuleVersion = '0.2.0'
GUID = 'fe78d2cb-2418-4308-9309-a0850e504cd6'
Author = 'trackd'
CompanyName = 'trackd'
Copyright = '(c) trackd. All rights reserved.'
Description = 'A PowerShell module for syntax highlighting using TextMate grammars. Using PwshSpectreConsole for rendering.'
Description = 'A PowerShell module for syntax highlighting using TextMate grammars with built-in Spectre rendering.'
PowerShellVersion = '7.4'
CompatiblePSEditions = 'Core'
CmdletsToExport = @(
'Format-TextMate'
'Format-CSharp'
'Format-Markdown'
'Format-PowerShell'
'Out-Page'
'Test-TextMate'
'Get-TextMateGrammar'
)
Expand All @@ -22,12 +23,13 @@
'fps'
'ftm'
'Show-TextMate'
'page'
)
FormatsToProcess = 'TextMate.format.ps1xml'
RequiredModules = @(
@{
ModuleName = 'PwshSpectreConsole'
ModuleVersion = '2.3.0'
ModuleVersion = '2.6.3'
MaximumVersion = '2.99.99'
}
)
Expand Down
44 changes: 44 additions & 0 deletions Module/TextMate.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using namespace System.IO
using namespace System.Management.Automation
using namespace System.Reflection

$importModule = Get-Command -Name Import-Module -Module Microsoft.PowerShell.Core
$isReload = $true
$alcAssemblyPath = [Path]::Combine($PSScriptRoot, 'lib', 'PSTextMate.ALC.dll')

if (-not (Test-Path -Path $alcAssemblyPath -PathType Leaf)) {
throw "Could not find required ALC assembly at '$alcAssemblyPath'."
}

if (-not ('PSTextMate.ALC.LoadContext' -as [type])) {
$isReload = $false
Add-Type -Path $alcAssemblyPath
}
else {
$loadedAlcAssemblyPath = [PSTextMate.ALC.LoadContext].Assembly.Location
if ([Path]::GetFullPath($loadedAlcAssemblyPath) -ne [Path]::GetFullPath($alcAssemblyPath)) {
throw "PSTextMate.ALC.LoadContext is already loaded from '$loadedAlcAssemblyPath'. Restart PowerShell to load this module from '$alcAssemblyPath'."
}
}

$mainModule = [PSTextMate.ALC.LoadContext]::Initialize()
$innerMod = &$importModule -Assembly $mainModule -PassThru


if ($isReload) {
# https://github.com/PowerShell/PowerShell/issues/20710
$addExportedCmdlet = [PSModuleInfo].GetMethod(
'AddExportedCmdlet',
[BindingFlags]'Instance, NonPublic'
)
$addExportedAlias = [PSModuleInfo].GetMethod(
'AddExportedAlias',
[BindingFlags]'Instance, NonPublic'
)
foreach ($cmd in $innerMod.ExportedCmdlets.Values) {
$addExportedCmdlet.Invoke($ExecutionContext.SessionState.Module, @(, $cmd))
}
foreach ($alias in $innerMod.ExportedAliases.Values) {
$addExportedAlias.Invoke($ExecutionContext.SessionState.Module, @(, $alias))
}
}
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# TextMate

TextMate delivers syntax-aware highlighting for PowerShell on top of TextMate grammars. It exposes a focused set of cmdlets that emit tokenized, theme-styled `HighlightedText` renderables you can write with PwshSpectreConsole or feed into any Spectre-based pipeline. Helper cmdlets make it easy to discover grammars and validate support for files, extensions, or language IDs before formatting.
TextMate delivers syntax-aware highlighting for PowerShell on top of TextMate grammars. It exposes a focused set of cmdlets that emit tokenized, theme-styled `HighlightedText` renderables you can write directly or feed into any Spectre-based pipeline. Helper cmdlets make it easy to discover grammars and validate support for files, extensions, or language IDs before formatting.

What it does

- Highlights source text using TextMate grammars such as PowerShell, C#, Markdown, and Python.
- Returns `HighlightedText` renderables that implement Spectre.Console's contract, so they can be written through PwshSpectreConsole or other Spectre hosts.
- Builtin pager, either through `-Page` or piping to `Out-Page`
- Returns `HighlightedText` renderables that implement Spectre.Console's contract, so they can be written directly or through other Spectre hosts.
- Provides discovery and testing helpers for installed grammars, extensions, or language IDs.
- Does inline Sixel images in markdown
- Sixel images in markdown.

![Demo](./assets/demo.png)

Expand All @@ -21,9 +22,10 @@ What it does
| [Format-PowerShell](docs/en-us/Format-PowerShell.md) | Highlight PowerShell code |
| [Get-TextMateGrammar](docs/en-us/Get-TextMateGrammar.md) | List available grammars and file extensions. |
| [Test-TextMate](docs/en-us/Test-TextMate.md) | Check support for a file, extension, or language ID. |
| [Out-Page](docs/en-us/Out-Page.md) | Builtin terminal pager |

```note
Format-CSharp/Markdown/Powershell is just sugar for Format-TextMate -Language CSharp/PowerShell/Markdown
Format-CSharp/Markdown/Powershell is just syntactic sugar for Format-TextMate -Language CSharp/PowerShell/Markdown
```

## Examples
Expand All @@ -35,6 +37,9 @@ Format-CSharp/Markdown/Powershell is just sugar for Format-TextMate -Language CS
# render a Markdown file with a theme
Get-Content README.md -Raw | Format-Markdown -Theme SolarizedLight

# FileInfo Object
Get-Item .\script.ps1 | Format-TextMate

# list supported grammars
Get-SupportedTextMate
```
Expand Down Expand Up @@ -77,8 +82,7 @@ Import-Module .\output\TextMate.psd1

- [TextMateSharp](https://github.com/danipen/TextMateSharp)
- [OnigWrap](https://github.com/aikawayataro/Onigwrap)
- [PwshSpectreConsole](https://github.com/ShaunLawrie/PwshSpectreConsole)
- [SpectreConsole](https://github.com/spectreconsole/spectre.console)
- [SpectreConsole](https://github.com/spectreconsole/spectre.console)

---

Expand Down
47 changes: 30 additions & 17 deletions TextMate.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ param(
[switch]$SkipHelp,
[switch]$SkipTests
)

Write-Host "$($PSBoundParameters.GetEnumerator())" -ForegroundColor Cyan

$modulename = [System.IO.Path]::GetFileName($PSCommandPath) -replace '\.build\.ps1$'
# $modulename = 'PSTextMate'

$script:folders = @{
ModuleName = $modulename
ProjectRoot = $PSScriptRoot
TempLib = Join-Path $PSScriptRoot 'templib'
SourcePath = Join-Path $PSScriptRoot 'src'
OutputPath = Join-Path $PSScriptRoot 'output'
DestinationPath = Join-Path $PSScriptRoot 'output' 'lib'
ModuleSourcePath = Join-Path $PSScriptRoot 'module'
DocsPath = Join-Path $PSScriptRoot 'docs' 'en-US'
TestPath = Join-Path $PSScriptRoot 'tests'
CsprojPath = Join-Path $PSScriptRoot 'src' "$modulename.csproj"
CsprojPath = Join-Path $PSScriptRoot 'src' 'PSTextMate' 'PSTextMate.csproj'
}

task Clean {
Expand All @@ -35,14 +36,32 @@ task Build {
Write-Warning 'C# project not found, skipping Build'
return
}
exec { dotnet publish $folders.CsprojPath --configuration $Configuration --nologo --verbosity minimal --output $folders.TempLib }
exec {
dotnet publish $folders.CsprojPath --configuration $Configuration --nologo --verbosity minimal --output $folders.TempLib
}
$null = New-Item -Path $folders.outputPath -ItemType Directory -Force
$rids = @('win-x64', 'osx-arm64', 'linux-x64','linux-arm64','win-arm64')
foreach ($rid in $rids) {
$ridDest = Join-Path $folders.DestinationPath $rid
$null = New-Item -Path $ridDest -ItemType Directory -Force
$nativePath = Join-Path $folders.TempLib 'runtimes' $rid 'native'
Get-ChildItem -Path $nativePath -File | Move-Item -Destination $ridDest -Force
if (-not (Test-Path $nativePath -PathType Container)) {
continue
}

foreach ($nativeFile in Get-ChildItem -Path $nativePath -File) {
$destinationFile = Join-Path $ridDest $nativeFile.Name
try {
if (Test-Path $destinationFile -PathType Leaf) {
Remove-Item -Path $destinationFile -Force
}

Move-Item -Path $nativeFile.FullName -Destination $ridDest -Force
}
catch {
Write-Warning "Skipping native file update for '$destinationFile': $($_.Exception.Message)"
}
}
}
Get-ChildItem -Path $folders.TempLib -File | Move-Item -Destination $folders.DestinationPath -Force
if (Test-Path -Path $folders.TempLib -PathType Container) {
Expand Down Expand Up @@ -77,12 +96,6 @@ task GenerateHelp -if (-not $SkipHelp) {
return
}

if (-Not (Get-Module PwshSpectreConsole -ListAvailable)) {
# just temporarily while im refactoring the PwshSpectreConsole module.
$ParentPath = Split-Path $folders.ProjectRoot -Parent
Import-Module (Join-Path $ParentPath 'PwshSpectreConsole' 'output' 'PwshSpectreConsole.psd1')
}

Import-Module $modulePath -Force

$helpOutputPath = Join-Path $folders.OutputPath 'en-US'
Expand Down Expand Up @@ -110,12 +123,6 @@ task Test -if (-not $SkipTests) {
return
}

if (-not (Get-Module PwshSpectreConsole -ListAvailable)) {
# just temporarily while im refactoring the PwshSpectreConsole module.
$ParentPath = Split-Path $folders.ProjectRoot -Parent
Import-Module (Join-Path $ParentPath 'PwshSpectreConsole' 'output' 'PwshSpectreConsole.psd1')
}

Import-Module (Join-Path $folders.OutputPath ($folders.ModuleName + '.psd1')) -ErrorAction Stop
Import-Module (Join-Path $folders.TestPath 'testhelper.psm1') -ErrorAction Stop

Expand All @@ -127,12 +134,18 @@ task Test -if (-not $SkipTests) {
Invoke-Pester -Configuration $pesterConfig
}

task DotNetTest -if (-not $SkipTests) {
exec {
dotnet test (Join-Path $PSScriptRoot 'tests' 'PSTextMate.InteractiveTests' 'PSTextMate.InteractiveTests.csproj') --configuration $Configuration --nologo
}
}

task CleanAfter {
if ($script:folders.DestinationPath -and (Test-Path $script:folders.DestinationPath)) {
Get-ChildItem $script:folders.DestinationPath -File -Recurse | Where-Object { $_.Extension -in '.pdb', '.json' } | Remove-Item -Force -ErrorAction Ignore
}
}


task All -Jobs Clean, Build, ModuleFiles, GenerateHelp, CleanAfter , Test
task All -Jobs Clean, Build, ModuleFiles, GenerateHelp, CleanAfter, Test, DotNetTest
task BuildAndTest -Jobs Clean, Build, ModuleFiles, CleanAfter #, Test
4 changes: 3 additions & 1 deletion TextMate.slnx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<Solution>
<Project Path="src/PSTextMate.csproj" />
<Project Path="src/PSTextMate/PSTextMate.csproj" />
<Project Path="src/PSTextMate.ALC/PSTextMate.ALC.csproj" />
<Project Path="tests/PSTextMate.InteractiveTests/PSTextMate.InteractiveTests.csproj" />
</Solution>
2 changes: 2 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ param(
)

$ErrorActionPreference = 'Stop'


# Helper function to get paths
$buildparams = @{
Configuration = $Configuration
Expand Down
Loading
Loading