language: Assert CodeLabel text ranges are correct (#40242)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth 2025-10-15 12:16:56 +02:00 committed by GitHub
parent b0b83ef5aa
commit 3882323f79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 246 additions and 290 deletions

View file

@ -12,7 +12,7 @@ use anyhow::Result;
use editor::{CompletionProvider, Editor, ExcerptId};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{App, Entity, Task, WeakEntity};
use language::{Buffer, CodeLabel, HighlightId};
use language::{Buffer, CodeLabel, CodeLabelBuilder, HighlightId};
use lsp::CompletionContext;
use project::lsp_store::{CompletionDocumentation, SymbolLocation};
use project::{
@ -673,7 +673,7 @@ impl ContextPickerCompletionProvider {
fn build_code_label_for_full_path(file_name: &str, directory: Option<&str>, cx: &App) -> CodeLabel {
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
let mut label = CodeLabel::default();
let mut label = CodeLabelBuilder::default();
label.push_str(file_name, None);
label.push_str(" ", None);
@ -682,9 +682,7 @@ fn build_code_label_for_full_path(file_name: &str, directory: Option<&str>, cx:
label.push_str(directory, comment_id);
}
label.filter_range = 0..label.text().len();
label
label.build()
}
impl CompletionProvider for ContextPickerCompletionProvider {

View file

@ -11,7 +11,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{App, Entity, Task, WeakEntity};
use http_client::HttpClientWithUrl;
use itertools::Itertools;
use language::{Buffer, CodeLabel, HighlightId};
use language::{Buffer, CodeLabel, CodeLabelBuilder, HighlightId};
use lsp::CompletionContext;
use project::lsp_store::SymbolLocation;
use project::{
@ -686,7 +686,8 @@ impl ContextPickerCompletionProvider {
};
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
let mut label = CodeLabel::plain(symbol.name.clone(), None);
let mut label = CodeLabelBuilder::default();
label.push_str(&symbol.name, None);
label.push_str(" ", None);
label.push_str(&file_name, comment_id);
label.push_str(&format!(" L{}", symbol.range.start.0.row + 1), comment_id);
@ -696,7 +697,7 @@ impl ContextPickerCompletionProvider {
Some(Completion {
replace_range: source_range.clone(),
new_text,
label,
label: label.build(),
documentation: None,
source: project::CompletionSource::Custom,
icon_path: Some(IconName::Code.path().into()),
@ -729,7 +730,7 @@ impl ContextPickerCompletionProvider {
fn build_code_label_for_full_path(file_name: &str, directory: Option<&str>, cx: &App) -> CodeLabel {
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
let mut label = CodeLabel::default();
let mut label = CodeLabelBuilder::default();
label.push_str(file_name, None);
label.push_str(" ", None);
@ -738,9 +739,7 @@ fn build_code_label_for_full_path(file_name: &str, directory: Option<&str>, cx:
label.push_str(directory, comment_id);
}
label.filter_range = 0..label.text().len();
label
label.build()
}
impl CompletionProvider for ContextPickerCompletionProvider {

View file

@ -9,6 +9,7 @@ use anyhow::Result;
use futures::StreamExt;
use futures::stream::{self, BoxStream};
use gpui::{App, SharedString, Task, WeakEntity, Window};
use language::CodeLabelBuilder;
use language::HighlightId;
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate, OffsetRangeExt};
pub use language_model::Role;
@ -328,15 +329,15 @@ impl SlashCommandLine {
}
pub fn create_label_for_command(command_name: &str, arguments: &[&str], cx: &App) -> CodeLabel {
let mut label = CodeLabel::default();
let mut label = CodeLabelBuilder::default();
label.push_str(command_name, None);
label.respan_filter_range(None);
label.push_str(" ", None);
label.push_str(
&arguments.join(" "),
cx.theme().syntax().highlight_id("comment").map(HighlightId),
);
label.filter_range = 0..command_name.len();
label
label.build()
}
#[cfg(test)]

View file

@ -7,7 +7,7 @@ use futures::Stream;
use futures::channel::mpsc;
use fuzzy::PathMatch;
use gpui::{App, Entity, Task, WeakEntity};
use language::{BufferSnapshot, CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
use language::{BufferSnapshot, CodeLabelBuilder, HighlightId, LineEnding, LspAdapterDelegate};
use project::{PathMatchCandidateSet, Project};
use serde::{Deserialize, Serialize};
use smol::stream::StreamExt;
@ -168,7 +168,7 @@ impl SlashCommand for FileSlashCommand {
.display(path_style)
.to_string();
let mut label = CodeLabel::default();
let mut label = CodeLabelBuilder::default();
let file_name = path_match.path.file_name()?;
let label_text = if path_match.is_dir {
format!("{}/ ", file_name)
@ -178,10 +178,10 @@ impl SlashCommand for FileSlashCommand {
label.push_str(label_text.as_str(), None);
label.push_str(&text, comment_id);
label.filter_range = 0..file_name.len();
label.respan_filter_range(Some(file_name));
Some(ArgumentCompletion {
label,
label: label.build(),
new_text: text,
after_completion: AfterCompletion::Compose,
replace_previous_arguments: false,

View file

@ -7,7 +7,7 @@ use collections::{HashMap, HashSet};
use editor::Editor;
use futures::future::join_all;
use gpui::{Task, WeakEntity};
use language::{BufferSnapshot, CodeLabel, HighlightId, LspAdapterDelegate};
use language::{BufferSnapshot, CodeLabel, CodeLabelBuilder, HighlightId, LspAdapterDelegate};
use std::sync::{Arc, atomic::AtomicBool};
use ui::{ActiveTheme, App, Window, prelude::*};
use util::{ResultExt, paths::PathStyle};
@ -308,10 +308,10 @@ fn create_tab_completion_label(
comment_id: Option<HighlightId>,
) -> CodeLabel {
let (parent_path, file_name) = path_style.split(path);
let mut label = CodeLabel::default();
let mut label = CodeLabelBuilder::default();
label.push_str(file_name, None);
label.push_str(" ", None);
label.push_str(parent_path.unwrap_or_default(), comment_id);
label.filter_range = 0..file_name.len();
label
label.respan_filter_range(Some(file_name));
label.build()
}

View file

@ -669,11 +669,7 @@ impl ConsoleQueryBarCompletionProvider {
&snapshot,
),
new_text: string_match.string.clone(),
label: CodeLabel {
filter_range: 0..string_match.string.len(),
text: string_match.string.clone(),
runs: Vec::new(),
},
label: CodeLabel::plain(string_match.string.clone(), None),
icon_path: None,
documentation: Some(CompletionDocumentation::MultiLineMarkdown(
variable_value.into(),
@ -782,11 +778,7 @@ impl ConsoleQueryBarCompletionProvider {
&snapshot,
),
new_text,
label: CodeLabel {
filter_range: 0..completion.label.len(),
text: completion.label,
runs: Vec::new(),
},
label: CodeLabel::plain(completion.label, None),
icon_path: None,
documentation: completion.detail.map(|detail| {
CompletionDocumentation::MultiLineMarkdown(detail.into())

View file

@ -328,11 +328,7 @@ impl CompletionsMenu {
.map(|choice| Completion {
replace_range: selection.start.text_anchor..selection.end.text_anchor,
new_text: choice.to_string(),
label: CodeLabel {
text: choice.to_string(),
runs: Default::default(),
filter_range: Default::default(),
},
label: CodeLabel::plain(choice.to_string(), None),
icon_path: None,
documentation: None,
confirm: None,

View file

@ -23077,11 +23077,7 @@ fn snippet_completions(
}),
lsp_defaults: None,
},
label: CodeLabel {
text: matching_prefix.clone(),
runs: Vec::new(),
filter_range: 0..matching_prefix.len(),
},
label: CodeLabel::plain(matching_prefix.clone(), None),
icon_path: None,
documentation: Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
single_line: snippet.name.clone().into(),

View file

@ -14878,12 +14878,7 @@ async fn test_multiline_completion(cx: &mut TestAppContext) {
} else {
item.label.clone()
};
let len = text.len();
Some(language::CodeLabel {
text,
runs: Vec::new(),
filter_range: 0..len,
})
Some(language::CodeLabel::plain(text, None))
})),
..FakeLspAdapter::default()
},

View file

@ -755,7 +755,7 @@ impl PickerDelegate for OpenPathDelegate {
.with_default_highlights(
&window.text_style(),
vec![(
delta..delta + label_len,
delta..label_len,
HighlightStyle::color(Color::Conflict.color(cx)),
)],
)
@ -765,7 +765,7 @@ impl PickerDelegate for OpenPathDelegate {
.with_default_highlights(
&window.text_style(),
vec![(
delta..delta + label_len,
delta..label_len,
HighlightStyle::color(Color::Created.color(cx)),
)],
)

View file

@ -180,8 +180,7 @@ impl StyledText {
"Can't use `with_default_highlights` and `with_highlights`"
);
let runs = Self::compute_runs(&self.text, default_style, highlights);
self.runs = Some(runs);
self
self.with_runs(runs)
}
/// Set the styling attributes for the given text, as well as
@ -194,7 +193,15 @@ impl StyledText {
self.runs.is_none(),
"Can't use `with_highlights` and `with_default_highlights`"
);
self.delayed_highlights = Some(highlights.into_iter().collect::<Vec<_>>());
self.delayed_highlights = Some(
highlights
.into_iter()
.inspect(|(run, _)| {
debug_assert!(self.text.is_char_boundary(run.start));
debug_assert!(self.text.is_char_boundary(run.end));
})
.collect::<Vec<_>>(),
);
self
}
@ -207,8 +214,10 @@ impl StyledText {
let mut ix = 0;
for (range, highlight) in highlights {
if ix < range.start {
debug_assert!(text.is_char_boundary(range.start));
runs.push(default_style.clone().to_run(range.start - ix));
}
debug_assert!(text.is_char_boundary(range.end));
runs.push(
default_style
.clone()
@ -225,6 +234,11 @@ impl StyledText {
/// Set the text runs for this piece of text.
pub fn with_runs(mut self, runs: Vec<TextRun>) -> Self {
let mut text = &**self.text;
for run in &runs {
text = text.get(run.len..).expect("invalid text run");
}
assert!(text.is_empty(), "invalid text run");
self.runs = Some(runs);
self
}

View file

@ -225,19 +225,15 @@ impl LineWrapper {
fn update_runs_after_truncation(result: &str, ellipsis: &str, runs: &mut Vec<TextRun>) {
let mut truncate_at = result.len() - ellipsis.len();
let mut run_end = None;
for (run_index, run) in runs.iter_mut().enumerate() {
if run.len <= truncate_at {
truncate_at -= run.len;
} else {
run.len = truncate_at + ellipsis.len();
run_end = Some(run_index + 1);
runs.truncate(run_index + 1);
break;
}
}
if let Some(run_end) = run_end {
runs.truncate(run_end);
}
}
/// A fragment of a line that can be wrapped.

View file

@ -670,6 +670,16 @@ pub struct CodeLabel {
pub filter_range: Range<usize>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CodeLabelBuilder {
/// The text to display.
text: String,
/// Syntax highlighting runs.
runs: Vec<(Range<usize>, HighlightId)>,
/// The portion of the text that should be used in fuzzy filtering.
filter_range: Range<usize>,
}
#[derive(Clone, Deserialize, JsonSchema)]
pub struct LanguageConfig {
/// Human-readable name of the language.
@ -2223,6 +2233,34 @@ impl Grammar {
}
}
impl CodeLabelBuilder {
pub fn respan_filter_range(&mut self, filter_text: Option<&str>) {
self.filter_range = filter_text
.and_then(|filter| self.text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..self.text.len());
}
pub fn push_str(&mut self, text: &str, highlight: Option<HighlightId>) {
let start_ix = self.text.len();
self.text.push_str(text);
if let Some(highlight) = highlight {
let end_ix = self.text.len();
self.runs.push((start_ix..end_ix, highlight));
}
}
pub fn build(mut self) -> CodeLabel {
if self.filter_range.end == 0 {
self.respan_filter_range(None);
}
CodeLabel {
text: self.text,
runs: self.runs,
filter_range: self.filter_range,
}
}
}
impl CodeLabel {
pub fn fallback_for_completion(
item: &lsp::CompletionItem,
@ -2286,22 +2324,36 @@ impl CodeLabel {
}
pub fn plain(text: String, filter_text: Option<&str>) -> Self {
Self::filtered(text, filter_text, Vec::new())
}
pub fn filtered(
text: String,
filter_text: Option<&str>,
runs: Vec<(Range<usize>, HighlightId)>,
) -> Self {
let filter_range = filter_text
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..text.len());
Self {
runs: Vec::new(),
filter_range,
text,
}
Self::new(text, filter_range, runs)
}
pub fn push_str(&mut self, text: &str, highlight: Option<HighlightId>) {
let start_ix = self.text.len();
self.text.push_str(text);
let end_ix = self.text.len();
if let Some(highlight) = highlight {
self.runs.push((start_ix..end_ix, highlight));
pub fn new(
text: String,
filter_range: Range<usize>,
runs: Vec<(Range<usize>, HighlightId)>,
) -> Self {
assert!(
text.get(filter_range.clone()).is_some(),
"invalid filter range"
);
runs.iter().for_each(|(range, _)| {
assert!(text.get(range.clone()).is_some(), "invalid run range");
});
Self {
runs,
filter_range,
text,
}
}

View file

@ -463,11 +463,7 @@ fn build_code_label(
let filter_range = label.filter_range.clone();
text.get(filter_range.clone())?;
Some(CodeLabel {
text,
runs,
filter_range,
})
Some(CodeLabel::new(text, filter_range, runs))
}
fn lsp_completion_to_extension(value: lsp::CompletionItem) -> extension::Completion {
@ -615,11 +611,7 @@ fn test_build_code_label() {
assert_eq!(
label,
CodeLabel {
text: label_text,
runs: label_runs,
filter_range: label.filter_range.clone()
}
CodeLabel::new(label_text, label.filter_range.clone(), label_runs)
)
}

View file

@ -188,11 +188,7 @@ impl super::LspAdapter for CLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(detail.len() + 1..text.len());
return Some(CodeLabel {
filter_range,
text,
runs,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
if completion.detail.is_some() =>
@ -208,11 +204,7 @@ impl super::LspAdapter for CLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(detail.len() + 1..text.len());
return Some(CodeLabel {
filter_range,
text,
runs,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
if completion.detail.is_some() =>
@ -236,11 +228,7 @@ impl super::LspAdapter for CLspAdapter {
filter_start..filter_end
});
return Some(CodeLabel {
filter_range,
text,
runs,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some(kind) => {
let highlight_name = match kind {
@ -324,11 +312,11 @@ impl super::LspAdapter for CLspAdapter {
_ => return None,
};
Some(CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
text: text[display_range].to_string(),
Some(CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
})
language.highlight_text(&text.as_str().into(), display_range),
))
}
fn prepare_initialize_params(

View file

@ -231,11 +231,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
text,
runs,
filter_range,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some((
lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE,
@ -256,11 +252,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
text,
runs,
filter_range,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some((lsp::CompletionItemKind::STRUCT, _)) => {
let text = format!("{label} struct {{}}");
@ -277,11 +269,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
text,
runs,
filter_range,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some((lsp::CompletionItemKind::INTERFACE, _)) => {
let text = format!("{label} interface {{}}");
@ -298,11 +286,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
text,
runs,
filter_range,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some((lsp::CompletionItemKind::FIELD, detail)) => {
let text = format!("{label} {detail}");
@ -320,11 +304,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
text,
runs,
filter_range,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
if let Some(signature) = detail.strip_prefix("func") {
@ -342,11 +322,7 @@ impl LspAdapter for GoLspAdapter {
.map(|start| start..start + filter_text.len())
})
.unwrap_or(0..label.len());
return Some(CodeLabel {
filter_range,
text,
runs,
});
return Some(CodeLabel::new(text, filter_range, runs));
}
}
_ => {}
@ -406,11 +382,11 @@ impl LspAdapter for GoLspAdapter {
_ => return None,
};
Some(CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
text: text[display_range].to_string(),
Some(CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
})
language.highlight_text(&text.as_str().into(), display_range),
))
}
fn diagnostic_message_to_markdown(&self, message: &str) -> Option<String> {
@ -810,15 +786,15 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "Hello(a B) c.D".to_string(),
filter_range: 0..5,
runs: vec![
Some(CodeLabel::new(
"Hello(a B) c.D".to_string(),
0..5,
vec![
(0..5, highlight_function),
(8..9, highlight_type),
(13..14, highlight_type),
],
})
]
))
);
// Nested methods
@ -834,15 +810,15 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "one.two.Three() [3]interface{}".to_string(),
filter_range: 0..13,
runs: vec![
Some(CodeLabel::new(
"one.two.Three() [3]interface{}".to_string(),
0..13,
vec![
(8..13, highlight_function),
(17..18, highlight_number),
(19..28, highlight_keyword),
],
})
))
);
// Nested fields
@ -858,11 +834,11 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "two.Three a.Bcd".to_string(),
filter_range: 0..9,
runs: vec![(4..9, highlight_field), (12..15, highlight_type)],
})
Some(CodeLabel::new(
"two.Three a.Bcd".to_string(),
0..9,
vec![(4..9, highlight_field), (12..15, highlight_type)],
))
);
}

View file

@ -407,11 +407,6 @@ impl LspAdapter for PyrightLspAdapter {
return None;
}
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| label.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..label.len());
let mut text = label.clone();
if let Some(completion_details) = item
.label_details
@ -420,14 +415,14 @@ impl LspAdapter for PyrightLspAdapter {
{
write!(&mut text, " {}", completion_details).ok();
}
Some(language::CodeLabel {
runs: highlight_id
Some(language::CodeLabel::filtered(
text,
item.filter_text.as_deref(),
highlight_id
.map(|id| (0..label.len(), id))
.into_iter()
.collect(),
text,
filter_range,
})
))
}
async fn label_for_symbol(
@ -458,11 +453,11 @@ impl LspAdapter for PyrightLspAdapter {
_ => return None,
};
Some(language::CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
text: text[display_range].to_string(),
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
})
language.highlight_text(&text.as_str().into(), display_range),
))
}
async fn workspace_configuration(
@ -1424,16 +1419,11 @@ impl LspAdapter for PyLspAdapter {
lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
_ => return None,
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| label.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..label.len());
Some(language::CodeLabel {
text: label.clone(),
runs: vec![(0..label.len(), highlight_id)],
filter_range,
})
Some(language::CodeLabel::filtered(
label.clone(),
item.filter_text.as_deref(),
vec![(0..label.len(), highlight_id)],
))
}
async fn label_for_symbol(
@ -1463,12 +1453,11 @@ impl LspAdapter for PyLspAdapter {
}
_ => return None,
};
Some(language::CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
text: text[display_range].to_string(),
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
})
language.highlight_text(&text.as_str().into(), display_range),
))
}
async fn workspace_configuration(
@ -1708,11 +1697,6 @@ impl LspAdapter for BasedPyrightLspAdapter {
return None;
}
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| label.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..label.len());
let mut text = label.clone();
if let Some(completion_details) = item
.label_details
@ -1721,14 +1705,14 @@ impl LspAdapter for BasedPyrightLspAdapter {
{
write!(&mut text, " {}", completion_details).ok();
}
Some(language::CodeLabel {
runs: highlight_id
Some(language::CodeLabel::filtered(
text,
item.filter_text.as_deref(),
highlight_id
.map(|id| (0..label.len(), id))
.into_iter()
.collect(),
text,
filter_range,
})
))
}
async fn label_for_symbol(
@ -1758,12 +1742,11 @@ impl LspAdapter for BasedPyrightLspAdapter {
}
_ => return None,
};
Some(language::CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
text: text[display_range].to_string(),
Some(language::CodeLabel::new(
text[display_range.clone()].to_string(),
filter_range,
})
language.highlight_text(&text.as_str().into(), display_range),
))
}
async fn workspace_configuration(

View file

@ -209,11 +209,7 @@ impl LspAdapter for RustLspAdapter {
})
.unwrap_or_else(filter_range);
CodeLabel {
text,
runs,
filter_range,
}
CodeLabel::new(text, filter_range, runs)
};
let mut label = match (detail_right, completion.kind) {
(Some(signature), Some(lsp::CompletionItemKind::FIELD)) => {
@ -364,11 +360,11 @@ impl LspAdapter for RustLspAdapter {
let filter_range = prefix.len()..prefix.len() + name.len();
let display_range = 0..filter_range.end;
Some(CodeLabel {
runs: language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range),
text: format!("{prefix}{name}"),
Some(CodeLabel::new(
format!("{prefix}{name}"),
filter_range,
})
language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range),
))
}
fn prepare_initialize_params(
@ -1166,10 +1162,10 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
filter_range: 0..5,
runs: vec![
Some(CodeLabel::new(
"hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
0..5,
vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
(11..17, highlight_type),
@ -1177,7 +1173,7 @@ mod tests {
(25..28, highlight_type),
(29..30, highlight_type),
],
})
))
);
assert_eq!(
adapter
@ -1194,10 +1190,10 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
filter_range: 0..5,
runs: vec![
Some(CodeLabel::new(
"hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
0..5,
vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
(11..17, highlight_type),
@ -1205,7 +1201,7 @@ mod tests {
(25..28, highlight_type),
(29..30, highlight_type),
],
})
))
);
assert_eq!(
adapter
@ -1219,11 +1215,11 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "len: usize".to_string(),
filter_range: 0..3,
runs: vec![(0..3, highlight_field), (5..10, highlight_type),],
})
Some(CodeLabel::new(
"len: usize".to_string(),
0..3,
vec![(0..3, highlight_field), (5..10, highlight_type),],
))
);
assert_eq!(
@ -1242,10 +1238,10 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
filter_range: 0..5,
runs: vec![
Some(CodeLabel::new(
"hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
0..5,
vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
(11..17, highlight_type),
@ -1253,7 +1249,7 @@ mod tests {
(25..28, highlight_type),
(29..30, highlight_type),
],
})
))
);
assert_eq!(
@ -1271,10 +1267,10 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
filter_range: 0..5,
runs: vec![
Some(CodeLabel::new(
"hello(&mut Option<T>) -> Vec<T> (use crate::foo)".to_string(),
0..5,
vec![
(0..5, highlight_function),
(7..10, highlight_keyword),
(11..17, highlight_type),
@ -1282,7 +1278,7 @@ mod tests {
(25..28, highlight_type),
(29..30, highlight_type),
],
})
))
);
assert_eq!(
@ -1301,16 +1297,16 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "await.as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(),
filter_range: 6..18,
runs: vec![
Some(CodeLabel::new(
"await.as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(),
6..18,
vec![
(6..18, HighlightId(2)),
(20..23, HighlightId(1)),
(33..40, HighlightId(0)),
(45..46, HighlightId(0))
],
})
))
);
assert_eq!(
@ -1331,10 +1327,10 @@ mod tests {
&language
)
.await,
Some(CodeLabel {
text: "pub fn as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(),
filter_range: 7..19,
runs: vec![
Some(CodeLabel::new(
"pub fn as_deref_mut(&mut self) -> IterMut<'_, T>".to_string(),
7..19,
vec![
(0..3, HighlightId(1)),
(4..6, HighlightId(1)),
(7..19, HighlightId(2)),
@ -1342,7 +1338,7 @@ mod tests {
(34..41, HighlightId(0)),
(46..47, HighlightId(0))
],
})
))
);
assert_eq!(
@ -1358,11 +1354,11 @@ mod tests {
&language,
)
.await,
Some(CodeLabel {
text: "inner_value: String".to_string(),
filter_range: 6..11,
runs: vec![(0..11, HighlightId(3)), (13..19, HighlightId(0))],
})
Some(CodeLabel::new(
"inner_value: String".to_string(),
6..11,
vec![(0..11, HighlightId(3)), (13..19, HighlightId(0))],
))
);
}
@ -1388,22 +1384,22 @@ mod tests {
adapter
.label_for_symbol("hello", lsp::SymbolKind::FUNCTION, &language)
.await,
Some(CodeLabel {
text: "fn hello".to_string(),
filter_range: 3..8,
runs: vec![(0..2, highlight_keyword), (3..8, highlight_function)],
})
Some(CodeLabel::new(
"fn hello".to_string(),
3..8,
vec![(0..2, highlight_keyword), (3..8, highlight_function)],
))
);
assert_eq!(
adapter
.label_for_symbol("World", lsp::SymbolKind::TYPE_PARAMETER, &language)
.await,
Some(CodeLabel {
text: "type World".to_string(),
filter_range: 5..10,
runs: vec![(0..4, highlight_keyword), (5..10, highlight_type)],
})
Some(CodeLabel::new(
"type World".to_string(),
5..10,
vec![(0..4, highlight_keyword), (5..10, highlight_type)],
))
);
}

View file

@ -777,16 +777,11 @@ impl LspAdapter for TypeScriptLspAdapter {
} else {
item.label.clone()
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..len);
Some(language::CodeLabel {
Some(language::CodeLabel::filtered(
text,
runs: vec![(0..len, highlight_id)],
filter_range,
})
item.filter_text.as_deref(),
vec![(0..len, highlight_id)],
))
}
async fn initialization_options(

View file

@ -201,16 +201,11 @@ impl LspAdapter for VtslsLspAdapter {
} else {
item.label.clone()
};
let filter_range = item
.filter_text
.as_deref()
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
.unwrap_or(0..len);
Some(language::CodeLabel {
Some(language::CodeLabel::filtered(
text,
runs: vec![(0..len, highlight_id)],
filter_range,
})
item.filter_text.as_deref(),
vec![(0..len, highlight_id)],
))
}
async fn workspace_configuration(

View file

@ -9365,11 +9365,7 @@ impl LspStore {
name: symbol.name,
kind: symbol.kind,
range: symbol.range,
label: CodeLabel {
text: Default::default(),
runs: Default::default(),
filter_range: Default::default(),
},
label: CodeLabel::default(),
},
cx,
)
@ -9559,11 +9555,7 @@ impl LspStore {
new_text: completion.new_text,
source: completion.source,
documentation: None,
label: CodeLabel {
text: Default::default(),
runs: Default::default(),
filter_range: Default::default(),
},
label: CodeLabel::default(),
insert_text_mode: None,
icon_path: None,
confirm: None,
@ -13060,19 +13052,19 @@ mod tests {
#[test]
fn test_multi_len_chars_normalization() {
let mut label = CodeLabel {
text: "myElˇ (parameter) myElˇ: {\n foo: string;\n}".to_string(),
runs: vec![(0..6, HighlightId(1))],
filter_range: 0..6,
};
let mut label = CodeLabel::new(
"myElˇ (parameter) myElˇ: {\n foo: string;\n}".to_string(),
0..6,
vec![(0..6, HighlightId(1))],
);
ensure_uniform_list_compatible_label(&mut label);
assert_eq!(
label,
CodeLabel {
text: "myElˇ (parameter) myElˇ: { foo: string; }".to_string(),
runs: vec![(0..6, HighlightId(1))],
filter_range: 0..6,
}
CodeLabel::new(
"myElˇ (parameter) myElˇ: { foo: string; }".to_string(),
0..6,
vec![(0..6, HighlightId(1))],
)
);
}
}