diff --git a/Cargo.lock b/Cargo.lock index 4a7097d4c1c6f2b9e56fb873b32b2da71e4d36ea..2e08fe8eed35168b13df21f1c4ab5d01ba1664c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4640,7 +4640,6 @@ dependencies = [ "sysinfo 0.37.2", "task", "tasks_ui", - "telemetry", "terminal_view", "text", "theme", diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index 79d9a4052a6502412f82a9d614f118b8ecbaa2c9..9ac84addcc80c806739570ad9951209f16c31bb1 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -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, ); }); diff --git a/crates/copilot/src/copilot_edit_prediction_delegate.rs b/crates/copilot/src/copilot_edit_prediction_delegate.rs index 6bb36b616f782e8109255b4bca9a643060399962..cfb5eef7e08caab8fec624a1cf364eecac16ec9b 100644 --- a/crates/copilot/src/copilot_edit_prediction_delegate.rs +++ b/crates/copilot/src/copilot_edit_prediction_delegate.rs @@ -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! { " + § + § ----- + a = 1 + b = 2 + a + § + § ----- + 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! { " + § + § ----- + a = 1 + b = 2 + § + § ----- + 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! {" + § + § ----- + a = 1 + b = 2 + § + § ----- + 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! {" + § + § ----- + a = 1 + b = 2 + § + § ----- + 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 diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index fb79b1b0790b28d7204774720bf9c413cfed64e6..f95712b05129b7f86699f658c4c2c3effbd7d216 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -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 diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 310a9036821a7071199eed2c22231fa8a8be18a0..124967650b31cd88e72b2867838fb3a4ecbcf920 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -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::(cx) else { - return; - }; - - if let Some(existing) = workspace.item_of_type::(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(); diff --git a/crates/debugger_ui/src/session.rs b/crates/debugger_ui/src/session.rs index fc11f40e851a48ff9d0d4634eb69dd899b48a113..32d3e5992a87b99978b5cd0332576d6f5872aad6 100644 --- a/crates/debugger_ui/src/session.rs +++ b/crates/debugger_ui/src/session.rs @@ -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, pub(crate) running_state: Entity, pub(crate) quirks: SessionQuirks, - stack_trace_view: OnceCell>, - _worktree_store: WeakEntity, - workspace: WeakEntity, } 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, - window: &mut Window, - cx: &mut Context, - ) -> &Entity { - 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 { self.running_state.read(cx).session().clone() } diff --git a/crates/debugger_ui/src/session/running/stack_frame_list.rs b/crates/debugger_ui/src/session/running/stack_frame_list.rs index ccdfa22e89f449d2d40ae72f6b794b27ee6c8934..3e8a28a40bfc194413e0bf19d371a86609ba58c7 100644 --- a/crates/debugger_ui/src/session/running/stack_frame_list.rs +++ b/crates/debugger_ui/src/session/running/stack_frame_list.rs @@ -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::(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() diff --git a/crates/debugger_ui/src/stack_trace_view.rs b/crates/debugger_ui/src/stack_trace_view.rs deleted file mode 100644 index 02d6e793f290863cc002101bba00ac7a46446bb7..0000000000000000000000000000000000000000 --- a/crates/debugger_ui/src/stack_trace_view.rs +++ /dev/null @@ -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, - multibuffer: Entity, - workspace: WeakEntity, - project: Entity, - stack_frame_list: Entity, - selected_stack_frame_id: Option, - highlights: Vec<(StackFrameId, Anchor)>, - excerpt_for_frames: collections::HashMap, - refresh_task: Option>>, - _subscription: Option, -} - -impl StackTraceView { - pub(crate) fn new( - workspace: WeakEntity, - project: Entity, - stack_frame_list: Entity, - window: &mut Window, - cx: &mut Context, - ) -> 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.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.editor.update(cx, |editor, _| { - editor.clear_row_highlights::() - }); - - 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::( - 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) -> impl IntoElement { - div().size_full().child(self.editor.clone()) - } -} - -impl EventEmitter 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.editor - .update(cx, |editor, cx| editor.deactivated(window, cx)); - } - - fn navigate( - &mut self, - data: Arc, - window: &mut Window, - cx: &mut Context, - ) -> bool { - self.editor - .update(cx, |editor, cx| editor.navigate(data, window, cx)) - } - - fn tab_tooltip_text(&self, _: &App) -> Option { - 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.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, - window: &mut Window, - cx: &mut Context, - ) -> Task> { - self.editor.save(options, project, window, cx) - } - - fn save_as( - &mut self, - _: Entity, - _: ProjectPath, - _window: &mut Window, - _: &mut Context, - ) -> Task> { - unreachable!() - } - - fn reload( - &mut self, - project: Entity, - window: &mut Window, - cx: &mut Context, - ) -> Task> { - self.editor.reload(project, window, cx) - } - - fn act_as_type<'a>( - &'a self, - type_id: TypeId, - self_handle: &'a Entity, - _: &'a App, - ) -> Option { - if type_id == TypeId::of::() { - Some(self_handle.clone().into()) - } else if type_id == TypeId::of::() { - Some(self.editor.clone().into()) - } else { - None - } - } - - fn as_searchable(&self, _: &Entity, _: &App) -> Option> { - Some(Box::new(self.editor.clone())) - } - - fn breadcrumb_location(&self, _: &App) -> ToolbarItemLocation { - ToolbarItemLocation::PrimaryLeft - } - - fn breadcrumbs(&self, cx: &App) -> Option> { - self.editor.breadcrumbs(cx) - } - - fn added_to_workspace( - &mut self, - workspace: &mut Workspace, - window: &mut Window, - cx: &mut Context, - ) { - self.editor.update(cx, |editor, cx| { - editor.added_to_workspace(workspace, window, cx) - }); - } -} diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 6df124c48472134b50b85241511f16f93cc4ad08..57ce6f03d2b56c9441bee763a28dcc7010f8311e 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -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); }); } } diff --git a/crates/edit_prediction_ui/src/rate_prediction_modal.rs b/crates/edit_prediction_ui/src/rate_prediction_modal.rs index 4dba14e2bf449fe4744f31f3f875599f7dc02692..d07dbe9bad72c2252ee2e33c8a014778d1331e96 100644 --- a/crates/edit_prediction_ui/src/rate_prediction_modal.rs +++ b/crates/edit_prediction_ui/src/rate_prediction_modal.rs @@ -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)) = diff --git a/crates/editor/src/bracket_colorization.rs b/crates/editor/src/bracket_colorization.rs index c4308075a8823819fc871f6c9a36b9dea56d2172..bf71d5c71c9580d04e0c61047215992c5cbd4a26 100644 --- a/crates/editor/src/bracket_colorization.rs +++ b/crates/editor/src/bracket_colorization.rs @@ -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«34»>3» = None; // a // b - + // c fn process_data_2«2()2» «2{ let other_map: Option«34»>3» = None; @@ -1331,7 +1333,7 @@ mod foo «1{ let map: Option«34»>3» = None; // a // b - + // c fn process_data_2«2()2» «2{ let other_map: Option«34»>3» = None; @@ -1381,7 +1383,7 @@ mod foo «1{ let map: Option«12»>1» = None; // a // b - + // c fn process_data_2«2()2» «2{ let other_map: Option«12»>1» = None; diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 8f30264a7f8ef6af86eedfb772235bfbe2d17465..db7eb53b39088c6026d3d36bef636f748c80d587 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -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); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 645f8053a868e4397207ed996d037cdbcb7bacd9..9a0033306a1032786535a188a9ea830cb44c3ca3 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -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::(move |params, _| async move { + .on_request::(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 + // 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::>() - ); - - 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::>() - ); - }); -} - #[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 }); @@ -21451,26 +21481,20 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) { ˇaaa ccc ddd - ggg hhh - lll mmm NNN - qqq rrr - uuu 111 222 333 - 666 777 - 000 !!!" .unindent(), @@ -21488,27 +21512,21 @@ async fn test_toggle_diff_expand_in_multi_buffer(cx: &mut TestAppContext) { - bbb ccc ddd - ggg hhh - lll mmm - nnn + NNN - qqq rrr - uuu 111 222 333 - + 666 777 - 000 !!!ˇ»" .unindent(), @@ -21525,15 +21543,18 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) { let buffer = cx.new(|cx| Buffer::local(text.to_string(), cx)); let multi_buffer = 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(2, 0)), - ExcerptRange::new(Point::new(4, 0)..Point::new(7, 0)), - ExcerptRange::new(Point::new(9, 0)..Point::new(10, 0)), + Point::new(0, 0)..Point::new(1, 3), + Point::new(4, 0)..Point::new(6, 3), + Point::new(9, 0)..Point::new(9, 3), ], + 0, cx, ); + assert_eq!(multibuffer.excerpt_ids().len(), 3); multibuffer }); @@ -21566,15 +21587,12 @@ async fn test_expand_diff_hunk_at_excerpt_boundary(cx: &mut TestAppContext) { ˇaaa - bbb + BBB - - ddd - eee + DDD + EEE fff - - iii - " + iii" .unindent(), ); } @@ -24164,31 +24182,37 @@ async fn test_folding_buffers(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, 0), + Point::new(5, 0)..Point::new(6, 0), + Point::new(9, 0)..Point::new(10, 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, 0), + Point::new(5, 0)..Point::new(6, 0), + Point::new(9, 0)..Point::new(10, 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, 0), + Point::new(5, 0)..Point::new(6, 0), + Point::new(9, 0)..Point::new(10, 4), ], + 0, cx, ); multi_buffer @@ -24205,7 +24229,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\naaaa\nbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", + "\n\naaaa\nbbbb\ncccc\n\nffff\ngggg\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\nqqqq\nrrrr\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n1111\n2222\n\n5555", ); multi_buffer_editor.update(cx, |editor, cx| { @@ -24213,7 +24237,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", + "\n\n\n\nllll\nmmmm\nnnnn\n\nqqqq\nrrrr\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n1111\n2222\n\n5555", "After folding the first buffer, its text should not be displayed" ); @@ -24222,7 +24246,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", + "\n\n\n\n\n\nvvvv\nwwww\nxxxx\n\n1111\n2222\n\n5555", "After folding the second buffer, its text should not be displayed" ); @@ -24247,7 +24271,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n", + "\n\n\n\nllll\nmmmm\nnnnn\n\nqqqq\nrrrr\n\nuuuu\n\n", "After unfolding the second buffer, its text should be displayed" ); @@ -24269,7 +24293,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\naaaa\nBbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n", + "\n\naaaa\nBbbbb\ncccc\n\nffff\ngggg\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\nqqqq\nrrrr\n\nuuuu\n\n", "After unfolding the first buffer, its and 2nd buffer's text should be displayed" ); @@ -24278,7 +24302,7 @@ async fn test_folding_buffers(cx: &mut TestAppContext) { }); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.display_text(cx)), - "\n\naaaa\nBbbbb\ncccc\n\n\nffff\ngggg\n\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\n\nqqqq\nrrrr\n\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n\n1111\n2222\n\n\n5555", + "\n\naaaa\nBbbbb\ncccc\n\nffff\ngggg\n\njjjj\n\n\nllll\nmmmm\nnnnn\n\nqqqq\nrrrr\n\nuuuu\n\n\nvvvv\nwwww\nxxxx\n\n1111\n2222\n\n5555", "After unfolding the all buffers, all original text should be displayed" ); } @@ -24332,19 +24356,25 @@ async fn test_folding_buffers_with_one_excerpt(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))], + [Point::new(0, 0)..Point::new(3, 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(3, 0))], + [Point::new(0, 0)..Point::new(3, 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(3, 0))], + [Point::new(0, 0)..Point::new(3, 0)], + 0, cx, ); multi_buffer @@ -24455,15 +24485,15 @@ async fn test_folding_buffer_when_multibuffer_has_only_one_excerpt(cx: &mut Test 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( - sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1, - 0, - ), - )], + [Point::new(0, 0) + ..Point::new( + sample_text.chars().filter(|&c| c == '\n').count() as u32 + 1, + 0, + )], + 0, cx, ); multi_buffer @@ -29709,19 +29739,25 @@ fn test_relative_line_numbers(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(2, 0))], + [Point::new(0, 0)..Point::new(2, 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(2, 0)], + 0, cx, ); - multibuffer.push_excerpts( + multibuffer.set_excerpts_for_path( + PathKey::sorted(2), buffer_3.clone(), - [ExcerptRange::new(Point::new(0, 0)..Point::new(2, 0))], + [Point::new(0, 0)..Point::new(2, 0)], + 0, cx, ); multibuffer diff --git a/crates/editor/src/inlays/inlay_hints.rs b/crates/editor/src/inlays/inlay_hints.rs index 19953659ac67db14c59513cea27090de669f0166..4158ebbf7c5c3594dc4f9f43e8c3a7f1a19c38cb 100644 --- a/crates/editor/src/inlays/inlay_hints.rs +++ b/crates/editor/src/inlays/inlay_hints.rs @@ -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 diff --git a/crates/editor/src/jsx_tag_auto_close.rs b/crates/editor/src/jsx_tag_auto_close.rs index 20843518a069d74ab3d7351091ecc27cb6755811..a7c0c5eed2aed44d69bcaa3657894bad4d9deeb1 100644 --- a/crates/editor/src/jsx_tag_auto_close.rs +++ b/crates/editor/src/jsx_tag_auto_close.rs @@ -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("( - 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::(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, cx: &TestAppContext, diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 398f8ef158de4cf0333233eba823e5df68f0cc08..101c1559a7a0fb6e5d0d5bba7281a0cb78ab4b65 100644 --- a/crates/editor/src/test/editor_test_context.rs +++ b/crates/editor/src/test/editor_test_context.rs @@ -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 }); diff --git a/crates/git_ui/src/text_diff_view.rs b/crates/git_ui/src/text_diff_view.rs index 43dd32a24ef0fa6df1f5797742ed65e57ee368a3..1419fa049ee2aae1992dac517aad8371800ac532 100644 --- a/crates/git_ui/src/text_diff_view.rs +++ b/crates/git_ui/src/text_diff_view.rs @@ -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 diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 03615ffcabb2a5c46469bc16198adfce95e0c6ef..a593280d245fd01d623051953e48128c9935df45 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -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( - &mut self, - buffer: Entity, - ranges: impl IntoIterator>, - cx: &mut Context, - ) -> Vec - 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> + 'a, @@ -3749,11 +3736,21 @@ impl MultiBuffer { cx: &mut gpui::App, ) -> Entity { 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::>(); 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::>() ); - 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()); diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 5e028e60f13034e73c0c2cb6ae05c6bf56911c87..7e27786a76a14783f54e42c73850a888e87a3ac7 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -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::::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::>(), - [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::>(), - [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::(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); diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index eeaf3c5995dc446a704857b2a111c0aeb50e48a1..09d17d7b7fe2e9e666ba6c5777216c9c8ba4dea0 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -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) -> 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, + ranges: impl IntoIterator>, + context_line_count: u32, + cx: &mut Context, + ) -> (Vec>, 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.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, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 6c0efcf9ea9c3c27d6abd6fb29bb3e77c93d9612..9ec4a8259f5d92b41ef8e3fc300bb23d8503b301 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -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, );