Skip to content

Instantly share code, notes, and snippets.

@DinoChiesa
Created June 29, 2025 16:07
Show Gist options
  • Save DinoChiesa/30e044408b127fa03ac8ee3218c7d985 to your computer and use it in GitHub Desktop.
Save DinoChiesa/30e044408b127fa03ac8ee3218c7d985 to your computer and use it in GitHub Desktop.
Update Tree Sitter langs to latest for Windows #ps1
<#
.SYNOPSIS
Downloads, extracts, renames, and symlinks the latest Emacs Tree-sitter grammars for Windows.
.DESCRIPTION
This script performs the following actions:
1. Fetches the latest "Release" from https://github.com/emacs-tree-sitter/tree-sitter-langs/releases.
2. Identifies and downloads the `tree-sitter-grammars-windows-0.XX.YYY.tar.gz` file to the system's TEMP directory.
3. Creates a new directory (e.g., `tree-sitter-v0.XX.YYY`) in the current working directory.
4. Extracts the contents of the downloaded archive into this new directory.
5. Renames all `*.dll` files within the extracted directory from `FOO.dll` to `libtree-sitter-FOO.dll`.
6. Navigates up one directory.
7. Deletes any existing junction named "tree-sitter" in the current directory.
8. Creates a new junction named "tree-sitter" pointing to the newly extracted and renamed directory.
.NOTES
- Requires internet connectivity to download the release.
- Assumes 'tar' command is available in the system's PATH.
- The script will create a new directory and a junction in the directory where it is executed.
#>
try {
Write-Host "Purpose: Update the tree-sitter DLLs by downloading the latest build from`ngithub.com/emacs-tree-sitter/tree-sitter-langs/, extracting and renaming." -ForegroundColor Green
Start-Sleep -Seconds 5
# Define the GitHub API URL for releases
$githubApiUrl = "https://api.github.com/repos/emacs-tree-sitter/tree-sitter-langs/releases/latest"
Write-Host "Fetching latest release information from GitHub API: $githubApiUrl" -ForegroundColor Cyan
# Fetch the latest release information
$releaseInfo = Invoke-RestMethod -Uri $githubApiUrl -ErrorAction Stop
# Extract the tag name (e.g., v0.XX.YYY)
$tagName = $releaseInfo.tag_name
Write-Host "Latest release tag found: $tagName" -ForegroundColor Green
# Find the asset URL for the Windows tar.gz file
$downloadAsset = $releaseInfo.assets | Where-Object { $_.name -match "^tree-sitter-grammars-windows-.*\.tar\.gz$" }
if (-not $downloadAsset) {
Write-Error "Could not find the expected 'tree-sitter-grammars-windows-0.XX.YYY.tar.gz' asset in the latest release."
exit 1
}
$downloadUrl = $downloadAsset.browser_download_url
$fileName = $downloadAsset.name
Write-Host "Found download URL: $downloadUrl" -ForegroundColor Green
Write-Host "File to download: $fileName" -ForegroundColor Green
# Define the path to store the downloaded file in TEMP directory
$tempDir = [System.IO.Path]::GetTempPath()
$downloadPath = Join-Path -Path $tempDir -ChildPath $fileName
Write-Host "Download path: $downloadPath" -ForegroundColor Cyan
# Check for existence of the file to be downloaded here. If the file
# already exists in the $tempDir, remove it, before downloading the file
# again.
if (Test-Path -Path $downloadPath) {
Write-Host "Existing file '$fileName' found in TEMP directory. Removing it..." -ForegroundColor Yellow
Remove-Item -Path $downloadPath -Force -ErrorAction SilentlyContinue
Write-Host "Existing file removed." -ForegroundColor Green
}
# Download the file
Write-Host "Downloading $fileName to $downloadPath..." -ForegroundColor Yellow
Invoke-WebRequest -Uri $downloadUrl -OutFile $downloadPath -ErrorAction Stop
Write-Host "Download complete." -ForegroundColor Green
# Extract version from the filename for the new directory name
# Example: tree-sitter-grammars-windows-0.20.0.tar.gz -> 0.20.0
$versionMatch = $fileName -match "tree-sitter-grammars-windows-(\d+\.\d+\.\d+)\.tar\.gz"
if (-not $versionMatch) {
Write-Error "Could not parse version from filename: $fileName. Exiting."
exit 1
}
$version = $Matches[1]
$newDirName = "tree-sitter-v$version"
Write-Host "Detected version: $version. New directory name will be: $newDirName" -ForegroundColor Cyan
# Store the current location to return to it later in the finally block
$originalLocation = Get-Location
# Change into the ~/.emacs.d directory
$emacsDPath = Join-Path -Path $env:USERPROFILE -ChildPath ".emacs.d"
if (-not (Test-Path -Path $emacsDPath -PathType Container)) {
Write-Host "Creating ~/.emacs.d directory: $emacsDPath" -ForegroundColor Cyan
New-Item -Path $emacsDPath -ItemType Directory -Force | Out-Null
}
Set-Location -Path $emacsDPath
Write-Host "Changed current directory to: $(Get-Location)" -ForegroundColor Green
# Define the full path for the new directory
$currentLocation = Get-Location
$newDirPath = Join-Path -Path $currentLocation.Path -ChildPath $newDirName
# Check for existence of the $newDirPath here. If it already exists,
# remove it. Then re-create it.
if (Test-Path -Path $newDirPath) {
Write-Host "Existing directory '$newDirName' found at '$newDirPath'. Removing it..." -ForegroundColor Yellow
Remove-Item -Path $newDirPath -Recurse -Force -ErrorAction SilentlyContinue
Write-Host "Existing directory removed." -ForegroundColor Green
}
Write-Host "Creating new directory: $newDirPath" -ForegroundColor Cyan
# Create the new directory and change into it
New-Item -Path $newDirPath -ItemType Directory -Force | Out-Null # -Force overwrites if exists
Set-Location -Path $newDirPath
Write-Host "Changed current directory to: $(Get-Location)" -ForegroundColor Green
# Extract the files using tar
Write-Host "Extracting files from $downloadPath to $(Get-Location)..." -ForegroundColor Yellow
& "$env:WINDIR\System32\tar.exe" -xvf $downloadPath
Write-Host "Extraction complete." -ForegroundColor Green
# Iterate over extracted files and rename DLLs
Write-Host "Renaming DLL files (FOO.dll to libtree-sitter-FOO.dll)..." -ForegroundColor Yellow
Get-ChildItem -Path . -Filter "*.dll" | Where-Object { -not $_.Name.StartsWith("libtree-sitter-") } | ForEach-Object {
$oldName = $_.Name
$newName = "libtree-sitter-$oldName"
Rename-Item -Path $_.FullName -NewName $newName -Force
Write-Host "Renamed '$oldName' to '$newName'" -ForegroundColor DarkGreen
}
Write-Host "DLL renaming complete." -ForegroundColor Green
# Change directory up one level (back to ~/.emacs.d)
Set-Location -Path ..
Write-Host "Changed current directory to: $(Get-Location)" -ForegroundColor Green
# Define the junction name and its target (fully qualified path)
$junctionName = "tree-sitter"
$junctionTarget = $newDirPath # This is already a fully qualified path
Write-Host "Creating new junction: $junctionName -> $junctionTarget" -ForegroundColor Yellow
# Delete existing junction if it exists
if (Test-Path -Path $junctionName) {
Write-Host "Deleting existing junction: $junctionName" -ForegroundColor Yellow
Remove-Item -Path $junctionName -Recurse -Force
Write-Host "Existing junction deleted." -ForegroundColor Green
} else {
Write-Host "No existing junction '$junctionName' found. Skipping deletion." -ForegroundColor Gray
}
Write-Host "\nIn Windows, users with the 'Create symbolic links' privilege (also known as SeCreateSymbolicLinkPrivilege) are permitted to create Junctions.\nIf you lack this privilege, the following may fail." -ForegroundColor Yellow
Start-Sleep -Seconds 3
# Create new junction
try {
New-Item -Path $junctionName -ItemType Junction -Value $junctionTarget -Force | Out-Null
Write-Host "Junction '$junctionName' created successfully, pointing to '$junctionTarget'." -ForegroundColor Green
Write-Host "Script execution completed successfully!" -ForegroundColor Green
}
catch {
Write-Error "Failed to create junction '$junctionName'."
Write-Error "Error message: $($_.Exception.Message)"
Write-Error " "
Write-Error "To avoid this message in the future, you can grant the 'Create symbolic links' privilege (SeCreateSymbolicLinkPrivilege) to your user account using gpedit.msc. Granting this right requires Administrator privilege."
Write-Error " "
Write-Error "Alternatively, you can manually create the junction by running the following command with elevated (Administrator) permissions:"
Write-Error " New-Item -Path '$junctionName' -ItemType Junction -Value '$junctionTarget' -Force"
exit 1 # Exit with an error code
}
}
catch {
Write-Error "An error occurred during script execution:"
Write-Error $_.Exception.Message
Write-Error "Please ensure you have internet access and 'tar' is in your system's PATH."
exit 1
}
finally {
# Clean up: Remove the downloaded file from the TEMP directory
if (Test-Path -Path $downloadPath) {
Write-Host "Cleaning up: Removing downloaded file '$fileName' from TEMP directory..." -ForegroundColor DarkGray
Remove-Item -Path $downloadPath -Force -ErrorAction SilentlyContinue
Write-Host "Downloaded file removed." -ForegroundColor DarkGray
}
# Return to the original directory where the script was executed
if (Test-Path -Path $originalLocation) {
Set-Location -Path $originalLocation -ErrorAction SilentlyContinue
Write-Host "Returned to original directory: $(Get-Location)" -ForegroundColor DarkGray
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment