Instantly share code, notes, and snippets.
Last active
November 19, 2024 05:00
-
Star
10
(10)
You must be signed in to star a gist -
Fork
3
(3)
You must be signed in to fork a gist
-
Save jcwillox/a9b480f8d180d44368e7df2eb7009207 to your computer and use it in GitHub Desktop.
PowerShell script to automatically add context menu entries for Jetbrains IDEs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Automatically add context menu entries for Jetbrains IDEs. | |
.PARAMETER Name | |
The name or names of the IDEs to add context menus for, use -List to see available IDEs. | |
.PARAMETER BasePath | |
The path to the Toolbox apps directory, defaults to "$env:LOCALAPPDATA\JetBrains\Toolbox\apps". | |
.PARAMETER Global | |
Install context menu entries in HKLM registry (machine wide), requires running as administrator. | |
.PARAMETER Force | |
Overwrite current registry entries, useful when updating existing entries. | |
.PARAMETER Remove | |
Will remove the context menu entries for specified IDEs | |
.PARAMETER List | |
List available IDEs installed via Toolbox | |
.PARAMETER UseNircmd | |
Will use nircmd (if installed) to invisibly run the IDE's batch script, this will avoid you having to | |
re-run this script each time an IDE is updated via Toolbox. | |
.PARAMETER NirCmdPath | |
Specify the location of the nircmd executable to use, will attempt to locate it from $PATH if not specified. | |
.PARAMETER AppDir | |
If you are not using Toolbox you can specify the IDE's installation directory. | |
.PARAMETER DefaultChannel | |
Specify the default channel to use when there are multiple channels found, the user will be prompted to | |
choose a channel if the default channel is not found. | |
#> | |
[CmdletBinding()] | |
param ( | |
[array]$Name, | |
[string]$BasePath, | |
[switch]$Global, | |
[switch]$Force, | |
[switch]$Remove, | |
[switch]$List, | |
[switch]$UseNircmd, | |
[string]$NirCmdPath, | |
[string]$AppDir, | |
[ArgumentCompleter({ return @("'Release'", "'Early Access Program'") })] | |
[string]$DefaultChannel | |
) | |
$ErrorActionPreference = "Stop" | |
trap { throw $Error[0] } | |
$toolbox = if ($BasePath) { $BasePath } else { Join-Path $env:LOCALAPPDATA "JetBrains\Toolbox\apps" } | |
$regRoot = if ($Global) { "HKLM" } else { "HKCU" } | |
Write-Verbose "Toolbox: '$toolbox'" | |
if ($UseNircmd -and -not (Get-Command "nircmd.exe" -ErrorAction Ignore)) { | |
Write-Error "nircmd.exe is not installed or missing from the path" | |
Write-Error "either install nircmd or specify its location using '-NirCmdPath'" | |
return | |
} | |
function Get-Channel([string]$Path) { | |
$channels = Get-ChildItem (Join-Path $Path "ch-*") | |
if (-not $channels) { | |
Write-Warning "no channels found in '$Path'" | |
return | |
} | |
# immediately return if only 1 channel | |
if ($channels.Length -eq 1) { | |
return $channels[0] | |
} | |
$channelsOutput = "" | |
for ($i = 0; $i -lt $channels.Length; $i++) { | |
$channel = $channels[$i] | |
# extract channel type | |
$channelSettings = (Get-Content (Join-Path $channel.FullName ".channel.settings.json") | ConvertFrom-Json) | |
$channelName = $channelSettings.filter.quality_filter.name | |
# skip the prompt if we match the default channel | |
if ($DefaultChannel -eq $channelName) { | |
return $channel | |
} | |
$channelsOutput += "[$i]: '$($channel.BaseName)' ($channelName)" | |
if ($i -lt $channels.Length - 1) { | |
$channelsOutput += "`n" | |
} | |
} | |
# prompt user to select which channel to use | |
Write-Host (Split-Path -Leaf $Path) -ForegroundColor Green | |
Write-Host $channelsOutput | |
$i = $null | |
while (-not ($i -ge 0 -and $i -lt $channels.Length)) { | |
if ($null -ne $i) { | |
Write-Host "'$i' is not a valid index." -ForegroundColor Red | |
} | |
$i = Read-Host "Enter the number of the desired channel" | |
} | |
return $channels[$i] | |
} | |
function Get-IDEs { | |
$IDEs = @{} | |
foreach ($item in (Get-ChildItem $toolbox -Exclude Toolbox)) { | |
$channel = Get-Channel -Path $item.FullName | |
if (-not $channel) { continue } | |
$versionPath = (Get-ChildItem -Path $channel -Directory -Exclude "*.plugins" | Sort-Object BaseName -Descending | Select-Object -First 1 | Select-Object -ExpandProperty FullName) | |
Write-Verbose "Version Path: '$versionPath'" | |
$binPath = Join-Path $versionPath "bin" | |
if (Test-Path $binPath) { | |
$IDEs[$item.BaseName] = $versionPath | |
} | |
} | |
return $IDEs | |
} | |
if ($List) { | |
return Get-IDEs | |
} | |
function Add-ShellKeys { | |
param ( | |
[string]$Name, | |
[string]$ExePath, | |
[string]$Path, | |
[string]$Action, | |
[string]$LaunchArgs | |
) | |
$Path = Join-Path "${regRoot}:" $Path | |
if ($UseNircmd) { | |
# extract shell link path | |
$scriptPath = Get-Content (Join-Path (Split-Path $ExePath) "../.." ".shellLink") | |
# move icon outside of version directory so it will survive updates | |
$baseName = (Split-Path $ExePath -LeafBase).Replace("64", "") | |
$iconPath = Join-Path (Split-Path $ExePath) "$baseName.ico" | |
$destPath = Join-Path (Split-Path (Split-Path (Split-Path $ExePath))) "$baseName.ico" | |
Move-Item -Path $iconPath -Destination $destPath -ErrorAction Ignore | |
$iconPath = "`"$destPath`",0" | |
# contruct args | |
$ExePath = if ($NirCmdPath) { $NirCmdPath } else { (Get-Command "nircmd.exe").Path } | |
$LaunchArgs = "execmd $scriptPath $LaunchArgs" | |
} else { | |
$iconPath = $ExePath | |
} | |
if (-not $Force -and (Test-Path -LiteralPath "$Path\$Name")) { | |
Write-Host "EXISTS: $Path\$Name" -f Yellow | |
} else { | |
New-Item ` | |
-Path "$Path\$Name" ` | |
-Value "$Action with $Name" ` | |
-Force:$Force ` | |
-Confirm:$false | Out-Null | |
Write-Host "ADDED: $Path\$Name" -f DarkGreen | |
} | |
if (-not $Force -and (Get-ItemProperty -LiteralPath "$Path\$Name" -Name "Icon" -ErrorAction Ignore)) { | |
Write-Host "EXISTS: $Path\$Name [Icon]" -f Yellow | |
} else { | |
New-ItemProperty ` | |
-LiteralPath "$Path\$Name" ` | |
-PropertyType ExpandString ` | |
-Name "Icon" ` | |
-Value $iconPath ` | |
-Force:$Force ` | |
-Confirm:$false | Out-Null | |
Write-Host "ADDED: $Path\$Name [Icon]" -f DarkGreen | |
} | |
if (-not $Force -and (Get-Item -LiteralPath "$Path\$Name\command" -ErrorAction Ignore)) { | |
Write-Host "EXISTS: $Path\$Name\command" -f Yellow | |
} else { | |
New-Item ` | |
-Path "$Path\$Name\command" ` | |
-Value "`"$exePath`" $LaunchArgs" ` | |
-Force:$Force ` | |
-Confirm:$false | Out-Null | |
Write-Host "ADDED: $Path\$Name\command" -f DarkGreen | |
} | |
} | |
function Add-RegKey([string]$Path) { | |
$Path = Join-Path "${regRoot}:" $Path | |
if (-not (Test-Path -LiteralPath $Path)) { | |
New-Item -Path $Path -Force -ErrorAction Ignore -Confirm:$false | Out-Null | |
} | |
} | |
function Add-ContextMenu { | |
param ( | |
[string]$Name, | |
[string]$ExePath | |
) | |
# ensure base folders exist | |
Add-RegKey "SOFTWARE\Classes\`*\shell" | |
Add-RegKey "SOFTWARE\Classes\Directory\shell" | |
Add-RegKey "SOFTWARE\Classes\Directory\Background\shell" | |
# add to file context menu | |
Write-Output "ACTION: Edit with $Name (Files)" | |
Add-ShellKeys $Name $ExePath "SOFTWARE\Classes\*\shell" "Edit" "`"%1`"" | |
# add to directory context menu | |
Write-Output "ACTION: Open with $Name (Directory)" | |
Add-ShellKeys $Name $ExePath "SOFTWARE\Classes\Directory\shell" "Open" "`"%1`"" | |
# add to directory background context menu | |
Write-Output "ACTION: Open with $Name (Directory Background)" | |
Add-ShellKeys $Name $ExePath "SOFTWARE\Classes\Directory\Background\shell" "Open" "`"%V`"" | |
} | |
function Remove-Reg([string]$Path) { | |
$Path = Join-Path "${regRoot}:" $Path | |
if (-not (Test-Path -LiteralPath $Path)) { | |
Write-Host "MISSING: $Path" -f Yellow | |
} else { | |
Remove-Item -LiteralPath $Path -Recurse | |
Write-Host "REMOVED: $Path" -f Red | |
} | |
} | |
function Remove-ContextMenu([string]$Name) { | |
Remove-Reg "SOFTWARE\Classes\*\shell\$Name" | |
Remove-Reg "SOFTWARE\Classes\Directory\shell\$Name" | |
Remove-Reg "SOFTWARE\Classes\Directory\Background\shell\$Name" | |
} | |
function Start-ContextMenu([string]$AppDir) { | |
$info = (Get-Content (Join-Path $AppDir "product-info.json") | ConvertFrom-Json) | |
$friendlyName = $info.Name | |
$exePath = Join-Path $AppDir $info.launch[0].launcherPath | |
if ($Remove) { | |
Write-Host "Removing Context Menu for '$friendlyName'" -ForegroundColor Cyan | |
Remove-ContextMenu -Name $friendlyName | |
} else { | |
Write-Host "Adding Context Menu for '$friendlyName'" -ForegroundColor Cyan | |
Add-ContextMenu -Name $friendlyName -ExePath $exePath | |
} | |
} | |
if ($AppDir) { | |
Start-ContextMenu $AppDir | |
} else { | |
$ides = (Get-IDEs) | |
foreach ($ide in $ides.Keys) { | |
foreach ($match in $Name) { | |
if (($match -eq "*") -or ($ide.ToLower().StartsWith($match.ToLower()))) { | |
Start-ContextMenu $ides[$ide] | |
break | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yep thanks, the download command is broken in my terminal now too, pretty bad of them to break that in a minor update, but anyhow, I've updated the download command.
That comment provides the usage instructions too, and it looks like you're not running the correct command, for toolbox v2, you should use something like this