This is the VS Code extension for PowerShell — an LSP client that communicates with PowerShellEditorServices (PSES), the LSP server. The extension manages the PSES process lifecycle, provides UI features, and handles debugging.
npm install --include=optional # Install all deps (lint/test tools are in optionalDependencies)
npm run compile # Build with esbuild (outputs to dist/)
npm run lint # ESLint (strict TypeScript-aware rules)
npm run format # Prettier check (with organize-imports plugin)
npm test # Integration tests via @vscode/test-cliAfter any code change, always run npm run compile, npm run lint, and npm run format. Tests run inside a VS Code Insiders instance — there is no way to run a
single test from the command line. Tests live in test/ mirroring src/ structure and use
Mocha BDD (describe/it).
activate() in src/extension.ts creates the Logger, SessionManager, and two groups of
features:
- Standalone features — commands that don't need the LSP client (e.g.
PesterTestsFeature) - LanguageClientConsumers — features extending
LanguageClientConsumerthat depend on the LSP client and overrideonLanguageClientSet()to register their handlers
SessionManager (src/session.ts) owns the full lifecycle: finding a PowerShell executable
(src/platform.ts), spawning PSES (src/process.ts), connecting the LanguageClient, and
restarting on critical setting changes.
Each feature in src/features/ exports one vscode.Disposable class. Custom LSP message
types are defined as RequestType/NotificationType constants in the same file.
IPowerShellExtensionClient in src/features/ExternalApi.ts is the public API for other
extensions.
The modules/ folder contains the PSES, PSReadLine, and PSScriptAnalyzer PowerShell modules. In development the whole
folder is a symlink to ../PowerShellEditorServices/module — PowerShellEditorServices must be
cloned as a sibling and built before npm run compile will succeed. For cross-repo work, use pwsh-extension-dev.code-workspace.
Cross-repo dev/test cycle. Because modules/ is a symlink into the sibling PSES checkout's
module/ directory, building PSES deploys its DLLs straight into the path the extension loads from —
there is no copy step:
- Edit PSES C# (server) → rebuild PSES (e.g.
dotnet build src/PowerShellEditorServices/PowerShellEditorServices.csproj, orInvoke-Build Buildfor a full build). The build deploys intomodule/PowerShellEditorServices/bin, which the symlinkedmodules/exposes to the extension automatically. The extension (and its tests) then load the new DLL — no copy, but you must rebuild PSES, since the extension does not. - Edit extension TypeScript (client) →
npm run compile. - Verify end-to-end →
npm test. This launches a real VS Code Extension Host with PSES connected and runs the Mocha suite, exercising the locally-built PSES through the symlink. Prefer this over only eyeballing the Extension Development Host: it is the way to confirm cross-repo (client + server) changes actually work, and to catch regressions. After changing a setting's default or any shared behavior, run the full suite — e.g. ISE-compatibility tests assert against setting defaults.
- VS Code best practices: Follow the Extension Guidelines and UX Guidelines. Use VS Code's APIs idiomatically and prefer disposable patterns for lifecycle management.
- Logging: Use
ILogger(notconsole.log). Tests useTestLoggerfromtest/utils.ts. - Settings: Defined in
package.jsonundercontributes.configuration, read viavscode.workspace.getConfiguration("powershell")at point of use. Helpers insrc/settings.ts. - TypeScript: Strict mode, ESNext,
verbatimModuleSyntax.explicit-function-return-typeenforced. Unused vars prefixed_. Formatting via Prettier with theorganize-importsplugin. Useimport x = require("x")for Node/VS Code built-ins. - File headers: Every source file starts with
// Copyright (c) Microsoft Corporation.and// Licensed under the MIT License.
Every pull request must be labeled before it is opened so it is triaged correctly and lands in the right changelog section.
Each PR requires:
- At least one
Area-*label describing the part of the codebase it touches (e.g.Area-Debugging,Area-IntelliSense,Area-Workspaces,Area-Documentation). This is used for triage and filtering. - Exactly one
Issue-*label describing the kind of change:Issue-Bug— a bug fix.Issue-Enhancement— a new feature or changed behavior.Issue-Performance— a performance improvement.
The Issue-* label is not optional: GitHub's auto-generated release notes use it
to pick the changelog category. See .github/release.yml for the
authoritative mapping (Issue-Enhancement → "Enhancements & Features ✨",
Issue-Bug → "Squashed Bugs 🐛"). Any PR without an Issue-* label falls
through the "*" catch-all into "Other Changes 🙏" and is silently
miscategorized, so always set one correctly.
Additionally:
- Add the relevant
OS-*label (OS-Windows,OS-macOS,OS-Linux) when a change is platform-specific. - Use the
Ignorelabel only for changes that should be excluded from the release notes entirely (e.g. pure CI, test, or docs chores the maintainers do not want in the changelog)..github/release.ymlexcludes this label from the changelog.
The full, authoritative set of labels drifts over time — do not hard-code it
here. List the current labels with gh label list --repo PowerShell/vscode-powershell
or the repo's Labels page
and pick the closest matches.