diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index f1d7a73872b5e4ade9fe71396ac914e77432cf31..e68639b26046522027feb70ae91bd0f499d9203f 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -399,7 +399,7 @@ pub fn serialize_completion(completion: &Completion) -> proto::Completion { pub async fn deserialize_completion( completion: proto::Completion, - language: Option<&Arc>, + language: Option>, ) -> Result { let old_start = completion .old_start diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 47cb2b1e3f8334a613025a4916ab1203e05ca439..015862a05f6c307a366cc0772f61076918561108 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -113,6 +113,7 @@ pub struct Project { collaborators: HashMap, client_subscriptions: Vec, _subscriptions: Vec, + lsp_settings_changed: Option>, opened_buffer: (Rc>>, watch::Receiver<()>), shared_buffers: HashMap>, loading_buffers: HashMap< @@ -488,6 +489,7 @@ impl Project { next_language_server_id: 0, nonce: StdRng::from_entropy().gen(), initialized_persistent_state: false, + lsp_settings_changed: None, } }) } @@ -602,6 +604,7 @@ impl Project { buffer_snapshots: Default::default(), nonce: StdRng::from_entropy().gen(), initialized_persistent_state: false, + lsp_settings_changed: None, }; for worktree in worktrees { this.add_worktree(&worktree, cx); @@ -710,51 +713,55 @@ impl Project { fn on_settings_changed(&mut self, cx: &mut ModelContext<'_, Self>) { let settings = cx.global::(); - - let mut language_servers_to_start = Vec::new(); - for buffer in self.opened_buffers.values() { - if let Some(buffer) = buffer.upgrade(cx) { - let buffer = buffer.read(cx); - if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) - { - if settings.enable_language_server(Some(&language.name())) { - let worktree = file.worktree.read(cx); - language_servers_to_start.push(( - worktree.id(), - worktree.as_local().unwrap().abs_path().clone(), - language.clone(), - )); + self.lsp_settings_changed = Some(cx.spawn(|_, _| async { + let mut language_servers_to_start = Vec::new(); + for buffer in self.opened_buffers.values() { + if let Some(buffer) = buffer.upgrade(cx) { + let buffer = buffer.read(cx); + if let Some((file, language)) = + File::from_dyn(buffer.file()).zip(buffer.language()) + { + if settings.enable_language_server(Some(&language.name())) { + let worktree = file.worktree.read(cx); + language_servers_to_start.push(( + worktree.id(), + worktree.as_local().unwrap().abs_path().clone(), + language.clone(), + )); + } } } } - } - let mut language_servers_to_stop = Vec::new(); - for language in self.languages.to_vec() { - if let Some(lsp_adapter) = language.lsp_adapter() { - if !settings.enable_language_server(Some(&language.name())) { - let lsp_name = lsp_adapter.name().await; - for (worktree_id, started_lsp_name) in self.started_language_servers.keys() { - if lsp_name == *started_lsp_name { - language_servers_to_stop.push((*worktree_id, started_lsp_name.clone())); + let mut language_servers_to_stop = Vec::new(); + for language in self.languages.to_vec() { + if let Some(lsp_adapter) = language.lsp_adapter() { + if !settings.enable_language_server(Some(&language.name())) { + let lsp_name = lsp_adapter.name().await; + for (worktree_id, started_lsp_name) in self.started_language_servers.keys() + { + if lsp_name == *started_lsp_name { + language_servers_to_stop + .push((*worktree_id, started_lsp_name.clone())); + } } } } } - } - // Stop all newly-disabled language servers. - for (worktree_id, adapter_name) in language_servers_to_stop { - self.stop_language_server(worktree_id, adapter_name, cx) - .detach(); - } + // Stop all newly-disabled language servers. + for (worktree_id, adapter_name) in language_servers_to_stop { + self.stop_language_server(worktree_id, adapter_name, cx) + .detach(); + } - // Start all the newly-enabled language servers. - for (worktree_id, worktree_path, language) in language_servers_to_start { - self.start_language_server(worktree_id, worktree_path, language, cx); - } + // Start all the newly-enabled language servers. + for (worktree_id, worktree_path, language) in language_servers_to_start { + self.start_language_server(worktree_id, worktree_path, language, cx); + } - cx.notify(); + cx.notify(); + })) } pub fn buffer_for_id(&self, remote_id: u64, cx: &AppContext) -> Option> { @@ -3482,19 +3489,18 @@ impl Project { let snapshot = this.snapshot(); let clipped_position = this.clip_point_utf16(position, Bias::Left); let mut range_for_token = None; - Ok(completions - .into_iter() - .filter_map(|lsp_completion| { - // For now, we can only handle additional edits if they are returned - // when resolving the completion, not if they are present initially. - if lsp_completion - .additional_text_edits - .as_ref() - .map_or(false, |edits| !edits.is_empty()) - { - return None; - } - + let result = Vec::new(); + + for lsp_completion in completions.into_iter() { + // For now, we can only handle additional edits if they are returned + // when resolving the completion, not if they are present initially. + if lsp_completion + .additional_text_edits + .as_ref() + .map_or(false, |edits| !edits.is_empty()) + { + continue; + } let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() { // If the language server provides a range to overwrite, then @@ -3540,9 +3546,17 @@ impl Project { text.clone(), ) } - Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => { - log::info!("unsupported insert/replace completion"); - return None; + ( + snapshot.anchor_before(start)..snapshot.anchor_after(end), + edit.new_text.clone(), + ) + } + // If the language server does not provide a range, then infer + // the range based on the syntax tree. + None => { + if position != clipped_position { + log::info!("completion out of expected range"); + continue; } }; @@ -3560,12 +3574,54 @@ impl Project { lsp_completion.label.clone(), lsp_completion.filter_text.as_deref(), ) + let Range { start, end } = range_for_token + .get_or_insert_with(|| { + let offset = position.to_offset(&snapshot); + let (range, kind) = snapshot.surrounding_word(offset); + if kind == Some(CharKind::Word) { + range + } else { + offset..offset + } }) - }, - lsp_completion, - }) - }) - .collect()) + .clone(); + let text = lsp_completion + .insert_text + .as_ref() + .unwrap_or(&lsp_completion.label) + .clone(); + ( + snapshot.anchor_before(start)..snapshot.anchor_after(end), + text.clone(), + ) + } + Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => { + log::info!("unsupported insert/replace completion"); + continue; + } + }; + + let completion = Completion { + old_range, + new_text, + label: { + match language.as_ref() { + Some(l) => l.label_for_completion(&lsp_completion).await, + None => None, + } + .unwrap_or_else(|| { + CodeLabel::plain( + lsp_completion.label.clone(), + lsp_completion.filter_text.as_deref(), + ) + }) + }, + lsp_completion, + }; + + result.push(completion); + } + Ok(result) }) }) } else if let Some(project_id) = self.remote_id() { @@ -3587,10 +3643,8 @@ impl Project { let completions = Vec::new(); for completion in response.completions.into_iter() { - completions.push( - language::proto::deserialize_completion(completion, language.as_ref()) - .await, - ); + completions + .push(language::proto::deserialize_completion(completion, language).await); } completions.into_iter().collect() }) @@ -5205,7 +5259,7 @@ impl Project { .payload .completion .ok_or_else(|| anyhow!("invalid completion"))?, - language, + language.cloned(), ); Ok::<_, anyhow::Error>((buffer, completion)) })?; @@ -5605,43 +5659,52 @@ impl Project { }) } - async fn deserialize_symbol(&self, serialized_symbol: proto::Symbol) -> Result { - let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id); - let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id); - let start = serialized_symbol - .start - .ok_or_else(|| anyhow!("invalid start"))?; - let end = serialized_symbol - .end - .ok_or_else(|| anyhow!("invalid end"))?; - let kind = unsafe { mem::transmute(serialized_symbol.kind) }; - let path = PathBuf::from(serialized_symbol.path); - let language = self.languages.select_language(&path); - Ok(Symbol { - source_worktree_id, - worktree_id, - language_server_name: LanguageServerName(serialized_symbol.language_server_name.into()), - label: { - match language { - Some(language) => { - language - .label_for_symbol(&serialized_symbol.name, kind) - .await + fn deserialize_symbol( + &self, + serialized_symbol: proto::Symbol, + ) -> impl Future> { + let languages = self.languages.clone(); + async move { + let source_worktree_id = WorktreeId::from_proto(serialized_symbol.source_worktree_id); + let worktree_id = WorktreeId::from_proto(serialized_symbol.worktree_id); + let start = serialized_symbol + .start + .ok_or_else(|| anyhow!("invalid start"))?; + let end = serialized_symbol + .end + .ok_or_else(|| anyhow!("invalid end"))?; + let kind = unsafe { mem::transmute(serialized_symbol.kind) }; + let path = PathBuf::from(serialized_symbol.path); + let language = languages.select_language(&path); + Ok(Symbol { + source_worktree_id, + worktree_id, + language_server_name: LanguageServerName( + serialized_symbol.language_server_name.into(), + ), + label: { + match language { + Some(language) => { + language + .label_for_symbol(&serialized_symbol.name, kind) + .await + } + None => None, } - None => None, - } - .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)) - }, + .unwrap_or_else(|| CodeLabel::plain(serialized_symbol.name.clone(), None)) + }, - name: serialized_symbol.name, - path, - range: PointUtf16::new(start.row, start.column)..PointUtf16::new(end.row, end.column), - kind, - signature: serialized_symbol - .signature - .try_into() - .map_err(|_| anyhow!("invalid signature"))?, - }) + name: serialized_symbol.name, + path, + range: PointUtf16::new(start.row, start.column) + ..PointUtf16::new(end.row, end.column), + kind, + signature: serialized_symbol + .signature + .try_into() + .map_err(|_| anyhow!("invalid signature"))?, + }) + } } async fn handle_buffer_saved(