Detailed changes
@@ -379,24 +379,24 @@
},
// Settings specific to our elixir integration
"elixir": {
- // Set Zed to use the experimental Next LS LSP server.
+ // Change the LSP zed uses for elixir.
// Note that changing this setting requires a restart of Zed
// to take effect.
//
// May take 3 values:
- // 1. Use the standard elixir-ls LSP server
- // "next": "off"
- // 2. Use a bundled version of the next Next LS LSP server
- // "next": "on",
- // 3. Use a local build of the next Next LS LSP server:
- // "next": {
+ // 1. Use the standard ElixirLS, this is the default
+ // "lsp": "elixir_ls"
+ // 2. Use the experimental NextLs
+ // "lsp": "next_ls",
+ // 3. Use a language server installed locally on your machine:
+ // "lsp": {
// "local": {
// "path": "~/next-ls/bin/start",
// "arguments": ["--stdio"]
// }
// },
//
- "next": "off"
+ "lsp": "elixir_ls"
},
// Different settings for specific languages.
"languages": {
@@ -6,12 +6,11 @@ use rust_embed::RustEmbed;
use std::{borrow::Cow, str, sync::Arc};
use util::asset_str;
-use self::elixir_next::ElixirSettings;
+use self::elixir::ElixirSettings;
mod c;
mod css;
mod elixir;
-mod elixir_next;
mod go;
mod html;
mod json;
@@ -46,7 +45,7 @@ pub fn init(
node_runtime: Arc<dyn NodeRuntime>,
cx: &mut AppContext,
) {
- settings::register::<elixir_next::ElixirSettings>(cx);
+ settings::register::<elixir::ElixirSettings>(cx);
let language = |name, grammar, adapters| {
languages.register(name, load_config(name), grammar, adapters, load_queries)
@@ -72,21 +71,21 @@ pub fn init(
],
);
- match &settings::get::<ElixirSettings>(cx).next {
- elixir_next::ElixirNextSetting::Off => language(
+ match &settings::get::<ElixirSettings>(cx).lsp {
+ elixir::ElixirLspSetting::ElixirLs => language(
"elixir",
tree_sitter_elixir::language(),
vec![Arc::new(elixir::ElixirLspAdapter)],
),
- elixir_next::ElixirNextSetting::On => language(
+ elixir::ElixirLspSetting::NextLs => language(
"elixir",
tree_sitter_elixir::language(),
- vec![Arc::new(elixir_next::NextLspAdapter)],
+ vec![Arc::new(elixir::NextLspAdapter)],
),
- elixir_next::ElixirNextSetting::Local { path, arguments } => language(
+ elixir::ElixirLspSetting::Local { path, arguments } => language(
"elixir",
tree_sitter_elixir::language(),
- vec![Arc::new(elixir_next::LocalNextLspAdapter {
+ vec![Arc::new(elixir::LocalLspAdapter {
path: path.clone(),
arguments: arguments.clone(),
})],
@@ -1,12 +1,17 @@
-use anyhow::{anyhow, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
use async_trait::async_trait;
use futures::StreamExt;
use gpui::{AsyncAppContext, Task};
pub use language::*;
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
+use schemars::JsonSchema;
+use serde_derive::{Deserialize, Serialize};
+use settings::Setting;
use smol::fs::{self, File};
use std::{
any::Any,
+ env::consts,
+ ops::Deref,
path::PathBuf,
sync::{
atomic::{AtomicBool, Ordering::SeqCst},
@@ -14,11 +19,50 @@ use std::{
},
};
use util::{
+ async_iife,
fs::remove_matching,
github::{latest_github_release, GitHubLspBinaryVersion},
ResultExt,
};
+#[derive(Clone, Serialize, Deserialize, JsonSchema)]
+pub struct ElixirSettings {
+ pub lsp: ElixirLspSetting,
+}
+
+#[derive(Clone, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ElixirLspSetting {
+ ElixirLs,
+ NextLs,
+ Local {
+ path: String,
+ arguments: Vec<String>,
+ },
+}
+
+#[derive(Clone, Serialize, Default, Deserialize, JsonSchema)]
+pub struct ElixirSettingsContent {
+ lsp: Option<ElixirLspSetting>,
+}
+
+impl Setting for ElixirSettings {
+ const KEY: Option<&'static str> = Some("elixir");
+
+ type FileContent = ElixirSettingsContent;
+
+ fn load(
+ default_value: &Self::FileContent,
+ user_values: &[&Self::FileContent],
+ _: &gpui::AppContext,
+ ) -> Result<Self>
+ where
+ Self: Sized,
+ {
+ Self::load_via_json_merge(default_value, user_values)
+ }
+}
+
pub struct ElixirLspAdapter;
#[async_trait]
@@ -144,14 +188,14 @@ impl LspAdapter for ElixirLspAdapter {
container_dir: PathBuf,
_: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir).await
+ get_cached_server_binary_elixir_ls(container_dir).await
}
async fn installation_test_binary(
&self,
container_dir: PathBuf,
) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir).await
+ get_cached_server_binary_elixir_ls(container_dir).await
}
async fn label_for_completion(
@@ -238,7 +282,9 @@ impl LspAdapter for ElixirLspAdapter {
}
}
-async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
+async fn get_cached_server_binary_elixir_ls(
+ container_dir: PathBuf,
+) -> Option<LanguageServerBinary> {
(|| async move {
let mut last = None;
let mut entries = fs::read_dir(&container_dir).await?;
@@ -254,3 +300,247 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
.await
.log_err()
}
+
+pub struct NextLspAdapter;
+
+#[async_trait]
+impl LspAdapter for NextLspAdapter {
+ async fn name(&self) -> LanguageServerName {
+ LanguageServerName("next-ls".into())
+ }
+
+ fn short_name(&self) -> &'static str {
+ "next-ls"
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ delegate: &dyn LspAdapterDelegate,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ let release =
+ latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?;
+ let version = release.name.clone();
+ let platform = match consts::ARCH {
+ "x86_64" => "darwin_arm64",
+ "aarch64" => "darwin_amd64",
+ other => bail!("Running on unsupported platform: {other}"),
+ };
+ let asset_name = format!("next_ls_{}", platform);
+ 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: version,
+ 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("next-ls");
+
+ 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 mut file = smol::fs::File::create(&binary_path).await?;
+ if !response.status().is_success() {
+ Err(anyhow!(
+ "download failed with status {}",
+ response.status().to_string()
+ ))?;
+ }
+ futures::io::copy(response.body_mut(), &mut file).await?;
+
+ fs::set_permissions(
+ &binary_path,
+ <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
+ )
+ .await?;
+ }
+
+ Ok(LanguageServerBinary {
+ path: binary_path,
+ arguments: vec!["--stdio".into()],
+ })
+ }
+
+ async fn cached_server_binary(
+ &self,
+ container_dir: PathBuf,
+ _: &dyn LspAdapterDelegate,
+ ) -> Option<LanguageServerBinary> {
+ get_cached_server_binary_next(container_dir)
+ .await
+ .map(|mut binary| {
+ binary.arguments = vec!["--stdio".into()];
+ binary
+ })
+ }
+
+ async fn installation_test_binary(
+ &self,
+ container_dir: PathBuf,
+ ) -> Option<LanguageServerBinary> {
+ get_cached_server_binary_next(container_dir)
+ .await
+ .map(|mut binary| {
+ binary.arguments = vec!["--help".into()];
+ binary
+ })
+ }
+
+ async fn label_for_completion(
+ &self,
+ completion: &lsp::CompletionItem,
+ language: &Arc<Language>,
+ ) -> Option<CodeLabel> {
+ label_for_completion_elixir(completion, language)
+ }
+
+ async fn label_for_symbol(
+ &self,
+ name: &str,
+ symbol_kind: SymbolKind,
+ language: &Arc<Language>,
+ ) -> Option<CodeLabel> {
+ label_for_symbol_elixir(name, symbol_kind, language)
+ }
+}
+
+async fn get_cached_server_binary_next(container_dir: PathBuf) -> Option<LanguageServerBinary> {
+ async_iife!({
+ 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 == "next-ls")
+ {
+ last_binary_path = Some(entry.path());
+ }
+ }
+
+ if let Some(path) = last_binary_path {
+ Ok(LanguageServerBinary {
+ path,
+ arguments: Vec::new(),
+ })
+ } else {
+ Err(anyhow!("no cached binary"))
+ }
+ })
+ .await
+ .log_err()
+}
+
+pub struct LocalLspAdapter {
+ pub path: String,
+ pub arguments: Vec<String>,
+}
+
+#[async_trait]
+impl LspAdapter for LocalLspAdapter {
+ async fn name(&self) -> LanguageServerName {
+ LanguageServerName("local-ls".into())
+ }
+
+ fn short_name(&self) -> &'static str {
+ "local-ls"
+ }
+
+ async fn fetch_latest_server_version(
+ &self,
+ _: &dyn LspAdapterDelegate,
+ ) -> Result<Box<dyn 'static + Send + Any>> {
+ Ok(Box::new(()) as Box<_>)
+ }
+
+ async fn fetch_server_binary(
+ &self,
+ _: Box<dyn 'static + Send + Any>,
+ _: PathBuf,
+ _: &dyn LspAdapterDelegate,
+ ) -> Result<LanguageServerBinary> {
+ let path = shellexpand::full(&self.path)?;
+ Ok(LanguageServerBinary {
+ path: PathBuf::from(path.deref()),
+ arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
+ })
+ }
+
+ async fn cached_server_binary(
+ &self,
+ _: PathBuf,
+ _: &dyn LspAdapterDelegate,
+ ) -> Option<LanguageServerBinary> {
+ let path = shellexpand::full(&self.path).ok()?;
+ Some(LanguageServerBinary {
+ path: PathBuf::from(path.deref()),
+ arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
+ })
+ }
+
+ async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
+ let path = shellexpand::full(&self.path).ok()?;
+ Some(LanguageServerBinary {
+ path: PathBuf::from(path.deref()),
+ arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
+ })
+ }
+
+ async fn label_for_completion(
+ &self,
+ completion: &lsp::CompletionItem,
+ language: &Arc<Language>,
+ ) -> Option<CodeLabel> {
+ label_for_completion_elixir(completion, language)
+ }
+
+ async fn label_for_symbol(
+ &self,
+ name: &str,
+ symbol: SymbolKind,
+ language: &Arc<Language>,
+ ) -> Option<CodeLabel> {
+ label_for_symbol_elixir(name, symbol, language)
+ }
+}
+
+fn label_for_completion_elixir(
+ completion: &lsp::CompletionItem,
+ language: &Arc<Language>,
+) -> Option<CodeLabel> {
+ return Some(CodeLabel {
+ runs: language.highlight_text(&completion.label.clone().into(), 0..completion.label.len()),
+ text: completion.label.clone(),
+ filter_range: 0..completion.label.len(),
+ });
+}
+
+fn label_for_symbol_elixir(
+ name: &str,
+ _: SymbolKind,
+ language: &Arc<Language>,
+) -> Option<CodeLabel> {
+ Some(CodeLabel {
+ runs: language.highlight_text(&name.into(), 0..name.len()),
+ text: name.to_string(),
+ filter_range: 0..name.len(),
+ })
+}
@@ -1,266 +0,0 @@
-use anyhow::{anyhow, bail, Result};
-
-use async_trait::async_trait;
-pub use language::*;
-use lsp::{LanguageServerBinary, SymbolKind};
-use schemars::JsonSchema;
-use serde_derive::{Deserialize, Serialize};
-use settings::Setting;
-use smol::{fs, stream::StreamExt};
-use std::{any::Any, env::consts, ops::Deref, path::PathBuf, sync::Arc};
-use util::{
- async_iife,
- github::{latest_github_release, GitHubLspBinaryVersion},
- ResultExt,
-};
-
-#[derive(Clone, Serialize, Deserialize, JsonSchema)]
-pub struct ElixirSettings {
- pub next: ElixirNextSetting,
-}
-
-#[derive(Clone, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum ElixirNextSetting {
- Off,
- On,
- Local {
- path: String,
- arguments: Vec<String>,
- },
-}
-
-#[derive(Clone, Serialize, Default, Deserialize, JsonSchema)]
-pub struct ElixirSettingsContent {
- next: Option<ElixirNextSetting>,
-}
-
-impl Setting for ElixirSettings {
- const KEY: Option<&'static str> = Some("elixir");
-
- type FileContent = ElixirSettingsContent;
-
- fn load(
- default_value: &Self::FileContent,
- user_values: &[&Self::FileContent],
- _: &gpui::AppContext,
- ) -> Result<Self>
- where
- Self: Sized,
- {
- Self::load_via_json_merge(default_value, user_values)
- }
-}
-
-pub struct NextLspAdapter;
-
-#[async_trait]
-impl LspAdapter for NextLspAdapter {
- async fn name(&self) -> LanguageServerName {
- LanguageServerName("next-ls".into())
- }
-
- fn short_name(&self) -> &'static str {
- "next-ls"
- }
-
- async fn fetch_latest_server_version(
- &self,
- delegate: &dyn LspAdapterDelegate,
- ) -> Result<Box<dyn 'static + Send + Any>> {
- let release =
- latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?;
- let version = release.name.clone();
- let platform = match consts::ARCH {
- "x86_64" => "darwin_arm64",
- "aarch64" => "darwin_amd64",
- other => bail!("Running on unsupported platform: {other}"),
- };
- let asset_name = format!("next_ls_{}", platform);
- 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: version,
- 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("next-ls");
-
- 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 mut file = smol::fs::File::create(&binary_path).await?;
- if !response.status().is_success() {
- Err(anyhow!(
- "download failed with status {}",
- response.status().to_string()
- ))?;
- }
- futures::io::copy(response.body_mut(), &mut file).await?;
-
- fs::set_permissions(
- &binary_path,
- <fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
- )
- .await?;
- }
-
- Ok(LanguageServerBinary {
- path: binary_path,
- arguments: vec!["--stdio".into()],
- })
- }
-
- async fn cached_server_binary(
- &self,
- container_dir: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir)
- .await
- .map(|mut binary| {
- binary.arguments = vec!["--stdio".into()];
- binary
- })
- }
-
- async fn installation_test_binary(
- &self,
- container_dir: PathBuf,
- ) -> Option<LanguageServerBinary> {
- get_cached_server_binary(container_dir)
- .await
- .map(|mut binary| {
- binary.arguments = vec!["--help".into()];
- binary
- })
- }
-
- async fn label_for_symbol(
- &self,
- name: &str,
- symbol_kind: SymbolKind,
- language: &Arc<Language>,
- ) -> Option<CodeLabel> {
- label_for_symbol_next(name, symbol_kind, language)
- }
-}
-
-async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
- async_iife!({
- 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 == "next-ls")
- {
- last_binary_path = Some(entry.path());
- }
- }
-
- if let Some(path) = last_binary_path {
- Ok(LanguageServerBinary {
- path,
- arguments: Vec::new(),
- })
- } else {
- Err(anyhow!("no cached binary"))
- }
- })
- .await
- .log_err()
-}
-
-pub struct LocalNextLspAdapter {
- pub path: String,
- pub arguments: Vec<String>,
-}
-
-#[async_trait]
-impl LspAdapter for LocalNextLspAdapter {
- async fn name(&self) -> LanguageServerName {
- LanguageServerName("local-next-ls".into())
- }
-
- fn short_name(&self) -> &'static str {
- "next-ls"
- }
-
- async fn fetch_latest_server_version(
- &self,
- _: &dyn LspAdapterDelegate,
- ) -> Result<Box<dyn 'static + Send + Any>> {
- Ok(Box::new(()) as Box<_>)
- }
-
- async fn fetch_server_binary(
- &self,
- _: Box<dyn 'static + Send + Any>,
- _: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Result<LanguageServerBinary> {
- let path = shellexpand::full(&self.path)?;
- Ok(LanguageServerBinary {
- path: PathBuf::from(path.deref()),
- arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
- })
- }
-
- async fn cached_server_binary(
- &self,
- _: PathBuf,
- _: &dyn LspAdapterDelegate,
- ) -> Option<LanguageServerBinary> {
- let path = shellexpand::full(&self.path).ok()?;
- Some(LanguageServerBinary {
- path: PathBuf::from(path.deref()),
- arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
- })
- }
-
- async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
- let path = shellexpand::full(&self.path).ok()?;
- Some(LanguageServerBinary {
- path: PathBuf::from(path.deref()),
- arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
- })
- }
-
- async fn label_for_symbol(
- &self,
- name: &str,
- symbol: SymbolKind,
- language: &Arc<Language>,
- ) -> Option<CodeLabel> {
- label_for_symbol_next(name, symbol, language)
- }
-}
-
-fn label_for_symbol_next(name: &str, _: SymbolKind, language: &Arc<Language>) -> Option<CodeLabel> {
- Some(CodeLabel {
- runs: language.highlight_text(&name.into(), 0..name.len()),
- text: name.to_string(),
- filter_range: 0..name.len(),
- })
-}