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.
This commit is contained in:
Antonio Scandurra 2022-02-22 10:01:08 +01:00
parent 8f375a5026
commit 8a8ae0fbcd
7 changed files with 107 additions and 51 deletions

View file

@ -31,7 +31,7 @@ use gpui::{
use items::{BufferItemHandle, MultiBufferItemHandle}; use items::{BufferItemHandle, MultiBufferItemHandle};
use itertools::Itertools as _; use itertools::Itertools as _;
use language::{ use language::{
AnchorRangeExt as _, BracketPair, Buffer, CodeAction, Completion, CompletionLabel, Diagnostic, AnchorRangeExt as _, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic,
DiagnosticSeverity, Language, Point, Selection, SelectionGoal, TransactionId, DiagnosticSeverity, Language, Point, Selection, SelectionGoal, TransactionId,
}; };
use multi_buffer::MultiBufferChunks; use multi_buffer::MultiBufferChunks;
@ -600,7 +600,7 @@ impl CompletionsMenu {
.with_highlights(combine_syntax_and_fuzzy_match_highlights( .with_highlights(combine_syntax_and_fuzzy_match_highlights(
&completion.label.text, &completion.label.text,
settings.style.text.color.into(), settings.style.text.color.into(),
styled_runs_for_completion_label( styled_runs_for_code_label(
&completion.label, &completion.label,
settings.style.text.color, settings.style.text.color,
&settings.style.syntax, &settings.style.syntax,
@ -5654,8 +5654,8 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
result result
} }
fn styled_runs_for_completion_label<'a>( pub fn styled_runs_for_code_label<'a>(
label: &'a CompletionLabel, label: &'a CodeLabel,
default_color: Color, default_color: Color,
syntax_theme: &'a theme::SyntaxTheme, syntax_theme: &'a theme::SyntaxTheme,
) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> { ) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {

View file

@ -7,7 +7,7 @@ pub use crate::{
use crate::{ use crate::{
diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
outline::OutlineItem, outline::OutlineItem,
range_from_lsp, CompletionLabel, Outline, ToLspPosition, range_from_lsp, CodeLabel, Outline, ToLspPosition,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clock::ReplicaId; use clock::ReplicaId;
@ -117,7 +117,7 @@ pub struct Diagnostic {
pub struct Completion { pub struct Completion {
pub old_range: Range<Anchor>, pub old_range: Range<Anchor>,
pub new_text: String, pub new_text: String,
pub label: CompletionLabel, pub label: CodeLabel,
pub lsp_completion: lsp::CompletionItem, pub lsp_completion: lsp::CompletionItem,
} }

View file

@ -77,21 +77,19 @@ pub trait LspExt: 'static + Send + Sync {
) -> BoxFuture<'static, Result<PathBuf>>; ) -> BoxFuture<'static, Result<PathBuf>>;
fn cached_server_binary(&self, download_dir: Arc<Path>) -> BoxFuture<'static, Option<PathBuf>>; fn cached_server_binary(&self, download_dir: Arc<Path>) -> BoxFuture<'static, Option<PathBuf>>;
fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams); fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams);
fn label_for_completion( fn label_for_completion(&self, _: &lsp::CompletionItem, _: &Language) -> Option<CodeLabel> {
&self, None
_: &lsp::CompletionItem, }
_: &Language, fn label_for_symbol(&self, _: &lsp::SymbolInformation, _: &Language) -> Option<CodeLabel> {
) -> Option<CompletionLabel> {
None None
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionLabel { pub struct CodeLabel {
pub text: String, pub text: String,
pub runs: Vec<(Range<usize>, HighlightId)>, pub runs: Vec<(Range<usize>, HighlightId)>,
pub filter_range: Range<usize>, pub filter_range: Range<usize>,
pub left_aligned_len: usize,
} }
#[derive(Default, Deserialize)] #[derive(Default, Deserialize)]
@ -431,15 +429,16 @@ impl Language {
} }
} }
pub fn label_for_completion( pub fn label_for_completion(&self, completion: &lsp::CompletionItem) -> Option<CodeLabel> {
&self,
completion: &lsp::CompletionItem,
) -> Option<CompletionLabel> {
self.lsp_ext self.lsp_ext
.as_ref()? .as_ref()?
.label_for_completion(completion, self) .label_for_completion(completion, self)
} }
pub fn label_for_symbol(&self, symbol: &lsp::SymbolInformation) -> Option<CodeLabel> {
self.lsp_ext.as_ref()?.label_for_symbol(symbol, self)
}
pub fn highlight_text<'a>( pub fn highlight_text<'a>(
&'a self, &'a self,
text: &'a Rope, text: &'a Rope,
@ -507,16 +506,15 @@ impl Grammar {
} }
} }
impl CompletionLabel { impl CodeLabel {
pub fn plain(completion: &lsp::CompletionItem) -> Self { pub fn plain(text: String, filter_text: Option<&str>) -> Self {
let mut result = Self { let mut result = Self {
text: completion.label.clone(),
runs: Vec::new(), runs: Vec::new(),
left_aligned_len: completion.label.len(), filter_range: 0..text.len(),
filter_range: 0..completion.label.len(), text,
}; };
if let Some(filter_text) = &completion.filter_text { if let Some(filter_text) = filter_text {
if let Some(ix) = completion.label.find(filter_text) { if let Some(ix) = result.text.find(filter_text) {
result.filter_range = ix..ix + filter_text.len(); result.filter_range = ix..ix + filter_text.len();
} }
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
diagnostic_set::DiagnosticEntry, CodeAction, Completion, CompletionLabel, Diagnostic, Language, diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, Diagnostic, Language,
Operation, Operation,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
@ -421,7 +421,10 @@ pub fn deserialize_completion(
new_text: completion.new_text, new_text: completion.new_text,
label: language label: language
.and_then(|l| l.label_for_completion(&lsp_completion)) .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, lsp_completion,
}) })
} }

View file

@ -10,11 +10,11 @@ use collections::{hash_map, HashMap, HashSet};
use futures::{future::Shared, Future, FutureExt}; use futures::{future::Shared, Future, FutureExt};
use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet}; use fuzzy::{PathMatch, PathMatchCandidate, PathMatchCandidateSet};
use gpui::{ use gpui::{
fonts::HighlightStyle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task,
MutableAppContext, Task, UpgradeModelHandle, WeakModelHandle, UpgradeModelHandle, WeakModelHandle,
}; };
use language::{ 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, Diagnostic, DiagnosticEntry, File as _, Language, LanguageRegistry, Operation, PointUtf16,
ToLspPosition, ToOffset, ToPointUtf16, Transaction, ToLspPosition, ToOffset, ToPointUtf16, Transaction,
}; };
@ -119,8 +119,8 @@ pub struct Definition {
} }
pub struct ProjectSymbol { pub struct ProjectSymbol {
pub text: String, pub label: CodeLabel,
pub highlight_ranges: Vec<(Range<usize>, HighlightStyle)>, pub lsp_symbol: lsp::SymbolInformation,
} }
#[derive(Default)] #[derive(Default)]
@ -1221,6 +1221,50 @@ impl Project {
self.request_lsp(buffer.clone(), GetDefinition { position }, cx) self.request_lsp(buffer.clone(), GetDefinition { position }, cx)
} }
pub fn symbols(
&self,
query: &str,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<ProjectSymbol>>> {
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::request::WorkspaceSymbol>(
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<T: ToPointUtf16>( pub fn completions<T: ToPointUtf16>(
&self, &self,
source_buffer_handle: &ModelHandle<Buffer>, source_buffer_handle: &ModelHandle<Buffer>,
@ -1300,7 +1344,12 @@ impl Project {
label: language label: language
.as_ref() .as_ref()
.and_then(|l| l.label_for_completion(&lsp_completion)) .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, lsp_completion,
}) })
} else { } else {

View file

@ -1,6 +1,8 @@
use std::{cmp, sync::Arc}; 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 fuzzy::StringMatch;
use gpui::{ use gpui::{
action, action,
@ -167,7 +169,12 @@ impl ProjectSymbolsView {
cx.emit(Event::Dismissed); cx.emit(Event::Dismissed);
} }
fn update_matches(&mut self, _: &mut ViewContext<Self>) {} fn update_matches(&mut self, cx: &mut ViewContext<Self>) {
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 { fn render_matches(&self) -> ElementBox {
if self.matches.is_empty() { if self.matches.is_empty() {
@ -215,13 +222,18 @@ impl ProjectSymbolsView {
&settings.theme.selector.item &settings.theme.selector.item
}; };
let symbol = &self.symbols[string_match.candidate_id]; 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_soft_wrap(false)
.with_highlights(combine_syntax_and_fuzzy_match_highlights( .with_highlights(combine_syntax_and_fuzzy_match_highlights(
&symbol.text, &symbol.label.text,
style.label.text.clone().into(), style.label.text.clone().into(),
symbol.highlight_ranges.iter().cloned(), syntax_runs,
&string_match.positions, &string_match.positions,
)) ))
.contained() .contained()

View file

@ -159,7 +159,7 @@ impl LspExt for RustLsp {
&self, &self,
completion: &lsp::CompletionItem, completion: &lsp::CompletionItem,
language: &Language, language: &Language,
) -> Option<CompletionLabel> { ) -> Option<CodeLabel> {
match completion.kind { match completion.kind {
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => { Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
let detail = completion.detail.as_ref().unwrap(); let detail = completion.detail.as_ref().unwrap();
@ -167,11 +167,10 @@ impl LspExt for RustLsp {
let text = format!("{}: {}", name, detail); let text = format!("{}: {}", name, detail);
let source = Rope::from(format!("struct S {{ {} }}", text).as_str()); let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
let runs = language.highlight_text(&source, 11..11 + text.len()); let runs = language.highlight_text(&source, 11..11 + text.len());
return Some(CompletionLabel { return Some(CodeLabel {
text, text,
runs, runs,
filter_range: 0..name.len(), filter_range: 0..name.len(),
left_aligned_len: name.len(),
}); });
} }
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE) Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
@ -182,11 +181,10 @@ impl LspExt for RustLsp {
let text = format!("{}: {}", name, detail); let text = format!("{}: {}", name, detail);
let source = Rope::from(format!("let {} = ();", text).as_str()); let source = Rope::from(format!("let {} = ();", text).as_str());
let runs = language.highlight_text(&source, 4..4 + text.len()); let runs = language.highlight_text(&source, 4..4 + text.len());
return Some(CompletionLabel { return Some(CodeLabel {
text, text,
runs, runs,
filter_range: 0..name.len(), filter_range: 0..name.len(),
left_aligned_len: name.len(),
}); });
} }
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD) 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 text = REGEX.replace(&completion.label, &detail[2..]).to_string();
let source = Rope::from(format!("fn {} {{}}", text).as_str()); let source = Rope::from(format!("fn {} {{}}", text).as_str());
let runs = language.highlight_text(&source, 3..3 + text.len()); let runs = language.highlight_text(&source, 3..3 + text.len());
return Some(CompletionLabel { return Some(CodeLabel {
left_aligned_len: text.find("->").unwrap_or(text.len()),
filter_range: 0..completion.label.find('(').unwrap_or(text.len()), filter_range: 0..completion.label.find('(').unwrap_or(text.len()),
text, text,
runs, runs,
@ -222,7 +219,7 @@ impl LspExt for RustLsp {
_ => None, _ => None,
}; };
let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name?)?; 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(( label.runs.push((
0..label.text.rfind('(').unwrap_or(label.text.len()), 0..label.text.rfind('(').unwrap_or(label.text.len()),
highlight_id, highlight_id,
@ -350,7 +347,7 @@ mod tests {
detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()), detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
..Default::default() ..Default::default()
}), }),
Some(CompletionLabel { Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T>".to_string(), text: "hello(&mut Option<T>) -> Vec<T>".to_string(),
filter_range: 0..5, filter_range: 0..5,
runs: vec![ runs: vec![
@ -361,7 +358,6 @@ mod tests {
(25..28, highlight_type), (25..28, highlight_type),
(29..30, highlight_type), (29..30, highlight_type),
], ],
left_aligned_len: 22,
}) })
); );
@ -372,11 +368,10 @@ mod tests {
detail: Some("usize".to_string()), detail: Some("usize".to_string()),
..Default::default() ..Default::default()
}), }),
Some(CompletionLabel { Some(CodeLabel {
text: "len: usize".to_string(), text: "len: usize".to_string(),
filter_range: 0..3, filter_range: 0..3,
runs: vec![(0..3, highlight_field), (5..10, highlight_type),], 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<T>) -> Vec<T>".to_string()), detail: Some("fn(&mut Option<T>) -> Vec<T>".to_string()),
..Default::default() ..Default::default()
}), }),
Some(CompletionLabel { Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T>".to_string(), text: "hello(&mut Option<T>) -> Vec<T>".to_string(),
filter_range: 0..5, filter_range: 0..5,
runs: vec![ runs: vec![
@ -398,7 +393,6 @@ mod tests {
(25..28, highlight_type), (25..28, highlight_type),
(29..30, highlight_type), (29..30, highlight_type),
], ],
left_aligned_len: 22,
}) })
); );
} }