Skip to content
Closed
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bab1e0f
refactor: update vue.rs
GamerGirlandCo Dec 8, 2025
c1d63e6
Merge branch 'main' into main
MrSubidubi Dec 15, 2025
1a5e204
fix: remove debug log
GamerGirlandCo Dec 25, 2025
cf4b6a5
Merge branch 'main' of github.com:GamerGirlandCo/zed-vue
GamerGirlandCo Dec 25, 2025
749d4c4
Merge remote-tracking branch 'gamergirlandco/main'
firu11 Jan 22, 2026
53f5144
fix settings load
firu11 Jan 22, 2026
0fe13b1
always return absolute path
firu11 Jan 22, 2026
7da02b9
Merge branch 'main' into main
firu11 Feb 24, 2026
42c18c5
Update CI workflows to `e439c295c5` (#102)
zed-zippy[bot] Feb 25, 2026
d3af8f3
Document Vue LSP settings options in README (#87)
aizigao Mar 9, 2026
04b6724
Fix duplicate TypeScript completions when Vue extension is enabled (#…
Obapelumi Mar 10, 2026
b5ddb84
vue: Fix query formatting (#105)
smitbarmase Mar 10, 2026
a539d6f
Bump version to 0.3.1 (#104)
zed-zippy[bot] Mar 10, 2026
53d5000
Update CI workflows to `0c49aaa` (#106)
zed-zippy[bot] Mar 11, 2026
12acedd
Update CI workflows to `3e7f2e3` (#109)
zed-zippy[bot] Mar 13, 2026
307ea97
Update CI workflows to `30d3467` (#110)
zed-zippy[bot] Mar 18, 2026
3ac6563
Allow themes to style Vue directives separately (#103)
icarusgk Mar 18, 2026
6df500f
Pin vue-language-server to v3.2.1 to avoid upstream crash (#107)
theiter8r Mar 18, 2026
91bbab9
Bump version to 0.3.2 (#111)
zed-zippy[bot] Mar 18, 2026
d3d0a6c
Update CI workflows to `3183c04` (#113)
zed-zippy[bot] Mar 25, 2026
58d202c
Merge branch 'main' into main
firu11 Apr 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 61 additions & 23 deletions src/vue.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::path::PathBuf;
use std::{env, fs};

use serde::Deserialize;
Expand Down Expand Up @@ -29,8 +30,17 @@ struct PackageJson {
dev_dependencies: HashMap<String, String>,
}

#[derive(Debug, Deserialize)]
struct Settings {
#[serde(default)]
server_path: String,
#[serde(default)]
package_json_path: String,
}

struct VueExtension {
did_find_server: bool,
settings: Settings,
}

impl VueExtension {
Expand All @@ -43,46 +53,75 @@ impl VueExtension {
language_server_id: &zed::LanguageServerId,
worktree: &zed::Worktree,
) -> Result<String> {
let server_exists = self.server_exists();
if self.did_find_server && server_exists {
// Load settings under key "lsp.vue-language-server"
let lsp_settings = LspSettings::for_worktree("vue-language-server", worktree)
.ok()
.and_then(|settings| settings.settings)
.unwrap_or_else(|| json!({}));

// Deserialize settings
if let Ok(new_settings) = serde_json::from_value::<Settings>(lsp_settings) {
if !new_settings.server_path.is_empty() {
self.settings.server_path = new_settings.server_path;
}
if !new_settings.package_json_path.is_empty() {
self.settings.package_json_path = new_settings.package_json_path;
}
}

// Resolve the configured path against the worktree root
let worktree_candidate =
PathBuf::from(worktree.root_path()).join(&self.settings.server_path);

// If the file exists in the worktree, return the absolute path immediately
if fs::metadata(&worktree_candidate).is_ok_and(|stat| stat.is_file()) {
self.install_typescript_if_needed(worktree)?;
self.install_ts_plugin_if_needed()?;
return Ok(SERVER_PATH.to_string());
self.did_find_server = true;
// Return the absolute path so command logic doesn't have to guess
return Ok(worktree_candidate.to_string_lossy().to_string());
}

// Fallback: try the hardcoded one
let extension_cwd = env::current_dir().map_err(|e| e.to_string())?;
let internal_candidate = extension_cwd.join(SERVER_PATH);

if fs::metadata(&internal_candidate)
.map(|s| s.is_file())
.unwrap_or(false)
{
self.did_find_server = true;
return Ok(internal_candidate.to_string_lossy().to_string());
}

// If neither exists, download the default package
zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
);

let version = PINNED_SERVER_VERSION.to_string();
let installed_version = zed::npm_package_installed_version(PACKAGE_NAME)?;

if !server_exists
|| zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version)
{
if !fs::metadata(SERVER_PATH).is_ok() || installed_version.as_ref() != Some(&version) {
zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::Downloading,
);
let result = zed::npm_install_package(PACKAGE_NAME, &version);
match result {
Ok(()) => {
if !self.server_exists() {
Err(format!(
"installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'",
))?;
}
}
Err(error) => {
if !self.server_exists() {
Err(error)?;
}
}
_ => {}
}
}

self.install_typescript_if_needed(worktree)?;
self.did_find_server = true;
Ok(SERVER_PATH.to_string())
Ok(internal_candidate.to_string_lossy().to_string())
}

/// Returns whether a local copy of TypeScript exists in the worktree.
Expand Down Expand Up @@ -138,7 +177,9 @@ impl VueExtension {
}

fn get_ts_plugin_root_path(&self, worktree: &zed::Worktree) -> Result<Option<String>> {
let package_json = worktree.read_text_file("package.json")?;
let pbuf = PathBuf::from(self.settings.package_json_path.to_string()).join("package.json");

let package_json = worktree.read_text_file(pbuf.to_string_lossy().to_string().as_str())?;
let package_json: PackageJson = serde_json::from_str(&package_json)
.map_err(|err| format!("failed to parse package.json: {err}"))?;

Expand All @@ -165,6 +206,10 @@ impl zed::Extension for VueExtension {
fn new() -> Self {
Self {
did_find_server: false,
settings: Settings {
server_path: SERVER_PATH.to_string(),
package_json_path: ".".to_string(),
},
}
}

Expand All @@ -176,14 +221,7 @@ impl zed::Extension for VueExtension {
let server_path = self.server_script_path(language_server_id, worktree)?;
Ok(zed::Command {
command: zed::node_binary_path()?,
args: vec![
env::current_dir()
.unwrap()
.join(&server_path)
.to_string_lossy()
.to_string(),
"--stdio".to_string(),
],
args: vec![server_path, "--stdio".to_string()],
env: Default::default(),
})
}
Expand Down