Skip to content

feat(vscode): add likec4.exclude setting for file/folder exclusion#2861

Merged
davydkov merged 8 commits into
mainfrom
vscode-exclude-config
Apr 8, 2026
Merged

feat(vscode): add likec4.exclude setting for file/folder exclusion#2861
davydkov merged 8 commits into
mainfrom
vscode-exclude-config

Conversation

@davydkov

@davydkov davydkov commented Apr 8, 2026

Copy link
Copy Markdown
Member

Adds a new likec4.exclude workspace-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 .likec4rc config 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 — New likec4.exclude setting (array of glob strings)
  • packages/vscode/src/useLanguageClient.tswatchDebounced on config.exclude to restart the language server on change
  • packages/language-server/src/workspace/WorkspaceManager.tsreadExcludeConfig() reads initial config with p-timeout fallback before workspace scan
  • packages/language-server/src/workspace/ProjectsManager.tssetWorkspaceExcludePatterns(string[]) with defensive guards, workspace-level exclude predicate used in isExcluded()

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • I've rebased my branch onto main before creating this PR.
  • I've added tests to cover my changes (if applicable).
  • I've verified pnpm typecheck and pnpm test.
  • I've added changesets (you can use /changeset-generator SKILL).
  • My change requires documentation updates.
  • I've updated the documentation accordingly (or will do in follow-up PR).

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-bot

changeset-bot Bot commented Apr 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 269d893

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@likec4/language-server Patch
likec4-vscode Patch
@likec4/playground Patch
@likec4/language-services Patch
likec4 Patch
@likec4/lsp Patch
@likec4/mcp Patch
@likec4/vite-plugin Patch
@likec4/docs-astro Patch
@likec4/style-preset Patch
@likec4/styles Patch
@likec4/config Patch
@likec4/core Patch
@likec4/diagram Patch
@likec4/generators Patch
@likec4/layouts Patch
@likec4/leanix-bridge Patch
@likec4/log Patch
@likec4/react Patch
@likec4/tsconfig Patch
@likec4/vscode-preview Patch

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

@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new likec4.exclude VS Code setting (object of glob->boolean) and wires it into the language server so workspace-level exclude patterns are read at startup, applied with highest precedence to project/document inclusion, and trigger a language-server restart when changed.

Changes

Cohort / File(s) Summary
Changeset & Extension manifest
\.changeset/vscode-exclude-config.md, packages/vscode/package.json
New changeset and new VS Code configuration key likec4.exclude (object of glob => boolean) added to the extension manifest.
Language server runtime deps
packages/language-server/package.json
Added p-timeout dependency to language-server package.
Projects manager (exclusion logic)
packages/language-server/src/workspace/ProjectsManager.ts
Added workspace-level exclude matcher, #isExcludedByWorkspace(uri), precedence checks in #excludedDocuments/isIncluded(...), and public setWorkspaceExcludePatterns(patterns) to compile/clear matcher from VS Code patterns.
Workspace startup/config read
packages/language-server/src/workspace/WorkspaceManager.ts
Added readInitialExcludeConfig() called during performStartup() to fetch likec4.exclude via ConfigurationProvider (with p-timeout guards) and apply to ProjectsManager; directory entry checks now also consult ProjectsManager exclusion.
VS Code extension client
packages/vscode/src/useLanguageClient.ts
Added watcher on config.exclude that logs changes and restarts the language server when exclude config updates.

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)
Loading
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)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main feature addition: introducing a new likec4.exclude VS Code setting for file/folder exclusion.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering objectives, key changes, and a completed checklist with only documentation updates pending as expected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vscode-exclude-config

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 | 🟠 Major

This only covers one of the workspace scan paths.

includeEntry() now prunes the default workspace walk, but performStartup() still registers every config returned by scanProjectFiles(), and loadAdditionalDocuments() still recurses every includePath without consulting ProjectsManager.isExcluded(). Files under an excluded tree can still create projects or be loaded through include.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

📥 Commits

Reviewing files that changed from the base of the PR and between c0048b6 and ad0f6fb.

📒 Files selected for processing (5)
  • .changeset/vscode-exclude-config.md
  • packages/language-server/src/Rpc.ts
  • packages/language-server/src/workspace/ProjectsManager.ts
  • packages/language-server/src/workspace/WorkspaceManager.ts
  • packages/vscode/package.json

Comment thread packages/language-server/src/Rpc.ts Outdated
Comment on lines +384 to +412
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 })
}
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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),
+        )
As per coding guidelines, "Avoid using `any`, casts with `as`".
🤖 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.

Comment thread packages/language-server/src/workspace/ProjectsManager.ts Outdated
Comment thread packages/language-server/src/workspace/ProjectsManager.ts Outdated
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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between ad0f6fb and 4f5e4b2.

📒 Files selected for processing (4)
  • packages/language-server/src/Rpc.ts
  • packages/language-server/src/workspace/ProjectsManager.ts
  • packages/language-server/src/workspace/WorkspaceManager.ts
  • packages/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

Comment thread packages/language-server/src/workspace/ProjectsManager.ts Outdated
davydkov and others added 2 commits April 8, 2026 19:18
…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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 125cb5d and 2b3e332.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • packages/language-server/package.json
  • packages/language-server/src/workspace/WorkspaceManager.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/language-server/package.json

Comment thread packages/language-server/src/workspace/WorkspaceManager.ts Outdated
davydkov and others added 4 commits April 8, 2026 19:45
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.
@davydkov davydkov merged commit 9687f89 into main Apr 8, 2026
17 checks passed
@davydkov davydkov deleted the vscode-exclude-config branch April 8, 2026 20:55
@likec4-ci likec4-ci Bot mentioned this pull request Apr 8, 2026
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.

1 participant