-
-
Notifications
You must be signed in to change notification settings - Fork 9
feat: add Zed extension and make language server editor-agnostic #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e6b8b2d
c9e210b
41d29a8
8baf11f
e922aaa
a4ef65d
3601a47
bd342c7
5723821
fea3872
2fe4301
e94126d
07e5986
fc8da4e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| /target | ||
| /extension.wasm | ||
| /Cargo.lock |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [package] | ||
| name = "zed-npmx" | ||
| version = "0.0.1" | ||
| edition = "2021" | ||
| license = "MIT" | ||
|
|
||
| [lib] | ||
| crate-type = [ "cdylib" ] | ||
|
|
||
| [dependencies] | ||
| serde_json = "1" | ||
| zed_extension_api = "0.7.0" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # npmx for Zed | ||
|
|
||
| This is the in-repo Zed extension for `npmx`. It runs the shared `npmx-language-server` | ||
| over stdio, so Zed gets the same core package intelligence used by the VS Code extension. | ||
|
|
||
| ## Status | ||
|
|
||
| - Uses the shared `npmx-language-server` | ||
| - Targets local development from this monorepo first | ||
| - Defaults to `packages/language-server/dist/index.cjs` | ||
| - Launches the language server over `--stdio` | ||
| - Supports overriding the launched command through Zed `lsp.npmx.binary` settings | ||
| - Forwards `lsp.npmx.settings` to the language server as `npmx` workspace configuration | ||
|
|
||
| ## Features | ||
|
|
||
| - Hover links to package pages and docs on [npmx.dev](https://npmx.dev) | ||
| - Emoji hover icons for non-VS Code editors | ||
| - Version completion with provenance and prerelease settings | ||
| - Diagnostics for upgrades, deprecations, replacements, vulnerabilities, dist tags, and engine mismatches | ||
| - Document links for package names | ||
| - Workspace-aware dependency resolution for npm, pnpm, yarn, and bun projects | ||
|
|
||
| ## Local Development | ||
|
|
||
| 1. Build the language server from the repo root with `pnpm build`. | ||
| 2. In Zed, install `extensions/zed` as a dev extension. | ||
| 3. If you want a custom launch command, configure `lsp.npmx.binary` in your Zed settings. | ||
|
|
||
| ## Settings | ||
|
|
||
| Zed settings under `lsp.npmx.settings` are forwarded directly to the language server. | ||
| Use scoped npmx settings without the leading `npmx.` prefix: | ||
|
|
||
| ```json | ||
| { | ||
| "lsp": { | ||
| "npmx": { | ||
| "settings": { | ||
| "hover": { | ||
| "enabled": true | ||
| }, | ||
| "completion": { | ||
| "version": "provenance-only", | ||
| "excludePrerelease": true | ||
| }, | ||
| "diagnostics": { | ||
| "upgrade": true, | ||
| "deprecation": true, | ||
| "replacement": true, | ||
| "vulnerability": true, | ||
| "distTag": true, | ||
| "engineMismatch": true | ||
| }, | ||
| "packageLinks": "declared" | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| To override the launched language server command: | ||
|
|
||
| ```json | ||
| { | ||
| "lsp": { | ||
| "npmx": { | ||
| "binary": { | ||
| "path": "node", | ||
| "arguments": [ | ||
| "/absolute/path/to/vscode-npmx/packages/language-server/dist/index.cjs", | ||
| "--stdio" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Notes | ||
|
|
||
| - Zed dev extensions require Rust installed via `rustup`; the Zed docs explicitly call out that Homebrew Rust will not work for dev extension compilation. | ||
| - This dev extension expects the repo-local language server bundle at `packages/language-server/dist/index.cjs`, so build the monorepo before installing it in Zed. | ||
| - If you override `lsp.npmx.binary`, make sure the launched server process still receives an LSP transport argument such as `--stdio`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| id = "npmx" | ||
| name = "npmx" | ||
| description = "npmx language support for Zed" | ||
| version = "0.0.1" | ||
| schema_version = 1 | ||
| authors = [ "Xat <i@xat.sh>" ] | ||
| repository = "https://github.com/npmx-dev/vscode-npmx/tree/main/extensions/zed" | ||
|
|
||
| [language_servers.npmx] | ||
| name = "npmx" | ||
| languages = [ | ||
| "JSON", | ||
| "YAML", | ||
| "JavaScript", | ||
| "JSX", | ||
| "TypeScript", | ||
| "TSX", | ||
| "HTML", | ||
| "Vue", | ||
| "Astro", | ||
| "Svelte" | ||
| ] | ||
|
|
||
| [language_servers.npmx.language_ids] | ||
| JSON = "json" | ||
| YAML = "yaml" | ||
| JavaScript = "javascript" | ||
| JSX = "javascriptreact" | ||
| TypeScript = "typescript" | ||
| TSX = "typescriptreact" | ||
| HTML = "html" | ||
| Vue = "vue" | ||
| Astro = "astro" | ||
| Svelte = "svelte" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| use zed_extension_api::{self as zed, serde_json, settings::LspSettings, LanguageServerId}; | ||
|
|
||
| struct NpmxExtension; | ||
|
|
||
| impl NpmxExtension { | ||
| fn language_server_settings( | ||
| language_server_id: &LanguageServerId, | ||
| worktree: &zed::Worktree, | ||
| ) -> LspSettings { | ||
| LspSettings::for_worktree(language_server_id.as_ref(), worktree) | ||
| .ok() | ||
| .unwrap_or_default() | ||
| } | ||
|
|
||
| fn default_server_script() -> String { | ||
| format!( | ||
| "{}/../../packages/language-server/dist/index.cjs", | ||
| env!("CARGO_MANIFEST_DIR") | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| impl zed::Extension for NpmxExtension { | ||
| fn new() -> Self { | ||
| Self | ||
| } | ||
|
|
||
| fn language_server_command( | ||
| &mut self, | ||
| language_server_id: &LanguageServerId, | ||
| worktree: &zed::Worktree, | ||
| ) -> zed::Result<zed::Command> { | ||
| let lsp_settings = Self::language_server_settings(language_server_id, worktree); | ||
| if let Some(binary) = lsp_settings.binary { | ||
| let command = match binary.path { | ||
| Some(path) => path, | ||
| None => zed::node_binary_path()?, | ||
| }; | ||
| let args = binary.arguments.unwrap_or_default(); | ||
| let env = worktree | ||
| .shell_env() | ||
| .into_iter() | ||
| .chain(binary.env.unwrap_or_default()) | ||
| .collect(); | ||
|
|
||
| return Ok(zed::Command { command, args, env }); | ||
| } | ||
|
|
||
| Ok(zed::Command { | ||
| command: zed::node_binary_path()?, | ||
| args: vec![Self::default_server_script(), String::from("--stdio")], | ||
| env: worktree.shell_env().into_iter().collect(), | ||
| }) | ||
| } | ||
|
|
||
| fn language_server_workspace_configuration( | ||
| &mut self, | ||
| language_server_id: &LanguageServerId, | ||
| worktree: &zed::Worktree, | ||
| ) -> zed::Result<Option<serde_json::Value>> { | ||
| let settings = Self::language_server_settings(language_server_id, worktree); | ||
| let workspace_settings = settings.settings.unwrap_or_default(); | ||
|
|
||
| Ok(Some(serde_json::json!({ | ||
| "npmx": workspace_settings | ||
| }))) | ||
| } | ||
|
Comment on lines
+56
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In zed_extension_api version 0.7.0, the language_server_initialization_options method is a provided method in the Extension trait. It allows extensions to customize the initialization options passed to a language server. Signature: fn language_server_initialization_options( &mut self, _language_server_id: &LanguageServerId, _worktree: &Worktree, ) -> Result<Option<serde_json::Value>> Purpose: Returns the initialization options (as an optional JSON Value) to pass to the specified language server. Defaults to Ok(None) if not overridden. Usage example: fn language_server_initialization_options( &mut self, _language_server_id: &LanguageServerId, _worktree: &Worktree, ) -> Result<Option<serde_json::Value>> { Ok(Some(json!({ "typescript": { "tsdk": "./node_modules/typescript/lib" }, "diagnostics": { "enable": true } }))) } This method is called by Zed when starting the language server for the given ID and worktree. Extensions implement it to provide custom LSP initializationOptions. Note: Compatible with Zed versions 0.205.x. Newer schema methods like language_server_initialization_options_schema are for v0.8+. Citations:
🏁 Script executed: # First, check the full lib.rs to see which Extension trait methods are currently implemented
cd extensions/zed && wc -l src/lib.rsRepository: npmx-dev/vscode-npmx Length of output: 78 🏁 Script executed: # Check if language_server_initialization_options is already implemented in lib.rs
rg "language_server_initialization_options" extensions/zed/src/lib.rsRepository: npmx-dev/vscode-npmx Length of output: 46 🏁 Script executed: # Check what methods are currently implemented in the Extension trait
rg -A 2 "fn language_server" extensions/zed/src/lib.rsRepository: npmx-dev/vscode-npmx Length of output: 396 🏁 Script executed: # Find the language-server directory and check server.ts
find . -name "server.ts" -type f | head -5Repository: npmx-dev/vscode-npmx Length of output: 105 🏁 Script executed: # Check server.ts around line 47 to see how clientFeatures is read
sed -n '40,60p' packages/language-server/src/server.tsRepository: npmx-dev/vscode-npmx Length of output: 715 🏁 Script executed: # Search for readClientFeatures and DEFAULT_CLIENT_FEATURES in server.ts
rg "readClientFeatures|DEFAULT_CLIENT_FEATURES|clientFeatures|initializationOptions" packages/language-server/src/server.ts -A 2 -B 2Repository: npmx-dev/vscode-npmx Length of output: 1093 🏁 Script executed: # View the full lib.rs file since it's only 70 lines
cat -n extensions/zed/src/lib.rsRepository: npmx-dev/vscode-npmx Length of output: 2711 Implement The server reads The defaults match the README, so this is not broken — but consider implementing ♻️ Sketchfn language_server_initialization_options(
&mut self,
language_server_id: &LanguageServerId,
worktree: &zed::Worktree,
) -> zed::Result<Option<serde_json::Value>> {
let settings = Self::language_server_settings(language_server_id, worktree);
let workspace_settings = settings.settings.unwrap_or_default();
let client_features = workspace_settings
.get("clientFeatures")
.cloned()
.unwrap_or(serde_json::json!({ "iconStyle": "emoji", "catalogInlayHints": true }));
Ok(Some(serde_json::json!({ "npmx": { "clientFeatures": client_features } })))
} |
||
| } | ||
|
|
||
| zed::register_extension!(NpmxExtension); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
What is the latest released version of the zed_extension_api crate on crates.io and is 0.7.0 still compatible with current Zed releases?💡 Result:
The latest released version of the zed_extension_api crate on crates.io is 0.7.0, published on 2025-09-12. 0.7.0 is compatible with Zed 0.205.x and older versions listed in the compatibility table (up to 0.205.x supports 0.0.1 - 0.7.0). Current Zed releases are in the 0.23x range (e.g., v0.230.0 on 2026-04-01, v0.232.2 latest), which are significantly newer. The README explicitly states "Extensions created using newer versions of the Zed extension API won't be compatible with older versions of Zed," implying forward compatibility issues. A newer extension API (v0.8.0+) with additional methods exists in Zed development (merged PR
#48334in March 2026), but not yet released on crates.io. Thus, 0.7.0 may not be fully compatible with current Zed releases, as they likely require or expect the newer API version.Citations:
Update
zed_extension_apito a compatible version; optionally mark crate aspublish = false.The dependency
zed_extension_api = "0.7.0"targets Zed 0.205.x and older. Current Zed releases are in the 0.23x range (as of April 2026). This version mismatch means the extension will likely not be compatible with current Zed releases. Whilst a newer API version (v0.8.0+) exists in Zed's development branch, it has not yet been released to crates.io. Verify the minimum required Zed version for this extension and updatezed_extension_apiaccordingly.Additionally, since
zed-npmxis an in-repo extension only consumed by Zed (compiled toextension.wasmand registered viaextension.toml), addpublish = falseto the manifest. This prevents accidental uploads to crates.io and silencescargo publishwarnings about missing fields likedescriptionandrepository.♻️ Proposed change