From 59f05088b9628086b35631338e49ae8f061dcba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 17 Jan 2022 16:28:56 +0900 Subject: [PATCH] Optimize rendering by using Ropey::byte_slice This avoids costly conversions via byte_to_char (which are then reversed back into bytes internally in Ropey). Reduces time spent in slice/byte_to_char from ~24% to ~5%. --- Cargo.lock | 10 ++++++++-- helix-core/Cargo.toml | 3 ++- helix-core/src/graphemes.rs | 5 +---- helix-core/src/syntax.rs | 8 ++------ helix-term/src/ui/editor.rs | 7 +++---- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80645e1ea..060f93e88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -862,10 +862,10 @@ checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" [[package]] name = "ropey" version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b9aa65bcd9f308d37c7158b4a1afaaa32b8450213e20c9b98e7d5b3cc2fec3" +source = "git+https://github.com/cessen/ropey#38531e083f8cf5c5b1aeb328361895fb5a9b867c" dependencies = [ "smallvec", + "str_indices", ] [[package]] @@ -1031,6 +1031,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" +[[package]] +name = "str_indices" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283baa48c486e4c5e27b4d92c435db9eaceac236a74dab5e3293570e2c3fa4aa" + [[package]] name = "syn" version = "1.0.88" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 8152da574..3e5aabaaa 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -15,7 +15,8 @@ include = ["src/**/*", "README.md"] [dependencies] helix-loader = { version = "0.6", path = "../helix-loader" } -ropey = "1.3" +# ropey = "1.3" +ropey = { git = "https://github.com/cessen/ropey" } smallvec = "1.8" smartstring = "1.0.0" unicode-segmentation = "1.9" diff --git a/helix-core/src/graphemes.rs b/helix-core/src/graphemes.rs index aa8986844..c0c617750 100644 --- a/helix-core/src/graphemes.rs +++ b/helix-core/src/graphemes.rs @@ -333,10 +333,7 @@ impl<'a> Iterator for RopeGraphemes<'a> { } if a < self.cur_chunk_start { - let a_char = self.text.byte_to_char(a); - let b_char = self.text.byte_to_char(b); - - Some(self.text.slice(a_char..b_char)) + Some(self.text.byte_slice(a..b)) } else { let a2 = a - self.cur_chunk_start; let b2 = b - self.cur_chunk_start; diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 3fc91efc8..f76683b94 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -576,9 +576,7 @@ pub struct Syntax { } fn byte_range_to_str(range: std::ops::Range, source: RopeSlice) -> Cow { - let start_char = source.byte_to_char(range.start); - let end_char = source.byte_to_char(range.end); - Cow::from(source.slice(start_char..end_char)) + Cow::from(source.byte_slice(range)) } impl Syntax { @@ -1197,9 +1195,7 @@ impl<'a> TextProvider<'a> for RopeProvider<'a> { type I = ChunksBytes<'a>; fn text(&mut self, node: Node) -> Self::I { - let start_char = self.0.byte_to_char(node.start_byte()); - let end_char = self.0.byte_to_char(node.end_byte()); - let fragment = self.0.slice(start_char..end_char); + let fragment = self.0.byte_slice(node.start_byte()..node.end_byte()); ChunksBytes { chunks: fragment.chunks(), } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 31a9bfc86..2b9de5d62 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -183,6 +183,7 @@ impl EditorView { .highlight_iter(text.slice(..), Some(range), None) .map(|event| event.unwrap()) .map(move |event| match event { + // TODO: use byte slices directly // convert byte offsets to char offset HighlightEvent::Source { start, end } => { let start = @@ -317,6 +318,8 @@ impl EditorView { theme: &Theme, highlights: H, ) { + // It's slightly more efficient to produce a full RopeSlice from the Rope, then slice that a bunch + // of times than it is to always call Rope::slice/get_slice (it will internally always hit RSEnum::Light). let text = doc.text().slice(..); let mut spans = Vec::new(); @@ -327,10 +330,6 @@ impl EditorView { let text_style = theme.get("ui.text"); - // It's slightly more efficient to produce a full RopeSlice from the Rope, then slice that a bunch - // of times than it is to always call Rope::slice/get_slice (it will internally always hit RSEnum::Light). - let text = text.slice(..); - 'outer: for event in highlights { match event { HighlightEvent::HighlightStart(span) => {