Add local next LSP adapter

This commit is contained in:
Mikayla 2023-09-21 18:06:00 -07:00
parent 0cceb3fdf1
commit 02a85b1252
No known key found for this signature in database
5 changed files with 206 additions and 12 deletions

View file

@ -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
} }

View file

@ -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)
}

View file

@ -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(),
)); ));

View file

@ -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(

View 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,
})
}
}