feat(vscode): add likec4.exclude setting for file/folder exclusion#2861
Conversation
Add a workspace-level exclude configuration option to the VSCode extension, similar to VS Code's built-in `files.exclude`. Users can configure glob patterns to exclude files and folders from LikeC4 processing directly from VSCode settings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 269d893 The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
participant VSCode as VS Code Settings
participant WM as WorkspaceManager
participant PM as ProjectsManager
participant LSS as Language Server
LSS->>WM: performStartup()
WM->>WM: readInitialExcludeConfig()
WM->>VSCode: getConfiguration('likec4.exclude') (with timeouts)
VSCode-->>WM: exclude patterns
WM->>PM: setWorkspaceExcludePatterns(patterns)
PM->>PM: compile patterns (picomatch) or clear matcher
WM->>WM: scan & register workspace entries
WM->>PM: isExcluded(entry.uri)
PM-->>WM: exclusion decision (workspace-level precedence)
sequenceDiagram
participant VSCode as VS Code Settings
participant Ext as VS Code Extension
participant LSS as Language Server
VSCode->>Ext: likec4.exclude changed
Ext->>Ext: watcher logs update
Ext->>LSS: restartLanguageServer()
LSS->>LSS: stop current instance
LSS->>LSS: start new instance (reads updated config)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/language-server/src/workspace/WorkspaceManager.ts (1)
129-136:⚠️ Potential issue | 🟠 MajorThis only covers one of the workspace scan paths.
includeEntry()now prunes the default workspace walk, butperformStartup()still registers every config returned byscanProjectFiles(), andloadAdditionalDocuments()still recurses everyincludePathwithout consultingProjectsManager.isExcluded(). Files under an excluded tree can still create projects or be loaded throughinclude.paths.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/language-server/src/workspace/WorkspaceManager.ts` around lines 129 - 136, includeEntry() now respects ProjectsManager.isExcluded but other startup/load paths still process excluded files: update performStartup() where scanProjectFiles() results are registered and update loadAdditionalDocuments() which recurses include.paths so both consult ProjectsManager.isExcluded(entry.uri) (or equivalent ProjectsManager.isExcludedPath) before registering projects or loading files; specifically, filter the outputs of scanProjectFiles() in performStartup() and short-circuit recursion in loadAdditionalDocuments() for any includePath or discovered file that ProjectsManager.isExcluded reports as excluded to prevent excluded-tree files from creating projects or being loaded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/language-server/src/Rpc.ts`:
- Around line 384-412: The configuration handler currently only applies
workspace.exclude when present, leaving the previous matcher active if the user
removes the setting; update the onConfigurationSectionUpdate callback and the
initial ready.then handler to normalize the payload once (e.g., convert
undefined/null to an empty object) and call
workspace.ProjectsManager.applyWorkspaceExcludePatterns with that normalized
object so the matcher is cleared when exclude is deleted; remove the two
explicit casts to Record<string, boolean> and ensure both the runtime update
path (inside workspace.ConfigurationProvider.onConfigurationSectionUpdate) and
the initial read path (inside workspace.ConfigurationProvider.ready.then) use
the same normalization logic before calling applyWorkspaceExcludePatterns.
In `@packages/language-server/src/workspace/ProjectsManager.ts`:
- Around line 333-344: The current applyWorkspaceExcludePatterns flow only calls
WorkspaceManager.rebuildAll and so only reprocesses known URIs; update it to
trigger a full workspace rescan and project rediscovery and to unload documents
that are now excluded. Concretely, after setWorkspaceExcludePatterns and
before/after resetCaches/notifyListeners, call the WorkspaceManager APIs that
perform a full folder rescan and project discovery (e.g. a
rescanWorkspaceFolders or discoverProjects method) or pass an option to
rebuildAll to force a rescan, and ensure excluded open documents are
closed/unloaded (e.g. invoke an unloadExcludedDocuments or
closeDocumentsMatching patterns routine) so the live workspace state matches the
new likec4.exclude value.
- Around line 315-327: setWorkspaceExcludePatterns currently always returns true
when given a non-empty patterns set, causing repeated identical calls to still
be treated as changes; update it to detect no-op updates by storing and
comparing the normalized exclude list before replacing the matcher.
Specifically, add/maintain a private field (e.g., `#workspaceExcludePatterns` or
similar) to hold the current array or serialized string of enabled patterns,
compute the new excludePatterns in setWorkspaceExcludePatterns, compare it to
the stored value (deep equality or simple string join), and only update
this.#workspaceExclude and return true when the lists differ; when they are
identical return false. Also keep the existing behavior for transitioning from
non-empty to empty (clear both `#workspaceExclude` and the stored pattern field
and return true).
---
Outside diff comments:
In `@packages/language-server/src/workspace/WorkspaceManager.ts`:
- Around line 129-136: includeEntry() now respects ProjectsManager.isExcluded
but other startup/load paths still process excluded files: update
performStartup() where scanProjectFiles() results are registered and update
loadAdditionalDocuments() which recurses include.paths so both consult
ProjectsManager.isExcluded(entry.uri) (or equivalent
ProjectsManager.isExcludedPath) before registering projects or loading files;
specifically, filter the outputs of scanProjectFiles() in performStartup() and
short-circuit recursion in loadAdditionalDocuments() for any includePath or
discovered file that ProjectsManager.isExcluded reports as excluded to prevent
excluded-tree files from creating projects or being loaded.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5c978ba1-1b93-4da8-98b3-56ebccdc3d34
📒 Files selected for processing (5)
.changeset/vscode-exclude-config.mdpackages/language-server/src/Rpc.tspackages/language-server/src/workspace/ProjectsManager.tspackages/language-server/src/workspace/WorkspaceManager.tspackages/vscode/package.json
| workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => { | ||
| if (update.section !== this.services.LanguageMetaData.languageId) return | ||
| const excludeConfig = update.configuration?.exclude | ||
| if (excludeConfig !== undefined) { | ||
| logger.debug`received workspace exclude patterns update` | ||
| void workspace.ProjectsManager.applyWorkspaceExcludePatterns( | ||
| (excludeConfig as Record<string, boolean>) ?? {}, | ||
| ) | ||
| } | ||
| }), | ||
| Disposable.create(() => { | ||
| notifyModelParsed.cancel() | ||
| }), | ||
| ) | ||
|
|
||
| // Read initial exclude configuration (onConfigurationSectionUpdate does not fire for the initial load) | ||
| void workspace.ConfigurationProvider.ready.then(async () => { | ||
| try { | ||
| const excludeConfig = await workspace.ConfigurationProvider.getConfiguration( | ||
| this.services.LanguageMetaData.languageId, | ||
| 'exclude', | ||
| ) | ||
| if (excludeConfig && typeof excludeConfig === 'object') { | ||
| await workspace.ProjectsManager.applyWorkspaceExcludePatterns(excludeConfig as Record<string, boolean>) | ||
| } | ||
| } catch (e) { | ||
| logger.warn('Failed to read initial exclude configuration', { error: e }) | ||
| } | ||
| }) |
There was a problem hiding this comment.
Clear the matcher when likec4.exclude is deleted.
This only updates ProjectsManager when exclude is present. If the user removes the setting, the old matcher stays active until restart. Normalizing the payload once also lets you drop the two as Record<string, boolean> casts here.
🛠️ Proposed fix
+ const normalizeExcludePatterns = (value: unknown): Record<string, boolean> =>
+ value && typeof value === 'object'
+ ? Object.fromEntries(
+ Object.entries(value).filter((entry): entry is [string, boolean] => typeof entry[1] === 'boolean'),
+ )
+ : {}
+
// ----------
workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
if (update.section !== this.services.LanguageMetaData.languageId) return
- const excludeConfig = update.configuration?.exclude
- if (excludeConfig !== undefined) {
- logger.debug`received workspace exclude patterns update`
- void workspace.ProjectsManager.applyWorkspaceExcludePatterns(
- (excludeConfig as Record<string, boolean>) ?? {},
- )
- }
+ logger.debug`received workspace exclude patterns update`
+ void workspace.ProjectsManager.applyWorkspaceExcludePatterns(
+ normalizeExcludePatterns(update.configuration?.exclude),
+ )
}),
@@
- if (excludeConfig && typeof excludeConfig === 'object') {
- await workspace.ProjectsManager.applyWorkspaceExcludePatterns(excludeConfig as Record<string, boolean>)
- }
+ await workspace.ProjectsManager.applyWorkspaceExcludePatterns(
+ normalizeExcludePatterns(excludeConfig),
+ )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/language-server/src/Rpc.ts` around lines 384 - 412, The
configuration handler currently only applies workspace.exclude when present,
leaving the previous matcher active if the user removes the setting; update the
onConfigurationSectionUpdate callback and the initial ready.then handler to
normalize the payload once (e.g., convert undefined/null to an empty object) and
call workspace.ProjectsManager.applyWorkspaceExcludePatterns with that
normalized object so the matcher is cleared when exclude is deleted; remove the
two explicit casts to Record<string, boolean> and ensure both the runtime update
path (inside workspace.ConfigurationProvider.onConfigurationSectionUpdate) and
the initial read path (inside workspace.ConfigurationProvider.ready.then) use
the same normalization logic before calling applyWorkspaceExcludePatterns.
Instead of dynamically rebuilding workspace when exclude config changes, restart the language server entirely. This sidesteps issues with incomplete rebuild (missed scan paths, stale documents, no-op detection bugs). Moves initial exclude config reading from Rpc to WorkspaceManager.performStartup with a 1s timeout fallback for third-party IDEs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/vscode/src/useLanguageClient.ts (1)
75-78: Consider adding error handling for the restart operation.If
restartLanguageServer()fails (e.g., network issue, extension host crash), the rejected promise in the async watch callback could lead to unhandled errors. This is consistent with the existing watch at line 69-73, but both could benefit from a try-catch.♻️ Proposed improvement
watch(() => config.exclude, async () => { logger.info('likec4.exclude configuration changed, restarting language server') - await restartLanguageServer() + try { + await restartLanguageServer() + } catch (error) { + logger.error('Failed to restart language server after config change', { error }) + } })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vscode/src/useLanguageClient.ts` around lines 75 - 78, The watch callback that calls restartLanguageServer() lacks error handling; wrap the async callback body for the watch(() => config.exclude, ...) watcher in a try-catch so any rejection from restartLanguageServer() is caught and logged via logger.error (include context like 'failed to restart language server' and the caught error), mirroring the pattern used for the other watcher around line 69-73; this prevents unhandled promise rejections while preserving the logger.info('likec4.exclude configuration changed, restarting language server') call and still calling restartLanguageServer() inside the try block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/language-server/src/workspace/ProjectsManager.ts`:
- Around line 315-326: Add a defensive guard at the start of
setWorkspaceExcludePatterns to handle patterns being null or undefined: if
patterns is falsy (null/undefined), set this.#workspaceExclude = undefined and
return so Object.entries isn't called; keep the rest of the logic (filter/map
and picomatch assignment) unchanged. Refer to the setWorkspaceExcludePatterns
method and this.#workspaceExclude and picomatch when applying the change.
---
Nitpick comments:
In `@packages/vscode/src/useLanguageClient.ts`:
- Around line 75-78: The watch callback that calls restartLanguageServer() lacks
error handling; wrap the async callback body for the watch(() => config.exclude,
...) watcher in a try-catch so any rejection from restartLanguageServer() is
caught and logged via logger.error (include context like 'failed to restart
language server' and the caught error), mirroring the pattern used for the other
watcher around line 69-73; this prevents unhandled promise rejections while
preserving the logger.info('likec4.exclude configuration changed, restarting
language server') call and still calling restartLanguageServer() inside the try
block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3104f4e3-1f70-4a35-80d7-49bc63211784
📒 Files selected for processing (4)
packages/language-server/src/Rpc.tspackages/language-server/src/workspace/ProjectsManager.tspackages/language-server/src/workspace/WorkspaceManager.tspackages/vscode/src/useLanguageClient.ts
✅ Files skipped from review due to trivial changes (1)
- packages/language-server/src/Rpc.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/language-server/src/workspace/WorkspaceManager.ts
…ceExcludePatterns Handles null/undefined patterns and catches picomatch errors gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace manual Promise.race with setTimeout with p-timeout library, which is already in the workspace catalog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/language-server/src/workspace/WorkspaceManager.ts`:
- Around line 79-82: The code is casting excludeConfig to Record<string,
boolean> without validating values; update WorkspaceManager to build a sanitized
boolean-only map instead of using "as": when excludeConfig (the variable) is an
object, iterate its own keys and create a new Record<string, boolean> containing
only entries where the value === true (or explicitly convert truthy values to
boolean if desired), then pass that sanitized map into
this.services.workspace.ProjectsManager.setWorkspaceExcludePatterns; remove the
unsafe cast and ensure the produced object matches the expected type.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a7033d0a-e2af-4746-ac66-1b514f4cf078
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
packages/language-server/package.jsonpackages/language-server/src/workspace/WorkspaceManager.ts
✅ Files skipped from review due to trivial changes (1)
- packages/language-server/package.json
Delay restart by 800ms to avoid race with workspace/didChangeConfiguration notification that VS Code sends simultaneously. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace manual setTimeout debounce with watchDebounced. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added "pathe" to language-server package.json dependencies. - Refactored ProjectsManager to use "pathe" for path handling and improved logging. - Updated workspace exclude patterns handling in ProjectsManager. - Changed method names for clarity in WorkspaceManager. - Updated VS Code extension package.json to require VS Code version ^1.110.0. - Improved markdown descriptions in VS Code settings. - Updated vitest and related dependencies to version 4.1.3 across the project. - Updated pnpm workspace configuration to reflect new vitest versions.
Adds a new
likec4.excludeworkspace-level configuration option to the VSCode extension. Users can configure glob patterns as an array of strings (e.g.["**/generated/**", "**/.worktrees/**"]) to exclude files and folders from LikeC4 processing directly from VSCode settings, without needing a.likec4rcconfig file.The language server reads the initial exclude configuration during workspace startup (in
WorkspaceManager.performStartup) with a 1s timeout fallback for third-party IDEs. When the setting changes at runtime, the VSCode extension restarts the language server (debounced 800ms) so a fresh scan picks up the new patterns — this avoids the complexity of incremental rebuilds with incomplete workspace rescanning.Key changes
packages/vscode/package.json— Newlikec4.excludesetting (array of glob strings)packages/vscode/src/useLanguageClient.ts—watchDebouncedonconfig.excludeto restart the language server on changepackages/language-server/src/workspace/WorkspaceManager.ts—readExcludeConfig()reads initial config withp-timeoutfallback before workspace scanpackages/language-server/src/workspace/ProjectsManager.ts—setWorkspaceExcludePatterns(string[])with defensive guards, workspace-level exclude predicate used inisExcluded()Checklist
mainbefore creating this PR.pnpm typecheckandpnpm test./changeset-generatorSKILL).