editor: Shrink size of Inlay slightly (#39089)

And some other smaller cleanup things I noticed while reading through
some stuff

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Lukas Wirth
2025-09-29 15:33:21 +02:00
committed by GitHub
parent ed7217ff46
commit f2efe78feb
10 changed files with 119 additions and 122 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ pub enum MessageEditorEvent {
impl EventEmitter<MessageEditorEvent> for MessageEditor {}
const COMMAND_HINT_INLAY_ID: usize = 0;
const COMMAND_HINT_INLAY_ID: u32 = 0;
impl MessageEditor {
pub fn new(
+1 -5
View File
@@ -1443,11 +1443,7 @@ impl<'a> Iterator for FoldChunks<'a> {
[(self.inlay_offset - buffer_chunk_start).0..(chunk_end - buffer_chunk_start).0];
let bit_end = (chunk_end - buffer_chunk_start).0;
let mask = if bit_end >= 128 {
u128::MAX
} else {
(1u128 << bit_end) - 1
};
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;
+50 -41
View File
@@ -8,7 +8,7 @@ use multi_buffer::{
use std::{
cmp,
ops::{Add, AddAssign, Range, Sub, SubAssign},
sync::Arc,
sync::{Arc, OnceLock},
};
use sum_tree::{Bias, Cursor, Dimensions, SumTree};
use text::{ChunkBitmaps, Patch, Rope};
@@ -41,12 +41,17 @@ enum Transform {
pub struct Inlay {
pub id: InlayId,
pub position: Anchor,
pub text: text::Rope,
color: Option<Hsla>,
pub content: InlayContent,
}
#[derive(Debug, Clone)]
pub enum InlayContent {
Text(text::Rope),
Color(Hsla),
}
impl Inlay {
pub fn hint(id: usize, position: Anchor, hint: &project::InlayHint) -> Self {
pub fn hint(id: u32, position: Anchor, hint: &project::InlayHint) -> Self {
let mut text = hint.text();
if hint.padding_right && text.reversed_chars_at(text.len()).next() != Some(' ') {
text.push(" ");
@@ -57,51 +62,57 @@ impl Inlay {
Self {
id: InlayId::Hint(id),
position,
text,
color: None,
content: InlayContent::Text(text),
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn mock_hint(id: usize, position: Anchor, text: impl Into<Rope>) -> Self {
pub fn mock_hint(id: u32, position: Anchor, text: impl Into<Rope>) -> Self {
Self {
id: InlayId::Hint(id),
position,
text: text.into(),
color: None,
content: InlayContent::Text(text.into()),
}
}
pub fn color(id: usize, position: Anchor, color: Rgba) -> Self {
pub fn color(id: u32, position: Anchor, color: Rgba) -> Self {
Self {
id: InlayId::Color(id),
position,
text: Rope::from(""),
color: Some(Hsla::from(color)),
content: InlayContent::Color(color.into()),
}
}
pub fn edit_prediction<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
pub fn edit_prediction<T: Into<Rope>>(id: u32, position: Anchor, text: T) -> Self {
Self {
id: InlayId::EditPrediction(id),
position,
text: text.into(),
color: None,
content: InlayContent::Text(text.into()),
}
}
pub fn debugger<T: Into<Rope>>(id: usize, position: Anchor, text: T) -> Self {
pub fn debugger<T: Into<Rope>>(id: u32, position: Anchor, text: T) -> Self {
Self {
id: InlayId::DebuggerValue(id),
position,
text: text.into(),
color: None,
content: InlayContent::Text(text.into()),
}
}
pub fn text(&self) -> &Rope {
static COLOR_TEXT: OnceLock<Rope> = OnceLock::new();
match &self.content {
InlayContent::Text(text) => text,
InlayContent::Color(_) => COLOR_TEXT.get_or_init(|| Rope::from("")),
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn get_color(&self) -> Option<Hsla> {
self.color
match self.content {
InlayContent::Color(color) => Some(color),
_ => None,
}
}
}
@@ -116,7 +127,7 @@ impl sum_tree::Item for Transform {
},
Transform::Inlay(inlay) => TransformSummary {
input: TextSummary::default(),
output: inlay.text.summary(),
output: inlay.text().summary(),
},
}
}
@@ -354,7 +365,7 @@ impl<'a> Iterator for InlayChunks<'a> {
let mut renderer = None;
let mut highlight_style = match inlay.id {
InlayId::EditPrediction(_) => self.highlight_styles.edit_prediction.map(|s| {
if inlay.text.chars().all(|c| c.is_whitespace()) {
if inlay.text().chars().all(|c| c.is_whitespace()) {
s.whitespace
} else {
s.insertion
@@ -363,7 +374,7 @@ impl<'a> Iterator for InlayChunks<'a> {
InlayId::Hint(_) => self.highlight_styles.inlay_hint,
InlayId::DebuggerValue(_) => self.highlight_styles.inlay_hint,
InlayId::Color(_) => {
if let Some(color) = inlay.color {
if let InlayContent::Color(color) = inlay.content {
renderer = Some(ChunkRenderer {
id: ChunkRendererId::Inlay(inlay.id),
render: Arc::new(move |cx| {
@@ -410,7 +421,7 @@ impl<'a> Iterator for InlayChunks<'a> {
let start = offset_in_inlay;
let end = cmp::min(self.max_output_offset, self.transforms.end().0)
- self.transforms.start().0;
let chunks = inlay.text.chunks_in_range(start.0..end.0);
let chunks = inlay.text().chunks_in_range(start.0..end.0);
text::ChunkWithBitmaps(chunks)
});
let ChunkBitmaps {
@@ -706,7 +717,7 @@ impl InlayMap {
for inlay_to_insert in to_insert {
// Avoid inserting empty inlays.
if inlay_to_insert.text.is_empty() {
if inlay_to_insert.text().is_empty() {
continue;
}
@@ -744,7 +755,7 @@ impl InlayMap {
#[cfg(test)]
pub(crate) fn randomly_mutate(
&mut self,
next_inlay_id: &mut usize,
next_inlay_id: &mut u32,
rng: &mut rand::rngs::StdRng,
) -> (InlaySnapshot, Vec<InlayEdit>) {
use rand::prelude::*;
@@ -822,7 +833,7 @@ impl InlaySnapshot {
InlayPoint(cursor.start().1.0 + (buffer_end - buffer_start))
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text.offset_to_point(overshoot);
let overshoot = inlay.text().offset_to_point(overshoot);
InlayPoint(cursor.start().1.0 + overshoot)
}
None => self.max_point(),
@@ -852,7 +863,7 @@ impl InlaySnapshot {
InlayOffset(cursor.start().1.0 + (buffer_offset_end - buffer_offset_start))
}
Some(Transform::Inlay(inlay)) => {
let overshoot = inlay.text.point_to_offset(overshoot);
let overshoot = inlay.text().point_to_offset(overshoot);
InlayOffset(cursor.start().1.0 + overshoot)
}
None => self.len(),
@@ -1064,7 +1075,7 @@ impl InlaySnapshot {
Some(Transform::Inlay(inlay)) => {
let suffix_start = overshoot;
let suffix_end = cmp::min(cursor.end().0, range.end).0 - cursor.start().0.0;
summary = inlay.text.cursor(suffix_start).summary(suffix_end);
summary = inlay.text().cursor(suffix_start).summary(suffix_end);
cursor.next();
}
None => {}
@@ -1086,7 +1097,7 @@ impl InlaySnapshot {
}
Some(Transform::Inlay(inlay)) => {
let prefix_end = overshoot;
summary += inlay.text.cursor(0).summary::<TextSummary>(prefix_end);
summary += inlay.text().cursor(0).summary::<TextSummary>(prefix_end);
}
None => {}
}
@@ -1269,7 +1280,7 @@ mod tests {
resolve_state: ResolveState::Resolved,
},
)
.text
.text()
.to_string(),
"a",
"Should not pad label if not requested"
@@ -1289,7 +1300,7 @@ mod tests {
resolve_state: ResolveState::Resolved,
},
)
.text
.text()
.to_string(),
" a ",
"Should pad label for every side requested"
@@ -1309,7 +1320,7 @@ mod tests {
resolve_state: ResolveState::Resolved,
},
)
.text
.text()
.to_string(),
" a ",
"Should not change already padded label"
@@ -1329,7 +1340,7 @@ mod tests {
resolve_state: ResolveState::Resolved,
},
)
.text
.text()
.to_string(),
" a ",
"Should not change already padded label"
@@ -1352,7 +1363,7 @@ mod tests {
resolve_state: ResolveState::Resolved,
},
)
.text
.text()
.to_string(),
" 🎨 ",
"Should pad single emoji correctly"
@@ -1750,7 +1761,7 @@ mod tests {
.collect::<Vec<_>>();
let mut expected_text = Rope::from(&buffer_snapshot.text());
for (offset, inlay) in inlays.iter().rev() {
expected_text.replace(*offset..*offset, &inlay.text.to_string());
expected_text.replace(*offset..*offset, &inlay.text().to_string());
}
assert_eq!(inlay_snapshot.text(), expected_text.to_string());
@@ -1803,7 +1814,7 @@ mod tests {
.into_iter()
.filter_map(|i| {
let (_, inlay) = &inlays[i];
let inlay_text_len = inlay.text.len();
let inlay_text_len = inlay.text().len();
match inlay_text_len {
0 => None,
1 => Some(InlayHighlight {
@@ -1812,7 +1823,7 @@ mod tests {
range: 0..1,
}),
n => {
let inlay_text = inlay.text.to_string();
let inlay_text = inlay.text().to_string();
let mut highlight_end = rng.random_range(1..n);
let mut highlight_start = rng.random_range(0..highlight_end);
while !inlay_text.is_char_boundary(highlight_end) {
@@ -2138,8 +2149,7 @@ mod tests {
let inlay = Inlay {
id: InlayId::Hint(0),
position,
text: text::Rope::from(inlay_text),
color: None,
content: InlayContent::Text(text::Rope::from(inlay_text)),
};
let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
@@ -2253,8 +2263,7 @@ mod tests {
let inlay = Inlay {
id: InlayId::Hint(0),
position,
text: text::Rope::from(test_case.inlay_text),
color: None,
content: InlayContent::Text(text::Rope::from(test_case.inlay_text)),
};
let (inlay_snapshot, _) = inlay_map.splice(&[], vec![inlay]);
+7 -4
View File
@@ -53,9 +53,12 @@ pub fn replacement(c: char) -> Option<&'static str> {
} else if contains(c, PRESERVE) {
None
} else {
Some("\u{2007}") // fixed width space
Some(FIXED_WIDTH_SPACE)
}
}
const FIXED_WIDTH_SPACE: &str = "\u{2007}";
// IDEOGRAPHIC SPACE is common alongside Chinese and other wide character sets.
// We don't highlight this for now (as it already shows up wide in the editor),
// but could if we tracked state in the classifier.
@@ -117,11 +120,11 @@ const PRESERVE: &[(char, char)] = &[
];
fn contains(c: char, list: &[(char, char)]) -> bool {
for (start, end) in list {
if c < *start {
for &(start, end) in list {
if c < start {
return false;
}
if c <= *end {
if c <= end {
return true;
}
}
+37 -45
View File
@@ -54,9 +54,7 @@ impl TabMap {
new_snapshot.version += 1;
}
let mut tab_edits = Vec::with_capacity(fold_edits.len());
if old_snapshot.tab_size == new_snapshot.tab_size {
let tab_edits = if old_snapshot.tab_size == new_snapshot.tab_size {
// Expand each edit to include the next tab on the same line as the edit,
// and any subsequent tabs on that line that moved across the tab expansion
// boundary.
@@ -112,7 +110,7 @@ impl TabMap {
let _old_alloc_ptr = fold_edits.as_ptr();
// Combine any edits that overlap due to the expansion.
let mut fold_edits = fold_edits.into_iter();
let fold_edits = if let Some(mut first_edit) = fold_edits.next() {
if let Some(mut first_edit) = fold_edits.next() {
// This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
#[allow(clippy::filter_map_identity)]
let mut v: Vec<_> = fold_edits
@@ -132,29 +130,30 @@ impl TabMap {
.collect();
v.push(first_edit);
debug_assert_eq!(v.as_ptr(), _old_alloc_ptr, "Fold edits were reallocated");
v
v.into_iter()
.map(|fold_edit| {
let old_start = fold_edit.old.start.to_point(&old_snapshot.fold_snapshot);
let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);
let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
TabEdit {
old: old_snapshot.to_tab_point(old_start)
..old_snapshot.to_tab_point(old_end),
new: new_snapshot.to_tab_point(new_start)
..new_snapshot.to_tab_point(new_end),
}
})
.collect()
} else {
vec![]
};
for fold_edit in fold_edits {
let old_start = fold_edit.old.start.to_point(&old_snapshot.fold_snapshot);
let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot);
let new_start = fold_edit.new.start.to_point(&new_snapshot.fold_snapshot);
let new_end = fold_edit.new.end.to_point(&new_snapshot.fold_snapshot);
tab_edits.push(TabEdit {
old: old_snapshot.to_tab_point(old_start)..old_snapshot.to_tab_point(old_end),
new: new_snapshot.to_tab_point(new_start)..new_snapshot.to_tab_point(new_end),
});
}
} else {
new_snapshot.version += 1;
tab_edits.push(TabEdit {
vec![TabEdit {
old: TabPoint::zero()..old_snapshot.max_point(),
new: TabPoint::zero()..new_snapshot.max_point(),
});
}
}]
};
*old_snapshot = new_snapshot;
(old_snapshot.clone(), tab_edits)
}
@@ -195,37 +194,28 @@ impl TabSnapshot {
.fold_snapshot
.text_summary_for_range(input_start..input_end);
let mut first_line_chars = 0;
let line_end = if range.start.row() == range.end.row() {
range.end
} else {
self.max_point()
};
for c in self
let first_line_chars = self
.chunks(range.start..line_end, false, Highlights::default())
.flat_map(|chunk| chunk.text.chars())
{
if c == '\n' {
break;
}
first_line_chars += 1;
}
.take_while(|&c| c != '\n')
.count() as u32;
let mut last_line_chars = 0;
if range.start.row() == range.end.row() {
last_line_chars = first_line_chars;
let last_line_chars = if range.start.row() == range.end.row() {
first_line_chars
} else {
for _ in self
.chunks(
TabPoint::new(range.end.row(), 0)..range.end,
false,
Highlights::default(),
)
.flat_map(|chunk| chunk.text.chars())
{
last_line_chars += 1;
}
}
self.chunks(
TabPoint::new(range.end.row(), 0)..range.end,
false,
Highlights::default(),
)
.flat_map(|chunk| chunk.text.chars())
.count() as u32
};
TextSummary {
lines: range.end.0 - range.start.0,
@@ -514,15 +504,17 @@ impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
pub struct TabChunks<'a> {
snapshot: &'a TabSnapshot,
max_expansion_column: u32,
max_output_position: Point,
tab_size: NonZeroU32,
// region: iteration state
fold_chunks: FoldChunks<'a>,
chunk: Chunk<'a>,
column: u32,
max_expansion_column: u32,
output_position: Point,
input_column: u32,
max_output_position: Point,
tab_size: NonZeroU32,
inside_leading_tab: bool,
// endregion: iteration state
}
impl TabChunks<'_> {
+8 -8
View File
@@ -279,15 +279,15 @@ impl InlineValueCache {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum InlayId {
EditPrediction(usize),
DebuggerValue(usize),
EditPrediction(u32),
DebuggerValue(u32),
// LSP
Hint(usize),
Color(usize),
Hint(u32),
Color(u32),
}
impl InlayId {
fn id(&self) -> usize {
fn id(&self) -> u32 {
match self {
Self::EditPrediction(id) => *id,
Self::DebuggerValue(id) => *id,
@@ -1118,7 +1118,8 @@ pub struct Editor {
edit_prediction_indent_conflict: bool,
edit_prediction_requires_modifier_in_indent_conflict: bool,
inlay_hint_cache: InlayHintCache,
next_inlay_id: usize,
next_inlay_id: u32,
next_color_inlay_id: u32,
_subscriptions: Vec<Subscription>,
pixel_position_of_newest_cursor: Option<gpui::Point<Pixels>>,
gutter_dimensions: GutterDimensions,
@@ -1182,7 +1183,6 @@ pub struct Editor {
pub change_list: ChangeList,
inline_value_cache: InlineValueCache,
selection_drag_state: SelectionDragState,
next_color_inlay_id: usize,
colors: Option<LspColorData>,
folding_newlines: Task<()>,
pub lookup_key: Option<Box<dyn Any + Send + Sync>>,
@@ -20531,7 +20531,7 @@ impl Editor {
Anchor::in_buffer(excerpt_id, buffer_id, hint.position),
hint.text(),
);
if !inlay.text.chars().contains(&'\n') {
if !inlay.text().chars().contains(&'\n') {
new_inlays.push(inlay);
}
});
+1 -1
View File
@@ -370,7 +370,7 @@ pub fn update_inlay_link_and_hover_points(
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: extra_shift_left
..hovered_hint.text.len() + extra_shift_right,
..hovered_hint.text().len() + extra_shift_right,
},
},
window,
+1 -1
View File
@@ -3571,7 +3571,7 @@ pub mod tests {
editor
.visible_inlay_hints(cx)
.into_iter()
.map(|hint| hint.text.to_string())
.map(|hint| hint.text().to_string())
.collect()
}
}
+3 -3
View File
@@ -54,7 +54,7 @@ use util::post_inc;
const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExcerptId(usize);
pub struct ExcerptId(u32);
/// One or more [`Buffers`](Buffer) being edited in a single view.
///
@@ -7202,7 +7202,7 @@ impl ExcerptId {
}
pub fn max() -> Self {
Self(usize::MAX)
Self(u32::MAX)
}
pub fn to_proto(self) -> u64 {
@@ -7222,7 +7222,7 @@ impl ExcerptId {
impl From<ExcerptId> for usize {
fn from(val: ExcerptId) -> Self {
val.0
val.0 as usize
}
}
+10 -13
View File
@@ -62,26 +62,23 @@ impl Anchor {
}
pub fn bias(&self, bias: Bias, buffer: &BufferSnapshot) -> Anchor {
if bias == Bias::Left {
self.bias_left(buffer)
} else {
self.bias_right(buffer)
match bias {
Bias::Left => self.bias_left(buffer),
Bias::Right => self.bias_right(buffer),
}
}
pub fn bias_left(&self, buffer: &BufferSnapshot) -> Anchor {
if self.bias == Bias::Left {
*self
} else {
buffer.anchor_before(self)
match self.bias {
Bias::Left => *self,
Bias::Right => buffer.anchor_before(self),
}
}
pub fn bias_right(&self, buffer: &BufferSnapshot) -> Anchor {
if self.bias == Bias::Right {
*self
} else {
buffer.anchor_after(self)
match self.bias {
Bias::Left => buffer.anchor_after(self),
Bias::Right => *self,
}
}
@@ -96,7 +93,7 @@ impl Anchor {
pub fn is_valid(&self, buffer: &BufferSnapshot) -> bool {
if *self == Anchor::MIN || *self == Anchor::MAX {
true
} else if self.buffer_id != Some(buffer.remote_id) {
} else if self.buffer_id.is_none_or(|id| id != buffer.remote_id) {
false
} else {
let Some(fragment_id) = buffer.try_fragment_id_for_anchor(self) else {