Merge pull request #1142 from zed-industries/multibuffer-jump-to
Show "Jump to Buffer" icon on every excerpt header
This commit is contained in:
@@ -5,16 +5,17 @@ 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,
|
||||
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;
|
||||
@@ -27,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]);
|
||||
@@ -38,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);
|
||||
}
|
||||
|
||||
@@ -194,30 +194,6 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
}
|
||||
|
||||
fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
|
||||
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>()?;
|
||||
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<usize>, cx: &mut ViewContext<Self>) {
|
||||
let mut paths = Vec::new();
|
||||
self.paths_to_update.retain(|path, server_id| {
|
||||
@@ -348,7 +324,10 @@ impl ProjectDiagnosticsEditor {
|
||||
.insert_excerpts_after(
|
||||
&prev_excerpt_id,
|
||||
buffer.clone(),
|
||||
[excerpt_start..excerpt_end],
|
||||
[ExcerptRange {
|
||||
context: excerpt_start..excerpt_end,
|
||||
primary: Some(range.clone()),
|
||||
}],
|
||||
excerpts_cx,
|
||||
)
|
||||
.pop()
|
||||
@@ -363,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,
|
||||
});
|
||||
}
|
||||
@@ -633,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::<Settings>();
|
||||
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();
|
||||
@@ -685,43 +649,6 @@ fn diagnostic_header_renderer(
|
||||
.aligned()
|
||||
.boxed()
|
||||
}))
|
||||
.with_child(
|
||||
MouseEventHandler::new::<JumpIcon, _, _>(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)
|
||||
|
||||
@@ -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};
|
||||
@@ -97,8 +97,9 @@ struct Transform {
|
||||
pub enum TransformBlock {
|
||||
Custom(Arc<Block>),
|
||||
ExcerptHeader {
|
||||
key: usize,
|
||||
buffer: BufferSnapshot,
|
||||
range: Range<text::Anchor>,
|
||||
range: ExcerptRange<text::Anchor>,
|
||||
height: u8,
|
||||
starts_new_buffer: bool,
|
||||
},
|
||||
@@ -126,7 +127,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(),
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
+78
-13
@@ -36,10 +36,11 @@ 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};
|
||||
use project::{HoverBlock, Project, ProjectPath, ProjectTransaction};
|
||||
use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
@@ -85,6 +86,13 @@ pub struct HoverAt {
|
||||
point: Option<DisplayPoint>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Jump {
|
||||
path: ProjectPath,
|
||||
position: Point,
|
||||
anchor: language::Anchor,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, PartialEq)]
|
||||
pub struct Input(pub String);
|
||||
|
||||
@@ -215,7 +223,7 @@ impl_actions!(
|
||||
]
|
||||
);
|
||||
|
||||
impl_internal_actions!(editor, [Scroll, Select, HoverAt]);
|
||||
impl_internal_actions!(editor, [Scroll, Select, HoverAt, Jump]);
|
||||
|
||||
enum DocumentHighlightRead {}
|
||||
enum DocumentHighlightWrite {}
|
||||
@@ -305,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);
|
||||
@@ -2833,11 +2842,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;
|
||||
}
|
||||
@@ -5825,6 +5834,34 @@ impl Editor {
|
||||
nav_history.borrow_mut().enable();
|
||||
});
|
||||
}
|
||||
|
||||
fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
|
||||
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>()?;
|
||||
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)
|
||||
};
|
||||
|
||||
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(())
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
}
|
||||
|
||||
impl EditorSnapshot {
|
||||
@@ -7718,12 +7755,18 @@ 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),
|
||||
primary: None,
|
||||
}],
|
||||
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),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer
|
||||
@@ -9595,8 +9638,14 @@ 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),
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(1, 0)..Point::new(1, 4),
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
);
|
||||
@@ -9634,6 +9683,10 @@ mod tests {
|
||||
[aaaa
|
||||
(bbbb]
|
||||
cccc)"});
|
||||
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);
|
||||
@@ -9687,8 +9740,14 @@ 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),
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(1, 0)..Point::new(2, 4),
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
)
|
||||
@@ -9771,8 +9830,14 @@ 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),
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(1, 0)..Point::new(2, 4),
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -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::<Settings>().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::<JumpIcon, _, _>(*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 =
|
||||
@@ -838,7 +890,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 =
|
||||
@@ -853,6 +906,7 @@ impl EditorElement {
|
||||
)
|
||||
.contained()
|
||||
.with_style(style.filename.container)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
)
|
||||
.with_children(parent_path.map(|path| {
|
||||
@@ -862,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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,14 @@ 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),
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: Point::new(2, 0)..Point::new(3, 2),
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -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<MultiBufferSnapshot>,
|
||||
buffers: RefCell<HashMap<usize, BufferState>>,
|
||||
used_excerpt_ids: SumTree<ExcerptId>,
|
||||
next_excerpt_key: usize,
|
||||
subscriptions: Topic,
|
||||
singleton: bool,
|
||||
replica_id: ReplicaId,
|
||||
@@ -102,23 +104,31 @@ pub struct MultiBufferSnapshot {
|
||||
|
||||
pub struct ExcerptBoundary {
|
||||
pub id: ExcerptId,
|
||||
pub key: usize,
|
||||
pub row: u32,
|
||||
pub buffer: BufferSnapshot,
|
||||
pub range: Range<text::Anchor>,
|
||||
pub range: ExcerptRange<text::Anchor>,
|
||||
pub starts_new_buffer: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Excerpt {
|
||||
id: ExcerptId,
|
||||
key: usize,
|
||||
buffer_id: usize,
|
||||
buffer: BufferSnapshot,
|
||||
range: Range<text::Anchor>,
|
||||
range: ExcerptRange<text::Anchor>,
|
||||
max_buffer_row: u32,
|
||||
text_summary: TextSummary,
|
||||
has_trailing_newline: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ExcerptRange<T> {
|
||||
pub context: Range<T>,
|
||||
pub primary: Option<Range<T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ExcerptSummary {
|
||||
excerpt_id: ExcerptId,
|
||||
@@ -161,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,
|
||||
@@ -198,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,
|
||||
@@ -215,7 +227,14 @@ impl MultiBuffer {
|
||||
pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> 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,
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
this.snapshot.borrow_mut().singleton = true;
|
||||
this
|
||||
}
|
||||
@@ -343,8 +362,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 +375,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 +388,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 +419,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 +559,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 +680,7 @@ impl MultiBuffer {
|
||||
pub fn push_excerpts<O>(
|
||||
&mut self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
ranges: impl IntoIterator<Item = Range<O>>,
|
||||
ranges: impl IntoIterator<Item = ExcerptRange<O>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Vec<ExcerptId>
|
||||
where
|
||||
@@ -692,7 +728,10 @@ impl MultiBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
excerpt_ranges.push(excerpt_start..excerpt_end);
|
||||
excerpt_ranges.push(ExcerptRange {
|
||||
context: excerpt_start..excerpt_end,
|
||||
primary: Some(range),
|
||||
});
|
||||
range_counts.push(ranges_in_excerpt);
|
||||
}
|
||||
|
||||
@@ -722,7 +761,7 @@ impl MultiBuffer {
|
||||
&mut self,
|
||||
prev_excerpt_id: &ExcerptId,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
ranges: impl IntoIterator<Item = Range<O>>,
|
||||
ranges: impl IntoIterator<Item = ExcerptRange<O>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Vec<ExcerptId>
|
||||
where
|
||||
@@ -786,10 +825,17 @@ 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),
|
||||
primary: range.primary.map(|primary| {
|
||||
buffer_snapshot.anchor_before(&primary.start)
|
||||
..buffer_snapshot.anchor_after(&primary.end)
|
||||
}),
|
||||
};
|
||||
let excerpt = Excerpt::new(
|
||||
id.clone(),
|
||||
post_inc(&mut self.next_excerpt_key),
|
||||
buffer_id,
|
||||
buffer_snapshot.clone(),
|
||||
range,
|
||||
@@ -846,7 +892,7 @@ impl MultiBuffer {
|
||||
&self,
|
||||
buffer: &ModelHandle<Buffer>,
|
||||
cx: &AppContext,
|
||||
) -> Vec<(ExcerptId, Range<text::Anchor>)> {
|
||||
) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
|
||||
let mut excerpts = Vec::new();
|
||||
let snapshot = self.read(cx);
|
||||
let buffers = self.buffers.borrow();
|
||||
@@ -894,7 +940,7 @@ impl MultiBuffer {
|
||||
.unwrap()
|
||||
.buffer
|
||||
.clone(),
|
||||
excerpt.range.clone(),
|
||||
excerpt.range.context.clone(),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -914,7 +960,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 +989,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 +1280,7 @@ impl MultiBuffer {
|
||||
buffer
|
||||
.edits_since_in_range::<usize>(
|
||||
old_excerpt.buffer.version(),
|
||||
old_excerpt.range.clone(),
|
||||
old_excerpt.range.context.clone(),
|
||||
)
|
||||
.map(|mut edit| {
|
||||
let excerpt_old_start = cursor.start().1;
|
||||
@@ -1249,6 +1295,7 @@ impl MultiBuffer {
|
||||
|
||||
new_excerpt = Excerpt::new(
|
||||
id.clone(),
|
||||
old_excerpt.key,
|
||||
buffer_id,
|
||||
buffer.snapshot(),
|
||||
old_excerpt.range.clone(),
|
||||
@@ -1384,7 +1431,10 @@ 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,
|
||||
primary: None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
log::info!(
|
||||
@@ -1393,7 +1443,7 @@ impl MultiBuffer {
|
||||
ranges,
|
||||
ranges
|
||||
.iter()
|
||||
.map(|range| &buffer_text[range.clone()])
|
||||
.map(|range| &buffer_text[range.context.clone()])
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
@@ -1465,7 +1515,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 +1526,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 +1631,7 @@ impl MultiBufferSnapshot {
|
||||
let mut cursor = self.excerpts.cursor::<usize>();
|
||||
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 +1650,7 @@ impl MultiBufferSnapshot {
|
||||
let mut cursor = self.excerpts.cursor::<Point>();
|
||||
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 +1671,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 +1740,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 +1761,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 +1782,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 +1804,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 +1825,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 +1862,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 +1898,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 +1932,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 +1958,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::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_end = excerpt.range.end.summary::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_start =
|
||||
excerpt.range.context.start.summary::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
|
||||
let buffer_position = cmp::min(
|
||||
excerpt_buffer_end,
|
||||
anchor.text_anchor.summary::<D>(&excerpt.buffer),
|
||||
@@ -1954,8 +2006,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::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_end = excerpt.range.end.summary::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_start =
|
||||
excerpt.range.context.start.summary::<D>(&excerpt.buffer);
|
||||
let excerpt_buffer_end =
|
||||
excerpt.range.context.end.summary::<D>(&excerpt.buffer);
|
||||
summaries.extend(
|
||||
excerpt
|
||||
.buffer
|
||||
@@ -2036,10 +2090,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 +2107,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 +2165,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 {
|
||||
@@ -2196,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(),
|
||||
@@ -2239,8 +2299,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 +2403,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 +2493,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();
|
||||
}
|
||||
@@ -2617,15 +2684,18 @@ impl History {
|
||||
impl Excerpt {
|
||||
fn new(
|
||||
id: ExcerptId,
|
||||
key: usize,
|
||||
buffer_id: usize,
|
||||
buffer: BufferSnapshot,
|
||||
range: Range<text::Anchor>,
|
||||
range: ExcerptRange<text::Anchor>,
|
||||
has_trailing_newline: bool,
|
||||
) -> Self {
|
||||
Excerpt {
|
||||
id,
|
||||
max_buffer_row: range.end.to_point(&buffer).row,
|
||||
text_summary: buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer)),
|
||||
key,
|
||||
max_buffer_row: range.context.end.to_point(&buffer).row,
|
||||
text_summary: buffer
|
||||
.text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
|
||||
buffer_id,
|
||||
buffer,
|
||||
range,
|
||||
@@ -2638,7 +2708,7 @@ impl Excerpt {
|
||||
range: Range<usize>,
|
||||
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 +2730,7 @@ impl Excerpt {
|
||||
}
|
||||
|
||||
fn bytes_in_range(&self, range: Range<usize>) -> 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 +2750,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 +2769,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 +2880,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 +2900,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 +3157,14 @@ 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),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
assert_eq!(
|
||||
subscription.consume().into_inner(),
|
||||
[Edit {
|
||||
@@ -3088,8 +3173,22 @@ 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),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_2.clone(),
|
||||
[ExcerptRange {
|
||||
context: Point::new(3, 1)..Point::new(3, 3),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
assert_eq!(
|
||||
subscription.consume().into_inner(),
|
||||
[Edit {
|
||||
@@ -3253,7 +3352,7 @@ mod tests {
|
||||
boundary.row,
|
||||
boundary
|
||||
.buffer
|
||||
.text_for_range(boundary.range)
|
||||
.text_for_range(boundary.range.context)
|
||||
.collect::<String>(),
|
||||
boundary.starts_new_buffer,
|
||||
)
|
||||
@@ -3334,8 +3433,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(), [0..4], cx);
|
||||
multibuffer.push_excerpts(buffer_2.clone(), [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);
|
||||
@@ -3385,7 +3498,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(), [0..7], cx)
|
||||
.push_excerpts(
|
||||
buffer_1.clone(),
|
||||
[ExcerptRange {
|
||||
context: 0..7,
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.pop()
|
||||
.unwrap()
|
||||
});
|
||||
@@ -3397,7 +3517,24 @@ 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,
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: 6..10,
|
||||
primary: None,
|
||||
},
|
||||
ExcerptRange {
|
||||
context: 12..16,
|
||||
primary: None,
|
||||
},
|
||||
],
|
||||
cx,
|
||||
)
|
||||
.into_iter();
|
||||
(ids.next().unwrap(), ids.next().unwrap())
|
||||
});
|
||||
@@ -3437,7 +3574,15 @@ 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,
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.pop()
|
||||
.unwrap()
|
||||
});
|
||||
@@ -3584,7 +3729,10 @@ mod tests {
|
||||
.insert_excerpts_after(
|
||||
&prev_excerpt_id,
|
||||
buffer_handle.clone(),
|
||||
[start_ix..end_ix],
|
||||
[ExcerptRange {
|
||||
context: start_ix..end_ix,
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
)
|
||||
.pop()
|
||||
@@ -3893,8 +4041,22 @@ 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(),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
multibuffer.push_excerpts(
|
||||
buffer_2.clone(),
|
||||
[ExcerptRange {
|
||||
context: 0..buffer_2.read(cx).len(),
|
||||
primary: None,
|
||||
}],
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
let mut now = Instant::now();
|
||||
|
||||
@@ -49,7 +49,7 @@ lazy_static! {
|
||||
|
||||
pub struct Buffer {
|
||||
text: TextBuffer,
|
||||
file: Option<Box<dyn File>>,
|
||||
file: Option<Arc<dyn File>>,
|
||||
saved_version: clock::Global,
|
||||
saved_mtime: SystemTime,
|
||||
language: Option<Arc<Language>>,
|
||||
@@ -72,7 +72,7 @@ pub struct Buffer {
|
||||
pub struct BufferSnapshot {
|
||||
text: text::BufferSnapshot,
|
||||
tree: Option<Tree>,
|
||||
path: Option<Arc<Path>>,
|
||||
file: Option<Arc<dyn File>>,
|
||||
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<T: Into<Arc<str>>>(
|
||||
replica_id: ReplicaId,
|
||||
base_text: T,
|
||||
file: Box<dyn File>,
|
||||
file: Arc<dyn File>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
Self::build(
|
||||
@@ -322,7 +322,7 @@ impl Buffer {
|
||||
pub fn from_proto(
|
||||
replica_id: ReplicaId,
|
||||
message: proto::BufferState,
|
||||
file: Option<Box<dyn File>>,
|
||||
file: Option<Arc<dyn File>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<Self> {
|
||||
let buffer = TextBuffer::new(
|
||||
@@ -403,7 +403,7 @@ impl Buffer {
|
||||
self
|
||||
}
|
||||
|
||||
fn build(buffer: TextBuffer, file: Option<Box<dyn File>>) -> Self {
|
||||
fn build(buffer: TextBuffer, file: Option<Arc<dyn File>>) -> 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<Box<dyn File>>,
|
||||
new_file: Option<Arc<dyn File>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
self.saved_mtime = mtime;
|
||||
@@ -550,7 +550,7 @@ impl Buffer {
|
||||
|
||||
pub fn file_updated(
|
||||
&mut self,
|
||||
new_file: Box<dyn File>,
|
||||
new_file: Arc<dyn File>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> 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<Path>> {
|
||||
self.path.as_ref()
|
||||
pub fn file(&self) -> Option<&dyn File> {
|
||||
self.file.as_deref()
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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<dyn language::File>);
|
||||
Some(Arc::new(File::from_proto(file, worktree.clone(), cx)?)
|
||||
as Arc<dyn language::File>);
|
||||
buffer_worktree = Some(worktree);
|
||||
Ok::<_, anyhow::Error>(())
|
||||
})?;
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<IconButton>,
|
||||
}
|
||||
|
||||
#[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<IconButton>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Default)]
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user