From ffb75b0f021160b5891d332bf555b2316d6d231f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 12:13:04 +0200 Subject: [PATCH 1/6] Extract an `ExcerptRange` containing a `context` field in `multi_buffer` This lays the groundwork for adding an optional `primary` field that can be supplied to provide the "jump to" feature. --- crates/diagnostics/src/diagnostics.rs | 7 +- crates/editor/src/display_map/block_map.rs | 4 +- crates/editor/src/editor.rs | 42 +++- crates/editor/src/movement.rs | 10 +- crates/editor/src/multi_buffer.rs | 266 ++++++++++++++------- crates/search/src/buffer_search.rs | 2 +- 6 files changed, 231 insertions(+), 100 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 806703a544d681fd690e5777597000558797ba28..0ce451a401c201ad33141496df7634bef8bf1a16 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -5,7 +5,8 @@ use collections::{BTreeMap, HashSet}; use editor::{ diagnostic_block_renderer, display_map::{BlockDisposition, BlockId, BlockProperties, RenderBlock}, - highlight_diagnostic_message, Autoscroll, Editor, ExcerptId, MultiBuffer, ToOffset, + highlight_diagnostic_message, Autoscroll, Editor, ExcerptId, ExcerptRange, MultiBuffer, + ToOffset, }; use gpui::{ actions, elements::*, fonts::TextStyle, impl_internal_actions, platform::CursorStyle, @@ -348,7 +349,9 @@ impl ProjectDiagnosticsEditor { .insert_excerpts_after( &prev_excerpt_id, buffer.clone(), - [excerpt_start..excerpt_end], + [ExcerptRange { + context: excerpt_start..excerpt_end, + }], excerpts_cx, ) .pop() diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index bdfff4862c41d156cfad9c5bf8376e0e84a6a9e7..013ef305b3e9aa44795c7d94ec08414c73ba36f6 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -2,7 +2,7 @@ use super::{ wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot}, TextHighlights, }; -use crate::{Anchor, ToPoint as _}; +use crate::{Anchor, ExcerptRange, ToPoint as _}; use collections::{Bound, HashMap, HashSet}; use gpui::{ElementBox, RenderContext}; use language::{BufferSnapshot, Chunk, Patch}; @@ -98,7 +98,7 @@ pub enum TransformBlock { Custom(Arc), ExcerptHeader { buffer: BufferSnapshot, - range: Range, + range: ExcerptRange, height: u8, starts_new_buffer: bool, }, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index cce1ebd17c7881560d57f7b17d22684212884038..2b283683e8fac67af7ac7a957305a3223a9c0bcd 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -36,7 +36,8 @@ use language::{ }; use multi_buffer::MultiBufferChunks; pub use multi_buffer::{ - Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, + Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, + ToPoint, }; use ordered_float::OrderedFloat; use project::{HoverBlock, Project, ProjectTransaction}; @@ -2833,11 +2834,11 @@ impl Editor { let start = highlight .range .start - .max(&excerpt_range.start, cursor_buffer_snapshot); + .max(&excerpt_range.context.start, cursor_buffer_snapshot); let end = highlight .range .end - .min(&excerpt_range.end, cursor_buffer_snapshot); + .min(&excerpt_range.context.end, cursor_buffer_snapshot); if start.cmp(&end, cursor_buffer_snapshot).is_ge() { continue; } @@ -7718,12 +7719,16 @@ mod tests { let mut multibuffer = MultiBuffer::new(0); multibuffer.push_excerpts( toml_buffer.clone(), - [Point::new(0, 0)..Point::new(2, 0)], + [ExcerptRange { + context: Point::new(0, 0)..Point::new(2, 0), + }], cx, ); multibuffer.push_excerpts( rust_buffer.clone(), - [Point::new(0, 0)..Point::new(1, 0)], + [ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 0), + }], cx, ); multibuffer @@ -9595,8 +9600,12 @@ mod tests { multibuffer.push_excerpts( buffer.clone(), [ - Point::new(0, 0)..Point::new(0, 4), - Point::new(1, 0)..Point::new(1, 4), + ExcerptRange { + context: Point::new(0, 0)..Point::new(0, 4), + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(1, 4), + }, ], cx, ); @@ -9634,6 +9643,9 @@ mod tests { [aaaa (bbbb] cccc)"}); + let excerpt_ranges = excerpt_ranges + .into_iter() + .map(|context| ExcerptRange { context }); let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); @@ -9687,8 +9699,12 @@ mod tests { .push_excerpts( buffer.clone(), [ - Point::new(0, 0)..Point::new(1, 4), - Point::new(1, 0)..Point::new(2, 4), + ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 4), + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(2, 4), + }, ], cx, ) @@ -9771,8 +9787,12 @@ mod tests { .push_excerpts( buffer.clone(), [ - Point::new(0, 0)..Point::new(1, 4), - Point::new(1, 0)..Point::new(2, 4), + ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 4), + }, + ExcerptRange { + context: Point::new(1, 0)..Point::new(2, 4), + }, ], cx, ) diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 1f4486739bd41a0764ec5ed02093e181d097d739..c4e28ca7c871d8d3a0208810c0bdc811c5ce6d67 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -272,7 +272,7 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range< #[cfg(test)] mod tests { use super::*; - use crate::{test::marked_display_snapshot, Buffer, DisplayMap, MultiBuffer}; + use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; use language::Point; use settings::Settings; @@ -494,8 +494,12 @@ mod tests { multibuffer.push_excerpts( buffer.clone(), [ - Point::new(0, 0)..Point::new(1, 4), - Point::new(2, 0)..Point::new(3, 2), + ExcerptRange { + context: Point::new(0, 0)..Point::new(1, 4), + }, + ExcerptRange { + context: Point::new(2, 0)..Point::new(3, 2), + }, ], cx, ); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 32e2021fd2b2fc52d9b87a8e021fe28f95dab1a8..fb3eb94c1d06d7a874421fd87e08a1fadb1d4e53 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -104,7 +104,7 @@ pub struct ExcerptBoundary { pub id: ExcerptId, pub row: u32, pub buffer: BufferSnapshot, - pub range: Range, + pub range: ExcerptRange, pub starts_new_buffer: bool, } @@ -113,12 +113,17 @@ struct Excerpt { id: ExcerptId, buffer_id: usize, buffer: BufferSnapshot, - range: Range, + range: ExcerptRange, max_buffer_row: u32, text_summary: TextSummary, has_trailing_newline: bool, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ExcerptRange { + pub context: Range, +} + #[derive(Clone, Debug, Default)] struct ExcerptSummary { excerpt_id: ExcerptId, @@ -215,7 +220,13 @@ impl MultiBuffer { pub fn singleton(buffer: ModelHandle, cx: &mut ModelContext) -> Self { let mut this = Self::new(buffer.read(cx).replica_id()); this.singleton = true; - this.push_excerpts(buffer, [text::Anchor::MIN..text::Anchor::MAX], cx); + this.push_excerpts( + buffer, + [ExcerptRange { + context: text::Anchor::MIN..text::Anchor::MAX, + }], + cx, + ); this.snapshot.borrow_mut().singleton = true; this } @@ -343,8 +354,12 @@ impl MultiBuffer { } let start_excerpt = cursor.item().expect("start offset out of bounds"); let start_overshoot = range.start - cursor.start(); - let buffer_start = - start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot; + let buffer_start = start_excerpt + .range + .context + .start + .to_offset(&start_excerpt.buffer) + + start_overshoot; cursor.seek(&range.end, Bias::Right, &()); if cursor.item().is_none() && range.end == *cursor.start() { @@ -352,7 +367,12 @@ impl MultiBuffer { } let end_excerpt = cursor.item().expect("end offset out of bounds"); let end_overshoot = range.end - cursor.start(); - let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot; + let buffer_end = end_excerpt + .range + .context + .start + .to_offset(&end_excerpt.buffer) + + end_overshoot; if start_excerpt.id == end_excerpt.id { buffer_edits @@ -360,10 +380,18 @@ impl MultiBuffer { .or_insert(Vec::new()) .push((buffer_start..buffer_end, new_text, true)); } else { - let start_excerpt_range = - buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer); - let end_excerpt_range = - end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end; + let start_excerpt_range = buffer_start + ..start_excerpt + .range + .context + .end + .to_offset(&start_excerpt.buffer); + let end_excerpt_range = end_excerpt + .range + .context + .start + .to_offset(&end_excerpt.buffer) + ..buffer_end; buffer_edits .entry(start_excerpt.buffer_id) .or_insert(Vec::new()) @@ -383,7 +411,7 @@ impl MultiBuffer { .entry(excerpt.buffer_id) .or_insert(Vec::new()) .push(( - excerpt.range.to_offset(&excerpt.buffer), + excerpt.range.context.to_offset(&excerpt.buffer), new_text.clone(), false, )); @@ -523,8 +551,8 @@ impl MultiBuffer { break; } - let mut start = excerpt.range.start.clone(); - let mut end = excerpt.range.end.clone(); + let mut start = excerpt.range.context.start.clone(); + let mut end = excerpt.range.context.end.clone(); if excerpt.id == selection.start.excerpt_id { start = selection.start.text_anchor.clone(); } @@ -644,7 +672,7 @@ impl MultiBuffer { pub fn push_excerpts( &mut self, buffer: ModelHandle, - ranges: impl IntoIterator>, + ranges: impl IntoIterator>, cx: &mut ModelContext, ) -> Vec where @@ -692,7 +720,9 @@ impl MultiBuffer { } } - excerpt_ranges.push(excerpt_start..excerpt_end); + excerpt_ranges.push(ExcerptRange { + context: excerpt_start..excerpt_end, + }); range_counts.push(ranges_in_excerpt); } @@ -722,7 +752,7 @@ impl MultiBuffer { &mut self, prev_excerpt_id: &ExcerptId, buffer: ModelHandle, - ranges: impl IntoIterator>, + ranges: impl IntoIterator>, cx: &mut ModelContext, ) -> Vec where @@ -786,8 +816,10 @@ impl MultiBuffer { if let Err(ix) = buffer_state.excerpts.binary_search(&id) { buffer_state.excerpts.insert(ix, id.clone()); } - let range = buffer_snapshot.anchor_before(&range.start) - ..buffer_snapshot.anchor_after(&range.end); + let range = ExcerptRange { + context: buffer_snapshot.anchor_before(&range.context.start) + ..buffer_snapshot.anchor_after(&range.context.end), + }; let excerpt = Excerpt::new( id.clone(), buffer_id, @@ -846,7 +878,7 @@ impl MultiBuffer { &self, buffer: &ModelHandle, cx: &AppContext, - ) -> Vec<(ExcerptId, Range)> { + ) -> Vec<(ExcerptId, ExcerptRange)> { let mut excerpts = Vec::new(); let snapshot = self.read(cx); let buffers = self.buffers.borrow(); @@ -894,7 +926,7 @@ impl MultiBuffer { .unwrap() .buffer .clone(), - excerpt.range.clone(), + excerpt.range.context.clone(), ) }) } @@ -914,7 +946,7 @@ impl MultiBuffer { } cursor.item().map(|excerpt| { - let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let buffer_point = excerpt_start + offset - *cursor.start(); let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone(); @@ -943,7 +975,7 @@ impl MultiBuffer { if excerpt.has_trailing_newline { end_before_newline -= 1; } - let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start()); let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start()); let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone(); @@ -1234,7 +1266,7 @@ impl MultiBuffer { buffer .edits_since_in_range::( old_excerpt.buffer.version(), - old_excerpt.range.clone(), + old_excerpt.range.context.clone(), ) .map(|mut edit| { let excerpt_old_start = cursor.start().1; @@ -1384,7 +1416,9 @@ impl MultiBuffer { let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right); let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); - start_ix..end_ix + ExcerptRange { + context: start_ix..end_ix, + } }) .collect::>(); log::info!( @@ -1393,7 +1427,7 @@ impl MultiBuffer { ranges, ranges .iter() - .map(|range| &buffer_text[range.clone()]) + .map(|range| &buffer_text[range.context.clone()]) .collect::>() ); @@ -1465,7 +1499,7 @@ impl MultiBufferSnapshot { cursor.seek(&offset, Bias::Left, &()); let mut excerpt_chunks = cursor.item().map(|excerpt| { let end_before_footer = cursor.start() + excerpt.text_summary.bytes; - let start = excerpt.range.start.to_offset(&excerpt.buffer); + let start = excerpt.range.context.start.to_offset(&excerpt.buffer); let end = start + (cmp::min(offset, end_before_footer) - cursor.start()); excerpt.buffer.reversed_chunks_in_range(start..end) }); @@ -1476,7 +1510,7 @@ impl MultiBufferSnapshot { excerpt_chunks = Some( excerpt .buffer - .reversed_chunks_in_range(excerpt.range.clone()), + .reversed_chunks_in_range(excerpt.range.context.clone()), ); } @@ -1581,7 +1615,7 @@ impl MultiBufferSnapshot { let mut cursor = self.excerpts.cursor::(); cursor.seek(&offset, Bias::Right, &()); let overshoot = if let Some(excerpt) = cursor.item() { - let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let buffer_offset = excerpt .buffer .clip_offset(excerpt_start + (offset - cursor.start()), bias); @@ -1600,7 +1634,7 @@ impl MultiBufferSnapshot { let mut cursor = self.excerpts.cursor::(); cursor.seek(&point, Bias::Right, &()); let overshoot = if let Some(excerpt) = cursor.item() { - let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer); let buffer_point = excerpt .buffer .clip_point(excerpt_start + (point - cursor.start()), bias); @@ -1621,7 +1655,7 @@ impl MultiBufferSnapshot { let overshoot = if let Some(excerpt) = cursor.item() { let excerpt_start = excerpt .buffer - .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer)); + .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); let buffer_point = excerpt .buffer .clip_point_utf16(excerpt_start + (point - cursor.start()), bias); @@ -1690,8 +1724,8 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let (start_offset, start_point) = cursor.start(); let overshoot = offset - start_offset; - let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer); - let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer); + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); let buffer_point = excerpt .buffer .offset_to_point(excerpt_start_offset + overshoot); @@ -1711,8 +1745,8 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let (start_offset, start_point) = cursor.start(); let overshoot = offset - start_offset; - let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer); - let excerpt_start_point = excerpt.range.start.to_point_utf16(&excerpt.buffer); + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point_utf16(&excerpt.buffer); let buffer_point = excerpt .buffer .offset_to_point_utf16(excerpt_start_offset + overshoot); @@ -1732,8 +1766,9 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let (start_offset, start_point) = cursor.start(); let overshoot = point - start_offset; - let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer); - let excerpt_start_point_utf16 = excerpt.range.start.to_point_utf16(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); + let excerpt_start_point_utf16 = + excerpt.range.context.start.to_point_utf16(&excerpt.buffer); let buffer_point = excerpt .buffer .point_to_point_utf16(excerpt_start_point + overshoot); @@ -1753,8 +1788,8 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let (start_point, start_offset) = cursor.start(); let overshoot = point - start_point; - let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer); - let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer); + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer); let buffer_offset = excerpt .buffer .point_to_offset(excerpt_start_point + overshoot); @@ -1774,10 +1809,10 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { let (start_point, start_offset) = cursor.start(); let overshoot = point - start_point; - let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer); let excerpt_start_point = excerpt .buffer - .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer)); + .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); let buffer_offset = excerpt .buffer .point_utf16_to_offset(excerpt_start_point + overshoot); @@ -1811,8 +1846,8 @@ impl MultiBufferSnapshot { cursor.seek(&Point::new(row, 0), Bias::Right, &()); if let Some(excerpt) = cursor.item() { let overshoot = row - cursor.start().row; - let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer); - let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer); + let excerpt_end = excerpt.range.context.end.to_point(&excerpt.buffer); let buffer_row = excerpt_start.row + overshoot; let line_start = Point::new(buffer_row, 0); let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row)); @@ -1847,7 +1882,7 @@ impl MultiBufferSnapshot { end_before_newline -= 1; } - let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let start_in_excerpt = excerpt_start + (range.start - cursor.start()); let end_in_excerpt = excerpt_start + (cmp::min(end_before_newline, range.end) - cursor.start()); @@ -1881,7 +1916,7 @@ impl MultiBufferSnapshot { if let Some(excerpt) = cursor.item() { range.end = cmp::max(*cursor.start(), range.end); - let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let end_in_excerpt = excerpt_start + (range.end - cursor.start()); summary.add_assign( &excerpt @@ -1907,8 +1942,9 @@ impl MultiBufferSnapshot { let mut position = D::from_text_summary(&cursor.start().text); if let Some(excerpt) = cursor.item() { if excerpt.id == anchor.excerpt_id { - let excerpt_buffer_start = excerpt.range.start.summary::(&excerpt.buffer); - let excerpt_buffer_end = excerpt.range.end.summary::(&excerpt.buffer); + let excerpt_buffer_start = + excerpt.range.context.start.summary::(&excerpt.buffer); + let excerpt_buffer_end = excerpt.range.context.end.summary::(&excerpt.buffer); let buffer_position = cmp::min( excerpt_buffer_end, anchor.text_anchor.summary::(&excerpt.buffer), @@ -1954,8 +1990,10 @@ impl MultiBufferSnapshot { let position = D::from_text_summary(&cursor.start().text); if let Some(excerpt) = cursor.item() { if excerpt.id == *excerpt_id { - let excerpt_buffer_start = excerpt.range.start.summary::(&excerpt.buffer); - let excerpt_buffer_end = excerpt.range.end.summary::(&excerpt.buffer); + let excerpt_buffer_start = + excerpt.range.context.start.summary::(&excerpt.buffer); + let excerpt_buffer_end = + excerpt.range.context.end.summary::(&excerpt.buffer); summaries.extend( excerpt .buffer @@ -2036,10 +2074,14 @@ impl MultiBufferSnapshot { anchor = if let Some(excerpt) = next_excerpt { let mut text_anchor = excerpt .range + .context .start .bias(anchor.text_anchor.bias, &excerpt.buffer); - if text_anchor.cmp(&excerpt.range.end, &excerpt.buffer).is_gt() { - text_anchor = excerpt.range.end.clone(); + if text_anchor + .cmp(&excerpt.range.context.end, &excerpt.buffer) + .is_gt() + { + text_anchor = excerpt.range.context.end.clone(); } Anchor { buffer_id: Some(excerpt.buffer_id), @@ -2049,13 +2091,14 @@ impl MultiBufferSnapshot { } else if let Some(excerpt) = prev_excerpt { let mut text_anchor = excerpt .range + .context .end .bias(anchor.text_anchor.bias, &excerpt.buffer); if text_anchor - .cmp(&excerpt.range.start, &excerpt.buffer) + .cmp(&excerpt.range.context.start, &excerpt.buffer) .is_lt() { - text_anchor = excerpt.range.start.clone(); + text_anchor = excerpt.range.context.start.clone(); } Anchor { buffer_id: Some(excerpt.buffer_id), @@ -2106,7 +2149,7 @@ impl MultiBufferSnapshot { bias = Bias::Right; } - let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer); + let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let text_anchor = excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias)); Anchor { @@ -2239,8 +2282,11 @@ impl MultiBufferSnapshot { return None; } - let excerpt_buffer_start = - start_excerpt.range.start.to_offset(&start_excerpt.buffer); + let excerpt_buffer_start = start_excerpt + .range + .context + .start + .to_offset(&start_excerpt.buffer); let excerpt_buffer_end = excerpt_buffer_start + start_excerpt.text_summary.bytes; let start_in_buffer = @@ -2340,8 +2386,11 @@ impl MultiBufferSnapshot { return None; } - let excerpt_buffer_start = - start_excerpt.range.start.to_offset(&start_excerpt.buffer); + let excerpt_buffer_start = start_excerpt + .range + .context + .start + .to_offset(&start_excerpt.buffer); let excerpt_buffer_end = excerpt_buffer_start + start_excerpt.text_summary.bytes; let start_in_buffer = @@ -2427,7 +2476,8 @@ impl MultiBufferSnapshot { cursor .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id) .flat_map(move |excerpt| { - let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone(); + let mut query_range = + excerpt.range.context.start.clone()..excerpt.range.context.end.clone(); if excerpt.id == range.start.excerpt_id { query_range.start = range.start.text_anchor.clone(); } @@ -2619,13 +2669,14 @@ impl Excerpt { id: ExcerptId, buffer_id: usize, buffer: BufferSnapshot, - range: Range, + range: ExcerptRange, has_trailing_newline: bool, ) -> Self { Excerpt { id, - max_buffer_row: range.end.to_point(&buffer).row, - text_summary: buffer.text_summary_for_range::(range.to_offset(&buffer)), + max_buffer_row: range.context.end.to_point(&buffer).row, + text_summary: buffer + .text_summary_for_range::(range.context.to_offset(&buffer)), buffer_id, buffer, range, @@ -2638,7 +2689,7 @@ impl Excerpt { range: Range, language_aware: bool, ) -> ExcerptChunks<'a> { - let content_start = self.range.start.to_offset(&self.buffer); + let content_start = self.range.context.start.to_offset(&self.buffer); let chunks_start = content_start + range.start; let chunks_end = content_start + cmp::min(range.end, self.text_summary.bytes); @@ -2660,7 +2711,7 @@ impl Excerpt { } fn bytes_in_range(&self, range: Range) -> ExcerptBytes { - let content_start = self.range.start.to_offset(&self.buffer); + let content_start = self.range.context.start.to_offset(&self.buffer); let bytes_start = content_start + range.start; let bytes_end = content_start + cmp::min(range.end, self.text_summary.bytes); let footer_height = if self.has_trailing_newline @@ -2680,10 +2731,16 @@ impl Excerpt { } fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor { - if text_anchor.cmp(&self.range.start, &self.buffer).is_lt() { - self.range.start.clone() - } else if text_anchor.cmp(&self.range.end, &self.buffer).is_gt() { - self.range.end.clone() + if text_anchor + .cmp(&self.range.context.start, &self.buffer) + .is_lt() + { + self.range.context.start.clone() + } else if text_anchor + .cmp(&self.range.context.end, &self.buffer) + .is_gt() + { + self.range.context.end.clone() } else { text_anchor } @@ -2693,11 +2750,13 @@ impl Excerpt { Some(self.buffer_id) == anchor.buffer_id && self .range + .context .start .cmp(&anchor.text_anchor, &self.buffer) .is_le() && self .range + .context .end .cmp(&anchor.text_anchor, &self.buffer) .is_ge() @@ -2802,7 +2861,7 @@ impl<'a> MultiBufferRows<'a> { if let Some(excerpt) = self.excerpts.item() { let overshoot = row - self.excerpts.start().row; - let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer).row; + let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer).row; self.buffer_row_range.start = excerpt_start + overshoot; self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1; } @@ -2822,7 +2881,7 @@ impl<'a> Iterator for MultiBufferRows<'a> { self.excerpts.item()?; self.excerpts.next(&()); let excerpt = self.excerpts.item()?; - self.buffer_row_range.start = excerpt.range.start.to_point(&excerpt.buffer).row; + self.buffer_row_range.start = excerpt.range.context.start.to_point(&excerpt.buffer).row; self.buffer_row_range.end = self.buffer_row_range.start + excerpt.text_summary.lines.row + 1; } @@ -3079,7 +3138,13 @@ mod tests { let subscription = multibuffer.update(cx, |multibuffer, cx| { let subscription = multibuffer.subscribe(); - multibuffer.push_excerpts(buffer_1.clone(), [Point::new(1, 2)..Point::new(2, 5)], cx); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: Point::new(1, 2)..Point::new(2, 5), + }], + cx, + ); assert_eq!( subscription.consume().into_inner(), [Edit { @@ -3088,8 +3153,20 @@ mod tests { }] ); - multibuffer.push_excerpts(buffer_1.clone(), [Point::new(3, 3)..Point::new(4, 4)], cx); - multibuffer.push_excerpts(buffer_2.clone(), [Point::new(3, 1)..Point::new(3, 3)], cx); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: Point::new(3, 3)..Point::new(4, 4), + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: Point::new(3, 1)..Point::new(3, 3), + }], + cx, + ); assert_eq!( subscription.consume().into_inner(), [Edit { @@ -3253,7 +3330,7 @@ mod tests { boundary.row, boundary .buffer - .text_for_range(boundary.range) + .text_for_range(boundary.range.context) .collect::(), boundary.starts_new_buffer, ) @@ -3334,8 +3411,8 @@ mod tests { let buffer_2 = cx.add_model(|cx| Buffer::new(0, "efghi", cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); - multibuffer.push_excerpts(buffer_1.clone(), [0..4], cx); - multibuffer.push_excerpts(buffer_2.clone(), [0..5], cx); + multibuffer.push_excerpts(buffer_1.clone(), [ExcerptRange { context: 0..4 }], cx); + multibuffer.push_excerpts(buffer_2.clone(), [ExcerptRange { context: 0..5 }], cx); multibuffer }); let old_snapshot = multibuffer.read(cx).snapshot(cx); @@ -3385,7 +3462,7 @@ mod tests { buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], cx)); let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| { multibuffer - .push_excerpts(buffer_1.clone(), [0..7], cx) + .push_excerpts(buffer_1.clone(), [ExcerptRange { context: 0..7 }], cx) .pop() .unwrap() }); @@ -3397,7 +3474,15 @@ mod tests { let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| { multibuffer.remove_excerpts([&excerpt_id_1], cx); let mut ids = multibuffer - .push_excerpts(buffer_2.clone(), [0..4, 6..10, 12..16], cx) + .push_excerpts( + buffer_2.clone(), + [ + ExcerptRange { context: 0..4 }, + ExcerptRange { context: 6..10 }, + ExcerptRange { context: 12..16 }, + ], + cx, + ) .into_iter(); (ids.next().unwrap(), ids.next().unwrap()) }); @@ -3437,7 +3522,12 @@ mod tests { let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| { multibuffer.remove_excerpts([&excerpt_id_3], cx); multibuffer - .insert_excerpts_after(&excerpt_id_3, buffer_2.clone(), [5..8], cx) + .insert_excerpts_after( + &excerpt_id_3, + buffer_2.clone(), + [ExcerptRange { context: 5..8 }], + cx, + ) .pop() .unwrap() }); @@ -3584,7 +3674,9 @@ mod tests { .insert_excerpts_after( &prev_excerpt_id, buffer_handle.clone(), - [start_ix..end_ix], + [ExcerptRange { + context: start_ix..end_ix, + }], cx, ) .pop() @@ -3893,8 +3985,20 @@ mod tests { let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); let group_interval = multibuffer.read(cx).history.group_interval; multibuffer.update(cx, |multibuffer, cx| { - multibuffer.push_excerpts(buffer_1.clone(), [0..buffer_1.read(cx).len()], cx); - multibuffer.push_excerpts(buffer_2.clone(), [0..buffer_2.read(cx).len()], cx); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..buffer_1.read(cx).len(), + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: 0..buffer_2.read(cx).len(), + }], + cx, + ); }); let mut now = Instant::now(); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 26c260c4c3d69fbcbe399233c9cc1710ff844f98..ce36e63ad29aebc01891bc64f690c1026d784e46 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -485,7 +485,7 @@ impl BufferSearchBar { ); } else { for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) { - let excerpt_range = excerpt.range.to_offset(&excerpt.buffer); + let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer); let rope = excerpt.buffer.as_rope().slice(excerpt_range.clone()); ranges.extend(query.search(&rope).await.into_iter().map(|range| { let start = excerpt From 15b13fe51188fb550abb93fcee37cb9a21e542c0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 12:23:12 +0200 Subject: [PATCH 2/6] Introduce an optional `primary` field to `ExcerptRange` --- crates/diagnostics/src/diagnostics.rs | 1 + crates/editor/src/editor.rs | 10 ++++- crates/editor/src/movement.rs | 2 + crates/editor/src/multi_buffer.rs | 61 ++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 0ce451a401c201ad33141496df7634bef8bf1a16..2e7525cf1438f1f2cf393084d134f1ef39250f8e 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -351,6 +351,7 @@ impl ProjectDiagnosticsEditor { buffer.clone(), [ExcerptRange { context: excerpt_start..excerpt_end, + primary: Some(range.clone()), }], excerpts_cx, ) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2b283683e8fac67af7ac7a957305a3223a9c0bcd..580635a35b856212763a731821f1d77901f46984 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7721,6 +7721,7 @@ mod tests { toml_buffer.clone(), [ExcerptRange { context: Point::new(0, 0)..Point::new(2, 0), + primary: None, }], cx, ); @@ -7728,6 +7729,7 @@ mod tests { rust_buffer.clone(), [ExcerptRange { context: Point::new(0, 0)..Point::new(1, 0), + primary: None, }], cx, ); @@ -9602,9 +9604,11 @@ mod tests { [ ExcerptRange { context: Point::new(0, 0)..Point::new(0, 4), + primary: None, }, ExcerptRange { context: Point::new(1, 0)..Point::new(1, 4), + primary: None, }, ], cx, @@ -9645,7 +9649,7 @@ mod tests { cccc)"}); let excerpt_ranges = excerpt_ranges .into_iter() - .map(|context| ExcerptRange { context }); + .map(|context| ExcerptRange { context, primary: None }); let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); @@ -9701,9 +9705,11 @@ mod tests { [ ExcerptRange { context: Point::new(0, 0)..Point::new(1, 4), + primary: None, }, ExcerptRange { context: Point::new(1, 0)..Point::new(2, 4), + primary: None, }, ], cx, @@ -9789,9 +9795,11 @@ mod tests { [ ExcerptRange { context: Point::new(0, 0)..Point::new(1, 4), + primary: None, }, ExcerptRange { context: Point::new(1, 0)..Point::new(2, 4), + primary: None, }, ], cx, diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index c4e28ca7c871d8d3a0208810c0bdc811c5ce6d67..1b9a8db2280d2ec5d153feac8651226eabf6d84a 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -496,9 +496,11 @@ mod tests { [ ExcerptRange { context: Point::new(0, 0)..Point::new(1, 4), + primary: None, }, ExcerptRange { context: Point::new(2, 0)..Point::new(3, 2), + primary: None, }, ], cx, diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index fb3eb94c1d06d7a874421fd87e08a1fadb1d4e53..8651e9c435d47ddc1148d0fb14fe47566863da1d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -122,6 +122,7 @@ struct Excerpt { #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExcerptRange { pub context: Range, + pub primary: Option>, } #[derive(Clone, Debug, Default)] @@ -224,6 +225,7 @@ impl MultiBuffer { buffer, [ExcerptRange { context: text::Anchor::MIN..text::Anchor::MAX, + primary: None, }], cx, ); @@ -722,6 +724,7 @@ impl MultiBuffer { excerpt_ranges.push(ExcerptRange { context: excerpt_start..excerpt_end, + primary: Some(range), }); range_counts.push(ranges_in_excerpt); } @@ -819,6 +822,10 @@ impl MultiBuffer { let range = ExcerptRange { context: buffer_snapshot.anchor_before(&range.context.start) ..buffer_snapshot.anchor_after(&range.context.end), + primary: range.primary.map(|primary| { + buffer_snapshot.anchor_before(&primary.start) + ..buffer_snapshot.anchor_after(&primary.end) + }), }; let excerpt = Excerpt::new( id.clone(), @@ -1418,6 +1425,7 @@ impl MultiBuffer { let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left); ExcerptRange { context: start_ix..end_ix, + primary: None, } }) .collect::>(); @@ -3142,6 +3150,7 @@ mod tests { buffer_1.clone(), [ExcerptRange { context: Point::new(1, 2)..Point::new(2, 5), + primary: None, }], cx, ); @@ -3157,6 +3166,7 @@ mod tests { buffer_1.clone(), [ExcerptRange { context: Point::new(3, 3)..Point::new(4, 4), + primary: None, }], cx, ); @@ -3164,6 +3174,7 @@ mod tests { buffer_2.clone(), [ExcerptRange { context: Point::new(3, 1)..Point::new(3, 3), + primary: None, }], cx, ); @@ -3411,8 +3422,22 @@ mod tests { let buffer_2 = cx.add_model(|cx| Buffer::new(0, "efghi", cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); - multibuffer.push_excerpts(buffer_1.clone(), [ExcerptRange { context: 0..4 }], cx); - multibuffer.push_excerpts(buffer_2.clone(), [ExcerptRange { context: 0..5 }], cx); + multibuffer.push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..4, + primary: None, + }], + cx, + ); + multibuffer.push_excerpts( + buffer_2.clone(), + [ExcerptRange { + context: 0..5, + primary: None, + }], + cx, + ); multibuffer }); let old_snapshot = multibuffer.read(cx).snapshot(cx); @@ -3462,7 +3487,14 @@ mod tests { buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], cx)); let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| { multibuffer - .push_excerpts(buffer_1.clone(), [ExcerptRange { context: 0..7 }], cx) + .push_excerpts( + buffer_1.clone(), + [ExcerptRange { + context: 0..7, + primary: None, + }], + cx, + ) .pop() .unwrap() }); @@ -3477,9 +3509,18 @@ mod tests { .push_excerpts( buffer_2.clone(), [ - ExcerptRange { context: 0..4 }, - ExcerptRange { context: 6..10 }, - ExcerptRange { context: 12..16 }, + ExcerptRange { + context: 0..4, + primary: None, + }, + ExcerptRange { + context: 6..10, + primary: None, + }, + ExcerptRange { + context: 12..16, + primary: None, + }, ], cx, ) @@ -3525,7 +3566,10 @@ mod tests { .insert_excerpts_after( &excerpt_id_3, buffer_2.clone(), - [ExcerptRange { context: 5..8 }], + [ExcerptRange { + context: 5..8, + primary: None, + }], cx, ) .pop() @@ -3676,6 +3720,7 @@ mod tests { buffer_handle.clone(), [ExcerptRange { context: start_ix..end_ix, + primary: None, }], cx, ) @@ -3989,6 +4034,7 @@ mod tests { buffer_1.clone(), [ExcerptRange { context: 0..buffer_1.read(cx).len(), + primary: None, }], cx, ); @@ -3996,6 +4042,7 @@ mod tests { buffer_2.clone(), [ExcerptRange { context: 0..buffer_2.read(cx).len(), + primary: None, }], cx, ); From eda569d6b2cae6a3ff4cd398f5f1dc1881d1f0f2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 14:28:53 +0200 Subject: [PATCH 3/6] Snapshot file instead of path when creating a buffer snapshot --- crates/editor/src/display_map/block_map.rs | 2 +- crates/editor/src/element.rs | 3 ++- crates/language/src/buffer.rs | 24 +++++++++++----------- crates/project/src/project.rs | 8 ++++---- crates/project/src/worktree.rs | 4 ++-- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 013ef305b3e9aa44795c7d94ec08414c73ba36f6..43b28974a9792f8d823b180d7bcf5310492ec794 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -126,7 +126,7 @@ impl Debug for TransformBlock { Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(), Self::ExcerptHeader { buffer, .. } => f .debug_struct("ExcerptHeader") - .field("path", &buffer.path()) + .field("path", &buffer.file().map(|f| f.path())) .finish(), } } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 594c047d5b3e1119c775b43b90309209d2b92ef1..f3ecc6e7732312603c7f3f634bdbdffe5a062d1b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -838,7 +838,8 @@ impl EditorElement { let mut filename = None; let mut parent_path = None; - if let Some(path) = buffer.path() { + if let Some(file) = buffer.file() { + let path = file.path(); filename = path.file_name().map(|f| f.to_string_lossy().to_string()); parent_path = diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 38dbaa29fde63e8a9b42e0cdc6b0a39e9ce06d2d..de7425b76bf5962ea6b113bebbdd6e6ad5d53029 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -49,7 +49,7 @@ lazy_static! { pub struct Buffer { text: TextBuffer, - file: Option>, + file: Option>, saved_version: clock::Global, saved_mtime: SystemTime, language: Option>, @@ -72,7 +72,7 @@ pub struct Buffer { pub struct BufferSnapshot { text: text::BufferSnapshot, tree: Option, - path: Option>, + file: Option>, diagnostics: DiagnosticSet, diagnostics_update_count: usize, file_update_count: usize, @@ -152,7 +152,7 @@ pub enum Event { Closed, } -pub trait File { +pub trait File: Send + Sync { fn as_local(&self) -> Option<&dyn LocalFile>; fn is_local(&self) -> bool { @@ -306,7 +306,7 @@ impl Buffer { pub fn from_file>>( replica_id: ReplicaId, base_text: T, - file: Box, + file: Arc, cx: &mut ModelContext, ) -> Self { Self::build( @@ -322,7 +322,7 @@ impl Buffer { pub fn from_proto( replica_id: ReplicaId, message: proto::BufferState, - file: Option>, + file: Option>, cx: &mut ModelContext, ) -> Result { let buffer = TextBuffer::new( @@ -403,7 +403,7 @@ impl Buffer { self } - fn build(buffer: TextBuffer, file: Option>) -> Self { + fn build(buffer: TextBuffer, file: Option>) -> Self { let saved_mtime; if let Some(file) = file.as_ref() { saved_mtime = file.mtime(); @@ -438,7 +438,7 @@ impl Buffer { BufferSnapshot { text: self.text.snapshot(), tree: self.syntax_tree(), - path: self.file.as_ref().map(|f| f.path().clone()), + file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), diagnostics_update_count: self.diagnostics_update_count, @@ -496,7 +496,7 @@ impl Buffer { &mut self, version: clock::Global, mtime: SystemTime, - new_file: Option>, + new_file: Option>, cx: &mut ModelContext, ) { self.saved_mtime = mtime; @@ -550,7 +550,7 @@ impl Buffer { pub fn file_updated( &mut self, - new_file: Box, + new_file: Arc, cx: &mut ModelContext, ) -> Task<()> { let old_file = if let Some(file) = self.file.as_ref() { @@ -1980,8 +1980,8 @@ impl BufferSnapshot { self.selections_update_count } - pub fn path(&self) -> Option<&Arc> { - self.path.as_ref() + pub fn file(&self) -> Option<&Arc> { + self.file.as_ref() } pub fn file_update_count(&self) -> usize { @@ -1994,7 +1994,7 @@ impl Clone for BufferSnapshot { Self { text: self.text.clone(), tree: self.tree.clone(), - path: self.path.clone(), + file: self.file.clone(), remote_selections: self.remote_selections.clone(), diagnostics: self.diagnostics.clone(), selections_update_count: self.selections_update_count, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c502b1eccc4ea52083b13a05dda7de7d61901343..63a77f2642858d58e05a4dc7bfcefb1591c29a02 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4013,7 +4013,7 @@ impl Project { }) .log_err(); } - buffer.file_updated(Box::new(new_file), cx).detach(); + buffer.file_updated(Arc::new(new_file), cx).detach(); } }); } else { @@ -4565,7 +4565,7 @@ impl Project { .and_then(|b| b.upgrade(cx)) .ok_or_else(|| anyhow!("no such buffer"))?; buffer.update(cx, |buffer, cx| { - buffer.file_updated(Box::new(file), cx).detach(); + buffer.file_updated(Arc::new(file), cx).detach(); }); Ok(()) }) @@ -5089,8 +5089,8 @@ impl Project { anyhow!("no worktree found for id {}", file.worktree_id) })?; buffer_file = - Some(Box::new(File::from_proto(file, worktree.clone(), cx)?) - as Box); + Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?) + as Arc); buffer_worktree = Some(worktree); Ok::<_, anyhow::Error>(()) })?; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 599d6f0a2a5778856c8c267bd70f9522655d7e80..1007b43d75eb988815d64f2ca5e50d3b5ab2ce65 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -519,7 +519,7 @@ impl LocalWorktree { let (file, contents) = this .update(&mut cx, |t, cx| t.as_local().unwrap().load(&path, cx)) .await?; - Ok(cx.add_model(|cx| Buffer::from_file(0, contents, Box::new(file), cx))) + Ok(cx.add_model(|cx| Buffer::from_file(0, contents, Arc::new(file), cx))) }) } @@ -648,7 +648,7 @@ impl LocalWorktree { }; buffer_handle.update(&mut cx, |buffer, cx| { - buffer.did_save(version, file.mtime, Some(Box::new(file)), cx); + buffer.did_save(version, file.mtime, Some(Arc::new(file)), cx); }); Ok(()) From 5fdafbe8c9494446965d4b9102af68daec709724 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 15:02:52 +0200 Subject: [PATCH 4/6] Expose a unique `key: usize` in `ExcerptBoundary` --- crates/editor/src/multi_buffer.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 8651e9c435d47ddc1148d0fb14fe47566863da1d..de268d95b9ede3d34a8a2f6837ebb280ad8f687e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -31,6 +31,7 @@ use text::{ Edit, Point, PointUtf16, TextSummary, }; use theme::SyntaxTheme; +use util::post_inc; const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize]; @@ -40,6 +41,7 @@ pub struct MultiBuffer { snapshot: RefCell, buffers: RefCell>, used_excerpt_ids: SumTree, + next_excerpt_key: usize, subscriptions: Topic, singleton: bool, replica_id: ReplicaId, @@ -102,6 +104,7 @@ pub struct MultiBufferSnapshot { pub struct ExcerptBoundary { pub id: ExcerptId, + pub key: usize, pub row: u32, pub buffer: BufferSnapshot, pub range: ExcerptRange, @@ -111,6 +114,7 @@ pub struct ExcerptBoundary { #[derive(Clone)] struct Excerpt { id: ExcerptId, + key: usize, buffer_id: usize, buffer: BufferSnapshot, range: ExcerptRange, @@ -167,6 +171,7 @@ impl MultiBuffer { snapshot: Default::default(), buffers: Default::default(), used_excerpt_ids: Default::default(), + next_excerpt_key: Default::default(), subscriptions: Default::default(), singleton: false, replica_id, @@ -204,7 +209,8 @@ impl MultiBuffer { Self { snapshot: RefCell::new(self.snapshot.borrow().clone()), buffers: RefCell::new(buffers), - used_excerpt_ids: Default::default(), + used_excerpt_ids: self.used_excerpt_ids.clone(), + next_excerpt_key: self.next_excerpt_key, subscriptions: Default::default(), singleton: self.singleton, replica_id: self.replica_id, @@ -829,6 +835,7 @@ impl MultiBuffer { }; let excerpt = Excerpt::new( id.clone(), + post_inc(&mut self.next_excerpt_key), buffer_id, buffer_snapshot.clone(), range, @@ -1288,6 +1295,7 @@ impl MultiBuffer { new_excerpt = Excerpt::new( id.clone(), + old_excerpt.key, buffer_id, buffer.snapshot(), old_excerpt.range.clone(), @@ -2247,6 +2255,7 @@ impl MultiBufferSnapshot { let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id; let boundary = ExcerptBoundary { id: excerpt.id.clone(), + key: excerpt.key, row: cursor.start().1.row, buffer: excerpt.buffer.clone(), range: excerpt.range.clone(), @@ -2675,6 +2684,7 @@ impl History { impl Excerpt { fn new( id: ExcerptId, + key: usize, buffer_id: usize, buffer: BufferSnapshot, range: ExcerptRange, @@ -2682,6 +2692,7 @@ impl Excerpt { ) -> Self { Excerpt { id, + key, max_buffer_row: range.context.end.to_point(&buffer).row, text_summary: buffer .text_summary_for_range::(range.context.to_offset(&buffer)), From c2eaf6128e380e52d8487d42e952d909c051fa68 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 15:08:11 +0200 Subject: [PATCH 5/6] Move `ProjectDiagnosticsEditor::jump` to `Editor::jump` --- crates/diagnostics/src/diagnostics.rs | 27 +---------------- crates/editor/src/editor.rs | 43 +++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 2e7525cf1438f1f2cf393084d134f1ef39250f8e..a2f16fa0c4dde772a30de3c32a06c723d6164ad4 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -28,7 +28,7 @@ use std::{ path::PathBuf, sync::Arc, }; -use util::{ResultExt, TryFutureExt}; +use util::TryFutureExt; use workspace::{ItemHandle as _, ItemNavHistory, Workspace}; actions!(diagnostics, [Deploy]); @@ -39,7 +39,6 @@ const CONTEXT_LINE_COUNT: u32 = 1; pub fn init(cx: &mut MutableAppContext) { cx.add_action(ProjectDiagnosticsEditor::deploy); - cx.add_action(ProjectDiagnosticsEditor::jump); items::init(cx); } @@ -195,30 +194,6 @@ impl ProjectDiagnosticsEditor { } } - fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext) { - let editor = workspace.open_path(action.path.clone(), true, cx); - let position = action.position; - let anchor = action.anchor; - cx.spawn_weak(|_, mut cx| async move { - let editor = editor.await.log_err()?.downcast::()?; - editor.update(&mut cx, |editor, cx| { - let buffer = editor.buffer().read(cx).as_singleton()?; - let buffer = buffer.read(cx); - let cursor = if buffer.can_resolve(&anchor) { - anchor.to_point(buffer) - } else { - buffer.clip_point(position, Bias::Left) - }; - editor.change_selections(Some(Autoscroll::Newest), cx, |s| { - s.select_ranges([cursor..cursor]); - }); - Some(()) - })?; - Some(()) - }) - .detach() - } - fn update_excerpts(&mut self, language_server_id: Option, cx: &mut ViewContext) { let mut paths = Vec::new(); self.paths_to_update.retain(|path, server_id| { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 580635a35b856212763a731821f1d77901f46984..6e1b146bad58c129894362c4871142651c7d69b3 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -40,7 +40,7 @@ pub use multi_buffer::{ ToPoint, }; use ordered_float::OrderedFloat; -use project::{HoverBlock, Project, ProjectTransaction}; +use project::{HoverBlock, Project, ProjectPath, ProjectTransaction}; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; @@ -86,6 +86,13 @@ pub struct HoverAt { point: Option, } +#[derive(Clone, Debug, PartialEq)] +pub struct Jump { + path: ProjectPath, + position: Point, + anchor: language::Anchor, +} + #[derive(Clone, Deserialize, PartialEq)] pub struct Input(pub String); @@ -216,7 +223,7 @@ impl_actions!( ] ); -impl_internal_actions!(editor, [Scroll, Select, HoverAt]); +impl_internal_actions!(editor, [Scroll, Select, HoverAt, Jump]); enum DocumentHighlightRead {} enum DocumentHighlightWrite {} @@ -306,6 +313,7 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::hover); cx.add_action(Editor::hover_at); cx.add_action(Editor::open_excerpts); + cx.add_action(Editor::jump); cx.add_action(Editor::restart_language_server); cx.add_async_action(Editor::confirm_completion); cx.add_async_action(Editor::confirm_code_action); @@ -5826,6 +5834,30 @@ impl Editor { nav_history.borrow_mut().enable(); }); } + + fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext) { + let editor = workspace.open_path(action.path.clone(), true, cx); + let position = action.position; + let anchor = action.anchor; + cx.spawn_weak(|_, mut cx| async move { + let editor = editor.await.log_err()?.downcast::()?; + editor.update(&mut cx, |editor, cx| { + let buffer = editor.buffer().read(cx).as_singleton()?; + let buffer = buffer.read(cx); + let cursor = if buffer.can_resolve(&anchor) { + language::ToPoint::to_point(&anchor, buffer) + } else { + buffer.clip_point(position, Bias::Left) + }; + editor.change_selections(Some(Autoscroll::Newest), cx, |s| { + s.select_ranges([cursor..cursor]); + }); + Some(()) + })?; + Some(()) + }) + .detach() + } } impl EditorSnapshot { @@ -9647,9 +9679,10 @@ mod tests { [aaaa (bbbb] cccc)"}); - let excerpt_ranges = excerpt_ranges - .into_iter() - .map(|context| ExcerptRange { context, primary: None }); + let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange { + context, + primary: None, + }); let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx)); let multibuffer = cx.add_model(|cx| { let mut multibuffer = MultiBuffer::new(0); From 712d47d94fe065ef0be4e5100279e7ee099deb9c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Jun 2022 15:31:29 +0200 Subject: [PATCH 6/6] Render a "Jump to Buffer" icon on all excerpt headers --- crates/diagnostics/src/diagnostics.rs | 64 ++------------------ crates/editor/src/display_map/block_map.rs | 2 + crates/editor/src/editor.rs | 4 ++ crates/editor/src/element.rs | 68 ++++++++++++++++++++-- crates/language/src/buffer.rs | 4 +- crates/theme/src/theme.rs | 2 +- styles/src/styleTree/editor.ts | 16 ++--- 7 files changed, 86 insertions(+), 74 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index a2f16fa0c4dde772a30de3c32a06c723d6164ad4..c7d97c1ef35464b1d87e67138eb8740fd53920c7 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -9,13 +9,13 @@ use editor::{ ToOffset, }; use gpui::{ - actions, elements::*, fonts::TextStyle, impl_internal_actions, platform::CursorStyle, - serde_json, AnyViewHandle, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, - Task, View, ViewContext, ViewHandle, WeakViewHandle, + actions, elements::*, fonts::TextStyle, impl_internal_actions, serde_json, AnyViewHandle, + AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, + ViewHandle, WeakViewHandle, }; use language::{ Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, - SelectionGoal, ToPoint, + SelectionGoal, }; use project::{DiagnosticSummary, Project, ProjectPath}; use serde_json::json; @@ -342,20 +342,13 @@ impl ProjectDiagnosticsEditor { is_first_excerpt_for_group = false; let mut primary = group.entries[group.primary_ix].diagnostic.clone(); - let anchor = group.entries[group.primary_ix].range.start; - let position = anchor.to_point(&snapshot); primary.message = primary.message.split('\n').next().unwrap().to_string(); group_state.block_count += 1; blocks_to_add.push(BlockProperties { position: header_position, height: 2, - render: diagnostic_header_renderer( - primary, - path.clone(), - position, - anchor, - ), + render: diagnostic_header_renderer(primary), disposition: BlockDisposition::Above, }); } @@ -612,18 +605,10 @@ impl workspace::Item for ProjectDiagnosticsEditor { } } -fn diagnostic_header_renderer( - diagnostic: Diagnostic, - path: ProjectPath, - position: Point, - anchor: Anchor, -) -> RenderBlock { - enum JumpIcon {} - +fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock { let (message, highlights) = highlight_diagnostic_message(&diagnostic.message); Arc::new(move |cx| { let settings = cx.global::(); - let tooltip_style = settings.theme.tooltip.clone(); let theme = &settings.theme.editor; let style = theme.diagnostic_header.clone(); let font_size = (style.text_scale_factor * settings.buffer_font_size).round(); @@ -664,43 +649,6 @@ fn diagnostic_header_renderer( .aligned() .boxed() })) - .with_child( - MouseEventHandler::new::(diagnostic.group_id, cx, |state, _| { - let style = style.jump_icon.style_for(state, false); - Svg::new("icons/jump.svg") - .with_color(style.color) - .constrained() - .with_width(style.icon_width) - .aligned() - .contained() - .with_style(style.container) - .constrained() - .with_width(style.button_width) - .with_height(style.button_width) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click({ - let path = path.clone(); - move |_, _, cx| { - cx.dispatch_action(Jump { - path: path.clone(), - position, - anchor, - }); - } - }) - .with_tooltip( - diagnostic.group_id, - "Jump to diagnostic".to_string(), - Some(Box::new(editor::OpenExcerpts)), - tooltip_style, - cx, - ) - .aligned() - .flex_float() - .boxed(), - ) .contained() .with_style(style.container) .with_padding_left(x_padding) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 43b28974a9792f8d823b180d7bcf5310492ec794..36abef60e5a8cb00785569d52fbb592ac579d41b 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -97,6 +97,7 @@ struct Transform { pub enum TransformBlock { Custom(Arc), ExcerptHeader { + key: usize, buffer: BufferSnapshot, range: ExcerptRange, height: u8, @@ -359,6 +360,7 @@ impl BlockMap { .from_point(Point::new(excerpt_boundary.row, 0), Bias::Left) .row(), TransformBlock::ExcerptHeader { + key: excerpt_boundary.key, buffer: excerpt_boundary.buffer, range: excerpt_boundary.range, height: if excerpt_boundary.starts_new_buffer { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6e1b146bad58c129894362c4871142651c7d69b3..8cd2e8a96df45138b46a2dcd095cea6020025566 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5849,9 +5849,13 @@ impl Editor { } else { buffer.clip_point(position, Bias::Left) }; + + let nav_history = editor.nav_history.take(); editor.change_selections(Some(Autoscroll::Newest), cx, |s| { s.select_ranges([cursor..cursor]); }); + editor.nav_history = nav_history; + Some(()) })?; Some(()) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index f3ecc6e7732312603c7f3f634bdbdffe5a062d1b..d73569a03f8f0f9e3c35b4e9277ad9dacce088c6 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -27,6 +27,7 @@ use gpui::{ }; use json::json; use language::{Bias, DiagnosticSeverity, Selection}; +use project::ProjectPath; use settings::Settings; use smallvec::SmallVec; use std::{ @@ -795,6 +796,7 @@ impl EditorElement { return Default::default(); }; + let tooltip_style = cx.global::().theme.tooltip.clone(); let scroll_x = snapshot.scroll_position.x(); snapshot .blocks_in_range(rows.clone()) @@ -827,10 +829,60 @@ impl EditorElement { }) } TransformBlock::ExcerptHeader { + key, buffer, + range, starts_new_buffer, .. } => { + let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { + let jump_position = range + .primary + .as_ref() + .map_or(range.context.start, |primary| primary.start); + let jump_action = crate::Jump { + path: ProjectPath { + worktree_id: file.worktree_id(cx), + path: file.path.clone(), + }, + position: language::ToPoint::to_point(&jump_position, buffer), + anchor: jump_position, + }; + + enum JumpIcon {} + cx.render(&editor, |_, cx| { + MouseEventHandler::new::(*key, cx, |state, _| { + let style = style.jump_icon.style_for(state, false); + Svg::new("icons/jump.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .contained() + .with_style(style.container) + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click({ + move |_, _, cx| cx.dispatch_action(jump_action.clone()) + }) + .with_tooltip( + *key, + "Jump to Buffer".to_string(), + Some(Box::new(crate::OpenExcerpts)), + tooltip_style.clone(), + cx, + ) + .aligned() + .flex_float() + .boxed() + }) + }); + + let padding = gutter_padding + scroll_x * em_width; if *starts_new_buffer { let style = &self.style.diagnostic_path_header; let font_size = @@ -854,6 +906,7 @@ impl EditorElement { ) .contained() .with_style(style.filename.container) + .aligned() .boxed(), ) .with_children(parent_path.map(|path| { @@ -863,20 +916,25 @@ impl EditorElement { ) .contained() .with_style(style.path.container) + .aligned() .boxed() })) - .aligned() - .left() + .with_children(jump_icon) .contained() .with_style(style.container) - .with_padding_left(gutter_padding + scroll_x * em_width) + .with_padding_left(padding) + .with_padding_right(padding) .expanded() .named("path header block") } else { let text_style = self.style.text.clone(); - Label::new("…".to_string(), text_style) + Flex::row() + .with_child(Label::new("…".to_string(), text_style).boxed()) + .with_children(jump_icon) .contained() - .with_padding_left(gutter_padding + scroll_x * em_width) + .with_padding_left(padding) + .with_padding_right(padding) + .expanded() .named("collapsed context") } } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index de7425b76bf5962ea6b113bebbdd6e6ad5d53029..425bff2242dd9fa7f4c8494d363d2e98a0a4f13b 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1980,8 +1980,8 @@ impl BufferSnapshot { self.selections_update_count } - pub fn file(&self) -> Option<&Arc> { - self.file.as_ref() + pub fn file(&self) -> Option<&dyn File> { + self.file.as_deref() } pub fn file_update_count(&self) -> usize { diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 8e282100d4b65d2d8f6ad959fe80299fce377e58..80e9a8619f3b3b251fefb5d2ccfdfea7ae74e124 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -454,6 +454,7 @@ pub struct Editor { pub code_actions_indicator: Color, pub unnecessary_code_fade: f32, pub hover_popover: HoverPopover, + pub jump_icon: Interactive, } #[derive(Clone, Deserialize, Default)] @@ -473,7 +474,6 @@ pub struct DiagnosticHeader { pub code: ContainedText, pub text_scale_factor: f32, pub icon_width_factor: f32, - pub jump_icon: Interactive, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 14e7f9449fa1b321d88870a34a037cccbaeefbb5..9637e969d188ef6e32f58f7906abd49bb62fc7e4 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -101,14 +101,6 @@ export default function editor(theme: Theme) { background: backgroundColor(theme, 300), iconWidthFactor: 1.5, textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these. - jumpIcon: { - color: iconColor(theme, "primary"), - iconWidth: 10, - buttonWidth: 10, - hover: { - color: iconColor(theme, "active") - } - }, border: border(theme, "secondary", { bottom: true, top: true, @@ -147,6 +139,14 @@ export default function editor(theme: Theme) { invalidInformationDiagnostic: diagnostic(theme, "muted"), invalidWarningDiagnostic: diagnostic(theme, "muted"), hover_popover: hoverPopover(theme), + jumpIcon: { + color: iconColor(theme, "primary"), + iconWidth: 10, + buttonWidth: 10, + hover: { + color: iconColor(theme, "active") + } + }, syntax, }; }