Start work on allowing language servers to support multiple languages

This commit is contained in:
Max Brunsfeld 2022-03-29 16:57:18 -07:00
parent 0e1d371a67
commit 158d987965
8 changed files with 118 additions and 102 deletions

View file

@ -8,7 +8,7 @@ mod tests;
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use client::http::{self, HttpClient}; use client::http::{self, HttpClient};
use collections::HashSet; use collections::{HashMap, HashSet};
use futures::{ use futures::{
future::{BoxFuture, Shared}, future::{BoxFuture, Shared},
FutureExt, TryFutureExt, FutureExt, TryFutureExt,
@ -67,8 +67,11 @@ pub struct GitHubLspBinaryVersion {
pub url: http::Url, pub url: http::Url,
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct LanguageServerName(pub Arc<str>);
pub trait LspAdapter: 'static + Send + Sync { pub trait LspAdapter: 'static + Send + Sync {
fn name(&self) -> &'static str; fn name(&self) -> LanguageServerName;
fn fetch_latest_server_version( fn fetch_latest_server_version(
&self, &self,
http: Arc<dyn HttpClient>, http: Arc<dyn HttpClient>,
@ -159,7 +162,6 @@ pub struct Language {
pub(crate) config: LanguageConfig, pub(crate) config: LanguageConfig,
pub(crate) grammar: Option<Arc<Grammar>>, pub(crate) grammar: Option<Arc<Grammar>>,
pub(crate) adapter: Option<Arc<dyn LspAdapter>>, pub(crate) adapter: Option<Arc<dyn LspAdapter>>,
lsp_binary_path: Mutex<Option<Shared<BoxFuture<'static, Result<PathBuf, Arc<anyhow::Error>>>>>>,
} }
pub struct Grammar { pub struct Grammar {
@ -186,6 +188,12 @@ pub struct LanguageRegistry {
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>, lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
login_shell_env_loaded: Shared<Task<()>>, login_shell_env_loaded: Shared<Task<()>>,
lsp_binary_paths: Mutex<
HashMap<
LanguageServerName,
Shared<BoxFuture<'static, Result<PathBuf, Arc<anyhow::Error>>>>,
>,
>,
} }
impl LanguageRegistry { impl LanguageRegistry {
@ -197,6 +205,7 @@ impl LanguageRegistry {
lsp_binary_statuses_tx, lsp_binary_statuses_tx,
lsp_binary_statuses_rx, lsp_binary_statuses_rx,
login_shell_env_loaded: login_shell_env_loaded.shared(), login_shell_env_loaded: login_shell_env_loaded.shared(),
lsp_binary_paths: Default::default(),
} }
} }
@ -246,7 +255,7 @@ impl LanguageRegistry {
} }
pub fn start_language_server( pub fn start_language_server(
&self, self: &Arc<Self>,
language: Arc<Language>, language: Arc<Language>,
root_path: Arc<Path>, root_path: Arc<Path>,
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
@ -291,16 +300,18 @@ impl LanguageRegistry {
.ok_or_else(|| anyhow!("language server download directory has not been assigned")) .ok_or_else(|| anyhow!("language server download directory has not been assigned"))
.log_err()?; .log_err()?;
let this = self.clone();
let adapter = language.adapter.clone()?; let adapter = language.adapter.clone()?;
let background = cx.background().clone(); let background = cx.background().clone();
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone(); let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
let login_shell_env_loaded = self.login_shell_env_loaded.clone(); let login_shell_env_loaded = self.login_shell_env_loaded.clone();
Some(cx.background().spawn(async move { Some(cx.background().spawn(async move {
login_shell_env_loaded.await; login_shell_env_loaded.await;
let server_binary_path = language let server_binary_path = this
.lsp_binary_path .lsp_binary_paths
.lock() .lock()
.get_or_insert_with(|| { .entry(adapter.name())
.or_insert_with(|| {
get_server_binary_path( get_server_binary_path(
adapter.clone(), adapter.clone(),
language.clone(), language.clone(),
@ -342,7 +353,7 @@ async fn get_server_binary_path(
download_dir: Arc<Path>, download_dir: Arc<Path>,
statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>, statuses: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
) -> Result<PathBuf> { ) -> Result<PathBuf> {
let container_dir = download_dir.join(adapter.name()); let container_dir = download_dir.join(adapter.name().0.as_ref());
if !container_dir.exists() { if !container_dir.exists() {
smol::fs::create_dir_all(&container_dir) smol::fs::create_dir_all(&container_dir)
.await .await
@ -415,10 +426,13 @@ impl Language {
}) })
}), }),
adapter: None, adapter: None,
lsp_binary_path: Default::default(),
} }
} }
pub fn lsp_adapter(&self) -> Option<Arc<dyn LspAdapter>> {
self.adapter.clone()
}
pub fn with_highlights_query(mut self, source: &str) -> Result<Self> { pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
let grammar = self let grammar = self
.grammar .grammar

View file

@ -223,22 +223,19 @@ impl LspCommand for PerformRename {
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<ProjectTransaction> { ) -> Result<ProjectTransaction> {
if let Some(edit) = message { if let Some(edit) = message {
let language_server = project let (lsp_adapter, lsp_server) = project
.read_with(&cx, |project, cx| { .read_with(&cx, |project, cx| {
project project
.language_server_for_buffer(buffer.read(cx), cx) .language_server_for_buffer(buffer.read(cx), cx)
.cloned() .cloned()
}) })
.ok_or_else(|| anyhow!("no language server found for buffer"))?; .ok_or_else(|| anyhow!("no language server found for buffer"))?;
let language = buffer
.read_with(&cx, |buffer, _| buffer.language().cloned())
.ok_or_else(|| anyhow!("no language for buffer"))?;
Project::deserialize_workspace_edit( Project::deserialize_workspace_edit(
project, project,
edit, edit,
self.push_to_history, self.push_to_history,
language.name(), lsp_adapter,
language_server, lsp_server,
&mut cx, &mut cx,
) )
.await .await
@ -343,16 +340,13 @@ impl LspCommand for GetDefinition {
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<Vec<Location>> { ) -> Result<Vec<Location>> {
let mut definitions = Vec::new(); let mut definitions = Vec::new();
let language_server = project let (lsp_adapter, language_server) = project
.read_with(&cx, |project, cx| { .read_with(&cx, |project, cx| {
project project
.language_server_for_buffer(buffer.read(cx), cx) .language_server_for_buffer(buffer.read(cx), cx)
.cloned() .cloned()
}) })
.ok_or_else(|| anyhow!("no language server found for buffer"))?; .ok_or_else(|| anyhow!("no language server found for buffer"))?;
let language = buffer
.read_with(&cx, |buffer, _| buffer.language().cloned())
.ok_or_else(|| anyhow!("no language for buffer"))?;
if let Some(message) = message { if let Some(message) = message {
let mut unresolved_locations = Vec::new(); let mut unresolved_locations = Vec::new();
@ -377,7 +371,7 @@ impl LspCommand for GetDefinition {
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp( this.open_local_buffer_via_lsp(
target_uri, target_uri,
language.name(), lsp_adapter.clone(),
language_server.clone(), language_server.clone(),
cx, cx,
) )
@ -521,16 +515,13 @@ impl LspCommand for GetReferences {
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
) -> Result<Vec<Location>> { ) -> Result<Vec<Location>> {
let mut references = Vec::new(); let mut references = Vec::new();
let language_server = project let (lsp_adapter, language_server) = project
.read_with(&cx, |project, cx| { .read_with(&cx, |project, cx| {
project project
.language_server_for_buffer(buffer.read(cx), cx) .language_server_for_buffer(buffer.read(cx), cx)
.cloned() .cloned()
}) })
.ok_or_else(|| anyhow!("no language server found for buffer"))?; .ok_or_else(|| anyhow!("no language server found for buffer"))?;
let language = buffer
.read_with(&cx, |buffer, _| buffer.language().cloned())
.ok_or_else(|| anyhow!("no language for buffer"))?;
if let Some(locations) = locations { if let Some(locations) = locations {
for lsp_location in locations { for lsp_location in locations {
@ -538,7 +529,7 @@ impl LspCommand for GetReferences {
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
this.open_local_buffer_via_lsp( this.open_local_buffer_via_lsp(
lsp_location.uri, lsp_location.uri,
language.name(), lsp_adapter.clone(),
language_server.clone(), language_server.clone(),
cx, cx,
) )

View file

@ -18,8 +18,8 @@ use language::{
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
range_from_lsp, Anchor, Bias, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, range_from_lsp, Anchor, Bias, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry,
LocalFile, OffsetRangeExt, Operation, Patch, PointUtf16, TextBufferSnapshot, ToLspPosition, LanguageServerName, LocalFile, LspAdapter, OffsetRangeExt, Operation, Patch, PointUtf16,
ToOffset, ToPointUtf16, Transaction, TextBufferSnapshot, ToLspPosition, ToOffset, ToPointUtf16, Transaction,
}; };
use lsp::{DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer}; use lsp::{DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer};
use lsp_command::*; use lsp_command::*;
@ -57,8 +57,10 @@ pub struct Project {
worktrees: Vec<WorktreeHandle>, worktrees: Vec<WorktreeHandle>,
active_entry: Option<ProjectEntryId>, active_entry: Option<ProjectEntryId>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>, language_servers:
started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>, HashMap<(WorktreeId, LanguageServerName), (Arc<dyn LspAdapter>, Arc<LanguageServer>)>,
started_language_servers:
HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
language_server_statuses: BTreeMap<usize, LanguageServerStatus>, language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
language_server_settings: Arc<Mutex<serde_json::Value>>, language_server_settings: Arc<Mutex<serde_json::Value>>,
next_language_server_id: usize, next_language_server_id: usize,
@ -185,7 +187,7 @@ pub struct DocumentHighlight {
pub struct Symbol { pub struct Symbol {
pub source_worktree_id: WorktreeId, pub source_worktree_id: WorktreeId,
pub worktree_id: WorktreeId, pub worktree_id: WorktreeId,
pub language_name: String, pub language_server_name: LanguageServerName,
pub path: PathBuf, pub path: PathBuf,
pub label: CodeLabel, pub label: CodeLabel,
pub name: String, pub name: String,
@ -957,8 +959,8 @@ impl Project {
fn open_local_buffer_via_lsp( fn open_local_buffer_via_lsp(
&mut self, &mut self,
abs_path: lsp::Url, abs_path: lsp::Url,
lang_name: Arc<str>, lsp_adapter: Arc<dyn LspAdapter>,
lang_server: Arc<LanguageServer>, lsp_server: Arc<LanguageServer>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<ModelHandle<Buffer>>> { ) -> Task<Result<ModelHandle<Buffer>>> {
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
@ -976,8 +978,10 @@ impl Project {
}) })
.await?; .await?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.language_servers this.language_servers.insert(
.insert((worktree.read(cx).id(), lang_name), lang_server); (worktree.read(cx).id(), lsp_adapter.name()),
(lsp_adapter, lsp_server),
);
}); });
(worktree, PathBuf::new()) (worktree, PathBuf::new())
}; };
@ -1120,7 +1124,7 @@ impl Project {
} }
} }
if let Some(server) = language_server { if let Some((_, server)) = language_server {
server server
.notify::<lsp::notification::DidOpenTextDocument>( .notify::<lsp::notification::DidOpenTextDocument>(
lsp::DidOpenTextDocumentParams { lsp::DidOpenTextDocumentParams {
@ -1153,7 +1157,7 @@ impl Project {
if let Some(file) = File::from_dyn(buffer.file()) { if let Some(file) = File::from_dyn(buffer.file()) {
if file.is_local() { if file.is_local() {
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap(); let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
if let Some(server) = this.language_server_for_buffer(buffer, cx) { if let Some((_, server)) = this.language_server_for_buffer(buffer, cx) {
server server
.notify::<lsp::notification::DidCloseTextDocument>( .notify::<lsp::notification::DidCloseTextDocument>(
lsp::DidCloseTextDocumentParams { lsp::DidCloseTextDocumentParams {
@ -1189,7 +1193,7 @@ impl Project {
cx.background().spawn(request).detach_and_log_err(cx); cx.background().spawn(request).detach_and_log_err(cx);
} }
BufferEvent::Edited { .. } => { BufferEvent::Edited { .. } => {
let language_server = self let (_, language_server) = self
.language_server_for_buffer(buffer.read(cx), cx)? .language_server_for_buffer(buffer.read(cx), cx)?
.clone(); .clone();
let buffer = buffer.read(cx); let buffer = buffer.read(cx);
@ -1262,11 +1266,11 @@ impl Project {
fn language_servers_for_worktree( fn language_servers_for_worktree(
&self, &self,
worktree_id: WorktreeId, worktree_id: WorktreeId,
) -> impl Iterator<Item = (&str, &Arc<LanguageServer>)> { ) -> impl Iterator<Item = &(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
self.language_servers.iter().filter_map( self.language_servers.iter().filter_map(
move |((language_server_worktree_id, language_name), server)| { move |((language_server_worktree_id, _), server)| {
if *language_server_worktree_id == worktree_id { if *language_server_worktree_id == worktree_id {
Some((language_name.as_ref(), server)) Some(server)
} else { } else {
None None
} }
@ -1302,7 +1306,12 @@ impl Project {
language: Arc<Language>, language: Arc<Language>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
let key = (worktree_id, language.name()); let adapter = if let Some(adapter) = language.lsp_adapter() {
adapter
} else {
return;
};
let key = (worktree_id, adapter.name());
self.started_language_servers self.started_language_servers
.entry(key.clone()) .entry(key.clone())
.or_insert_with(|| { .or_insert_with(|| {
@ -1416,7 +1425,7 @@ impl Project {
let language_server = language_server.initialize().await.log_err()?; let language_server = language_server.initialize().await.log_err()?;
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.language_servers this.language_servers
.insert(key.clone(), language_server.clone()); .insert(key.clone(), (adapter, language_server.clone()));
this.language_server_statuses.insert( this.language_server_statuses.insert(
server_id, server_id,
LanguageServerStatus { LanguageServerStatus {
@ -1459,7 +1468,10 @@ impl Project {
} else { } else {
continue; continue;
}; };
if (file.worktree.read(cx).id(), language.name()) != key { if file.worktree.read(cx).id() != key.0
|| language.lsp_adapter().map(|a| a.name())
!= Some(key.1.clone())
{
continue; continue;
} }
@ -1675,7 +1687,7 @@ impl Project {
} }
pub fn set_language_server_settings(&mut self, settings: serde_json::Value) { pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
for server in self.language_servers.values() { for (_, server) in self.language_servers.values() {
server server
.notify::<lsp::notification::DidChangeConfiguration>( .notify::<lsp::notification::DidChangeConfiguration>(
lsp::DidChangeConfigurationParams { lsp::DidChangeConfigurationParams {
@ -1925,7 +1937,7 @@ impl Project {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
if let Some(file) = File::from_dyn(buffer.file()) { if let Some(file) = File::from_dyn(buffer.file()) {
if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) { if let Some(buffer_abs_path) = file.as_local().map(|f| f.abs_path(cx)) {
if let Some(server) = self.language_server_for_buffer(buffer, cx) { if let Some((_, server)) = self.language_server_for_buffer(buffer, cx) {
local_buffers.push((buffer_handle, buffer_abs_path, server.clone())); local_buffers.push((buffer_handle, buffer_abs_path, server.clone()));
} }
} else { } else {
@ -2062,25 +2074,24 @@ impl Project {
pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> { pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
if self.is_local() { if self.is_local() {
let mut language_servers = HashMap::default(); let mut language_servers = HashMap::default();
for ((worktree_id, language_name), language_server) in self.language_servers.iter() { for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() {
if let Some((worktree, language)) = self if let Some(worktree) = self
.worktree_for_id(*worktree_id, cx) .worktree_for_id(*worktree_id, cx)
.and_then(|worktree| worktree.read(cx).as_local()) .and_then(|worktree| worktree.read(cx).as_local())
.zip(self.languages.get_language(language_name))
{ {
language_servers language_servers
.entry(Arc::as_ptr(language_server)) .entry(Arc::as_ptr(language_server))
.or_insert(( .or_insert((
lsp_adapter.clone(),
language_server.clone(), language_server.clone(),
*worktree_id, *worktree_id,
worktree.abs_path().clone(), worktree.abs_path().clone(),
language.clone(),
)); ));
} }
} }
let mut requests = Vec::new(); let mut requests = Vec::new();
for (language_server, _, _, _) in language_servers.values() { for (_, language_server, _, _) in language_servers.values() {
requests.push(language_server.request::<lsp::request::WorkspaceSymbol>( requests.push(language_server.request::<lsp::request::WorkspaceSymbol>(
lsp::WorkspaceSymbolParams { lsp::WorkspaceSymbolParams {
query: query.to_string(), query: query.to_string(),
@ -2095,7 +2106,7 @@ impl Project {
let mut symbols = Vec::new(); let mut symbols = Vec::new();
if let Some(this) = this.upgrade(&cx) { if let Some(this) = this.upgrade(&cx) {
this.read_with(&cx, |this, cx| { this.read_with(&cx, |this, cx| {
for ((_, source_worktree_id, worktree_abs_path, language), lsp_symbols) in for ((adapter, _, source_worktree_id, worktree_abs_path), lsp_symbols) in
language_servers.into_values().zip(responses) language_servers.into_values().zip(responses)
{ {
symbols.extend(lsp_symbols.into_iter().flatten().filter_map( symbols.extend(lsp_symbols.into_iter().flatten().filter_map(
@ -2112,8 +2123,13 @@ impl Project {
path = relativize_path(&worktree_abs_path, &abs_path); path = relativize_path(&worktree_abs_path, &abs_path);
} }
let label = language let label = this
.label_for_symbol(&lsp_symbol.name, lsp_symbol.kind) .languages
.select_language(&path)
.and_then(|language| {
language
.label_for_symbol(&lsp_symbol.name, lsp_symbol.kind)
})
.unwrap_or_else(|| { .unwrap_or_else(|| {
CodeLabel::plain(lsp_symbol.name.clone(), None) CodeLabel::plain(lsp_symbol.name.clone(), None)
}); });
@ -2122,7 +2138,7 @@ impl Project {
Some(Symbol { Some(Symbol {
source_worktree_id, source_worktree_id,
worktree_id, worktree_id,
language_name: language.name().to_string(), language_server_name: adapter.name(),
name: lsp_symbol.name, name: lsp_symbol.name,
kind: lsp_symbol.kind, kind: lsp_symbol.kind,
label, label,
@ -2169,9 +2185,9 @@ impl Project {
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<ModelHandle<Buffer>>> { ) -> Task<Result<ModelHandle<Buffer>>> {
if self.is_local() { if self.is_local() {
let language_server = if let Some(server) = self.language_servers.get(&( let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&(
symbol.source_worktree_id, symbol.source_worktree_id,
Arc::from(symbol.language_name.as_str()), symbol.language_server_name.clone(),
)) { )) {
server.clone() server.clone()
} else { } else {
@ -2196,12 +2212,7 @@ impl Project {
return Task::ready(Err(anyhow!("invalid symbol path"))); return Task::ready(Err(anyhow!("invalid symbol path")));
}; };
self.open_local_buffer_via_lsp( self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx)
symbol_uri,
Arc::from(symbol.language_name.as_str()),
language_server,
cx,
)
} else if let Some(project_id) = self.remote_id() { } else if let Some(project_id) = self.remote_id() {
let request = self.client.request(proto::OpenBufferForSymbol { let request = self.client.request(proto::OpenBufferForSymbol {
project_id, project_id,
@ -2242,7 +2253,7 @@ impl Project {
if worktree.read(cx).as_local().is_some() { if worktree.read(cx).as_local().is_some() {
let buffer_abs_path = buffer_abs_path.unwrap(); let buffer_abs_path = buffer_abs_path.unwrap();
let lang_server = let (_, lang_server) =
if let Some(server) = self.language_server_for_buffer(source_buffer, cx) { if let Some(server) = self.language_server_for_buffer(source_buffer, cx) {
server.clone() server.clone()
} else { } else {
@ -2356,7 +2367,8 @@ impl Project {
let buffer_id = buffer.remote_id(); let buffer_id = buffer.remote_id();
if self.is_local() { if self.is_local() {
let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) { let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
{
server.clone() server.clone()
} else { } else {
return Task::ready(Ok(Default::default())); return Task::ready(Ok(Default::default()));
@ -2447,7 +2459,8 @@ impl Project {
if worktree.read(cx).as_local().is_some() { if worktree.read(cx).as_local().is_some() {
let buffer_abs_path = buffer_abs_path.unwrap(); let buffer_abs_path = buffer_abs_path.unwrap();
let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) { let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
{
server.clone() server.clone()
} else { } else {
return Task::ready(Ok(Default::default())); return Task::ready(Ok(Default::default()));
@ -2534,16 +2547,12 @@ impl Project {
) -> Task<Result<ProjectTransaction>> { ) -> Task<Result<ProjectTransaction>> {
if self.is_local() { if self.is_local() {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
let lang_name = if let Some(lang) = buffer.language() { let (lsp_adapter, lang_server) =
lang.name() if let Some(server) = self.language_server_for_buffer(buffer, cx) {
} else { server.clone()
return Task::ready(Ok(Default::default())); } else {
}; return Task::ready(Ok(Default::default()));
let lang_server = if let Some(server) = self.language_server_for_buffer(buffer, cx) { };
server.clone()
} else {
return Task::ready(Ok(Default::default()));
};
let range = action.range.to_point_utf16(buffer); let range = action.range.to_point_utf16(buffer);
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
@ -2580,7 +2589,7 @@ impl Project {
this, this,
edit, edit,
push_to_history, push_to_history,
lang_name, lsp_adapter,
lang_server, lang_server,
&mut cx, &mut cx,
) )
@ -2616,7 +2625,7 @@ impl Project {
this: ModelHandle<Self>, this: ModelHandle<Self>,
edit: lsp::WorkspaceEdit, edit: lsp::WorkspaceEdit,
push_to_history: bool, push_to_history: bool,
language_name: Arc<str>, lsp_adapter: Arc<dyn LspAdapter>,
language_server: Arc<LanguageServer>, language_server: Arc<LanguageServer>,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<ProjectTransaction> { ) -> Result<ProjectTransaction> {
@ -2693,7 +2702,7 @@ impl Project {
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.open_local_buffer_via_lsp( this.open_local_buffer_via_lsp(
op.text_document.uri, op.text_document.uri,
language_name.clone(), lsp_adapter.clone(),
language_server.clone(), language_server.clone(),
cx, cx,
) )
@ -2988,7 +2997,7 @@ impl Project {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
if self.is_local() { if self.is_local() {
let file = File::from_dyn(buffer.file()).and_then(File::as_local); let file = File::from_dyn(buffer.file()).and_then(File::as_local);
if let Some((file, language_server)) = if let Some((file, (_, language_server))) =
file.zip(self.language_server_for_buffer(buffer, cx).cloned()) file.zip(self.language_server_for_buffer(buffer, cx).cloned())
{ {
let lsp_params = request.to_lsp(&file.abs_path(cx), cx); let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
@ -4086,9 +4095,8 @@ impl Project {
} }
fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result<Symbol> { fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result<Symbol> {
let language = self let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id);
.languages let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id);
.get_language(&serialized_symbol.language_name);
let start = serialized_symbol let start = serialized_symbol
.start .start
.ok_or_else(|| anyhow!("invalid start"))?; .ok_or_else(|| anyhow!("invalid start"))?;
@ -4096,15 +4104,17 @@ impl Project {
.end .end
.ok_or_else(|| anyhow!("invalid end"))?; .ok_or_else(|| anyhow!("invalid end"))?;
let kind = unsafe { mem::transmute(serialized_symbol.kind) }; let kind = unsafe { mem::transmute(serialized_symbol.kind) };
let path = PathBuf::from(serialized_symbol.path);
let language = self.languages.select_language(&path);
Ok(Symbol { Ok(Symbol {
source_worktree_id: WorktreeId::from_proto(serialized_symbol.source_worktree_id), source_worktree_id,
worktree_id: WorktreeId::from_proto(serialized_symbol.worktree_id), worktree_id,
language_name: serialized_symbol.language_name.clone(), language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()),
label: language label: language
.and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind)) .and_then(|language| language.label_for_symbol(&serialized_symbol.name, kind))
.unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)), .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)),
name: serialized_symbol.name, name: serialized_symbol.name,
path: PathBuf::from(serialized_symbol.path), path,
range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column), range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column),
kind, kind,
signature: serialized_symbol signature: serialized_symbol
@ -4349,10 +4359,11 @@ impl Project {
&self, &self,
buffer: &Buffer, buffer: &Buffer,
cx: &AppContext, cx: &AppContext,
) -> Option<&Arc<LanguageServer>> { ) -> Option<&(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) { if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
let worktree_id = file.worktree_id(cx); let worktree_id = file.worktree_id(cx);
self.language_servers.get(&(worktree_id, language.name())) self.language_servers
.get(&(worktree_id, language.lsp_adapter()?.name()))
} else { } else {
None None
} }
@ -4466,7 +4477,7 @@ impl Entity for Project {
let shutdown_futures = self let shutdown_futures = self
.language_servers .language_servers
.drain() .drain()
.filter_map(|(_, server)| server.shutdown()) .filter_map(|(_, (_, server))| server.shutdown())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Some( Some(
async move { async move {
@ -4537,7 +4548,7 @@ fn serialize_symbol(symbol: &Symbol) -> proto::Symbol {
proto::Symbol { proto::Symbol {
source_worktree_id: symbol.source_worktree_id.to_proto(), source_worktree_id: symbol.source_worktree_id.to_proto(),
worktree_id: symbol.worktree_id.to_proto(), worktree_id: symbol.worktree_id.to_proto(),
language_name: symbol.language_name.clone(), language_server_name: symbol.language_server_name.0.to_string(),
name: symbol.name.clone(), name: symbol.name.clone(),
kind: unsafe { mem::transmute(symbol.kind) }, kind: unsafe { mem::transmute(symbol.kind) },
path: symbol.path.to_string_lossy().to_string(), path: symbol.path.to_string_lossy().to_string(),

View file

@ -229,7 +229,7 @@ message GetProjectSymbolsResponse {
message Symbol { message Symbol {
uint64 source_worktree_id = 1; uint64 source_worktree_id = 1;
uint64 worktree_id = 2; uint64 worktree_id = 2;
string language_name = 3; string language_server_name = 3;
string name = 4; string name = 4;
int32 kind = 5; int32 kind = 5;
string path = 6; string path = 6;

View file

@ -3,7 +3,7 @@ use client::http::{self, HttpClient, Method};
use futures::{future::BoxFuture, FutureExt, StreamExt}; use futures::{future::BoxFuture, FutureExt, StreamExt};
pub use language::*; pub use language::*;
use smol::fs::{self, File}; use smol::fs::{self, File};
use std::{any::Any, path::PathBuf, str, sync::Arc}; use std::{any::Any, path::PathBuf, sync::Arc};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use super::GithubRelease; use super::GithubRelease;
@ -11,8 +11,8 @@ use super::GithubRelease;
pub struct CLspAdapter; pub struct CLspAdapter;
impl super::LspAdapter for CLspAdapter { impl super::LspAdapter for CLspAdapter {
fn name(&self) -> &'static str { fn name(&self) -> LanguageServerName {
"clangd" LanguageServerName("clangd".into())
} }
fn fetch_latest_server_version( fn fetch_latest_server_version(

View file

@ -1,7 +1,7 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use client::http::HttpClient; use client::http::HttpClient;
use futures::{future::BoxFuture, FutureExt, StreamExt}; use futures::{future::BoxFuture, FutureExt, StreamExt};
use language::LspAdapter; use language::{LanguageServerName, LspAdapter};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use smol::fs; use smol::fs;
@ -16,8 +16,8 @@ impl JsonLspAdapter {
} }
impl LspAdapter for JsonLspAdapter { impl LspAdapter for JsonLspAdapter {
fn name(&self) -> &'static str { fn name(&self) -> LanguageServerName {
"vscode-json-languageserver" LanguageServerName("vscode-json-languageserver".into())
} }
fn server_args(&self) -> &[&str] { fn server_args(&self) -> &[&str] {

View file

@ -14,8 +14,8 @@ use super::GithubRelease;
pub struct RustLspAdapter; pub struct RustLspAdapter;
impl LspAdapter for RustLspAdapter { impl LspAdapter for RustLspAdapter {
fn name(&self) -> &'static str { fn name(&self) -> LanguageServerName {
"rust-analyzer" LanguageServerName("rust-analyzer".into())
} }
fn fetch_latest_server_version( fn fetch_latest_server_version(

View file

@ -1,7 +1,7 @@
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use client::http::HttpClient; use client::http::HttpClient;
use futures::{future::BoxFuture, FutureExt, StreamExt}; use futures::{future::BoxFuture, FutureExt, StreamExt};
use language::LspAdapter; use language::{LanguageServerName, LspAdapter};
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use smol::fs; use smol::fs;
@ -20,8 +20,8 @@ struct Versions {
} }
impl LspAdapter for TypeScriptLspAdapter { impl LspAdapter for TypeScriptLspAdapter {
fn name(&self) -> &'static str { fn name(&self) -> LanguageServerName {
"typescript-language-server" LanguageServerName("typescript-language-server".into())
} }
fn server_args(&self) -> &[&str] { fn server_args(&self) -> &[&str] {