diff --git a/Cargo.lock b/Cargo.lock index 35d21eb7862a1aed599c4b4821dfac6b75cb42a4..75e353144de2b4e89c48646b85086b79eda60103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1531,6 +1531,7 @@ dependencies = [ "ctor", "env_logger", "gpui", + "itertools", "language", "lazy_static", "log", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 24dbcff3da878a303964edc1f1f5a447450f275a..96d302d78ba979e921043374a086efcac58898bf 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -28,6 +28,7 @@ util = { path = "../util" } workspace = { path = "../workspace" } aho-corasick = "0.7" anyhow = "1.0" +itertools = "0.10" lazy_static = "1.4" log = "0.4" parking_lot = "0.11" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 07a01f73638a4b40aa0e6951cfcdb313449d602e..88b2d5837415fd91570a3af1de17ce9f88626901 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3006,61 +3006,90 @@ impl Editor { } } - pub fn all_selections_in_range<'a, D>( + pub fn visible_selections<'a>( &'a self, - range: Range, + display_rows: Range, cx: &'a mut MutableAppContext, - ) -> HashMap>> - where - D: TextDimension + Ord + Sub, - { - let mut result = HashMap::new(); - + ) -> HashMap>> { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; - let range = range.start.to_offset(&display_map, Bias::Left) - ..range.end.to_offset(&display_map, Bias::Left); - let anchor_range = buffer.anchor_before(range.start)..buffer.anchor_after(range.end); + let start = if display_rows.start == 0 { + Anchor::min() + } else { + buffer.anchor_before( + DisplayPoint::new(display_rows.start, 0).to_offset(&display_map, Bias::Left), + ) + }; + let end = if display_rows.end > display_map.max_point().row() { + Anchor::max() + } else { + buffer.anchor_before( + DisplayPoint::new(display_rows.end, 0).to_offset(&display_map, Bias::Right), + ) + }; + + dbg!(&start, &end); + dbg!(&self.selections); + let start_ix = match self .selections - .binary_search_by(|probe| probe.end.cmp(&anchor_range.start, &buffer).unwrap()) + .binary_search_by(|probe| probe.end.cmp(&start, &buffer).unwrap()) { Ok(ix) | Err(ix) => ix, }; let end_ix = match self .selections - .binary_search_by(|probe| probe.start.cmp(&anchor_range.end, &buffer).unwrap()) + .binary_search_by(|probe| probe.start.cmp(&end, &buffer).unwrap()) { - Ok(ix) | Err(ix) => ix, + Ok(ix) => ix + 1, + Err(ix) => ix, }; - let selections = &self.selections[start_ix..end_ix]; - let mut summaries = buffer - .summaries_for_anchors::(selections.iter().flat_map(|s| [&s.start, &s.end])) - .into_iter(); + dbg!(start_ix, end_ix); + + fn display_selection( + selection: &Selection, + display_map: &DisplaySnapshot, + ) -> Selection { + Selection { + id: selection.id, + start: selection.start.to_display_point(&display_map), + end: selection.end.to_display_point(&display_map), + reversed: selection.reversed, + goal: selection.goal, + } + } + + let mut result = HashMap::new(); result.insert( self.replica_id(cx), - selections + self.selections[start_ix..end_ix] .iter() - .map(|s| Selection { - id: s.id, - start: summaries.next().unwrap(), - end: summaries.next().unwrap(), - reversed: s.reversed, - goal: s.goal, - }) + .chain( + self.pending_selection + .as_ref() + .map(|pending| &pending.selection), + ) + .map(|s| display_selection(s, &display_map)) .collect(), ); for (replica_id, selections) in display_map .buffer_snapshot - .remote_selections_in_range(range) + .remote_selections_in_range(start..end) { - result.insert(replica_id, selections.collect()); + result.insert( + replica_id, + selections + .map(|s| display_selection(&s, &display_map)) + .collect(), + ); } + dbg!(&result); + result } @@ -3137,6 +3166,31 @@ impl Editor { } } + fn resolve_selections< + 'a, + D: TextDimension + Ord + Sub, + I: 'a + Iterator>, + >( + &self, + selections: I, + buffer: &MultiBufferSnapshot, + ) -> impl 'a + Iterator> { + use itertools::Itertools as _; + + let (to_map, to_summarize) = selections.tee(); + let mut summaries = buffer + .summaries_for_anchors::(to_summarize.flat_map(|s| [&s.start, &s.end])) + .into_iter(); + + to_map.map(move |s| Selection { + id: s.id, + start: summaries.next().unwrap(), + end: summaries.next().unwrap(), + reversed: s.reversed, + goal: s.goal, + }) + } + fn selection_count<'a>(&self) -> usize { let mut count = self.selections.len(); if self.pending_selection.is_some() { @@ -3729,8 +3783,6 @@ pub fn diagnostic_style( #[cfg(test)] mod tests { - use std::mem; - use super::*; use language::LanguageConfig; use text::Point; @@ -3786,6 +3838,7 @@ mod tests { view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx); }); + eprintln!(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); assert_eq!( editor.update(cx, |view, cx| view.selection_ranges(cx)), [ @@ -3984,6 +4037,8 @@ mod tests { }); view.update(cx, |view, cx| { + dbg!(&view.selections); + assert_eq!( view.selection_ranges(cx), &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)] @@ -5691,16 +5746,16 @@ mod tests { impl Editor { fn selection_ranges(&self, cx: &mut MutableAppContext) -> Vec> { - let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - self.selections - .iter() + self.visible_selections(0..self.max_point(cx).row() + 1, cx) + .get(&self.replica_id(cx)) + .unwrap() + .into_iter() .map(|s| { - let mut range = - s.start.to_display_point(&snapshot)..s.end.to_display_point(&snapshot); if s.reversed { - mem::swap(&mut range.start, &mut range.end); + s.end..s.start + } else { + s.start..s.end } - range }) .collect() } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 27b29e678c16859560f4ffd3500d7386fc2124f7..3572d839b6ea10e01aa49f8d0984b2ce99a5db6c 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -733,37 +733,28 @@ impl Element for EditorElement { let scroll_top = scroll_position.y() * line_height; let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen - let selections = HashMap::new(); - let active_rows = BTreeMap::new(); + let mut active_rows = BTreeMap::new(); let mut highlighted_row = None; - self.update_view(cx.app, |view, cx| { + let selections = self.update_view(cx.app, |view, cx| { highlighted_row = view.highlighted_row(); - - // TODO: Get this working with editors owning their own selections - - // for selection_set_id in view.active_selection_sets(cx).collect::>() { - // let replica_selections = view.intersecting_selections( - // selection_set_id, - // DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0), - // cx, - // ); - // for selection in &replica_selections { - // if selection_set_id == view.selection_set_id { - // let is_empty = selection.start == selection.end; - // let selection_start = snapshot.prev_row_boundary(selection.start).0; - // let selection_end = snapshot.next_row_boundary(selection.end).0; - // for row in cmp::max(selection_start.row(), start_row) - // ..=cmp::min(selection_end.row(), end_row) - // { - // let contains_non_empty_selection = - // active_rows.entry(row).or_insert(!is_empty); - // *contains_non_empty_selection |= !is_empty; - // } - // } - // } - - // selections.insert(selection_set_id.replica_id, replica_selections); - // } + let selections = view.visible_selections(start_row..end_row, cx); + for (replica_id, selections) in &selections { + if *replica_id == view.replica_id(cx) { + for selection in selections { + let is_empty = selection.start == selection.end; + let selection_start = snapshot.prev_row_boundary(selection.start).0; + let selection_end = snapshot.next_row_boundary(selection.end).0; + for row in cmp::max(selection_start.row(), start_row) + ..=cmp::min(selection_end.row(), end_row) + { + let contains_non_empty_selection = + active_rows.entry(row).or_insert(!is_empty); + *contains_non_empty_selection |= !is_empty; + } + } + } + } + selections }); let line_number_layouts = self.layout_rows(start_row..end_row, &active_rows, &snapshot, cx); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 51879bcf63c901c99939a96ef7be332602c5dbe6..de81bce210a60c84c14a798d466680f3e8b2208b 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -7,8 +7,8 @@ use clock::ReplicaId; use collections::HashMap; use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use language::{ - Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Selection, - ToOffset as _, ToPoint as _, TransactionId, + Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, FromAnchor, + Language, Selection, ToOffset as _, ToPoint as _, TransactionId, }; use std::{ cell::{Ref, RefCell}, @@ -805,19 +805,18 @@ impl MultiBufferSnapshot { let mut summaries = Vec::new(); while let Some(anchor) = anchors.peek() { let excerpt_id = &anchor.excerpt_id; - cursor.seek(&Some(excerpt_id), Bias::Left, &()); - if let Some(excerpt) = cursor.item() { - let excerpt_exists = excerpt.id == *excerpt_id; - let excerpt_anchors = std::iter::from_fn(|| { - let anchor = anchors.peek()?; - if anchor.excerpt_id == *excerpt_id { - Some(&anchors.next().unwrap().text_anchor) - } else { - None - } - }); + let excerpt_anchors = std::iter::from_fn(|| { + let anchor = anchors.peek()?; + if anchor.excerpt_id == *excerpt_id { + Some(&anchors.next().unwrap().text_anchor) + } else { + None + } + }); - if excerpt_exists { + cursor.seek_forward(&Some(excerpt_id), Bias::Left, &()); + if let Some(excerpt) = cursor.item() { + if excerpt.id == *excerpt_id { let mut excerpt_start = D::from_text_summary(&cursor.start().text); excerpt_start.add_summary(&excerpt.header_summary(), &()); let excerpt_buffer_start = excerpt.range.start.summary::(&excerpt.buffer); @@ -834,12 +833,12 @@ impl MultiBufferSnapshot { excerpt_start }), ); - } else { - excerpt_anchors.for_each(drop); + continue; } - } else { - break; } + + let summary = D::from_text_summary(&cursor.start().text); + summaries.extend(excerpt_anchors.map(|_| summary.clone())); } summaries @@ -935,17 +934,34 @@ impl MultiBufferSnapshot { None } - pub fn remote_selections_in_range<'a, I, O>( + pub fn remote_selections_in_range<'a>( &'a self, - range: Range, - ) -> impl 'a + Iterator>)> - where - I: ToOffset, - O: TextDimension, - { + range: Range, + ) -> impl 'a + Iterator>)> { + // TODO + let excerpt_id = self.excerpts.first().unwrap().id.clone(); self.as_singleton() .unwrap() - .remote_selections_in_range(range.start.to_offset(self)..range.end.to_offset(self)) + .remote_selections_in_range(range.start.text_anchor..range.end.text_anchor) + .map(move |(replica_id, selections)| { + let excerpt_id = excerpt_id.clone(); + ( + replica_id, + selections.map(move |s| Selection { + id: s.id, + start: Anchor { + excerpt_id: excerpt_id.clone(), + text_anchor: s.start.clone(), + }, + end: Anchor { + excerpt_id: excerpt_id.clone(), + text_anchor: s.end.clone(), + }, + reversed: s.reversed, + goal: s.goal, + }), + ) + }) } } diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index 2b8b35ef4114eea3cd8620903fb8d562fffa44a4..2cc4817a92b9f8d22de3e2e3640ea14eec669061 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -31,15 +31,16 @@ impl Anchor { pub fn cmp<'a>(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Result { let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id); if excerpt_id_cmp.is_eq() { - if self.excerpt_id == ExcerptId::max() { - return Ok(Ordering::Equal); + if self.excerpt_id == ExcerptId::min() || self.excerpt_id == ExcerptId::max() { + Ok(Ordering::Equal) + } else { + self.text_anchor.cmp( + &other.text_anchor, + snapshot + .buffer_snapshot_for_excerpt(&self.excerpt_id) + .ok_or_else(|| anyhow!("excerpt {:?} not found", self.excerpt_id))?, + ) } - self.text_anchor.cmp( - &other.text_anchor, - snapshot - .buffer_snapshot_for_excerpt(&self.excerpt_id) - .ok_or_else(|| anyhow!("excerpt {:?} not found", self.excerpt_id))?, - ) } else { return Ok(excerpt_id_cmp); } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index c0f968cb271f649ddd5cede478d454b9f878e3b0..f3eb3dc97403b92a11a046fd0b010d0e744e36b0 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1648,15 +1648,11 @@ impl BufferSnapshot { .min_by_key(|(open_range, close_range)| close_range.end - open_range.start) } - pub fn remote_selections_in_range<'a, I, O>( + pub fn remote_selections_in_range<'a>( &'a self, - range: Range, - ) -> impl 'a + Iterator>)> - where - I: ToOffset, - O: TextDimension, + range: Range, + ) -> impl 'a + Iterator>)> { - let range = self.anchor_before(range.start)..self.anchor_after(range.end); self.remote_selections .iter() .map(move |(replica_id, selections)| { @@ -1671,17 +1667,7 @@ impl BufferSnapshot { Ok(ix) | Err(ix) => ix, }; - let selections = &selections[start_ix..end_ix]; - let mut summaries = - self.summaries_for_anchors(selections.iter().flat_map(|s| [&s.start, &s.end])); - let resolved = selections.iter().map(move |s| Selection { - id: s.id, - start: summaries.next().unwrap(), - end: summaries.next().unwrap(), - reversed: s.reversed, - goal: s.goal, - }); - (*replica_id, resolved) + (*replica_id, selections[start_ix..end_ix].iter()) }) }