From 8a8ae0fbcd9260c5407eb56c0830b7f6173ed52c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 22 Feb 2022 10:01:08 +0100 Subject: [PATCH] Rename `CompletionLabel` to `CodeLabel` and add `Project::symbols` This only works locally for now and we haven't implemented the `RustLsp::label_for_symbol` method yet. --- crates/editor/src/editor.rs | 8 +-- crates/language/src/buffer.rs | 4 +- crates/language/src/language.rs | 34 +++++------ crates/language/src/proto.rs | 7 ++- crates/project/src/project.rs | 61 +++++++++++++++++-- crates/project_symbols/src/project_symbols.rs | 22 +++++-- crates/zed/src/language.rs | 22 +++---- 7 files changed, 107 insertions(+), 51 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ebe24da4e2..72bb002ed7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -31,7 +31,7 @@ use gpui::{ use items::{BufferItemHandle, MultiBufferItemHandle}; use itertools::Itertools as _; use language::{ - AnchorRangeExt as _, BracketPair, Buffer, CodeAction, Completion, CompletionLabel, Diagnostic, + AnchorRangeExt as _, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity, Language, Point, Selection, SelectionGoal, TransactionId, }; use multi_buffer::MultiBufferChunks; @@ -600,7 +600,7 @@ impl CompletionsMenu { .with_highlights(combine_syntax_and_fuzzy_match_highlights( &completion.label.text, settings.style.text.color.into(), - styled_runs_for_completion_label( + styled_runs_for_code_label( &completion.label, settings.style.text.color, &settings.style.syntax, @@ -5654,8 +5654,8 @@ pub fn combine_syntax_and_fuzzy_match_highlights( result } -fn styled_runs_for_completion_label<'a>( - label: &'a CompletionLabel, +pub fn styled_runs_for_code_label<'a>( + label: &'a CodeLabel, default_color: Color, syntax_theme: &'a theme::SyntaxTheme, ) -> impl 'a + Iterator, HighlightStyle)> { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b4543b02b0..336ad737c3 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -7,7 +7,7 @@ pub use crate::{ use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, outline::OutlineItem, - range_from_lsp, CompletionLabel, Outline, ToLspPosition, + range_from_lsp, CodeLabel, Outline, ToLspPosition, }; use anyhow::{anyhow, Result}; use clock::ReplicaId; @@ -117,7 +117,7 @@ pub struct Diagnostic { pub struct Completion { pub old_range: Range, pub new_text: String, - pub label: CompletionLabel, + pub label: CodeLabel, pub lsp_completion: lsp::CompletionItem, } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 9f685befff..007706c41a 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -77,21 +77,19 @@ pub trait LspExt: 'static + Send + Sync { ) -> BoxFuture<'static, Result>; fn cached_server_binary(&self, download_dir: Arc) -> BoxFuture<'static, Option>; fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams); - fn label_for_completion( - &self, - _: &lsp::CompletionItem, - _: &Language, - ) -> Option { + fn label_for_completion(&self, _: &lsp::CompletionItem, _: &Language) -> Option { + None + } + fn label_for_symbol(&self, _: &lsp::SymbolInformation, _: &Language) -> Option { None } } #[derive(Clone, Debug, PartialEq, Eq)] -pub struct CompletionLabel { +pub struct CodeLabel { pub text: String, pub runs: Vec<(Range, HighlightId)>, pub filter_range: Range, - pub left_aligned_len: usize, } #[derive(Default, Deserialize)] @@ -431,15 +429,16 @@ impl Language { } } - pub fn label_for_completion( - &self, - completion: &lsp::CompletionItem, - ) -> Option { + pub fn label_for_completion(&self, completion: &lsp::CompletionItem) -> Option { self.lsp_ext .as_ref()? .label_for_completion(completion, self) } + pub fn label_for_symbol(&self, symbol: &lsp::SymbolInformation) -> Option { + self.lsp_ext.as_ref()?.label_for_symbol(symbol, self) + } + pub fn highlight_text<'a>( &'a self, text: &'a Rope, @@ -507,16 +506,15 @@ impl Grammar { } } -impl CompletionLabel { - pub fn plain(completion: &lsp::CompletionItem) -> Self { +impl CodeLabel { + pub fn plain(text: String, filter_text: Option<&str>) -> Self { let mut result = Self { - text: completion.label.clone(), runs: Vec::new(), - left_aligned_len: completion.label.len(), - filter_range: 0..completion.label.len(), + filter_range: 0..text.len(), + text, }; - if let Some(filter_text) = &completion.filter_text { - if let Some(ix) = completion.label.find(filter_text) { + if let Some(filter_text) = filter_text { + if let Some(ix) = result.text.find(filter_text) { result.filter_range = ix..ix + filter_text.len(); } } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index a4267cfda2..80c990455c 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -1,5 +1,5 @@ use crate::{ - diagnostic_set::DiagnosticEntry, CodeAction, Completion, CompletionLabel, Diagnostic, Language, + diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, Diagnostic, Language, Operation, }; use anyhow::{anyhow, Result}; @@ -421,7 +421,10 @@ pub fn deserialize_completion( new_text: completion.new_text, label: language .and_then(|l| l.label_for_completion(&lsp_completion)) - .unwrap_or(CompletionLabel::plain(&lsp_completion)), + .unwrap_or(CodeLabel::plain( + lsp_completion.label.clone(), + lsp_completion.filter_text.as_deref(), + )), lsp_completion, }) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1aeefc5c8e..0be374fcdb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -10,11 +10,11 @@ use collections::{hash_map, HashMap, HashSet}; use futures::{future::Shared, Future, FutureExt}; use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet}; use gpui::{ - fonts::HighlightStyle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, - MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle, + AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, + UpgradeModelHandle, WeakModelHandle, }; use language::{ - range_from_lsp, Anchor, AnchorRangeExt, Bias, Buffer, CodeAction, Completion, CompletionLabel, + range_from_lsp, Anchor, AnchorRangeExt, Bias, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16, ToLspPosition, ToOffset, ToPointUtf16, Transaction, }; @@ -119,8 +119,8 @@ pub struct Definition { } pub struct ProjectSymbol { - pub text: String, - pub highlight_ranges: Vec<(Range, HighlightStyle)>, + pub label: CodeLabel, + pub lsp_symbol: lsp::SymbolInformation, } #[derive(Default)] @@ -1221,6 +1221,50 @@ impl Project { self.request_lsp(buffer.clone(), GetDefinition { position }, cx) } + pub fn symbols( + &self, + query: &str, + cx: &mut ModelContext, + ) -> Task>> { + if self.is_local() { + let mut language_servers = HashMap::default(); + for ((_, language_name), language_server) in self.language_servers.iter() { + let language = self.languages.get_language(language_name).unwrap(); + language_servers + .entry(Arc::as_ptr(language_server)) + .or_insert((language_server.clone(), language.clone())); + } + + let mut requests = Vec::new(); + for (language_server, _) in language_servers.values() { + requests.push(language_server.request::( + lsp::WorkspaceSymbolParams { + query: query.to_string(), + ..Default::default() + }, + )); + } + + cx.foreground().spawn(async move { + let responses = futures::future::try_join_all(requests).await?; + let mut symbols = Vec::new(); + for ((_, language), lsp_symbols) in language_servers.values().zip(responses) { + for lsp_symbol in lsp_symbols.into_iter().flatten() { + let label = language + .label_for_symbol(&lsp_symbol) + .unwrap_or_else(|| CodeLabel::plain(lsp_symbol.name.clone(), None)); + symbols.push(ProjectSymbol { label, lsp_symbol }); + } + } + Ok(symbols) + }) + } else if let Some(project_id) = self.remote_id() { + todo!() + } else { + Task::ready(Ok(Default::default())) + } + } + pub fn completions( &self, source_buffer_handle: &ModelHandle, @@ -1300,7 +1344,12 @@ impl Project { label: language .as_ref() .and_then(|l| l.label_for_completion(&lsp_completion)) - .unwrap_or_else(|| CompletionLabel::plain(&lsp_completion)), + .unwrap_or_else(|| { + CodeLabel::plain( + lsp_completion.label.clone(), + lsp_completion.filter_text.as_deref(), + ) + }), lsp_completion, }) } else { diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 40aeca87bd..dea94f1df2 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -1,6 +1,8 @@ use std::{cmp, sync::Arc}; -use editor::{combine_syntax_and_fuzzy_match_highlights, Editor, EditorSettings}; +use editor::{ + combine_syntax_and_fuzzy_match_highlights, styled_runs_for_code_label, Editor, EditorSettings, +}; use fuzzy::StringMatch; use gpui::{ action, @@ -167,7 +169,12 @@ impl ProjectSymbolsView { cx.emit(Event::Dismissed); } - fn update_matches(&mut self, _: &mut ViewContext) {} + fn update_matches(&mut self, cx: &mut ViewContext) { + let query = self.query_editor.read(cx).text(cx); + self.project + .update(cx, |project, cx| project.symbols(&query, cx)) + .detach_and_log_err(cx); + } fn render_matches(&self) -> ElementBox { if self.matches.is_empty() { @@ -215,13 +222,18 @@ impl ProjectSymbolsView { &settings.theme.selector.item }; let symbol = &self.symbols[string_match.candidate_id]; + let syntax_runs = styled_runs_for_code_label( + &symbol.label, + style.label.text.color, + &settings.theme.editor.syntax, + ); - Text::new(symbol.text.clone(), style.label.text.clone()) + Text::new(symbol.label.text.clone(), style.label.text.clone()) .with_soft_wrap(false) .with_highlights(combine_syntax_and_fuzzy_match_highlights( - &symbol.text, + &symbol.label.text, style.label.text.clone().into(), - symbol.highlight_ranges.iter().cloned(), + syntax_runs, &string_match.positions, )) .contained() diff --git a/crates/zed/src/language.rs b/crates/zed/src/language.rs index 748e82ffe8..af195bfa59 100644 --- a/crates/zed/src/language.rs +++ b/crates/zed/src/language.rs @@ -159,7 +159,7 @@ impl LspExt for RustLsp { &self, completion: &lsp::CompletionItem, language: &Language, - ) -> Option { + ) -> Option { match completion.kind { Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => { let detail = completion.detail.as_ref().unwrap(); @@ -167,11 +167,10 @@ impl LspExt for RustLsp { let text = format!("{}: {}", name, detail); let source = Rope::from(format!("struct S {{ {} }}", text).as_str()); let runs = language.highlight_text(&source, 11..11 + text.len()); - return Some(CompletionLabel { + return Some(CodeLabel { text, runs, filter_range: 0..name.len(), - left_aligned_len: name.len(), }); } Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE) @@ -182,11 +181,10 @@ impl LspExt for RustLsp { let text = format!("{}: {}", name, detail); let source = Rope::from(format!("let {} = ();", text).as_str()); let runs = language.highlight_text(&source, 4..4 + text.len()); - return Some(CompletionLabel { + return Some(CodeLabel { text, runs, filter_range: 0..name.len(), - left_aligned_len: name.len(), }); } Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD) @@ -201,8 +199,7 @@ impl LspExt for RustLsp { let text = REGEX.replace(&completion.label, &detail[2..]).to_string(); let source = Rope::from(format!("fn {} {{}}", text).as_str()); let runs = language.highlight_text(&source, 3..3 + text.len()); - return Some(CompletionLabel { - left_aligned_len: text.find("->").unwrap_or(text.len()), + return Some(CodeLabel { filter_range: 0..completion.label.find('(').unwrap_or(text.len()), text, runs, @@ -222,7 +219,7 @@ impl LspExt for RustLsp { _ => None, }; let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name?)?; - let mut label = CompletionLabel::plain(&completion); + let mut label = CodeLabel::plain(completion.label.clone(), None); label.runs.push(( 0..label.text.rfind('(').unwrap_or(label.text.len()), highlight_id, @@ -350,7 +347,7 @@ mod tests { detail: Some("fn(&mut Option) -> Vec".to_string()), ..Default::default() }), - Some(CompletionLabel { + Some(CodeLabel { text: "hello(&mut Option) -> Vec".to_string(), filter_range: 0..5, runs: vec![ @@ -361,7 +358,6 @@ mod tests { (25..28, highlight_type), (29..30, highlight_type), ], - left_aligned_len: 22, }) ); @@ -372,11 +368,10 @@ mod tests { detail: Some("usize".to_string()), ..Default::default() }), - Some(CompletionLabel { + Some(CodeLabel { text: "len: usize".to_string(), filter_range: 0..3, runs: vec![(0..3, highlight_field), (5..10, highlight_type),], - left_aligned_len: 3, }) ); @@ -387,7 +382,7 @@ mod tests { detail: Some("fn(&mut Option) -> Vec".to_string()), ..Default::default() }), - Some(CompletionLabel { + Some(CodeLabel { text: "hello(&mut Option) -> Vec".to_string(), filter_range: 0..5, runs: vec![ @@ -398,7 +393,6 @@ mod tests { (25..28, highlight_type), (29..30, highlight_type), ], - left_aligned_len: 22, }) ); }