Allow highlighting editor rows from multiple sources concurrently (#9153)
This commit is contained in:
@@ -43,7 +43,7 @@ use anyhow::{anyhow, Context as _, Result};
|
||||
use blink_manager::BlinkManager;
|
||||
use client::{Collaborator, ParticipantIndex};
|
||||
use clock::ReplicaId;
|
||||
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
||||
use collections::{hash_map, BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
||||
use convert_case::{Case, Casing};
|
||||
use copilot::Copilot;
|
||||
use debounced_delay::DebouncedDelay;
|
||||
@@ -386,7 +386,8 @@ pub struct Editor {
|
||||
show_gutter: bool,
|
||||
show_wrap_guides: Option<bool>,
|
||||
placeholder_text: Option<Arc<str>>,
|
||||
highlighted_rows: Option<Range<u32>>,
|
||||
highlight_order: usize,
|
||||
highlighted_rows: HashMap<TypeId, Vec<(usize, Range<Anchor>, Hsla)>>,
|
||||
background_highlights: BTreeMap<TypeId, BackgroundHighlight>,
|
||||
nav_history: Option<ItemNavHistory>,
|
||||
context_menu: RwLock<Option<ContextMenu>>,
|
||||
@@ -1523,7 +1524,8 @@ impl Editor {
|
||||
show_gutter: mode == EditorMode::Full,
|
||||
show_wrap_guides: None,
|
||||
placeholder_text: None,
|
||||
highlighted_rows: None,
|
||||
highlight_order: 0,
|
||||
highlighted_rows: HashMap::default(),
|
||||
background_highlights: Default::default(),
|
||||
nav_history: None,
|
||||
context_menu: RwLock::new(None),
|
||||
@@ -8921,12 +8923,93 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
|
||||
self.highlighted_rows = rows;
|
||||
/// Adds or removes (on `None` color) a highlight for the rows corresponding to the anchor range given.
|
||||
/// On matching anchor range, replaces the old highlight; does not clear the other existing highlights.
|
||||
/// If multiple anchor ranges will produce highlights for the same row, the last range added will be used.
|
||||
pub fn highlight_rows<T: 'static>(
|
||||
&mut self,
|
||||
rows: Range<Anchor>,
|
||||
color: Option<Hsla>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
|
||||
match self.highlighted_rows.entry(TypeId::of::<T>()) {
|
||||
hash_map::Entry::Occupied(o) => {
|
||||
let row_highlights = o.into_mut();
|
||||
let existing_highlight_index =
|
||||
row_highlights.binary_search_by(|(_, highlight_range, _)| {
|
||||
highlight_range
|
||||
.start
|
||||
.cmp(&rows.start, &multi_buffer_snapshot)
|
||||
.then(highlight_range.end.cmp(&rows.end, &multi_buffer_snapshot))
|
||||
});
|
||||
match color {
|
||||
Some(color) => {
|
||||
let insert_index = match existing_highlight_index {
|
||||
Ok(i) => i,
|
||||
Err(i) => i,
|
||||
};
|
||||
row_highlights.insert(
|
||||
insert_index,
|
||||
(post_inc(&mut self.highlight_order), rows, color),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
if let Ok(i) = existing_highlight_index {
|
||||
row_highlights.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
hash_map::Entry::Vacant(v) => {
|
||||
if let Some(color) = color {
|
||||
v.insert(vec![(post_inc(&mut self.highlight_order), rows, color)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlighted_rows(&self) -> Option<Range<u32>> {
|
||||
self.highlighted_rows.clone()
|
||||
/// Clear all anchor ranges for a certain highlight context type, so no corresponding rows will be highlighted.
|
||||
pub fn clear_row_highlights<T: 'static>(&mut self) {
|
||||
self.highlighted_rows.remove(&TypeId::of::<T>());
|
||||
}
|
||||
|
||||
/// For a highlight given context type, gets all anchor ranges that will be used for row highlighting.
|
||||
pub fn highlighted_rows<T: 'static>(
|
||||
&self,
|
||||
) -> Option<impl Iterator<Item = (&Range<Anchor>, &Hsla)>> {
|
||||
Some(
|
||||
self.highlighted_rows
|
||||
.get(&TypeId::of::<T>())?
|
||||
.iter()
|
||||
.map(|(_, range, color)| (range, color)),
|
||||
)
|
||||
}
|
||||
|
||||
// Merges all anchor ranges for all context types ever set, picking the last highlight added in case of a row conflict.
|
||||
// Rerturns a map of display rows that are highlighted and their corresponding highlight color.
|
||||
pub fn highlighted_display_rows(&mut self, cx: &mut WindowContext) -> BTreeMap<u32, Hsla> {
|
||||
let snapshot = self.snapshot(cx);
|
||||
let mut used_highlight_orders = HashMap::default();
|
||||
self.highlighted_rows
|
||||
.iter()
|
||||
.flat_map(|(_, highlighted_rows)| highlighted_rows.iter())
|
||||
.fold(
|
||||
BTreeMap::<u32, Hsla>::new(),
|
||||
|mut unique_rows, (highlight_order, anchor_range, hsla)| {
|
||||
let start_row = anchor_range.start.to_display_point(&snapshot).row();
|
||||
let end_row = anchor_range.end.to_display_point(&snapshot).row();
|
||||
for row in start_row..=end_row {
|
||||
let used_index =
|
||||
used_highlight_orders.entry(row).or_insert(*highlight_order);
|
||||
if highlight_order >= used_index {
|
||||
*used_index = *highlight_order;
|
||||
unique_rows.insert(row, *hsla);
|
||||
}
|
||||
}
|
||||
unique_rows
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn highlight_background<T: 'static>(
|
||||
|
||||
@@ -665,19 +665,53 @@ impl EditorElement {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(highlighted_rows) = &layout.highlighted_rows {
|
||||
let mut paint_highlight = |highlight_row_start: u32, highlight_row_end: u32, color| {
|
||||
let origin = point(
|
||||
bounds.origin.x,
|
||||
bounds.origin.y
|
||||
+ (layout.position_map.line_height * highlighted_rows.start as f32)
|
||||
+ (layout.position_map.line_height * highlight_row_start as f32)
|
||||
- scroll_top,
|
||||
);
|
||||
let size = size(
|
||||
bounds.size.width,
|
||||
layout.position_map.line_height * highlighted_rows.len() as f32,
|
||||
layout.position_map.line_height
|
||||
* (highlight_row_end + 1 - highlight_row_start) as f32,
|
||||
);
|
||||
let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
|
||||
cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
|
||||
cx.paint_quad(fill(Bounds { origin, size }, color));
|
||||
};
|
||||
let mut last_row = None;
|
||||
let mut highlight_row_start = 0u32;
|
||||
let mut highlight_row_end = 0u32;
|
||||
for (&row, &color) in &layout.highlighted_rows {
|
||||
let paint = last_row.map_or(false, |(last_row, last_color)| {
|
||||
last_color != color || last_row + 1 < row
|
||||
});
|
||||
|
||||
if paint {
|
||||
let paint_range_is_unfinished = highlight_row_end == 0;
|
||||
if paint_range_is_unfinished {
|
||||
highlight_row_end = row;
|
||||
last_row = None;
|
||||
}
|
||||
paint_highlight(highlight_row_start, highlight_row_end, color);
|
||||
highlight_row_start = 0;
|
||||
highlight_row_end = 0;
|
||||
if !paint_range_is_unfinished {
|
||||
highlight_row_start = row;
|
||||
last_row = Some((row, color));
|
||||
}
|
||||
} else {
|
||||
if last_row.is_none() {
|
||||
highlight_row_start = row;
|
||||
} else {
|
||||
highlight_row_end = row;
|
||||
}
|
||||
last_row = Some((row, color));
|
||||
}
|
||||
}
|
||||
if let Some((row, hsla)) = last_row {
|
||||
highlight_row_end = row;
|
||||
paint_highlight(highlight_row_start, highlight_row_end, hsla);
|
||||
}
|
||||
|
||||
let scroll_left =
|
||||
@@ -2064,7 +2098,7 @@ impl EditorElement {
|
||||
let mut active_rows = BTreeMap::new();
|
||||
let is_singleton = editor.is_singleton(cx);
|
||||
|
||||
let highlighted_rows = editor.highlighted_rows();
|
||||
let highlighted_rows = editor.highlighted_display_rows(cx);
|
||||
let highlighted_ranges = editor.background_highlights_in_range(
|
||||
start_anchor..end_anchor,
|
||||
&snapshot.display_snapshot,
|
||||
@@ -3198,7 +3232,7 @@ pub struct LayoutState {
|
||||
visible_anchor_range: Range<Anchor>,
|
||||
visible_display_row_range: Range<u32>,
|
||||
active_rows: BTreeMap<u32, bool>,
|
||||
highlighted_rows: Option<Range<u32>>,
|
||||
highlighted_rows: BTreeMap<u32, Hsla>,
|
||||
line_numbers: Vec<Option<ShapedLine>>,
|
||||
display_hunks: Vec<DisplayDiffHunk>,
|
||||
blocks: Vec<BlockLayout>,
|
||||
|
||||
@@ -81,8 +81,8 @@ impl Editor {
|
||||
|
||||
let mut target_top;
|
||||
let mut target_bottom;
|
||||
if let Some(highlighted_rows) = &self.highlighted_rows {
|
||||
target_top = highlighted_rows.start as f32;
|
||||
if let Some(first_highlighted_row) = &self.highlighted_display_rows(cx).first_entry() {
|
||||
target_top = *first_highlighted_row.key() as f32;
|
||||
target_bottom = target_top + 1.;
|
||||
} else {
|
||||
let selections = self.selections.all::<Point>(cx);
|
||||
@@ -205,10 +205,7 @@ impl Editor {
|
||||
let mut target_left;
|
||||
let mut target_right;
|
||||
|
||||
if self.highlighted_rows.is_some() {
|
||||
target_left = px(0.);
|
||||
target_right = px(0.);
|
||||
} else {
|
||||
if self.highlighted_rows.is_empty() {
|
||||
target_left = px(f32::INFINITY);
|
||||
target_right = px(0.);
|
||||
for selection in selections {
|
||||
@@ -229,6 +226,9 @@ impl Editor {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
target_left = px(0.);
|
||||
target_right = px(0.);
|
||||
}
|
||||
|
||||
target_right = target_right.min(scroll_width);
|
||||
|
||||
Reference in New Issue
Block a user