Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"editor.tabSize": 2,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
Expand Down
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ Good to know:
- In VS Code, you can view the Extension Host log with `F1 > Output: Show Output Channels... > Extension Host`.
- If the extension fails to load, the log will be output there.

### Zed Extension

Debugging Zed Extension is a bit tricky.

1. Run `npm run build`
1. Start `CMK_LOAD_LOCAL_TS_PLUGIN=0 zed examples/1-basic`

Good to know:

- If `CMK_LOAD_LOCAL_TS_PLUGIN` is set to `1`, the `ts-plugin` built with `npm run build` will be loaded.
- When it is not `1`, the `ts-plugin` downloaded from npmjs.com is loaded.
- In Zed, you can view the tsserver log with `F1 > dev: Open language server logs > vtsls (1-basic)`.
- When you start zed with the `--foreground` option, you can view the stdout of the Extension.
- e.g. `CMK_LOAD_LOCAL_TS_PLUGIN=0 zed --foreground examples/1-basic`

## Pull Request Guidelines

1. Write your code
Expand Down
1 change: 0 additions & 1 deletion crates/zed/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ license = "MIT"
crate-type = ["cdylib"]

[dependencies]
serde = { version = "1.0.219", features = ["derive"] }
zed_extension_api = "0.5.0"
86 changes: 38 additions & 48 deletions crates/zed/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,52 @@
use std::collections::HashMap;
use std::env;

use serde::Deserialize;
use zed_extension_api::{self as zed, Result, serde_json};

const TS_PLUGIN_PACKAGE_NAME: &str = "@css-modules-kit/ts-plugin";

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct PackageJson {
#[serde(default)]
dependencies: HashMap<String, String>,
#[serde(default)]
dev_dependencies: HashMap<String, String>,
}

struct CSSModulesKitExtension {}

impl CSSModulesKitExtension {
fn install_ts_plugin_if_needed(&self) -> Result<()> {
let installed_plugin_version = zed::npm_package_installed_version(TS_PLUGIN_PACKAGE_NAME)?;
let latest_plugin_version = zed::npm_package_latest_version(TS_PLUGIN_PACKAGE_NAME)?;
/// Install ts-plugin in "{extension_work_dir}/node_modules"
fn install_ts_plugin_if_needed() -> Result<()> {
let installed_plugin_version = zed::npm_package_installed_version(TS_PLUGIN_PACKAGE_NAME)?;
let latest_plugin_version = zed::npm_package_latest_version(TS_PLUGIN_PACKAGE_NAME)?;

if installed_plugin_version.as_ref() != Some(&latest_plugin_version) {
println!("installing {TS_PLUGIN_PACKAGE_NAME}@{latest_plugin_version}");
zed::npm_install_package(TS_PLUGIN_PACKAGE_NAME, &latest_plugin_version)?;
} else {
println!("ts-plugin already installed");
}
Ok(())
if installed_plugin_version.as_ref() != Some(&latest_plugin_version) {
println!("installing {TS_PLUGIN_PACKAGE_NAME}@{latest_plugin_version}");
zed::npm_install_package(TS_PLUGIN_PACKAGE_NAME, &latest_plugin_version)?;
} else {
println!("ts-plugin already installed");
}
Ok(())
}

fn get_ts_plugin_root_path(&self, worktree: &zed::Worktree) -> Result<Option<String>> {
let package_json = worktree.read_text_file("package.json")?;
let package_json: PackageJson = serde_json::from_str(&package_json)
.map_err(|err| format!("failed to parse package.json: {err}"))?;

let has_local_plugin = package_json
.dev_dependencies
.contains_key(TS_PLUGIN_PACKAGE_NAME)
|| package_json
.dependencies
.contains_key(TS_PLUGIN_PACKAGE_NAME);

if has_local_plugin {
println!("Using local installation of {TS_PLUGIN_PACKAGE_NAME}");
return Ok(None);
}

self.install_ts_plugin_if_needed()?;
fn get_ts_plugin_search_location(worktree: &zed::Worktree) -> String {
// If `CMK_LOAD_LOCAL_TS_PLUGIN` is "1", use the local ts-plugin.
// This feature is for debugging purposes and is not meant for general users.
let load_local_ts_plugin = worktree
.shell_env()
.into_iter()
.find(|(key, _)| key == "CMK_LOAD_LOCAL_TS_PLUGIN")
.map(|(_, value)| value);

if load_local_ts_plugin == Some("1".to_string()) {
println!("Using local installation of {TS_PLUGIN_PACKAGE_NAME}");
return worktree.root_path();
}

println!("Using global installation of {TS_PLUGIN_PACKAGE_NAME}");
Ok(Some(
env::current_dir().unwrap().to_string_lossy().to_string(),
))
if let Err(_) = install_ts_plugin_if_needed() {
return worktree.root_path();
}

// The cwd for the extension is "~/Library/Application Support/Zed/extensions/work/css-modules-kit" on macOS.
// ref: https://github.com/zed-industries/zed/blob/2d9cd2ac8888a144ef41e59c9820ffbecee66ed1/crates/extension_host/src/wasm_host.rs#L679
let extension_work_dir = env::current_dir()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| worktree.root_path());
println!("Using global installation of {TS_PLUGIN_PACKAGE_NAME}");
extension_work_dir
}

struct CSSModulesKitExtension {}

impl zed::Extension for CSSModulesKitExtension {
fn new() -> Self {
Self {}
Expand Down Expand Up @@ -87,7 +77,7 @@ impl zed::Extension for CSSModulesKitExtension {
"typescript-language-server" => Ok(Some(serde_json::json!({
"plugins": [{
"name": TS_PLUGIN_PACKAGE_NAME,
"location": self.get_ts_plugin_root_path(worktree)?.unwrap_or_else(|| worktree.root_path()),
"location": get_ts_plugin_search_location(worktree),
"languages": ["css"]
}],
}))),
Expand All @@ -107,7 +97,7 @@ impl zed::Extension for CSSModulesKitExtension {
"tsserver": {
"globalPlugins": [{
"name": TS_PLUGIN_PACKAGE_NAME,
"location": self.get_ts_plugin_root_path(worktree)?.unwrap_or_else(|| worktree.root_path()),
"location": get_ts_plugin_search_location(worktree),
"enableForWorkspaceTypeScriptVersions": true,
"languages": ["css"]
}]
Expand Down