editor: Improve range_to_utf16 and range_from_utf16 by use Rope. (#1268)

Now, we can work on 200K lines (editing).

--------

Now, all cost are on tree-sitter parse.

<img width="1091" height="672" alt="image"
src="https://github.com/user-attachments/assets/b111bc58-1334-40b2-9920-7006ad86bb3c"
/>
This commit is contained in:
Jason Lee 2025-09-22 14:07:30 +08:00 committed by GitHub
parent 692057f799
commit aa0f6d6222
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 14 additions and 31 deletions

View file

@ -15,7 +15,8 @@ UI components for building fantastic desktop applications using [GPUI](https://g
- **High Performance**: Virtualized Table and List components for smooth large-data rendering. - **High Performance**: Virtualized Table and List components for smooth large-data rendering.
- **Content Rendering**: Native support for Markdown and simple HTML. - **Content Rendering**: Native support for Markdown and simple HTML.
- **Charting**: Built-in charts for visualization your data. - **Charting**: Built-in charts for visualization your data.
- **Code Highlighting**: Code Editor and Syntax highlighting. - **Editor**: High performance code editor (support up to 200K lines) with LSP (diagnostics, completion, hover, etc).
- **Syntax Highlighting**: Syntax highlighting for editor and markdown components using Tree Sitter.
- **Wef**: (Experimental) Offscreen rendering webview based on [CEF](https://github.com/chromiumembedded/cef). - **Wef**: (Experimental) Offscreen rendering webview based on [CEF](https://github.com/chromiumembedded/cef).
## Showcase ## Showcase
@ -87,7 +88,7 @@ Check out [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
| Syntax Highlight | [Tree Sitter] | [Syntect] | [Syntect] | [QSyntaxHighlighter] | | Syntax Highlight | [Tree Sitter] | [Syntect] | [Syntect] | [QSyntaxHighlighter] |
| Markdown Rendering | Yes | Yes | Basic | No | | Markdown Rendering | Yes | Yes | Basic | No |
| Markdown mix HTML | Yes | No | No | No | | Markdown mix HTML | Yes | No | No | No |
| HTML Rendering | Basic | No | No | Basic | | HTML Rendering | Basic | No | No | Basic |
| Text Selection | TextView | No | Any Label | No | | Text Selection | TextView | No | Any Label | No |
| Themes | Yes | No | No | No | | Themes | Yes | No | No | No |
| I18n | Yes | Yes | Yes | Yes | | I18n | Yes | Yes | Yes | Yes |

View file

@ -200,12 +200,12 @@ impl CompletionProvider for ExampleLspStore {
return Task::ready(Ok(vec![])); return Task::ready(Ok(vec![]));
} }
let _ = rope.to_string(); // Just to use the rope parameter.
let pos = rope.offset_to_position(offset);
// Simulate to delay for fetching completions // Simulate to delay for fetching completions
let rope = rope.clone();
let items = self.completions.clone(); let items = self.completions.clone();
cx.background_spawn(async move { cx.background_spawn(async move {
let pos = rope.offset_to_position(offset);
// Simulate a slow completion source, to test Editor async handling. // Simulate a slow completion source, to test Editor async handling.
smol::Timer::after(Duration::from_millis(20)).await; smol::Timer::after(Duration::from_millis(20)).await;
@ -940,7 +940,7 @@ fn main() {
cx.activate(true); cx.activate(true);
story::create_new_window_with_size( story::create_new_window_with_size(
"Code Editor", "Editor",
Some(size(px(1200.), px(960.))), Some(size(px(1200.), px(960.))),
|window, cx| cx.new(|cx| Example::new(name, window, cx)), |window, cx| cx.new(|cx| Example::new(name, window, cx)),
cx, cx,

View file

@ -11,7 +11,7 @@ use gpui::{
ScrollWheelEvent, SharedString, Styled as _, Subscription, Task, UTF16Selection, Window, ScrollWheelEvent, SharedString, Styled as _, Subscription, Task, UTF16Selection, Window,
WrappedLine, WrappedLine,
}; };
use rope::Rope; use rope::{OffsetUtf16, Rope};
use serde::Deserialize; use serde::Deserialize;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cell::RefCell; use std::cell::RefCell;
@ -1968,40 +1968,22 @@ impl InputState {
cx.notify() cx.notify()
} }
#[inline]
pub(super) fn offset_from_utf16(&self, offset: usize) -> usize { pub(super) fn offset_from_utf16(&self, offset: usize) -> usize {
let mut utf8_offset = 0; self.text.offset_utf16_to_offset(OffsetUtf16(offset))
let mut utf16_count = 0;
for ch in self.text.chars() {
if utf16_count >= offset {
break;
}
utf16_count += ch.len_utf16();
utf8_offset += ch.len_utf8();
}
utf8_offset
} }
#[inline]
pub(super) fn offset_to_utf16(&self, offset: usize) -> usize { pub(super) fn offset_to_utf16(&self, offset: usize) -> usize {
let mut utf16_offset = 0; self.text.offset_to_offset_utf16(offset).0
let mut utf8_count = 0;
for ch in self.text.chars() {
if utf8_count >= offset {
break;
}
utf8_count += ch.len_utf8();
utf16_offset += ch.len_utf16();
}
utf16_offset
} }
#[inline]
pub(super) fn range_to_utf16(&self, range: &Range<usize>) -> Range<usize> { pub(super) fn range_to_utf16(&self, range: &Range<usize>) -> Range<usize> {
self.offset_to_utf16(range.start)..self.offset_to_utf16(range.end) self.offset_to_utf16(range.start)..self.offset_to_utf16(range.end)
} }
#[inline]
pub(super) fn range_from_utf16(&self, range_utf16: &Range<usize>) -> Range<usize> { pub(super) fn range_from_utf16(&self, range_utf16: &Range<usize>) -> Range<usize> {
self.offset_from_utf16(range_utf16.start)..self.offset_from_utf16(range_utf16.end) self.offset_from_utf16(range_utf16.start)..self.offset_from_utf16(range_utf16.end)
} }