#Requires -Version 5.1 <# Windows-natives Pendant zu bootstrap/neovim.sh (macOS/FreeBSD). bootstrap/neovim.sh bleibt unveraendert - dieses Skript ist bewusst eigenstaendig, da sich die Paketquellen (winget/npm statt brew/pkg) und das Fehlerverhalten zu stark unterscheiden fuer ein gemeinsames Skript. Nutzt ausschliesslich native Windows-Werkzeuge (winget, npm, pwsh/ powershell.exe) - keine Git-Bash- oder WSL-Abhaengigkeit. Aufruf (PowerShell, keine Admin-Rechte noetig): .\bootstrap-win\neovim.ps1 Hinweis: yamlfmt, gawk und xmllint/libxml2 sind bewusst NICHT enthalten - im Praxistest (24.07.2026) bestaetigt, dass es dafuer keine winget-Pakete unter den urspruenglich vermuteten IDs gibt. Siehe Install-Formatters und neovim-windows-setup.md fuer manuelle Alternativen. mvdan.shfmt und astral-sh.ruff liefen im Test erfolgreich durch. Schlaegt eine winget-Installation trotzdem fehl, meldet das Skript das als Warnung (bricht nicht ab) und zeigt die letzten Zeilen der winget- Ausgabe zur Fehlersuche an. C-Compiler fuer nvim-treesitter: kein winget-Paket enthalten, da MSVC auf den Zielsystemen bereits vorhanden ist. Neovim muss dafuer aus einer "Developer PowerShell for VS" gestartet werden (siehe Install-Tools). #> $ErrorActionPreference = "Stop" $script:Failures = @() function Write-Ok($msg) { Write-Host "OK $msg" -ForegroundColor Green } function Write-Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan } function Write-Skip($msg) { Write-Host "- $msg bereits vorhanden" -ForegroundColor DarkGray } function Write-Warn($msg) { Write-Host "! $msg" -ForegroundColor Yellow $script:Failures += $msg } function Test-CommandExists([string]$Name) { return [bool](Get-Command $Name -ErrorAction SilentlyContinue) } function Find-BinaryDirectory([string]$Binary) { # Sucht nach ".exe" unter den ueblichen Installationswurzeln. # Deckt den Fall ab, dass ein winget-Paket (v.a. im --silent-Modus) # die PATH-Registrierung ausgelassen hat, obwohl es korrekt installiert # wurde - beobachtet bei LLVM.LLVM/clangd. $searchRoots = @( $env:ProgramFiles, ${env:ProgramFiles(x86)}, (Join-Path $env:LOCALAPPDATA "Programs") ) | Where-Object { $_ -and (Test-Path $_) } foreach ($root in $searchRoots) { $found = Get-ChildItem -Path $root -Recurse -Depth 4 -Filter "$Binary.exe" -File -ErrorAction SilentlyContinue | Select-Object -First 1 if ($found) { return $found.DirectoryName } } return $null } function Add-ToUserPath([string]$Dir) { $userPath = [Environment]::GetEnvironmentVariable("PATH", "User") $entries = $userPath -split ";" if ($entries -contains $Dir) { return $false } [Environment]::SetEnvironmentVariable("PATH", "$userPath;$Dir", "User") $env:PATH = "$env:PATH;$Dir" return $true } function Install-WingetPackage([string]$Binary, [string]$Id) { if (Test-CommandExists $Binary) { Write-Skip $Binary return } Write-Step "winget install $Id" $output = winget install --id $Id -e --source winget --accept-package-agreements --accept-source-agreements --silent 2>&1 if ($LASTEXITCODE -ne 0) { $text = $output -join " " $lastLine = ($output | Select-Object -Last 3) -join " / " if ($text -match "bereits ein vorhandenes Paket|already installed|No applicable update") { # Paket ist laut winget installiert, aber die Binary steht trotzdem # nicht im PATH - z.B. weil der Installer (im --silent-Modus) die # PATH-Registrierung uebersprungen hat. winget search hilft hier # nicht weiter, da das Paket ja gefunden wurde - stattdessen selbst # danach suchen und den User-PATH ergaenzen (dauerhaft + fuer diese # Sitzung). $dir = Find-BinaryDirectory $Binary if ($dir) { Add-ToUserPath $dir | Out-Null if (Test-CommandExists $Binary) { Write-Ok "$Id ('$Binary' in $dir gefunden, User-PATH ergaenzt)" } else { Write-Warn "$Id: $dir zum User-PATH hinzugefuegt, aber '$Binary' laesst sich immer noch nicht ausfuehren - manuell pruefen." } } else { Write-Warn "$Id ist laut winget bereits installiert, aber '$Binary' wurde nicht gefunden und konnte auch nicht automatisch lokalisiert werden (durchsucht: Program Files, Program Files (x86), $env:LOCALAPPDATA\Programs). Installationsordner manuell suchen und zum PATH hinzufuegen." } } else { Write-Warn "winget install $Id fehlgeschlagen (Binary '$Binary' nicht gefunden): $lastLine -- Pruefen mit: winget search $Id" } return } Write-Ok $Id } function Install-NpmPackage([string]$Binary, [string]$Package) { if (Test-CommandExists $Binary) { Write-Skip $Binary return } if (-not (Test-CommandExists "npm")) { Write-Warn "npm wurde nicht gefunden - kann '$Package' nicht installieren." return } Write-Step "npm install -g $Package" npm install -g $Package | Out-Null if ($LASTEXITCODE -ne 0) { Write-Warn "npm install -g $Package fehlgeschlagen." return } Write-Ok $Package } function Test-Prerequisites { if (-not (Test-CommandExists "winget")) { Write-Host "Fehler: winget wurde nicht gefunden. 'App Installer' aus dem Microsoft Store installieren." -ForegroundColor Red exit 1 } } function Update-SessionPath { # winget/npm-Installer tragen PATH-Aenderungen in die Registry (User- # und/oder Machine-Scope) ein, aber der laufende PowerShell-Prozess # bekommt das nicht automatisch mit. Ohne diesen Refresh wuerde z.B. # "npm" gleich im Anschluss an die Node-Installation als "nicht # gefunden" gemeldet, obwohl es gerade erst installiert wurde. $machine = [Environment]::GetEnvironmentVariable("PATH", "Machine") $user = [Environment]::GetEnvironmentVariable("PATH", "User") $env:PATH = "$machine;$user" } function Install-Prerequisites { Install-WingetPackage -Binary "git" -Id "Git.Git" Install-WingetPackage -Binary "node" -Id "OpenJS.NodeJS.LTS" Update-SessionPath } function Install-Formatters { Install-WingetPackage -Binary "jq" -Id "jqlang.jq" Install-NpmPackage -Binary "prettier" -Package "prettier" Install-WingetPackage -Binary "ruff" -Id "astral-sh.ruff" Install-WingetPackage -Binary "shfmt" -Id "mvdan.shfmt" Install-WingetPackage -Binary "stylua" -Id "JohnnyMorganz.StyLua" # yamlfmt, gawk, xmllint: im Praxistest bestaetigt, dass es unter diesen # IDs kein winget-Paket gibt ("Es wurde kein Paket gefunden, das den # Eingabekriterien entspricht"). Kein verifizierter Ersatz gefunden, # deshalb bewusst nicht automatisiert - siehe neovim-windows-setup.md # fuer manuelle Alternativen. conform.nvim faellt bei fehlendem # Formatter fuer die jeweilige Dateiart einfach auf LSP-Formatierung # zurueck (default_format_opts.lsp_format = "fallback"), betrifft also # nur awk/xml/xsd/yaml-Dateien. } function Install-Tools { Install-WingetPackage -Binary "rg" -Id "BurntSushi.ripgrep.MSVC" Install-WingetPackage -Binary "fd" -Id "sharkdp.fd" Install-NpmPackage -Binary "tree-sitter" -Package "tree-sitter-cli" # C-Compiler fuer nvim-treesitter: kein winget-Paket noetig, MSVC (cl.exe) # ist auf diesen Systemen bereits vorhanden. nvim-treesitter findet cl.exe # automatisch, ABER nur wenn INCLUDE/LIB gesetzt sind - das passiert nicht # in einer normalen PowerShell, sondern nur in einer "Developer PowerShell # for VS" bzw. "x64 Native Tools Command Prompt". Neovim also immer aus # so einer Shell heraus starten, sonst schlaegt die Parser-Kompilierung # mit "cannot find
.h" o.ae. fehl, obwohl cl.exe selbst gefunden wird. # opt.makeprg = "gmake" (nvim/lua/config/options.lua) - Alias liegt in # windows/bin/gmake.cmd, reicht an "make" durch. Einmalig windows\bin # zum PATH hinzufuegen (PowerShell, User-Scope, nicht setx mit %PATH% # verwenden - das wuerde den gesamten Prozess-PATH inkl. System-Anteil # dauerhaft in den User-PATH kopieren): # $userPath = [Environment]::GetEnvironmentVariable("PATH", "User") # [Environment]::SetEnvironmentVariable("PATH", "$userPath;$env:USERPROFILE\dotfiles\windows\bin", "User") # GnuWin32.Make ist im Praxistest fehlgeschlagen (Paket 2006er-Stand, # unklar ob im winget-Repo ueberhaupt noch gepflegt). ezwinports.make # ist ein aktiver gepflegter GNU-Make-Port fuer Windows. Install-WingetPackage -Binary "make" -Id "ezwinports.make" } function Install-Lsp { Install-NpmPackage -Binary "basedpyright-langserver" -Package "basedpyright" Install-NpmPackage -Binary "bash-language-server" -Package "bash-language-server" Install-WingetPackage -Binary "clangd" -Id "LLVM.LLVM" Install-WingetPackage -Binary "shellcheck" -Id "koalaman.shellcheck" Install-NpmPackage -Binary "vscode-html-language-server" -Package "vscode-langservers-extracted" Install-NpmPackage -Binary "vscode-json-language-server" -Package "vscode-langservers-extracted" Install-WingetPackage -Binary "lua-language-server" -Id "LuaLS.lua-language-server" Install-NpmPackage -Binary "prisma-language-server" -Package "@prisma/language-server" Install-NpmPackage -Binary "vtsls" -Package "@vtsls/language-server" } function Main { Test-Prerequisites Install-Prerequisites Install-Formatters Install-Tools Install-Lsp Write-Host "" if ($script:Failures.Count -eq 0) { Write-Host "Bootstrap abgeschlossen." -ForegroundColor Green } else { Write-Host "Bootstrap abgeschlossen, mit $($script:Failures.Count) Warnung(en):" -ForegroundColor Yellow foreach ($f in $script:Failures) { Write-Host " - $f" -ForegroundColor Yellow } } } Main