Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
93 changes: 89 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ path = "src/svelte.rs"
crate-type = ["cdylib"]

[dependencies]
serde = "1.0.228"
which = "8.0.0"
zed_extension_api = "0.7.0"
77 changes: 77 additions & 0 deletions src/runtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::path::PathBuf;
use std::process::Command as SystemCommand;
use zed_extension_api::{self as zed, Result};
use which::which;

pub enum Runtime {
Bun(PathBuf),
Node
}

impl Runtime {
pub fn new() -> Self {
match which("bun") {
Ok(path) => {
println!("Bun detected at {:?}, using it as the runtime.", path);
Self::Bun(path)
}
Err(_) => {
println!("Bun not found. Falling back to Zed's built-in Node.js runtime.");
Self::Node
}
}
}

pub fn server_command(&self, server_path: &str) -> Result<zed::Command> {
let command = match self {
Runtime::Bun(path) => path.to_string_lossy().to_string(),
Runtime::Node => zed::node_binary_path()?
};

Ok(zed::Command {
command,
args: vec![server_path.to_string(), "--stdio".to_string()],
env: Default::default()
})
}

pub fn install_package(&self, package_name: &str, version: &str) -> Result<()> {
println!("Installing {}@{} using {:?}...", package_name, version, self);
match self {
Runtime::Bun(path) => {
let exit_status = SystemCommand::new(path)
.arg("add")
.arg(format!("{}@{}", package_name, version))
.status()
.map_err(|e| format!("Failed to execute bun: {}", e))?;

if !exit_status.success() {
return Err(format!("'bun add' failed with status: {}", exit_status).into())
}
}
Runtime::Node => {
zed::npm_install_package(package_name, version)?;
}
};
Ok(())
}

pub fn latest_package_version(&self, package_name: &str) -> Result<String>{
match self {
Runtime::Bun(_) | Runtime::Node => zed::npm_package_latest_version(package_name)
}
}

pub fn installed_package_version(&self, package_name: &str) -> Result<Option<String>> {
zed::npm_package_installed_version(package_name)
}
}

impl std::fmt::Debug for Runtime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Bun(path) => write!(f, "Bun({:?})", path),
Self::Node => write!(f, "Node"),
}
}
}
38 changes: 20 additions & 18 deletions src/svelte.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
mod runtime;

use std::{collections::HashSet, env};
use zed_extension_api::{self as zed, serde_json, Result};
use zed_extension_api::{self as zed, Result, serde_json};
use runtime::Runtime;

struct SvelteExtension {
installed: HashSet<String>,
runtime: Runtime
}

const PACKAGE_NAME: &str = "svelte-language-server";
Expand All @@ -14,7 +18,7 @@ impl SvelteExtension {
id: &zed::LanguageServerId,
package_name: &str,
) -> Result<()> {
let installed_version = zed::npm_package_installed_version(package_name)?;
let installed_version = self.runtime.installed_package_version(package_name)?;

// If package is already installed in this session, then we won't reinstall it
if installed_version.is_some() && self.installed.contains(package_name) {
Expand All @@ -26,7 +30,7 @@ impl SvelteExtension {
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
);

let latest_version = zed::npm_package_latest_version(package_name)?;
let latest_version = self.runtime.latest_package_version(package_name)?;

if installed_version.as_ref() != Some(&latest_version) {
println!("Installing {package_name}@{latest_version}...");
Expand All @@ -36,7 +40,7 @@ impl SvelteExtension {
&zed::LanguageServerInstallationStatus::Downloading,
);

if let Err(error) = zed::npm_install_package(package_name, &latest_version) {
if let Err(error) = self.runtime.install_package(package_name, &latest_version){
// If installation failed, but we don't want to error but rather reuse existing version
if installed_version.is_none() {
Err(error)?;
Expand All @@ -55,6 +59,7 @@ impl zed::Extension for SvelteExtension {
fn new() -> Self {
Self {
installed: HashSet::new(),
runtime: Runtime::new()
}
}

Expand All @@ -66,27 +71,24 @@ impl zed::Extension for SvelteExtension {
self.install_package_if_needed(id, PACKAGE_NAME)?;
self.install_package_if_needed(id, TS_PLUGIN_PACKAGE_NAME)?;

Ok(zed::Command {
command: zed::node_binary_path()?,
args: vec![
env::current_dir()
.unwrap()
.join("node_modules")
.join(PACKAGE_NAME)
.join("bin/server.js")
.to_string_lossy()
.to_string(),
"--stdio".to_string(),
],
env: Default::default(),
})
let server_path = env::current_dir()
.unwrap()
.join("node_modules")
.join(PACKAGE_NAME)
.join("bin/server.js")
.to_string_lossy()
.to_string();

self.runtime.server_command(&server_path)

}

fn language_server_initialization_options(
&mut self,
_: &zed::LanguageServerId,
_: &zed::Worktree,
) -> Result<Option<serde_json::Value>> {

let config = serde_json::json!({
"inlayHints": {
"parameterNames": {
Expand Down