Skip to content

feat(catalog): make resolved version display configurable - #158

Open
hyoban wants to merge 2 commits into
npmx-dev:mainfrom
hyoban:feat/catalog-display
Open

hyoban wants to merge 2 commits into
npmx-dev:mainfrom
hyoban:feat/catalog-display

Conversation

@hyoban

@hyoban hyoban commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Catalog versions in VS Code currently appear as line-end decorations. When another extension such as Error Lens also appends text to the line, the relative order can vary. Decorations can already appear before Error Lens (including in the playground); this adds a choice for users who prefer the version next to its catalog reference.

Display option

Add npmx.catalog.display with two values:

  • decoration (default): keep the existing gray version at the end of the line.
  • inlay-hint: show the resolved version next to the catalog reference using the existing language-server provider, at its existing position before the closing quote. It follows VS Code's inlay-hint styling and requires editor.inlayHints.enabled to allow hints.

Changes take effect without reloading the window, including across visible split editors. The VS Code client enables the server capability and filters requests according to the setting, while disabling the corresponding decorations. Pending responses are checked against the current mode so a switch does not bring back the previous display.

Screenshots

The same playground file with Error Lens enabled in both modes.

decoration (default) — resolved versions appear at the end of the line, after any trailing comma. In this session, they already appear before Error Lens.

Catalog versions rendered as line-end decorations with Error Lens

inlay-hint — resolved versions appear next to the catalog reference, before the closing quote, using the editor's inlay-hint styling. The existing language-server hint position and Zed behavior are preserved.

Catalog versions rendered as inlay hints with Error Lens

Validation

  • pnpm lint:fix, pnpm build, and pnpm typecheck passed.
  • pnpm test --run: 150 tests passed, including mode switching and pending responses.
  • Tested in the VS Code Extension Development Host with Error Lens: switched both ways without reload, verified both split editors update without duplicate or leftover versions, and confirmed hints use the existing position before the closing quote and appear before Error Lens. Temporary playground settings were restored.

If maintainers feel this option is unnecessary, please feel free to close this PR.

@agentscanapp

agentscanapp Bot commented Sep 30, 2026

Copy link
Copy Markdown

Mixed activity

Activity patterns show a mix of organic and automated signals.

View full analysis →

Evidence
  • Rapid PRs to repository: 6 PRs opened to langgenius/dify within 5s intervals

Last 5 PRs:

This is an automated analysis by AgentScan

@hyoban
hyoban marked this pull request as ready for review September 30, 2026 02:17
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

The VS Code extension adds the npmx.catalog.display setting. It supports decoration and inlay-hint modes, with decoration as the default. Inlay hints are registered through client middleware and are returned only when the setting remains enabled. Decorations are suppressed in inlay-hint mode and recalculated for visible editors when the display setting changes. Tests cover default and explicit modes, plus a mode change during a pending hint request.

Suggested reviewers: 9romise

Priority: ⬇️ Low

Change: Feature

Merge Risk: 🔵 Low · up to 2217e

Switching display modes can leave catalog versions missing or duplicated until another hint refresh occurs. Add configuration-triggered hint refresh; the remaining risk is a bounded display issue.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 2217e

The change selects how catalog versions appear in the editor while preserving file and package-manifest restrictions. No new privileged action was identified. Immediate switching, overlapping updates, and cleanup remain partly unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The newly reachable hint route is bounded to catalog presentation for file-backed package manifests handled by the existing language service. The inspected route does not introduce a command-execution, credential, or cross-service authority sink; this conclusion does not establish complete security coverage of dependency resolution.

Trust Boundaries and Controls

  • observed — The configurable display mode controls presentation, not dependency-resolution authority. Manifest-derived resolved text reaches a hint label through the existing workspace resolver, while the language-service producer independently enforces document and protocol restrictions.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description clearly matches the changes. It documents the new npmx.catalog.display setting, both display modes, configuration behaviour, implementation details, and validation results…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

extensions/vscode/package.json

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

extensions/vscode/src/client.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

extensions/vscode/src/providers/catalog-display.test.ts

ESLint skipped: the matched ESLint configuration already failed (missing-dependency).

  • 3 others

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.

@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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: ba077ed5-6134-447e-9e79-d7bc362b9776

📥 Commits

Reviewing files that changed from the base of the PR and between dee1eb4 and 2217edb.

📒 Files selected for processing (7)
  • extensions/vscode/README.md
  • extensions/vscode/package.json
  • extensions/vscode/src/client.ts
  • extensions/vscode/src/providers/catalog-display.test.ts
  • extensions/vscode/src/providers/catalog-display.ts
  • extensions/vscode/src/providers/decorators.ts
  • packages/language-service/src/config.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment on lines +9 to +15
export const provideCatalogInlayHints: NonNullable<Middleware['provideInlayHints']> = async (document, range, token, next) => {
if (!usesCatalogInlayHints())
return []

const hints = await next(document, range, token)
return usesCatalogInlayHints() ? hints : []
}

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

git diff dee1eb4b1585699d0c7ff4babd8cdcf5c13aa803 2217edb492a9c924b37a39ce7de54575517aebd1 -- extensions/vscode/src/providers/catalog-display.ts extensions/vscode/src/client.ts extensions/vscode/src/providers/decorators.ts
rg -n 'inlayHint|inlayHints|refresh|didChangeConfiguration' packages/language-service/src extensions/vscode/src

Repository: npmx-dev/vscode-npmx

Length of output: 5953


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- client.ts ---'
cat -n extensions/vscode/src/client.ts
printf '%s\n' '--- catalog language-service plugin ---'
cat -n packages/language-service/src/plugins/catalog.ts
printf '%s\n' '--- configuration and display references ---'
rg -n -C 4 'catalogDisplay|catalogInlayHints|inlayHintProvider|sendNotification|onDidChangeConfiguration|InlayHint' extensions packages --glob '*.{ts,tsx,json,md}'
printf '%s\n' '--- package manifests and lock versions ---'
rg -n -C 3 '"@volar/vscode"|"vscode-languageclient"|volar' extensions packages package.json pnpm-lock.yaml

Repository: npmx-dev/vscode-npmx

Length of output: 41635


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- all inlay-hint refresh/provider references ---'
rg -n -C 5 'inlayHint/refresh|InlayHintsProvider|onDidChangeInlayHints|provideInlayHints|workspace/inlayHint' . --glob '!pnpm-lock.yaml' --glob '!*.map'
printf '%s\n' '--- display configuration declarations and documentation ---'
rg -n -C 6 'catalog\.display|catalogDisplay|inlay-hint|decoration' extensions packages README.md docs .github --glob '*.{ts,tsx,json,md,yaml,yml}' 2>/dev/null
printf '%s\n' '--- display provider tests ---'
cat -n extensions/vscode/src/providers/catalog-display.test.ts
printf '%s\n' '--- decorator configuration refresh ---'
cat -n extensions/vscode/src/providers/decorators.ts
printf '%s\n' '--- language-server configuration handling ---'
cat -n packages/language-server/src/server.ts
cat -n packages/language-service/src/config.ts

Repository: npmx-dev/vscode-npmx

Length of output: 20197


🌐 Web query:

vscode-languageclient 9.0.1 inlay hints middleware onDidChangeInlayHints refresh implementation

💡 Result:

In `vscode-languageclient` **9.0.1**, `onDidChangeInlayHints` is the VS Code provider’s **change event**, not a middleware callback you implement. The inlay-hints middleware hook is for providing hints; the provider exposes `onDidChangeInlayHints` so VS Code knows when to request them again. The VS Code API describes it as an optional event signaling that the provider’s hints changed. ([code.visualstudio.com](https://code.visualstudio.com/api/references/vscode-api%5C?utm_source=openai))

So, to refresh hints, fire the provider’s event when your data changes—for example, with an `EventEmitter<void>`—and return its `.event` as `onDidChangeInlayHints`. The language client’s registered provider connects that event to VS Code’s refresh behavior.

I couldn’t verify the exact 9.0.1 implementation from an accessible version-pinned source; the repository search result points to the current branch, not necessarily 9.0.1. The API contract above is established, but details such as the exact provider wiring in 9.0.1 remain unconfirmed. ([github.com](https://github.com/microsoft/vscode-languageserver-node/blob/main/client/src/common/client.ts?utm_source=openai))

Citations:

- 1: https://code.visualstudio.com/api/references/vscode-api%5C?utm_source=openai
- 2: https://github.com/microsoft/vscode-languageserver-node/blob/main/client/src/common/client.ts?utm_source=openai

🌐 Web query:

site:github.com/microsoft/vscode-languageserver-node v9.0.1 client provideInlayHints onDidChangeInlayHints

💡 Result:

`provideInlayHints` and `onDidChangeInlayHints` are **VS Code extension API** provider members, not methods of `vscode-languageserver-node`’s language client. The repository’s current protocol source defines LSP inlay-hint support (`inlayHintProvider`), but that’s a separate server/protocol mechanism. ([github.com](https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/protocol.ts?utm_source=openai))

I couldn’t verify those exact names in the repository’s **v9.0.1** source: the available repository results point to current sources, not that version. The VS Code source shows an example provider using both names, but it is not evidence about `vscode-languageserver-node` v9.0.1. ([github.com](https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts?utm_source=openai))

Citations:

- 1: https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/protocol.ts?utm_source=openai
- 2: https://github.com/microsoft/vscode/blob/main/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts?utm_source=openai

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- repository refresh symbols ---'
rg -n 'inlayHint/refresh|onDidChangeInlayHints|InlayHintsProvider|provideInlayHints|sendNotification|sendRequest' extensions packages --glob '*.{ts,tsx}'
printf '%s\n' '--- extension activation and provider setup ---'
cat -n extensions/vscode/src/index.ts
printf '%s\n' '--- exact current change ---'
git diff --unified=20 dee1eb4b1585699d0c7ff4babd8cdcf5c13aa803 2217edb492a9c924b37a39ce7de54575517aebd1 -- extensions/vscode/src/providers/catalog-display.ts extensions/vscode/src/providers/decorators.ts extensions/vscode/src/client.ts extensions/vscode/src/providers/catalog-display.test.ts
printf '%s\n' '--- installed/cache candidates for pinned client source ---'
find . -path '*vscode-languageclient*' -o -path '*@volar/vscode*' | head -80

Repository: npmx-dev/vscode-npmx

Length of output: 11714


🏁 Script executed:

#!/bin/bash
set -e
for url in \
  'https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/9.0.1/client/src/common/inlayHint.ts' \
  'https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/release/9.0.1/client/src/common/inlayHint.ts' \
  'https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/9.0.1/client/src/common/client.ts' \
  'https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/release/9.0.1/client/src/common/client.ts'
do
  echo "--- $url ---"
  curl -L --fail --silent --show-error "$url" | rg -n -C 8 'InlayHint|inlayHint|onDidChange|refresh' || true
done

Repository: npmx-dev/vscode-npmx

Length of output: 828


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import io
import json
import tarfile
import urllib.request

meta = json.load(urllib.request.urlopen('https://registry.npmjs.org/vscode-languageclient/9.0.1'))
data = urllib.request.urlopen(meta['dist']['tarball']).read()
with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as archive:
    names = [n for n in archive.getnames() if any(term in n.lower() for term in ('inlayhint', 'client.d.ts', 'inlay'))]
    for name in names:
        print(f'--- {name} ---')
        member = archive.getmember(name)
        text = archive.extractfile(member).read().decode('utf-8', errors='replace')
        for number, line in enumerate(text.splitlines(), 1):
            if any(term in line.lower() for term in ('inlayhint', 'onDidChange', 'refresh', 'provideInlay')):
                start = max(1, number - 8)
                end = min(len(text.splitlines()), number + 8)
                lines = text.splitlines()
                for i in range(start, end + 1):
                    print(f'{i}: {lines[i - 1]}')
                print()
PY

Repository: npmx-dev/vscode-npmx

Length of output: 2444


🏁 Script executed:

#!/bin/bash
set -e
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -k --fail --silent --show-error 'https://registry.npmjs.org/vscode-languageclient/9.0.1' > "$tmpdir/meta.json"
python3 - "$tmpdir/meta.json" "$tmpdir/package.tgz" <<'PY'
import json
import sys
import urllib.request

with open(sys.argv[1]) as f:
    meta = json.load(f)
url = meta['dist']['tarball']
request = urllib.request.Request(url)
with urllib.request.urlopen(request, context=__import__('ssl')._create_unverified_context()) as source, open(sys.argv[2], 'wb') as target:
    target.write(source.read())
PY
tar -xOf "$tmpdir/package.tgz" package/client.js 2>/dev/null | rg -n -C 12 'InlayHint|inlayHint|onDidChange|provideInlay|refresh' || true
tar -tzf "$tmpdir/package.tgz" | rg -i 'inlay|client\.(js|d\.ts)$'

Repository: npmx-dev/vscode-npmx

Length of output: 366


🏁 Script executed:

#!/bin/bash
set -e
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -k --fail --silent --show-error 'https://registry.npmjs.org/vscode-languageclient/9.0.1' > "$tmpdir/meta.json"
python3 - "$tmpdir/meta.json" "$tmpdir/package.tgz" <<'PY'
import json
import ssl
import sys
import urllib.request

with open(sys.argv[1]) as f:
    meta = json.load(f)
with urllib.request.urlopen(meta['dist']['tarball'], context=ssl._create_unverified_context()) as source, open(sys.argv[2], 'wb') as target:
    target.write(source.read())
PY
for member in package/lib/common/inlayHint.js package/lib/common/inlayHint.d.ts package/lib/common/client.js package/lib/common/client.d.ts; do
  echo "--- $member ---"
  tar -xOf "$tmpdir/package.tgz" "$member" | grep -n -C 12 -E 'InlayHint|inlayHint|onDidChange|provideInlay|refresh' || true
done

Repository: npmx-dev/vscode-npmx

Length of output: 29514


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- language server setup ---'
cat -n packages/language-server/src/server.ts
printf '%s\n' '--- language service configuration hook ---'
cat -n packages/language-service/src/config.ts
printf '%s\n' '--- workspace state and server event references ---'
rg -n -C 8 'onDidChangeConfiguration|didChangeConfiguration|connection\.|workspace/inlayHint|InlayHintRefresh|InlayHintRequest' packages/language-server packages/language-service --glob '*.ts'

Repository: npmx-dev/vscode-npmx

Length of output: 14340


Refresh inlay hints when npmx.catalog.display changes.

provideCatalogInlayHints filters only future requests. It does not invalidate the result already held by VS Code. When the setting changes, VS Code can keep empty hints after switching to "inlay-hint", or keep old hints after switching to "decoration". This violates the documented no-reload behaviour.

Send the standard InlayHintRefreshRequest when configuration changes. The pinned language client handles this request and fires its inlay-hint provider change event.

Suggested fix
 import type { ClientFeatures } from 'npmx-language-service/types'
+import { InlayHintRefreshRequest } from '@volar/language-server'
 import { createConnection, createServer, createSimpleProject } from '@volar/language-server/node'
 import { createNpmxLanguageServicePlugins } from 'npmx-language-service'
 import { DEFAULT_CLIENT_FEATURES } from 'npmx-language-service/types'
@@
   connection.onInitialized(() => {
     connection.console.info('npmx language server initialized')

     server.initialized()
   })
+  connection.onDidChangeConfiguration(() => {
+    void connection.sendRequest(InlayHintRefreshRequest.type)
+  })
   connection.onShutdown(server.shutdown)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant