Skip to content

Release 0.2.0 with multi-project support based on vscode-clangd 0.6.0 - #11

Open
planger wants to merge 157 commits into
mainfrom
release-0.2.0
Open

planger wants to merge 157 commits into
mainfrom
release-0.2.0

Conversation

@planger

@planger planger commented Nov 9, 2026 •

Copy link
Copy Markdown

So far main still contains the multi-client proof of concept from 2024 (0.1.0). The multi-project-2026-10 branch reimplements the multi-project support on top of vscode-clangd 0.6.0 and its master branch up to 58df17e, adds API v2 and packages the fork as eclipse-cdt.vscode-clangd-cdtcloud 0.2.0.

A plain merge conflicts in seven files, as both lines of history touch the same code. Thus, this PR contains a merge commit that takes the tree of multi-project-2026-10 (9929b9f) as is, so main keeps its history but ends up with exactly the content of the branch.

After merging, I'll push the tag v0.2.0 on main. The CI workflow then checks that the tag matches the version, creates the GitHub release and publishes 0.2.0 to Open VSX and the VS Code Marketplace.

WDYT?

sam-mccall and others added 30 commits March 3, 2024 00:55
…iate. (clangd#398)

As recommended in microsoft/vscode#78806
This is an incomplete fix but better than nothing.
Also files microsoft/vscode#164310 in case
VSCode can improve the behavior here.

fixes clangd#390
The intention was to only catch the case where the language server
is already starting because running the restart command triggered
activation of the plugin, but it was also catching the case where
the language server wasn't running at all.

Fixes clangd#599
Prior to db14900, this function call had the possible side effect of showing the user a prompt, and we were calling it for this side effect.
In VSCode 1.72 and later (which uses node 20), this requires passing `shell: true` in the executable options.
With `shell: true`, this is needed in case the path contains spaces.
Co-authored-by: Jeroen Van Antwerpen <JVanAntwerpen@omp.com>
…perly (clangd#728)

As part of this change, uses of the non-null assertion operator (`!`) are removed.

To facilitate doing this for `ClangdContext.client`, the code is reorganized
slightly so that in the cases where this field would have been null, a
ClangdContext object is not created in the first place.
…gd#730)

In VSCode 1.92 and later (which uses node 20), this requires passing `shell: true` in the executable options.

However, using `shell: true` changes the behavior in some other ways, e.g. the path and arguments now have to quoted in case they contain spaces.

To avoid the potential for regressions from using `shell: true`, its use is made conditional on a new clangd option, "clangd.useScriptAsExecutable".

Fixes clangd#683
Save the disposables passed in to install.activate() for cleanup
…langd#743)

Other minor cleanup:
  - do not await config.get(), it's not async
  - move initialization of clangdArguments into create()
The workflow is manually triggered, and creates a Github
release containing a build from the master branch.
dependabot Bot added 8 commits October 20, 2026 15:57
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.16.0 to 1.16.1.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](follow-redirects/follow-redirects@v1.16.0...v1.16.1)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [markdown-it](https://github.com/markdown-it/markdown-it) from 14.3.2 to 14.3.3.
- [Changelog](https://github.com/markdown-it/markdown-it/blob/14.3.3/CHANGELOG.md)
- [Commits](markdown-it/markdown-it@14.3.2...14.3.3)

---
updated-dependencies:
- dependency-name: markdown-it
  dependency-version: 14.3.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.8 to 3.1.9.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.8...v3.1.9)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 7.1.2 to 7.1.4.
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](yahoo/serialize-javascript@v7.1.2...v7.1.4)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-version: 7.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [undici](https://github.com/nodejs/undici) from 7.30.0 to 7.30.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](nodejs/undici@v7.30.0...v7.30.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.30.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@azure/msal-node](https://github.com/AzureAD/microsoft-authentication-library-for-js) and [@azure/identity](https://github.com/Azure/azure-sdk-for-js/tree/HEAD/sdk/identity/identity). These dependencies needed to be updated together.

Updates `@azure/msal-node` from 6.0.1 to 7.1.2
- [Release notes](https://github.com/AzureAD/microsoft-authentication-library-for-js/releases)
- [Commits](AzureAD/microsoft-authentication-library-for-js@msal-node-v6.0.1...msal-node-v7.1.2)

Updates `@azure/identity` from 4.13.3 to 4.14.1
- [Release notes](https://github.com/Azure/azure-sdk-for-js/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-js/blob/@azure/identity_4.14.1/sdk/identity/identity/CHANGELOG.md)
- [Commits](https://github.com/Azure/azure-sdk-for-js/commits/@azure/identity_4.14.1/sdk/identity/identity)

---
updated-dependencies:
- dependency-name: "@azure/msal-node"
  dependency-version: 7.1.2
  dependency-type: indirect
- dependency-name: "@azure/identity"
  dependency-version: 4.14.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [qs](https://github.com/ljharb/qs) from 6.15.2 to 6.17.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](ljharb/qs@v6.15.2...v6.17.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.17.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps the npm-packages group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [vscode-languageclient](https://github.com/Microsoft/vscode-languageserver-node/tree/HEAD/client) | `10.1.2` | `11.0.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.6.3` | `26.6.6` |
| [@types/sinon](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/sinon) | `22.0.0` | `22.0.2` |
| [@vscode/vsce](https://github.com/Microsoft/vsce) | `4.0.0` | `4.1.0` |
| [glob](https://github.com/isaacs/node-glob) | `13.0.6` | `14.0.3` |
| [mocha](https://github.com/mochajs/mocha) | `12.0.2` | `12.0.3` |
| [ovsx](https://github.com/eclipse-openvsx/openvsx/tree/HEAD/cli) | `1.2.0` | `1.3.0` |


Updates `vscode-languageclient` from 10.1.2 to 11.0.0
- [Release notes](https://github.com/Microsoft/vscode-languageserver-node/releases)
- [Commits](https://github.com/Microsoft/vscode-languageserver-node/commits/release/client/11.0.0/client)

Updates `@types/node` from 26.6.3 to 26.6.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@types/sinon` from 22.0.0 to 22.0.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/sinon)

Updates `@vscode/vsce` from 4.0.0 to 4.1.0
- [Release notes](https://github.com/Microsoft/vsce/releases)
- [Commits](microsoft/vscode-vsce@v4.0.0...v4.1.0)

Updates `glob` from 13.0.6 to 14.0.3
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](isaacs/node-glob@v13.0.6...v14.0.3)

Updates `mocha` from 12.0.2 to 12.0.3
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](mochajs/mocha@v12.0.2...v12.0.3)

Updates `ovsx` from 1.2.0 to 1.3.0
- [Release notes](https://github.com/eclipse-openvsx/openvsx/releases)
- [Changelog](https://github.com/eclipse-openvsx/openvsx/blob/main/cli/CHANGELOG.md)
- [Commits](https://github.com/eclipse-openvsx/openvsx/commits/v1.3.0/cli)

---
updated-dependencies:
- dependency-name: vscode-languageclient
  dependency-version: 11.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: npm-packages
- dependency-name: "@types/node"
  dependency-version: 26.6.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-packages
- dependency-name: "@types/sinon"
  dependency-version: 22.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-packages
- dependency-name: "@vscode/vsce"
  dependency-version: 4.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-packages
- dependency-name: glob
  dependency-version: 14.0.3
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm-packages
- dependency-name: mocha
  dependency-version: 12.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-packages
- dependency-name: ovsx
  dependency-version: 1.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-packages
...

Signed-off-by: dependabot[bot] <support@github.com>
@planger
planger requested a review from tortmayr November 9, 2026 10:52

@tortmayr tortmayr left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this looks good to me. The merge approach is fine, I checked that the merge tree is identical to add1ed9, so main ends up exactly at the branch. Type check, clang-format and vsce package pass locally (I couldn't run the integration tests offline). My main point is the release notes: 0.2.0 drops the 0.1.0 settings, the CMake strategy and the old API without saying so. The rest are small things.

Comment thread CHANGELOG.md Outdated
@@ -1,5 +1,100 @@
# Change Log

## Unreleased

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still says "Unreleased" and only has the upstream entries, but it ships in the VSIX and is what users see in the Marketplace changelog tab. 0.2.0 drops everything 0.1.0 users relied on: the clangd.multiProject.* settings (main), the CMake strategy (main) and the ProjectService returned from activate() (main). I think we should add a 0.2.0 entry that lists these breaking changes and points to clangd.enablePerFolderServer and API v2. Otherwise anyone with multiProject.enabled: true silently ends up with a single clangd.

Comment thread README.md Outdated
`Memory Usage` etc. haven been disabled for now and do not working in `clangd-vscode-multi-project`.
| Version | Based on |
|---|---|
| [0.2.0](https://github.com/eclipse-cdt-cloud/vscode-clangd/releases/tag/v0.2.0) | [vscode-clangd 0.6.0](https://github.com/clangd/vscode-clangd/releases/tag/0.6.0) ([c055d25](https://github.com/clangd/vscode-clangd/commit/c055d25)) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table says 0.2.0 is based on c055d25, but the branch actually sits on 58df17e (8 more dependabot bumps on top). Could you please either reference 58df17e or say "0.6.0 plus dependency updates"?

Comment thread README.md
Comment on lines +26 to +28
The extension id is `eclipse-cdt.vscode-clangd-cdtcloud`. It contributes the
same commands and settings as vscode-clangd, so only one of the two extensions
should be enabled.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both extensions register the same command ids, so whichever one activates second fails in registerCommand and basically nothing works. A warning when llvm-vs-code-extensions.vscode-clangd is enabled (similar to the cpptools check, main) would save users some head scratching. Fine as a follow-up.

Comment thread .github/workflows/ci.yml
- main
tags:
- '*'
branches: [ main ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any tag publishes to both marketplaces, and nothing checks that the tag matches the version in package.json. I'd restrict this to v* (as described in DEVELOPING.md) and fail the build if the tag name isn't v${version}.

Comment thread api/package.json
@@ -0,0 +1,15 @@
{
"name": "@clangd/vscode-clangd",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package is still @clangd/vscode-clangd with the upstream description, and nothing in this repo publishes it, so API v2 consumers can't get the ClangdApiV2 types from npm. Also it pins vscode-languageclient 10.0.0-next.20 while the extension now uses ^11.0.0, so the BaseLanguageClient types consumers compile against may not match what they get at runtime. Not a blocker for the release, but in the long run we should probably publish the types under the fork's name.

@tortmayr

tortmayr commented Nov 9, 2026

Copy link
Copy Markdown

ECA checks fails and im not sure how we can resolve this

@tortmayr
tortmayr self-requested a review November 9, 2026 11:22

@tortmayr tortmayr left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 👍🏼 Thanks for the fast fixes.

planger and others added 3 commits November 9, 2026 13:33
test/index.ts looked for *.test.ts files in out/, which only contains the
compiled *.test.js files, so no test ran. Also, run() returned before
mocha finished, so VS Code exited before any test could report a
failure.

Look for *.test.js and resolve run() once mocha is done, rejecting if
tests failed.

The two InactiveRegionsFeature tests have been failing unnoticed since the
decoration type is created only once the visible editors change: they
now create it before sending the notifications.
vscode-languageclient declares handleFailedRequest(type, token, error,
defaultValue, ...), but the override declared (type, error, token,
defaultValue). The error therefore arrived in the token parameter, so the
ResponseError check never matched and failed workspace/executeCommand
requests (e.g. applyTweak) were never reported to the user. The swapped
arguments were also passed on to the base implementation.

Use the library's parameter order and forward the optional
showNotification and throwOnCancel arguments.
Per-project clangd instances need to read settings in the scope of the
folder they serve, and ${workspaceFolder} must then refer to that
folder rather than to the first workspace folder.

config.get() takes an optional folder. With a folder, the value is read
from the folder's configuration scope, ${workspaceFolder} (and its
aliases) resolves to the folder's path and ${workspaceFolderBasename}
to its name. Without a folder, the substitutions are unchanged, and the
value is read for no specific resource (scope null): that gives the same
values as before, and VS Code expects it for resource-scoped settings,
which it otherwise reads with a warning in the log.

Co-authored-by: Daan De Meyer <daan.j.demeyer@gmail.com>
planger and others added 10 commits November 9, 2026 14:24
The activation code owned a single ClangdContext, and the context ran the
installer and started its own client from the constructor. Supporting more
than one clangd instance needs a single owner for the session state, and a
context whose wiring is complete before its client starts.

Introduce ClangdContextManager, which runs install.activate, creates the
context and implements restart and shutdown. extension.ts registers the
commands and delegates to it, and the API reads the current client from
the manager. API v1 returns no client while clangd is stopped, where it
used to return the stopped client.

Restart and shutdown keep the guard of before: they do nothing while the
client is starting. They now also do nothing while the session itself
starts (install.activate and the creation of the context), where a
restart used to create a second context concurrently.

ClangdContext now takes the clangd path and its manager, exposes the
documentSelector it serves, and starts its client only from an explicit
startClient(). dispose() no longer calls stop() on a client that is
starting, which the language client rejects: it waits until the client
runs or failed to start, also when the client restarts after a crash,
and ignores stop() failures, which the client logs. A client disposed
while it starts still initializes its features; what they subscribe to
from then on is disposed right away.

Contexts are owned by the manager and are no longer pushed into the
extension's subscriptions on every restart.

API v1's languageClient is now undefined while there is no client, e.g.
after clangd.shutdown or while a restart reinstalls clangd, where it
used to be the stopped client.
Each feature registered its commands, tree views and the status bar item
while a context started its client, so a second context would fail with
duplicate command and view ids.

Split every feature into a UI part, registered once per session through
registerFeatureCommands(manager), which sits next to the list of
per-client activations in clangd-context.ts, and a per-client part that
stays a StaticFeature of the context. The UI part looks up the context
to act on through the manager:

- editor commands (AST, type hierarchy, switch header/source) use the
  context serving the document;
- the type hierarchy view remembers the client that produced its root
  and closes when that client's context goes away, also while its
  request is in flight;
- the memory usage view has one top-level node per client and leaves
  out clients whose request fails; if all fail, the command fails with
  the error, as before;
- the file status keeps the statuses per context;
- the legacy inlay hints provider uses the context's document selector.

The commands exist exactly while a session exists, as before. With the
single context of today, they behave as before, except when the client
has stopped for good (e.g. it crashed too often): the editor commands
and the memory usage command then say that clangd is not running
instead of failing with a request error.

Co-authored-by: Daan De Meyer <daan.j.demeyer@gmail.com>
Running one clangd per project needs a description of the projects and
their roots, independent of where the projects come from.

A ClangdProject is a root directory with an optional name, and a
ClangdProjectsChangeEvent lists added, removed and updated projects.

Provide the helpers the manager needs:

- uriContains() compares paths segment-wise (so /a/p does not contain
  /a/p2) and requires the same scheme and authority;
- projectKey() identifies a project by its root;
- both ignore case on Windows and macOS, as VS Code does when it
  matches the RelativePatterns of document selectors;
- projectLabel() names a project in the UI: its name, or its path if
  another project has the same name;
- normalizeProjects() drops projects that are not on disk, duplicates
  and nested projects; the outermost project wins, because document
  selectors can't exclude a sub-directory and a client receives every
  document below its root;
- diffProjects() reports added, removed and updated (renamed) projects.
Every clangd advertises the same executeCommandProvider commands
(clangd.applyFix and clangd.applyTweak). vscode-languageclient's
ExecuteCommandFeature registers each of them as a VS Code command, so a
second client fails to register them.

ServerCommandRegistry registers each command once and keeps the set of
clients that advertised it; the registration goes away with the last
of them. An invocation goes to the client serving the document named in
the arguments (TweakArgs.file for applyTweak, the first document of the
WorkspaceEdit for applyFix, see ClangdLSPServer.cpp), else to the client
of the active editor, else to the only owner. Failures are reported
through the client's handleFailedRequest().

SharedServerCommandsFeature moves a client's commands to the registry
in preInitialize(), before the built-in feature sees them. It replaces
executeCommandProvider in the capabilities copy handed to the features,
so the client's initializeResult still lists all commands. Once detached,
e.g. from a client that is disposed while it starts, it still hides the
commands from the built-in feature but no longer adds them to the
registry.
A single clangd serving all workspace folders can't use per-folder
compilation databases, arguments or working directories, and a crash or
a large index in one folder affects all of them.

With the new window-scoped setting clangd.enablePerFolderServer (default
false, changing it restarts clangd), the manager runs one clangd per
project, i.e. per workspace folder:

- install.activate runs once; the commands and views are registered
  once per session.
- A ProjectSet in project.ts follows the workspace folders, normalizes
  them (the outermost of nested folders wins) and reports the
  differences. The manager only reacts to them: removed projects lose
  their context and output channel, renamed ones restart their client,
  and added ones start for visible editors. A channel is disposed right
  away; the writes of its clients that are still stopping are dropped.
  The projects are kept across restarts and cleared when the default
  mode starts.
- A project's context is created lazily when one of its files is shown
  in a visible editor (initially and on onDidChangeVisibleTextEditors),
  not when a document is merely opened. The pending creation is stored
  per project key before anything is awaited, so a project gets one
  context, and a creation that completes after the project was removed,
  stopped or the session was torn down is disposed. A caller waiting for
  an abandoned creation gets the context of the one that superseded it.
- clangd.restart and clangd.shutdown take an optional Uri and then act
  on the owning project only; the config file watcher passes the
  project's root. A project shut down this way stays stopped until it is
  restarted. Without a Uri, all clients restart or stop. A restart
  recreates every context that existed before, also those without a
  visible file.
- In multi-project mode, nothing blocks a restart or shutdown of all
  clients: a context whose client is starting stops it once it has
  started, its server commands never reach the shared registry, and the
  session can be torn down while it starts. In the default mode, a
  starting session or client still blocks them, as before. A restart
  requested while the session starts or restarts is served by that
  start or restart, unless it was abandoned, e.g. by a shutdown, or
  clangd.enablePerFolderServer changed meanwhile; it then runs
  afterwards, so that two sessions never start at the same time.

Each project's client selects only the clangd documents below its root
through a RelativePattern, is locked to its workspace folder, runs in
the root directory with clangd.arguments (read in the folder scope),
and logs to its own channel "clangd (<name>)", or "clangd (<root path>)"
if another project has the same name. clangd.trace gets the project's name and
a hash of its root inserted before the extension (trace.json becomes
trace.<name>-<hash>.json), so that the instances don't write to the
same trace file, even if projects have the same name.

The server commands of all clients go through the shared registry, also
in the default mode, where the single client owns them; a mode change
therefore never registers a command twice.

The UI follows the project: the file status shows the project's label,
the memory usage view shows one node per project, clangd.projectConfig
opens the active project's .clangd, and API v1 and the file status'
"open output" command use the client of the active editor if it shows
a C/C++ file, else the one of the clangd editor focused last. The
context keys that enable the AST, memory usage, inlay hints and type
hierarchy commands are owned by the shared part of each feature and
true while any running client's server supports the feature; the
manager's new onDidChangeClients event tells when to recompute them.

Files outside all projects are not served; a command invoked on such a
file says so. With the setting off, the behavior is unchanged, except
that the context keys of the AST, memory usage and inlay hints
commands, like the one of the type hierarchy before, are false while
clangd is not running.

Co-authored-by: Daan De Meyer <daan.j.demeyer@gmail.com>
With clangd.enablePerFolderServer, every clangd instance belongs to a
workspace folder, but most of its settings were window-scoped and read
without a scope, so folders couldn't configure their clangd differently.

Move the settings that concern a single clangd instance to resource
scope and read them in the context's workspace folder: arguments,
fallbackFlags, trace, restartAfterCrash, onConfigChanged,
onConfigChangedForceEnable, enableCodeCompletion, enableHover,
serverCompletionRanking and inactiveRegions.*. The completion and hover
middleware read them in the folder of the client that serves the
document.

Settings of the binary and of the extension as a whole (enable,
checkUpdates, path, useScriptAsExecutable) and the deprecated
semanticHighlighting stay as they are.

In the default mode, the context has no folder, so the settings are read
for no specific resource (config.get passes the scope null), which gives
the values of before and logs no warning; VS Code ignores folder values
of resource-scoped settings read that way. The description of
clangd.enablePerFolderServer and docs/settings.md say so; the
descriptions of the moved settings are unchanged.

Co-authored-by: Daan De Meyer <daan.j.demeyer@gmail.com>
API v1 offers a single languageClient snapshot, which can't express the
clients of the multi-project mode. Extensions need to look up the client
serving a file, e.g. to send clangd's custom requests to it.

getApi(2) returns ClangdApiV2, a superset of the version 2 proposed in
clangd#810:

- getLanguageClient(uri?) returns the client serving uri, or without
  uri the one of the active editor if it shows a C/C++ file, else of the
  clangd editor focused last; in the default mode, the single client.
- getAllLanguageClients() returns all clients, and onDidChangeClients
  fires when a client is added or removed, or starts or stops.
- startLanguageClient(uri) starts the client serving uri if needed and
  resolves to it once it runs, or to undefined if there is none or it
  fails to start. It waits for a start or restart of the session in
  progress.
- multiProject tells whether clangd.enablePerFolderServer is in effect.

All of them return only clients that are starting or running: requests
to a starting client are delayed until it runs, and clients that have
stopped, e.g. after crashing too often, are left out.

getApi(1) is unchanged, and unknown versions still throw.

The declarations now refer to the vscode module, so @types/vscode
becomes a peer dependency of the API package.
The projects can only be the workspace folders. Build-system extensions
need to contribute projects that are sub-folders, e.g. sibling build
directories in one workspace folder, each with its own arguments.

API v2 gains:

- projects, getProject(uri), activeProject, onDidChangeActiveProject and
  onDidChangeProjects, which describe the projects; they are empty in
  the default mode, and kept while clangd restarts or is shut down, so a
  global restart reports no changes.
- registerProjectProvider(id, provider), which adds a source of
  projects. While any provider is registered, the ProjectSet takes the
  union of the results of all providers, normalized as before
  (outermost wins, non-file roots and duplicates are dropped), instead
  of the workspace folders. A union doesn't depend on the order in which
  providers activate. Registering and unregistering a provider, and its
  onDidChangeProjects, re-query the providers and apply the difference:
  a provider registered after the workspace-folder clients started
  replaces them once, and an updated project restarts only its client.
  Providers are kept but not queried in the default mode. A provider
  whose onDidChangeProjects throws is not registered.
- ClangdProject.arguments, passed to the project's clangd after
  clangd.arguments.

ClangdProject and ClangdProjectsChangeEvent move from project.ts to the
API declarations.

Querying the providers is asynchronous and not part of the session's
startup, so a slow provider never blocks restart or shutdown. If a
provider fails, doesn't respond within 10 seconds or returns something
other than a list of projects, its projects of the last successful query
are kept (none if there was none), which is logged: a transient failure,
e.g. during a build-system reconfigure, doesn't tear down its clients.
An answer after the 10 seconds is still applied, unless the provider was
queried again meanwhile: the provider has no reason to fire
onDidChangeProjects again. A query is cancelled when a newer one starts
or the session is torn down. startLanguageClient() waits for the
projects to be up to date.

Providers are never waited for during activation: they can only
register once the extension has returned its API.
Fork only, not meant for upstream.

Publish the extension as eclipse-cdt.vscode-clangd-cdtcloud with the
fork's display name, description, homepage and repository, and an
independent version, 0.2.0. The README images are served from the
fork's main branch, and bugs in the extension are filed at the fork.

The README gets a section about the fork and a releases table recording
the vscode-clangd version each release is based on: 0.2.0 is based on
vscode-clangd 0.6.0 and its master branch up to 58df17e. The changelog
gets an entry for 0.2.0, which takes over upstream's unreleased one and
lists what breaks for users of the fork's version 0.1.0. The API README
names the extension id that provides API version 2.

CI is the fork's tag-driven workflow again, updated to the current
action versions and Node 22: main and pull requests are built, tested,
format-checked as upstream does and packaged; a tag v<version> must
match the version in package.json and additionally creates a GitHub
release and publishes to Open VSX and the VS Code Marketplace.
Upstream's release workflows, which publish on version bumps of master,
are removed, and DEVELOPING.md describes the tag-driven release.
main takes the tree of multi-project-2026-10 as is. That branch
reimplements the multi-project support of the 2024 proof of concept on
top of vscode-clangd 0.6.0 and its master branch up to 58df17e, so it
supersedes the earlier changes on main.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.