editor: Use unbounded shifts for chunk bitmaps (#40879)

This simplifies some code and is also more correct in some others (I
believe some of these might've overflowed causing panics in sentry)

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-10-22 11:05:32 +00:00
committed by GitHub
parent bd69124f2b
commit c81ffaffb6
12 changed files with 169 additions and 227 deletions
+4 -4
View File
@@ -26,8 +26,8 @@ use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap};
use text::{BufferId, Edit};
use ui::ElementId;
const NEWLINES: &[u8; u128::BITS as usize] = &[b'\n'; _];
const BULLETS: &[u8; u128::BITS as usize] = &[b'*'; _];
const NEWLINES: &[u8; rope::Chunk::MASK_BITS] = &[b'\n'; _];
const BULLETS: &[u8; rope::Chunk::MASK_BITS] = &[b'*'; _];
/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
///
@@ -1783,11 +1783,11 @@ impl<'a> Iterator for BlockChunks<'a> {
if self.masked {
// Not great for multibyte text because to keep cursor math correct we
// need to have the same number of bytes in the input as output.
// need to have the same number of chars in the input as output.
let chars_count = prefix.chars().count();
let bullet_len = chars_count;
prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
chars = 1u128.unbounded_shl(bullet_len as u32) - 1;
chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
tabs = 0;
}
@@ -132,37 +132,31 @@ impl<'a> Iterator for CustomHighlightsChunks<'a> {
}
}
let chunk = self
.buffer_chunk
.get_or_insert_with(|| self.buffer_chunks.next().unwrap_or_default());
if chunk.text.is_empty() {
let chunk = match &mut self.buffer_chunk {
Some(it) => it,
slot => slot.insert(self.buffer_chunks.next()?),
};
while chunk.text.is_empty() {
*chunk = self.buffer_chunks.next()?;
}
let split_idx = chunk.text.len().min(next_highlight_endpoint - self.offset);
let (prefix, suffix) = chunk.text.split_at(split_idx);
let (chars, tabs) = if split_idx == 128 {
let output = (chunk.chars, chunk.tabs);
chunk.chars = 0;
chunk.tabs = 0;
output
} else {
let mask = (1 << split_idx) - 1;
let output = (chunk.chars & mask, chunk.tabs & mask);
chunk.chars = chunk.chars >> split_idx;
chunk.tabs = chunk.tabs >> split_idx;
output
};
chunk.text = suffix;
self.offset += prefix.len();
let mask = 1u128.unbounded_shl(split_idx as u32).wrapping_sub(1);
let chars = chunk.chars & mask;
let tabs = chunk.tabs & mask;
let mut prefix = Chunk {
text: prefix,
chars,
tabs,
..chunk.clone()
};
chunk.chars = chunk.chars.unbounded_shr(split_idx as u32);
chunk.tabs = chunk.tabs.unbounded_shr(split_idx as u32);
chunk.text = suffix;
if !self.active_highlights.is_empty() {
prefix.highlight_style = self
.active_highlights
+5 -4
View File
@@ -1436,14 +1436,15 @@ impl<'a> Iterator for FoldChunks<'a> {
let transform_end = self.transform_cursor.end().1;
let chunk_end = buffer_chunk_end.min(transform_end);
chunk.text = &chunk.text
[(self.inlay_offset - buffer_chunk_start).0..(chunk_end - buffer_chunk_start).0];
let bit_start = (self.inlay_offset - buffer_chunk_start).0;
let bit_end = (chunk_end - buffer_chunk_start).0;
chunk.text = &chunk.text[bit_start..bit_end];
let bit_end = (chunk_end - buffer_chunk_start).0;
let mask = 1u128.unbounded_shl(bit_end as u32).wrapping_sub(1);
chunk.tabs = (chunk.tabs >> (self.inlay_offset - buffer_chunk_start).0) & mask;
chunk.chars = (chunk.chars >> (self.inlay_offset - buffer_chunk_start).0) & mask;
chunk.tabs = (chunk.tabs >> bit_start) & mask;
chunk.chars = (chunk.chars >> bit_start) & mask;
if chunk_end == transform_end {
self.transform_cursor.next();
+17 -28
View File
@@ -325,21 +325,16 @@ impl<'a> Iterator for InlayChunks<'a> {
};
let (prefix, suffix) = chunk.text.split_at(split_index);
let (chars, tabs) = if split_index == 128 {
let output = (chunk.chars, chunk.tabs);
chunk.chars = 0;
chunk.tabs = 0;
output
} else {
let mask = (1 << split_index) - 1;
let output = (chunk.chars & mask, chunk.tabs & mask);
chunk.chars = chunk.chars >> split_index;
chunk.tabs = chunk.tabs >> split_index;
output
};
chunk.text = suffix;
self.output_offset.0 += prefix.len();
let mask = 1u128.unbounded_shl(split_index as u32).wrapping_sub(1);
let chars = chunk.chars & mask;
let tabs = chunk.tabs & mask;
chunk.chars = chunk.chars.unbounded_shr(split_index as u32);
chunk.tabs = chunk.tabs.unbounded_shr(split_index as u32);
chunk.text = suffix;
InlayChunk {
chunk: Chunk {
text: prefix,
@@ -457,18 +452,12 @@ impl<'a> Iterator for InlayChunks<'a> {
let (chunk, remainder) = inlay_chunk.split_at(split_index);
*inlay_chunk = remainder;
let (chars, tabs) = if split_index == 128 {
let output = (*chars, *tabs);
*chars = 0;
*tabs = 0;
output
} else {
let mask = (1 << split_index as u32) - 1;
let output = (*chars & mask, *tabs & mask);
*chars = *chars >> split_index;
*tabs = *tabs >> split_index;
output
};
let mask = 1u128.unbounded_shl(split_index as u32).wrapping_sub(1);
let new_chars = *chars & mask;
let new_tabs = *tabs & mask;
*chars = chars.unbounded_shr(split_index as u32);
*tabs = tabs.unbounded_shr(split_index as u32);
if inlay_chunk.is_empty() {
self.inlay_chunk = None;
@@ -479,8 +468,8 @@ impl<'a> Iterator for InlayChunks<'a> {
InlayChunk {
chunk: Chunk {
text: chunk,
chars,
tabs,
chars: new_chars,
tabs: new_tabs,
highlight_style,
is_inlay: true,
..Chunk::default()
+40 -49
View File
@@ -11,7 +11,7 @@ use sum_tree::Bias;
const MAX_EXPANSION_COLUMN: u32 = 256;
// Handles a tab width <= 128
const SPACES: &[u8; u128::BITS as usize] = &[b' '; _];
const SPACES: &[u8; rope::Chunk::MASK_BITS] = &[b' '; _];
const MAX_TABS: NonZeroU32 = NonZeroU32::new(SPACES.len() as u32).unwrap();
/// Keeps track of hard tabs in a text buffer.
@@ -569,56 +569,47 @@ impl<'a> Iterator for TabChunks<'a> {
//todo(improve performance by using tab cursor)
for (ix, c) in self.chunk.text.char_indices() {
match c {
'\t' if ix > 0 => {
let (prefix, suffix) = self.chunk.text.split_at(ix);
let mask = 1u128.unbounded_shl(ix as u32).wrapping_sub(1);
let chars = self.chunk.chars & mask;
let tabs = self.chunk.tabs & mask;
self.chunk.tabs = self.chunk.tabs.unbounded_shr(ix as u32);
self.chunk.chars = self.chunk.chars.unbounded_shr(ix as u32);
self.chunk.text = suffix;
return Some(Chunk {
text: prefix,
chars,
tabs,
..self.chunk.clone()
});
}
'\t' => {
if ix > 0 {
let (prefix, suffix) = self.chunk.text.split_at(ix);
let (chars, tabs) = if ix == 128 {
let output = (self.chunk.chars, self.chunk.tabs);
self.chunk.chars = 0;
self.chunk.tabs = 0;
output
} else {
let mask = (1 << ix) - 1;
let output = (self.chunk.chars & mask, self.chunk.tabs & mask);
self.chunk.chars = self.chunk.chars >> ix;
self.chunk.tabs = self.chunk.tabs >> ix;
output
};
self.chunk.text = suffix;
return Some(Chunk {
text: prefix,
chars,
tabs,
..self.chunk.clone()
});
self.chunk.text = &self.chunk.text[1..];
self.chunk.tabs >>= 1;
self.chunk.chars >>= 1;
let tab_size = if self.input_column < self.max_expansion_column {
self.tab_size.get()
} else {
self.chunk.text = &self.chunk.text[1..];
self.chunk.tabs >>= 1;
self.chunk.chars >>= 1;
let tab_size = if self.input_column < self.max_expansion_column {
self.tab_size.get()
} else {
1
};
let mut len = tab_size - self.column % tab_size;
let next_output_position = cmp::min(
self.output_position + Point::new(0, len),
self.max_output_position,
);
len = next_output_position.column - self.output_position.column;
self.column += len;
self.input_column += 1;
self.output_position = next_output_position;
return Some(Chunk {
text: unsafe { std::str::from_utf8_unchecked(&SPACES[..len as usize]) },
is_tab: true,
chars: 1u128.unbounded_shl(len) - 1,
tabs: 0,
..self.chunk.clone()
});
}
1
};
let mut len = tab_size - self.column % tab_size;
let next_output_position = cmp::min(
self.output_position + Point::new(0, len),
self.max_output_position,
);
len = next_output_position.column - self.output_position.column;
self.column += len;
self.input_column += 1;
self.output_position = next_output_position;
return Some(Chunk {
text: unsafe { std::str::from_utf8_unchecked(&SPACES[..len as usize]) },
is_tab: true,
chars: 1u128.unbounded_shl(len) - 1,
tabs: 0,
..self.chunk.clone()
});
}
'\n' => {
self.column = 0;
+5 -12
View File
@@ -972,18 +972,11 @@ impl<'a> Iterator for WrapChunks<'a> {
let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
let (chars, tabs) = if input_len == 128 {
let output = (self.input_chunk.chars, self.input_chunk.tabs);
self.input_chunk.chars = 0;
self.input_chunk.tabs = 0;
output
} else {
let mask = (1 << input_len) - 1;
let output = (self.input_chunk.chars & mask, self.input_chunk.tabs & mask);
self.input_chunk.chars = self.input_chunk.chars >> input_len;
self.input_chunk.tabs = self.input_chunk.tabs >> input_len;
output
};
let mask = 1u128.unbounded_shl(input_len as u32).wrapping_sub(1);
let chars = self.input_chunk.chars & mask;
let tabs = self.input_chunk.tabs & mask;
self.input_chunk.tabs = self.input_chunk.tabs.unbounded_shr(input_len as u32);
self.input_chunk.chars = self.input_chunk.chars.unbounded_shr(input_len as u32);
self.input_chunk.text = suffix;
Some(Chunk {