vue: Extract to zed-extensions/vue repository (#19426)

Marshall Bowers created

This PR extracts the Vue extension to the
[zed-extensions/vue](https://github.com/zed-extensions/vue) repository.

Release Notes:

- N/A

Change summary

Cargo.lock                                  |   8 
Cargo.toml                                  |   1 
extensions/vue/Cargo.toml                   |  17 -
extensions/vue/LICENSE-APACHE               |   1 
extensions/vue/extension.toml               |  19 --
extensions/vue/languages/vue/brackets.scm   |   2 
extensions/vue/languages/vue/config.toml    |  22 --
extensions/vue/languages/vue/highlights.scm |  15 -
extensions/vue/languages/vue/injections.scm |  81 ---------
extensions/vue/languages/vue/overrides.scm  |   7 
extensions/vue/src/vue.rs                   | 205 -----------------------
11 files changed, 378 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -14894,14 +14894,6 @@ dependencies = [
  "zed_extension_api 0.1.0",
 ]
 
-[[package]]
-name = "zed_vue"
-version = "0.1.1"
-dependencies = [
- "serde",
- "zed_extension_api 0.1.0",
-]
-
 [[package]]
 name = "zed_zig"
 version = "0.3.1"

Cargo.toml 🔗

@@ -162,7 +162,6 @@ members = [
     "extensions/test-extension",
     "extensions/toml",
     "extensions/uiua",
-    "extensions/vue",
     "extensions/zig",
 
     #

extensions/vue/Cargo.toml 🔗

@@ -1,17 +0,0 @@
-[package]
-name = "zed_vue"
-version = "0.1.1"
-edition = "2021"
-publish = false
-license = "Apache-2.0"
-
-[lints]
-workspace = true
-
-[lib]
-path = "src/vue.rs"
-crate-type = ["cdylib"]
-
-[dependencies]
-serde = { version = "1.0", features = ["derive"] }
-zed_extension_api = "0.1.0"

extensions/vue/extension.toml 🔗

@@ -1,19 +0,0 @@
-id = "vue"
-name = "Vue"
-description = "Vue support."
-version = "0.1.1"
-schema_version = 1
-authors = ["Piotr Osiewicz <piotr@zed.dev>"]
-repository = "https://github.com/zed-industries/zed"
-
-[language_servers.vue-language-server]
-name = "Vue Language Server"
-language = "Vue.js"
-language_ids = { "Vue.js" = "vue" }
-# REFACTOR is explicitly disabled, as vue-lsp does not adhere to LSP protocol for code actions with these - it
-# sends back a CodeAction with neither `command` nor `edits` fields set, which is against the spec.
-code_action_kinds = ["", "quickfix", "refactor.rewrite"]
-
-[grammars.vue]
-repository = "https://github.com/tree-sitter-grammars/tree-sitter-vue"
-commit = "7e48557b903a9db9c38cea3b7839ef7e1f36c693"

extensions/vue/languages/vue/config.toml 🔗

@@ -1,22 +0,0 @@
-name = "Vue.js"
-code_fence_block_name = "vue"
-grammar = "vue"
-path_suffixes = ["vue"]
-block_comment = ["<!-- ", " -->"]
-autoclose_before = ";:.,=}])>"
-brackets = [
-    { start = "{", end = "}", close = true, newline = true },
-    { start = "[", end = "]", close = true, newline = true },
-    { start = "(", end = ")", close = true, newline = true },
-    { start = "<", end = ">", close = true, newline = true, not_in = ["string", "comment"] },
-    { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
-    { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
-    { start = "`", end = "`", close = true, newline = false, not_in = ["string"] },
-]
-word_characters = ["-"]
-scope_opt_in_language_servers = ["tailwindcss-language-server"]
-prettier_parser_name = "vue"
-
-[overrides.string]
-word_characters = ["-"]
-opt_into_language_servers = ["tailwindcss-language-server"]

extensions/vue/languages/vue/highlights.scm 🔗

@@ -1,15 +0,0 @@
-(attribute) @property
-(directive_attribute) @property
-(quoted_attribute_value) @string
-(interpolation) @punctuation.special
-(raw_text) @embedded
-
-((tag_name) @type
- (#match? @type "^[A-Z]"))
-
-(directive_name) @keyword
-(directive_argument) @constant
-
-(start_tag) @tag
-(end_tag) @tag
-(self_closing_tag) @tag

extensions/vue/languages/vue/injections.scm 🔗

@@ -1,81 +0,0 @@
-; <script>
-((script_element
-    (start_tag) @_no_lang
-    (raw_text) @content)
-  (#not-match? @_no_lang "lang=")
-  (#set! "language" "javascript"))
-
-; <script lang="js">
-((script_element
-  (start_tag
-    (attribute
-      (attribute_name) @_lang
-      (quoted_attribute_value
-        (attribute_value) @_js)))
-  (raw_text) @content)
-  (#eq? @_lang "lang")
-  (#eq? @_js "js")
-  (#set! "language" "javascript"))
-
-; <script lang="ts">
-((script_element
-  (start_tag
-    (attribute
-      (attribute_name) @_lang
-      (quoted_attribute_value
-        (attribute_value) @_ts)))
-  (raw_text) @content)
-  (#eq? @_lang "lang")
-  (#eq? @_ts "ts")
-  (#set! "language" "typescript"))
-
-; <script lang="tsx">
-; <script lang="jsx">
-; Zed built-in tsx, we mark it as tsx ^:)
-(script_element
-  (start_tag
-    (attribute
-      (attribute_name) @_attr
-      (quoted_attribute_value
-        (attribute_value) @language)))
-  (#eq? @_attr "lang")
-  (#any-of? @language "tsx" "jsx")
-  (raw_text) @content)
-
-
-; {{ }}
-((interpolation
-  (raw_text) @content)
-  (#set! "language" "typescript"))
-
-; v-
-(directive_attribute
-  (quoted_attribute_value
-    (attribute_value) @content
-    (#set! "language" "typescript")))
-
-; Vue <style lang="css"> injections
-(style_element
-    (start_tag
-        (attribute
-            (attribute_name) @_attr_name
-            (#eq? @_attr_name "lang")
-            (quoted_attribute_value
-                (attribute_value) @language
-            )
-        )
-    )
-    (raw_text) @content
-)
-
-; Vue <style> css injections (no lang attribute)
-(style_element
-    (start_tag
-        (attribute
-            (attribute_name) @_attr_name
-        )*
-    )
-    (raw_text) @content
-    (#not-any-of? @_attr_name "lang")
-    (#set! language "css")
-)

extensions/vue/src/vue.rs 🔗

@@ -1,205 +0,0 @@
-use std::collections::HashMap;
-use std::{env, fs};
-
-use serde::Deserialize;
-use zed::lsp::{Completion, CompletionKind};
-use zed::CodeLabelSpan;
-use zed_extension_api::{self as zed, serde_json, Result};
-
-const SERVER_PATH: &str = "node_modules/@vue/language-server/bin/vue-language-server.js";
-const PACKAGE_NAME: &str = "@vue/language-server";
-
-const TYPESCRIPT_PACKAGE_NAME: &str = "typescript";
-
-/// The relative path to TypeScript's SDK.
-const TYPESCRIPT_TSDK_PATH: &str = "node_modules/typescript/lib";
-
-#[derive(Debug, Deserialize)]
-#[serde(rename_all = "camelCase")]
-struct PackageJson {
-    #[serde(default)]
-    dependencies: HashMap<String, String>,
-    #[serde(default)]
-    dev_dependencies: HashMap<String, String>,
-}
-
-struct VueExtension {
-    did_find_server: bool,
-    typescript_tsdk_path: String,
-}
-
-impl VueExtension {
-    fn server_exists(&self) -> bool {
-        fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file())
-    }
-
-    fn server_script_path(
-        &mut self,
-        language_server_id: &zed::LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<String> {
-        let server_exists = self.server_exists();
-        if self.did_find_server && server_exists {
-            self.install_typescript_if_needed(worktree)?;
-            return Ok(SERVER_PATH.to_string());
-        }
-
-        zed::set_language_server_installation_status(
-            language_server_id,
-            &zed::LanguageServerInstallationStatus::CheckingForUpdate,
-        );
-        // We hardcode the version to 1.8 since we do not support @vue/language-server 2.0 yet.
-        let version = "1.8".to_string();
-
-        if !server_exists
-            || zed::npm_package_installed_version(PACKAGE_NAME)?.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())
-    }
-
-    /// Returns whether a local copy of TypeScript exists in the worktree.
-    fn typescript_exists_for_worktree(&self, worktree: &zed::Worktree) -> Result<bool> {
-        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 dev_dependencies = &package_json.dev_dependencies;
-        let dependencies = &package_json.dependencies;
-
-        // Since the extension is not allowed to read the filesystem within the project
-        // except through the worktree (which does not contains `node_modules`), we check
-        // the `package.json` to see if `typescript` is listed in the dependencies.
-        Ok(dev_dependencies.contains_key(TYPESCRIPT_PACKAGE_NAME)
-            || dependencies.contains_key(TYPESCRIPT_PACKAGE_NAME))
-    }
-
-    fn install_typescript_if_needed(&mut self, worktree: &zed::Worktree) -> Result<()> {
-        if self
-            .typescript_exists_for_worktree(worktree)
-            .unwrap_or_default()
-        {
-            println!("found local TypeScript installation at '{TYPESCRIPT_TSDK_PATH}'");
-            return Ok(());
-        }
-
-        let installed_typescript_version =
-            zed::npm_package_installed_version(TYPESCRIPT_PACKAGE_NAME)?;
-        let latest_typescript_version = zed::npm_package_latest_version(TYPESCRIPT_PACKAGE_NAME)?;
-
-        if installed_typescript_version.as_ref() != Some(&latest_typescript_version) {
-            println!("installing {TYPESCRIPT_PACKAGE_NAME}@{latest_typescript_version}");
-            zed::npm_install_package(TYPESCRIPT_PACKAGE_NAME, &latest_typescript_version)?;
-        } else {
-            println!("typescript already installed");
-        }
-
-        self.typescript_tsdk_path = env::current_dir()
-            .unwrap()
-            .join(TYPESCRIPT_TSDK_PATH)
-            .to_string_lossy()
-            .to_string();
-
-        Ok(())
-    }
-}
-
-impl zed::Extension for VueExtension {
-    fn new() -> Self {
-        Self {
-            did_find_server: false,
-            typescript_tsdk_path: TYPESCRIPT_TSDK_PATH.to_owned(),
-        }
-    }
-
-    fn language_server_command(
-        &mut self,
-        language_server_id: &zed::LanguageServerId,
-        worktree: &zed::Worktree,
-    ) -> Result<zed::Command> {
-        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(),
-            ],
-            env: Default::default(),
-        })
-    }
-
-    fn language_server_initialization_options(
-        &mut self,
-        _language_server_id: &zed::LanguageServerId,
-        _worktree: &zed::Worktree,
-    ) -> Result<Option<serde_json::Value>> {
-        Ok(Some(serde_json::json!({
-            "typescript": {
-                "tsdk": self.typescript_tsdk_path
-            }
-        })))
-    }
-
-    fn label_for_completion(
-        &self,
-        _language_server_id: &zed::LanguageServerId,
-        completion: Completion,
-    ) -> Option<zed::CodeLabel> {
-        let highlight_name = match completion.kind? {
-            CompletionKind::Class | CompletionKind::Interface => "type",
-            CompletionKind::Constructor => "type",
-            CompletionKind::Constant => "constant",
-            CompletionKind::Function | CompletionKind::Method => "function",
-            CompletionKind::Property | CompletionKind::Field => "tag",
-            CompletionKind::Variable => "type",
-            CompletionKind::Keyword => "keyword",
-            CompletionKind::Value => "tag",
-            _ => return None,
-        };
-
-        let len = completion.label.len();
-        let name_span = CodeLabelSpan::literal(completion.label, Some(highlight_name.to_string()));
-
-        Some(zed::CodeLabel {
-            code: Default::default(),
-            spans: if let Some(detail) = completion.detail {
-                vec![
-                    name_span,
-                    CodeLabelSpan::literal(" ", None),
-                    CodeLabelSpan::literal(detail, None),
-                ]
-            } else {
-                vec![name_span]
-            },
-            filter_range: (0..len).into(),
-        })
-    }
-}
-
-zed::register_extension!(VueExtension);