notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 352ace0ec1352981d669baee0f211c2f4bdf3839
parent 23b2817320e75791ace9e7aa5d0d6900fbb7c932
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 13 Feb 2026 13:31:10 -0800

egui-md-stream: fix O(n²) collapse_text_elements

Replace Vec::remove loop with single-pass read/write pointer
approach. Vec::remove shifts all subsequent elements on each
call, making it quadratic. The new approach swaps in place and
truncates once at the end.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mcrates/egui-md-stream/src/inline.rs | 21+++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)

diff --git a/crates/egui-md-stream/src/inline.rs b/crates/egui-md-stream/src/inline.rs @@ -371,16 +371,25 @@ fn parse_link(text: &str) -> Option<(String, String, usize)> { /// Collapse adjacent Text elements into one. fn collapse_text_elements(elements: &mut Vec<InlineElement>) { - let mut i = 0; - while i + 1 < elements.len() { - if let (InlineElement::Text(a), InlineElement::Text(b)) = (&elements[i], &elements[i + 1]) { + if elements.len() < 2 { + return; + } + + let mut write = 0; + for read in 1..elements.len() { + if let (InlineElement::Text(a), InlineElement::Text(b)) = + (&elements[write], &elements[read]) + { let combined = format!("{}{}", a, b); - elements[i] = InlineElement::Text(combined); - elements.remove(i + 1); + elements[write] = InlineElement::Text(combined); } else { - i += 1; + write += 1; + if write != read { + elements.swap(write, read); + } } } + elements.truncate(write + 1); } /// Streaming inline parser state.