Rework go to line infrastructure (#23654)

Closes https://github.com/zed-industries/zed/issues/12024


https://github.com/user-attachments/assets/60ea3dbd-b594-4bf5-a44d-4bff925b815f

* Fixes incorrect line selection for certain corner cases

Before:

<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/35aaee6c-c120-4bf1-9355-448a29d1b9b5"
/>

After:

<img width="1728" alt="image"
src="https://github.com/user-attachments/assets/abd97339-4594-4e8e-8605-50d74581ae86"
/>


* Reworks https://github.com/zed-industries/zed/pull/16420 to display
selection length with less performance overhead.
Improves the performance more, doing a single selections loop instead of
two.

* Fixes incorrect caret position display when text contains UTF-8 chars
with size > 1
Also fixes tooltop values for this case

* Fixes go to line to treat UTF-8 chars with size > 1 properly when
navigating

* Adds a way to fill go to line text editor with its tooltip on `Tab`

Release Notes:

- Fixed incorrect UTF-8 characters handling in `GoToLine` and caret
position
This commit is contained in:
Kirill Bulatov 2025-01-25 21:24:19 +02:00 committed by GitHub
parent 7c0a39daa6
commit da2bd4b8e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 365 additions and 69 deletions

View file

@ -162,9 +162,11 @@ impl<'a> ChunkSlice<'a> {
#[inline(always)]
pub fn text_summary(&self) -> TextSummary {
let (longest_row, longest_row_chars) = self.longest_row();
let mut chars = 0;
let (longest_row, longest_row_chars) = self.longest_row(&mut chars);
TextSummary {
len: self.len(),
chars,
len_utf16: self.len_utf16(),
lines: self.lines(),
first_line_chars: self.first_line_chars(),
@ -229,16 +231,19 @@ impl<'a> ChunkSlice<'a> {
}
/// Get the longest row in the chunk and its length in characters.
/// Calculate the total number of characters in the chunk along the way.
#[inline(always)]
pub fn longest_row(&self) -> (u32, u32) {
pub fn longest_row(&self, total_chars: &mut usize) -> (u32, u32) {
let mut chars = self.chars;
let mut newlines = self.newlines;
*total_chars = 0;
let mut row = 0;
let mut longest_row = 0;
let mut longest_row_chars = 0;
while newlines > 0 {
let newline_ix = newlines.trailing_zeros();
let row_chars = (chars & ((1 << newline_ix) - 1)).count_ones() as u8;
*total_chars += usize::from(row_chars);
if row_chars > longest_row_chars {
longest_row = row;
longest_row_chars = row_chars;
@ -249,9 +254,11 @@ impl<'a> ChunkSlice<'a> {
chars >>= newline_ix;
chars >>= 1;
row += 1;
*total_chars += 1;
}
let row_chars = chars.count_ones() as u8;
*total_chars += usize::from(row_chars);
if row_chars > longest_row_chars {
(row, row_chars as u32)
} else {
@ -908,7 +915,7 @@ mod tests {
}
// Verify longest row
let (longest_row, longest_chars) = chunk.longest_row();
let (longest_row, longest_chars) = chunk.longest_row(&mut 0);
let mut max_chars = 0;
let mut current_row = 0;
let mut current_chars = 0;

View file

@ -965,8 +965,10 @@ impl sum_tree::Summary for ChunkSummary {
/// Summary of a string of text.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct TextSummary {
/// Length in UTF-8
/// Length in bytes.
pub len: usize,
/// Length in UTF-8.
pub chars: usize,
/// Length in UTF-16 code units
pub len_utf16: OffsetUtf16,
/// A point representing the number of lines and the length of the last line
@ -994,6 +996,7 @@ impl TextSummary {
pub fn newline() -> Self {
Self {
len: 1,
chars: 1,
len_utf16: OffsetUtf16(1),
first_line_chars: 0,
last_line_chars: 0,
@ -1022,7 +1025,9 @@ impl<'a> From<&'a str> for TextSummary {
let mut last_line_len_utf16 = 0;
let mut longest_row = 0;
let mut longest_row_chars = 0;
let mut chars = 0;
for c in text.chars() {
chars += 1;
len_utf16.0 += c.len_utf16();
if c == '\n' {
@ -1047,6 +1052,7 @@ impl<'a> From<&'a str> for TextSummary {
TextSummary {
len: text.len(),
chars,
len_utf16,
lines,
first_line_chars,
@ -1103,6 +1109,7 @@ impl<'a> ops::AddAssign<&'a Self> for TextSummary {
self.last_line_len_utf16 = other.last_line_len_utf16;
}
self.chars += other.chars;
self.len += other.len;
self.len_utf16 += other.len_utf16;
self.lines += other.lines;