Reduce amount of allocations in RustLsp label handling (#35786)

There can be a lot of completions after all


Release Notes:

- N/A
This commit is contained in:
Lukas Wirth 2025-08-07 15:24:29 +02:00 committed by GitHub
parent c397027ec2
commit 4dbd24d75f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 40 additions and 65 deletions

View file

@ -876,7 +876,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S
vec![Inlay::edit_prediction( vec![Inlay::edit_prediction(
post_inc(&mut next_inlay_id), post_inc(&mut next_inlay_id),
snapshot.buffer_snapshot.anchor_before(position), snapshot.buffer_snapshot.anchor_before(position),
format!("Test inlay {next_inlay_id}"), Rope::from_iter(["Test inlay ", "next_inlay_id"]),
)], )],
cx, cx,
); );

View file

@ -48,16 +48,16 @@ pub struct Inlay {
impl Inlay { impl Inlay {
pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self { pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self {
let mut text = hint.text(); let mut text = hint.text();
if hint.padding_right && !text.ends_with(' ') { if hint.padding_right && text.chars_at(text.len().saturating_sub(1)).next() != Some(' ') {
text.push(' '); text.push(" ");
} }
if hint.padding_left && !text.starts_with(' ') { if hint.padding_left && text.chars_at(0).next() != Some(' ') {
text.insert(0, ' '); text.push_front(" ");
} }
Self { Self {
id: InlayId::Hint(id), id: InlayId::Hint(id),
position, position,
text: text.into(), text,
color: None, color: None,
} }
} }
@ -737,13 +737,13 @@ impl InlayMap {
Inlay::mock_hint( Inlay::mock_hint(
post_inc(next_inlay_id), post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias), snapshot.buffer.anchor_at(position, bias),
text.clone(), &text,
) )
} else { } else {
Inlay::edit_prediction( Inlay::edit_prediction(
post_inc(next_inlay_id), post_inc(next_inlay_id),
snapshot.buffer.anchor_at(position, bias), snapshot.buffer.anchor_at(position, bias),
text.clone(), &text,
) )
}; };
let inlay_id = next_inlay.id; let inlay_id = next_inlay.id;
@ -1694,7 +1694,7 @@ mod tests {
(offset, inlay.clone()) (offset, inlay.clone())
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut expected_text = Rope::from(buffer_snapshot.text()); let mut expected_text = Rope::from(&buffer_snapshot.text());
for (offset, inlay) in inlays.iter().rev() { for (offset, inlay) in inlays.iter().rev() {
expected_text.replace(*offset..*offset, &inlay.text.to_string()); expected_text.replace(*offset..*offset, &inlay.text.to_string());
} }

View file

@ -3546,7 +3546,7 @@ pub mod tests {
let excerpt_hints = excerpt_hints.read(); let excerpt_hints = excerpt_hints.read();
for id in &excerpt_hints.ordered_hints { for id in &excerpt_hints.ordered_hints {
let hint = &excerpt_hints.hints_by_id[id]; let hint = &excerpt_hints.hints_by_id[id];
let mut label = hint.text(); let mut label = hint.text().to_string();
if hint.padding_left { if hint.padding_left {
label.insert(0, ' '); label.insert(0, ' ');
} }

View file

@ -191,7 +191,7 @@ impl Editor {
if let Some(language) = language { if let Some(language) = language {
for signature in &mut signature_help.signatures { for signature in &mut signature_help.signatures {
let text = Rope::from(signature.label.to_string()); let text = Rope::from(signature.label.as_ref());
let highlights = language let highlights = language
.highlight_text(&text, 0..signature.label.len()) .highlight_text(&text, 0..signature.label.len())
.into_iter() .into_iter()

View file

@ -341,7 +341,7 @@ impl LspAdapter for RustLspAdapter {
let name = &completion.label; let name = &completion.label;
let text = format!("{name}: {signature}"); let text = format!("{name}: {signature}");
let prefix = "struct S { "; let prefix = "struct S { ";
let source = Rope::from(format!("{prefix}{text} }}")); let source = Rope::from_iter([prefix, &text, " }"]);
let runs = let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
mk_label(text, runs) mk_label(text, runs)
@ -353,7 +353,7 @@ impl LspAdapter for RustLspAdapter {
let name = &completion.label; let name = &completion.label;
let text = format!("{name}: {signature}",); let text = format!("{name}: {signature}",);
let prefix = "let "; let prefix = "let ";
let source = Rope::from(format!("{prefix}{text} = ();")); let source = Rope::from_iter([prefix, &text, " = ();"]);
let runs = let runs =
language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
mk_label(text, runs) mk_label(text, runs)
@ -387,7 +387,7 @@ impl LspAdapter for RustLspAdapter {
&completion.label &completion.label
}; };
let text = format!("{label}{suffix}"); let text = format!("{label}{suffix}");
let source = Rope::from(format!("{prefix} {text} {{}}")); let source = Rope::from_iter([prefix, " ", &text, " {}"]);
let run_start = prefix.len() + 1; let run_start = prefix.len() + 1;
let runs = language.highlight_text(&source, run_start..run_start + text.len()); let runs = language.highlight_text(&source, run_start..run_start + text.len());
mk_label(text, runs) mk_label(text, runs)
@ -450,55 +450,22 @@ impl LspAdapter for RustLspAdapter {
kind: lsp::SymbolKind, kind: lsp::SymbolKind,
language: &Arc<Language>, language: &Arc<Language>,
) -> Option<CodeLabel> { ) -> Option<CodeLabel> {
let (text, filter_range, display_range) = match kind { let (prefix, suffix) = match kind {
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => ("fn ", " () {}"),
let text = format!("fn {} () {{}}", name); lsp::SymbolKind::STRUCT => ("struct ", " {}"),
let filter_range = 3..3 + name.len(); lsp::SymbolKind::ENUM => ("enum ", " {}"),
let display_range = 0..filter_range.end; lsp::SymbolKind::INTERFACE => ("trait ", " {}"),
(text, filter_range, display_range) lsp::SymbolKind::CONSTANT => ("const ", ": () = ();"),
} lsp::SymbolKind::MODULE => ("mod ", " {}"),
lsp::SymbolKind::STRUCT => { lsp::SymbolKind::TYPE_PARAMETER => ("type ", " {}"),
let text = format!("struct {} {{}}", name);
let filter_range = 7..7 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
lsp::SymbolKind::ENUM => {
let text = format!("enum {} {{}}", name);
let filter_range = 5..5 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
lsp::SymbolKind::INTERFACE => {
let text = format!("trait {} {{}}", name);
let filter_range = 6..6 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
lsp::SymbolKind::CONSTANT => {
let text = format!("const {}: () = ();", name);
let filter_range = 6..6 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
lsp::SymbolKind::MODULE => {
let text = format!("mod {} {{}}", name);
let filter_range = 4..4 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
lsp::SymbolKind::TYPE_PARAMETER => {
let text = format!("type {} {{}}", name);
let filter_range = 5..5 + name.len();
let display_range = 0..filter_range.end;
(text, filter_range, display_range)
}
_ => return None, _ => return None,
}; };
let filter_range = prefix.len()..prefix.len() + name.len();
let display_range = 0..filter_range.end;
Some(CodeLabel { Some(CodeLabel {
runs: language.highlight_text(&text.as_str().into(), display_range.clone()), runs: language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range),
text: text[display_range].to_string(), text: format!("{prefix}{name}"),
filter_range, filter_range,
}) })
} }

View file

@ -73,7 +73,6 @@ use gpui::{
App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString, App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString,
Task, WeakEntity, Window, Task, WeakEntity, Window,
}; };
use itertools::Itertools;
use language::{ use language::{
Buffer, BufferEvent, Capability, CodeLabel, CursorShape, DiagnosticSourceKind, Language, Buffer, BufferEvent, Capability, CodeLabel, CursorShape, DiagnosticSourceKind, Language,
LanguageName, LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList, LanguageName, LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList,
@ -113,7 +112,7 @@ use std::{
use task_store::TaskStore; use task_store::TaskStore;
use terminals::Terminals; use terminals::Terminals;
use text::{Anchor, BufferId, OffsetRangeExt, Point}; use text::{Anchor, BufferId, OffsetRangeExt, Point, Rope};
use toolchain_store::EmptyToolchainStore; use toolchain_store::EmptyToolchainStore;
use util::{ use util::{
ResultExt as _, ResultExt as _,
@ -668,10 +667,10 @@ pub enum ResolveState {
} }
impl InlayHint { impl InlayHint {
pub fn text(&self) -> String { pub fn text(&self) -> Rope {
match &self.label { match &self.label {
InlayHintLabel::String(s) => s.to_owned(), InlayHintLabel::String(s) => Rope::from(s),
InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""), InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(),
} }
} }
} }

View file

@ -18,6 +18,7 @@ use git::{
use git2::RepositoryInitOptions; use git2::RepositoryInitOptions;
use gpui::{App, BackgroundExecutor, SemanticVersion, UpdateGlobal}; use gpui::{App, BackgroundExecutor, SemanticVersion, UpdateGlobal};
use http_client::Url; use http_client::Url;
use itertools::Itertools;
use language::{ use language::{
Diagnostic, DiagnosticEntry, DiagnosticSet, DiskState, FakeLspAdapter, LanguageConfig, Diagnostic, DiagnosticEntry, DiagnosticSet, DiskState, FakeLspAdapter, LanguageConfig,
LanguageMatcher, LanguageName, LineEnding, OffsetRangeExt, Point, ToPoint, LanguageMatcher, LanguageName, LineEnding, OffsetRangeExt, Point, ToPoint,

View file

@ -471,11 +471,19 @@ impl<'a> FromIterator<&'a str> for Rope {
} }
impl From<String> for Rope { impl From<String> for Rope {
#[inline(always)]
fn from(text: String) -> Self { fn from(text: String) -> Self {
Rope::from(text.as_str()) Rope::from(text.as_str())
} }
} }
impl From<&String> for Rope {
#[inline(always)]
fn from(text: &String) -> Self {
Rope::from(text.as_str())
}
}
impl fmt::Display for Rope { impl fmt::Display for Rope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for chunk in self.chunks() { for chunk in self.chunks() {

View file

@ -713,7 +713,7 @@ impl Buffer {
let mut base_text = base_text.into(); let mut base_text = base_text.into();
let line_ending = LineEnding::detect(&base_text); let line_ending = LineEnding::detect(&base_text);
LineEnding::normalize(&mut base_text); LineEnding::normalize(&mut base_text);
Self::new_normalized(replica_id, remote_id, line_ending, Rope::from(base_text)) Self::new_normalized(replica_id, remote_id, line_ending, Rope::from(&*base_text))
} }
pub fn new_normalized( pub fn new_normalized(