Add local next LSP adapter
This commit is contained in:
parent
0cceb3fdf1
commit
02a85b1252
5 changed files with 206 additions and 12 deletions
|
@ -712,11 +712,11 @@ impl LanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name<'a>(self: &'a Arc<Self>) -> &'a str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capabilities<'a>(self: &'a Arc<Self>) -> &'a ServerCapabilities {
|
pub fn capabilities(&self) -> &ServerCapabilities {
|
||||||
&self.capabilities
|
&self.capabilities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2267,11 +2267,13 @@ impl Project {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
|
for (_, _, server) in self.language_servers_for_worktree(worktree_id) {
|
||||||
|
let text = include_text(server.as_ref()).then(|| buffer.read(cx).text());
|
||||||
|
|
||||||
server
|
server
|
||||||
.notify::<lsp::notification::DidSaveTextDocument>(
|
.notify::<lsp::notification::DidSaveTextDocument>(
|
||||||
lsp::DidSaveTextDocumentParams {
|
lsp::DidSaveTextDocumentParams {
|
||||||
text_document: text_document.clone(),
|
text_document: text_document.clone(),
|
||||||
text: None,
|
text,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.log_err();
|
.log_err();
|
||||||
|
@ -8274,3 +8276,19 @@ async fn wait_for_loading_buffer(
|
||||||
receiver.next().await;
|
receiver.next().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn include_text(server: &lsp::LanguageServer) -> bool {
|
||||||
|
server
|
||||||
|
.capabilities()
|
||||||
|
.text_document_sync
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|sync| match sync {
|
||||||
|
lsp::TextDocumentSyncCapability::Kind(_) => None,
|
||||||
|
lsp::TextDocumentSyncCapability::Options(options) => options.save.as_ref(),
|
||||||
|
})
|
||||||
|
.and_then(|save_options| match save_options {
|
||||||
|
lsp::TextDocumentSyncSaveOptions::Supported(_) => None,
|
||||||
|
lsp::TextDocumentSyncSaveOptions::SaveOptions(options) => options.include_text,
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl ProjectSymbolsDelegate {
|
||||||
&self.external_match_candidates,
|
&self.external_match_candidates,
|
||||||
query,
|
query,
|
||||||
false,
|
false,
|
||||||
MAX_MATCHES - visible_matches.len(),
|
MAX_MATCHES - visible_matches.len().min(MAX_MATCHES),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
cx.background().clone(),
|
cx.background().clone(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -72,22 +72,20 @@ pub fn init(
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
match settings::get::<ElixirSettings>(cx).next {
|
match &settings::get::<ElixirSettings>(cx).next {
|
||||||
elixir_next::ElixirNextSetting::Off => language(
|
elixir_next::ElixirNextSetting::Off => language(
|
||||||
"elixir",
|
"elixir",
|
||||||
tree_sitter_elixir::language(),
|
tree_sitter_elixir::language(),
|
||||||
vec![Arc::new(elixir::ElixirLspAdapter)],
|
vec![Arc::new(elixir::ElixirLspAdapter)],
|
||||||
),
|
),
|
||||||
elixir_next::ElixirNextSetting::On => language(
|
elixir_next::ElixirNextSetting::On => todo!(),
|
||||||
|
elixir_next::ElixirNextSetting::Local { path } => language(
|
||||||
"elixir",
|
"elixir",
|
||||||
tree_sitter_elixir::language(),
|
tree_sitter_elixir::language(),
|
||||||
vec![Arc::new(elixir_next::BundledNextLspAdapter)],
|
vec![Arc::new(elixir_next::LocalNextLspAdapter {
|
||||||
|
path: path.clone(),
|
||||||
|
})],
|
||||||
),
|
),
|
||||||
elixir_next::ElixirNextSetting::Local { port } => unimplemented!(), /*language(
|
|
||||||
"elixir",
|
|
||||||
tree_sitter_elixir::language(),
|
|
||||||
vec![Arc::new(elixir_next::LocalNextLspAdapter { port })],
|
|
||||||
)*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
language(
|
language(
|
||||||
|
|
178
crates/zed/src/languages/elixir_next.rs
Normal file
178
crates/zed/src/languages/elixir_next.rs
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
pub use language::*;
|
||||||
|
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use settings::Setting;
|
||||||
|
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
|
#[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 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 LocalNextLspAdapter {
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapter for LocalNextLspAdapter {
|
||||||
|
async fn name(&self) -> LanguageServerName {
|
||||||
|
LanguageServerName("elixir-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> {
|
||||||
|
Ok(LanguageServerBinary {
|
||||||
|
path: self.path.clone().into(),
|
||||||
|
arguments: vec!["--stdio".into()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cached_server_binary(
|
||||||
|
&self,
|
||||||
|
_: PathBuf,
|
||||||
|
_: &dyn LspAdapterDelegate,
|
||||||
|
) -> Option<LanguageServerBinary> {
|
||||||
|
Some(LanguageServerBinary {
|
||||||
|
path: self.path.clone().into(),
|
||||||
|
arguments: vec!["--stdio".into()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||||
|
Some(LanguageServerBinary {
|
||||||
|
path: self.path.clone().into(),
|
||||||
|
arguments: vec!["--stdio".into()],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_completion(
|
||||||
|
&self,
|
||||||
|
completion: &lsp::CompletionItem,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
match completion.kind.zip(completion.detail.as_ref()) {
|
||||||
|
Some((_, detail)) if detail.starts_with("(function)") => {
|
||||||
|
let text = detail.strip_prefix("(function) ")?;
|
||||||
|
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||||
|
let source = Rope::from(format!("def {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 4..4 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: text.to_string(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((_, detail)) if detail.starts_with("(macro)") => {
|
||||||
|
let text = detail.strip_prefix("(macro) ")?;
|
||||||
|
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||||
|
let source = Rope::from(format!("defmacro {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 9..9 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: text.to_string(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((
|
||||||
|
CompletionItemKind::CLASS
|
||||||
|
| CompletionItemKind::MODULE
|
||||||
|
| CompletionItemKind::INTERFACE
|
||||||
|
| CompletionItemKind::STRUCT,
|
||||||
|
_,
|
||||||
|
)) => {
|
||||||
|
let filter_range = 0..completion
|
||||||
|
.label
|
||||||
|
.find(" (")
|
||||||
|
.unwrap_or(completion.label.len());
|
||||||
|
let text = &completion.label[filter_range.clone()];
|
||||||
|
let source = Rope::from(format!("defmodule {text}").as_str());
|
||||||
|
let runs = language.highlight_text(&source, 10..10 + text.len());
|
||||||
|
return Some(CodeLabel {
|
||||||
|
text: completion.label.clone(),
|
||||||
|
runs,
|
||||||
|
filter_range,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn label_for_symbol(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
kind: SymbolKind,
|
||||||
|
language: &Arc<Language>,
|
||||||
|
) -> Option<CodeLabel> {
|
||||||
|
let (text, filter_range, display_range) = match kind {
|
||||||
|
SymbolKind::METHOD | SymbolKind::FUNCTION => {
|
||||||
|
let text = format!("def {}", name);
|
||||||
|
let filter_range = 4..4 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
SymbolKind::CLASS | SymbolKind::MODULE | SymbolKind::INTERFACE | SymbolKind::STRUCT => {
|
||||||
|
let text = format!("defmodule {}", name);
|
||||||
|
let filter_range = 10..10 + name.len();
|
||||||
|
let display_range = 0..filter_range.end;
|
||||||
|
(text, filter_range, display_range)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(CodeLabel {
|
||||||
|
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||||
|
text: text[display_range].to_string(),
|
||||||
|
filter_range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue