Cargo.lock 🔗
@@ -4640,7 +4640,6 @@ dependencies = [
"sysinfo 0.37.2",
"task",
"tasks_ui",
- "telemetry",
"terminal_view",
"text",
"theme",
Cole Miller and Conrad Irwin created
Paving the way to remove `ExcerptId`. Done in this PR:
- Unshipped the stack trace view
- Get rid of `push_excerpts`
- Get rid of some callers of `remove_excerpts`
We still need to remove some calls to `remove_excerpts` and other APIs,
especially in `randomly_edit_excerpts` and collaboration.
Release Notes:
- The stack trace multibuffer view has been removed.
---------
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Cargo.lock | 1
crates/agent_ui/src/inline_assistant.rs | 19
crates/copilot/src/copilot_edit_prediction_delegate.rs | 131 +
crates/debugger_ui/Cargo.toml | 1
crates/debugger_ui/src/debugger_ui.rs | 40
crates/debugger_ui/src/session.rs | 37
crates/debugger_ui/src/session/running/stack_frame_list.rs | 14
crates/debugger_ui/src/stack_trace_view.rs | 458 ------
crates/diagnostics/src/diagnostics.rs | 12
crates/edit_prediction_ui/src/rate_prediction_modal.rs | 13
crates/editor/src/bracket_colorization.rs | 42
crates/editor/src/display_map/block_map.rs | 95
crates/editor/src/editor_tests.rs | 560 ++++---
crates/editor/src/inlays/inlay_hints.rs | 85
crates/editor/src/jsx_tag_auto_close.rs | 23
crates/editor/src/movement.rs | 13
crates/editor/src/semantic_tokens.rs | 210 --
crates/editor/src/test/editor_test_context.rs | 22
crates/git_ui/src/text_diff_view.rs | 6
crates/multi_buffer/src/multi_buffer.rs | 42
crates/multi_buffer/src/multi_buffer_tests.rs | 351 +++-
crates/multi_buffer/src/path_key.rs | 28
crates/search/src/buffer_search.rs | 14
23 files changed, 846 insertions(+), 1,371 deletions(-)
@@ -4640,7 +4640,6 @@ dependencies = [
"sysinfo 0.37.2",
"task",
"tasks_ui",
- "telemetry",
"terminal_view",
"text",
"theme",
@@ -26,8 +26,8 @@ use editor::RowExt;
use editor::SelectionEffects;
use editor::scroll::ScrollOffset;
use editor::{
- Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange,
- HighlightKey, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,
+ Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, HighlightKey,
+ MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint,
actions::SelectAll,
display_map::{
BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, EditorMargins,
@@ -1495,11 +1495,11 @@ impl InlineAssistant {
let mut new_blocks = Vec::new();
for (new_row, old_row_range) in deleted_row_ranges {
- let (_, buffer_start) = old_snapshot
- .point_to_buffer_offset(Point::new(*old_row_range.start(), 0))
+ let (_, start, _) = old_snapshot
+ .point_to_buffer_point(Point::new(*old_row_range.start(), 0))
.unwrap();
- let (_, buffer_end) = old_snapshot
- .point_to_buffer_offset(Point::new(
+ let (_, end, _) = old_snapshot
+ .point_to_buffer_point(Point::new(
*old_row_range.end(),
old_snapshot.line_len(MultiBufferRow(*old_row_range.end())),
))
@@ -1509,10 +1509,11 @@ impl InlineAssistant {
let multi_buffer =
cx.new(|_| MultiBuffer::without_headers(language::Capability::ReadOnly));
multi_buffer.update(cx, |multi_buffer, cx| {
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_buffer(
old_buffer.clone(),
- // todo(lw): buffer_start and buffer_end might come from different snapshots!
- Some(ExcerptRange::new(buffer_start..buffer_end)),
+ // todo(lw): start and end might come from different snapshots!
+ [start..end],
+ 0,
cx,
);
});
@@ -233,8 +233,8 @@ mod tests {
use super::*;
use edit_prediction_types::EditPredictionGranularity;
use editor::{
- Editor, ExcerptRange, MultiBuffer, MultiBufferOffset, SelectionEffects,
- test::editor_lsp_test_context::EditorLspTestContext,
+ Editor, MultiBuffer, MultiBufferOffset, PathKey, SelectionEffects,
+ test::{editor_content_with_blocks, editor_lsp_test_context::EditorLspTestContext},
};
use fs::FakeFs;
use futures::StreamExt;
@@ -685,32 +685,32 @@ mod tests {
let buffer_2 = cx.new(|cx| Buffer::local("c = 3\nd = 4\n", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
multibuffer
});
- let editor =
- cx.add_window(|window, cx| Editor::for_multibuffer(multibuffer, None, window, cx));
- editor
- .update(cx, |editor, window, cx| {
- use gpui::Focusable;
- window.focus(&editor.focus_handle(cx), cx);
- })
- .unwrap();
+ let (editor, cx) =
+ cx.add_window_view(|window, cx| Editor::for_multibuffer(multibuffer, None, window, cx));
+ editor.update_in(cx, |editor, window, cx| {
+ use gpui::Focusable;
+ window.focus(&editor.focus_handle(cx), cx);
+ });
let copilot_provider = cx.new(|_| CopilotEditPredictionDelegate::new(copilot));
- editor
- .update(cx, |editor, window, cx| {
- editor.set_edit_prediction_provider(Some(copilot_provider), window, cx)
- })
- .unwrap();
+ editor.update_in(cx, |editor, window, cx| {
+ editor.set_edit_prediction_provider(Some(copilot_provider), window, cx)
+ });
handle_copilot_completion_request(
&copilot_lsp,
@@ -724,7 +724,7 @@ mod tests {
},
}],
);
- _ = editor.update(cx, |editor, window, cx| {
+ _ = editor.update_in(cx, |editor, window, cx| {
// Ensure copilot suggestions are shown for the first excerpt.
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([Point::new(1, 5)..Point::new(1, 5)])
@@ -732,14 +732,22 @@ mod tests {
editor.show_edit_prediction(&Default::default(), window, cx);
});
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
- _ = editor.update(cx, |editor, _, cx| {
+ _ = editor.update_in(cx, |editor, _, _| {
assert!(editor.has_active_edit_prediction());
- assert_eq!(
- editor.display_text(cx),
- "\n\na = 1\nb = 2 + a\n\n\n\nc = 3\nd = 4\n"
- );
- assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
});
+ pretty_assertions::assert_eq!(
+ editor_content_with_blocks(&editor, cx),
+ indoc! { "
+ § <no file>
+ § -----
+ a = 1
+ b = 2 + a
+ § <no file>
+ § -----
+ c = 3
+ d = 4"
+ }
+ );
handle_copilot_completion_request(
&copilot_lsp,
@@ -753,38 +761,61 @@ mod tests {
},
}],
);
- _ = editor.update(cx, |editor, window, cx| {
+ _ = editor.update_in(cx, |editor, window, cx| {
// Move to another excerpt, ensuring the suggestion gets cleared.
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([Point::new(4, 5)..Point::new(4, 5)])
});
assert!(!editor.has_active_edit_prediction());
- assert_eq!(
- editor.display_text(cx),
- "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4\n"
- );
- assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4\n");
-
+ });
+ pretty_assertions::assert_eq!(
+ editor_content_with_blocks(&editor, cx),
+ indoc! { "
+ § <no file>
+ § -----
+ a = 1
+ b = 2
+ § <no file>
+ § -----
+ c = 3
+ d = 4"}
+ );
+ editor.update_in(cx, |editor, window, cx| {
// Type a character, ensuring we don't even try to interpolate the previous suggestion.
editor.handle_input(" ", window, cx);
assert!(!editor.has_active_edit_prediction());
- assert_eq!(
- editor.display_text(cx),
- "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 \n"
- );
- assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
});
+ pretty_assertions::assert_eq!(
+ editor_content_with_blocks(&editor, cx),
+ indoc! {"
+ § <no file>
+ § -----
+ a = 1
+ b = 2
+ § <no file>
+ § -----
+ c = 3
+ d = 4\x20"
+ },
+ );
// Ensure the new suggestion is displayed when the debounce timeout expires.
executor.advance_clock(COPILOT_DEBOUNCE_TIMEOUT);
- _ = editor.update(cx, |editor, _, cx| {
+ _ = editor.update(cx, |editor, _| {
assert!(editor.has_active_edit_prediction());
- assert_eq!(
- editor.display_text(cx),
- "\n\na = 1\nb = 2\n\n\n\nc = 3\nd = 4 + c\n"
- );
- assert_eq!(editor.text(cx), "a = 1\nb = 2\n\nc = 3\nd = 4 \n");
});
+ assert_eq!(
+ editor_content_with_blocks(&editor, cx),
+ indoc! {"
+ § <no file>
+ § -----
+ a = 1
+ b = 2
+ § <no file>
+ § -----
+ c = 3
+ d = 4 + c"}
+ );
}
#[gpui::test]
@@ -947,14 +978,18 @@ mod tests {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
private_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
public_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(6, 0))],
+ [Point::new(0, 0)..Point::new(6, 0)],
+ 0,
cx,
);
multibuffer
@@ -64,7 +64,6 @@ settings.workspace = true
sysinfo.workspace = true
task.workspace = true
tasks_ui.workspace = true
-telemetry.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
@@ -8,11 +8,11 @@ use project::debugger::{self, breakpoint_store::SourceBreakpoint, session::Threa
use schemars::JsonSchema;
use serde::Deserialize;
use session::DebugSession;
-use stack_trace_view::StackTraceView;
+
use tasks_ui::{Spawn, TaskOverrides};
use ui::{FluentBuilder, InteractiveElement};
use util::maybe;
-use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace};
+use workspace::{ShutdownDebugAdapters, Workspace};
use zed_actions::debug_panel::{Toggle, ToggleFocus};
pub mod attach_modal;
@@ -21,7 +21,6 @@ mod dropdown_menus;
mod new_process_modal;
mod persistence;
pub(crate) mod session;
-mod stack_trace_view;
#[cfg(any(test, feature = "test-support"))]
pub mod tests;
@@ -70,8 +69,6 @@ actions!(
FocusLoadedSources,
/// Focuses on the terminal panel.
FocusTerminal,
- /// Shows the stack trace for the current thread.
- ShowStackTrace,
/// Toggles the thread picker dropdown.
ToggleThreadPicker,
/// Toggles the session picker dropdown.
@@ -207,39 +204,6 @@ pub fn init(cx: &mut App) {
.ok();
}
})
- .on_action(cx.listener(
- |workspace, _: &ShowStackTrace, window, cx| {
- let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
- return;
- };
-
- if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx) {
- let is_active = workspace
- .active_item(cx)
- .is_some_and(|item| item.item_id() == existing.item_id());
- workspace.activate_item(&existing, true, !is_active, window, cx);
- } else {
- let Some(active_session) = debug_panel.read(cx).active_session()
- else {
- return;
- };
-
- let project = workspace.project();
-
- let stack_trace_view = active_session.update(cx, |session, cx| {
- session.stack_trace_view(project, window, cx).clone()
- });
-
- workspace.add_item_to_active_pane(
- Box::new(stack_trace_view),
- None,
- true,
- window,
- cx,
- );
- }
- },
- ))
})
.when(supports_detach, |div| {
let active_item = active_item.clone();
@@ -1,14 +1,13 @@
pub mod running;
-use crate::{StackTraceView, persistence::SerializedLayout, session::running::DebugTerminal};
+use crate::{persistence::SerializedLayout, session::running::DebugTerminal};
use dap::client::SessionId;
use gpui::{App, Axis, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity};
use project::debugger::session::Session;
-use project::worktree_store::WorktreeStore;
+
use project::{Project, debugger::session::SessionQuirks};
use rpc::proto;
use running::RunningState;
-use std::cell::OnceCell;
use ui::prelude::*;
use workspace::{
CollaboratorId, FollowableItem, ViewId, Workspace,
@@ -19,9 +18,6 @@ pub struct DebugSession {
remote_id: Option<workspace::ViewId>,
pub(crate) running_state: Entity<RunningState>,
pub(crate) quirks: SessionQuirks,
- stack_trace_view: OnceCell<Entity<StackTraceView>>,
- _worktree_store: WeakEntity<WorktreeStore>,
- workspace: WeakEntity<Workspace>,
}
impl DebugSession {
@@ -49,13 +45,10 @@ impl DebugSession {
});
let quirks = session.read(cx).quirks();
- cx.new(|cx| Self {
+ cx.new(|_| Self {
remote_id: None,
running_state,
quirks,
- stack_trace_view: OnceCell::new(),
- _worktree_store: project.read(cx).worktree_store().downgrade(),
- workspace,
})
}
@@ -63,30 +56,6 @@ impl DebugSession {
self.running_state.read(cx).session_id()
}
- pub(crate) fn stack_trace_view(
- &mut self,
- project: &Entity<Project>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> &Entity<StackTraceView> {
- let workspace = self.workspace.clone();
- let running_state = self.running_state.clone();
-
- self.stack_trace_view.get_or_init(|| {
- let stackframe_list = running_state.read(cx).stack_frame_list().clone();
-
- cx.new(|cx| {
- StackTraceView::new(
- workspace.clone(),
- project.clone(),
- stackframe_list,
- window,
- cx,
- )
- })
- })
- }
-
pub fn session(&self, cx: &App) -> Entity<Session> {
self.running_state.read(cx).session().clone()
}
@@ -15,13 +15,13 @@ use util::{
paths::{PathStyle, is_absolute},
};
-use crate::{StackTraceView, ToggleUserFrames};
+use crate::ToggleUserFrames;
use language::PointUtf16;
use project::debugger::breakpoint_store::ActiveStackFrame;
use project::debugger::session::{Session, SessionEvent, StackFrame, ThreadStatus};
use project::{ProjectItem, ProjectPath};
use ui::{Tooltip, WithScrollbar, prelude::*};
-use workspace::{ItemHandle, Workspace, WorkspaceId};
+use workspace::{Workspace, WorkspaceId};
use super::RunningState;
@@ -154,6 +154,7 @@ impl StackFrameList {
&self.entries
}
+ #[cfg(test)]
pub(crate) fn flatten_entries(
&self,
show_collapsed: bool,
@@ -437,14 +438,7 @@ impl StackFrameList {
.project_path(cx)
.context("Could not select a stack frame for unnamed buffer")?;
- let open_preview = !workspace
- .item_of_type::<StackTraceView>(cx)
- .map(|viewer| {
- workspace
- .active_item(cx)
- .is_some_and(|item| item.item_id() == viewer.item_id())
- })
- .unwrap_or_default();
+ let open_preview = true;
let active_debug_line_pane = workspace
.project()
@@ -1,458 +0,0 @@
-use std::{
- any::{Any, TypeId},
- sync::Arc,
-};
-
-use collections::HashMap;
-use dap::StackFrameId;
-use editor::{
- Anchor, Bias, DebugStackFrameLine, Editor, EditorEvent, ExcerptId, ExcerptRange, HighlightKey,
- MultiBuffer, RowHighlightOptions, SelectionEffects, ToPoint, scroll::Autoscroll,
-};
-use gpui::{
- App, AppContext, Entity, EventEmitter, Focusable, IntoElement, Render, SharedString,
- Subscription, Task, WeakEntity, Window,
-};
-use language::{BufferSnapshot, Capability, Point, Selection, SelectionGoal, TreeSitterOptions};
-use project::{Project, ProjectPath};
-use ui::{ActiveTheme as _, Context, ParentElement as _, Styled as _, div};
-use util::ResultExt as _;
-use workspace::{
- Item, ItemHandle as _, ItemNavHistory, ToolbarItemLocation, Workspace,
- item::{BreadcrumbText, ItemEvent, SaveOptions},
- searchable::SearchableItemHandle,
-};
-
-use crate::session::running::stack_frame_list::{StackFrameList, StackFrameListEvent};
-use anyhow::Result;
-
-pub(crate) struct StackTraceView {
- editor: Entity<Editor>,
- multibuffer: Entity<MultiBuffer>,
- workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
- stack_frame_list: Entity<StackFrameList>,
- selected_stack_frame_id: Option<StackFrameId>,
- highlights: Vec<(StackFrameId, Anchor)>,
- excerpt_for_frames: collections::HashMap<ExcerptId, StackFrameId>,
- refresh_task: Option<Task<Result<()>>>,
- _subscription: Option<Subscription>,
-}
-
-impl StackTraceView {
- pub(crate) fn new(
- workspace: WeakEntity<Workspace>,
- project: Entity<Project>,
- stack_frame_list: Entity<StackFrameList>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> Self {
- telemetry::event!("Stack Trace View Deployed");
-
- let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
- let editor = cx.new(|cx| {
- let mut editor =
- Editor::for_multibuffer(multibuffer.clone(), Some(project.clone()), window, cx);
- editor.set_vertical_scroll_margin(5, cx);
- editor
- });
-
- cx.subscribe_in(&editor, window, |this, editor, event, window, cx| {
- if let EditorEvent::SelectionsChanged { local: true } = event {
- let excerpt_id = editor.update(cx, |editor, cx| {
- let position: Point = editor
- .selections
- .newest(&editor.display_snapshot(cx))
- .head();
-
- editor
- .snapshot(window, cx)
- .buffer_snapshot()
- .excerpt_containing(position..position)
- .map(|excerpt| excerpt.id())
- });
-
- if let Some(stack_frame_id) = excerpt_id
- .and_then(|id| this.excerpt_for_frames.get(&id))
- .filter(|id| Some(**id) != this.selected_stack_frame_id)
- {
- this.stack_frame_list.update(cx, |list, cx| {
- list.go_to_stack_frame(*stack_frame_id, window, cx).detach();
- });
- }
- }
- })
- .detach();
-
- cx.subscribe_in(
- &stack_frame_list,
- window,
- |this, stack_frame_list, event, window, cx| match event {
- StackFrameListEvent::BuiltEntries => {
- this.selected_stack_frame_id =
- stack_frame_list.read(cx).opened_stack_frame_id();
- this.update_excerpts(window, cx);
- }
- StackFrameListEvent::SelectedStackFrameChanged(selected_frame_id) => {
- this.selected_stack_frame_id = Some(*selected_frame_id);
- this.update_highlights(window, cx);
-
- if let Some(frame_anchor) = this
- .highlights
- .iter()
- .find(|(frame_id, _)| frame_id == selected_frame_id)
- .map(|highlight| highlight.1)
- {
- this.editor.update(cx, |editor, cx| {
- if frame_anchor.excerpt_id
- != editor.selections.newest_anchor().head().excerpt_id
- {
- let effects = SelectionEffects::scroll(
- Autoscroll::center().for_anchor(frame_anchor),
- );
-
- editor.change_selections(effects, window, cx, |selections| {
- let selection_id = selections.new_selection_id();
-
- let selection = Selection {
- id: selection_id,
- start: frame_anchor,
- end: frame_anchor,
- goal: SelectionGoal::None,
- reversed: false,
- };
-
- selections.select_anchors(vec![selection]);
- })
- }
- });
- }
- }
- },
- )
- .detach();
-
- let mut this = Self {
- editor,
- multibuffer,
- workspace,
- project,
- excerpt_for_frames: HashMap::default(),
- highlights: Vec::default(),
- stack_frame_list,
- selected_stack_frame_id: None,
- refresh_task: None,
- _subscription: None,
- };
-
- this.update_excerpts(window, cx);
- this
- }
-
- fn update_excerpts(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- self.refresh_task.take();
- self.editor.update(cx, |editor, cx| {
- editor.clear_highlights(HighlightKey::DebugStackFrameLine, cx)
- });
-
- let stack_frames = self
- .stack_frame_list
- .read_with(cx, |list, _| list.flatten_entries(false, false));
-
- let frames_to_open: Vec<_> = stack_frames
- .into_iter()
- .filter_map(|frame| {
- Some((
- frame.id,
- frame.line as u32 - 1,
- StackFrameList::abs_path_from_stack_frame(&frame)?,
- ))
- })
- .collect();
-
- self.multibuffer
- .update(cx, |multi_buffer, cx| multi_buffer.clear(cx));
-
- let task = cx.spawn_in(window, async move |this, cx| {
- let mut to_highlights = Vec::default();
-
- for (stack_frame_id, line, abs_path) in frames_to_open {
- let (worktree, relative_path) = this
- .update(cx, |this, cx| {
- this.workspace.update(cx, |workspace, cx| {
- workspace.project().update(cx, |this, cx| {
- this.find_or_create_worktree(&abs_path, false, cx)
- })
- })
- })??
- .await?;
-
- let project_path = ProjectPath {
- worktree_id: worktree.read_with(cx, |tree, _| tree.id()),
- path: relative_path,
- };
-
- if let Some(buffer) = this
- .read_with(cx, |this, _| this.project.clone())?
- .update(cx, |project, cx| project.open_buffer(project_path, cx))
- .await
- .log_err()
- {
- this.update(cx, |this, cx| {
- this.multibuffer.update(cx, |multi_buffer, cx| {
- let line_point = Point::new(line, 0);
- let start_context = Self::heuristic_syntactic_expand(
- &buffer.read(cx).snapshot(),
- line_point,
- );
-
- // Users will want to see what happened before an active debug line in most cases
- let range = ExcerptRange {
- context: start_context..Point::new(line.saturating_add(1), 0),
- primary: line_point..line_point,
- };
- multi_buffer.push_excerpts(buffer.clone(), vec![range], cx);
-
- let line_anchor =
- multi_buffer.buffer_point_to_anchor(&buffer, line_point, cx);
-
- if let Some(line_anchor) = line_anchor {
- this.excerpt_for_frames
- .insert(line_anchor.excerpt_id, stack_frame_id);
- to_highlights.push((stack_frame_id, line_anchor));
- }
- });
- })
- .ok();
- }
- }
-
- this.update_in(cx, |this, window, cx| {
- this.highlights = to_highlights;
- this.update_highlights(window, cx);
- })
- .ok();
-
- anyhow::Ok(())
- });
-
- self.refresh_task = Some(task);
- }
-
- fn update_highlights(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- self.editor.update(cx, |editor, _| {
- editor.clear_row_highlights::<DebugStackFrameLine>()
- });
-
- let stack_frames = self
- .stack_frame_list
- .read_with(cx, |session, _| session.flatten_entries(false, false));
-
- let active_idx = self
- .selected_stack_frame_id
- .and_then(|id| {
- stack_frames
- .iter()
- .enumerate()
- .find_map(|(idx, frame)| if frame.id == id { Some(idx) } else { None })
- })
- .unwrap_or(0);
-
- self.editor.update(cx, |editor, cx| {
- let snapshot = editor.snapshot(window, cx).display_snapshot;
- let first_color = cx.theme().colors().editor_debugger_active_line_background;
-
- let color = first_color.opacity(0.5);
-
- let mut is_first = true;
-
- for (_, highlight) in self.highlights.iter().skip(active_idx) {
- let position = highlight.to_point(&snapshot.buffer_snapshot());
- let color = if is_first {
- is_first = false;
- first_color
- } else {
- color
- };
-
- let start = snapshot
- .buffer_snapshot()
- .clip_point(Point::new(position.row, 0), Bias::Left);
- let end = start + Point::new(1, 0);
- let start = snapshot.buffer_snapshot().anchor_before(start);
- let end = snapshot.buffer_snapshot().anchor_before(end);
- editor.highlight_rows::<DebugStackFrameLine>(
- start..end,
- color,
- RowHighlightOptions::default(),
- cx,
- );
- }
- })
- }
-
- fn heuristic_syntactic_expand(snapshot: &BufferSnapshot, selected_point: Point) -> Point {
- let mut text_objects = snapshot.text_object_ranges(
- selected_point..selected_point,
- TreeSitterOptions::max_start_depth(4),
- );
-
- let mut start_position = text_objects
- .find(|(_, obj)| matches!(obj, language::TextObject::AroundFunction))
- .map(|(range, _)| snapshot.offset_to_point(range.start))
- .map(|point| Point::new(point.row.max(selected_point.row.saturating_sub(8)), 0))
- .unwrap_or(selected_point);
-
- if start_position.row == selected_point.row {
- start_position.row = start_position.row.saturating_sub(1);
- }
-
- start_position
- }
-}
-
-impl Render for StackTraceView {
- fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
- div().size_full().child(self.editor.clone())
- }
-}
-
-impl EventEmitter<EditorEvent> for StackTraceView {}
-impl Focusable for StackTraceView {
- fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
- self.editor.focus_handle(cx)
- }
-}
-
-impl Item for StackTraceView {
- type Event = EditorEvent;
-
- fn to_item_events(event: &EditorEvent, f: &mut dyn FnMut(ItemEvent)) {
- Editor::to_item_events(event, f)
- }
-
- fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
- self.editor
- .update(cx, |editor, cx| editor.deactivated(window, cx));
- }
-
- fn navigate(
- &mut self,
- data: Arc<dyn Any + Send>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> bool {
- self.editor
- .update(cx, |editor, cx| editor.navigate(data, window, cx))
- }
-
- fn tab_tooltip_text(&self, _: &App) -> Option<SharedString> {
- Some("Stack Frame Viewer".into())
- }
-
- fn tab_content_text(&self, _detail: usize, _: &App) -> SharedString {
- "Stack Frames".into()
- }
-
- fn for_each_project_item(
- &self,
- cx: &App,
- f: &mut dyn FnMut(gpui::EntityId, &dyn project::ProjectItem),
- ) {
- self.editor.for_each_project_item(cx, f)
- }
-
- fn set_nav_history(
- &mut self,
- nav_history: ItemNavHistory,
- _: &mut Window,
- cx: &mut Context<Self>,
- ) {
- self.editor.update(cx, |editor, _| {
- editor.set_nav_history(Some(nav_history));
- });
- }
-
- fn is_dirty(&self, cx: &App) -> bool {
- self.multibuffer.read(cx).is_dirty(cx)
- }
-
- fn has_deleted_file(&self, cx: &App) -> bool {
- self.multibuffer.read(cx).has_deleted_file(cx)
- }
-
- fn has_conflict(&self, cx: &App) -> bool {
- self.multibuffer.read(cx).has_conflict(cx)
- }
-
- fn can_save(&self, _: &App) -> bool {
- true
- }
-
- fn save(
- &mut self,
- options: SaveOptions,
- project: Entity<Project>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> Task<Result<()>> {
- self.editor.save(options, project, window, cx)
- }
-
- fn save_as(
- &mut self,
- _: Entity<Project>,
- _: ProjectPath,
- _window: &mut Window,
- _: &mut Context<Self>,
- ) -> Task<Result<()>> {
- unreachable!()
- }
-
- fn reload(
- &mut self,
- project: Entity<Project>,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) -> Task<Result<()>> {
- self.editor.reload(project, window, cx)
- }
-
- fn act_as_type<'a>(
- &'a self,
- type_id: TypeId,
- self_handle: &'a Entity<Self>,
- _: &'a App,
- ) -> Option<gpui::AnyEntity> {
- if type_id == TypeId::of::<Self>() {
- Some(self_handle.clone().into())
- } else if type_id == TypeId::of::<Editor>() {
- Some(self.editor.clone().into())
- } else {
- None
- }
- }
-
- fn as_searchable(&self, _: &Entity<Self>, _: &App) -> Option<Box<dyn SearchableItemHandle>> {
- Some(Box::new(self.editor.clone()))
- }
-
- fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation {
- ToolbarItemLocation::PrimaryLeft
- }
-
- fn breadcrumbs(&self, cx: &App) -> Option<Vec<BreadcrumbText>> {
- self.editor.breadcrumbs(cx)
- }
-
- fn added_to_workspace(
- &mut self,
- workspace: &mut Workspace,
- window: &mut Window,
- cx: &mut Context<Self>,
- ) {
- self.editor.update(cx, |editor, cx| {
- editor.added_to_workspace(workspace, window, cx)
- });
- }
-}
@@ -322,16 +322,14 @@ impl ProjectDiagnosticsEditor {
if !has_no_blocks {
continue;
}
- let is_dirty = self
- .multibuffer
- .read(cx)
- .buffer(buffer_id)
- .is_none_or(|buffer| buffer.read(cx).is_dirty());
- if is_dirty {
+ let Some(buffer) = self.multibuffer.read(cx).buffer(buffer_id) else {
+ continue;
+ };
+ if buffer.read(cx).is_dirty() {
continue;
}
self.multibuffer.update(cx, |b, cx| {
- b.remove_excerpts_for_buffer(buffer_id, cx);
+ b.remove_excerpts_for_path(PathKey::for_buffer(&buffer, cx), cx);
});
}
}
@@ -1,6 +1,6 @@
use buffer_diff::BufferDiff;
use edit_prediction::{EditPrediction, EditPredictionRating, EditPredictionStore};
-use editor::{Editor, ExcerptRange, Inlay, MultiBuffer};
+use editor::{Editor, Inlay, MultiBuffer};
use feature_flags::FeatureFlag;
use gpui::{
App, BorderStyle, DismissEvent, EdgesRefinement, Entity, EventEmitter, FocusHandle, Focusable,
@@ -359,16 +359,9 @@ impl RatePredictionsModal {
editor.disable_header_for_buffer(new_buffer_id, cx);
let excerpt_id = editor.buffer().update(cx, |multibuffer, cx| {
multibuffer.clear(cx);
- let excerpt_ids = multibuffer.push_excerpts(
- new_buffer,
- vec![ExcerptRange {
- context: start..end,
- primary: start..end,
- }],
- cx,
- );
+ multibuffer.set_excerpts_for_buffer(new_buffer, [start..end], 0, cx);
multibuffer.add_diff(diff, cx);
- excerpt_ids.into_iter().next()
+ multibuffer.excerpt_ids().into_iter().next()
});
if let Some((excerpt_id, cursor_position)) =
@@ -169,7 +169,7 @@ mod tests {
use itertools::Itertools;
use language::{Capability, markdown_lang};
use languages::rust_lang;
- use multi_buffer::{ExcerptRange, MultiBuffer};
+ use multi_buffer::{MultiBuffer, PathKey};
use pretty_assertions::assert_eq;
use project::Project;
use rope::Point;
@@ -1239,32 +1239,34 @@ mod foo «1{
let multi_buffer = cx.new(|cx| {
let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
let excerpt_rows = 5;
let rest_of_first_except_rows = 3;
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(excerpt_rows, 0)),
- ExcerptRange::new(
- Point::new(
- comment_lines as u32 + excerpt_rows + rest_of_first_except_rows,
+ Point::new(0, 0)..Point::new(excerpt_rows, 0),
+ Point::new(
+ comment_lines as u32 + excerpt_rows + rest_of_first_except_rows,
+ 0,
+ )
+ ..Point::new(
+ comment_lines as u32
+ + excerpt_rows
+ + rest_of_first_except_rows
+ + excerpt_rows,
0,
- )
- ..Point::new(
- comment_lines as u32
- + excerpt_rows
- + rest_of_first_except_rows
- + excerpt_rows,
- 0,
- ),
- ),
+ ),
],
+ 0,
cx,
);
multi_buffer
@@ -1291,7 +1293,7 @@ mod foo «1{
let map: Option«3<Vec«4<«5()5»>4»>3» = None;
// a
// b
-
+ // c
fn process_data_2«2()2» «2{
let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
@@ -1331,7 +1333,7 @@ mod foo «1{
let map: Option«3<Vec«4<«5()5»>4»>3» = None;
// a
// b
-
+ // c
fn process_data_2«2()2» «2{
let other_map: Option«3<Vec«4<«5()5»>4»>3» = None;
@@ -1381,7 +1383,7 @@ mod foo «1{
let map: Option«1<Vec«2<«1()1»>2»>1» = None;
// a
// b
-
+ // c
fn process_data_2«2()2» «2{
let other_map: Option«1<Vec«2<«1()1»>2»>1» = None;
@@ -2852,8 +2852,8 @@ mod tests {
use buffer_diff::BufferDiff;
use gpui::{App, AppContext as _, Element, div, font, px};
use itertools::Itertools;
- use language::{Buffer, Capability};
- use multi_buffer::{ExcerptRange, MultiBuffer};
+ use language::{Buffer, Capability, Point};
+ use multi_buffer::{MultiBuffer, PathKey};
use rand::prelude::*;
use settings::SettingsStore;
use std::env;
@@ -3063,27 +3063,32 @@ mod tests {
let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
- let mut excerpt_ids = Vec::new();
let multi_buffer = cx.new(|cx| {
let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
- excerpt_ids.extend(multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer1.clone(),
- [ExcerptRange::new(0..buffer1.read(cx).len())],
+ [Point::zero()..buffer1.read(cx).max_point()],
+ 0,
cx,
- ));
- excerpt_ids.extend(multi_buffer.push_excerpts(
+ );
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer2.clone(),
- [ExcerptRange::new(0..buffer2.read(cx).len())],
+ [Point::zero()..buffer2.read(cx).max_point()],
+ 0,
cx,
- ));
- excerpt_ids.extend(multi_buffer.push_excerpts(
+ );
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer3.clone(),
- [ExcerptRange::new(0..buffer3.read(cx).len())],
+ [Point::zero()..buffer3.read(cx).max_point()],
+ 0,
cx,
- ));
-
+ );
multi_buffer
});
+ let excerpt_ids = multi_buffer.read_with(cx, |mb, _| mb.excerpt_ids());
let font = test_font();
let font_size = px(14.);
@@ -3410,30 +3415,32 @@ mod tests {
fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
cx.update(init_test);
- let text = "111\n222\n333\n444\n555\n666";
+ let text = "111\n\n222\n\n333\n\n444\n\n555\n\n666";
let buffer = cx.update(|cx| {
- MultiBuffer::build_multi(
+ let multibuffer = MultiBuffer::build_multi(
[
(text, vec![Point::new(0, 0)..Point::new(0, 3)]),
(
text,
vec![
- Point::new(1, 0)..Point::new(1, 3),
Point::new(2, 0)..Point::new(2, 3),
- Point::new(3, 0)..Point::new(3, 3),
+ Point::new(4, 0)..Point::new(4, 3),
+ Point::new(6, 0)..Point::new(6, 3),
],
),
(
text,
vec![
- Point::new(4, 0)..Point::new(4, 3),
- Point::new(5, 0)..Point::new(5, 3),
+ Point::new(8, 0)..Point::new(8, 3),
+ Point::new(10, 0)..Point::new(10, 3),
],
),
],
cx,
- )
+ );
+ assert_eq!(multibuffer.read(cx).excerpt_ids().len(), 6);
+ multibuffer
});
let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
let buffer_ids = buffer_snapshot
@@ -3469,16 +3476,16 @@ mod tests {
Some(0),
None,
None,
- Some(1),
- None,
Some(2),
None,
- Some(3),
+ Some(4),
None,
+ Some(6),
None,
- Some(4),
None,
- Some(5),
+ Some(8),
+ None,
+ Some(10),
]
);
@@ -3540,19 +3547,19 @@ mod tests {
None,
None,
None,
- Some(1),
+ Some(2),
None,
None,
- Some(2),
+ Some(4),
None,
- Some(3),
+ Some(6),
None,
None,
None,
None,
- Some(4),
+ Some(8),
None,
- Some(5),
+ Some(10),
None,
]
);
@@ -3608,19 +3615,19 @@ mod tests {
None,
None,
None,
- Some(1),
+ Some(2),
None,
None,
- Some(2),
+ Some(4),
None,
- Some(3),
+ Some(6),
None,
None,
None,
None,
- Some(4),
+ Some(8),
None,
- Some(5),
+ Some(10),
None,
]
);
@@ -3671,9 +3678,9 @@ mod tests {
None,
None,
None,
- Some(4),
+ Some(8),
None,
- Some(5),
+ Some(10),
None,
]
);
@@ -3727,9 +3734,9 @@ mod tests {
None,
None,
None,
- Some(4),
+ Some(8),
None,
- Some(5),
+ Some(10),
None,
]
);
@@ -4597,9 +4604,10 @@ mod tests {
let lhs_multibuffer = cx.new(|cx| {
let mut mb = MultiBuffer::new(Capability::ReadWrite);
- mb.push_excerpts(
+ mb.set_excerpts_for_buffer(
lhs_buffer.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..lhs_buffer.read(cx).max_point()],
+ 0,
cx,
);
mb.add_inverted_diff(diff.clone(), rhs_buffer.clone(), cx);
@@ -4607,9 +4615,10 @@ mod tests {
});
let rhs_multibuffer = cx.new(|cx| {
let mut mb = MultiBuffer::new(Capability::ReadWrite);
- mb.push_excerpts(
+ mb.set_excerpts_for_buffer(
rhs_buffer.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..rhs_buffer.read(cx).max_point()],
+ 0,
cx,
);
mb.add_diff(diff.clone(), cx);
@@ -7,7 +7,7 @@ use crate::{
linked_editing_ranges::LinkedEditingRanges,
scroll::scroll_amount::ScrollAmount,
test::{
- assert_text_with_selections, build_editor,
+ assert_text_with_selections, build_editor, editor_content_with_blocks,
editor_lsp_test_context::{EditorLspTestContext, git_commit_lang},
editor_test_context::EditorTestContext,
select_ranges,
@@ -35,9 +35,7 @@ use language_settings::Formatter;
use languages::markdown_lang;
use languages::rust_lang;
use lsp::{CompletionParams, DEFAULT_LSP_REQUEST_TIMEOUT};
-use multi_buffer::{
- ExcerptRange, IndentGuide, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey,
-};
+use multi_buffer::{IndentGuide, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey};
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_ne};
use project::{
@@ -64,7 +62,6 @@ use util::{
assert_set_eq, path,
rel_path::rel_path,
test::{TextRangeMarker, marked_text_ranges, marked_text_ranges_by, sample_text},
- uri,
};
use workspace::{
CloseActiveItem, CloseAllItems, CloseOtherItems, MultiWorkspace, NavigationEntry, OpenOptions,
@@ -3393,14 +3390,18 @@ fn test_newline_below_multibuffer(cx: &mut TestAppContext) {
let buffer_2 = cx.new(|cx| Buffer::local("ddd\neee\nfff", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
multibuffer
@@ -3467,14 +3468,18 @@ fn test_newline_below_multibuffer_middle_of_excerpt(cx: &mut TestAppContext) {
let buffer_2 = cx.new(|cx| Buffer::local("ddd\neee\nfff", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
multibuffer
@@ -3528,14 +3533,18 @@ fn test_newline_below_multibuffer_last_line_of_last_excerpt(cx: &mut TestAppCont
let buffer_2 = cx.new(|cx| Buffer::local("ddd\neee\nfff", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
multibuffer
@@ -3589,14 +3598,18 @@ fn test_newline_below_multibuffer_multiple_cursors(cx: &mut TestAppContext) {
let buffer_2 = cx.new(|cx| Buffer::local("ddd\neee\nfff", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 3))],
+ [Point::new(0, 0)..Point::new(2, 3)],
+ 0,
cx,
);
multibuffer
@@ -4671,14 +4684,18 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
cx.new(|cx| Buffer::local("const c: usize = 3;\n", cx).with_language(rust_language, cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
toml_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
+ [Point::new(0, 0)..Point::new(2, 0)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
rust_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
multibuffer
@@ -8128,9 +8145,11 @@ async fn test_clipboard_line_numbers_from_multibuffer(cx: &mut TestAppContext) {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer.clone(),
- [ExcerptRange::new(Point::new(2, 0)..Point::new(5, 0))],
+ [Point::new(2, 0)..Point::new(5, 0)],
+ 0,
cx,
);
multibuffer
@@ -9645,31 +9664,25 @@ async fn test_select_previous_multibuffer(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let mut cx =
- EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc\n»ddd", "aaa\n«bbb\nccc\n»ddd"]);
+ EditorTestContext::new_multibuffer(cx, ["aaa\n«bbb\nccc»\nddd", "aaa\n«bbb\nccc»\nddd"]);
cx.assert_editor_state(indoc! {"
ˇbbb
ccc
-
bbb
- ccc
- "});
+ ccc"});
cx.dispatch_action(SelectPrevious::default());
cx.assert_editor_state(indoc! {"
«bbbˇ»
ccc
-
bbb
- ccc
- "});
+ ccc"});
cx.dispatch_action(SelectPrevious::default());
cx.assert_editor_state(indoc! {"
«bbbˇ»
ccc
-
«bbbˇ»
- ccc
- "});
+ ccc"});
}
#[gpui::test]
@@ -12772,10 +12785,10 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
sample_text_2,
"llll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu"
);
- let sample_text_3 = sample_text(rows, cols, 'v');
+ let sample_text_3 = sample_text(rows, cols, 'v').replace('\u{7f}', ".");
assert_eq!(
sample_text_3,
- "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}"
+ "vvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n...."
);
let fs = FakeFs::new(cx.executor());
@@ -12834,33 +12847,40 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
let multi_buffer = cx.new(|cx| {
let mut multi_buffer = MultiBuffer::new(ReadWrite);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 4),
+ Point::new(5, 0)..Point::new(6, 4),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 4),
+ Point::new(5, 0)..Point::new(6, 4),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 4),
+ Point::new(5, 0)..Point::new(6, 4),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
+ assert_eq!(multi_buffer.excerpt_ids().len(), 9);
multi_buffer
});
let multi_buffer_editor = cx.new_window_entity(|window, cx| {
@@ -12874,30 +12894,61 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
});
multi_buffer_editor.update_in(cx, |editor, window, cx| {
+ let a = editor.text(cx).find("aaaa").unwrap();
editor.change_selections(
SelectionEffects::scroll(Autoscroll::Next),
window,
cx,
- |s| s.select_ranges(Some(MultiBufferOffset(1)..MultiBufferOffset(2))),
+ |s| s.select_ranges(Some(MultiBufferOffset(a + 1)..MultiBufferOffset(a + 2))),
);
editor.insert("|one|two|three|", window, cx);
});
assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
multi_buffer_editor.update_in(cx, |editor, window, cx| {
+ let n = editor.text(cx).find("nnnn").unwrap();
editor.change_selections(
SelectionEffects::scroll(Autoscroll::Next),
window,
cx,
- |s| s.select_ranges(Some(MultiBufferOffset(60)..MultiBufferOffset(70))),
+ |s| s.select_ranges(Some(MultiBufferOffset(n + 4)..MultiBufferOffset(n + 14))),
);
editor.insert("|four|five|six|", window, cx);
});
assert!(cx.read(|cx| multi_buffer_editor.is_dirty(cx)));
// First two buffers should be edited, but not the third one.
- assert_eq!(
- multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
- "a|one|two|three|aa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\nllll\nmmmm\nnnnn|four|five|six|\nr\n\nuuuu\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}",
+ pretty_assertions::assert_eq!(
+ editor_content_with_blocks(&multi_buffer_editor, cx),
+ indoc! {"
+ § main.rs
+ § -----
+ a|one|two|three|aa
+ bbbb
+ cccc
+ § -----
+ ffff
+ gggg
+ § -----
+ jjjj
+ § other.rs
+ § -----
+ llll
+ mmmm
+ nnnn|four|five|six|
+ § -----
+
+ § -----
+ uuuu
+ § lib.rs
+ § -----
+ vvvv
+ wwww
+ xxxx
+ § -----
+ {{{{
+ ||||
+ § -----
+ ...."}
);
buffer_1.update(cx, |buffer, _| {
assert!(buffer.is_dirty());
@@ -12910,7 +12961,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
assert!(buffer.is_dirty());
assert_eq!(
buffer.text(),
- "llll\nmmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu",
+ "llll\nmmmm\nnnnn|four|five|six|\noooo\npppp\n\nssss\ntttt\nuuuu",
)
});
buffer_3.update(cx, |buffer, _| {
@@ -12936,10 +12987,10 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
let fake_server = fake_servers.next().await.unwrap();
fake_server
.server
- .on_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
+ .on_request::<lsp::request::Formatting, _, _>(move |_params, _| async move {
Ok(Some(vec![lsp::TextEdit::new(
lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
- format!("[{} formatted]", params.text_document.uri),
+ "[formatted]".to_string(),
)]))
})
.detach();
@@ -12948,23 +12999,61 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) {
// After multibuffer saving, only first two buffers should be reformatted, but not the third one (as it was not dirty).
assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx)));
assert_eq!(
- multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)),
- uri!(
- "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"
- ),
+ editor_content_with_blocks(&multi_buffer_editor, cx),
+ indoc! {"
+ § main.rs
+ § -----
+ a|o[formatted]bbbb
+ cccc
+ § -----
+ ffff
+ gggg
+ § -----
+ jjjj
+
+ § other.rs
+ § -----
+ lll[formatted]mmmm
+ nnnn|four|five|six|
+ § -----
+
+ § -----
+ uuuu
+
+ § lib.rs
+ § -----
+ vvvv
+ wwww
+ xxxx
+ § -----
+ {{{{
+ ||||
+ § -----
+ ...."}
);
buffer_1.update(cx, |buffer, _| {
assert!(!buffer.is_dirty());
assert_eq!(
buffer.text(),
- uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"),
+ "a|o[formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n",
)
});
+ // Diff < left / right > :
+ // lll[formatted]mmmm
+ // <nnnn|four|five|six|
+ // <oooo
+ // >nnnn|four|five|six|oooo
+ // pppp
+ // <
+ // ssss
+ // tttt
+ // uuuu
+
buffer_2.update(cx, |buffer, _| {
assert!(!buffer.is_dirty());
assert_eq!(
buffer.text(),
- uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"),
+ "lll[formatted]mmmm\nnnnn|four|five|six|\noooo\npppp\n\nssss\ntttt\nuuuu\n",
)
});
buffer_3.update(cx, |buffer, _| {
@@ -13021,19 +13110,25 @@ async fn test_autosave_with_dirty_buffers(cx: &mut TestAppContext) {
// Create a multi-buffer with all three buffers
let multi_buffer = cx.new(|cx| {
let mut multi_buffer = MultiBuffer::new(ReadWrite);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);
multi_buffer
@@ -15520,7 +15615,9 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
10.satu;
//
- // separate cursors so they open in different excerpts (manually reproducible)
+ // separate1
+ // separate2
+ // separate3
//
10.satu20;
@@ -15532,8 +15629,6 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
//
- //
-
10.satuˇ20;
}
"};
@@ -15543,15 +15638,10 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
//
- //
-
10.saturating_sub()ˇ;
}
"};
- let first_excerpt_end = buffer_text.find("//").unwrap() + 3;
- let second_excerpt_end = buffer_text.rfind("//").unwrap() - 4;
-
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/a"),
@@ -15591,14 +15681,14 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
let multi_buffer = cx.new(|cx| {
let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
- multi_buffer.push_excerpts(
- buffer.clone(),
- [ExcerptRange::new(0..first_excerpt_end)],
- cx,
- );
- multi_buffer.push_excerpts(
+ multi_buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer.clone(),
- [ExcerptRange::new(second_excerpt_end..buffer_text.len())],
+ [
+ Point::zero()..Point::new(2, 0),
+ Point::new(7, 0)..buffer.read(cx).max_point(),
+ ],
+ 0,
cx,
);
multi_buffer
@@ -15632,7 +15722,7 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([
Point::new(1, 11)..Point::new(1, 11),
- Point::new(7, 11)..Point::new(7, 11),
+ Point::new(5, 11)..Point::new(5, 11),
])
});
@@ -15651,12 +15741,12 @@ async fn test_completion_in_multibuffer_with_replace_range(cx: &mut TestAppConte
lsp::InsertReplaceEdit {
new_text: "saturating_sub()".to_owned(),
insert: lsp::Range::new(
- lsp::Position::new(7, 7),
- lsp::Position::new(7, 11),
+ lsp::Position::new(9, 7),
+ lsp::Position::new(9, 11),
),
replace: lsp::Range::new(
- lsp::Position::new(7, 7),
- lsp::Position::new(7, 13),
+ lsp::Position::new(9, 7),
+ lsp::Position::new(9, 13),
),
},
)),
@@ -17738,24 +17828,26 @@ async fn test_toggle_block_comment(cx: &mut TestAppContext) {
fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
init_test(cx, |_| {});
- let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
+ let buffer = cx.new(|cx| Buffer::local(sample_text(6, 4, 'a'), cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(0, 4)),
- ExcerptRange::new(Point::new(1, 0)..Point::new(1, 4)),
+ Point::new(0, 0)..Point::new(0, 4),
+ Point::new(5, 0)..Point::new(5, 4),
],
+ 0,
cx,
);
- assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb");
+ assert_eq!(multibuffer.read(cx).text(), "aaaa\nffff");
multibuffer
});
let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
editor.update_in(cx, |editor, window, cx| {
- assert_eq!(editor.text(cx), "aaaa\nbbbb");
+ assert_eq!(editor.text(cx), "aaaa\nffff");
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
s.select_ranges([
Point::new(0, 0)..Point::new(0, 0),
@@ -17764,7 +17856,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
});
editor.handle_input("X", window, cx);
- assert_eq!(editor.text(cx), "Xaaaa\nXbbbb");
+ assert_eq!(editor.text(cx), "Xaaaa\nXffff");
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[
@@ -17778,7 +17870,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
s.select_ranges([Point::new(0, 2)..Point::new(1, 2)])
});
editor.backspace(&Default::default(), window, cx);
- assert_eq!(editor.text(cx), "Xa\nbbb");
+ assert_eq!(editor.text(cx), "Xa\nfff");
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(1, 0)..Point::new(1, 0)]
@@ -17788,7 +17880,7 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
s.select_ranges([Point::new(1, 1)..Point::new(0, 1)])
});
editor.backspace(&Default::default(), window, cx);
- assert_eq!(editor.text(cx), "X\nbb");
+ assert_eq!(editor.text(cx), "X\nff");
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[Point::new(0, 1)..Point::new(0, 1)]
@@ -17796,115 +17888,23 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
});
}
-#[gpui::test]
-fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
- init_test(cx, |_| {});
-
- let markers = vec![('[', ']').into(), ('(', ')').into()];
- let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
- indoc! {"
- [aaaa
- (bbbb]
- cccc)",
- },
- markers.clone(),
- );
- let excerpt_ranges = markers.into_iter().map(|marker| {
- let context = excerpt_ranges.remove(&marker).unwrap()[0].clone();
- ExcerptRange::new(context)
- });
- let buffer = cx.new(|cx| Buffer::local(initial_text, cx));
- let multibuffer = cx.new(|cx| {
- let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
- multibuffer
- });
-
- let (editor, cx) = cx.add_window_view(|window, cx| build_editor(multibuffer, window, cx));
- editor.update_in(cx, |editor, window, cx| {
- let (expected_text, selection_ranges) = marked_text_ranges(
- indoc! {"
- aaaa
- bˇbbb
- bˇbbˇb
- cccc"
- },
- true,
- );
- assert_eq!(editor.text(cx), expected_text);
- editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
- s.select_ranges(
- selection_ranges
- .iter()
- .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)),
- )
- });
-
- editor.handle_input("X", window, cx);
-
- let (expected_text, expected_selections) = marked_text_ranges(
- indoc! {"
- aaaa
- bXˇbbXb
- bXˇbbXˇb
- cccc"
- },
- false,
- );
- assert_eq!(editor.text(cx), expected_text);
- assert_eq!(
- editor.selections.ranges(&editor.display_snapshot(cx)),
- expected_selections
- .iter()
- .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
- .collect::<Vec<_>>()
- );
-
- editor.newline(&Newline, window, cx);
- let (expected_text, expected_selections) = marked_text_ranges(
- indoc! {"
- aaaa
- bX
- ˇbbX
- b
- bX
- ˇbbX
- ˇb
- cccc"
- },
- false,
- );
- assert_eq!(editor.text(cx), expected_text);
- assert_eq!(
- editor.selections.ranges(&editor.display_snapshot(cx)),
- expected_selections
- .iter()
- .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
- .collect::<Vec<_>>()
- );
- });
-}
-
#[gpui::test]
fn test_refresh_selections(cx: &mut TestAppContext) {
init_test(cx, |_| {});
- let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
- let mut excerpt1_id = None;
+ let buffer = cx.new(|cx| Buffer::local(sample_text(5, 4, 'a'), cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- excerpt1_id = multibuffer
- .push_excerpts(
- buffer.clone(),
- [
- ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
- ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
- ],
- cx,
- )
- .into_iter()
- .next();
- assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ [
+ Point::new(0, 0)..Point::new(1, 4),
+ Point::new(3, 0)..Point::new(4, 4),
+ ],
+ 0,
+ cx,
+ );
multibuffer
});
@@ -17944,7 +17944,13 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
});
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ [Point::new(3, 0)..Point::new(4, 4)],
+ 0,
+ cx,
+ );
});
_ = editor.update(cx, |editor, window, cx| {
// Removing an excerpt causes the first selection to become degenerate.
@@ -17962,8 +17968,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
[
+ Point::new(0, 0)..Point::new(0, 0),
Point::new(0, 1)..Point::new(0, 1),
- Point::new(0, 3)..Point::new(0, 3)
]
);
assert!(editor.selections.pending_anchor().is_some());
@@ -17974,22 +17980,20 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
init_test(cx, |_| {});
- let buffer = cx.new(|cx| Buffer::local(sample_text(3, 4, 'a'), cx));
- let mut excerpt1_id = None;
+ let buffer = cx.new(|cx| Buffer::local(sample_text(5, 4, 'a'), cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- excerpt1_id = multibuffer
- .push_excerpts(
- buffer.clone(),
- [
- ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
- ExcerptRange::new(Point::new(1, 0)..Point::new(2, 4)),
- ],
- cx,
- )
- .into_iter()
- .next();
- assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\nbbbb\ncccc");
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ [
+ Point::new(0, 0)..Point::new(1, 4),
+ Point::new(3, 0)..Point::new(4, 4),
+ ],
+ 0,
+ cx,
+ );
+ assert_eq!(multibuffer.read(cx).text(), "aaaa\nbbbb\ndddd\neeee");
multibuffer
});
@@ -18011,7 +18015,13 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
});
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.remove_excerpts([excerpt1_id.unwrap()], cx);
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ [Point::new(3, 0)..Point::new(4, 4)],
+ 0,
+ cx,
+ );
});
_ = editor.update(cx, |editor, window, cx| {
assert_eq!(
@@ -18023,7 +18033,7 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| s.refresh());
assert_eq!(
editor.selections.ranges(&editor.display_snapshot(cx)),
- [Point::new(0, 3)..Point::new(0, 3)]
+ [Point::new(0, 0)..Point::new(0, 0)]
);
assert!(editor.selections.pending_anchor().is_some());
});
@@ -18540,9 +18550,10 @@ async fn test_following_with_multiple_excerpts(cx: &mut TestAppContext) {
// Remove some excerpts.
leader.update(cx, |leader, cx| {
leader.buffer.update(cx, |multibuffer, cx| {
- let excerpt_ids = multibuffer.excerpt_ids();
- multibuffer.remove_excerpts([excerpt_ids[1], excerpt_ids[2]], cx);
- multibuffer.remove_excerpts([excerpt_ids[0]], cx);
+ multibuffer.remove_excerpts_for_path(
+ PathKey::with_sort_prefix(1, rel_path("b.txt").into_arc()),
+ cx,
+ );
});
});
@@ -20773,31 +20784,37 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
multibuffer
@@ -20824,7 +20841,7 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
cx.executor().run_until_parked();
editor.update_in(cx, |editor, window, cx| {
- assert_eq!(editor.text(cx), "Xaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
+ assert_eq!(editor.display_text(cx), "\n\nXaaa\nXbbb\nXccc\n\nXfff\nXggg\n\nXjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\n\n\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}");
editor.select_all(&SelectAll, window, cx);
editor.git_restore(&Default::default(), window, cx);
});
@@ -20832,7 +20849,7 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
// When all ranges are selected, all buffer hunks are reverted.
editor.update(cx, |editor, cx| {
- assert_eq!(editor.text(cx), "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n");
+ assert_eq!(editor.display_text(cx), "\n\naaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\n\n\n\n\nllll\nmmmm\nnnnn\noooo\npppp\nqqqq\nrrrr\nssss\ntttt\nuuuu\n\n\n\n\n\n\nvvvv\nwwww\nxxxx\nyyyy\nzzzz\n{{{{\n||||\n}}}}\n~~~~\n\u{7f}\u{7f}\u{7f}\u{7f}\n\n\n\n");
});
buffer_1.update(cx, |buffer, _| {
assert_eq!(buffer.text(), base_text_1);
@@ -20850,7 +20867,7 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
editor.update_in(cx, |editor, window, cx| {
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
- s.select_ranges(Some(Point::new(0, 0)..Point::new(6, 0)));
+ s.select_ranges(Some(Point::new(0, 0)..Point::new(5, 0)));
});
editor.git_restore(&Default::default(), window, cx);
});
@@ -20859,8 +20876,8 @@ async fn test_multibuffer_reverts(cx: &mut TestAppContext) {
// but not affect buffer_2 and its related excerpts.
editor.update(cx, |editor, cx| {
assert_eq!(
- editor.text(cx),
- "aaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
+ editor.display_text(cx),
+ "\n\naaaa\nbbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n\n\n\n\n\n\nXlll\nXmmm\nXnnn\n\nXqqq\nXrrr\n\nXuuu\n\n\nXvvv\nXwww\nXxxx\n\nX{{{\nX|||\n\nX\u{7f}\u{7f}\u{7f}"
);
});
buffer_1.update(cx, |buffer, _| {
@@ -20915,31 +20932,37 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) {
let multi_buffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 4)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(5, 0)..Point::new(6, 0),
+ Point::new(9, 0)..Point::new(9, 4),
],
+ 0,
cx,
);
multibuffer
@@ -21394,33 +21417,40 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) {
let multi_buffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
+ Point::new(0, 0)..Point::new(2, 3),
+ Point::new(5, 0)..Point::new(6, 3),
+ Point::new(9, 0)..Point::new(10, 3),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
+ Point::new(0, 0)..Point::new(2, 3),
+ Point::new(5, 0)..Point::new(6, 3),
+ Point::new(9, 0)..Point::new(10, 3),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0)),
- ExcerptRange::new(Point::new(5, 0)..Point::new(7, 0)),
- ExcerptRange::new(Point::new(9, 0)..Point::new(10, 3)),
+ Point::new(0, 0)..Point::new(2, 3),
+ Point::new(5, 0)..Point::new(6, 3),
+ Point::new(9, 0)..Point::new(10, 3),
],
+ 0,
cx,
);
+ assert_eq!(multibuffer.excerpt_ids().len(), 9);
multibuffer
});
@@ -960,9 +960,9 @@ fn spawn_editor_hints_refresh(
pub mod tests {
use crate::editor_tests::update_test_language_settings;
use crate::inlays::inlay_hints::InlayHintRefreshReason;
+ use crate::scroll::Autoscroll;
use crate::scroll::ScrollAmount;
use crate::{Editor, SelectionEffects};
- use crate::{ExcerptRange, scroll::Autoscroll};
use collections::HashSet;
use futures::{StreamExt, future};
use gpui::{AppContext as _, Context, TestAppContext, WindowHandle};
@@ -972,7 +972,7 @@ pub mod tests {
use language::{Language, LanguageConfig, LanguageMatcher};
use languages::rust_lang;
use lsp::{DEFAULT_LSP_REQUEST_TIMEOUT, FakeLanguageServer};
- use multi_buffer::{MultiBuffer, MultiBufferOffset};
+ use multi_buffer::{MultiBuffer, MultiBufferOffset, PathKey};
use parking_lot::Mutex;
use pretty_assertions::assert_eq;
use project::{FakeFs, Project};
@@ -2322,28 +2322,32 @@ pub mod tests {
.unwrap();
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0)),
- ExcerptRange::new(Point::new(4, 0)..Point::new(11, 0)),
- ExcerptRange::new(Point::new(22, 0)..Point::new(33, 0)),
- ExcerptRange::new(Point::new(44, 0)..Point::new(55, 0)),
- ExcerptRange::new(Point::new(56, 0)..Point::new(66, 0)),
- ExcerptRange::new(Point::new(67, 0)..Point::new(77, 0)),
+ Point::new(0, 0)..Point::new(2, 0),
+ Point::new(4, 0)..Point::new(11, 0),
+ Point::new(22, 0)..Point::new(33, 0),
+ Point::new(44, 0)..Point::new(55, 0),
+ Point::new(56, 0)..Point::new(66, 0),
+ Point::new(67, 0)..Point::new(77, 0),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 1)..Point::new(2, 1)),
- ExcerptRange::new(Point::new(4, 1)..Point::new(11, 1)),
- ExcerptRange::new(Point::new(22, 1)..Point::new(33, 1)),
- ExcerptRange::new(Point::new(44, 1)..Point::new(55, 1)),
- ExcerptRange::new(Point::new(56, 1)..Point::new(66, 1)),
- ExcerptRange::new(Point::new(67, 1)..Point::new(77, 1)),
+ Point::new(0, 1)..Point::new(2, 1),
+ Point::new(4, 1)..Point::new(11, 1),
+ Point::new(22, 1)..Point::new(33, 1),
+ Point::new(44, 1)..Point::new(55, 1),
+ Point::new(56, 1)..Point::new(66, 1),
+ Point::new(67, 1)..Point::new(77, 1),
],
+ 0,
cx,
);
multibuffer
@@ -2733,19 +2737,21 @@ let c = 3;"#
.unwrap();
let multi_buffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
[
- // Have first excerpt to spawn over 2 chunks (50 lines each).
- ExcerptRange::new(Point::new(49, 0)..Point::new(53, 0)),
- // Have 2nd excerpt to be in the 2nd chunk only.
- ExcerptRange::new(Point::new(70, 0)..Point::new(73, 0)),
+ Point::new(49, 0)..Point::new(53, 0),
+ Point::new(70, 0)..Point::new(73, 0),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(4, 0))],
+ [Point::new(0, 0)..Point::new(4, 0)],
+ 0,
cx,
);
multibuffer
@@ -2931,16 +2937,23 @@ let c = 3;"#
.unwrap();
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
- let buffer_1_excerpts = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
+ [Point::new(0, 0)..Point::new(2, 0)],
+ 0,
cx,
);
- let buffer_2_excerpts = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 1)..Point::new(2, 1))],
+ [Point::new(0, 1)..Point::new(2, 1)],
+ 0,
cx,
);
+ let excerpt_ids = multibuffer.excerpt_ids();
+ let buffer_1_excerpts = vec![excerpt_ids[0]];
+ let buffer_2_excerpts = vec![excerpt_ids[1]];
(buffer_1_excerpts, buffer_2_excerpts)
});
@@ -3047,7 +3060,7 @@ let c = 3;"#
editor
.update(cx, |editor, _, cx| {
editor.buffer().update(cx, |multibuffer, cx| {
- multibuffer.remove_excerpts(buffer_2_excerpts, cx)
+ multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx);
})
})
.unwrap();
@@ -4001,20 +4014,24 @@ let c = 3;"#
.unwrap();
let multi_buffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_2.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(10, 0)),
- ExcerptRange::new(Point::new(23, 0)..Point::new(34, 0)),
+ Point::new(0, 0)..Point::new(10, 0),
+ Point::new(23, 0)..Point::new(34, 0),
],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_1.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(10, 0)),
- ExcerptRange::new(Point::new(13, 0)..Point::new(23, 0)),
+ Point::new(0, 0)..Point::new(10, 0),
+ Point::new(13, 0)..Point::new(23, 0),
],
+ 0,
cx,
);
multibuffer
@@ -619,7 +619,7 @@ mod jsx_tag_autoclose_tests {
use super::*;
use gpui::{AppContext as _, TestAppContext};
use languages::language;
- use multi_buffer::{ExcerptRange, MultiBufferOffset};
+ use multi_buffer::{MultiBufferOffset, PathKey};
use text::Selection;
async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext {
@@ -816,21 +816,12 @@ mod jsx_tag_autoclose_tests {
let buffer_c = cx.new(|cx| language::Buffer::local("<span", cx));
let buffer = cx.new(|cx| {
let mut buf = MultiBuffer::new(language::Capability::ReadWrite);
- buf.push_excerpts(
- buffer_a,
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
- cx,
- );
- buf.push_excerpts(
- buffer_b,
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
- cx,
- );
- buf.push_excerpts(
- buffer_c,
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
- cx,
- );
+ let range_a = language::Point::zero()..buffer_a.read(cx).max_point();
+ let range_b = language::Point::zero()..buffer_b.read(cx).max_point();
+ let range_c = language::Point::zero()..buffer_c.read(cx).max_point();
+ buf.set_excerpts_for_path(PathKey::sorted(0), buffer_a, [range_a], 0, cx);
+ buf.set_excerpts_for_path(PathKey::sorted(1), buffer_b, [range_b], 0, cx);
+ buf.set_excerpts_for_path(PathKey::sorted(2), buffer_c, [range_c], 0, cx);
buf
});
let editor = cx.add_window(|window, cx| build_editor(buffer.clone(), window, cx));
@@ -911,12 +911,13 @@ pub fn split_display_range_by_lines(
mod tests {
use super::*;
use crate::{
- Buffer, DisplayMap, DisplayRow, ExcerptRange, FoldPlaceholder, MultiBuffer,
+ Buffer, DisplayMap, DisplayRow, FoldPlaceholder, MultiBuffer,
inlays::Inlay,
test::{editor_test_context::EditorTestContext, marked_display_snapshot},
};
use gpui::{AppContext as _, font, px};
use language::Capability;
+ use multi_buffer::PathKey;
use project::project_settings::DiagnosticSeverity;
use settings::SettingsStore;
use util::post_inc;
@@ -1229,15 +1230,17 @@ mod tests {
let font = font("Helvetica");
- let buffer = cx.new(|cx| Buffer::local("abc\ndefg\nhijkl\nmn", cx));
+ let buffer = cx.new(|cx| Buffer::local("abc\ndefg\na\na\na\nhijkl\nmn", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer.clone(),
[
- ExcerptRange::new(Point::new(0, 0)..Point::new(1, 4)),
- ExcerptRange::new(Point::new(2, 0)..Point::new(3, 2)),
+ Point::new(0, 0)..Point::new(1, 4),
+ Point::new(5, 0)..Point::new(6, 2),
],
+ 0,
cx,
);
multibuffer
@@ -467,7 +467,7 @@ mod tests {
use language::{Language, LanguageConfig, LanguageMatcher};
use languages::FakeLspAdapter;
use multi_buffer::{
- AnchorRangeExt, ExcerptRange, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset,
+ AnchorRangeExt, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset, PathKey,
};
use project::Project;
use rope::Point;
@@ -1163,14 +1163,18 @@ mod tests {
});
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
toml_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(0, 4)],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
rust_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(0, 4)],
+ 0,
cx,
);
multibuffer
@@ -1237,202 +1241,6 @@ mod tests {
);
}
- #[gpui::test]
- async fn lsp_semantic_tokens_multibuffer_shared(cx: &mut TestAppContext) {
- init_test(cx, |_| {});
-
- update_test_language_settings(cx, &|language_settings| {
- language_settings.languages.0.insert(
- "TOML".into(),
- LanguageSettingsContent {
- semantic_tokens: Some(SemanticTokens::Full),
- ..LanguageSettingsContent::default()
- },
- );
- });
-
- let toml_language = Arc::new(Language::new(
- LanguageConfig {
- name: "TOML".into(),
- matcher: LanguageMatcher {
- path_suffixes: vec!["toml".into()],
- ..LanguageMatcher::default()
- },
- ..LanguageConfig::default()
- },
- None,
- ));
-
- let toml_legend = lsp::SemanticTokensLegend {
- token_types: vec!["property".into()],
- token_modifiers: Vec::new(),
- };
-
- let app_state = cx.update(workspace::AppState::test);
-
- cx.update(|cx| {
- assets::Assets.load_test_fonts(cx);
- crate::init(cx);
- workspace::init(app_state.clone(), cx);
- });
-
- let project = Project::test(app_state.fs.clone(), [], cx).await;
- let language_registry = project.read_with(cx, |project, _| project.languages().clone());
- let full_counter_toml = Arc::new(AtomicUsize::new(0));
- let full_counter_toml_clone = full_counter_toml.clone();
-
- let mut toml_server = language_registry.register_fake_lsp(
- toml_language.name(),
- FakeLspAdapter {
- name: "toml",
- capabilities: lsp::ServerCapabilities {
- semantic_tokens_provider: Some(
- lsp::SemanticTokensServerCapabilities::SemanticTokensOptions(
- lsp::SemanticTokensOptions {
- legend: toml_legend,
- full: Some(lsp::SemanticTokensFullOptions::Delta { delta: None }),
- ..lsp::SemanticTokensOptions::default()
- },
- ),
- ),
- ..lsp::ServerCapabilities::default()
- },
- initializer: Some(Box::new({
- let full_counter_toml_clone = full_counter_toml_clone.clone();
- move |fake_server| {
- let full_counter = full_counter_toml_clone.clone();
- fake_server
- .set_request_handler::<lsp::request::SemanticTokensFullRequest, _, _>(
- move |_, _| {
- full_counter.fetch_add(1, atomic::Ordering::Release);
- async move {
- Ok(Some(lsp::SemanticTokensResult::Tokens(
- lsp::SemanticTokens {
- // highlight 'a' as a property
- data: vec![
- 0, // delta_line
- 0, // delta_start
- 1, // length
- 0, // token_type
- 0, // token_modifiers_bitset
- ],
- result_id: Some("a".into()),
- },
- )))
- }
- },
- );
- }
- })),
- ..FakeLspAdapter::default()
- },
- );
- language_registry.add(toml_language.clone());
-
- app_state
- .fs
- .as_fake()
- .insert_tree(
- EditorLspTestContext::root_path(),
- json!({
- ".git": {},
- "dir": {
- "foo.toml": "a = 1\nb = 2\n",
- }
- }),
- )
- .await;
-
- let (multi_workspace, cx) =
- cx.add_window_view(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
- let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
- project
- .update(cx, |project, cx| {
- project.find_or_create_worktree(EditorLspTestContext::root_path(), true, cx)
- })
- .await
- .unwrap();
- cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
- .await;
-
- let toml_file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
- let toml_item = workspace
- .update_in(cx, |workspace, window, cx| {
- workspace.open_path(toml_file, None, true, window, cx)
- })
- .await
- .expect("Could not open test file");
-
- let toml_editor = cx.update(|_, cx| {
- toml_item
- .act_as::<Editor>(cx)
- .expect("Opened test file wasn't an editor")
- });
- let toml_buffer = cx.read(|cx| {
- toml_editor
- .read(cx)
- .buffer()
- .read(cx)
- .as_singleton()
- .unwrap()
- });
- let multibuffer = cx.new(|cx| {
- let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
- toml_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
- cx,
- );
- multibuffer.push_excerpts(
- toml_buffer.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))],
- cx,
- );
- multibuffer
- });
-
- let editor = workspace.update_in(cx, |_, window, cx| {
- cx.new(|cx| build_editor_with_project(project, multibuffer, window, cx))
- });
- editor.update_in(cx, |editor, window, cx| {
- let nav_history = workspace
- .read(cx)
- .active_pane()
- .read(cx)
- .nav_history_for_item(&cx.entity());
- editor.set_nav_history(Some(nav_history));
- window.focus(&editor.focus_handle(cx), cx)
- });
-
- let _toml_server = toml_server.next().await.unwrap();
-
- // Initial request.
- cx.executor().advance_clock(Duration::from_millis(200));
- let task = editor.update_in(cx, |e, _, _| e.semantic_token_state.take_update_task());
- cx.run_until_parked();
- task.await;
- assert_eq!(full_counter_toml.load(atomic::Ordering::Acquire), 1);
-
- // Edit two parts of the multibuffer, which both map to the same buffer.
- //
- // Without debouncing, this grabs semantic tokens 4 times (twice for the
- // toml editor, and twice for the multibuffer).
- editor.update_in(cx, |editor, _, cx| {
- editor.edit([(MultiBufferOffset(0)..MultiBufferOffset(1), "b")], cx);
- editor.edit([(MultiBufferOffset(12)..MultiBufferOffset(13), "c")], cx);
- });
- cx.executor().advance_clock(Duration::from_millis(200));
- let task = editor.update_in(cx, |e, _, _| e.semantic_token_state.take_update_task());
- cx.run_until_parked();
- task.await;
- assert_eq!(
- extract_semantic_highlights(&editor, &cx),
- vec![MultiBufferOffset(0)..MultiBufferOffset(1)]
- );
-
- assert_eq!(full_counter_toml.load(atomic::Ordering::Acquire), 2);
- }
-
fn extract_semantic_highlights(
editor: &Entity<Editor>,
cx: &TestAppContext,
@@ -13,7 +13,7 @@ use gpui::{
};
use itertools::Itertools;
use language::{Buffer, BufferSnapshot, LanguageRegistry};
-use multi_buffer::{Anchor, ExcerptRange, MultiBufferOffset, MultiBufferRow};
+use multi_buffer::{Anchor, ExcerptRange, MultiBufferOffset, MultiBufferRow, PathKey};
use parking_lot::RwLock;
use project::{FakeFs, Project};
use std::{
@@ -128,10 +128,26 @@ impl EditorTestContext {
) -> EditorTestContext {
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
let buffer = cx.new(|cx| {
- for excerpt in excerpts.into_iter() {
+ for (index, excerpt) in excerpts.into_iter().enumerate() {
let (text, ranges) = marked_text_ranges(excerpt, false);
let buffer = cx.new(|cx| Buffer::local(text, cx));
- multibuffer.push_excerpts(buffer, ranges.into_iter().map(ExcerptRange::new), cx);
+ let point_ranges: Vec<_> = {
+ let snapshot = buffer.read(cx);
+ ranges
+ .into_iter()
+ .map(|range| {
+ snapshot.offset_to_point(range.start)
+ ..snapshot.offset_to_point(range.end)
+ })
+ .collect()
+ };
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(index as u64),
+ buffer,
+ point_ranges,
+ 0,
+ cx,
+ );
}
multibuffer
});
@@ -145,11 +145,7 @@ impl TextDiffView {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(language::Capability::ReadWrite);
- multibuffer.push_excerpts(
- source_buffer.clone(),
- [editor::ExcerptRange::new(source_range)],
- cx,
- );
+ multibuffer.set_excerpts_for_buffer(source_buffer.clone(), [source_range], 0, cx);
multibuffer.add_diff(diff_buffer.clone(), cx);
multibuffer
@@ -1160,12 +1160,11 @@ impl MultiBuffer {
},
);
this.singleton = true;
- let buffer_id = buffer.read(cx).remote_id();
- this.push_excerpts(
- buffer,
- [ExcerptRange::new(text::Anchor::min_max_range_for_buffer(
- buffer_id,
- ))],
+ this.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ [Point::zero()..buffer.read(cx).max_point()],
+ 0,
cx,
);
this
@@ -1734,18 +1733,6 @@ impl MultiBuffer {
}
}
- pub fn push_excerpts<O>(
- &mut self,
- buffer: Entity<Buffer>,
- ranges: impl IntoIterator<Item = ExcerptRange<O>>,
- cx: &mut Context<Self>,
- ) -> Vec<ExcerptId>
- where
- O: text::ToOffset,
- {
- self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx)
- }
-
#[instrument(skip_all)]
fn merge_excerpt_ranges<'a>(
expanded_ranges: impl IntoIterator<Item = &'a ExcerptRange<Point>> + 'a,
@@ -3749,11 +3736,21 @@ impl MultiBuffer {
cx: &mut gpui::App,
) -> Entity<Self> {
let multi = cx.new(|_| Self::new(Capability::ReadWrite));
- for (text, ranges) in excerpts {
+ for (ix, (text, ranges)) in excerpts.into_iter().enumerate() {
let buffer = cx.new(|cx| Buffer::local(text, cx));
- let excerpt_ranges = ranges.into_iter().map(ExcerptRange::new);
+ let snapshot = buffer.read(cx).snapshot();
+ let excerpt_ranges = ranges
+ .into_iter()
+ .map(ExcerptRange::new)
+ .collect::<Vec<_>>();
multi.update(cx, |multi, cx| {
- multi.push_excerpts(buffer, excerpt_ranges, cx)
+ multi.set_excerpt_ranges_for_path(
+ PathKey::sorted(ix as u64),
+ buffer,
+ &snapshot,
+ excerpt_ranges,
+ cx,
+ )
});
}
@@ -3887,7 +3884,8 @@ impl MultiBuffer {
.collect::<Vec<_>>()
);
- let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
+ let excerpt_id =
+ self.insert_excerpts_after(ExcerptId::max(), buffer_handle, ranges, cx);
log::info!("Inserted with ids: {:?}", excerpt_id);
} else {
let remove_count = rng.random_range(1..=excerpt_ids.len());
@@ -105,8 +105,8 @@ fn test_remote(cx: &mut App) {
#[gpui::test]
fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
- let buffer_1 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
- let buffer_2 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
+ let buffer_1 = cx.new(|cx| Buffer::local(sample_text(7, 6, 'a'), cx));
+ let buffer_2 = cx.new(|cx| Buffer::local(sample_text(7, 6, 'g'), cx));
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let events = Arc::new(RwLock::new(Vec::<Event>::new()));
@@ -122,9 +122,11 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
let subscription = multibuffer.update(cx, |multibuffer, cx| {
let subscription = multibuffer.subscribe();
- multibuffer.push_excerpts(
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(1, 2)..Point::new(2, 5))],
+ &buffer_1.read(cx).snapshot(),
+ vec![ExcerptRange::new(Point::new(1, 2)..Point::new(2, 5))],
cx,
);
assert_eq!(
@@ -135,14 +137,21 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
}]
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(3, 3)..Point::new(4, 4))],
+ &buffer_1.read(cx).snapshot(),
+ vec![
+ ExcerptRange::new(Point::new(1, 2)..Point::new(2, 5)),
+ ExcerptRange::new(Point::new(5, 3)..Point::new(6, 4)),
+ ],
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(3, 1)..Point::new(3, 3))],
+ &buffer_2.read(cx).snapshot(),
+ vec![ExcerptRange::new(Point::new(3, 1)..Point::new(3, 3))],
cx,
);
assert_eq!(
@@ -179,8 +188,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
"
bbbb
ccccc
- ddd
- eeee
+ fff
+ gggg
jj"
),
);
@@ -189,14 +198,14 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
.row_infos(MultiBufferRow(0))
.map(|info| info.buffer_row)
.collect::<Vec<_>>(),
- [Some(1), Some(2), Some(3), Some(4), Some(3)]
+ [Some(1), Some(2), Some(5), Some(6), Some(3)]
);
assert_eq!(
snapshot
.row_infos(MultiBufferRow(2))
.map(|info| info.buffer_row)
.collect::<Vec<_>>(),
- [Some(3), Some(4), Some(3)]
+ [Some(5), Some(6), Some(3)]
);
assert_eq!(
snapshot
@@ -217,7 +226,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
&[
(MultiBufferRow(0), "bbbb\nccccc".to_string(), true),
- (MultiBufferRow(2), "ddd\neeee".to_string(), false),
+ (MultiBufferRow(2), "fff\ngggg".to_string(), false),
(MultiBufferRow(4), "jj".to_string(), true),
]
);
@@ -235,15 +244,15 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
);
assert_eq!(
boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
- &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
+ &[(MultiBufferRow(2), "fff\ngggg".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
- &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
+ &[(MultiBufferRow(2), "fff\ngggg".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
- &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
+ &[(MultiBufferRow(2), "fff\ngggg".to_string(), false)]
);
assert_eq!(
boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
@@ -273,8 +282,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
"bbbb\n", // Preserve newlines
"c\n", //
"cc\n", //
- "ddd\n", //
- "eeee\n", //
+ "fff\n", //
+ "gggg\n", //
"jj" //
)
);
@@ -310,9 +319,7 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
);
let snapshot = multibuffer.update(cx, |multibuffer, cx| {
- let (buffer_2_excerpt_id, _) =
- multibuffer.excerpts_for_buffer(buffer_2.read(cx).remote_id(), cx)[0].clone();
- multibuffer.remove_excerpts([buffer_2_excerpt_id], cx);
+ multibuffer.remove_excerpts_for_path(PathKey::sorted(1), cx);
multibuffer.snapshot(cx)
});
@@ -322,8 +329,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
"bbbb\n", // Preserve newlines
"c\n", //
"cc\n", //
- "ddd\n", //
- "eeee", //
+ "fff\n", //
+ "gggg", //
)
);
@@ -747,18 +754,29 @@ fn test_excerpt_events(cx: &mut App) {
.detach();
});
+ let buffer_1_snapshot = buffer_1.read(cx).snapshot();
+ let buffer_2_snapshot = buffer_2.read(cx).snapshot();
leader_multibuffer.update(cx, |leader, cx| {
- leader.push_excerpts(
+ leader.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(0..8), ExcerptRange::new(12..16)],
+ &buffer_1_snapshot,
+ vec![
+ ExcerptRange::new((0..8).to_point(&buffer_1_snapshot)),
+ ExcerptRange::new((22..26).to_point(&buffer_1_snapshot)),
+ ],
cx,
);
- leader.insert_excerpts_after(
- leader.excerpt_ids()[0],
+ leader.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(0..5), ExcerptRange::new(10..15)],
+ &buffer_2_snapshot,
+ vec![
+ ExcerptRange::new((0..5).to_point(&buffer_2_snapshot)),
+ ExcerptRange::new((20..25).to_point(&buffer_2_snapshot)),
+ ],
cx,
- )
+ );
});
assert_eq!(
leader_multibuffer.read(cx).snapshot(cx).text(),
@@ -767,34 +785,26 @@ fn test_excerpt_events(cx: &mut App) {
assert_eq!(*follower_edit_event_count.read(), 2);
leader_multibuffer.update(cx, |leader, cx| {
- let excerpt_ids = leader.excerpt_ids();
- leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
- });
- assert_eq!(
- leader_multibuffer.read(cx).snapshot(cx).text(),
- follower_multibuffer.read(cx).snapshot(cx).text(),
- );
- assert_eq!(*follower_edit_event_count.read(), 3);
-
- // Removing an empty set of excerpts is a noop.
- leader_multibuffer.update(cx, |leader, cx| {
- leader.remove_excerpts([], cx);
- });
- assert_eq!(
- leader_multibuffer.read(cx).snapshot(cx).text(),
- follower_multibuffer.read(cx).snapshot(cx).text(),
- );
- assert_eq!(*follower_edit_event_count.read(), 3);
-
- // Adding an empty set of excerpts is a noop.
- leader_multibuffer.update(cx, |leader, cx| {
- leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
+ leader.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
+ buffer_1.clone(),
+ &buffer_1_snapshot,
+ vec![ExcerptRange::new((0..8).to_point(&buffer_1_snapshot))],
+ cx,
+ );
+ leader.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
+ buffer_2,
+ &buffer_2_snapshot,
+ vec![ExcerptRange::new((0..5).to_point(&buffer_2_snapshot))],
+ cx,
+ );
});
assert_eq!(
leader_multibuffer.read(cx).snapshot(cx).text(),
follower_multibuffer.read(cx).snapshot(cx).text(),
);
- assert_eq!(*follower_edit_event_count.read(), 3);
+ assert_eq!(*follower_edit_event_count.read(), 4);
leader_multibuffer.update(cx, |leader, cx| {
leader.clear(cx);
@@ -803,7 +813,7 @@ fn test_excerpt_events(cx: &mut App) {
leader_multibuffer.read(cx).snapshot(cx).text(),
follower_multibuffer.read(cx).snapshot(cx).text(),
);
- assert_eq!(*follower_edit_event_count.read(), 4);
+ assert_eq!(*follower_edit_event_count.read(), 5);
}
#[gpui::test]
@@ -1013,7 +1023,13 @@ async fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
let diff = cx
.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer.read(cx).text_snapshot(), cx));
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.push_excerpts(buffer.clone(), [ExcerptRange::new(0..0)], cx);
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
+ buffer.clone(),
+ &buffer.read(cx).snapshot(),
+ vec![ExcerptRange::new(Point::zero()..Point::zero())],
+ cx,
+ );
multibuffer.set_all_diff_hunks_expanded(cx);
multibuffer.add_diff(diff.clone(), cx);
});
@@ -1031,7 +1047,13 @@ async fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
let buf2 = cx.new(|cx| Buffer::local("X", cx));
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.push_excerpts(buf2, [ExcerptRange::new(0..1)], cx);
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
+ buf2,
+ [Point::new(0, 0)..Point::new(0, 1)],
+ 0,
+ cx,
+ );
});
buffer.update(cx, |buffer, cx| {
@@ -1105,8 +1127,20 @@ fn test_multibuffer_anchors(cx: &mut App) {
let buffer_2 = cx.new(|cx| Buffer::local("efghi", cx));
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..4)], cx);
- multibuffer.push_excerpts(buffer_2.clone(), [ExcerptRange::new(0..5)], cx);
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer_1.clone(),
+ [Point::new(0, 0)..Point::new(0, 4)],
+ 0,
+ cx,
+ );
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
+ buffer_2.clone(),
+ [Point::new(0, 0)..Point::new(0, 5)],
+ 0,
+ cx,
+ );
multibuffer
});
let old_snapshot = multibuffer.read(cx).snapshot(cx);
@@ -1219,29 +1253,39 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
// Add an excerpt from buffer 1 that spans this new insertion.
buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
- multibuffer
- .push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..7)], cx)
- .pop()
- .unwrap()
+ let buffer_1_snapshot = buffer_1.read(cx).snapshot();
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(0),
+ buffer_1,
+ &buffer_1_snapshot,
+ vec![ExcerptRange::new((0..7).to_point(&buffer_1_snapshot))],
+ cx,
+ );
+ multibuffer.excerpt_ids().into_iter().next().unwrap()
});
let snapshot_1 = multibuffer.read(cx).snapshot(cx);
assert_eq!(snapshot_1.text(), "abcd123");
// Replace the buffer 1 excerpt with new excerpts from buffer 2.
- let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.remove_excerpts([excerpt_id_1], cx);
+ let (excerpt_id_2, _excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
+ multibuffer.remove_excerpts_for_path(PathKey::sorted(0), cx);
+ let snapshot_2 = buffer_2.read(cx).snapshot();
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
+ buffer_2.clone(),
+ &buffer_2.read(cx).snapshot(),
+ vec![
+ ExcerptRange::new((0..4).to_point(&snapshot_2)),
+ ExcerptRange::new((6..10).to_point(&snapshot_2)),
+ ExcerptRange::new((12..16).to_point(&snapshot_2)),
+ ],
+ cx,
+ );
let mut ids = multibuffer
- .push_excerpts(
- buffer_2.clone(),
- [
- ExcerptRange::new(0..4),
- ExcerptRange::new(6..10),
- ExcerptRange::new(12..16),
- ],
- cx,
- )
- .into_iter();
+ .excerpts_for_buffer(buffer_2.read(cx).remote_id(), cx)
+ .into_iter()
+ .map(|(id, _)| id);
(ids.next().unwrap(), ids.next().unwrap())
});
let snapshot_2 = multibuffer.read(cx).snapshot(cx);
@@ -1283,22 +1327,33 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
// Replace the middle excerpt with a smaller excerpt in buffer 2,
// that intersects the old excerpt.
- let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.remove_excerpts([excerpt_id_3], cx);
- multibuffer
- .insert_excerpts_after(
- excerpt_id_2,
- buffer_2.clone(),
- [ExcerptRange::new(5..8)],
- cx,
- )
- .pop()
- .unwrap()
+ multibuffer.update(cx, |multibuffer, cx| {
+ let snapshot_2 = buffer_2.read(cx).snapshot();
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
+ buffer_2.clone(),
+ &buffer_2.read(cx).snapshot(),
+ vec![
+ ExcerptRange::new((0..4).to_point(&snapshot_2)),
+ ExcerptRange::new((12..16).to_point(&snapshot_2)),
+ ],
+ cx,
+ );
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
+ buffer_2.clone(),
+ &buffer_2.read(cx).snapshot(),
+ vec![
+ ExcerptRange::new((0..4).to_point(&snapshot_2)),
+ ExcerptRange::new((5..8).to_point(&snapshot_2)),
+ ExcerptRange::new((12..16).to_point(&snapshot_2)),
+ ],
+ cx,
+ );
});
let snapshot_3 = multibuffer.read(cx).snapshot(cx);
assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP");
- assert_ne!(excerpt_id_5, excerpt_id_3);
// Resolve some anchors from the previous snapshot in the new snapshot.
// The third anchor can't be resolved, since its excerpt has been removed,
@@ -2149,14 +2204,18 @@ async fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..buffer_1.read(cx).max_point()],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..buffer_2.read(cx).max_point()],
+ 0,
cx,
);
multibuffer.add_diff(diff_1.clone(), cx);
@@ -3431,14 +3490,18 @@ fn test_history(cx: &mut App) {
this.set_group_interval(group_interval);
});
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(0..buffer_1.read(cx).len())],
+ [Point::zero()..buffer_1.read(cx).max_point()],
+ 0,
cx,
);
- multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(0..buffer_2.read(cx).len())],
+ [Point::zero()..buffer_2.read(cx).max_point()],
+ 0,
cx,
);
});
@@ -3691,18 +3754,23 @@ async fn test_summaries_for_anchors(cx: &mut TestAppContext) {
let multibuffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
multibuffer.set_all_diff_hunks_expanded(cx);
- ids.extend(multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..buffer_1.read(cx).max_point()],
+ 0,
cx,
- ));
- ids.extend(multibuffer.push_excerpts(
+ );
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
+ [Point::zero()..buffer_2.read(cx).max_point()],
+ 0,
cx,
- ));
+ );
multibuffer.add_diff(diff_1.clone(), cx);
multibuffer.add_diff(diff_2.clone(), cx);
+ ids = multibuffer.excerpt_ids();
multibuffer
});
@@ -3747,7 +3815,14 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
cx.run_until_parked();
let multibuffer = cx.new(|cx| {
- let mut multibuffer = MultiBuffer::singleton(buffer_1.clone(), cx);
+ let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
+ buffer_1.clone(),
+ [Point::zero()..buffer_1.read(cx).max_point()],
+ 0,
+ cx,
+ );
multibuffer.add_diff(diff_1.clone(), cx);
multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
multibuffer
@@ -3790,9 +3865,11 @@ async fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
let text_2 = "foo\n".to_owned();
let buffer_2 = cx.new(|cx| Buffer::local(&text_2, cx));
multibuffer.update(cx, |multibuffer, cx| {
- multibuffer.push_excerpts(
+ multibuffer.set_excerpt_ranges_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ &buffer_2.read(cx).snapshot(),
+ vec![ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
cx,
);
});
@@ -4893,25 +4970,36 @@ fn test_excerpts_containment_functions(cx: &mut App) {
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let (excerpt_1_id, excerpt_2_id, excerpt_3_id) = multibuffer.update(cx, |multibuffer, cx| {
- let excerpt_1_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 3))],
+ [Point::new(0, 0)..Point::new(1, 3)],
+ 0,
cx,
- )[0];
+ );
- let excerpt_2_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 3))],
+ [Point::new(0, 0)..Point::new(1, 3)],
+ 0,
cx,
- )[0];
+ );
- let excerpt_3_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(2),
buffer_3.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(0, 3))],
+ [Point::new(0, 0)..Point::new(0, 3)],
+ 0,
cx,
- )[0];
+ );
- (excerpt_1_id, excerpt_2_id, excerpt_3_id)
+ let mut ids = multibuffer.excerpt_ids().into_iter();
+ (
+ ids.next().unwrap(),
+ ids.next().unwrap(),
+ ids.next().unwrap(),
+ )
});
let snapshot = multibuffer.read(cx).snapshot(cx);
@@ -4996,19 +5084,25 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let (excerpt_1_id, excerpt_2_id) = multibuffer.update(cx, |multibuffer, cx| {
- let excerpt_1_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 3))],
+ [Point::new(0, 0)..Point::new(1, 3)],
+ 0,
cx,
- )[0];
+ );
- let excerpt_2_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_2.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(0, 3))],
+ [Point::new(0, 0)..Point::new(0, 3)],
+ 0,
cx,
- )[0];
+ );
- (excerpt_1_id, excerpt_2_id)
+ let excerpt_ids = multibuffer.excerpt_ids();
+
+ (excerpt_ids[0], excerpt_ids[1])
});
let snapshot = multibuffer.read(cx).snapshot(cx);
@@ -5058,19 +5152,24 @@ fn test_range_to_buffer_ranges_with_range_bounds(cx: &mut App) {
let multibuffer_trailing_empty = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
let (te_excerpt_1_id, te_excerpt_2_id) =
multibuffer_trailing_empty.update(cx, |multibuffer, cx| {
- let excerpt_1_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer_1.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 3))],
+ [Point::new(0, 0)..Point::new(1, 3)],
+ 0,
cx,
- )[0];
+ );
- let excerpt_2_id = multibuffer.push_excerpts(
+ multibuffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer_empty.clone(),
- [ExcerptRange::new(Point::new(0, 0)..Point::new(0, 0))],
+ [Point::new(0, 0)..Point::new(0, 0)],
+ 0,
cx,
- )[0];
+ );
- (excerpt_1_id, excerpt_2_id)
+ let excerpt_ids = multibuffer.excerpt_ids();
+ (excerpt_ids[0], excerpt_ids[1])
});
let snapshot_trailing = multibuffer_trailing_empty.read(cx).snapshot(cx);
@@ -5,7 +5,7 @@ use gpui::{App, AppContext, Context, Entity};
use itertools::Itertools;
use language::{Buffer, BufferSnapshot};
use rope::Point;
-use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};
+use text::{Bias, OffsetRangeExt, locator::Locator};
use util::{post_inc, rel_path::RelPath};
use ztracing::instrument;
@@ -27,6 +27,12 @@ pub struct PathKey {
}
impl PathKey {
+ pub fn sorted(sort_prefix: u64) -> Self {
+ Self {
+ sort_prefix: Some(sort_prefix),
+ path: RelPath::empty().into_arc(),
+ }
+ }
pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
Self {
sort_prefix: Some(sort_prefix),
@@ -86,6 +92,17 @@ impl MultiBuffer {
Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
}
+ pub fn set_excerpts_for_buffer(
+ &mut self,
+ buffer: Entity<Buffer>,
+ ranges: impl IntoIterator<Item = Range<Point>>,
+ context_line_count: u32,
+ cx: &mut Context<Self>,
+ ) -> (Vec<Range<Anchor>>, bool) {
+ let path = PathKey::for_buffer(&buffer, cx);
+ self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
+ }
+
/// Sets excerpts, returns `true` if at least one new excerpt was added.
#[instrument(skip_all)]
pub fn set_excerpts_for_path(
@@ -172,15 +189,6 @@ impl MultiBuffer {
}
}
- pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
- self.remove_excerpts(
- self.excerpts_for_buffer(buffer, cx)
- .into_iter()
- .map(|(excerpt, _)| excerpt),
- cx,
- );
- }
-
pub(super) fn expand_excerpts_with_paths(
&mut self,
ids: impl IntoIterator<Item = ExcerptId>,
@@ -1876,7 +1876,7 @@ mod tests {
use super::*;
use editor::{
- DisplayPoint, Editor, ExcerptRange, MultiBuffer, SearchSettings, SelectionEffects,
+ DisplayPoint, Editor, MultiBuffer, PathKey, SearchSettings, SelectionEffects,
display_map::DisplayRow, test::editor_test_context::EditorTestContext,
};
use gpui::{Hsla, TestAppContext, UpdateGlobal, VisualTestContext};
@@ -1934,14 +1934,18 @@ mod tests {
let mut buffer = MultiBuffer::new(language::Capability::ReadWrite);
//[ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))]
- buffer.push_excerpts(
+ buffer.set_excerpts_for_path(
+ PathKey::sorted(0),
buffer1,
- [ExcerptRange::new(Point::new(0, 0)..Point::new(3, 0))],
+ [Point::new(0, 0)..Point::new(3, 0)],
+ 0,
cx,
);
- buffer.push_excerpts(
+ buffer.set_excerpts_for_path(
+ PathKey::sorted(1),
buffer2,
- [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
+ [Point::new(0, 0)..Point::new(1, 0)],
+ 0,
cx,
);