Detailed changes
@@ -5438,7 +5438,6 @@ dependencies = [
"tree-sitter-heex",
"tree-sitter-jsdoc",
"tree-sitter-json 0.20.0",
- "tree-sitter-lua",
"tree-sitter-markdown",
"tree-sitter-nix",
"tree-sitter-nu",
@@ -10435,16 +10434,6 @@ dependencies = [
"tree-sitter",
]
-[[package]]
-name = "tree-sitter-lua"
-version = "0.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d489873fd1a2fa6d5f04930bfc5c081c96f0c038c1437104518b5b842c69b282"
-dependencies = [
- "cc",
- "tree-sitter",
-]
-
[[package]]
name = "tree-sitter-markdown"
version = "0.0.1"
@@ -12648,6 +12637,13 @@ dependencies = [
"zed_extension_api 0.0.4",
]
+[[package]]
+name = "zed_lua"
+version = "0.0.1"
+dependencies = [
+ "zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "zed_php"
version = "0.0.1"
@@ -110,6 +110,7 @@ members = [
"extensions/gleam",
"extensions/haskell",
"extensions/html",
+ "extensions/lua",
"extensions/php",
"extensions/prisma",
"extensions/purescript",
@@ -323,7 +324,6 @@ tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex
tree-sitter-html = "0.19.0"
tree-sitter-jsdoc = { git = "https://github.com/tree-sitter/tree-sitter-jsdoc", ref = "6a6cf9e7341af32d8e2b2e24a37fbfebefc3dc55" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
-tree-sitter-lua = "0.0.14"
tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" }
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "7dd29f9616822e5fc259f5b4ae6c4ded9a71a132" }
@@ -8,67 +8,71 @@ use extension::ExtensionStore;
use gpui::{Model, VisualContext};
use language::Buffer;
use ui::{SharedString, ViewContext};
-use workspace::notifications::NotificationId;
-use workspace::{notifications::simple_message_notification, Workspace};
+use workspace::{
+ notifications::{simple_message_notification, NotificationId},
+ Workspace,
+};
+
+const SUGGESTIONS_BY_EXTENSION_ID: &[(&str, &[&str])] = &[
+ ("astro", &["astro"]),
+ ("beancount", &["beancount"]),
+ ("clojure", &["bb", "clj", "cljc", "cljs", "edn"]),
+ ("csharp", &["cs"]),
+ ("dart", &["dart"]),
+ ("dockerfile", &["Dockerfile"]),
+ ("elisp", &["el"]),
+ ("elm", &["elm"]),
+ ("erlang", &["erl", "hrl"]),
+ ("fish", &["fish"]),
+ (
+ "git-firefly",
+ &[
+ ".gitconfig",
+ ".gitignore",
+ "COMMIT_EDITMSG",
+ "EDIT_DESCRIPTION",
+ "MERGE_MSG",
+ "NOTES_EDITMSG",
+ "TAG_EDITMSG",
+ "git-rebase-todo",
+ ],
+ ),
+ ("gleam", &["gleam"]),
+ ("glsl", &["vert", "frag"]),
+ ("graphql", &["gql", "graphql"]),
+ ("haskell", &["hs"]),
+ ("html", &["htm", "html", "shtml"]),
+ ("java", &["java"]),
+ ("kotlin", &["kt"]),
+ ("latex", &["tex"]),
+ ("lua", &["lua"]),
+ ("make", &["Makefile"]),
+ ("nix", &["nix"]),
+ ("php", &["php"]),
+ ("prisma", &["prisma"]),
+ ("purescript", &["purs"]),
+ ("r", &["r", "R"]),
+ ("sql", &["sql"]),
+ ("svelte", &["svelte"]),
+ ("swift", &["swift"]),
+ ("templ", &["templ"]),
+ ("toml", &["Cargo.lock", "toml"]),
+ ("wgsl", &["wgsl"]),
+ ("zig", &["zig"]),
+];
fn suggested_extensions() -> &'static HashMap<&'static str, Arc<str>> {
- static SUGGESTED: OnceLock<HashMap<&str, Arc<str>>> = OnceLock::new();
- SUGGESTED.get_or_init(|| {
- [
- ("astro", "astro"),
- ("beancount", "beancount"),
- ("clojure", "bb"),
- ("clojure", "clj"),
- ("clojure", "cljc"),
- ("clojure", "cljs"),
- ("clojure", "edn"),
- ("csharp", "cs"),
- ("dart", "dart"),
- ("dockerfile", "Dockerfile"),
- ("elisp", "el"),
- ("elm", "elm"),
- ("erlang", "erl"),
- ("erlang", "hrl"),
- ("fish", "fish"),
- ("git-firefly", ".gitconfig"),
- ("git-firefly", ".gitignore"),
- ("git-firefly", "COMMIT_EDITMSG"),
- ("git-firefly", "EDIT_DESCRIPTION"),
- ("git-firefly", "MERGE_MSG"),
- ("git-firefly", "NOTES_EDITMSG"),
- ("git-firefly", "TAG_EDITMSG"),
- ("git-firefly", "git-rebase-todo"),
- ("gleam", "gleam"),
- ("glsl", "vert"),
- ("glsl", "frag"),
- ("graphql", "gql"),
- ("graphql", "graphql"),
- ("haskell", "hs"),
- ("html", "htm"),
- ("html", "html"),
- ("html", "shtml"),
- ("java", "java"),
- ("kotlin", "kt"),
- ("latex", "tex"),
- ("make", "Makefile"),
- ("nix", "nix"),
- ("php", "php"),
- ("prisma", "prisma"),
- ("purescript", "purs"),
- ("r", "r"),
- ("r", "R"),
- ("sql", "sql"),
- ("svelte", "svelte"),
- ("swift", "swift"),
- ("templ", "templ"),
- ("toml", "Cargo.lock"),
- ("toml", "toml"),
- ("wgsl", "wgsl"),
- ("zig", "zig"),
- ]
- .into_iter()
- .map(|(name, file)| (file, name.into()))
- .collect()
+ static SUGGESTIONS_BY_PATH_SUFFIX: OnceLock<HashMap<&str, Arc<str>>> = OnceLock::new();
+ SUGGESTIONS_BY_PATH_SUFFIX.get_or_init(|| {
+ SUGGESTIONS_BY_EXTENSION_ID
+ .into_iter()
+ .flat_map(|(name, path_suffixes)| {
+ let name = Arc::<str>::from(*name);
+ path_suffixes
+ .into_iter()
+ .map(move |suffix| (*suffix, name.clone()))
+ })
+ .collect()
})
}
@@ -49,7 +49,6 @@ tree-sitter-hcl.workspace = true
tree-sitter-heex.workspace = true
tree-sitter-jsdoc.workspace = true
tree-sitter-json.workspace = true
-tree-sitter-lua.workspace = true
tree-sitter-markdown.workspace = true
tree-sitter-nix.workspace = true
tree-sitter-nu.workspace = true
@@ -18,7 +18,6 @@ mod deno;
mod elixir;
mod go;
mod json;
-mod lua;
mod nu;
mod ocaml;
mod python;
@@ -69,7 +68,6 @@ pub fn init(
("heex", tree_sitter_heex::language()),
("jsdoc", tree_sitter_jsdoc::language()),
("json", tree_sitter_json::language()),
- ("lua", tree_sitter_lua::language()),
("markdown", tree_sitter_markdown::language()),
("nix", tree_sitter_nix::language()),
("nu", tree_sitter_nu::language()),
@@ -280,7 +278,6 @@ pub fn init(
language!("scheme");
language!("racket");
language!("regex");
- language!("lua", vec![Arc::new(lua::LuaLspAdapter)]);
language!(
"yaml",
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
@@ -1,146 +0,0 @@
-use anyhow::{anyhow, bail, Result};
-use async_compression::futures::bufread::GzipDecoder;
-use async_tar::Archive;
-use async_trait::async_trait;
-use futures::{io::BufReader, StreamExt};
-use language::{LanguageServerName, LspAdapterDelegate};
-use lsp::LanguageServerBinary;
-use smol::fs;
-use std::{any::Any, env::consts, path::PathBuf};
-use util::{
- github::{latest_github_release, GitHubLspBinaryVersion},
- maybe, ResultExt,
-};
-
-#[derive(Copy, Clone)]
-pub struct LuaLspAdapter;
-
-#[async_trait(?Send)]
-impl super::LspAdapter for LuaLspAdapter {
- fn name(&self) -> LanguageServerName {
- LanguageServerName("lua-language-server".into())
- }
-
- async fn fetch_latest_server_version(
- &self,
- delegate: &dyn LspAdapterDelegate,
- ) -> Result<Box<dyn 'static + Send + Any>> {
- let os = match consts::OS {
- "macos" => "darwin",
- "linux" => "linux",
- "windows" => "win32",
- other => bail!("Running on unsupported os: {other}"),
- };
- let platform = match consts::ARCH {
- "x86_64" => "x64",
- "aarch64" => "arm64",
- other => bail!("Running on unsupported platform: {other}"),
- };
- let release = latest_github_release(
- "LuaLS/lua-language-server",
- true,
- false,
- delegate.http_client(),
- )
- .await?;
- let version = &release.tag_name;
- let asset_name = format!("lua-language-server-{version}-{os}-{platform}.tar.gz");
- let asset = release
- .assets
- .iter()
- .find(|asset| asset.name == asset_name)
- .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
- let version = GitHubLspBinaryVersion {
- name: release.tag_name,
- url: asset.browser_download_url.clone(),
- };
- Ok(Box::new(version) as Box<_>)
- }
-
- async fn fetch_server_binary(
- &self,
- version: Box<dyn 'static + Send + Any>,
- container_dir: PathBuf,
- delegate: &dyn LspAdapterDelegate,
- ) -> Result<LanguageServerBinary> {
- let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
-
- let binary_path = container_dir.join("bin/lua-language-server");
-
- if fs::metadata(&binary_path).await.is_err() {
- let mut response = delegate
- .http_client()
- .get(&version.url, Default::default(), true)
- .await
- .map_err(|err| anyhow!("error downloading release: {}", err))?;
- let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
- let archive = Archive::new(decompressed_bytes);
- archive.unpack(container_dir).await?;
- }
-
- // todo("windows")
- #[cfg(not(windows))]
- {
- fs::set_permissions(
- &binary_path,
- <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
- )
- .await?;
- }
- Ok(LanguageServerBinary {
- path: binary_path,
- env: None,
- arguments: Vec::new(),
- })
- }
-
- async fn cached_server_binary(
- &self,
- container_dir: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir).await
- }
-
- async fn installation_test_binary(
- &self,
- container_dir: PathBuf,
- ) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir)
- .await
- .map(|mut binary| {
- binary.arguments = vec!["--version".into()];
- binary
- })
- }
-}
-
-async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
- maybe!(async {
- let mut last_binary_path = None;
- let mut entries = fs::read_dir(&container_dir).await?;
- while let Some(entry) = entries.next().await {
- let entry = entry?;
- if entry.file_type().await?.is_file()
- && entry
- .file_name()
- .to_str()
- .map_or(false, |name| name == "lua-language-server")
- {
- last_binary_path = Some(entry.path());
- }
- }
-
- if let Some(path) = last_binary_path {
- Ok(LanguageServerBinary {
- path,
- env: None,
- arguments: Vec::new(),
- })
- } else {
- Err(anyhow!("no cached binary"))
- }
- })
- .await
- .log_err()
-}
@@ -0,0 +1,16 @@
+[package]
+name = "zed_lua"
+version = "0.0.1"
+edition = "2021"
+publish = false
+license = "Apache-2.0"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/lua.rs"
+crate-type = ["cdylib"]
+
+[dependencies]
+zed_extension_api = "0.0.6"
@@ -0,0 +1 @@
+../../LICENSE-APACHE
@@ -0,0 +1,15 @@
+id = "lua"
+name = "Lua"
+description = "Lua support."
+version = "0.1.0"
+schema_version = 1
+authors = ["Kaylee Simmons <kay@the-simmons.net>"]
+repository = "https://github.com/zed-industries/zed"
+
+[language_servers.lua-language-server]
+name = "LuaLS"
+language = "Lua"
+
+[grammars.lua]
+repository = "https://github.com/tree-sitter-grammars/tree-sitter-lua"
+commit = "a24dab177e58c9c6832f96b9a73102a0cfbced4a"
@@ -150,9 +150,9 @@
;; Tables
-(field name: (identifier) @field)
+(field name: (identifier) @property)
-(dot_index_expression field: (identifier) @field)
+(dot_index_expression field: (identifier) @property)
(table_constructor
[
@@ -176,7 +176,7 @@
(dot_index_expression field: (identifier) @function.definition)
])
-(method_index_expression method: (identifier) @method)
+(method_index_expression method: (identifier) @function.method)
(function_call
(identifier) @function.builtin
@@ -195,4 +195,4 @@
(number) @number
-(string) @string
+(string) @string
@@ -0,0 +1,158 @@
+use std::fs;
+use zed::lsp::CompletionKind;
+use zed::{CodeLabel, CodeLabelSpan, LanguageServerId};
+use zed_extension_api::{self as zed, Result};
+
+struct LuaExtension {
+ cached_binary_path: Option<String>,
+}
+
+impl LuaExtension {
+ fn language_server_binary_path(
+ &mut self,
+ language_server_id: &LanguageServerId,
+ worktree: &zed::Worktree,
+ ) -> Result<String> {
+ if let Some(path) = &self.cached_binary_path {
+ if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
+ return Ok(path.clone());
+ }
+ }
+
+ if let Some(path) = worktree.which("lua-language-server") {
+ self.cached_binary_path = Some(path.clone());
+ return Ok(path);
+ }
+
+ zed::set_language_server_installation_status(
+ &language_server_id,
+ &zed::LanguageServerInstallationStatus::CheckingForUpdate,
+ );
+ let release = zed::latest_github_release(
+ "LuaLS/lua-language-server",
+ zed::GithubReleaseOptions {
+ require_assets: true,
+ pre_release: false,
+ },
+ )?;
+
+ let (platform, arch) = zed::current_platform();
+ let asset_name = format!(
+ "lua-language-server-{version}-{os}-{arch}.tar.gz",
+ version = release.version,
+ os = match platform {
+ zed::Os::Mac => "darwin",
+ zed::Os::Linux => "linux",
+ zed::Os::Windows => "win32",
+ },
+ arch = match arch {
+ zed::Architecture::Aarch64 => "arm64",
+ zed::Architecture::X8664 => "x86_64",
+ zed::Architecture::X86 => return Err("unsupported platform x86".into()),
+ },
+ );
+
+ let asset = release
+ .assets
+ .iter()
+ .find(|asset| asset.name == asset_name)
+ .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
+
+ let version_dir = format!("lua-language-server-{}", release.version);
+ let binary_path = format!("{version_dir}/bin/lua-language-server");
+
+ if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
+ zed::set_language_server_installation_status(
+ &language_server_id,
+ &zed::LanguageServerInstallationStatus::Downloading,
+ );
+
+ zed::download_file(
+ &asset.download_url,
+ &version_dir,
+ zed::DownloadedFileType::GzipTar,
+ )
+ .map_err(|e| format!("failed to download file: {e}"))?;
+
+ let entries =
+ fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
+ for entry in entries {
+ let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
+ if entry.file_name().to_str() != Some(&version_dir) {
+ fs::remove_dir_all(&entry.path()).ok();
+ }
+ }
+ }
+
+ self.cached_binary_path = Some(binary_path.clone());
+ Ok(binary_path)
+ }
+}
+
+impl zed::Extension for LuaExtension {
+ fn new() -> Self {
+ Self {
+ cached_binary_path: None,
+ }
+ }
+
+ fn language_server_command(
+ &mut self,
+ language_server_id: &LanguageServerId,
+ worktree: &zed::Worktree,
+ ) -> Result<zed::Command> {
+ Ok(zed::Command {
+ command: self.language_server_binary_path(language_server_id, worktree)?,
+ args: Default::default(),
+ env: Default::default(),
+ })
+ }
+
+ fn label_for_completion(
+ &self,
+ _language_server_id: &LanguageServerId,
+ completion: zed::lsp::Completion,
+ ) -> Option<CodeLabel> {
+ match completion.kind? {
+ CompletionKind::Method | CompletionKind::Function => {
+ let name_len = completion.label.find('(').unwrap_or(completion.label.len());
+ Some(CodeLabel {
+ spans: vec![CodeLabelSpan::code_range(0..completion.label.len())],
+ filter_range: (0..name_len).into(),
+ code: completion.label,
+ })
+ }
+ CompletionKind::Field => Some(CodeLabel {
+ spans: vec![CodeLabelSpan::literal(
+ completion.label.clone(),
+ Some("property".into()),
+ )],
+ filter_range: (0..completion.label.len()).into(),
+ code: Default::default(),
+ }),
+ _ => None,
+ }
+ }
+
+ fn label_for_symbol(
+ &self,
+ _language_server_id: &LanguageServerId,
+ symbol: zed::lsp::Symbol,
+ ) -> Option<CodeLabel> {
+ let prefix = "let a = ";
+ let suffix = match symbol.kind {
+ zed::lsp::SymbolKind::Method => "()",
+ _ => "",
+ };
+ let code = format!("{prefix}{}{suffix}", symbol.name);
+ Some(CodeLabel {
+ spans: vec![CodeLabelSpan::code_range(
+ prefix.len()..code.len() - suffix.len(),
+ )],
+ filter_range: (0..symbol.name.len()).into(),
+ code,
+ })
+ }
+}
+
+zed::register_extension!(LuaExtension);