From 4dbd24d75f61345996eb3ad0d92b46fb854a3c4c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:24:29 +0200 Subject: [PATCH] Reduce amount of allocations in RustLsp label handling (#35786) There can be a lot of completions after all Release Notes: - N/A --- crates/diagnostics/src/diagnostics_tests.rs | 2 +- crates/editor/src/display_map/inlay_map.rs | 16 +++--- crates/editor/src/inlay_hint_cache.rs | 2 +- crates/editor/src/signature_help.rs | 2 +- crates/languages/src/rust.rs | 63 +++++---------------- crates/project/src/project.rs | 9 ++- crates/project/src/project_tests.rs | 1 + crates/rope/src/rope.rs | 8 +++ crates/text/src/text.rs | 2 +- 9 files changed, 40 insertions(+), 65 deletions(-) diff --git a/crates/diagnostics/src/diagnostics_tests.rs b/crates/diagnostics/src/diagnostics_tests.rs index 1bb84488e8..8fb223b2cb 100644 --- a/crates/diagnostics/src/diagnostics_tests.rs +++ b/crates/diagnostics/src/diagnostics_tests.rs @@ -876,7 +876,7 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S vec![Inlay::edit_prediction( post_inc(&mut next_inlay_id), snapshot.buffer_snapshot.anchor_before(position), - format!("Test inlay {next_inlay_id}"), + Rope::from_iter(["Test inlay ", "next_inlay_id"]), )], cx, ); diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index fd49c262c6..b296b3e62a 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -48,16 +48,16 @@ pub struct Inlay { impl Inlay { pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self { let mut text = hint.text(); - if hint.padding_right && !text.ends_with(' ') { - text.push(' '); + if hint.padding_right && text.chars_at(text.len().saturating_sub(1)).next() != Some(' ') { + text.push(" "); } - if hint.padding_left && !text.starts_with(' ') { - text.insert(0, ' '); + if hint.padding_left && text.chars_at(0).next() != Some(' ') { + text.push_front(" "); } Self { id: InlayId::Hint(id), position, - text: text.into(), + text, color: None, } } @@ -737,13 +737,13 @@ impl InlayMap { Inlay::mock_hint( post_inc(next_inlay_id), snapshot.buffer.anchor_at(position, bias), - text.clone(), + &text, ) } else { Inlay::edit_prediction( post_inc(next_inlay_id), snapshot.buffer.anchor_at(position, bias), - text.clone(), + &text, ) }; let inlay_id = next_inlay.id; @@ -1694,7 +1694,7 @@ mod tests { (offset, inlay.clone()) }) .collect::>(); - 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() { expected_text.replace(*offset..*offset, &inlay.text.to_string()); } diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index db01cc7ad1..60ad0e5bf6 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -3546,7 +3546,7 @@ pub mod tests { let excerpt_hints = excerpt_hints.read(); for id in &excerpt_hints.ordered_hints { let hint = &excerpt_hints.hints_by_id[id]; - let mut label = hint.text(); + let mut label = hint.text().to_string(); if hint.padding_left { label.insert(0, ' '); } diff --git a/crates/editor/src/signature_help.rs b/crates/editor/src/signature_help.rs index 3447e66ccd..e9f8d2dbd3 100644 --- a/crates/editor/src/signature_help.rs +++ b/crates/editor/src/signature_help.rs @@ -191,7 +191,7 @@ impl Editor { if let Some(language) = language { 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 .highlight_text(&text, 0..signature.label.len()) .into_iter() diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 7fb6f44a5b..b6567c6e33 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -341,7 +341,7 @@ impl LspAdapter for RustLspAdapter { let name = &completion.label; let text = format!("{name}: {signature}"); let prefix = "struct S { "; - let source = Rope::from(format!("{prefix}{text} }}")); + let source = Rope::from_iter([prefix, &text, " }"]); let runs = language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); mk_label(text, runs) @@ -353,7 +353,7 @@ impl LspAdapter for RustLspAdapter { let name = &completion.label; let text = format!("{name}: {signature}",); let prefix = "let "; - let source = Rope::from(format!("{prefix}{text} = ();")); + let source = Rope::from_iter([prefix, &text, " = ();"]); let runs = language.highlight_text(&source, prefix.len()..prefix.len() + text.len()); mk_label(text, runs) @@ -387,7 +387,7 @@ impl LspAdapter for RustLspAdapter { &completion.label }; 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 runs = language.highlight_text(&source, run_start..run_start + text.len()); mk_label(text, runs) @@ -450,55 +450,22 @@ impl LspAdapter for RustLspAdapter { kind: lsp::SymbolKind, language: &Arc, ) -> Option { - let (text, filter_range, display_range) = match kind { - lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => { - let text = format!("fn {} () {{}}", name); - let filter_range = 3..3 + name.len(); - let display_range = 0..filter_range.end; - (text, filter_range, display_range) - } - lsp::SymbolKind::STRUCT => { - 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) - } + let (prefix, suffix) = match kind { + lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => ("fn ", " () {}"), + lsp::SymbolKind::STRUCT => ("struct ", " {}"), + lsp::SymbolKind::ENUM => ("enum ", " {}"), + lsp::SymbolKind::INTERFACE => ("trait ", " {}"), + lsp::SymbolKind::CONSTANT => ("const ", ": () = ();"), + lsp::SymbolKind::MODULE => ("mod ", " {}"), + lsp::SymbolKind::TYPE_PARAMETER => ("type ", " {}"), _ => return None, }; + let filter_range = prefix.len()..prefix.len() + name.len(); + let display_range = 0..filter_range.end; Some(CodeLabel { - runs: language.highlight_text(&text.as_str().into(), display_range.clone()), - text: text[display_range].to_string(), + runs: language.highlight_text(&Rope::from_iter([prefix, name, suffix]), display_range), + text: format!("{prefix}{name}"), filter_range, }) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index cca026ec87..614d514cd4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -73,7 +73,6 @@ use gpui::{ App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla, SharedString, Task, WeakEntity, Window, }; -use itertools::Itertools; use language::{ Buffer, BufferEvent, Capability, CodeLabel, CursorShape, DiagnosticSourceKind, Language, LanguageName, LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, Toolchain, ToolchainList, @@ -113,7 +112,7 @@ use std::{ use task_store::TaskStore; use terminals::Terminals; -use text::{Anchor, BufferId, OffsetRangeExt, Point}; +use text::{Anchor, BufferId, OffsetRangeExt, Point, Rope}; use toolchain_store::EmptyToolchainStore; use util::{ ResultExt as _, @@ -668,10 +667,10 @@ pub enum ResolveState { } impl InlayHint { - pub fn text(&self) -> String { + pub fn text(&self) -> Rope { match &self.label { - InlayHintLabel::String(s) => s.to_owned(), - InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""), + InlayHintLabel::String(s) => Rope::from(s), + InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &*part.value).collect(), } } } diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 75ebc8339a..9c6d9ec979 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -18,6 +18,7 @@ use git::{ use git2::RepositoryInitOptions; use gpui::{App, BackgroundExecutor, SemanticVersion, UpdateGlobal}; use http_client::Url; +use itertools::Itertools; use language::{ Diagnostic, DiagnosticEntry, DiagnosticSet, DiskState, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageName, LineEnding, OffsetRangeExt, Point, ToPoint, diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index aa3ed5db57..d8ed3bfac8 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -471,11 +471,19 @@ impl<'a> FromIterator<&'a str> for Rope { } impl From for Rope { + #[inline(always)] fn from(text: String) -> Self { 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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for chunk in self.chunks() { diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 68c7b2a2cd..9f7e49d24d 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -713,7 +713,7 @@ impl Buffer { let mut base_text = base_text.into(); let line_ending = LineEnding::detect(&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(