@@ -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]);
@@ -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<'_> {
@@ -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);
}
});