From c98b2d69448741fdf08828fc2814642ee6947c41 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 19 Nov 2025 23:00:58 +0100 Subject: [PATCH] multi_buffer: Typed `MultiBufferOffset` (#42707) This PR introduces a new `MultiBufferOffset` new type wrapping size. The goal of this is to make it clear at the type level when we are interacting with offsets of a multi buffer versus offsets of a language / text buffer. This improves readability of things quite a bit by making it clear what kind of offsets one is working with while also reducing accidental bugs by using the wrong kin of offset for the wrong API. This PR also uncovered two minor bugs due to that. Does not yet introduce the MultiBufferPoint equivalent, that is for a follow up PR. Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/agent_ui/src/acp/message_editor.rs | 26 +- crates/agent_ui/src/acp/thread_view.rs | 5 +- crates/agent_ui/src/agent_configuration.rs | 16 +- crates/agent_ui/src/buffer_codegen.rs | 7 +- .../src/context_picker/completion_provider.rs | 4 +- crates/agent_ui/src/inline_assistant.rs | 17 +- crates/agent_ui/src/inline_prompt_editor.rs | 4 +- crates/agent_ui/src/text_thread_editor.rs | 58 +- .../assistant_text_thread/src/text_thread.rs | 2 +- .../collab/src/tests/channel_buffer_tests.rs | 14 +- crates/collab/src/tests/editor_tests.rs | 61 +- crates/collab/src/tests/following_tests.rs | 44 +- crates/collab_ui/src/collab_panel.rs | 2 +- .../src/copilot_completion_provider.rs | 5 +- crates/debugger_ui/src/debugger_panel.rs | 30 +- crates/debugger_ui/src/debugger_ui.rs | 13 +- .../src/session/running/console.rs | 20 +- crates/diagnostics/src/diagnostics_tests.rs | 5 +- crates/diagnostics/src/items.rs | 13 +- .../src/edit_prediction_button.rs | 12 +- crates/editor/benches/display_map.rs | 9 +- crates/editor/src/display_map.rs | 50 +- crates/editor/src/display_map/block_map.rs | 16 +- .../src/display_map/custom_highlights.rs | 38 +- crates/editor/src/display_map/fold_map.rs | 247 ++- crates/editor/src/display_map/inlay_map.rs | 223 ++- crates/editor/src/display_map/tab_map.rs | 5 +- crates/editor/src/editor.rs | 521 +++--- crates/editor/src/editor_tests.rs | 321 ++-- crates/editor/src/git/blame.rs | 2 +- .../editor/src/highlight_matching_bracket.rs | 3 +- crates/editor/src/hover_links.rs | 13 +- crates/editor/src/hover_popover.rs | 24 +- crates/editor/src/inlays/inlay_hints.rs | 18 +- crates/editor/src/items.rs | 8 +- crates/editor/src/jsx_tag_auto_close.rs | 17 +- crates/editor/src/linked_editing_ranges.rs | 5 +- crates/editor/src/movement.rs | 33 +- crates/editor/src/selections_collection.rs | 85 +- crates/editor/src/signature_help.rs | 16 +- crates/editor/src/test.rs | 17 +- .../src/test/editor_lsp_test_context.rs | 39 +- crates/editor/src/test/editor_test_context.rs | 31 +- crates/git_ui/src/commit_view.rs | 7 +- crates/git_ui/src/git_panel.rs | 8 +- crates/git_ui/src/project_diff.rs | 3 +- crates/git_ui/src/text_diff_view.rs | 8 +- crates/go_to_line/src/cursor_position.rs | 6 +- crates/journal/src/journal.rs | 2 +- crates/language/src/buffer.rs | 2 +- crates/language_tools/src/lsp_log_view.rs | 4 +- crates/language_tools/src/syntax_tree_view.rs | 10 +- .../src/markdown_preview_view.rs | 25 +- crates/multi_buffer/src/anchor.rs | 23 +- crates/multi_buffer/src/multi_buffer.rs | 1488 ++++++++++++----- crates/multi_buffer/src/multi_buffer_tests.rs | 334 ++-- crates/multi_buffer/src/transaction.rs | 34 +- crates/outline/src/outline.rs | 8 +- crates/project_panel/src/project_panel.rs | 6 +- .../project_panel/src/project_panel_tests.rs | 24 +- crates/repl/src/repl_editor.rs | 6 +- crates/rope/src/offset_utf16.rs | 1 - crates/rope/src/rope.rs | 52 +- crates/search/src/buffer_search.rs | 10 +- crates/tasks_ui/src/tasks_ui.rs | 4 +- crates/text/src/selection.rs | 12 +- crates/text/src/subscription.rs | 43 +- crates/text/src/text.rs | 4 +- crates/vim/src/helix.rs | 8 +- crates/vim/src/helix/boundary.rs | 81 +- crates/vim/src/helix/duplicate.rs | 4 +- crates/vim/src/helix/paste.rs | 2 +- crates/vim/src/motion.rs | 39 +- crates/vim/src/normal/increment.rs | 9 +- crates/vim/src/normal/paste.rs | 10 +- crates/vim/src/object.rs | 66 +- crates/vim/src/surrounds.rs | 12 +- crates/vim/src/test.rs | 7 +- crates/vim/src/vim.rs | 8 +- crates/vim/src/visual.rs | 12 +- crates/zed/src/zed.rs | 30 +- 81 files changed, 2952 insertions(+), 1559 deletions(-) diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index 120fdb26b3937e92312a63056d67cc2f35809068..883a7424e47eaf412278995cb9f3d497fd4f5c67 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/crates/agent_ui/src/acp/message_editor.rs @@ -13,7 +13,7 @@ use collections::{HashMap, HashSet}; use editor::{ Addon, Anchor, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, EditorEvent, EditorMode, EditorSnapshot, EditorStyle, ExcerptId, FoldPlaceholder, Inlay, - MultiBuffer, ToOffset, + MultiBuffer, MultiBufferOffset, ToOffset, actions::Paste, code_context_menus::CodeContextMenu, display_map::{Crease, CreaseId, FoldId}, @@ -209,7 +209,7 @@ impl MessageEditor { let acp::AvailableCommandInput::Unstructured { mut hint } = available_command.input.clone()?; - let mut hint_pos = parsed_command.source_range.end + 1; + let mut hint_pos = MultiBufferOffset(parsed_command.source_range.end) + 1usize; if hint_pos > snapshot.len() { hint_pos = snapshot.len(); hint.insert(0, ' '); @@ -307,9 +307,9 @@ impl MessageEditor { return Task::ready(()); }; let excerpt_id = start_anchor.excerpt_id; - let end_anchor = snapshot - .buffer_snapshot() - .anchor_before(start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1); + let end_anchor = snapshot.buffer_snapshot().anchor_before( + start_anchor.to_offset(&snapshot.buffer_snapshot()) + content_len + 1usize, + ); let crease = if let MentionUri::File { abs_path } = &mention_uri && let Some(extension) = abs_path.extension() @@ -739,8 +739,8 @@ impl MessageEditor { }; let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot()); - if crease_range.start > ix { - let chunk = text[ix..crease_range.start].into(); + if crease_range.start.0 > ix { + let chunk = text[ix..crease_range.start.0].into(); chunks.push(chunk); } let chunk = match mention { @@ -808,7 +808,7 @@ impl MessageEditor { }), }; chunks.push(chunk); - ix = crease_range.end; + ix = crease_range.end.0; } if ix < text.len() { @@ -862,7 +862,7 @@ impl MessageEditor { let snapshot = editor.display_snapshot(cx); let cursor = editor.selections.newest::(&snapshot).head(); let offset = cursor.to_offset(&snapshot); - if offset > 0 { + if offset.0 > 0 { snapshot .buffer_snapshot() .reversed_chars_at(offset) @@ -1132,7 +1132,7 @@ impl MessageEditor { let cursor_anchor = editor.selections.newest_anchor().head(); let cursor_offset = cursor_anchor.to_offset(&editor_buffer.snapshot(cx)); let anchor = buffer.update(cx, |buffer, _cx| { - buffer.anchor_before(cursor_offset.min(buffer.len())) + buffer.anchor_before(cursor_offset.0.min(buffer.len())) }); let Some(workspace) = self.workspace.upgrade() else { return; @@ -1258,7 +1258,7 @@ impl MessageEditor { }); for (range, mention_uri, mention) in mentions { - let anchor = snapshot.anchor_before(range.start); + let anchor = snapshot.anchor_before(MultiBufferOffset(range.start)); let Some((crease_id, tx)) = insert_crease_for_mention( anchor.excerpt_id, anchor.text_anchor, @@ -1713,7 +1713,7 @@ mod tests { use agent::{HistoryStore, outline}; use agent_client_protocol as acp; use assistant_text_thread::TextThreadStore; - use editor::{AnchorRangeExt as _, Editor, EditorMode}; + use editor::{AnchorRangeExt as _, Editor, EditorMode, MultiBufferOffset}; use fs::FakeFs; use futures::StreamExt as _; use gpui::{ @@ -2682,7 +2682,7 @@ mod tests { editor.display_map.update(cx, |display_map, cx| { display_map .snapshot(cx) - .folds_in_range(0..snapshot.len()) + .folds_in_range(MultiBufferOffset(0)..snapshot.len()) .map(|fold| fold.range.to_point(&snapshot)) .collect() }) diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 312ca136a2bf6f8b134a0dae0ab01bb71497a3b2..c2d3e5262354b57ae3c7e6dbd10189dedefebfe6 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -6066,6 +6066,7 @@ pub(crate) mod tests { use acp_thread::StubAgentConnection; use agent_client_protocol::SessionId; use assistant_text_thread::TextThreadStore; + use editor::MultiBufferOffset; use fs::FakeFs; use gpui::{EventEmitter, SemanticVersion, TestAppContext, VisualTestContext}; use project::Project; @@ -7234,7 +7235,7 @@ pub(crate) mod tests { Editor::for_buffer(buffer.clone(), Some(project.clone()), window, cx); editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges([8..15]); + selections.select_ranges([MultiBufferOffset(8)..MultiBufferOffset(15)]); }); editor @@ -7296,7 +7297,7 @@ pub(crate) mod tests { Editor::for_buffer(buffer.clone(), Some(project.clone()), window, cx); editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges([8..15]); + selections.select_ranges([MultiBufferOffset(8)..MultiBufferOffset(15)]); }); editor diff --git a/crates/agent_ui/src/agent_configuration.rs b/crates/agent_ui/src/agent_configuration.rs index 60f8606baf7bcbd55a7e4bd9ee6dc44f394319bc..8652f5cbd6c750da9260970ddc9ddcaef8337451 100644 --- a/crates/agent_ui/src/agent_configuration.rs +++ b/crates/agent_ui/src/agent_configuration.rs @@ -12,7 +12,7 @@ use client::zed_urls; use cloud_llm_client::{Plan, PlanV1, PlanV2}; use collections::HashMap; use context_server::ContextServerId; -use editor::{Editor, SelectionEffects, scroll::Autoscroll}; +use editor::{Editor, MultiBufferOffset, SelectionEffects, scroll::Autoscroll}; use extension::ExtensionManifest; use extension_host::ExtensionStore; use fs::Fs; @@ -1362,7 +1362,15 @@ async fn open_new_agent_servers_entry_in_settings_editor( .map(|(range, _)| range.clone()) .collect::>(); - item.edit(edits, cx); + item.edit( + edits.into_iter().map(|(range, s)| { + ( + MultiBufferOffset(range.start)..MultiBufferOffset(range.end), + s, + ) + }), + cx, + ); if let Some((unique_server_name, buffer)) = unique_server_name.zip(item.buffer().read(cx).as_singleton()) { @@ -1375,7 +1383,9 @@ async fn open_new_agent_servers_entry_in_settings_editor( window, cx, |selections| { - selections.select_ranges(vec![range]); + selections.select_ranges(vec![ + MultiBufferOffset(range.start)..MultiBufferOffset(range.end), + ]); }, ); } diff --git a/crates/agent_ui/src/buffer_codegen.rs b/crates/agent_ui/src/buffer_codegen.rs index 51c3c5ad3cb2b89c85e3a276ff34af5cc46115f9..0b7c1f61988979565aa9b55d4bbd245682b680df 100644 --- a/crates/agent_ui/src/buffer_codegen.rs +++ b/crates/agent_ui/src/buffer_codegen.rs @@ -429,7 +429,12 @@ impl CodegenAlternative { let prompt = self .builder - .generate_inline_transformation_prompt(user_prompt, language_name, buffer, range) + .generate_inline_transformation_prompt( + user_prompt, + language_name, + buffer, + range.start.0..range.end.0, + ) .context("generating content prompt")?; let context_task = self.context_store.as_ref().and_then(|context_store| { diff --git a/crates/agent_ui/src/context_picker/completion_provider.rs b/crates/agent_ui/src/context_picker/completion_provider.rs index 5dee769b4d0f0d2556b407721eac5dc70f647060..60e27b305437003b99326da29137727faaaf5c7c 100644 --- a/crates/agent_ui/src/context_picker/completion_provider.rs +++ b/crates/agent_ui/src/context_picker/completion_provider.rs @@ -1082,7 +1082,7 @@ impl MentionCompletion { #[cfg(test)] mod tests { use super::*; - use editor::AnchorRangeExt; + use editor::{AnchorRangeExt, MultiBufferOffset}; use gpui::{EventEmitter, FocusHandle, Focusable, TestAppContext, VisualTestContext}; use project::{Project, ProjectPath}; use serde_json::json; @@ -1677,7 +1677,7 @@ mod tests { editor.display_map.update(cx, |display_map, cx| { display_map .snapshot(cx) - .folds_in_range(0..snapshot.len()) + .folds_in_range(MultiBufferOffset(0)..snapshot.len()) .map(|fold| fold.range.to_point(&snapshot)) .collect() }) diff --git a/crates/agent_ui/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs index b05dba59e6b19fa5091903882748de853cd9cb93..05cdd42544419969ce76ec168e671d5b2ac2402e 100644 --- a/crates/agent_ui/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -16,6 +16,7 @@ use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; use collections::{HashMap, HashSet, VecDeque, hash_map}; +use editor::MultiBufferOffset; use editor::RowExt; use editor::SelectionEffects; use editor::scroll::ScrollOffset; @@ -803,7 +804,7 @@ impl InlineAssistant { ( editor .selections - .newest::(&editor.display_snapshot(cx)), + .newest::(&editor.display_snapshot(cx)), editor.buffer().read(cx).snapshot(cx), ) }); @@ -836,7 +837,7 @@ impl InlineAssistant { ( editor .selections - .newest::(&editor.display_snapshot(cx)), + .newest::(&editor.display_snapshot(cx)), editor.buffer().read(cx).snapshot(cx), ) }); @@ -853,12 +854,14 @@ impl InlineAssistant { } else { let distance_from_selection = assist_range .start - .abs_diff(selection.start) - .min(assist_range.start.abs_diff(selection.end)) + .0 + .abs_diff(selection.start.0) + .min(assist_range.start.0.abs_diff(selection.end.0)) + assist_range .end - .abs_diff(selection.start) - .min(assist_range.end.abs_diff(selection.end)); + .0 + .abs_diff(selection.start.0) + .min(assist_range.end.0.abs_diff(selection.end.0)); match closest_assist_fallback { Some((_, old_distance)) => { if distance_from_selection < old_distance { @@ -935,7 +938,7 @@ impl InlineAssistant { EditorEvent::Edited { transaction_id } => { let buffer = editor.read(cx).buffer().read(cx); let edited_ranges = - buffer.edited_ranges_for_transaction::(*transaction_id, cx); + buffer.edited_ranges_for_transaction::(*transaction_id, cx); let snapshot = buffer.snapshot(cx); for assist_id in editor_assists.assist_ids.clone() { diff --git a/crates/agent_ui/src/inline_prompt_editor.rs b/crates/agent_ui/src/inline_prompt_editor.rs index 2d0538a9172ae69e50b2e4208e540662e7d838b2..3a0866f47063a6dc5f68df1c36c3fdb0e07d2b74 100644 --- a/crates/agent_ui/src/inline_prompt_editor.rs +++ b/crates/agent_ui/src/inline_prompt_editor.rs @@ -2,7 +2,7 @@ use agent::HistoryStore; use collections::{HashMap, VecDeque}; use editor::actions::Paste; use editor::display_map::{CreaseId, EditorMargins}; -use editor::{Addon, AnchorRangeExt as _}; +use editor::{Addon, AnchorRangeExt as _, MultiBufferOffset}; use editor::{ ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer, actions::{MoveDown, MoveUp}, @@ -1165,7 +1165,7 @@ impl GenerationMode { /// Stored information that can be used to resurrect a context crease when creating an editor for a past message. #[derive(Clone, Debug)] pub struct MessageCrease { - pub range: Range, + pub range: Range, pub icon_path: SharedString, pub label: SharedString, /// None for a deserialized message, Some otherwise. diff --git a/crates/agent_ui/src/text_thread_editor.rs b/crates/agent_ui/src/text_thread_editor.rs index 84f04f8821b2dd540e54f41f567a0b7735116875..daf5cc2c3d770f4e9bdad4ea882b1ad2afc93a4e 100644 --- a/crates/agent_ui/src/text_thread_editor.rs +++ b/crates/agent_ui/src/text_thread_editor.rs @@ -9,8 +9,8 @@ use assistant_slash_commands::{DefaultSlashCommand, FileSlashCommand, selections use client::{proto, zed_urls}; use collections::{BTreeSet, HashMap, HashSet, hash_map}; use editor::{ - Anchor, Editor, EditorEvent, MenuEditPredictionsPolicy, MultiBuffer, MultiBufferSnapshot, - RowExt, ToOffset as _, ToPoint, + Anchor, Editor, EditorEvent, MenuEditPredictionsPolicy, MultiBuffer, MultiBufferOffset, + MultiBufferSnapshot, RowExt, ToOffset as _, ToPoint, actions::{MoveToEndOfLine, Newline, ShowCompletions}, display_map::{ BlockPlacement, BlockProperties, BlockStyle, Crease, CreaseMetadata, CustomBlockId, FoldId, @@ -390,7 +390,7 @@ impl TextThreadEditor { let cursor = user_message .start .to_offset(self.text_thread.read(cx).buffer().read(cx)); - cursor..cursor + MultiBufferOffset(cursor)..MultiBufferOffset(cursor) }; self.editor.update(cx, |editor, cx| { editor.change_selections(Default::default(), window, cx, |selections| { @@ -431,7 +431,7 @@ impl TextThreadEditor { let cursors = self.cursors(cx); self.text_thread.update(cx, |text_thread, cx| { let messages = text_thread - .messages_for_offsets(cursors, cx) + .messages_for_offsets(cursors.into_iter().map(|cursor| cursor.0), cx) .into_iter() .map(|message| message.id) .collect(); @@ -439,9 +439,11 @@ impl TextThreadEditor { }); } - fn cursors(&self, cx: &mut App) -> Vec { + fn cursors(&self, cx: &mut App) -> Vec { let selections = self.editor.update(cx, |editor, cx| { - editor.selections.all::(&editor.display_snapshot(cx)) + editor + .selections + .all::(&editor.display_snapshot(cx)) }); selections .into_iter() @@ -1580,7 +1582,11 @@ impl TextThreadEditor { fn get_clipboard_contents( &mut self, cx: &mut Context, - ) -> (String, CopyMetadata, Vec>) { + ) -> ( + String, + CopyMetadata, + Vec>, + ) { let (mut selection, creases) = self.editor.update(cx, |editor, cx| { let mut selection = editor .selections @@ -1638,30 +1644,26 @@ impl TextThreadEditor { // If selection is empty, we want to copy the entire line if selection.range().is_empty() { - let snapshot = text_thread.buffer().read(cx).snapshot(); + let snapshot = self.editor.read(cx).buffer().read(cx).snapshot(cx); let point = snapshot.offset_to_point(selection.range().start); selection.start = snapshot.point_to_offset(Point::new(point.row, 0)); selection.end = snapshot .point_to_offset(cmp::min(Point::new(point.row + 1, 0), snapshot.max_point())); - for chunk in text_thread - .buffer() - .read(cx) - .text_for_range(selection.range()) - { + for chunk in snapshot.text_for_range(selection.range()) { text.push_str(chunk); } } else { for message in text_thread.messages(cx) { - if message.offset_range.start >= selection.range().end { + if message.offset_range.start >= selection.range().end.0 { break; - } else if message.offset_range.end >= selection.range().start { - let range = cmp::max(message.offset_range.start, selection.range().start) - ..cmp::min(message.offset_range.end, selection.range().end); + } else if message.offset_range.end >= selection.range().start.0 { + let range = cmp::max(message.offset_range.start, selection.range().start.0) + ..cmp::min(message.offset_range.end, selection.range().end.0); if !range.is_empty() { for chunk in text_thread.buffer().read(cx).text_for_range(range) { text.push_str(chunk); } - if message.offset_range.end < selection.range().end { + if message.offset_range.end < selection.range().end.0 { text.push('\n'); } } @@ -1743,7 +1745,7 @@ impl TextThreadEditor { self.editor.update(cx, |editor, cx| { let paste_position = editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .head(); editor.paste(action, window, cx); @@ -1791,13 +1793,16 @@ impl TextThreadEditor { editor.transact(window, cx, |editor, _window, cx| { let edits = editor .selections - .all::(&editor.display_snapshot(cx)) + .all::(&editor.display_snapshot(cx)) .into_iter() .map(|selection| (selection.start..selection.end, "\n")); editor.edit(edits, cx); let snapshot = editor.buffer().read(cx).snapshot(cx); - for selection in editor.selections.all::(&editor.display_snapshot(cx)) { + for selection in editor + .selections + .all::(&editor.display_snapshot(cx)) + { image_positions.push(snapshot.anchor_before(selection.end)); } }); @@ -1889,7 +1894,7 @@ impl TextThreadEditor { let range = selection .map(|endpoint| endpoint.to_offset(&buffer)) .range(); - text_thread.split_message(range, cx); + text_thread.split_message(range.start.0..range.end.0, cx); } }); } @@ -2963,7 +2968,7 @@ pub fn make_lsp_adapter_delegate( #[cfg(test)] mod tests { use super::*; - use editor::SelectionEffects; + use editor::{MultiBufferOffset, SelectionEffects}; use fs::FakeFs; use gpui::{App, TestAppContext, VisualTestContext}; use indoc::indoc; @@ -3169,15 +3174,16 @@ mod tests { text_thread: &Entity, message_ix: usize, cx: &mut TestAppContext, - ) -> Range { - text_thread.update(cx, |text_thread, cx| { + ) -> Range { + let range = text_thread.update(cx, |text_thread, cx| { text_thread .messages(cx) .nth(message_ix) .unwrap() .anchor_range .to_offset(&text_thread.buffer().read(cx).snapshot()) - }) + }); + MultiBufferOffset(range.start)..MultiBufferOffset(range.end) } fn assert_copy_paste_text_thread_editor( diff --git a/crates/assistant_text_thread/src/text_thread.rs b/crates/assistant_text_thread/src/text_thread.rs index 9ad383cdfd43eed236268349e2ff97c34a0178c0..ae5fe25d430e80b7be68000162b6f0b21807e2a2 100644 --- a/crates/assistant_text_thread/src/text_thread.rs +++ b/crates/assistant_text_thread/src/text_thread.rs @@ -667,7 +667,7 @@ pub struct TextThread { buffer: Entity, pub(crate) parsed_slash_commands: Vec, invoked_slash_commands: HashMap, - edits_since_last_parse: language::Subscription, + edits_since_last_parse: language::Subscription, slash_commands: Arc, pub(crate) slash_command_output_sections: Vec>, thought_process_output_sections: Vec>, diff --git a/crates/collab/src/tests/channel_buffer_tests.rs b/crates/collab/src/tests/channel_buffer_tests.rs index 8e857f4f02505998f226b543b843e68222016aeb..62c61d3cf0b22e7adad5ada7ec46598fbadf673c 100644 --- a/crates/collab/src/tests/channel_buffer_tests.rs +++ b/crates/collab/src/tests/channel_buffer_tests.rs @@ -7,7 +7,7 @@ use channel::ACKNOWLEDGE_DEBOUNCE_INTERVAL; use client::{Collaborator, ParticipantIndex, UserId}; use collab_ui::channel_view::ChannelView; use collections::HashMap; -use editor::{Anchor, Editor, ToOffset}; +use editor::{Anchor, Editor, MultiBufferOffset, ToOffset}; use futures::future; use gpui::{BackgroundExecutor, Context, Entity, TestAppContext, Window}; use rpc::{RECEIVE_TIMEOUT, proto::PeerId}; @@ -180,7 +180,7 @@ async fn test_channel_notes_participant_indices( notes.editor.update(cx, |editor, cx| { editor.insert("a", window, cx); editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges(vec![0..1]); + selections.select_ranges(vec![MultiBufferOffset(0)..MultiBufferOffset(1)]); }); }); }); @@ -190,7 +190,7 @@ async fn test_channel_notes_participant_indices( editor.move_down(&Default::default(), window, cx); editor.insert("b", window, cx); editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges(vec![1..2]); + selections.select_ranges(vec![MultiBufferOffset(1)..MultiBufferOffset(2)]); }); }); }); @@ -200,7 +200,7 @@ async fn test_channel_notes_participant_indices( editor.move_down(&Default::default(), window, cx); editor.insert("c", window, cx); editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges(vec![2..3]); + selections.select_ranges(vec![MultiBufferOffset(2)..MultiBufferOffset(3)]); }); }); }); @@ -287,12 +287,12 @@ async fn test_channel_notes_participant_indices( editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges(vec![0..1]); + selections.select_ranges(vec![MultiBufferOffset(0)..MultiBufferOffset(1)]); }); }); editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(Default::default(), window, cx, |selections| { - selections.select_ranges(vec![2..3]); + selections.select_ranges(vec![MultiBufferOffset(2)..MultiBufferOffset(3)]); }); }); executor.run_until_parked(); @@ -327,7 +327,7 @@ fn assert_remote_selections( let end = s.selection.end.to_offset(snapshot.buffer_snapshot()); let user_id = collaborators.get(&peer_id).unwrap().user_id; let participant_index = hub.user_participant_indices(cx).get(&user_id).copied(); - (participant_index, start..end) + (participant_index, start.0..end.0) }) .collect::>(); assert_eq!( diff --git a/crates/collab/src/tests/editor_tests.rs b/crates/collab/src/tests/editor_tests.rs index e015550df9482c5850396b8bcf10e9cee24d5b76..33f07bfb388763875565bc9e37bda363f02600f0 100644 --- a/crates/collab/src/tests/editor_tests.rs +++ b/crates/collab/src/tests/editor_tests.rs @@ -4,7 +4,8 @@ use crate::{ }; use call::ActiveCall; use editor::{ - DocumentColorsRenderMode, Editor, FETCH_COLORS_DEBOUNCE_TIMEOUT, RowInfo, SelectionEffects, + DocumentColorsRenderMode, Editor, FETCH_COLORS_DEBOUNCE_TIMEOUT, MultiBufferOffset, RowInfo, + SelectionEffects, actions::{ ConfirmCodeAction, ConfirmCompletion, ConfirmRename, ContextMenuFirst, ExpandMacroRecursively, MoveToEnd, Redo, Rename, SelectAll, ToggleCodeActions, Undo, @@ -381,7 +382,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu // Type a completion trigger character as the guest. editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input(".", window, cx); }); @@ -503,7 +504,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu // resolved editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([46..46]) + s.select_ranges([MultiBufferOffset(46)..MultiBufferOffset(46)]) }); editor.handle_input("; a", window, cx); editor.handle_input(".", window, cx); @@ -601,7 +602,7 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu // Add another completion trigger to test the second language server editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([68..68]) + s.select_ranges([MultiBufferOffset(68)..MultiBufferOffset(68)]) }); editor.handle_input("; b", window, cx); editor.handle_input(".", window, cx); @@ -950,7 +951,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T // Move cursor to a location that can be renamed. let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([7..7]) + s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(7)]) }); editor.rename(&Rename, window, cx).unwrap() }); @@ -977,17 +978,17 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T let buffer = editor.buffer().read(cx).snapshot(cx); assert_eq!( rename.range.start.to_offset(&buffer)..rename.range.end.to_offset(&buffer), - 6..9 + MultiBufferOffset(6)..MultiBufferOffset(9) ); rename.editor.update(cx, |rename_editor, cx| { - let rename_selection = rename_editor.selections.newest::(&rename_editor.display_snapshot(cx)); + let rename_selection = rename_editor.selections.newest::(&rename_editor.display_snapshot(cx)); assert_eq!( rename_selection.range(), - 0..3, + MultiBufferOffset(0)..MultiBufferOffset(3), "Rename that was triggered from zero selection caret, should propose the whole word." ); rename_editor.buffer().update(cx, |rename_buffer, cx| { - rename_buffer.edit([(0..3, "THREE")], None, cx); + rename_buffer.edit([(MultiBufferOffset(0)..MultiBufferOffset(3), "THREE")], None, cx); }); }); }); @@ -998,7 +999,7 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T }); let prepare_rename = editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([7..8]) + s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(8)]) }); editor.rename(&Rename, window, cx).unwrap() }); @@ -1025,16 +1026,16 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T let buffer = editor.buffer().read(cx).snapshot(cx); let lsp_rename_start = rename.range.start.to_offset(&buffer); let lsp_rename_end = rename.range.end.to_offset(&buffer); - assert_eq!(lsp_rename_start..lsp_rename_end, 6..9); + assert_eq!(lsp_rename_start..lsp_rename_end, MultiBufferOffset(6)..MultiBufferOffset(9)); rename.editor.update(cx, |rename_editor, cx| { - let rename_selection = rename_editor.selections.newest::(&rename_editor.display_snapshot(cx)); + let rename_selection = rename_editor.selections.newest::(&rename_editor.display_snapshot(cx)); assert_eq!( rename_selection.range(), - 1..2, + MultiBufferOffset(1)..MultiBufferOffset(2), "Rename that was triggered from a selection, should have the same selection range in the rename proposal" ); rename_editor.buffer().update(cx, |rename_buffer, cx| { - rename_buffer.edit([(0..lsp_rename_end - lsp_rename_start, "THREE")], None, cx); + rename_buffer.edit([(MultiBufferOffset(0)..MultiBufferOffset(lsp_rename_end - lsp_rename_start), "THREE")], None, cx); }); }); }); @@ -1237,7 +1238,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte // Move cursor to a location, this should trigger the code lens call. editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([7..7]) + s.select_ranges([MultiBufferOffset(7)..MultiBufferOffset(7)]) }); }); let () = request_started_rx.next().await.unwrap(); @@ -1259,7 +1260,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1]) + s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)]) }); }); let () = request_started_rx.next().await.unwrap(); @@ -1281,7 +1282,7 @@ async fn test_slow_lsp_server(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([2..2]) + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)]) }); }); let () = request_started_rx.next().await.unwrap(); @@ -1719,7 +1720,7 @@ async fn test_on_input_format_from_host_to_guest( cx_a.focus(&editor_a); editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input(">", window, cx); }); @@ -1828,7 +1829,7 @@ async fn test_on_input_format_from_guest_to_host( cx_b.focus(&editor_b); editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input(":", window, cx); }); @@ -2056,7 +2057,7 @@ async fn test_mutual_editor_inlay_hint_cache_update( let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1; editor_b.update_in(cx_b, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13].clone()) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)].clone()) }); editor.handle_input(":", window, cx); }); @@ -2080,7 +2081,7 @@ async fn test_mutual_editor_inlay_hint_cache_update( let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1; editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input("a change to increment both buffers' versions", window, cx); }); @@ -2520,7 +2521,7 @@ async fn test_lsp_document_color(cx_a: &mut TestAppContext, cx_b: &mut TestAppCo editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13].clone()) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)].clone()) }); editor.handle_input(":", window, cx); }); @@ -2957,7 +2958,7 @@ async fn test_lsp_pull_diagnostics( editor_a_main.update(cx_a, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); assert_eq!( all_diagnostics.len(), @@ -3086,7 +3087,7 @@ async fn test_lsp_pull_diagnostics( editor_a_main.update(cx_a, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); assert_eq!( all_diagnostics.len(), @@ -3133,7 +3134,7 @@ async fn test_lsp_pull_diagnostics( editor_b_main.update(cx_b, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); assert_eq!( all_diagnostics.len(), @@ -3180,7 +3181,7 @@ async fn test_lsp_pull_diagnostics( editor_b_lib.update(cx_b, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); let expected_messages = [ expected_pull_diagnostic_lib_message, @@ -3247,7 +3248,7 @@ async fn test_lsp_pull_diagnostics( editor_b_lib.update(cx_b, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); let expected_messages = [ expected_workspace_pull_diagnostics_lib_message, @@ -3382,7 +3383,7 @@ async fn test_lsp_pull_diagnostics( editor_b_lib.update(cx_b, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); let expected_messages = [ expected_workspace_pull_diagnostics_lib_message, @@ -3400,7 +3401,7 @@ async fn test_lsp_pull_diagnostics( editor_b_main.update(cx_b, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); assert_eq!(all_diagnostics.len(), 2); @@ -3419,7 +3420,7 @@ async fn test_lsp_pull_diagnostics( editor_a_main.update(cx_a, |editor, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); let all_diagnostics = snapshot - .diagnostics_in_range(0..snapshot.len()) + .diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) .collect::>(); assert_eq!(all_diagnostics.len(), 2); let expected_messages = [ diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index 07cf866a3513d27894307216e904b130eb023e22..f3827b6f1195392ddedcab4f45854a8e9790dc28 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -6,7 +6,7 @@ use collab_ui::{ channel_view::ChannelView, notifications::project_shared_notification::ProjectSharedNotification, }; -use editor::{Editor, MultiBuffer, PathKey, SelectionEffects}; +use editor::{Editor, MultiBuffer, MultiBufferOffset, PathKey, SelectionEffects}; use gpui::{ AppContext as _, BackgroundExecutor, BorrowAppContext, Entity, SharedString, TestAppContext, VisualContext, VisualTestContext, point, @@ -124,7 +124,7 @@ async fn test_basic_following( editor.select_left(&Default::default(), window, cx); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![3..2] + vec![MultiBufferOffset(3)..MultiBufferOffset(2)] ); }); editor_a2.update_in(cx_a, |editor, window, cx| { @@ -133,7 +133,7 @@ async fn test_basic_following( editor.select_left(&Default::default(), window, cx); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![2..1] + vec![MultiBufferOffset(2)..MultiBufferOffset(1)] ); }); @@ -158,13 +158,13 @@ async fn test_basic_following( editor_b2.update(cx_b, |editor, cx| editor .selections .ranges(&editor.display_snapshot(cx))), - vec![2..1] + vec![MultiBufferOffset(2)..MultiBufferOffset(1)] ); assert_eq!( editor_b1.update(cx_b, |editor, cx| editor .selections .ranges(&editor.display_snapshot(cx))), - vec![3..3] + vec![MultiBufferOffset(3)..MultiBufferOffset(3)] ); executor.run_until_parked(); @@ -386,7 +386,10 @@ async fn test_basic_following( // Changes to client A's editor are reflected on client B. editor_a1.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1, 2..2]) + s.select_ranges([ + MultiBufferOffset(1)..MultiBufferOffset(1), + MultiBufferOffset(2)..MultiBufferOffset(2), + ]) }); }); executor.advance_clock(workspace::item::LEADER_UPDATE_THROTTLE); @@ -396,7 +399,10 @@ async fn test_basic_following( editor_b1.update(cx_b, |editor, cx| { assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - &[1..1, 2..2] + &[ + MultiBufferOffset(1)..MultiBufferOffset(1), + MultiBufferOffset(2)..MultiBufferOffset(2) + ] ); }); @@ -408,7 +414,7 @@ async fn test_basic_following( editor_a1.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([3..3]) + s.select_ranges([MultiBufferOffset(3)..MultiBufferOffset(3)]) }); editor.set_scroll_position(point(0., 100.), window, cx); }); @@ -417,7 +423,7 @@ async fn test_basic_following( editor_b1.update(cx_b, |editor, cx| { assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - &[3..3] + &[MultiBufferOffset(3)..MultiBufferOffset(3)] ); }); @@ -1694,7 +1700,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T // b should follow a to position 1 editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1]) + s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)]) }) }); cx_a.executor() @@ -1703,7 +1709,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T editor_b.update(cx_b, |editor, cx| { assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![1..1] + vec![MultiBufferOffset(1)..MultiBufferOffset(1)] ) }); @@ -1719,7 +1725,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T // b should not follow a to position 2 editor_a.update_in(cx_a, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([2..2]) + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)]) }) }); cx_a.executor() @@ -1728,7 +1734,7 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T editor_b.update(cx_b, |editor, cx| { assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![1..1] + vec![MultiBufferOffset(1)..MultiBufferOffset(1)] ) }); cx_b.update(|_, cx| { @@ -1829,7 +1835,7 @@ async fn test_following_into_excluded_file( editor.select_left(&Default::default(), window, cx); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![3..2] + vec![MultiBufferOffset(3)..MultiBufferOffset(2)] ); }); editor_for_excluded_a.update_in(cx_a, |editor, window, cx| { @@ -1838,7 +1844,7 @@ async fn test_following_into_excluded_file( editor.select_left(&Default::default(), window, cx); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![18..17] + vec![MultiBufferOffset(18)..MultiBufferOffset(17)] ); }); @@ -1864,7 +1870,7 @@ async fn test_following_into_excluded_file( editor_for_excluded_b.update(cx_b, |editor, cx| editor .selections .ranges(&editor.display_snapshot(cx))), - vec![18..17] + vec![MultiBufferOffset(18)..MultiBufferOffset(17)] ); editor_for_excluded_a.update_in(cx_a, |editor, window, cx| { @@ -2040,7 +2046,7 @@ async fn test_following_to_channel_notes_without_a_shared_project( notes.editor.update(cx, |editor, cx| { editor.insert("Hello from A.", window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { - selections.select_ranges(vec![3..4]); + selections.select_ranges(vec![MultiBufferOffset(3)..MultiBufferOffset(4)]); }); }); }); @@ -2076,8 +2082,8 @@ async fn test_following_to_channel_notes_without_a_shared_project( assert_eq!( editor .selections - .ranges::(&editor.display_snapshot(cx)), - &[3..4] + .ranges::(&editor.display_snapshot(cx)), + &[MultiBufferOffset(3)..MultiBufferOffset(4)] ); }) }); diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index b57d0279545aed8f896179968c877efb72e7c772..8f2e959b17293af66279793e73fefceea413ca49 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -1496,7 +1496,7 @@ impl CollabPanel { fn reset_filter_editor_text(&mut self, window: &mut Window, cx: &mut Context) -> bool { self.filter_editor.update(cx, |editor, cx| { - if editor.buffer().read(cx).len(cx) > 0 { + if editor.buffer().read(cx).len(cx).0 > 0 { editor.set_text("", window, cx); true } else { diff --git a/crates/copilot/src/copilot_completion_provider.rs b/crates/copilot/src/copilot_completion_provider.rs index ba8ae82508896884f1b6e9741e7ccd6fd78dce76..30ef6de07ec92f66cc888b52a540cf9c7e673bb4 100644 --- a/crates/copilot/src/copilot_completion_provider.rs +++ b/crates/copilot/src/copilot_completion_provider.rs @@ -270,7 +270,7 @@ fn common_prefix, T2: Iterator>(a: T1, b: mod tests { use super::*; use editor::{ - Editor, ExcerptRange, MultiBuffer, SelectionEffects, + Editor, ExcerptRange, MultiBuffer, MultiBufferOffset, SelectionEffects, test::editor_lsp_test_context::EditorLspTestContext, }; use fs::FakeFs; @@ -1081,8 +1081,9 @@ mod tests { vec![complete_from_marker, replace_range_marker.clone()], ); + let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone(); let replace_range = - cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone()); + cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end)); let mut request = cx.set_request_handler::(move |url, params, _| { diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 5379591f8ed256d2703a8e61b09925e9743ed341..f2f8b5be32c696c61a50c71090f130fcf34ef271 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -14,13 +14,12 @@ use collections::IndexMap; use dap::adapters::DebugAdapterName; use dap::{DapRegistry, StartDebuggingRequestArguments}; use dap::{client::SessionId, debugger_settings::DebuggerSettings}; -use editor::Editor; +use editor::{Editor, MultiBufferOffset, ToPoint}; use gpui::{ Action, App, AsyncWindowContext, ClipboardItem, Context, DismissEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable, MouseButton, MouseDownEvent, Point, Subscription, Task, WeakEntity, anchored, deferred, }; -use text::ToPoint as _; use itertools::Itertools as _; use language::Buffer; @@ -1216,11 +1215,11 @@ impl DebugPanel { let mut last_offset = None; while let Some(mat) = matches.next() { if let Some(pos) = mat.captures.first().map(|m| m.node.byte_range().end) { - last_offset = Some(pos) + last_offset = Some(MultiBufferOffset(pos)) } } let mut edits = Vec::new(); - let mut cursor_position = 0; + let mut cursor_position = MultiBufferOffset(0); if let Some(pos) = last_offset { edits.push((pos..pos, format!(",\n{new_scenario}"))); @@ -1234,24 +1233,25 @@ impl DebugPanel { if let Some(mat) = matches.next() { if let Some(pos) = mat.captures.first().map(|m| m.node.byte_range().end - 1) { - edits.push((pos..pos, format!("\n{new_scenario}\n"))); - cursor_position = pos + "\n ".len(); + edits.push(( + MultiBufferOffset(pos)..MultiBufferOffset(pos), + format!("\n{new_scenario}\n"), + )); + cursor_position = MultiBufferOffset(pos) + "\n ".len(); } } else { - edits.push((0..0, format!("[\n{}\n]", new_scenario))); - cursor_position = "[\n ".len(); + edits.push(( + MultiBufferOffset(0)..MultiBufferOffset(0), + format!("[\n{}\n]", new_scenario), + )); + cursor_position = MultiBufferOffset("[\n ".len()); } } editor.transact(window, cx, |editor, window, cx| { editor.edit(edits, cx); - let snapshot = editor - .buffer() - .read(cx) - .as_singleton() - .unwrap() - .read(cx) - .snapshot(); + let snapshot = editor.buffer().read(cx).read(cx); let point = cursor_position.to_point(&snapshot); + drop(snapshot); editor.go_to_singleton_buffer_point(point, window, cx); }); Ok(editor.save(SaveOptions::default(), project, window, cx)) diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 0e38719c19b636918b441440568e8588e29b039e..a9abb50bb68851334285b05064176e0347474014 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -1,7 +1,7 @@ use std::any::TypeId; use debugger_panel::DebugPanel; -use editor::Editor; +use editor::{Editor, MultiBufferOffsetUtf16}; use gpui::{Action, App, DispatchPhase, EntityInputHandler, actions}; use new_process_modal::{NewProcessModal, NewProcessMode}; use onboarding_modal::DebuggerOnboardingModal; @@ -390,11 +390,14 @@ pub fn init(cx: &mut App) { maybe!({ let text = editor .update(cx, |editor, cx| { + let range = editor + .selections + .newest::( + &editor.display_snapshot(cx), + ) + .range(); editor.text_for_range( - editor - .selections - .newest(&editor.display_snapshot(cx)) - .range(), + range.start.0.0..range.end.0.0, &mut None, window, cx, diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index 23b3ca481722c7869caf43958754889f92dc2fe5..f72d92e038ce234327e29776f923c27d6592cf16 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -8,7 +8,7 @@ use collections::HashMap; use dap::{CompletionItem, CompletionItemType, OutputEvent}; use editor::{ Bias, CompletionProvider, Editor, EditorElement, EditorMode, EditorStyle, ExcerptId, - SizingBehavior, + MultiBufferOffset, SizingBehavior, }; use fuzzy::StringMatchCandidate; use gpui::{ @@ -161,7 +161,9 @@ impl Console { ) -> Task> { self.console.update(cx, |_, cx| { cx.spawn_in(window, async move |console, cx| { - let mut len = console.update(cx, |this, cx| this.buffer().read(cx).len(cx))?; + let mut len = console + .update(cx, |this, cx| this.buffer().read(cx).len(cx))? + .0; let (output, spans, background_spans) = cx .background_spawn(async move { let mut all_spans = Vec::new(); @@ -227,8 +229,8 @@ impl Console { for (range, color) in spans { let Some(color) = color else { continue }; let start_offset = range.start; - let range = - buffer.anchor_after(range.start)..buffer.anchor_before(range.end); + let range = buffer.anchor_after(MultiBufferOffset(range.start)) + ..buffer.anchor_before(MultiBufferOffset(range.end)); let style = HighlightStyle { color: Some(terminal_view::terminal_element::convert_color( &color, @@ -247,8 +249,8 @@ impl Console { for (range, color) in background_spans { let Some(color) = color else { continue }; let start_offset = range.start; - let range = - buffer.anchor_after(range.start)..buffer.anchor_before(range.end); + let range = buffer.anchor_after(MultiBufferOffset(range.start)) + ..buffer.anchor_before(MultiBufferOffset(range.end)); console.highlight_background_key::( start_offset, &[range], @@ -961,7 +963,7 @@ fn color_fetcher(color: ansi::Color) -> fn(&Theme) -> Hsla { mod tests { use super::*; use crate::tests::init_test; - use editor::test::editor_test_context::EditorTestContext; + use editor::{MultiBufferOffset, test::editor_test_context::EditorTestContext}; use gpui::TestAppContext; use language::Point; @@ -993,8 +995,8 @@ mod tests { cx.update_editor(|editor, _, cx| { editor.edit( vec![( - snapshot.offset_for_anchor(&replace_range.start) - ..snapshot.offset_for_anchor(&replace_range.end), + MultiBufferOffset(snapshot.offset_for_anchor(&replace_range.start)) + ..MultiBufferOffset(snapshot.offset_for_anchor(&replace_range.end)), replacement, )], cx, diff --git a/crates/diagnostics/src/diagnostics_tests.rs b/crates/diagnostics/src/diagnostics_tests.rs index 3d1d3840108c6842d57190bd56fd9db3984af7c6..d2504fde4a6bcb828db75f85f01aea2f296bd9dd 100644 --- a/crates/diagnostics/src/diagnostics_tests.rs +++ b/crates/diagnostics/src/diagnostics_tests.rs @@ -1,7 +1,7 @@ use super::*; use collections::{HashMap, HashSet}; use editor::{ - DisplayPoint, EditorSettings, Inlay, + DisplayPoint, EditorSettings, Inlay, MultiBufferOffset, actions::{GoToDiagnostic, GoToPreviousDiagnostic, Hover, MoveToBeginning}, display_map::DisplayRow, test::{ @@ -878,7 +878,8 @@ async fn test_random_diagnostics_with_inlays(cx: &mut TestAppContext, mut rng: S diagnostics.editor.update(cx, |editor, cx| { let snapshot = editor.snapshot(window, cx); if !snapshot.buffer_snapshot().is_empty() { - let position = rng.random_range(0..snapshot.buffer_snapshot().len()); + let position = rng + .random_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len()); let position = snapshot.buffer_snapshot().clip_offset(position, Bias::Left); log::info!( "adding inlay at {position}/{}: {:?}", diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 413bad5c0d696bfcba92a1127789c9e7c31edc30..b4ca52ea7239b6e4e76160a475d703ddd2933f44 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use editor::Editor; +use editor::{Editor, MultiBufferOffset}; use gpui::{ Context, Entity, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window, @@ -171,14 +171,19 @@ impl DiagnosticIndicator { let buffer = editor.buffer().read(cx).snapshot(cx); let cursor_position = editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .head(); (buffer, cursor_position) }); let new_diagnostic = buffer - .diagnostics_in_range::(cursor_position..cursor_position) + .diagnostics_in_range::(cursor_position..cursor_position) .filter(|entry| !entry.range.is_empty()) - .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) + .min_by_key(|entry| { + ( + entry.diagnostic.severity, + entry.range.end - entry.range.start, + ) + }) .map(|entry| entry.diagnostic); if new_diagnostic != self.current_diagnostic.as_ref() { let new_diagnostic = new_diagnostic.cloned(); diff --git a/crates/edit_prediction_button/src/edit_prediction_button.rs b/crates/edit_prediction_button/src/edit_prediction_button.rs index 51f228db76aaee5e286dd950c17dd01b303d29b8..4f5f60d5a2328e5e56d65e87add7338b7e572346 100644 --- a/crates/edit_prediction_button/src/edit_prediction_button.rs +++ b/crates/edit_prediction_button/src/edit_prediction_button.rs @@ -3,7 +3,9 @@ use client::{Client, UserStore, zed_urls}; use cloud_llm_client::UsageLimit; use codestral::CodestralCompletionProvider; use copilot::{Copilot, Status}; -use editor::{Editor, SelectionEffects, actions::ShowEditPrediction, scroll::Autoscroll}; +use editor::{ + Editor, MultiBufferOffset, SelectionEffects, actions::ShowEditPrediction, scroll::Autoscroll, +}; use feature_flags::{FeatureFlagAppExt, PredictEditsRateCompletionsFeatureFlag}; use fs::Fs; use gpui::{ @@ -1107,7 +1109,12 @@ async fn open_disabled_globs_setting_in_editor( }); if !edits.is_empty() { - item.edit(edits, cx); + item.edit( + edits + .into_iter() + .map(|(r, s)| (MultiBufferOffset(r.start)..MultiBufferOffset(r.end), s)), + cx, + ); } let text = item.buffer().read(cx).snapshot(cx).text(); @@ -1122,6 +1129,7 @@ async fn open_disabled_globs_setting_in_editor( .map(|inner_match| inner_match.start()..inner_match.end()) }); if let Some(range) = range { + let range = MultiBufferOffset(range.start)..MultiBufferOffset(range.end); item.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, diff --git a/crates/editor/benches/display_map.rs b/crates/editor/benches/display_map.rs index 919249ad01b87fe5fbabe1b5fe6e563179b41d10..2459e7466f8054189c9f644e404158b9612e2b9e 100644 --- a/crates/editor/benches/display_map.rs +++ b/crates/editor/benches/display_map.rs @@ -2,6 +2,7 @@ use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use editor::MultiBuffer; use gpui::TestDispatcher; use itertools::Itertools; +use multi_buffer::MultiBufferOffset; use rand::{Rng, SeedableRng, rngs::StdRng}; use std::num::NonZeroU32; use text::Bias; @@ -24,7 +25,9 @@ fn to_tab_point_benchmark(c: &mut Criterion) { let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot); let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone()); let fold_point = fold_snapshot.to_fold_point( - inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))), + inlay_snapshot.to_point(InlayOffset( + rng.random_range(MultiBufferOffset(0)..MultiBufferOffset(length)), + )), Bias::Left, ); let (_, snapshot) = TabMap::new(fold_snapshot, NonZeroU32::new(4).unwrap()); @@ -69,7 +72,9 @@ fn to_fold_point_benchmark(c: &mut Criterion) { let (_, fold_snapshot) = FoldMap::new(inlay_snapshot.clone()); let fold_point = fold_snapshot.to_fold_point( - inlay_snapshot.to_point(InlayOffset(rng.random_range(0..length))), + inlay_snapshot.to_point(InlayOffset( + rng.random_range(MultiBufferOffset(0)..MultiBufferOffset(length)), + )), Bias::Left, ); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index c4c49eb7911e0d7c5ed375d83697584fbb493b81..86aad01a2ea946057fe08876937853f5b84f00bf 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -44,12 +44,10 @@ pub use invisibles::{is_invisible, replacement}; use collections::{HashMap, HashSet}; use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle}; -use language::{ - OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings, -}; +use language::{Point, Subscription as BufferSubscription, language_settings::language_settings}; use multi_buffer::{ - Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, - RowInfo, ToOffset, ToPoint, + Anchor, AnchorRangeExt, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16, + MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint, }; use project::InlayId; use project::project_settings::DiagnosticSeverity; @@ -104,7 +102,7 @@ type InlayHighlights = TreeMap, - buffer_subscription: BufferSubscription, + buffer_subscription: BufferSubscription, /// Decides where the [`Inlay`]s should be displayed. inlay_map: InlayMap, /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded. @@ -198,7 +196,7 @@ impl DisplayMap { pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context) { self.fold( other - .folds_in_range(0..other.buffer_snapshot().len()) + .folds_in_range(MultiBufferOffset(0)..other.buffer_snapshot().len()) .map(|fold| { Crease::simple( fold.range.to_offset(other.buffer_snapshot()), @@ -794,7 +792,7 @@ impl DisplaySnapshot { } pub fn is_empty(&self) -> bool { - self.buffer_snapshot().len() == 0 + self.buffer_snapshot().len() == MultiBufferOffset(0) } pub fn row_infos(&self, start_row: DisplayRow) -> impl Iterator + '_ { @@ -1133,7 +1131,10 @@ impl DisplaySnapshot { }) } - pub fn buffer_chars_at(&self, mut offset: usize) -> impl Iterator + '_ { + pub fn buffer_chars_at( + &self, + mut offset: MultiBufferOffset, + ) -> impl Iterator + '_ { self.buffer_snapshot().chars_at(offset).map(move |ch| { let ret = (ch, offset); offset += ch.len_utf8(); @@ -1143,8 +1144,8 @@ impl DisplaySnapshot { pub fn reverse_buffer_chars_at( &self, - mut offset: usize, - ) -> impl Iterator + '_ { + mut offset: MultiBufferOffset, + ) -> impl Iterator + '_ { self.buffer_snapshot() .reversed_chars_at(offset) .map(move |ch| { @@ -1526,7 +1527,7 @@ impl DisplayPoint { map.display_point_to_point(self, Bias::Left) } - pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize { + pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> MultiBufferOffset { let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias); let tab_point = map.wrap_snapshot().to_tab_point(wrap_point); let fold_point = map.tab_snapshot().to_fold_point(tab_point, bias).0; @@ -1536,13 +1537,13 @@ impl DisplayPoint { } } -impl ToDisplayPoint for usize { +impl ToDisplayPoint for MultiBufferOffset { fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { map.point_to_display_point(self.to_point(map.buffer_snapshot()), Bias::Left) } } -impl ToDisplayPoint for OffsetUtf16 { +impl ToDisplayPoint for MultiBufferOffsetUtf16 { fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint { self.to_offset(map.buffer_snapshot()).to_display_point(map) } @@ -1685,7 +1686,7 @@ pub mod tests { let block_properties = (0..rng.random_range(1..=1)) .map(|_| { let position = buffer.anchor_after(buffer.clip_offset( - rng.random_range(0..=buffer.len()), + rng.random_range(MultiBufferOffset(0)..=buffer.len()), Bias::Left, )); @@ -1727,8 +1728,12 @@ pub mod tests { for _ in 0..rng.random_range(1..=3) { buffer.read_with(cx, |buffer, cx| { let buffer = buffer.read(cx); - let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right); - let start = buffer.clip_offset(rng.random_range(0..=end), Left); + let end = buffer.clip_offset( + rng.random_range(MultiBufferOffset(0)..=buffer.len()), + Right, + ); + let start = buffer + .clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left); ranges.push(start..end); }); } @@ -1954,7 +1959,7 @@ pub mod tests { ) ); - let ix = snapshot.buffer_snapshot().text().find("seven").unwrap(); + let ix = MultiBufferOffset(snapshot.buffer_snapshot().text().find("seven").unwrap()); buffer.update(cx, |buffer, cx| { buffer.edit([(ix..ix, "and ")], None, cx); }); @@ -2083,7 +2088,7 @@ pub mod tests { &[], vec![Inlay::edit_prediction( 0, - buffer_snapshot.anchor_after(0), + buffer_snapshot.anchor_after(MultiBufferOffset(0)), "\n", )], cx, @@ -2094,7 +2099,11 @@ pub mod tests { // Regression test: updating the display map does not crash when a // block is immediately followed by a multi-line inlay. buffer.update(cx, |buffer, cx| { - buffer.edit([(1..1, "b")], None, cx); + buffer.edit( + [(MultiBufferOffset(1)..MultiBufferOffset(1), "b")], + None, + cx, + ); }); map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\nab")); } @@ -2694,6 +2703,7 @@ pub mod tests { HighlightKey::Type(TypeId::of::()), highlighted_ranges .into_iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) .map(|range| { buffer_snapshot.anchor_before(range.start) ..buffer_snapshot.anchor_before(range.end) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 639d2a06579ca16eb938f3d23908e48b702254ef..b55c5330dd398428c549ae1932c1f0a25c8e1436 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -11,8 +11,8 @@ use collections::{Bound, HashMap, HashSet}; use gpui::{AnyElement, App, EntityId, Pixels, Window}; use language::{Patch, Point}; use multi_buffer::{ - Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, RowInfo, - ToOffset, ToPoint as _, + Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferOffset, MultiBufferRow, + MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _, }; use parking_lot::Mutex; use std::{ @@ -1208,7 +1208,7 @@ impl BlockMapWriter<'_> { pub fn remove_intersecting_replace_blocks( &mut self, - ranges: impl IntoIterator>, + ranges: impl IntoIterator>, inclusive: bool, ) { let wrap_snapshot = self.0.wrap_snapshot.borrow(); @@ -1283,7 +1283,7 @@ impl BlockMapWriter<'_> { fn blocks_intersecting_buffer_range( &self, - range: Range, + range: Range, inclusive: bool, ) -> &[Arc] { if range.is_empty() && !inclusive { @@ -3043,8 +3043,10 @@ mod tests { let block_properties = (0..block_count) .map(|_| { let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone()); - let offset = - buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left); + let offset = buffer.clip_offset( + rng.random_range(MultiBufferOffset(0)..=buffer.len()), + Bias::Left, + ); let mut min_height = 0; let placement = match rng.random_range(0..3) { 0 => { @@ -3244,7 +3246,7 @@ mod tests { // Note that this needs to be synced with the related section in BlockMap::sync expected_blocks.extend(block_map.header_and_footer_blocks( &buffer_snapshot, - 0.., + MultiBufferOffset(0).., &wraps_snapshot, )); diff --git a/crates/editor/src/display_map/custom_highlights.rs b/crates/editor/src/display_map/custom_highlights.rs index c6b22bb0b8247420200c2bb8d9e22f55d638386d..e3ae7c99208cb4549a7538ac7f2abcc601c6e6d0 100644 --- a/crates/editor/src/display_map/custom_highlights.rs +++ b/crates/editor/src/display_map/custom_highlights.rs @@ -1,7 +1,7 @@ use collections::BTreeMap; use gpui::HighlightStyle; use language::Chunk; -use multi_buffer::{MultiBufferChunks, MultiBufferSnapshot, ToOffset as _}; +use multi_buffer::{MultiBufferChunks, MultiBufferOffset, MultiBufferSnapshot, ToOffset as _}; use std::{ cmp, iter::{self, Peekable}, @@ -14,7 +14,7 @@ use crate::display_map::{HighlightKey, TextHighlights}; pub struct CustomHighlightsChunks<'a> { buffer_chunks: MultiBufferChunks<'a>, buffer_chunk: Option>, - offset: usize, + offset: MultiBufferOffset, multibuffer_snapshot: &'a MultiBufferSnapshot, highlight_endpoints: Peekable>, @@ -24,14 +24,14 @@ pub struct CustomHighlightsChunks<'a> { #[derive(Debug, Copy, Clone, Eq, PartialEq)] struct HighlightEndpoint { - offset: usize, + offset: MultiBufferOffset, tag: HighlightKey, style: Option, } impl<'a> CustomHighlightsChunks<'a> { pub fn new( - range: Range, + range: Range, language_aware: bool, text_highlights: Option<&'a TextHighlights>, multibuffer_snapshot: &'a MultiBufferSnapshot, @@ -52,7 +52,7 @@ impl<'a> CustomHighlightsChunks<'a> { } } - pub fn seek(&mut self, new_range: Range) { + pub fn seek(&mut self, new_range: Range) { self.highlight_endpoints = create_highlight_endpoints(&new_range, self.text_highlights, self.multibuffer_snapshot); self.offset = new_range.start; @@ -63,7 +63,7 @@ impl<'a> CustomHighlightsChunks<'a> { } fn create_highlight_endpoints( - range: &Range, + range: &Range, text_highlights: Option<&TextHighlights>, buffer: &MultiBufferSnapshot, ) -> iter::Peekable> { @@ -117,7 +117,7 @@ impl<'a> Iterator for CustomHighlightsChunks<'a> { type Item = Chunk<'a>; fn next(&mut self) -> Option { - let mut next_highlight_endpoint = usize::MAX; + let mut next_highlight_endpoint = MultiBufferOffset(usize::MAX); while let Some(endpoint) = self.highlight_endpoints.peek().copied() { if endpoint.offset <= self.offset { if let Some(style) = endpoint.style { @@ -224,20 +224,22 @@ mod tests { let range_count = rng.random_range(1..10); let text = buffer_snapshot.text(); for _ in 0..range_count { - if buffer_snapshot.len() == 0 { + if buffer_snapshot.len() == MultiBufferOffset(0) { continue; } - let mut start = rng.random_range(0..=buffer_snapshot.len().saturating_sub(10)); + let mut start = rng.random_range( + MultiBufferOffset(0)..=buffer_snapshot.len().saturating_sub_usize(10), + ); - while !text.is_char_boundary(start) { - start = start.saturating_sub(1); + while !text.is_char_boundary(start.0) { + start = start.saturating_sub_usize(1); } - let end_end = buffer_snapshot.len().min(start + 100); + let end_end = buffer_snapshot.len().min(start + 100usize); let mut end = rng.random_range(start..=end_end); - while !text.is_char_boundary(end) { - end = end.saturating_sub(1); + while !text.is_char_boundary(end.0) { + end = end.saturating_sub_usize(1); } if start < end { @@ -253,8 +255,12 @@ mod tests { } // Get all chunks and verify their bitmaps - let chunks = - CustomHighlightsChunks::new(0..buffer_snapshot.len(), false, None, &buffer_snapshot); + let chunks = CustomHighlightsChunks::new( + MultiBufferOffset(0)..buffer_snapshot.len(), + false, + None, + &buffer_snapshot, + ); for chunk in chunks { let chunk_text = chunk.text; diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index 4a628f866807aa9b1a1edd45fb9714a5fcc3d5d3..1fe68939adc5aebcb502a5bb28e83ecd668ff88b 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -5,16 +5,17 @@ use super::{ inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot}, }; use gpui::{AnyElement, App, ElementId, HighlightStyle, Pixels, Window}; -use language::{Edit, HighlightId, Point, TextSummary}; +use language::{Edit, HighlightId, Point}; use multi_buffer::{ - Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, + Anchor, AnchorRangeExt, MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot, + RowInfo, ToOffset, }; use project::InlayId; use std::{ any::TypeId, cmp::{self, Ordering}, fmt, iter, - ops::{Add, AddAssign, Deref, DerefMut, Range, Sub}, + ops::{Add, AddAssign, Deref, DerefMut, Range, Sub, SubAssign}, sync::Arc, usize, }; @@ -261,7 +262,7 @@ impl FoldMapWriter<'_> { fold_ixs_to_delete.dedup(); self.0.snapshot.folds = { - let mut cursor = self.0.snapshot.folds.cursor::(buffer); + let mut cursor = self.0.snapshot.folds.cursor::(buffer); let mut folds = SumTree::new(buffer); for fold_ix in fold_ixs_to_delete { folds.append(cursor.slice(&fold_ix, Bias::Right), buffer); @@ -413,7 +414,7 @@ impl FoldMap { let mut new_transforms = SumTree::::default(); let mut cursor = self.snapshot.transforms.cursor::(()); - cursor.seek(&InlayOffset(0), Bias::Right); + cursor.seek(&InlayOffset(MultiBufferOffset(0)), Bias::Right); while let Some(mut edit) = inlay_edits_iter.next() { if let Some(item) = cursor.item() @@ -436,7 +437,7 @@ impl FoldMap { cursor.seek(&edit.old.end, Bias::Right); cursor.next(); - let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize; + let mut delta = edit.new_len() as isize - edit.old_len() as isize; loop { edit.old.end = *cursor.start(); @@ -446,7 +447,7 @@ impl FoldMap { } let next_edit = inlay_edits_iter.next().unwrap(); - delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize; + delta += next_edit.new_len() as isize - next_edit.old_len() as isize; if next_edit.old.end >= edit.old.end { edit.old.end = next_edit.old.end; @@ -458,8 +459,9 @@ impl FoldMap { } } - edit.new.end = - InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize); + edit.new.end = InlayOffset(MultiBufferOffset( + ((edit.new.start + edit.old_len()).0.0 as isize + delta) as usize, + )); let anchor = inlay_snapshot .buffer @@ -522,7 +524,7 @@ impl FoldMap { new_transforms.push( Transform { summary: TransformSummary { - output: TextSummary::from(ELLIPSIS), + output: MBTextSummary::from(ELLIPSIS), input: inlay_snapshot .text_summary_for_range(fold_range.start..fold_range.end), }, @@ -579,7 +581,7 @@ impl FoldMap { edit.old.start = old_transforms.start().0; } let old_start = - old_transforms.start().1.0 + (edit.old.start - old_transforms.start().0).0; + old_transforms.start().1.0 + (edit.old.start - old_transforms.start().0); old_transforms.seek_forward(&edit.old.end, Bias::Right); if old_transforms.item().is_some_and(|t| t.is_fold()) { @@ -587,14 +589,14 @@ impl FoldMap { edit.old.end = old_transforms.start().0; } let old_end = - old_transforms.start().1.0 + (edit.old.end - old_transforms.start().0).0; + old_transforms.start().1.0 + (edit.old.end - old_transforms.start().0); new_transforms.seek(&edit.new.start, Bias::Left); if new_transforms.item().is_some_and(|t| t.is_fold()) { edit.new.start = new_transforms.start().0; } let new_start = - new_transforms.start().1.0 + (edit.new.start - new_transforms.start().0).0; + new_transforms.start().1.0 + (edit.new.start - new_transforms.start().0); new_transforms.seek_forward(&edit.new.end, Bias::Right); if new_transforms.item().is_some_and(|t| t.is_fold()) { @@ -602,7 +604,7 @@ impl FoldMap { edit.new.end = new_transforms.start().0; } let new_end = - new_transforms.start().1.0 + (edit.new.end - new_transforms.start().0).0; + new_transforms.start().1.0 + (edit.new.end - new_transforms.start().0); fold_edits.push(FoldEdit { old: FoldOffset(old_start)..FoldOffset(old_end), @@ -649,9 +651,13 @@ impl FoldSnapshot { #[cfg(test)] pub fn text(&self) -> String { - self.chunks(FoldOffset(0)..self.len(), false, Highlights::default()) - .map(|c| c.text) - .collect() + self.chunks( + FoldOffset(MultiBufferOffset(0))..self.len(), + false, + Highlights::default(), + ) + .map(|c| c.text) + .collect() } #[cfg(test)] @@ -659,8 +665,8 @@ impl FoldSnapshot { self.folds.items(&self.inlay_snapshot.buffer).len() } - pub fn text_summary_for_range(&self, range: Range) -> TextSummary { - let mut summary = TextSummary::default(); + pub fn text_summary_for_range(&self, range: Range) -> MBTextSummary { + let mut summary = MBTextSummary::default(); let mut cursor = self .transforms @@ -670,7 +676,7 @@ impl FoldSnapshot { let start_in_transform = range.start.0 - cursor.start().0.0; let end_in_transform = cmp::min(range.end, cursor.end().0).0 - cursor.start().0.0; if let Some(placeholder) = transform.placeholder.as_ref() { - summary = TextSummary::from( + summary = MBTextSummary::from( &placeholder.text [start_in_transform.column as usize..end_in_transform.column as usize], ); @@ -689,14 +695,14 @@ impl FoldSnapshot { if range.end > cursor.end().0 { cursor.next(); - summary += &cursor + summary += cursor .summary::<_, TransformSummary>(&range.end, Bias::Right) .output; if let Some(transform) = cursor.item() { let end_in_transform = range.end.0 - cursor.start().0.0; if let Some(placeholder) = transform.placeholder.as_ref() { summary += - TextSummary::from(&placeholder.text[..end_in_transform.column as usize]); + MBTextSummary::from(&placeholder.text[..end_in_transform.column as usize]); } else { let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1); let inlay_end = self @@ -839,8 +845,8 @@ impl FoldSnapshot { transform_cursor.seek(&range.start, Bias::Right); let inlay_start = { - let overshoot = range.start.0 - transform_cursor.start().0.0; - transform_cursor.start().1 + InlayOffset(overshoot) + let overshoot = range.start - transform_cursor.start().0; + transform_cursor.start().1 + overshoot }; let transform_end = transform_cursor.end(); @@ -851,8 +857,8 @@ impl FoldSnapshot { { inlay_start } else if range.end < transform_end.0 { - let overshoot = range.end.0 - transform_cursor.start().0.0; - transform_cursor.start().1 + InlayOffset(overshoot) + let overshoot = range.end - transform_cursor.start().0; + transform_cursor.start().1 + overshoot } else { transform_end.1 }; @@ -921,7 +927,7 @@ impl FoldSnapshot { } } -fn push_isomorphic(transforms: &mut SumTree, summary: TextSummary) { +fn push_isomorphic(transforms: &mut SumTree, summary: MBTextSummary) { let mut did_merge = false; transforms.update_last( |last| { @@ -950,13 +956,13 @@ fn push_isomorphic(transforms: &mut SumTree, summary: TextSummary) { fn intersecting_folds<'a>( inlay_snapshot: &'a InlaySnapshot, folds: &'a SumTree, - range: Range, + range: Range, inclusive: bool, -) -> FilterCursor<'a, 'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize> { +) -> FilterCursor<'a, 'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, MultiBufferOffset> { let buffer = &inlay_snapshot.buffer; let start = buffer.anchor_before(range.start.to_offset(buffer)); let end = buffer.anchor_after(range.end.to_offset(buffer)); - let mut cursor = folds.filter::<_, usize>(buffer, move |summary| { + let mut cursor = folds.filter::<_, MultiBufferOffset>(buffer, move |summary| { let start_cmp = start.cmp(&summary.max_end, buffer); let end_cmp = end.cmp(&summary.min_start, buffer); @@ -1061,8 +1067,8 @@ impl Transform { #[derive(Clone, Debug, Default, Eq, PartialEq)] struct TransformSummary { - output: TextSummary, - input: TextSummary, + output: MBTextSummary, + input: MBTextSummary, } impl sum_tree::Item for Transform { @@ -1079,8 +1085,8 @@ impl sum_tree::ContextLessSummary for TransformSummary { } fn add_summary(&mut self, other: &Self) { - self.input += &other.input; - self.output += &other.output; + self.input += other.input; + self.output += other.output; } } @@ -1211,7 +1217,7 @@ impl sum_tree::SeekTarget<'_, FoldSummary, FoldRange> for FoldRange { } } -impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize { +impl<'a> sum_tree::Dimension<'a, FoldSummary> for MultiBufferOffset { fn zero(_cx: &MultiBufferSnapshot) -> Self { Default::default() } @@ -1357,8 +1363,8 @@ impl FoldChunks<'_> { self.transform_cursor.seek(&range.start, Bias::Right); let inlay_start = { - let overshoot = range.start.0 - self.transform_cursor.start().0.0; - self.transform_cursor.start().1 + InlayOffset(overshoot) + let overshoot = range.start - self.transform_cursor.start().0; + self.transform_cursor.start().1 + overshoot }; let transform_end = self.transform_cursor.end(); @@ -1370,8 +1376,8 @@ impl FoldChunks<'_> { { inlay_start } else if range.end < transform_end.0 { - let overshoot = range.end.0 - self.transform_cursor.start().0.0; - self.transform_cursor.start().1 + InlayOffset(overshoot) + let overshoot = range.end - self.transform_cursor.start().0; + self.transform_cursor.start().1 + overshoot } else { transform_end.1 }; @@ -1423,8 +1429,8 @@ impl<'a> Iterator for FoldChunks<'a> { let transform_start = self.transform_cursor.start(); let transform_end = self.transform_cursor.end(); let inlay_end = if self.max_output_offset < transform_end.0 { - let overshoot = self.max_output_offset.0 - transform_start.0.0; - transform_start.1 + InlayOffset(overshoot) + let overshoot = self.max_output_offset - transform_start.0; + transform_start.1 + overshoot } else { transform_end.1 }; @@ -1441,15 +1447,15 @@ impl<'a> Iterator for FoldChunks<'a> { // Otherwise, take a chunk from the buffer's text. if let Some((buffer_chunk_start, mut inlay_chunk)) = self.inlay_chunk.clone() { let chunk = &mut inlay_chunk.chunk; - let buffer_chunk_end = buffer_chunk_start + InlayOffset(chunk.text.len()); + let buffer_chunk_end = buffer_chunk_start + chunk.text.len(); let transform_end = self.transform_cursor.end().1; let chunk_end = buffer_chunk_end.min(transform_end); - let bit_start = (self.inlay_offset - buffer_chunk_start).0; - let bit_end = (chunk_end - buffer_chunk_start).0; + let bit_start = self.inlay_offset - buffer_chunk_start; + let bit_end = chunk_end - buffer_chunk_start; chunk.text = &chunk.text[bit_start..bit_end]; - let bit_end = (chunk_end - buffer_chunk_start).0; + let bit_end = chunk_end - buffer_chunk_start; let mask = 1u128.unbounded_shl(bit_end as u32).wrapping_sub(1); chunk.tabs = (chunk.tabs >> bit_start) & mask; @@ -1483,7 +1489,7 @@ impl<'a> Iterator for FoldChunks<'a> { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct FoldOffset(pub usize); +pub struct FoldOffset(pub MultiBufferOffset); impl FoldOffset { pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint { @@ -1493,7 +1499,7 @@ impl FoldOffset { let overshoot = if item.is_none_or(|t| t.is_fold()) { Point::new(0, (self.0 - start.0.0) as u32) } else { - let inlay_offset = start.1.input.len + self.0 - start.0.0; + let inlay_offset = start.1.input.len + (self - start.0); let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset)); inlay_point.0 - start.1.input.lines }; @@ -1505,7 +1511,7 @@ impl FoldOffset { let (start, _, _) = snapshot .transforms .find::, _>((), &self, Bias::Right); - let overshoot = self.0 - start.0.0; + let overshoot = self - start.0; InlayOffset(start.1.0 + overshoot) } } @@ -1518,17 +1524,46 @@ impl Add for FoldOffset { } } +impl Sub for FoldOffset { + type Output = ::Output; + + fn sub(self, rhs: Self) -> Self::Output { + self.0 - rhs.0 + } +} + +impl SubAssign for FoldOffset +where + MultiBufferOffset: SubAssign, +{ + fn sub_assign(&mut self, rhs: T) { + self.0 -= rhs; + } +} + +impl Add for FoldOffset +where + MultiBufferOffset: Add, +{ + type Output = Self; + + fn add(self, rhs: T) -> Self::Output { + Self(self.0 + rhs) + } +} + impl AddAssign for FoldOffset { fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0; } } -impl Sub for FoldOffset { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) +impl AddAssign for FoldOffset +where + MultiBufferOffset: AddAssign, +{ + fn add_assign(&mut self, rhs: T) { + self.0 += rhs; } } @@ -1538,7 +1573,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset { } fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) { - self.0 += &summary.output.len; + self.0 += summary.output.len; } } @@ -1558,7 +1593,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset { } fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) { - self.0 += &summary.input.len; + self.0 += summary.input.len; } } @@ -1596,12 +1631,12 @@ mod tests { edits, &[ FoldEdit { - old: FoldOffset(2)..FoldOffset(16), - new: FoldOffset(2)..FoldOffset(5), + old: FoldOffset(MultiBufferOffset(2))..FoldOffset(MultiBufferOffset(16)), + new: FoldOffset(MultiBufferOffset(2))..FoldOffset(MultiBufferOffset(5)), }, FoldEdit { - old: FoldOffset(18)..FoldOffset(29), - new: FoldOffset(7)..FoldOffset(10) + old: FoldOffset(MultiBufferOffset(18))..FoldOffset(MultiBufferOffset(29)), + new: FoldOffset(MultiBufferOffset(7))..FoldOffset(MultiBufferOffset(10)), }, ] ); @@ -1626,12 +1661,12 @@ mod tests { edits, &[ FoldEdit { - old: FoldOffset(0)..FoldOffset(1), - new: FoldOffset(0)..FoldOffset(3), + old: FoldOffset(MultiBufferOffset(0))..FoldOffset(MultiBufferOffset(1)), + new: FoldOffset(MultiBufferOffset(0))..FoldOffset(MultiBufferOffset(3)), }, FoldEdit { - old: FoldOffset(6)..FoldOffset(6), - new: FoldOffset(8)..FoldOffset(11), + old: FoldOffset(MultiBufferOffset(6))..FoldOffset(MultiBufferOffset(6)), + new: FoldOffset(MultiBufferOffset(8))..FoldOffset(MultiBufferOffset(11)), }, ] ); @@ -1668,15 +1703,24 @@ mod tests { let mut map = FoldMap::new(inlay_snapshot.clone()).0; let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]); - writer.fold(vec![(5..8, FoldPlaceholder::test())]); + writer.fold(vec![( + MultiBufferOffset(5)..MultiBufferOffset(8), + FoldPlaceholder::test(), + )]); let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]); assert_eq!(snapshot.text(), "abcde⋯ijkl"); // Create an fold adjacent to the start of the first fold. let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]); writer.fold(vec![ - (0..1, FoldPlaceholder::test()), - (2..5, FoldPlaceholder::test()), + ( + MultiBufferOffset(0)..MultiBufferOffset(1), + FoldPlaceholder::test(), + ), + ( + MultiBufferOffset(2)..MultiBufferOffset(5), + FoldPlaceholder::test(), + ), ]); let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]); assert_eq!(snapshot.text(), "⋯b⋯ijkl"); @@ -1684,8 +1728,14 @@ mod tests { // Create an fold adjacent to the end of the first fold. let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]); writer.fold(vec![ - (11..11, FoldPlaceholder::test()), - (8..10, FoldPlaceholder::test()), + ( + MultiBufferOffset(11)..MultiBufferOffset(11), + FoldPlaceholder::test(), + ), + ( + MultiBufferOffset(8)..MultiBufferOffset(10), + FoldPlaceholder::test(), + ), ]); let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]); assert_eq!(snapshot.text(), "⋯b⋯kl"); @@ -1697,15 +1747,25 @@ mod tests { // Create two adjacent folds. let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]); writer.fold(vec![ - (0..2, FoldPlaceholder::test()), - (2..5, FoldPlaceholder::test()), + ( + MultiBufferOffset(0)..MultiBufferOffset(2), + FoldPlaceholder::test(), + ), + ( + MultiBufferOffset(2)..MultiBufferOffset(5), + FoldPlaceholder::test(), + ), ]); let (snapshot, _) = map.read(inlay_snapshot, vec![]); assert_eq!(snapshot.text(), "⋯fghijkl"); // Edit within one of the folds. let buffer_snapshot = buffer.update(cx, |buffer, cx| { - buffer.edit([(0..1, "12345")], None, cx); + buffer.edit( + [(MultiBufferOffset(0)..MultiBufferOffset(1), "12345")], + None, + cx, + ); buffer.snapshot(cx) }); let (inlay_snapshot, inlay_edits) = @@ -1849,7 +1909,7 @@ mod tests { for fold_range in map.merged_folds().into_iter().rev() { let fold_inlay_start = inlay_snapshot.to_inlay_offset(fold_range.start); let fold_inlay_end = inlay_snapshot.to_inlay_offset(fold_range.end); - expected_text.replace_range(fold_inlay_start.0..fold_inlay_end.0, "⋯"); + expected_text.replace_range(fold_inlay_start.0.0..fold_inlay_end.0.0, "⋯"); } assert_eq!(snapshot.text(), expected_text); @@ -1898,7 +1958,7 @@ mod tests { .chars() .count(); let mut fold_point = FoldPoint::new(0, 0); - let mut fold_offset = FoldOffset(0); + let mut fold_offset = FoldOffset(MultiBufferOffset(0)); let mut char_column = 0; for c in expected_text.chars() { let inlay_point = fold_point.to_inlay_point(&snapshot); @@ -1944,18 +2004,18 @@ mod tests { for _ in 0..5 { let mut start = snapshot.clip_offset( - FoldOffset(rng.random_range(0..=snapshot.len().0)), + FoldOffset(rng.random_range(MultiBufferOffset(0)..=snapshot.len().0)), Bias::Left, ); let mut end = snapshot.clip_offset( - FoldOffset(rng.random_range(0..=snapshot.len().0)), + FoldOffset(rng.random_range(MultiBufferOffset(0)..=snapshot.len().0)), Bias::Right, ); if start > end { mem::swap(&mut start, &mut end); } - let text = &expected_text[start.0..end.0]; + let text = &expected_text[start.0.0..end.0.0]; assert_eq!( snapshot .chunks(start..end, false, Highlights::default()) @@ -2004,9 +2064,12 @@ mod tests { } for _ in 0..5 { - let end = - buffer_snapshot.clip_offset(rng.random_range(0..=buffer_snapshot.len()), Right); - let start = buffer_snapshot.clip_offset(rng.random_range(0..=end), Left); + let end = buffer_snapshot.clip_offset( + rng.random_range(MultiBufferOffset(0)..=buffer_snapshot.len()), + Right, + ); + let start = + buffer_snapshot.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left); let expected_folds = map .snapshot .folds @@ -2046,7 +2109,7 @@ mod tests { let bytes = start.to_offset(&snapshot)..end.to_offset(&snapshot); assert_eq!( snapshot.text_summary_for_range(lines), - TextSummary::from(&text[bytes.start.0..bytes.end.0]) + MBTextSummary::from(&text[bytes.start.0.0..bytes.end.0.0]) ) } @@ -2054,8 +2117,8 @@ mod tests { for (snapshot, edits) in snapshot_edits.drain(..) { let new_text = snapshot.text(); for edit in edits { - let old_bytes = edit.new.start.0..edit.new.start.0 + edit.old_len().0; - let new_bytes = edit.new.start.0..edit.new.end.0; + let old_bytes = edit.new.start.0.0..edit.new.start.0.0 + edit.old_len(); + let new_bytes = edit.new.start.0.0..edit.new.end.0.0; text.replace_range(old_bytes, &new_text[new_bytes]); } @@ -2126,7 +2189,7 @@ mod tests { // Get all chunks and verify their bitmaps let chunks = snapshot.chunks( - FoldOffset(0)..FoldOffset(snapshot.len().0), + FoldOffset(MultiBufferOffset(0))..FoldOffset(snapshot.len().0), false, Highlights::default(), ); @@ -2195,7 +2258,7 @@ mod tests { } impl FoldMap { - fn merged_folds(&self) -> Vec> { + fn merged_folds(&self) -> Vec> { let inlay_snapshot = self.snapshot.inlay_snapshot.clone(); let buffer = &inlay_snapshot.buffer; let mut folds = self.snapshot.folds.items(buffer); @@ -2236,8 +2299,12 @@ mod tests { let buffer = &inlay_snapshot.buffer; let mut to_unfold = Vec::new(); for _ in 0..rng.random_range(1..=3) { - let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right); - let start = buffer.clip_offset(rng.random_range(0..=end), Left); + let end = buffer.clip_offset( + rng.random_range(MultiBufferOffset(0)..=buffer.len()), + Right, + ); + let start = + buffer.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left); to_unfold.push(start..end); } let inclusive = rng.random(); @@ -2252,8 +2319,12 @@ mod tests { let buffer = &inlay_snapshot.buffer; let mut to_fold = Vec::new(); for _ in 0..rng.random_range(1..=2) { - let end = buffer.clip_offset(rng.random_range(0..=buffer.len()), Right); - let start = buffer.clip_offset(rng.random_range(0..=end), Left); + let end = buffer.clip_offset( + rng.random_range(MultiBufferOffset(0)..=buffer.len()), + Right, + ); + let start = + buffer.clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left); to_fold.push((start..end, FoldPlaceholder::test())); } log::info!("folding {:?}", to_fold); diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index f3f3a3eee8ea6d1f95261ae4d313afb6f4d497e3..979c398a23efd34ec223ff6136f023a33dc4a81f 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -4,7 +4,10 @@ use crate::{ }; use collections::BTreeSet; use language::{Chunk, Edit, Point, TextSummary}; -use multi_buffer::{MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, RowInfo, ToOffset}; +use multi_buffer::{ + MBTextSummary, MultiBufferOffset, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, + RowInfo, ToOffset, +}; use project::InlayId; use std::{ cmp, @@ -42,7 +45,7 @@ impl std::ops::Deref for InlaySnapshot { #[derive(Clone, Debug)] enum Transform { - Isomorphic(TextSummary), + Isomorphic(MBTextSummary), Inlay(Inlay), } @@ -56,8 +59,8 @@ impl sum_tree::Item for Transform { output: *summary, }, Transform::Inlay(inlay) => TransformSummary { - input: TextSummary::default(), - output: inlay.text().summary(), + input: MBTextSummary::default(), + output: MBTextSummary::from(inlay.text().summary()), }, } } @@ -65,8 +68,8 @@ impl sum_tree::Item for Transform { #[derive(Clone, Debug, Default)] struct TransformSummary { - input: TextSummary, - output: TextSummary, + input: MBTextSummary, + output: MBTextSummary, } impl sum_tree::ContextLessSummary for TransformSummary { @@ -75,15 +78,15 @@ impl sum_tree::ContextLessSummary for TransformSummary { } fn add_summary(&mut self, other: &Self) { - self.input += &other.input; - self.output += &other.output; + self.input += other.input; + self.output += other.output; } } pub type InlayEdit = Edit; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct InlayOffset(pub usize); +pub struct InlayOffset(pub MultiBufferOffset); impl Add for InlayOffset { type Output = Self; @@ -94,10 +97,30 @@ impl Add for InlayOffset { } impl Sub for InlayOffset { - type Output = Self; + type Output = ::Output; fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) + self.0 - rhs.0 + } +} + +impl SubAssign for InlayOffset +where + MultiBufferOffset: SubAssign, +{ + fn sub_assign(&mut self, rhs: T) { + self.0 -= rhs; + } +} + +impl Add for InlayOffset +where + MultiBufferOffset: Add, +{ + type Output = Self; + + fn add(self, rhs: T) -> Self::Output { + Self(self.0 + rhs) } } @@ -107,9 +130,12 @@ impl AddAssign for InlayOffset { } } -impl SubAssign for InlayOffset { - fn sub_assign(&mut self, rhs: Self) { - self.0 -= rhs.0; +impl AddAssign for InlayOffset +where + MultiBufferOffset: AddAssign, +{ + fn add_assign(&mut self, rhs: T) { + self.0 += rhs; } } @@ -119,7 +145,7 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset { } fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) { - self.0 += &summary.output.len; + self.0 += summary.output.len; } } @@ -152,13 +178,13 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint { } } -impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize { +impl<'a> sum_tree::Dimension<'a, TransformSummary> for MultiBufferOffset { fn zero(_cx: ()) -> Self { Default::default() } fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) { - *self += &summary.input.len; + *self += summary.input.len; } } @@ -181,7 +207,7 @@ pub struct InlayBufferRows<'a> { } pub struct InlayChunks<'a> { - transforms: Cursor<'a, 'static, Transform, Dimensions>, + transforms: Cursor<'a, 'static, Transform, Dimensions>, buffer_chunks: CustomHighlightsChunks<'a>, buffer_chunk: Option>, inlay_chunks: Option>, @@ -332,12 +358,12 @@ impl<'a> Iterator for InlayChunks<'a> { let offset_in_inlay = self.output_offset - self.transforms.start().0; if let Some((style, highlight)) = inlay_style_and_highlight { let range = &highlight.range; - if offset_in_inlay.0 < range.start { - next_inlay_highlight_endpoint = range.start - offset_in_inlay.0; - } else if offset_in_inlay.0 >= range.end { + if offset_in_inlay < range.start { + next_inlay_highlight_endpoint = range.start - offset_in_inlay; + } else if offset_in_inlay >= range.end { next_inlay_highlight_endpoint = usize::MAX; } else { - next_inlay_highlight_endpoint = range.end - offset_in_inlay.0; + next_inlay_highlight_endpoint = range.end - offset_in_inlay; highlight_style = highlight_style .map(|highlight| highlight.highlight(*style)) .or_else(|| Some(*style)); @@ -350,7 +376,7 @@ impl<'a> Iterator for InlayChunks<'a> { let start = offset_in_inlay; let end = cmp::min(self.max_output_offset, self.transforms.end().0) - self.transforms.start().0; - let chunks = inlay.text().chunks_in_range(start.0..end.0); + let chunks = inlay.text().chunks_in_range(start..end); text::ChunkWithBitmaps(chunks) }); let ChunkBitmaps { @@ -488,7 +514,7 @@ impl InlayMap { pub fn sync( &mut self, buffer_snapshot: MultiBufferSnapshot, - mut buffer_edits: Vec>, + mut buffer_edits: Vec>, ) -> (InlaySnapshot, Vec) { let snapshot = &mut self.snapshot; @@ -519,7 +545,7 @@ impl InlayMap { let mut new_transforms = SumTree::default(); let mut cursor = snapshot .transforms - .cursor::>(()); + .cursor::>(()); let mut buffer_edits_iter = buffer_edits.iter().peekable(); while let Some(buffer_edit) = buffer_edits_iter.next() { new_transforms.append(cursor.slice(&buffer_edit.old.start, Bias::Left), ()); @@ -531,11 +557,9 @@ impl InlayMap { } // Remove all the inlays and transforms contained by the edit. - let old_start = - cursor.start().1 + InlayOffset(buffer_edit.old.start - cursor.start().0); + let old_start = cursor.start().1 + (buffer_edit.old.start - cursor.start().0); cursor.seek(&buffer_edit.old.end, Bias::Right); - let old_end = - cursor.start().1 + InlayOffset(buffer_edit.old.end - cursor.start().0); + let old_end = cursor.start().1 + (buffer_edit.old.end - cursor.start().0); // Push the unchanged prefix. let prefix_start = new_transforms.summary().input.len; @@ -687,7 +711,10 @@ impl InlayMap { let snapshot = &mut self.snapshot; for i in 0..rng.random_range(1..=5) { if self.inlays.is_empty() || rng.random() { - let position = snapshot.buffer.random_byte_range(0, rng).start; + let position = snapshot + .buffer + .random_byte_range(MultiBufferOffset(0), rng) + .start; let bias = if rng.random() { Bias::Left } else { @@ -740,9 +767,11 @@ impl InlayMap { impl InlaySnapshot { pub fn to_point(&self, offset: InlayOffset) -> InlayPoint { - let (start, _, item) = self - .transforms - .find::, _>((), &offset, Bias::Right); + let (start, _, item) = self.transforms.find::, _>((), &offset, Bias::Right); let overshoot = offset.0 - start.0.0; match item { Some(Transform::Isomorphic(_)) => { @@ -801,22 +830,24 @@ impl InlaySnapshot { None => self.buffer.max_point(), } } - pub fn to_buffer_offset(&self, offset: InlayOffset) -> usize { - let (start, _, item) = - self.transforms - .find::, _>((), &offset, Bias::Right); + pub fn to_buffer_offset(&self, offset: InlayOffset) -> MultiBufferOffset { + let (start, _, item) = self + .transforms + .find::, _>((), &offset, Bias::Right); match item { Some(Transform::Isomorphic(_)) => { let overshoot = offset - start.0; - start.1 + overshoot.0 + start.1 + overshoot } Some(Transform::Inlay(_)) => start.1, None => self.buffer.len(), } } - pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset { - let mut cursor = self.transforms.cursor::>(()); + pub fn to_inlay_offset(&self, offset: MultiBufferOffset) -> InlayOffset { + let mut cursor = self + .transforms + .cursor::>(()); cursor.seek(&offset, Bias::Left); loop { match cursor.item() { @@ -973,14 +1004,16 @@ impl InlaySnapshot { } } - pub fn text_summary(&self) -> TextSummary { + pub fn text_summary(&self) -> MBTextSummary { self.transforms.summary().output } - pub fn text_summary_for_range(&self, range: Range) -> TextSummary { - let mut summary = TextSummary::default(); + pub fn text_summary_for_range(&self, range: Range) -> MBTextSummary { + let mut summary = MBTextSummary::default(); - let mut cursor = self.transforms.cursor::>(()); + let mut cursor = self + .transforms + .cursor::>(()); cursor.seek(&range.start, Bias::Right); let overshoot = range.start.0 - cursor.start().0.0; @@ -996,7 +1029,12 @@ impl InlaySnapshot { Some(Transform::Inlay(inlay)) => { let suffix_start = overshoot; let suffix_end = cmp::min(cursor.end().0, range.end).0 - cursor.start().0.0; - summary = inlay.text().cursor(suffix_start).summary(suffix_end); + summary = MBTextSummary::from( + inlay + .text() + .cursor(suffix_start) + .summary::(suffix_end), + ); cursor.next(); } None => {} @@ -1014,7 +1052,7 @@ impl InlaySnapshot { let prefix_end = prefix_start + overshoot; summary += self .buffer - .text_summary_for_range::(prefix_start..prefix_end); + .text_summary_for_range::(prefix_start..prefix_end); } Some(Transform::Inlay(inlay)) => { let prefix_end = overshoot; @@ -1070,7 +1108,9 @@ impl InlaySnapshot { language_aware: bool, highlights: Highlights<'a>, ) -> InlayChunks<'a> { - let mut cursor = self.transforms.cursor::>(()); + let mut cursor = self + .transforms + .cursor::>(()); cursor.seek(&range.start, Bias::Right); let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end); @@ -1122,8 +1162,8 @@ impl InlaySnapshot { } } -fn push_isomorphic(sum_tree: &mut SumTree, summary: TextSummary) { - if summary.len == 0 { +fn push_isomorphic(sum_tree: &mut SumTree, summary: MBTextSummary) { + if summary.len == MultiBufferOffset(0) { return; } @@ -1279,7 +1319,10 @@ mod tests { &[], vec![Inlay::mock_hint( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_after(3), + buffer + .read(cx) + .snapshot(cx) + .anchor_after(MultiBufferOffset(3)), "|123|", )], ); @@ -1335,7 +1378,15 @@ mod tests { // Edits before or after the inlay should not affect it. buffer.update(cx, |buffer, cx| { - buffer.edit([(2..3, "x"), (3..3, "y"), (4..4, "z")], None, cx) + buffer.edit( + [ + (MultiBufferOffset(2)..MultiBufferOffset(3), "x"), + (MultiBufferOffset(3)..MultiBufferOffset(3), "y"), + (MultiBufferOffset(4)..MultiBufferOffset(4), "z"), + ], + None, + cx, + ) }); let (inlay_snapshot, _) = inlay_map.sync( buffer.read(cx).snapshot(cx), @@ -1344,7 +1395,13 @@ mod tests { assert_eq!(inlay_snapshot.text(), "abxy|123|dzefghi"); // An edit surrounding the inlay should invalidate it. - buffer.update(cx, |buffer, cx| buffer.edit([(4..5, "D")], None, cx)); + buffer.update(cx, |buffer, cx| { + buffer.edit( + [(MultiBufferOffset(4)..MultiBufferOffset(5), "D")], + None, + cx, + ) + }); let (inlay_snapshot, _) = inlay_map.sync( buffer.read(cx).snapshot(cx), buffer_edits.consume().into_inner(), @@ -1356,12 +1413,18 @@ mod tests { vec![ Inlay::mock_hint( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_before(3), + buffer + .read(cx) + .snapshot(cx) + .anchor_before(MultiBufferOffset(3)), "|123|", ), Inlay::edit_prediction( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_after(3), + buffer + .read(cx) + .snapshot(cx) + .anchor_after(MultiBufferOffset(3)), "|456|", ), ], @@ -1369,7 +1432,13 @@ mod tests { assert_eq!(inlay_snapshot.text(), "abx|123||456|yDzefghi"); // Edits ending where the inlay starts should not move it if it has a left bias. - buffer.update(cx, |buffer, cx| buffer.edit([(3..3, "JKL")], None, cx)); + buffer.update(cx, |buffer, cx| { + buffer.edit( + [(MultiBufferOffset(3)..MultiBufferOffset(3), "JKL")], + None, + cx, + ) + }); let (inlay_snapshot, _) = inlay_map.sync( buffer.read(cx).snapshot(cx), buffer_edits.consume().into_inner(), @@ -1571,17 +1640,26 @@ mod tests { vec![ Inlay::mock_hint( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_before(0), + buffer + .read(cx) + .snapshot(cx) + .anchor_before(MultiBufferOffset(0)), "|123|\n", ), Inlay::mock_hint( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_before(4), + buffer + .read(cx) + .snapshot(cx) + .anchor_before(MultiBufferOffset(4)), "|456|", ), Inlay::edit_prediction( post_inc(&mut next_inlay_id), - buffer.read(cx).snapshot(cx).anchor_before(7), + buffer + .read(cx) + .snapshot(cx) + .anchor_before(MultiBufferOffset(7)), "\n|567|\n", ), ], @@ -1658,7 +1736,7 @@ mod tests { .collect::>(); let mut expected_text = Rope::from(&buffer_snapshot.text()); for (offset, inlay) in inlays.iter().rev() { - expected_text.replace(*offset..*offset, &inlay.text().to_string()); + expected_text.replace(offset.0..offset.0, &inlay.text().to_string()); } assert_eq!(inlay_snapshot.text(), expected_text.to_string()); @@ -1681,7 +1759,7 @@ mod tests { let mut text_highlights = TextHighlights::default(); let text_highlight_count = rng.random_range(0_usize..10); let mut text_highlight_ranges = (0..text_highlight_count) - .map(|_| buffer_snapshot.random_byte_range(0, &mut rng)) + .map(|_| buffer_snapshot.random_byte_range(MultiBufferOffset(0), &mut rng)) .collect::>(); text_highlight_ranges.sort_by_key(|range| (range.start, Reverse(range.end))); log::info!("highlighting text ranges {text_highlight_ranges:?}"); @@ -1744,12 +1822,13 @@ mod tests { } for _ in 0..5 { - let mut end = rng.random_range(0..=inlay_snapshot.len().0); + let mut end = rng.random_range(0..=inlay_snapshot.len().0.0); end = expected_text.clip_offset(end, Bias::Right); let mut start = rng.random_range(0..=end); start = expected_text.clip_offset(start, Bias::Right); - let range = InlayOffset(start)..InlayOffset(end); + let range = + InlayOffset(MultiBufferOffset(start))..InlayOffset(MultiBufferOffset(end)); log::info!("calling inlay_snapshot.chunks({range:?})"); let actual_text = inlay_snapshot .chunks( @@ -1771,25 +1850,27 @@ mod tests { ); assert_eq!( - inlay_snapshot.text_summary_for_range(InlayOffset(start)..InlayOffset(end)), - expected_text.slice(start..end).summary() + inlay_snapshot.text_summary_for_range( + InlayOffset(MultiBufferOffset(start))..InlayOffset(MultiBufferOffset(end)) + ), + MBTextSummary::from(expected_text.slice(start..end).summary()) ); } for edit in inlay_edits { prev_inlay_text.replace_range( - edit.new.start.0..edit.new.start.0 + edit.old_len().0, - &inlay_snapshot.text()[edit.new.start.0..edit.new.end.0], + edit.new.start.0.0..edit.new.start.0.0 + edit.old_len(), + &inlay_snapshot.text()[edit.new.start.0.0..edit.new.end.0.0], ); } assert_eq!(prev_inlay_text, inlay_snapshot.text()); assert_eq!(expected_text.max_point(), inlay_snapshot.max_point().0); - assert_eq!(expected_text.len(), inlay_snapshot.len().0); + assert_eq!(expected_text.len(), inlay_snapshot.len().0.0); let mut buffer_point = Point::default(); let mut inlay_point = inlay_snapshot.to_inlay_point(buffer_point); - let mut buffer_chars = buffer_snapshot.chars_at(0); + let mut buffer_chars = buffer_snapshot.chars_at(MultiBufferOffset(0)); loop { // Ensure conversion from buffer coordinates to inlay coordinates // is consistent. @@ -1930,7 +2011,7 @@ mod tests { // Get all chunks and verify their bitmaps let chunks = snapshot.chunks( - InlayOffset(0)..InlayOffset(snapshot.len().0), + InlayOffset(MultiBufferOffset(0))..snapshot.len(), false, Highlights::default(), ); @@ -2064,7 +2145,7 @@ mod tests { // Collect chunks - this previously would panic let chunks: Vec<_> = inlay_snapshot .chunks( - InlayOffset(0)..InlayOffset(inlay_snapshot.len().0), + InlayOffset(MultiBufferOffset(0))..inlay_snapshot.len(), false, highlights, ) @@ -2178,7 +2259,7 @@ mod tests { let chunks: Vec<_> = inlay_snapshot .chunks( - InlayOffset(0)..InlayOffset(inlay_snapshot.len().0), + InlayOffset(MultiBufferOffset(0))..inlay_snapshot.len(), false, highlights, ) diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index ab3bddf7278605e89b816831059de73873853b32..a8ffbbb177f8806fdeec95fc73f4a1a217b9dc39 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -648,6 +648,7 @@ mod tests { inlay_map::InlayMap, }, }; + use multi_buffer::MultiBufferOffset; use rand::{Rng, prelude::StdRng}; use util; @@ -1156,7 +1157,7 @@ mod tests { let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot); let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); let chunks = fold_snapshot.chunks( - FoldOffset(0)..fold_snapshot.len(), + FoldOffset(MultiBufferOffset(0))..fold_snapshot.len(), false, Default::default(), ); @@ -1318,7 +1319,7 @@ mod tests { let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot); let (_, fold_snapshot) = FoldMap::new(inlay_snapshot); let chunks = fold_snapshot.chunks( - FoldOffset(0)..fold_snapshot.len(), + FoldOffset(MultiBufferOffset(0))..fold_snapshot.len(), false, Default::default(), ); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4b8b6a8c881d51f6c702c7d7cb1301e7a54b5318..9401d5a1e2f36f7b0c00c4c3dec3fc90597290e5 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -64,8 +64,9 @@ pub use items::MAX_TAB_TITLE_LEN; pub use lsp::CompletionContext; pub use lsp_ext::lsp_tasks; pub use multi_buffer::{ - Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, PathKey, - RowInfo, ToOffset, ToPoint, + Anchor, AnchorRangeExt, BufferOffset, ExcerptId, ExcerptRange, MBTextSummary, MultiBuffer, + MultiBufferOffset, MultiBufferOffsetUtf16, MultiBufferSnapshot, PathKey, RowInfo, ToOffset, + ToPoint, }; pub use text::Bias; @@ -118,8 +119,7 @@ use language::{ BufferSnapshot, Capability, CharClassifier, CharKind, CharScopeContext, CodeLabel, CursorShape, DiagnosticEntryRef, DiffOptions, EditPredictionsMode, EditPreview, HighlightedText, IndentKind, IndentSize, Language, LanguageRegistry, OffsetRangeExt, OutlineItem, Point, Runnable, - RunnableRange, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, - WordsQuery, + Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery, language_settings::{ self, LspInsertMode, RewrapBehavior, WordsCompletionMode, all_language_settings, language_settings, @@ -856,9 +856,6 @@ pub struct ResolvedTasks { position: Anchor, } -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] -struct BufferOffset(usize); - /// Addons allow storing per-editor state in other crates (e.g. Vim) pub trait Addon: 'static { fn extend_key_context(&self, _: &mut KeyContext, _: &App) {} @@ -1575,7 +1572,7 @@ pub struct ClipboardSelection { // selections, scroll behavior, was newest selection reversed type SelectSyntaxNodeHistoryState = ( - Box<[Selection]>, + Box<[Selection]>, SelectSyntaxNodeScrollBehavior, bool, ); @@ -1930,16 +1927,18 @@ impl Editor { } } project::Event::SnippetEdit(id, snippet_edits) => { - if let Some(buffer) = editor.buffer.read(cx).buffer(*id) { + // todo(lw): Non singletons + if let Some(buffer) = editor.buffer.read(cx).as_singleton() { + let snapshot = buffer.read(cx).snapshot(); let focus_handle = editor.focus_handle(cx); - if focus_handle.is_focused(window) { - let snapshot = buffer.read(cx).snapshot(); + if snapshot.remote_id() == *id && focus_handle.is_focused(window) { for (range, snippet) in snippet_edits { - let editor_range = + let buffer_range = language::range_from_lsp(*range).to_offset(&snapshot); editor .insert_snippet( - &[editor_range], + &[MultiBufferOffset(buffer_range.start) + ..MultiBufferOffset(buffer_range.end)], snippet.clone(), window, cx, @@ -2516,7 +2515,7 @@ impl Editor { } self.selections - .disjoint_in_range::(range.clone(), &self.display_snapshot(cx)) + .disjoint_in_range::(range.clone(), &self.display_snapshot(cx)) .into_iter() .any(|selection| { // This is needed to cover a corner case, if we just check for an existing @@ -3177,7 +3176,9 @@ impl Editor { // Copy selections to primary selection buffer #[cfg(any(target_os = "linux", target_os = "freebsd"))] if local { - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); let buffer_handle = self.buffer.read(cx).read(cx); let mut text = String::new(); @@ -3333,8 +3334,8 @@ impl Editor { .iter() .map(|selection| { ( - selection.start.to_offset(&snapshot), - selection.end.to_offset(&snapshot), + selection.start.to_offset(&snapshot).0, + selection.end.to_offset(&snapshot).0, ) }) .collect(); @@ -3376,7 +3377,7 @@ impl Editor { return; }; let inmemory_folds = display_snapshot - .folds_in_range(0..display_snapshot.buffer_snapshot().len()) + .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len()) .map(|fold| { fold.range.start.text_anchor.to_point(&snapshot) ..fold.range.end.text_anchor.to_point(&snapshot) @@ -3392,7 +3393,7 @@ impl Editor { let background_executor = cx.background_executor().clone(); let editor_id = cx.entity().entity_id().as_u64() as ItemId; let db_folds = display_snapshot - .folds_in_range(0..display_snapshot.buffer_snapshot().len()) + .folds_in_range(MultiBufferOffset(0)..display_snapshot.buffer_snapshot().len()) .map(|fold| { ( fold.range.start.text_anchor.to_offset(&snapshot), @@ -3649,7 +3650,10 @@ impl Editor { cx: &mut Context, ) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - let tail = self.selections.newest::(&display_map).tail(); + let tail = self + .selections + .newest::(&display_map) + .tail(); let click_count = click_count.max(match self.selections.select_mode() { SelectMode::Character => 1, SelectMode::Word(_) => 2, @@ -3758,7 +3762,7 @@ impl Editor { auto_scroll = true; } _ => { - start = buffer.anchor_before(0); + start = buffer.anchor_before(MultiBufferOffset(0)); end = buffer.anchor_before(buffer.len()); mode = SelectMode::All; auto_scroll = false; @@ -3971,7 +3975,9 @@ impl Editor { fn end_selection(&mut self, window: &mut Window, cx: &mut Context) { self.columnar_selection_state.take(); if let Some(pending_mode) = self.selections.pending_mode() { - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select(selections); s.clear_pending(); @@ -4505,17 +4511,19 @@ impl Editor { let new_anchor_selections = new_selections.iter().map(|e| &e.0); let new_selection_deltas = new_selections.iter().map(|e| e.1); let map = this.display_map.update(cx, |map, cx| map.snapshot(cx)); - let new_selections = - resolve_selections_wrapping_blocks::(new_anchor_selections, &map) - .zip(new_selection_deltas) - .map(|(selection, delta)| Selection { - id: selection.id, - start: selection.start + delta, - end: selection.end + delta, - reversed: selection.reversed, - goal: SelectionGoal::None, - }) - .collect::>(); + let new_selections = resolve_selections_wrapping_blocks::( + new_anchor_selections, + &map, + ) + .zip(new_selection_deltas) + .map(|(selection, delta)| Selection { + id: selection.id, + start: selection.start + delta, + end: selection.end + delta, + reversed: selection.reversed, + goal: SelectionGoal::None, + }) + .collect::>(); let mut i = 0; for (position, delta, selection_id, pair) in new_autoclose_regions { @@ -4651,7 +4659,9 @@ impl Editor { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); self.transact(window, cx, |this, window, cx| { let (edits_with_flags, selection_info): (Vec<_>, Vec<_>) = { - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); let multi_buffer = this.buffer.read(cx); let buffer = multi_buffer.snapshot(cx); selections @@ -5151,7 +5161,9 @@ impl Editor { /// If any empty selections is touching the start of its innermost containing autoclose /// region, expand it to select the brackets. fn select_autoclose_pair(&mut self, window: &mut Window, cx: &mut Context) { - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); let buffer = self.buffer.read(cx).read(cx); let new_selections = self .selections_with_autoclose_regions(selections, &buffer) @@ -5162,7 +5174,7 @@ impl Editor { if let Some(region) = region { let mut range = region.range.to_offset(&buffer); - if selection.start == range.start && range.start >= region.pair.start.len() { + if selection.start == range.start && range.start.0 >= region.pair.start.len() { range.start -= region.pair.start.len(); if buffer.contains_str_at(range.start, ®ion.pair.start) && buffer.contains_str_at(range.end, ®ion.pair.end) @@ -5193,7 +5205,7 @@ impl Editor { if buffer.contains_str_at(selection.start, &pair.end) { let pair_start_len = pair.start.len(); if buffer.contains_str_at( - selection.start.saturating_sub(pair_start_len), + selection.start.saturating_sub_usize(pair_start_len), &pair.start, ) { selection.start -= pair_start_len; @@ -5330,7 +5342,7 @@ impl Editor { ( multi_buffer.buffer(buffer.remote_id()).unwrap(), buffer.version().clone(), - excerpt_visible_range, + excerpt_visible_range.start.0..excerpt_visible_range.end.0, ), )) } @@ -6013,14 +6025,17 @@ impl Editor { .start .text_anchor .to_offset(buffer) - .saturating_sub(replace_range.start); + .saturating_sub(replace_range.start.0); let lookahead = replace_range .end + .0 .saturating_sub(newest_anchor.end.text_anchor.to_offset(buffer)); let prefix = &old_text[..old_text.len().saturating_sub(lookahead)]; let suffix = &old_text[lookbehind.min(old_text.len())..]; - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); let mut ranges = Vec::new(); let mut linked_edits = HashMap::<_, Vec<_>>::default(); @@ -6031,8 +6046,8 @@ impl Editor { let mut range = selection.range(); // if prefix is present, don't duplicate it - if snapshot.contains_str_at(range.start.saturating_sub(lookbehind), prefix) { - range.start = range.start.saturating_sub(lookbehind); + if snapshot.contains_str_at(range.start.saturating_sub_usize(lookbehind), prefix) { + range.start = range.start.saturating_sub_usize(lookbehind); // if suffix is also present, mimic the newest cursor and replace it if selection.id != newest_anchor.id @@ -7008,7 +7023,10 @@ impl Editor { for (buffer_snapshot, search_range, excerpt_id) in buffer_ranges { match_ranges.extend( regex - .search(buffer_snapshot, Some(search_range.clone())) + .search( + buffer_snapshot, + Some(search_range.start.0..search_range.end.0), + ) .await .into_iter() .filter_map(|match_range| { @@ -7055,12 +7073,12 @@ impl Editor { } let task = cx.background_spawn(async move { let new_newlines = snapshot - .buffer_chars_at(0) + .buffer_chars_at(MultiBufferOffset(0)) .filter_map(|(c, i)| { if c == '\n' { Some( snapshot.buffer_snapshot().anchor_after(i) - ..snapshot.buffer_snapshot().anchor_before(i + 1), + ..snapshot.buffer_snapshot().anchor_before(i + 1usize), ) } else { None @@ -7068,7 +7086,7 @@ impl Editor { }) .collect::>(); let existing_newlines = snapshot - .folds_in_range(0..snapshot.buffer_snapshot().len()) + .folds_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len()) .filter_map(|fold| { if fold.placeholder.type_tag == Some(type_id) { Some(fold.range.start..fold.range.end) @@ -7165,7 +7183,7 @@ impl Editor { .is_none_or(|(prev_anchor_range, _)| prev_anchor_range != &query_range) { let multi_buffer_start = multi_buffer_snapshot - .anchor_before(0) + .anchor_before(MultiBufferOffset(0)) .to_point(&multi_buffer_snapshot); let multi_buffer_end = multi_buffer_snapshot .anchor_after(multi_buffer_snapshot.len()) @@ -7597,7 +7615,7 @@ impl Editor { let snapshot = self.buffer.read(cx).snapshot(cx); let cursor_offset = self .selections - .newest::(&self.display_snapshot(cx)) + .newest::(&self.display_snapshot(cx)) .head(); let insertion = edits.iter().find_map(|(range, text)| { let range = range.to_offset(&snapshot); @@ -8589,16 +8607,17 @@ impl Editor { let snapshot = self.buffer.read(cx).snapshot(cx); let offset = self .selections - .newest::(&self.display_snapshot(cx)) + .newest::(&self.display_snapshot(cx)) .head(); - let excerpt = snapshot.excerpt_containing(offset..offset)?; + let mut excerpt = snapshot.excerpt_containing(offset..offset)?; + let offset = excerpt.map_offset_to_buffer(offset); let buffer_id = excerpt.buffer().remote_id(); let layer = excerpt.buffer().syntax_layer_at(offset)?; let mut cursor = layer.node().walk(); - while cursor.goto_first_child_for_byte(offset).is_some() { - if cursor.node().end_byte() == offset { + while cursor.goto_first_child_for_byte(offset.0).is_some() { + if cursor.node().end_byte() == offset.0 { cursor.goto_next_sibling(); } } @@ -8610,7 +8629,7 @@ impl Editor { let symbol_start_row = excerpt.buffer().offset_to_point(node.start_byte()).row; // Check if this node contains our offset - if node_range.start <= offset && node_range.end >= offset { + if node_range.start <= offset.0 && node_range.end >= offset.0 { // If it contains offset, check for task if let Some(tasks) = self.tasks.get(&(buffer_id, symbol_start_row)) { let buffer = self.buffer.read(cx).buffer(buffer_id)?; @@ -9812,7 +9831,7 @@ impl Editor { pub fn insert_snippet( &mut self, - insertion_ranges: &[Range], + insertion_ranges: &[Range], snippet: Snippet, window: &mut Window, cx: &mut Context, @@ -9849,14 +9868,13 @@ impl Editor { .flat_map(|tabstop_range| { let mut delta = 0_isize; insertion_ranges.iter().map(move |insertion_range| { - let insertion_start = insertion_range.start as isize + delta; - delta += - snippet.text.len() as isize - insertion_range.len() as isize; - - let start = ((insertion_start + tabstop_range.start) as usize) - .min(snapshot.len()); - let end = ((insertion_start + tabstop_range.end) as usize) - .min(snapshot.len()); + let insertion_start = insertion_range.start + delta; + delta += snippet.text.len() as isize + - (insertion_range.end - insertion_range.start) as isize; + + let start = + (insertion_start + tabstop_range.start).min(snapshot.len()); + let end = (insertion_start + tabstop_range.end).min(snapshot.len()); snapshot.anchor_before(start)..snapshot.anchor_after(end) }) }) @@ -10506,7 +10524,9 @@ impl Editor { cx, ); }); - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); this.change_selections(Default::default(), window, cx, |s| s.select(selections)); }); } @@ -10523,7 +10543,7 @@ impl Editor { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); let selections = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into_iter() .map(|s| s.range()); @@ -10531,7 +10551,9 @@ impl Editor { this.buffer.update(cx, |buffer, cx| { buffer.autoindent_ranges(selections, cx); }); - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); this.change_selections(Default::default(), window, cx, |s| s.select(selections)); }); } @@ -10569,7 +10591,7 @@ impl Editor { } else { // If there isn't a line after the range, delete the \n from the line before the // start of the row range - edit_start = edit_start.saturating_sub(1); + edit_start = edit_start.saturating_sub_usize(1); (buffer.len(), rows.start.previous_row()) }; @@ -10820,7 +10842,9 @@ impl Editor { boundaries.into_iter() { let open_offset = start_before.to_offset(&buffer) + start_prefix_len; - let close_offset = end_after.to_offset(&buffer).saturating_sub(end_suffix_len); + let close_offset = end_after + .to_offset(&buffer) + .saturating_sub_usize(end_suffix_len); new_selections.push(open_offset..open_offset); new_selections.push(close_offset..close_offset); } @@ -10850,7 +10874,10 @@ impl Editor { self.hide_mouse_cursor(HideMouseCursorOrigin::TypingAction, cx); let mut buffer_ids = HashSet::default(); let snapshot = self.buffer().read(cx).snapshot(cx); - for selection in self.selections.all::(&self.display_snapshot(cx)) { + for selection in self + .selections + .all::(&self.display_snapshot(cx)) + { buffer_ids.extend(snapshot.buffer_ids_for_range(selection.range())) } @@ -11265,7 +11292,7 @@ impl Editor { .read(cx) .base_text() .as_rope() - .slice(hunk.diff_base_byte_range.clone()); + .slice(hunk.diff_base_byte_range.start.0..hunk.diff_base_byte_range.end.0); let buffer_snapshot = buffer.snapshot(); let buffer_revert_changes = revert_changes.entry(buffer.remote_id()).or_default(); if let Err(i) = buffer_revert_changes.binary_search_by(|probe| { @@ -11705,7 +11732,7 @@ impl Editor { let mut new_selections = Vec::new(); let mut edits = Vec::new(); - let mut selection_adjustment = 0i32; + let mut selection_adjustment = 0isize; for selection in self.selections.all_adjusted(&self.display_snapshot(cx)) { let selection_is_empty = selection.is_empty(); @@ -11721,18 +11748,20 @@ impl Editor { }; let text = buffer.text_for_range(start..end).collect::(); - let old_length = text.len() as i32; + let old_length = text.len() as isize; let text = callback(&text); new_selections.push(Selection { - start: (start as i32 - selection_adjustment) as usize, - end: ((start + text.len()) as i32 - selection_adjustment) as usize, + start: MultiBufferOffset((start.0 as isize - selection_adjustment) as usize), + end: MultiBufferOffset( + ((start.0 + text.len()) as isize - selection_adjustment) as usize, + ), goal: SelectionGoal::None, id: selection.id, reversed: selection.reversed, }); - selection_adjustment += old_length - text.len() as i32; + selection_adjustment += old_length - text.len() as isize; edits.push((start..end, text)); } @@ -12149,7 +12178,7 @@ impl Editor { let text_layout_details = &self.text_layout_details(window); self.transact(window, cx, |this, window, cx| { let edits = this.change_selections(Default::default(), window, cx, |s| { - let mut edits: Vec<(Range, String)> = Default::default(); + let mut edits: Vec<(Range, String)> = Default::default(); s.move_with(|display_map, selection| { if !selection.is_empty() { return; @@ -12160,10 +12189,10 @@ impl Editor { if head.column() == display_map.line_len(head.row()) { transpose_offset = display_map .buffer_snapshot() - .clip_offset(transpose_offset.saturating_sub(1), Bias::Left); + .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left); } - if transpose_offset == 0 { + if transpose_offset == MultiBufferOffset(0) { return; } @@ -12178,11 +12207,11 @@ impl Editor { let transpose_start = display_map .buffer_snapshot() - .clip_offset(transpose_offset.saturating_sub(1), Bias::Left); + .clip_offset(transpose_offset.saturating_sub_usize(1), Bias::Left); if edits.last().is_none_or(|e| e.0.end <= transpose_start) { let transpose_end = display_map .buffer_snapshot() - .clip_offset(transpose_offset + 1, Bias::Right); + .clip_offset(transpose_offset + 1usize, Bias::Right); if let Some(ch) = display_map .buffer_snapshot() .chars_at(transpose_start) @@ -12197,7 +12226,9 @@ impl Editor { }); this.buffer .update(cx, |buffer, cx| buffer.edit(edits, None, cx)); - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); this.change_selections(Default::default(), window, cx, |s| { s.select(selections); }); @@ -12804,8 +12835,11 @@ impl Editor { self.transact(window, cx, |this, window, cx| { let had_active_edit_prediction = this.has_active_edit_prediction(); let display_map = this.display_snapshot(cx); - let old_selections = this.selections.all::(&display_map); - let cursor_offset = this.selections.last::(&display_map).head(); + let old_selections = this.selections.all::(&display_map); + let cursor_offset = this + .selections + .last::(&display_map) + .head(); if let Some(mut clipboard_selections) = clipboard_selections { let all_selections_were_entire_line = @@ -12890,7 +12924,9 @@ impl Editor { ); }); - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); this.change_selections(Default::default(), window, cx, |s| s.select(selections)); } else { let url = url::Url::parse(&clipboard_text).ok(); @@ -12959,7 +12995,9 @@ impl Editor { window: &mut Window, cx: &mut Context, ) { - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); if selections.is_empty() { log::warn!("There should always be at least one selection in Zed. This is a bug."); @@ -14212,7 +14250,7 @@ impl Editor { } self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx); self.change_selections(Default::default(), window, cx, |s| { - s.select_ranges(vec![0..0]); + s.select_ranges(vec![Anchor::min()..Anchor::min()]); }); } @@ -14301,7 +14339,9 @@ impl Editor { pub fn select_to_end(&mut self, _: &SelectToEnd, window: &mut Window, cx: &mut Context) { self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx); let buffer = self.buffer.read(cx).snapshot(cx); - let mut selection = self.selections.first::(&self.display_snapshot(cx)); + let mut selection = self + .selections + .first::(&self.display_snapshot(cx)); selection.set_head(buffer.len(), SelectionGoal::None); self.change_selections(Default::default(), window, cx, |s| { s.select(vec![selection]); @@ -14310,9 +14350,8 @@ impl Editor { pub fn select_all(&mut self, _: &SelectAll, window: &mut Window, cx: &mut Context) { self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx); - let end = self.buffer.read(cx).read(cx).len(); self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges(vec![0..end]); + s.select_ranges(vec![Anchor::min()..Anchor::max()]); }); } @@ -14572,7 +14611,7 @@ impl Editor { fn select_match_ranges( &mut self, - range: Range, + range: Range, reversed: bool, replace_newest: bool, auto_scroll: Option, @@ -14611,7 +14650,7 @@ impl Editor { cx: &mut Context, ) -> Result<()> { let buffer = display_map.buffer_snapshot(); - let mut selections = self.selections.all::(&display_map); + let mut selections = self.selections.all::(&display_map); if let Some(mut select_next_state) = self.select_next_state.take() { let query = &select_next_state.query; if !select_next_state.done { @@ -14621,14 +14660,15 @@ impl Editor { let bytes_after_last_selection = buffer.bytes_in_range(last_selection.end..buffer.len()); - let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start); + let bytes_before_first_selection = + buffer.bytes_in_range(MultiBufferOffset(0)..first_selection.start); let query_matches = query .stream_find_iter(bytes_after_last_selection) .map(|result| (last_selection.end, result)) .chain( query .stream_find_iter(bytes_before_first_selection) - .map(|result| (0, result)), + .map(|result| (MultiBufferOffset(0), result)), ); for (start_offset, query_match) in query_matches { @@ -14686,7 +14726,7 @@ impl Editor { } if let Some(next_selection) = selections_iter.peek() { - if next_selection.range().len() == selection.range().len() { + if next_selection.len() == selection.len() { let next_selected_text = buffer .text_for_range(next_selection.range()) .collect::(); @@ -14774,18 +14814,21 @@ impl Editor { let mut new_selections = Vec::new(); - let reversed = self.selections.oldest::(&display_map).reversed; + let reversed = self + .selections + .oldest::(&display_map) + .reversed; let buffer = display_map.buffer_snapshot(); let query_matches = select_next_state .query - .stream_find_iter(buffer.bytes_in_range(0..buffer.len())); + .stream_find_iter(buffer.bytes_in_range(MultiBufferOffset(0)..buffer.len())); for query_match in query_matches.into_iter() { let query_match = query_match.context("query match for select all action")?; // can only fail due to I/O let offset_range = if reversed { - query_match.end()..query_match.start() + MultiBufferOffset(query_match.end())..MultiBufferOffset(query_match.start()) } else { - query_match.start()..query_match.end() + MultiBufferOffset(query_match.start())..MultiBufferOffset(query_match.end()) }; if !select_next_state.wordwise @@ -14838,7 +14881,7 @@ impl Editor { self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = display_map.buffer_snapshot(); - let mut selections = self.selections.all::(&display_map); + let mut selections = self.selections.all::(&display_map); if let Some(mut select_prev_state) = self.select_prev_state.take() { let query = &select_prev_state.query; if !select_prev_state.done { @@ -14847,7 +14890,7 @@ impl Editor { let mut next_selected_range = None; // When we're iterating matches backwards, the oldest match will actually be the furthest one in the buffer. let bytes_before_last_selection = - buffer.reversed_bytes_in_range(0..last_selection.start); + buffer.reversed_bytes_in_range(MultiBufferOffset(0)..last_selection.start); let bytes_after_first_selection = buffer.reversed_bytes_in_range(first_selection.end..buffer.len()); let query_matches = query @@ -14905,7 +14948,7 @@ impl Editor { } if let Some(next_selection) = selections_iter.peek() { - if next_selection.range().len() == selection.range().len() { + if next_selection.len() == selection.len() { let next_selected_text = buffer .text_for_range(next_selection.range()) .collect::(); @@ -15351,13 +15394,13 @@ impl Editor { let buffer = self.buffer.read(cx).snapshot(cx); let old_selections = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into_boxed_slice(); fn update_selection( - selection: &Selection, + selection: &Selection, buffer_snap: &MultiBufferSnapshot, - ) -> Option> { + ) -> Option> { let cursor = selection.head(); let (_buffer_id, symbols) = buffer_snap.symbols_containing(cursor, None)?; for symbol in symbols.iter().rev() { @@ -15409,7 +15452,7 @@ impl Editor { }; let old_selections: Box<[_]> = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into(); if old_selections.is_empty() { return; @@ -15568,7 +15611,7 @@ impl Editor { let buffer = self.buffer.read(cx).snapshot(cx); let selections = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into_iter() // subtracting the offset requires sorting .sorted_by_key(|i| i.start); @@ -15620,7 +15663,7 @@ impl Editor { let mut selections = vec![]; for (id, parent, text) in full_edits { let start = parent.start - offset; - offset += parent.len() - text.len(); + offset += (parent.end - parent.start) - text.len(); selections.push(Selection { id, start, @@ -15642,7 +15685,7 @@ impl Editor { ) { let old_selections: Box<[_]> = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into(); if old_selections.is_empty() { return; @@ -15658,8 +15701,18 @@ impl Editor { .map(|selection| { let old_range = selection.start..selection.end; - if let Some(node) = buffer.syntax_next_sibling(old_range) { - let new_range = node.byte_range(); + let old_range = + old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer); + let excerpt = buffer.excerpt_containing(old_range.clone()); + + if let Some(mut excerpt) = excerpt + && let Some(node) = excerpt + .buffer() + .syntax_next_sibling(excerpt.map_range_to_buffer(old_range)) + { + let new_range = excerpt.map_range_from_buffer( + BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end), + ); selected_sibling = true; Selection { id: selection.id, @@ -15694,7 +15747,7 @@ impl Editor { ) { let old_selections: Box<[_]> = self .selections - .all::(&self.display_snapshot(cx)) + .all::(&self.display_snapshot(cx)) .into(); if old_selections.is_empty() { return; @@ -15709,9 +15762,18 @@ impl Editor { .iter() .map(|selection| { let old_range = selection.start..selection.end; + let old_range = + old_range.start.to_offset(&buffer)..old_range.end.to_offset(&buffer); + let excerpt = buffer.excerpt_containing(old_range.clone()); - if let Some(node) = buffer.syntax_prev_sibling(old_range) { - let new_range = node.byte_range(); + if let Some(mut excerpt) = excerpt + && let Some(node) = excerpt + .buffer() + .syntax_prev_sibling(excerpt.map_range_to_buffer(old_range)) + { + let new_range = excerpt.map_range_from_buffer( + BufferOffset(node.byte_range().start)..BufferOffset(node.byte_range().end), + ); selected_sibling = true; Selection { id: selection.id, @@ -15860,7 +15922,7 @@ impl Editor { fn fetch_runnable_ranges( snapshot: &DisplaySnapshot, range: Range, - ) -> Vec { + ) -> Vec<(Range, language::RunnableRange)> { snapshot.buffer_snapshot().runnable_ranges(range).collect() } @@ -15868,12 +15930,12 @@ impl Editor { project: Entity, snapshot: DisplaySnapshot, prefer_lsp: bool, - runnable_ranges: Vec, + runnable_ranges: Vec<(Range, language::RunnableRange)>, cx: AsyncWindowContext, ) -> Task> { cx.spawn(async move |cx| { let mut runnable_rows = Vec::with_capacity(runnable_ranges.len()); - for mut runnable in runnable_ranges { + for (run_range, mut runnable) in runnable_ranges { let Some(tasks) = cx .update(|_, cx| Self::templates_with_tags(&project, &mut runnable.runnable, cx)) .ok() @@ -15891,10 +15953,7 @@ impl Editor { continue; } - let point = runnable - .run_range - .start - .to_point(&snapshot.buffer_snapshot()); + let point = run_range.start.to_point(&snapshot.buffer_snapshot()); let Some(row) = snapshot .buffer_snapshot() .buffer_line_for_row(MultiBufferRow(point.row)) @@ -15909,9 +15968,7 @@ impl Editor { (runnable.buffer_id, row), RunnableTasks { templates: tasks, - offset: snapshot - .buffer_snapshot() - .anchor_before(runnable.run_range.start), + offset: snapshot.buffer_snapshot().anchor_before(run_range.start), context_range, column: point.column, extra_variables: runnable.extra_captures, @@ -15998,7 +16055,7 @@ impl Editor { let mut best_destination = None; for (open, close) in enclosing_bracket_ranges { let close = close.to_inclusive(); - let length = close.end() - open.start; + let length = *close.end() - open.start; let inside = selection.start >= open.end && selection.end <= *close.start(); let in_bracket_range = open.to_inclusive().contains(&selection.head()) || close.contains(&selection.head()); @@ -16257,7 +16314,9 @@ impl Editor { cx: &mut Context, ) { let buffer = self.buffer.read(cx).snapshot(cx); - let selection = self.selections.newest::(&self.display_snapshot(cx)); + let selection = self + .selections + .newest::(&self.display_snapshot(cx)); let mut active_group_id = None; if let ActiveDiagnostic::Group(active_group) = &self.active_diagnostics @@ -16268,8 +16327,8 @@ impl Editor { fn filtered<'a>( severity: GoToDiagnosticSeverityFilter, - diagnostics: impl Iterator>, - ) -> impl Iterator> { + diagnostics: impl Iterator>, + ) -> impl Iterator> { diagnostics .filter(move |entry| severity.matches(entry.diagnostic.severity)) .filter(|entry| entry.range.start != entry.range.end) @@ -16279,7 +16338,7 @@ impl Editor { let before = filtered( severity, buffer - .diagnostics_in_range(0..selection.start) + .diagnostics_in_range(MultiBufferOffset(0)..selection.start) .filter(|entry| entry.range.start <= selection.start), ); let after = filtered( @@ -16289,7 +16348,7 @@ impl Editor { .filter(|entry| entry.range.start >= selection.start), ); - let mut found: Option> = None; + let mut found: Option> = None; if direction == Direction::Prev { 'outer: for prev_diagnostics in [before.collect::>(), after.collect::>()] { @@ -16681,7 +16740,7 @@ impl Editor { }; let head = self .selections - .newest::(&self.display_snapshot(cx)) + .newest::(&self.display_snapshot(cx)) .head(); let buffer = self.buffer.read(cx); let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else { @@ -17146,7 +17205,9 @@ impl Editor { window: &mut Window, cx: &mut Context, ) -> Option>> { - let selection = self.selections.newest::(&self.display_snapshot(cx)); + let selection = self + .selections + .newest::(&self.display_snapshot(cx)); let multi_buffer = self.buffer.read(cx); let head = selection.head(); @@ -17420,7 +17481,8 @@ impl Editor { this.take_rename(false, window, cx); let buffer = this.buffer.read(cx).read(cx); let cursor_offset = selection.head().to_offset(&buffer); - let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range); + let rename_start = + cursor_offset.saturating_sub_usize(cursor_offset_in_rename_range); let rename_end = rename_start + rename_buffer_range.len(); let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end); let mut old_highlight_id = None; @@ -17442,8 +17504,16 @@ impl Editor { let rename_editor = cx.new(|cx| { let mut editor = Editor::single_line(window, cx); editor.buffer.update(cx, |buffer, cx| { - buffer.edit([(0..0, old_name.clone())], None, cx) + buffer.edit( + [(MultiBufferOffset(0)..MultiBufferOffset(0), old_name.clone())], + None, + cx, + ) }); + let cursor_offset_in_rename_range = + MultiBufferOffset(cursor_offset_in_rename_range); + let cursor_offset_in_rename_range_end = + MultiBufferOffset(cursor_offset_in_rename_range_end); let rename_selection_range = match cursor_offset_in_rename_range .cmp(&cursor_offset_in_rename_range_end) { @@ -17458,7 +17528,7 @@ impl Editor { cursor_offset_in_rename_range_end..cursor_offset_in_rename_range } }; - if rename_selection_range.end > old_name.len() { + if rename_selection_range.end.0 > old_name.len() { editor.select_all(&SelectAll, window, cx); } else { editor.change_selections(Default::default(), window, cx, |s| { @@ -17623,7 +17693,7 @@ impl Editor { let cursor_in_rename_editor = rename.editor.update(cx, |editor, cx| { editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .head() }); @@ -17922,7 +17992,7 @@ impl Editor { let primary_range_start = active_diagnostics.active_range.start.to_offset(&buffer); let primary_range_end = active_diagnostics.active_range.end.to_offset(&buffer); let is_valid = buffer - .diagnostics_in_range::(primary_range_start..primary_range_end) + .diagnostics_in_range::(primary_range_start..primary_range_end) .any(|entry| { entry.diagnostic.is_primary && !entry.range.is_empty() @@ -17954,7 +18024,7 @@ impl Editor { fn activate_diagnostics( &mut self, buffer_id: BufferId, - diagnostic: DiagnosticEntryRef<'_, usize>, + diagnostic: DiagnosticEntryRef<'_, MultiBufferOffset>, window: &mut Window, cx: &mut Context, ) { @@ -18146,7 +18216,9 @@ impl Editor { let new_inline_diagnostics = cx .background_spawn(async move { let mut inline_diagnostics = Vec::<(Anchor, InlineDiagnostic)>::new(); - for diagnostic_entry in snapshot.diagnostics_in_range(0..snapshot.len()) { + for diagnostic_entry in + snapshot.diagnostics_in_range(MultiBufferOffset(0)..snapshot.len()) + { let message = diagnostic_entry .diagnostic .message @@ -18525,7 +18597,7 @@ impl Editor { if self.buffer.read(cx).is_singleton() { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let has_folds = display_map - .folds_in_range(0..display_map.buffer_snapshot().len()) + .folds_in_range(MultiBufferOffset(0)..display_map.buffer_snapshot().len()) .next() .is_some(); @@ -18728,7 +18800,10 @@ impl Editor { let snapshot = self.buffer.read(cx).snapshot(cx); let ranges = snapshot - .text_object_ranges(0..snapshot.len(), TreeSitterOptions::default()) + .text_object_ranges( + MultiBufferOffset(0)..snapshot.len(), + TreeSitterOptions::default(), + ) .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range)) .collect::>(); @@ -18885,7 +18960,12 @@ impl Editor { ) { if self.buffer.read(cx).is_singleton() { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); - self.unfold_ranges(&[0..display_map.buffer_snapshot().len()], true, true, cx); + self.unfold_ranges( + &[MultiBufferOffset(0)..display_map.buffer_snapshot().len()], + true, + true, + cx, + ); } else { self.toggle_fold_multiple_buffers = cx.spawn(async move |editor, cx| { editor @@ -19310,7 +19390,8 @@ impl Editor { &hunks .map(|hunk| buffer_diff::DiffHunk { buffer_range: hunk.buffer_range, - diff_base_byte_range: hunk.diff_base_byte_range, + diff_base_byte_range: hunk.diff_base_byte_range.start.0 + ..hunk.diff_base_byte_range.end.0, secondary_status: hunk.secondary_status, range: Point::zero()..Point::zero(), // unused }) @@ -20882,7 +20963,7 @@ impl Editor { ) -> Vec<(Range, Hsla)> { let snapshot = self.snapshot(window, cx); let buffer = &snapshot.buffer_snapshot(); - let start = buffer.anchor_before(0); + let start = buffer.anchor_before(MultiBufferOffset(0)); let end = buffer.anchor_after(buffer.len()); self.sorted_background_highlights_in_range(start..end, &snapshot, cx.theme()) } @@ -21562,7 +21643,7 @@ impl Editor { new_selections_by_buffer.insert( buffer, ( - vec![jump_to_offset..jump_to_offset], + vec![BufferOffset(jump_to_offset)..BufferOffset(jump_to_offset)], Some(*line_offset_from_top), ), ); @@ -21581,11 +21662,13 @@ impl Editor { .entry(buffer) .or_insert((Vec::new(), Some(*line_offset_from_top))) .0 - .push(buffer_offset..buffer_offset) + .push(BufferOffset(buffer_offset)..BufferOffset(buffer_offset)) } } None => { - let selections = self.selections.all::(&self.display_snapshot(cx)); + let selections = self + .selections + .all::(&self.display_snapshot(cx)); let multi_buffer = self.buffer.read(cx); for selection in selections { for (snapshot, range, _, anchor) in multi_buffer @@ -21601,7 +21684,7 @@ impl Editor { &anchor.text_anchor, &buffer_handle.read(cx).snapshot(), ); - let range = offset..offset; + let range = BufferOffset(offset)..BufferOffset(offset); new_selections_by_buffer .entry(buffer_handle) .or_insert((Vec::new(), None)) @@ -21689,7 +21772,10 @@ impl Editor { window, cx, |s| { - s.select_ranges(ranges); + s.select_ranges(ranges.into_iter().map(|range| { + // we checked that the editor is a singleton editor so the offsets are valid + MultiBufferOffset(range.start.0)..MultiBufferOffset(range.end.0) + })); }, ); editor.nav_history = nav_history; @@ -21705,7 +21791,7 @@ impl Editor { file.is_none_or(|file| project::File::from_dyn(Some(file)).is_some()) } - fn marked_text_ranges(&self, cx: &App) -> Option>> { + fn marked_text_ranges(&self, cx: &App) -> Option>> { let snapshot = self.buffer.read(cx).read(cx); let (_, ranges) = self.text_highlights::(cx)?; Some( @@ -21720,25 +21806,25 @@ impl Editor { fn selection_replacement_ranges( &self, - range: Range, + range: Range, cx: &mut App, - ) -> Vec> { + ) -> Vec> { let selections = self .selections - .all::(&self.display_snapshot(cx)); + .all::(&self.display_snapshot(cx)); let newest_selection = selections .iter() .max_by_key(|selection| selection.id) .unwrap(); - let start_delta = range.start.0 as isize - newest_selection.start.0 as isize; - let end_delta = range.end.0 as isize - newest_selection.end.0 as isize; + let start_delta = range.start.0.0 as isize - newest_selection.start.0.0 as isize; + let end_delta = range.end.0.0 as isize - newest_selection.end.0.0 as isize; let snapshot = self.buffer.read(cx).read(cx); selections .into_iter() .map(|mut selection| { - selection.start.0 = - (selection.start.0 as isize).saturating_add(start_delta) as usize; - selection.end.0 = (selection.end.0 as isize).saturating_add(end_delta) as usize; + selection.start.0.0 = + (selection.start.0.0 as isize).saturating_add(start_delta) as usize; + selection.end.0.0 = (selection.end.0.0 as isize).saturating_add(end_delta) as usize; snapshot.clip_offset_utf16(selection.start, Bias::Left) ..snapshot.clip_offset_utf16(selection.end, Bias::Right) }) @@ -21831,12 +21917,16 @@ impl Editor { None } else { Some( - snapshot.offset_utf16_to_offset(OffsetUtf16(selection.range.start)) - ..snapshot.offset_utf16_to_offset(OffsetUtf16(selection.range.end)), + snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16( + selection.range.start, + ))) + ..snapshot.offset_utf16_to_offset(MultiBufferOffsetUtf16(OffsetUtf16( + selection.range.end, + ))), ) } }) - .unwrap_or_else(|| 0..snapshot.len()); + .unwrap_or_else(|| MultiBufferOffset(0)..snapshot.len()); let chunks = snapshot.chunks(range, true); let mut lines = Vec::new(); @@ -21914,21 +22004,23 @@ impl Editor { if let Some(relative_utf16_range) = relative_utf16_range { let selections = self .selections - .all::(&self.display_snapshot(cx)); + .all::(&self.display_snapshot(cx)); self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { let new_ranges = selections.into_iter().map(|range| { - let start = OffsetUtf16( + let start = MultiBufferOffsetUtf16(OffsetUtf16( range .head() .0 + .0 .saturating_add_signed(relative_utf16_range.start), - ); - let end = OffsetUtf16( + )); + let end = MultiBufferOffsetUtf16(OffsetUtf16( range .head() .0 + .0 .saturating_add_signed(relative_utf16_range.end), - ); + )); start..end }); s.select_ranges(new_ranges); @@ -22039,7 +22131,9 @@ impl Editor { } let transaction = self.transact(window, cx, |this, window, cx| { - let selections = this.selections.all::(&this.display_snapshot(cx)); + let selections = this + .selections + .all::(&this.display_snapshot(cx)); let edits = selections .iter() .map(|selection| (selection.end..selection.end, pending.clone())); @@ -22058,7 +22152,7 @@ impl Editor { let snapshot = self.snapshot(window, cx); let ranges = self .selections - .all::(&snapshot.display_snapshot) + .all::(&snapshot.display_snapshot) .into_iter() .map(|selection| { snapshot.buffer_snapshot().anchor_after(selection.end) @@ -22310,8 +22404,8 @@ impl Editor { folds .into_iter() .map(|(start, end)| { - snapshot.clip_offset(start, Bias::Left) - ..snapshot.clip_offset(end, Bias::Right) + snapshot.clip_offset(MultiBufferOffset(start), Bias::Left) + ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right) }) .collect(), false, @@ -22328,8 +22422,8 @@ impl Editor { self.selection_history.mode = SelectionHistoryMode::Skipping; self.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges(selections.into_iter().map(|(start, end)| { - snapshot.clip_offset(start, Bias::Left) - ..snapshot.clip_offset(end, Bias::Right) + snapshot.clip_offset(MultiBufferOffset(start), Bias::Left) + ..snapshot.clip_offset(MultiBufferOffset(end), Bias::Right) })); }); self.selection_history.mode = SelectionHistoryMode::Normal; @@ -22388,10 +22482,10 @@ impl Editor { fn edit_for_markdown_paste<'a>( buffer: &MultiBufferSnapshot, - range: Range, + range: Range, to_insert: &'a str, url: Option, -) -> (Range, Cow<'a, str>) { +) -> (Range, Cow<'a, str>) { if url.is_none() { return (range, Cow::Borrowed(to_insert)); }; @@ -22541,22 +22635,23 @@ fn process_completion_for_edit( range_to_replace.end = *cursor_position; } + let replace_range = range_to_replace.to_offset(buffer); CompletionEdit { new_text, - replace_range: range_to_replace.to_offset(buffer), + replace_range: BufferOffset(replace_range.start)..BufferOffset(replace_range.end), snippet, } } struct CompletionEdit { new_text: String, - replace_range: Range, + replace_range: Range, snippet: Option, } fn insert_extra_newline_brackets( buffer: &MultiBufferSnapshot, - range: Range, + range: Range, language: &language::LanguageScope, ) -> bool { let leading_whitespace_len = buffer @@ -22578,11 +22673,17 @@ fn insert_extra_newline_brackets( enabled && pair.newline && buffer.contains_str_at(range.end, pair_end) - && buffer.contains_str_at(range.start.saturating_sub(pair_start.len()), pair_start) + && buffer.contains_str_at( + range.start.saturating_sub_usize(pair_start.len()), + pair_start, + ) }) } -fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range) -> bool { +fn insert_extra_newline_tree_sitter( + buffer: &MultiBufferSnapshot, + range: Range, +) -> bool { let (buffer, range) = match buffer.range_to_buffer_ranges(range).as_slice() { [(buffer, range, _)] => (*buffer, range.clone()), _ => return false, @@ -22591,9 +22692,9 @@ fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range = None; for pair in buffer - .all_bracket_ranges(range.clone()) + .all_bracket_ranges(range.start.0..range.end.0) .filter(move |pair| { - pair.open_range.start <= range.start && pair.close_range.end >= range.end + pair.open_range.start <= range.start.0 && pair.close_range.end >= range.end.0 }) { let len = pair.close_range.end - pair.open_range.start; @@ -22615,8 +22716,8 @@ fn insert_extra_newline_tree_sitter(buffer: &MultiBufferSnapshot, range: Range, ) -> Option { let snapshot = self.buffer.read(cx).read(cx); - let start = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.start), Bias::Left); - let end = snapshot.clip_offset_utf16(OffsetUtf16(range_utf16.end), Bias::Right); - if (start.0..end.0) != range_utf16 { - adjusted_range.replace(start.0..end.0); + let start = snapshot.clip_offset_utf16( + MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)), + Bias::Left, + ); + let end = snapshot.clip_offset_utf16( + MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)), + Bias::Right, + ); + if (start.0.0..end.0.0) != range_utf16 { + adjusted_range.replace(start.0.0..end.0.0); } Some(snapshot.text_for_range(start..end).collect()) } @@ -24242,11 +24350,11 @@ impl EntityInputHandler for Editor { let selection = self .selections - .newest::(&self.display_snapshot(cx)); + .newest::(&self.display_snapshot(cx)); let range = selection.range(); Some(UTF16Selection { - range: range.start.0..range.end.0, + range: range.start.0.0..range.end.0.0, reversed: selection.reversed, }) } @@ -24254,7 +24362,7 @@ impl EntityInputHandler for Editor { fn marked_text_range(&self, _: &mut Window, cx: &mut Context) -> Option> { let snapshot = self.buffer.read(cx).read(cx); let range = self.text_highlights::(cx)?.1.first()?; - Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0) + Some(range.start.to_offset_utf16(&snapshot).0.0..range.end.to_offset_utf16(&snapshot).0.0) } fn unmark_text(&mut self, _: &mut Window, cx: &mut Context) { @@ -24276,7 +24384,8 @@ impl EntityInputHandler for Editor { self.transact(window, cx, |this, window, cx| { let new_selected_ranges = if let Some(range_utf16) = range_utf16 { - let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); + let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)) + ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)); Some(this.selection_replacement_ranges(range_utf16, cx)) } else { this.marked_text_ranges(cx) @@ -24285,14 +24394,14 @@ impl EntityInputHandler for Editor { let range_to_replace = new_selected_ranges.as_ref().and_then(|ranges_to_replace| { let newest_selection_id = this.selections.newest_anchor().id; this.selections - .all::(&this.display_snapshot(cx)) + .all::(&this.display_snapshot(cx)) .iter() .zip(ranges_to_replace.iter()) .find_map(|(selection, range)| { if selection.id == newest_selection_id { Some( - (range.start.0 as isize - selection.head().0 as isize) - ..(range.end.0 as isize - selection.head().0 as isize), + (range.start.0.0 as isize - selection.head().0.0 as isize) + ..(range.end.0.0 as isize - selection.head().0.0 as isize), ) } else { None @@ -24341,8 +24450,8 @@ impl EntityInputHandler for Editor { let snapshot = this.buffer.read(cx).read(cx); if let Some(relative_range_utf16) = range_utf16.as_ref() { for marked_range in &mut marked_ranges { - marked_range.end.0 = marked_range.start.0 + relative_range_utf16.end; - marked_range.start.0 += relative_range_utf16.start; + marked_range.end = marked_range.start + relative_range_utf16.end; + marked_range.start += relative_range_utf16.start; marked_range.start = snapshot.clip_offset_utf16(marked_range.start, Bias::Left); marked_range.end = @@ -24351,7 +24460,8 @@ impl EntityInputHandler for Editor { } Some(marked_ranges) } else if let Some(range_utf16) = range_utf16 { - let range_utf16 = OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end); + let range_utf16 = MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)) + ..MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.end)); Some(this.selection_replacement_ranges(range_utf16, cx)) } else { None @@ -24360,14 +24470,14 @@ impl EntityInputHandler for Editor { let range_to_replace = ranges_to_replace.as_ref().and_then(|ranges_to_replace| { let newest_selection_id = this.selections.newest_anchor().id; this.selections - .all::(&this.display_snapshot(cx)) + .all::(&this.display_snapshot(cx)) .iter() .zip(ranges_to_replace.iter()) .find_map(|(selection, range)| { if selection.id == newest_selection_id { Some( - (range.start.0 as isize - selection.head().0 as isize) - ..(range.end.0 as isize - selection.head().0 as isize), + (range.start.0.0 as isize - selection.head().0.0 as isize) + ..(range.end.0.0 as isize - selection.head().0.0 as isize), ) } else { None @@ -24429,8 +24539,12 @@ impl EntityInputHandler for Editor { .into_iter() .map(|marked_range| { let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0; - let new_start = OffsetUtf16(new_selected_range.start + insertion_start); - let new_end = OffsetUtf16(new_selected_range.end + insertion_start); + let new_start = MultiBufferOffsetUtf16(OffsetUtf16( + insertion_start.0 + new_selected_range.start, + )); + let new_end = MultiBufferOffsetUtf16(OffsetUtf16( + insertion_start.0 + new_selected_range.end, + )); snapshot.clip_offset_utf16(new_start, Bias::Left) ..snapshot.clip_offset_utf16(new_end, Bias::Right) }) @@ -24473,7 +24587,8 @@ impl EntityInputHandler for Editor { let scroll_position = snapshot.scroll_position(); let scroll_left = scroll_position.x * ScrollOffset::from(em_advance); - let start = OffsetUtf16(range_utf16.start).to_display_point(&snapshot); + let start = + MultiBufferOffsetUtf16(OffsetUtf16(range_utf16.start)).to_display_point(&snapshot); let x = Pixels::from( ScrollOffset::from( snapshot.x_for_display_point(start, &text_layout_details) @@ -24503,7 +24618,7 @@ impl EntityInputHandler for Editor { .snapshot .display_point_to_anchor(display_point, Bias::Left); let utf16_offset = anchor.to_offset_utf16(&position_map.snapshot.buffer_snapshot()); - Some(utf16_offset.0) + Some(utf16_offset.0.0) } fn accepts_text_input(&self, _window: &mut Window, _cx: &mut Context) -> bool { diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 20ad9ca076ed4ee68679bd351386ddc49f18491a..d46293157ddb1bd6500c1b423279401c8195ea1f 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -35,7 +35,7 @@ use language_settings::Formatter; use languages::markdown_lang; use languages::rust_lang; use lsp::CompletionParams; -use multi_buffer::{IndentGuide, PathKey}; +use multi_buffer::{IndentGuide, MultiBufferOffset, MultiBufferOffsetUtf16, PathKey}; use parking_lot::Mutex; use pretty_assertions::{assert_eq, assert_ne}; use project::{ @@ -197,7 +197,7 @@ fn test_edit_events(cx: &mut TestAppContext) { // No event is emitted when the mutation is a no-op. _ = editor2.update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([0..0]) + s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)]) }); editor.backspace(&Backspace, window, cx); @@ -222,7 +222,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { _ = editor.update(cx, |editor, window, cx| { editor.start_transaction_at(now, window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([2..4]) + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(4)]) }); editor.insert("cd", window, cx); @@ -230,38 +230,46 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "12cd56"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![4..4] + vec![MultiBufferOffset(4)..MultiBufferOffset(4)] ); editor.start_transaction_at(now, window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([4..5]) + s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(5)]) }); editor.insert("e", window, cx); editor.end_transaction_at(now, cx); assert_eq!(editor.text(cx), "12cde6"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![5..5] + vec![MultiBufferOffset(5)..MultiBufferOffset(5)] ); now += group_interval + Duration::from_millis(1); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([2..2]) + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(2)]) }); // Simulate an edit in another editor buffer.update(cx, |buffer, cx| { buffer.start_transaction_at(now, cx); - buffer.edit([(0..1, "a")], None, cx); - buffer.edit([(1..1, "b")], None, cx); + buffer.edit( + [(MultiBufferOffset(0)..MultiBufferOffset(1), "a")], + None, + cx, + ); + buffer.edit( + [(MultiBufferOffset(1)..MultiBufferOffset(1), "b")], + None, + cx, + ); buffer.end_transaction_at(now, cx); }); assert_eq!(editor.text(cx), "ab2cde6"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![3..3] + vec![MultiBufferOffset(3)..MultiBufferOffset(3)] ); // Last transaction happened past the group interval in a different editor. @@ -270,7 +278,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "12cde6"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![2..2] + vec![MultiBufferOffset(2)..MultiBufferOffset(2)] ); // First two transactions happened within the group interval in this editor. @@ -280,7 +288,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "123456"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![0..0] + vec![MultiBufferOffset(0)..MultiBufferOffset(0)] ); // Redo the first two transactions together. @@ -288,7 +296,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "12cde6"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![5..5] + vec![MultiBufferOffset(5)..MultiBufferOffset(5)] ); // Redo the last transaction on its own. @@ -296,7 +304,7 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "ab2cde6"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - vec![6..6] + vec![MultiBufferOffset(6)..MultiBufferOffset(6)] ); // Test empty transactions. @@ -329,7 +337,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "äbcde"); assert_eq!( editor.marked_text_ranges(cx), - Some(vec![OffsetUtf16(0)..OffsetUtf16(1)]) + Some(vec![ + MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(1)) + ]) ); // Finalize IME composition. @@ -349,7 +359,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { editor.replace_and_mark_text_in_range(Some(0..1), "à", None, window, cx); assert_eq!( editor.marked_text_ranges(cx), - Some(vec![OffsetUtf16(0)..OffsetUtf16(1)]) + Some(vec![ + MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(1)) + ]) ); // Undoing during an IME composition cancels it. @@ -362,7 +374,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { assert_eq!(editor.text(cx), "ābcdè"); assert_eq!( editor.marked_text_ranges(cx), - Some(vec![OffsetUtf16(4)..OffsetUtf16(5)]) + Some(vec![ + MultiBufferOffsetUtf16(OffsetUtf16(4))..MultiBufferOffsetUtf16(OffsetUtf16(5)) + ]) ); // Finalize IME composition with an invalid replacement range, ensuring it gets clipped. @@ -373,9 +387,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { // Start a new IME composition with multiple cursors. editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges([ - OffsetUtf16(1)..OffsetUtf16(1), - OffsetUtf16(3)..OffsetUtf16(3), - OffsetUtf16(5)..OffsetUtf16(5), + MultiBufferOffsetUtf16(OffsetUtf16(1))..MultiBufferOffsetUtf16(OffsetUtf16(1)), + MultiBufferOffsetUtf16(OffsetUtf16(3))..MultiBufferOffsetUtf16(OffsetUtf16(3)), + MultiBufferOffsetUtf16(OffsetUtf16(5))..MultiBufferOffsetUtf16(OffsetUtf16(5)), ]) }); editor.replace_and_mark_text_in_range(Some(4..5), "XYZ", None, window, cx); @@ -383,9 +397,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { assert_eq!( editor.marked_text_ranges(cx), Some(vec![ - OffsetUtf16(0)..OffsetUtf16(3), - OffsetUtf16(4)..OffsetUtf16(7), - OffsetUtf16(8)..OffsetUtf16(11) + MultiBufferOffsetUtf16(OffsetUtf16(0))..MultiBufferOffsetUtf16(OffsetUtf16(3)), + MultiBufferOffsetUtf16(OffsetUtf16(4))..MultiBufferOffsetUtf16(OffsetUtf16(7)), + MultiBufferOffsetUtf16(OffsetUtf16(8))..MultiBufferOffsetUtf16(OffsetUtf16(11)) ]) ); @@ -395,9 +409,9 @@ fn test_ime_composition(cx: &mut TestAppContext) { assert_eq!( editor.marked_text_ranges(cx), Some(vec![ - OffsetUtf16(1)..OffsetUtf16(2), - OffsetUtf16(5)..OffsetUtf16(6), - OffsetUtf16(9)..OffsetUtf16(10) + MultiBufferOffsetUtf16(OffsetUtf16(1))..MultiBufferOffsetUtf16(OffsetUtf16(2)), + MultiBufferOffsetUtf16(OffsetUtf16(5))..MultiBufferOffsetUtf16(OffsetUtf16(6)), + MultiBufferOffsetUtf16(OffsetUtf16(9))..MultiBufferOffsetUtf16(OffsetUtf16(10)) ]) ); @@ -757,7 +771,11 @@ fn test_clone(cx: &mut TestAppContext) { _ = editor.update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges(selection_ranges.clone()) + s.select_ranges( + selection_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }); editor.fold_creases( vec![ @@ -794,9 +812,11 @@ fn test_clone(cx: &mut TestAppContext) { ); assert_eq!( cloned_snapshot - .folds_in_range(0..text.len()) + .folds_in_range(MultiBufferOffset(0)..MultiBufferOffset(text.len())) + .collect::>(), + snapshot + .folds_in_range(MultiBufferOffset(0)..MultiBufferOffset(text.len())) .collect::>(), - snapshot.folds_in_range(0..text.len()).collect::>(), ); assert_set_eq!( cloned_editor @@ -1418,7 +1438,11 @@ fn test_fold_at_level(cx: &mut TestAppContext) { ); editor.change_selections(SelectionEffects::default(), window, cx, |s| { - s.select_ranges(positions) + s.select_ranges( + positions + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }); editor.fold_at_level(&FoldAtLevel(2), window, cx); @@ -3700,7 +3724,11 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) { let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx); let mut editor = build_editor(buffer, window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([3..4, 11..12, 19..20]) + s.select_ranges([ + MultiBufferOffset(3)..MultiBufferOffset(4), + MultiBufferOffset(11)..MultiBufferOffset(12), + MultiBufferOffset(19)..MultiBufferOffset(20), + ]) }); editor }); @@ -3708,12 +3736,24 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) { _ = editor.update(cx, |editor, window, cx| { // Edit the buffer directly, deleting ranges surrounding the editor's selections editor.buffer.update(cx, |buffer, cx| { - buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], None, cx); + buffer.edit( + [ + (MultiBufferOffset(2)..MultiBufferOffset(5), ""), + (MultiBufferOffset(10)..MultiBufferOffset(13), ""), + (MultiBufferOffset(18)..MultiBufferOffset(21), ""), + ], + None, + cx, + ); assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent()); }); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - &[2..2, 7..7, 12..12], + &[ + MultiBufferOffset(2)..MultiBufferOffset(2), + MultiBufferOffset(7)..MultiBufferOffset(7), + MultiBufferOffset(12)..MultiBufferOffset(12) + ], ); editor.insert("Z", window, cx); @@ -3722,7 +3762,11 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) { // The selections are moved after the inserted characters assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - &[3..3, 9..9, 15..15], + &[ + MultiBufferOffset(3)..MultiBufferOffset(3), + MultiBufferOffset(9)..MultiBufferOffset(9), + MultiBufferOffset(15)..MultiBufferOffset(15) + ], ); }); } @@ -4692,7 +4736,7 @@ async fn test_custom_newlines_cause_no_false_positive_diffs( assert_eq!( snapshot .buffer_snapshot() - .diff_hunks_in_range(0..snapshot.buffer_snapshot().len()) + .diff_hunks_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len()) .collect::>(), Vec::new(), "Should not have any diffs for files with custom newlines" @@ -5964,27 +6008,27 @@ fn test_transpose(cx: &mut TestAppContext) { let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), window, cx); editor.set_style(EditorStyle::default(), window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1]) + s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)]) }); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bac"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [2..2] + [MultiBufferOffset(2)..MultiBufferOffset(2)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bca"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [3..3] + [MultiBufferOffset(3)..MultiBufferOffset(3)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bac"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [3..3] + [MultiBufferOffset(3)..MultiBufferOffset(3)] ); editor @@ -5994,37 +6038,37 @@ fn test_transpose(cx: &mut TestAppContext) { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx); editor.set_style(EditorStyle::default(), window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([3..3]) + s.select_ranges([MultiBufferOffset(3)..MultiBufferOffset(3)]) }); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "acb\nde"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [3..3] + [MultiBufferOffset(3)..MultiBufferOffset(3)] ); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([4..4]) + s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(4)]) }); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "acbd\ne"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [5..5] + [MultiBufferOffset(5)..MultiBufferOffset(5)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "acbde\n"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [6..6] + [MultiBufferOffset(6)..MultiBufferOffset(6)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "acbd\ne"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [6..6] + [MultiBufferOffset(6)..MultiBufferOffset(6)] ); editor @@ -6034,41 +6078,62 @@ fn test_transpose(cx: &mut TestAppContext) { let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), window, cx); editor.set_style(EditorStyle::default(), window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1, 2..2, 4..4]) + s.select_ranges([ + MultiBufferOffset(1)..MultiBufferOffset(1), + MultiBufferOffset(2)..MultiBufferOffset(2), + MultiBufferOffset(4)..MultiBufferOffset(4), + ]) }); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bacd\ne"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [2..2, 3..3, 5..5] + [ + MultiBufferOffset(2)..MultiBufferOffset(2), + MultiBufferOffset(3)..MultiBufferOffset(3), + MultiBufferOffset(5)..MultiBufferOffset(5) + ] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bcade\n"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [3..3, 4..4, 6..6] + [ + MultiBufferOffset(3)..MultiBufferOffset(3), + MultiBufferOffset(4)..MultiBufferOffset(4), + MultiBufferOffset(6)..MultiBufferOffset(6) + ] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bcda\ne"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [4..4, 6..6] + [ + MultiBufferOffset(4)..MultiBufferOffset(4), + MultiBufferOffset(6)..MultiBufferOffset(6) + ] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bcade\n"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [4..4, 6..6] + [ + MultiBufferOffset(4)..MultiBufferOffset(4), + MultiBufferOffset(6)..MultiBufferOffset(6) + ] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "bcaed\n"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [5..5, 6..6] + [ + MultiBufferOffset(5)..MultiBufferOffset(5), + MultiBufferOffset(6)..MultiBufferOffset(6) + ] ); editor @@ -6078,27 +6143,27 @@ fn test_transpose(cx: &mut TestAppContext) { let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), window, cx); editor.set_style(EditorStyle::default(), window, cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([4..4]) + s.select_ranges([MultiBufferOffset(4)..MultiBufferOffset(4)]) }); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "🏀🍐✋"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [8..8] + [MultiBufferOffset(8)..MultiBufferOffset(8)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "🏀✋🍐"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [11..11] + [MultiBufferOffset(11)..MultiBufferOffset(11)] ); editor.transpose(&Default::default(), window, cx); assert_eq!(editor.text(cx), "🏀🍐✋"); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - [11..11] + [MultiBufferOffset(11)..MultiBufferOffset(11)] ); editor @@ -9731,7 +9796,11 @@ async fn test_autoindent(cx: &mut TestAppContext) { editor.update_in(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([5..5, 8..8, 9..9]) + s.select_ranges([ + MultiBufferOffset(5)..MultiBufferOffset(5), + MultiBufferOffset(8)..MultiBufferOffset(8), + MultiBufferOffset(9)..MultiBufferOffset(9), + ]) }); editor.newline(&Newline, window, cx); assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n"); @@ -9796,7 +9865,11 @@ async fn test_autoindent_disabled(cx: &mut TestAppContext) { editor.update_in(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([5..5, 8..8, 9..9]) + s.select_ranges([ + MultiBufferOffset(5)..MultiBufferOffset(5), + MultiBufferOffset(8)..MultiBufferOffset(8), + MultiBufferOffset(9)..MultiBufferOffset(9), + ]) }); editor.newline(&Newline, window, cx); assert_eq!( @@ -10453,7 +10526,7 @@ async fn test_autoclose_with_embedded_language(cx: &mut TestAppContext) { let snapshot = editor.snapshot(window, cx); let cursors = editor .selections - .ranges::(&editor.display_snapshot(cx)); + .ranges::(&editor.display_snapshot(cx)); let languages = cursors .iter() .map(|c| snapshot.language_at(c.start).unwrap().name()) @@ -11143,17 +11216,26 @@ async fn test_snippet_placeholder_choices(cx: &mut TestAppContext) { let snippet = Snippet::parse("type ${1|,i32,u32|} = $2").unwrap(); editor - .insert_snippet(&insertion_ranges, snippet, window, cx) + .insert_snippet( + &insertion_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>(), + snippet, + window, + cx, + ) .unwrap(); fn assert(editor: &mut Editor, cx: &mut Context, marked_text: &str) { let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false); assert_eq!(editor.text(cx), expected_text); assert_eq!( - editor - .selections - .ranges::(&editor.display_snapshot(cx)), + editor.selections.ranges(&editor.display_snapshot(cx)), selection_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>() ); } @@ -11177,10 +11259,11 @@ async fn test_snippet_tabstop_navigation_with_placeholders(cx: &mut TestAppConte let (expected_text, selection_ranges) = marked_text_ranges(marked_text, false); assert_eq!(editor.text(cx), expected_text); assert_eq!( - editor - .selections - .ranges::(&editor.display_snapshot(cx)), + editor.selections.ranges(&editor.display_snapshot(cx)), selection_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>() ); } @@ -11198,7 +11281,15 @@ async fn test_snippet_tabstop_navigation_with_placeholders(cx: &mut TestAppConte let snippet = Snippet::parse("type ${1|,i32,u32|} = $2; $3").unwrap(); editor - .insert_snippet(&insertion_ranges, snippet, window, cx) + .insert_snippet( + &insertion_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>(), + snippet, + window, + cx, + ) .unwrap(); assert_state( @@ -11646,7 +11737,7 @@ async fn test_redo_after_noop_format(cx: &mut TestAppContext) { }); editor.update_in(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::default(), window, cx, |s| { - s.select_ranges([0..0]) + s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)]) }); }); assert!(!cx.read(|cx| editor.is_dirty(cx))); @@ -11812,7 +11903,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(1..2)), + |s| s.select_ranges(Some(MultiBufferOffset(1)..MultiBufferOffset(2))), ); editor.insert("|one|two|three|", window, cx); }); @@ -11822,7 +11913,7 @@ async fn test_multibuffer_format_during_save(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(60..70)), + |s| s.select_ranges(Some(MultiBufferOffset(60)..MultiBufferOffset(70))), ); editor.insert("|four|five|six|", window, cx); }); @@ -11990,7 +12081,7 @@ async fn test_autosave_with_dirty_buffers(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(10..10)), + |s| s.select_ranges(Some(MultiBufferOffset(10)..MultiBufferOffset(10))), ); editor.insert("// edited", window, cx); }); @@ -13428,7 +13519,7 @@ async fn test_signature_help(cx: &mut TestAppContext) { cx.update_editor(|editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([0..0]) + s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)]) }); }); @@ -16413,7 +16504,11 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { ); assert_eq!(editor.text(cx), expected_text); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges(selection_ranges) + s.select_ranges( + selection_ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }); editor.handle_input("X", window, cx); @@ -16431,6 +16526,9 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { 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); @@ -16451,6 +16549,9 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), expected_selections + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>() ); }); } @@ -16826,7 +16927,7 @@ async fn test_following(cx: &mut TestAppContext) { // Update the selections only _ = leader.update(cx, |leader, window, cx| { leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1]) + s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)]) }); }); follower @@ -16844,7 +16945,7 @@ async fn test_following(cx: &mut TestAppContext) { _ = follower.update(cx, |follower, _, cx| { assert_eq!( follower.selections.ranges(&follower.display_snapshot(cx)), - vec![1..1] + vec![MultiBufferOffset(1)..MultiBufferOffset(1)] ); }); assert!(*is_still_following.borrow()); @@ -16879,7 +16980,7 @@ async fn test_following(cx: &mut TestAppContext) { // via autoscroll, not via the leader's exact scroll position. _ = leader.update(cx, |leader, window, cx| { leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([0..0]) + s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)]) }); leader.request_autoscroll(Autoscroll::newest(), cx); leader.set_scroll_position(gpui::Point::new(1.5, 3.5), window, cx); @@ -16900,7 +17001,7 @@ async fn test_following(cx: &mut TestAppContext) { assert_eq!(follower.scroll_position(cx), gpui::Point::new(1.5, 0.0)); assert_eq!( follower.selections.ranges(&follower.display_snapshot(cx)), - vec![0..0] + vec![MultiBufferOffset(0)..MultiBufferOffset(0)] ); }); assert!(*is_still_following.borrow()); @@ -16908,7 +17009,7 @@ async fn test_following(cx: &mut TestAppContext) { // Creating a pending selection that precedes another selection _ = leader.update(cx, |leader, window, cx| { leader.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([1..1]) + s.select_ranges([MultiBufferOffset(1)..MultiBufferOffset(1)]) }); leader.begin_selection(DisplayPoint::new(DisplayRow(0), 0), true, 1, window, cx); }); @@ -16927,7 +17028,10 @@ async fn test_following(cx: &mut TestAppContext) { _ = follower.update(cx, |follower, _, cx| { assert_eq!( follower.selections.ranges(&follower.display_snapshot(cx)), - vec![0..0, 1..1] + vec![ + MultiBufferOffset(0)..MultiBufferOffset(0), + MultiBufferOffset(1)..MultiBufferOffset(1) + ] ); }); assert!(*is_still_following.borrow()); @@ -16951,13 +17055,17 @@ async fn test_following(cx: &mut TestAppContext) { _ = follower.update(cx, |follower, _, cx| { assert_eq!( follower.selections.ranges(&follower.display_snapshot(cx)), - vec![0..2] + vec![MultiBufferOffset(0)..MultiBufferOffset(2)] ); }); // Scrolling locally breaks the follow _ = follower.update(cx, |follower, window, cx| { - let top_anchor = follower.buffer().read(cx).read(cx).anchor_after(0); + let top_anchor = follower + .buffer() + .read(cx) + .read(cx) + .anchor_after(MultiBufferOffset(0)); follower.set_scroll_anchor( ScrollAnchor { anchor: top_anchor, @@ -19451,7 +19559,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(1..2)), + |s| s.select_ranges(Some(MultiBufferOffset(1)..MultiBufferOffset(2))), ); editor.open_excerpts(&OpenExcerpts, window, cx); }); @@ -19507,7 +19615,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(39..40)), + |s| s.select_ranges(Some(MultiBufferOffset(39)..MultiBufferOffset(40))), ); editor.open_excerpts(&OpenExcerpts, window, cx); }); @@ -19567,7 +19675,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut TestAppContext) { SelectionEffects::scroll(Autoscroll::Next), window, cx, - |s| s.select_ranges(Some(70..70)), + |s| s.select_ranges(Some(MultiBufferOffset(70)..MultiBufferOffset(70))), ); editor.open_excerpts(&OpenExcerpts, window, cx); }); @@ -22357,7 +22465,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) { (buffer.read(cx).remote_id(), 3), RunnableTasks { templates: vec![], - offset: snapshot.anchor_before(43), + offset: snapshot.anchor_before(MultiBufferOffset(43)), column: 0, extra_variables: HashMap::default(), context_range: BufferOffset(43)..BufferOffset(85), @@ -22367,7 +22475,7 @@ async fn test_find_enclosing_node_with_task(cx: &mut TestAppContext) { (buffer.read(cx).remote_id(), 8), RunnableTasks { templates: vec![], - offset: snapshot.anchor_before(86), + offset: snapshot.anchor_before(MultiBufferOffset(86)), column: 0, extra_variables: HashMap::default(), context_range: BufferOffset(86)..BufferOffset(191), @@ -25749,7 +25857,10 @@ fn assert_selection_ranges(marked_text: &str, editor: &mut Editor, cx: &mut Cont assert_eq!(editor.text(cx), text); assert_eq!( editor.selections.ranges(&editor.display_snapshot(cx)), - ranges, + ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>(), "Assert selections are {}", marked_text ); @@ -25995,10 +26106,12 @@ pub fn handle_completion_request( vec![complete_from_marker.clone(), replace_range_marker.clone()], ); - let complete_from_position = - cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start); + let complete_from_position = cx.to_lsp(MultiBufferOffset( + marked_ranges.remove(&complete_from_marker).unwrap()[0].start, + )); + let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone(); let replace_range = - cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone()); + cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end)); let mut request = cx.set_request_handler::(move |url, params, _| { @@ -26059,13 +26172,18 @@ pub fn handle_completion_request_with_insert_and_replace( ], ); - let complete_from_position = - cx.to_lsp(marked_ranges.remove(&complete_from_marker).unwrap()[0].start); + let complete_from_position = cx.to_lsp(MultiBufferOffset( + marked_ranges.remove(&complete_from_marker).unwrap()[0].start, + )); + let range = marked_ranges.remove(&replace_range_marker).unwrap()[0].clone(); let replace_range = - cx.to_lsp_range(marked_ranges.remove(&replace_range_marker).unwrap()[0].clone()); + cx.to_lsp_range(MultiBufferOffset(range.start)..MultiBufferOffset(range.end)); let insert_range = match marked_ranges.remove(&insert_range_marker) { - Some(ranges) if !ranges.is_empty() => cx.to_lsp_range(ranges[0].clone()), + Some(ranges) if !ranges.is_empty() => { + let range1 = ranges[0].clone(); + cx.to_lsp_range(MultiBufferOffset(range1.start)..MultiBufferOffset(range1.end)) + } _ => lsp::Range { start: replace_range.start, end: complete_from_position, @@ -26115,7 +26233,10 @@ fn handle_resolve_completion_request( .iter() .map(|(marked_string, new_text)| { let (_, marked_ranges) = marked_text_ranges(marked_string, false); - let replace_range = cx.to_lsp_range(marked_ranges[0].clone()); + let replace_range = cx.to_lsp_range( + MultiBufferOffset(marked_ranges[0].start) + ..MultiBufferOffset(marked_ranges[0].end), + ); lsp::TextEdit::new(replace_range, new_text.to_string()) }) .collect::>() @@ -26199,7 +26320,7 @@ fn assert_hunk_revert( let snapshot = editor.snapshot(window, cx); let reverted_hunk_statuses = snapshot .buffer_snapshot() - .diff_hunks_in_range(0..snapshot.buffer_snapshot().len()) + .diff_hunks_in_range(MultiBufferOffset(0)..snapshot.buffer_snapshot().len()) .map(|hunk| hunk.status().kind) .collect::>(); @@ -26823,7 +26944,9 @@ async fn test_newline_replacement_in_single_line(cx: &mut TestAppContext) { editor.update(cx, |editor, cx| { assert_eq!(editor.display_text(cx), "oops⋯⋯wow⋯"); }); - editor.update(cx, |editor, cx| editor.edit([(3..5, "")], cx)); + editor.update(cx, |editor, cx| { + editor.edit([(MultiBufferOffset(3)..MultiBufferOffset(5), "")], cx) + }); cx.run_until_parked(); editor.update(cx, |editor, cx| { assert_eq!(editor.display_text(cx), "oop⋯wow⋯"); @@ -27895,7 +28018,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) { // Scenario 1: Unfolded buffers, position cursor on "2", select all matches, then insert cx.update_editor(|editor, window, cx| { editor.change_selections(None.into(), window, cx, |s| { - s.select_ranges([2..3]); + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]); }); }); cx.assert_excerpts_with_selections(indoc! {" @@ -27952,7 +28075,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) { // Select "2" and select all matches cx.update_editor(|editor, window, cx| { editor.change_selections(None.into(), window, cx, |s| { - s.select_ranges([2..3]); + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]); }); editor .select_all_matches(&SelectAllMatches, window, cx) @@ -28003,7 +28126,7 @@ async fn test_multibuffer_selections_with_folding(cx: &mut TestAppContext) { // Select "2" and select all matches cx.update_editor(|editor, window, cx| { editor.change_selections(None.into(), window, cx, |s| { - s.select_ranges([2..3]); + s.select_ranges([MultiBufferOffset(2)..MultiBufferOffset(3)]); }); editor .select_all_matches(&SelectAllMatches, window, cx) diff --git a/crates/editor/src/git/blame.rs b/crates/editor/src/git/blame.rs index 52eab4f817acad25deebbfa6d807020f9ce1ac80..008630faef7cc1ccb3b9703e4b11c0b88b7cf17c 100644 --- a/crates/editor/src/git/blame.rs +++ b/crates/editor/src/git/blame.rs @@ -67,7 +67,7 @@ impl<'a> sum_tree::Dimension<'a, GitBlameEntrySummary> for u32 { struct GitBlameBuffer { entries: SumTree, buffer_snapshot: BufferSnapshot, - buffer_edits: text::Subscription, + buffer_edits: text::Subscription, commit_details: HashMap, } diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs index 286260e3b0f42da0c3416a07357128ac5e3d0c57..eaef28bed21bf480a32c3abd3440a6c41e42d5f1 100644 --- a/crates/editor/src/highlight_matching_bracket.rs +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -1,6 +1,7 @@ use crate::{Editor, RangeToAnchorExt}; use gpui::{Context, HighlightStyle, Window}; use language::CursorShape; +use multi_buffer::MultiBufferOffset; use theme::ActiveTheme; enum MatchingBracketHighlight {} @@ -15,7 +16,7 @@ impl Editor { let snapshot = self.snapshot(window, cx); let buffer_snapshot = snapshot.buffer_snapshot(); - let newest_selection = self.selections.newest::(&snapshot); + let newest_selection = self.selections.newest::(&snapshot); // Don't highlight brackets if the selection isn't empty if !newest_selection.is_empty() { return; diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 03fce48f146fbfa3bdab93937038c4101a04a484..5ef52f36dfd609a49d93eb77b74b2df3287df30a 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -738,6 +738,7 @@ mod tests { use gpui::Modifiers; use indoc::indoc; use lsp::request::{GotoDefinition, GotoTypeDefinition}; + use multi_buffer::MultiBufferOffset; use settings::InlayHintSettingsContent; use util::{assert_set_eq, path}; use workspace::item::Item; @@ -1067,8 +1068,8 @@ mod tests { .clone(); cx.update_editor(|editor, window, cx| { let snapshot = editor.buffer().read(cx).snapshot(cx); - let anchor_range = snapshot.anchor_before(selection_range.start) - ..snapshot.anchor_after(selection_range.end); + let anchor_range = snapshot.anchor_before(MultiBufferOffset(selection_range.start)) + ..snapshot.anchor_after(MultiBufferOffset(selection_range.end)); editor.change_selections(Default::default(), window, cx, |s| { s.set_pending_anchor_range(anchor_range, crate::SelectMode::Character) }); @@ -1122,7 +1123,7 @@ mod tests { } "})[0] .start; - let hint_position = cx.to_lsp(hint_start_offset); + let hint_position = cx.to_lsp(MultiBufferOffset(hint_start_offset)); let target_range = cx.lsp_range(indoc! {" struct «TestStruct»; @@ -1179,8 +1180,8 @@ mod tests { .unwrap(); let midpoint = cx.update_editor(|editor, window, cx| { let snapshot = editor.snapshot(window, cx); - let previous_valid = inlay_range.start.to_display_point(&snapshot); - let next_valid = inlay_range.end.to_display_point(&snapshot); + let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot); + let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot); assert_eq!(previous_valid.row(), next_valid.row()); assert!(previous_valid.column() < next_valid.column()); DisplayPoint::new( @@ -1203,7 +1204,7 @@ mod tests { let buffer_snapshot = editor.buffer().update(cx, |buffer, cx| buffer.snapshot(cx)); let expected_highlight = InlayHighlight { inlay: InlayId::Hint(0), - inlay_position: buffer_snapshot.anchor_after(inlay_range.start), + inlay_position: buffer_snapshot.anchor_after(MultiBufferOffset(inlay_range.start)), range: 0..hint_label.len(), }; assert_set_eq!(actual_highlights, vec![&expected_highlight]); diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 721fce34c8c030322207cd74a69a266119596086..ef16fc92d847763ecbc764c3913266fd84a26006 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -17,7 +17,7 @@ use itertools::Itertools; use language::{DiagnosticEntry, Language, LanguageRegistry}; use lsp::DiagnosticSeverity; use markdown::{Markdown, MarkdownElement, MarkdownStyle}; -use multi_buffer::{ToOffset, ToPoint}; +use multi_buffer::{MultiBufferOffset, ToOffset, ToPoint}; use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart}; use settings::Settings; use std::{borrow::Cow, cell::RefCell}; @@ -106,7 +106,7 @@ pub fn find_hovered_hint_part( hovered_offset: InlayOffset, ) -> Option<(InlayHintLabelPart, Range)> { if hovered_offset >= hint_start { - let mut hovered_character = (hovered_offset - hint_start).0; + let mut hovered_character = hovered_offset - hint_start; let mut part_start = hint_start; for part in label_parts { let part_len = part.value.chars().count(); @@ -316,12 +316,12 @@ fn show_hover( } else { snapshot .buffer_snapshot() - .diagnostics_with_buffer_ids_in_range::(offset..offset) + .diagnostics_with_buffer_ids_in_range::(offset..offset) .filter(|(_, diagnostic)| { Some(diagnostic.diagnostic.group_id) != active_group_id }) // Find the entry with the most specific range - .min_by_key(|(_, entry)| entry.range.len()) + .min_by_key(|(_, entry)| entry.range.end - entry.range.start) }; let diagnostic_popover = if let Some((buffer_id, local_diagnostic)) = local_diagnostic { @@ -1633,7 +1633,7 @@ mod tests { } "})[0] .start; - let hint_position = cx.to_lsp(hint_start_offset); + let hint_position = cx.to_lsp(MultiBufferOffset(hint_start_offset)); let new_type_target_range = cx.lsp_range(indoc! {" struct TestStruct; @@ -1708,8 +1708,8 @@ mod tests { .unwrap(); let new_type_hint_part_hover_position = cx.update_editor(|editor, window, cx| { let snapshot = editor.snapshot(window, cx); - let previous_valid = inlay_range.start.to_display_point(&snapshot); - let next_valid = inlay_range.end.to_display_point(&snapshot); + let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot); + let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot); assert_eq!(previous_valid.row(), next_valid.row()); assert!(previous_valid.column() < next_valid.column()); let exact_unclipped = DisplayPoint::new( @@ -1819,7 +1819,8 @@ mod tests { popover.symbol_range, RangeInEditor::Inlay(InlayHighlight { inlay: InlayId::Hint(0), - inlay_position: buffer_snapshot.anchor_after(inlay_range.start), + inlay_position: buffer_snapshot + .anchor_after(MultiBufferOffset(inlay_range.start)), range: ": ".len()..": ".len() + new_type_label.len(), }), "Popover range should match the new type label part" @@ -1832,8 +1833,8 @@ mod tests { let struct_hint_part_hover_position = cx.update_editor(|editor, window, cx| { let snapshot = editor.snapshot(window, cx); - let previous_valid = inlay_range.start.to_display_point(&snapshot); - let next_valid = inlay_range.end.to_display_point(&snapshot); + let previous_valid = MultiBufferOffset(inlay_range.start).to_display_point(&snapshot); + let next_valid = MultiBufferOffset(inlay_range.end).to_display_point(&snapshot); assert_eq!(previous_valid.row(), next_valid.row()); assert!(previous_valid.column() < next_valid.column()); let exact_unclipped = DisplayPoint::new( @@ -1873,7 +1874,8 @@ mod tests { popover.symbol_range, RangeInEditor::Inlay(InlayHighlight { inlay: InlayId::Hint(0), - inlay_position: buffer_snapshot.anchor_after(inlay_range.start), + inlay_position: buffer_snapshot + .anchor_after(MultiBufferOffset(inlay_range.start)), range: ": ".len() + new_type_label.len() + "<".len() ..": ".len() + new_type_label.len() + "<".len() + struct_label.len(), }), diff --git a/crates/editor/src/inlays/inlay_hints.rs b/crates/editor/src/inlays/inlay_hints.rs index 7aacd1c86e6ec9a2034493d26df6d2271d33724e..b30137cf7796d6c916623d100420ac34eb80224a 100644 --- a/crates/editor/src/inlays/inlay_hints.rs +++ b/crates/editor/src/inlays/inlay_hints.rs @@ -645,9 +645,9 @@ impl Editor { ) { let highlight_start = - (part_range.start - hint_start).0 + extra_shift_left; + (part_range.start - hint_start) + extra_shift_left; let highlight_end = - (part_range.end - hint_start).0 + extra_shift_right; + (part_range.end - hint_start) + extra_shift_right; let highlight = InlayHighlight { inlay: hovered_hint.id, inlay_position: hovered_hint.position, @@ -948,7 +948,7 @@ pub mod tests { use language::{Language, LanguageConfig, LanguageMatcher}; use languages::rust_lang; use lsp::FakeLanguageServer; - use multi_buffer::MultiBuffer; + use multi_buffer::{MultiBuffer, MultiBufferOffset}; use parking_lot::Mutex; use pretty_assertions::assert_eq; use project::{FakeFs, Project}; @@ -1029,7 +1029,7 @@ pub mod tests { editor .update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input("some change", window, cx); }) @@ -1429,7 +1429,7 @@ pub mod tests { rs_editor .update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input("some rs change", window, cx); }) @@ -1461,7 +1461,7 @@ pub mod tests { md_editor .update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input("some md change", window, cx); }) @@ -1909,7 +1909,7 @@ pub mod tests { editor .update(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input(change_after_opening, window, cx); }) @@ -1955,7 +1955,7 @@ pub mod tests { task_editor .update(&mut cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges([13..13]) + s.select_ranges([MultiBufferOffset(13)..MultiBufferOffset(13)]) }); editor.handle_input(async_later_change, window, cx); }) @@ -2706,7 +2706,7 @@ let c = 3;"# let mut editor = Editor::for_multibuffer(multi_buffer, Some(project.clone()), window, cx); editor.change_selections(SelectionEffects::default(), window, cx, |s| { - s.select_ranges([0..0]) + s.select_ranges([MultiBufferOffset(0)..MultiBufferOffset(0)]) }); editor }); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index a860e137a856a2e7982f1177c205391b80625944..0e97b95bb6d2818364ec0fa161f6360c850a6dcc 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -21,6 +21,7 @@ use language::{ SelectionGoal, proto::serialize_anchor as serialize_text_anchor, }; use lsp::DiagnosticSeverity; +use multi_buffer::MultiBufferOffset; use project::{ Project, ProjectItem as _, ProjectPath, lsp_store::FormatTrigger, project_settings::ProjectSettings, search::SearchQuery, @@ -1735,7 +1736,7 @@ impl SearchableItem for Editor { let mut ranges = Vec::new(); let search_within_ranges = if search_within_ranges.is_empty() { - vec![buffer.anchor_before(0)..buffer.anchor_after(buffer.len())] + vec![buffer.anchor_before(MultiBufferOffset(0))..buffer.anchor_after(buffer.len())] } else { search_within_ranges }; @@ -1746,7 +1747,10 @@ impl SearchableItem for Editor { { ranges.extend( query - .search(search_buffer, Some(search_range.clone())) + .search( + search_buffer, + Some(search_range.start.0..search_range.end.0), + ) .await .into_iter() .map(|match_range| { diff --git a/crates/editor/src/jsx_tag_auto_close.rs b/crates/editor/src/jsx_tag_auto_close.rs index 0e32bc686ad98a45b83712841c13fffc07421acb..e22fde313df4b99b7b650775ad7e7397e3c4f813 100644 --- a/crates/editor/src/jsx_tag_auto_close.rs +++ b/crates/editor/src/jsx_tag_auto_close.rs @@ -1,7 +1,7 @@ use anyhow::{Context as _, Result, anyhow}; use collections::HashMap; use gpui::{Context, Entity, Window}; -use multi_buffer::{MultiBuffer, ToOffset}; +use multi_buffer::{BufferOffset, MultiBuffer, ToOffset}; use std::ops::Range; use util::ResultExt as _; @@ -546,9 +546,10 @@ pub(crate) fn handle_from( if edit_range_offset.start != edit_range_offset.end { continue; } - if let Some(selection) = - buffer_selection_map.get_mut(&(edit_range_offset.start, edit_range_offset.end)) - { + if let Some(selection) = buffer_selection_map.get_mut(&( + BufferOffset(edit_range_offset.start), + BufferOffset(edit_range_offset.end), + )) { if selection.0.head().bias() != text::Bias::Right || selection.0.tail().bias() != text::Bias::Right { @@ -621,7 +622,7 @@ mod jsx_tag_autoclose_tests { use super::*; use gpui::{AppContext as _, TestAppContext}; use languages::language; - use multi_buffer::ExcerptRange; + use multi_buffer::{ExcerptRange, MultiBufferOffset}; use text::Selection; async fn test_setup(cx: &mut TestAppContext) -> EditorTestContext { @@ -842,9 +843,9 @@ mod jsx_tag_autoclose_tests { cx.update_editor(|editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { selections.select(vec![ - Selection::from_offset(4), - Selection::from_offset(9), - Selection::from_offset(15), + Selection::from_offset(MultiBufferOffset(4)), + Selection::from_offset(MultiBufferOffset(9)), + Selection::from_offset(MultiBufferOffset(15)), ]) }) }); diff --git a/crates/editor/src/linked_editing_ranges.rs b/crates/editor/src/linked_editing_ranges.rs index ab16fe7eb4bce28ef6bfee2c2bde1d52fda86561..33635a2ae2009031220ab0a58e99f8b07957de94 100644 --- a/crates/editor/src/linked_editing_ranges.rs +++ b/crates/editor/src/linked_editing_ranges.rs @@ -1,6 +1,7 @@ use collections::HashMap; use gpui::{AppContext, Context, Window}; use itertools::Itertools; +use multi_buffer::MultiBufferOffset; use std::{ops::Range, time::Duration}; use text::{AnchorRangeExt, BufferId, ToPoint}; use util::ResultExt; @@ -60,7 +61,9 @@ pub(super) fn refresh_linked_ranges( editor .update(cx, |editor, cx| { let display_snapshot = editor.display_snapshot(cx); - let selections = editor.selections.all::(&display_snapshot); + let selections = editor + .selections + .all::(&display_snapshot); let snapshot = display_snapshot.buffer_snapshot(); let buffer = editor.buffer.read(cx); for selection in selections { diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index a83545aaf26b0e148345c185f4f39910e97a727e..8635d89ed13e77d260307667740bf79ab4022e6f 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -8,7 +8,7 @@ use crate::{ }; use gpui::{Pixels, WindowTextSystem}; use language::{CharClassifier, Point}; -use multi_buffer::{MultiBufferRow, MultiBufferSnapshot}; +use multi_buffer::{MultiBufferOffset, MultiBufferRow, MultiBufferSnapshot}; use serde::Deserialize; use workspace::searchable::Direction; @@ -358,28 +358,28 @@ pub fn adjust_greedy_deletion( let mut whitespace_sequences = Vec::new(); let mut current_offset = trimmed_delete_range.start; - let mut whitespace_sequence_length = 0; - let mut whitespace_sequence_start = 0; + let mut whitespace_sequence_length = MultiBufferOffset(0); + let mut whitespace_sequence_start = MultiBufferOffset(0); for ch in map .buffer_snapshot() .text_for_range(trimmed_delete_range.clone()) .flat_map(str::chars) { if ch.is_whitespace() { - if whitespace_sequence_length == 0 { + if whitespace_sequence_length == MultiBufferOffset(0) { whitespace_sequence_start = current_offset; } whitespace_sequence_length += 1; } else { - if whitespace_sequence_length >= 2 { + if whitespace_sequence_length >= MultiBufferOffset(2) { whitespace_sequences.push((whitespace_sequence_start, current_offset)); } - whitespace_sequence_start = 0; - whitespace_sequence_length = 0; + whitespace_sequence_start = MultiBufferOffset(0); + whitespace_sequence_length = MultiBufferOffset(0); } current_offset += ch.len_utf8(); } - if whitespace_sequence_length >= 2 { + if whitespace_sequence_length >= MultiBufferOffset(2) { whitespace_sequences.push((whitespace_sequence_start, current_offset)); } @@ -731,7 +731,7 @@ pub fn find_preceding_boundary_trail( } let trail = trail_offset - .map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Left)); + .map(|trail_offset| map.clip_point(trail_offset.to_display_point(map), Bias::Left)); ( trail, @@ -779,7 +779,7 @@ pub fn find_boundary_trail( } let trail = trail_offset - .map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Right)); + .map(|trail_offset| map.clip_point(trail_offset.to_display_point(map), Bias::Right)); ( trail, @@ -810,8 +810,8 @@ pub fn find_boundary_exclusive( /// the [`DisplaySnapshot`]. The offsets are relative to the start of a buffer. pub fn chars_after( map: &DisplaySnapshot, - mut offset: usize, -) -> impl Iterator)> + '_ { + mut offset: MultiBufferOffset, +) -> impl Iterator)> + '_ { map.buffer_snapshot().chars_at(offset).map(move |ch| { let before = offset; offset += ch.len_utf8(); @@ -824,8 +824,8 @@ pub fn chars_after( /// the [`DisplaySnapshot`]. The offsets are relative to the start of a buffer. pub fn chars_before( map: &DisplaySnapshot, - mut offset: usize, -) -> impl Iterator)> + '_ { + mut offset: MultiBufferOffset, +) -> impl Iterator)> + '_ { map.buffer_snapshot() .reversed_chars_at(offset) .map(move |ch| { @@ -1018,8 +1018,9 @@ mod tests { // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary let mut id = 0; - let inlays = (0..buffer_snapshot.len()) + let inlays = (0..buffer_snapshot.len().0) .flat_map(|offset| { + let offset = MultiBufferOffset(offset); [ Inlay::edit_prediction( post_inc(&mut id), @@ -1058,7 +1059,7 @@ mod tests { ), snapshot .buffer_snapshot() - .offset_to_point(5) + .offset_to_point(MultiBufferOffset(5)) .to_display_point(&snapshot), "Should not stop at inlays when looking for boundaries" ); diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 7bb90deda0da84fa8719b9530dffef567c467c36..c1b8d11db94de7394b36ec706f42622993b63785 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -1,18 +1,18 @@ use std::{ cmp, fmt, iter, mem, - ops::{Deref, DerefMut, Range, Sub}, + ops::{AddAssign, Deref, DerefMut, Range, Sub}, sync::Arc, }; use collections::HashMap; use gpui::Pixels; use itertools::Itertools as _; -use language::{Bias, Point, Selection, SelectionGoal, TextDimension}; +use language::{Bias, Point, Selection, SelectionGoal}; +use multi_buffer::{MultiBufferDimension, MultiBufferOffset}; use util::post_inc; use crate::{ Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBufferSnapshot, SelectMode, ToOffset, - ToPoint, display_map::{DisplaySnapshot, ToDisplayPoint}, movement::TextLayoutDetails, }; @@ -97,7 +97,7 @@ impl SelectionsCollection { if self.pending.is_none() { self.disjoint_anchors_arc() } else { - let all_offset_selections = self.all::(snapshot); + let all_offset_selections = self.all::(snapshot); all_offset_selections .into_iter() .map(|selection| selection_to_anchor_selection(selection, snapshot)) @@ -113,10 +113,10 @@ impl SelectionsCollection { self.pending.as_mut().map(|pending| &mut pending.selection) } - pub fn pending>( - &self, - snapshot: &DisplaySnapshot, - ) -> Option> { + pub fn pending(&self, snapshot: &DisplaySnapshot) -> Option> + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { resolve_selections_wrapping_blocks(self.pending_anchor(), &snapshot).next() } @@ -124,9 +124,9 @@ impl SelectionsCollection { self.pending.as_ref().map(|pending| pending.mode.clone()) } - pub fn all<'a, D>(&self, snapshot: &DisplaySnapshot) -> Vec> + pub fn all(&self, snapshot: &DisplaySnapshot) -> Vec> where - D: 'a + TextDimension + Ord + Sub, + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, { let disjoint_anchors = &self.disjoint; let mut disjoint = @@ -204,13 +204,13 @@ impl SelectionsCollection { } } - pub fn disjoint_in_range<'a, D>( + pub fn disjoint_in_range( &self, range: Range, snapshot: &DisplaySnapshot, ) -> Vec> where - D: 'a + TextDimension + Ord + Sub + std::fmt::Debug, + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord + std::fmt::Debug, { let start_ix = match self .disjoint @@ -267,10 +267,10 @@ impl SelectionsCollection { .unwrap() } - pub fn newest>( - &self, - snapshot: &DisplaySnapshot, - ) -> Selection { + pub fn newest(&self, snapshot: &DisplaySnapshot) -> Selection + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { resolve_selections_wrapping_blocks([self.newest_anchor()], &snapshot) .next() .unwrap() @@ -290,10 +290,10 @@ impl SelectionsCollection { .unwrap() } - pub fn oldest>( - &self, - snapshot: &DisplaySnapshot, - ) -> Selection { + pub fn oldest(&self, snapshot: &DisplaySnapshot) -> Selection + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { resolve_selections_wrapping_blocks([self.oldest_anchor()], &snapshot) .next() .unwrap() @@ -306,27 +306,27 @@ impl SelectionsCollection { .unwrap_or_else(|| self.disjoint.first().cloned().unwrap()) } - pub fn first>( - &self, - snapshot: &DisplaySnapshot, - ) -> Selection { + pub fn first(&self, snapshot: &DisplaySnapshot) -> Selection + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { self.all(snapshot).first().unwrap().clone() } - pub fn last>( - &self, - snapshot: &DisplaySnapshot, - ) -> Selection { + pub fn last(&self, snapshot: &DisplaySnapshot) -> Selection + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { self.all(snapshot).last().unwrap().clone() } /// Returns a list of (potentially backwards!) ranges representing the selections. /// Useful for test assertions, but prefer `.all()` instead. #[cfg(any(test, feature = "test-support"))] - pub fn ranges>( - &self, - snapshot: &DisplaySnapshot, - ) -> Vec> { + pub fn ranges(&self, snapshot: &DisplaySnapshot) -> Vec> + where + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, + { self.all::(snapshot) .iter() .map(|s| { @@ -509,7 +509,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { }; if filtered_selections.is_empty() { - let default_anchor = self.snapshot.anchor_before(0); + let default_anchor = self.snapshot.anchor_before(MultiBufferOffset(0)); self.collection.disjoint = Arc::from([Selection { id: post_inc(&mut self.collection.next_selection_id), start: default_anchor, @@ -590,7 +590,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { pub fn insert_range(&mut self, range: Range) where - T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub + std::marker::Copy, + T: ToOffset, { let display_map = self.display_snapshot(); let mut selections = self.collection.all(&display_map); @@ -656,7 +656,8 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { pub fn select_anchors(&mut self, selections: Vec>) { let map = self.display_snapshot(); let resolved_selections = - resolve_selections_wrapping_blocks::(&selections, &map).collect::>(); + resolve_selections_wrapping_blocks::(&selections, &map) + .collect::>(); self.select(resolved_selections); } @@ -673,7 +674,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { fn select_offset_ranges(&mut self, ranges: I) where - I: IntoIterator>, + I: IntoIterator>, { let selections = ranges .into_iter() @@ -808,13 +809,13 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { pub fn move_offsets_with( &mut self, - mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection), + mut move_selection: impl FnMut(&MultiBufferSnapshot, &mut Selection), ) { let mut changed = false; let display_map = self.display_snapshot(); let selections = self .collection - .all::(&display_map) + .all::(&display_map) .into_iter() .map(|selection| { let mut moved_selection = selection.clone(); @@ -938,7 +939,7 @@ impl<'snap, 'a> MutableSelectionsCollection<'snap, 'a> { let map = self.display_snapshot(); let resolved_selections = resolve_selections_wrapping_blocks(adjusted_disjoint.iter(), &map).collect(); - self.select::(resolved_selections); + self.select::(resolved_selections); } if let Some(pending) = pending.as_mut() { @@ -981,7 +982,7 @@ impl DerefMut for MutableSelectionsCollection<'_, '_> { } fn selection_to_anchor_selection( - selection: Selection, + selection: Selection, buffer: &MultiBufferSnapshot, ) -> Selection { let end_bias = if selection.start == selection.end { @@ -1054,7 +1055,7 @@ fn resolve_selections_display<'a>( coalesce_selections(selections) } -/// Resolves the passed in anchors to [`TextDimension`]s `D` +/// Resolves the passed in anchors to [`MultiBufferDimension`]s `D` /// wrapping around blocks inbetween. /// /// # Panics @@ -1065,7 +1066,7 @@ pub(crate) fn resolve_selections_wrapping_blocks<'a, D, I>( map: &'a DisplaySnapshot, ) -> impl 'a + Iterator> where - D: TextDimension + Ord + Sub, + D: MultiBufferDimension + Sub + AddAssign<::Output> + Ord, I: 'a + IntoIterator>, { // Transforms `Anchor -> DisplayPoint -> Point -> DisplayPoint -> D` diff --git a/crates/editor/src/signature_help.rs b/crates/editor/src/signature_help.rs index 8d74638e4c2aaf356ffabdeef717b9b105487ee3..b394364e01cbd647a0e17afc0ddc13afdb12ced3 100644 --- a/crates/editor/src/signature_help.rs +++ b/crates/editor/src/signature_help.rs @@ -1,13 +1,13 @@ use crate::actions::ShowSignatureHelp; use crate::hover_popover::open_markdown_url; -use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp, hover_markdown_style}; +use crate::{BufferOffset, Editor, EditorSettings, ToggleAutoSignatureHelp, hover_markdown_style}; use gpui::{ App, Context, Entity, HighlightStyle, MouseButton, ScrollHandle, Size, StyledText, Task, TextStyle, Window, combine_highlights, }; use language::BufferSnapshot; use markdown::{Markdown, MarkdownElement}; -use multi_buffer::{Anchor, ToOffset}; +use multi_buffer::{Anchor, MultiBufferOffset, ToOffset}; use settings::Settings; use std::ops::Range; use text::Rope; @@ -82,7 +82,9 @@ impl Editor { if !(self.signature_help_state.is_shown() || self.auto_signature_help_enabled(cx)) { return false; } - let newest_selection = self.selections.newest::(&self.display_snapshot(cx)); + let newest_selection = self + .selections + .newest::(&self.display_snapshot(cx)); let head = newest_selection.head(); if !newest_selection.is_empty() && head != newest_selection.tail() { @@ -92,14 +94,14 @@ impl Editor { } let buffer_snapshot = self.buffer().read(cx).snapshot(cx); - let bracket_range = |position: usize| match (position, position + 1) { - (0, b) if b <= buffer_snapshot.len() => 0..b, - (0, b) => 0..b - 1, + let bracket_range = |position: MultiBufferOffset| match (position, position + 1usize) { + (MultiBufferOffset(0), b) if b <= buffer_snapshot.len() => MultiBufferOffset(0)..b, + (MultiBufferOffset(0), b) => MultiBufferOffset(0)..b - 1, (a, b) if b <= buffer_snapshot.len() => a - 1..b, (a, b) => a - 1..b - 1, }; let not_quote_like_brackets = - |buffer: &BufferSnapshot, start: Range, end: Range| { + |buffer: &BufferSnapshot, start: Range, end: Range| { let text_start = buffer.text_for_range(start).collect::(); let text_end = buffer.text_for_range(end).collect::(); QUOTE_PAIRS diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 9d1003e8c08b3d725ffa13b90eb0ee405520d8cd..5a0652bdd199a638f92234b1d50232071db18e07 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -16,7 +16,7 @@ use gpui::{ AppContext as _, Context, Entity, EntityId, Font, FontFeatures, FontStyle, FontWeight, Pixels, VisualTestContext, Window, font, size, }; -use multi_buffer::ToPoint; +use multi_buffer::{MultiBufferOffset, ToPoint}; use pretty_assertions::assert_eq; use project::{Project, project_settings::DiagnosticSeverity}; use ui::{App, BorrowAppContext, px}; @@ -78,7 +78,7 @@ pub fn marked_display_snapshot( let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); let markers = markers .into_iter() - .map(|offset| offset.to_display_point(&snapshot)) + .map(|offset| MultiBufferOffset(offset).to_display_point(&snapshot)) .collect(); (snapshot, markers) @@ -94,7 +94,11 @@ pub fn select_ranges( let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true); assert_eq!(editor.text(cx), unmarked_text); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { - s.select_ranges(text_ranges) + s.select_ranges( + text_ranges + .into_iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }); } @@ -108,7 +112,12 @@ pub fn assert_text_with_selections( assert_eq!(editor.text(cx), unmarked_text, "text doesn't match"); let actual = generate_marked_text( &editor.text(cx), - &editor.selections.ranges(&editor.display_snapshot(cx)), + &editor + .selections + .ranges::(&editor.display_snapshot(cx)) + .into_iter() + .map(|range| range.start.0..range.end.0) + .collect::>(), marked_text.contains("«"), ); assert_eq!(actual, marked_text, "Selections don't match"); diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 87cc3357783ef4503b584f9624d14a35a8487dd7..3afe0e6134221fc69837abd30618f2b74ae069f5 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -7,6 +7,7 @@ use std::{ use anyhow::Result; use language::{markdown_lang, rust_lang}; +use multi_buffer::MultiBufferOffset; use serde_json::json; use crate::{Editor, ToPoint}; @@ -333,50 +334,38 @@ impl EditorLspTestContext { #[track_caller] pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { let ranges = self.ranges(marked_text); - self.to_lsp_range(ranges[0].clone()) + self.to_lsp_range(MultiBufferOffset(ranges[0].start)..MultiBufferOffset(ranges[0].end)) } #[expect(clippy::wrong_self_convention, reason = "This is test code")] - pub fn to_lsp_range(&mut self, range: Range) -> lsp::Range { + pub fn to_lsp_range(&mut self, range: Range) -> lsp::Range { + use language::ToPointUtf16; let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx)); let start_point = range.start.to_point(&snapshot.buffer_snapshot()); let end_point = range.end.to_point(&snapshot.buffer_snapshot()); self.editor(|editor, _, cx| { let buffer = editor.buffer().read(cx); - let start = point_to_lsp( - buffer - .point_to_buffer_offset(start_point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ); - let end = point_to_lsp( - buffer - .point_to_buffer_offset(end_point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ); - + let (start_buffer, start_offset) = + buffer.point_to_buffer_offset(start_point, cx).unwrap(); + let start = point_to_lsp(start_offset.to_point_utf16(&start_buffer.read(cx))); + let (end_buffer, end_offset) = buffer.point_to_buffer_offset(end_point, cx).unwrap(); + let end = point_to_lsp(end_offset.to_point_utf16(&end_buffer.read(cx))); lsp::Range { start, end } }) } #[expect(clippy::wrong_self_convention, reason = "This is test code")] - pub fn to_lsp(&mut self, offset: usize) -> lsp::Position { + pub fn to_lsp(&mut self, offset: MultiBufferOffset) -> lsp::Position { + use language::ToPointUtf16; + let snapshot = self.update_editor(|editor, window, cx| editor.snapshot(window, cx)); let point = offset.to_point(&snapshot.buffer_snapshot()); self.editor(|editor, _, cx| { let buffer = editor.buffer().read(cx); - point_to_lsp( - buffer - .point_to_buffer_offset(point, cx) - .unwrap() - .1 - .to_point_utf16(&buffer.read(cx)), - ) + let (buffer, offset) = buffer.point_to_buffer_offset(point, cx).unwrap(); + point_to_lsp(offset.to_point_utf16(&buffer.read(cx))) }) } diff --git a/crates/editor/src/test/editor_test_context.rs b/crates/editor/src/test/editor_test_context.rs index 200c1f08cfb87dec47d66760c385aa357e45ce95..5793bcf576c7ed0e1604c30aada0fb362f65bb9f 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, MultiBufferRow}; +use multi_buffer::{Anchor, ExcerptRange, MultiBufferOffset, MultiBufferRow}; use parking_lot::RwLock; use project::{FakeFs, Project}; use std::{ @@ -267,7 +267,7 @@ impl EditorTestContext { let snapshot = self.editor.update_in(&mut self.cx, |editor, window, cx| { editor.snapshot(window, cx) }); - ranges[0].start.to_display_point(&snapshot) + MultiBufferOffset(ranges[0].start).to_display_point(&snapshot) } pub fn pixel_position(&mut self, marked_text: &str) -> Point { @@ -373,7 +373,11 @@ impl EditorTestContext { self.editor.update_in(&mut self.cx, |editor, window, cx| { editor.set_text(unmarked_text, window, cx); editor.change_selections(Default::default(), window, cx, |s| { - s.select_ranges(selection_ranges) + s.select_ranges( + selection_ranges + .into_iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }) }); state_context @@ -390,7 +394,11 @@ impl EditorTestContext { self.editor.update_in(&mut self.cx, |editor, window, cx| { assert_eq!(editor.text(cx), unmarked_text); editor.change_selections(Default::default(), window, cx, |s| { - s.select_ranges(selection_ranges) + s.select_ranges( + selection_ranges + .into_iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }) }); state_context @@ -576,6 +584,7 @@ impl EditorTestContext { .unwrap_or_default() .iter() .map(|range| range.to_offset(&snapshot.buffer_snapshot())) + .map(|range| range.start.0..range.end.0) .collect() }); assert_set_eq!(actual_ranges, expected_ranges); @@ -591,6 +600,7 @@ impl EditorTestContext { .unwrap_or_default() .into_iter() .map(|range| range.to_offset(&snapshot.buffer_snapshot())) + .map(|range| range.start.0..range.end.0) .collect(); assert_set_eq!(actual_ranges, expected_ranges); } @@ -608,14 +618,16 @@ impl EditorTestContext { fn editor_selections(&mut self) -> Vec> { self.editor .update(&mut self.cx, |editor, cx| { - editor.selections.all::(&editor.display_snapshot(cx)) + editor + .selections + .all::(&editor.display_snapshot(cx)) }) .into_iter() .map(|s| { if s.reversed { - s.end..s.start + s.end.0..s.start.0 } else { - s.start..s.end + s.start.0..s.end.0 } }) .collect::>() @@ -711,7 +723,10 @@ pub fn assert_state_with_diff( snapshot.buffer_snapshot().clone(), editor .selections - .ranges::(&snapshot.display_snapshot), + .ranges::(&snapshot.display_snapshot) + .into_iter() + .map(|range| range.start.0..range.end.0) + .collect::>(), ) }); diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 765e1f84a4a3a5b7e257e51df9a9542d0abff067..b0fa80fa7afef96fa48aa80883fb252beeed9629 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -1,6 +1,9 @@ use anyhow::{Context as _, Result}; use buffer_diff::{BufferDiff, BufferDiffSnapshot}; -use editor::{Editor, EditorEvent, MultiBuffer, SelectionEffects, multibuffer_context_lines}; +use editor::{ + Editor, EditorEvent, MultiBuffer, MultiBufferOffset, SelectionEffects, + multibuffer_context_lines, +}; use git::repository::{CommitDetails, CommitDiff, RepoPath}; use gpui::{ Action, AnyElement, AnyView, App, AppContext as _, AsyncApp, AsyncWindowContext, Context, @@ -187,7 +190,7 @@ impl CommitView { editor.update(cx, |editor, cx| { editor.disable_header_for_buffer(metadata_buffer_id.unwrap(), cx); editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { - selections.select_ranges(vec![0..0]); + selections.select_ranges(vec![MultiBufferOffset(0)..MultiBufferOffset(0)]); }); }); } diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index cd56473dceb48d3c7da3629818f06d79d656ee03..ad77820078d43bc72be12ff358f96b5f4edaea0e 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -13,7 +13,8 @@ use anyhow::Context as _; use askpass::AskPassDelegate; use db::kvp::KEY_VALUE_STORE; use editor::{ - Direction, Editor, EditorElement, EditorMode, MultiBuffer, actions::ExpandAllDiffHunks, + Direction, Editor, EditorElement, EditorMode, MultiBuffer, MultiBufferOffset, + actions::ExpandAllDiffHunks, }; use futures::StreamExt as _; use git::blame::ParsedCommitMessage; @@ -1551,7 +1552,10 @@ impl GitPanel { window: &mut Window, cx: &mut Context, ) -> Option { - let git_commit_language = self.commit_editor.read(cx).language_at(0, cx); + let git_commit_language = self + .commit_editor + .read(cx) + .language_at(MultiBufferOffset(0), cx); let message = self.commit_editor.read(cx).text(cx); if message.is_empty() { return self diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 6f8195c8b718640de4fed421253d5f1bd2f8f14e..041d8381e92d59d9ef572f26cbc380abdf2d30e5 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/crates/git_ui/src/project_diff.rs @@ -520,7 +520,8 @@ impl ProjectDiff { if was_empty { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { // TODO select the very beginning (possibly inside a deletion) - selections.select_ranges([0..0]) + selections + .select_ranges([multi_buffer::Anchor::min()..multi_buffer::Anchor::min()]) }); } if is_excerpt_newly_added diff --git a/crates/git_ui/src/text_diff_view.rs b/crates/git_ui/src/text_diff_view.rs index 28eafaf4992667966832eaadd77a2babced7d66c..0975df9402f5b8e8db8bcfa83f2d31272d9983eb 100644 --- a/crates/git_ui/src/text_diff_view.rs +++ b/crates/git_ui/src/text_diff_view.rs @@ -446,7 +446,7 @@ impl Render for TextDiffView { #[cfg(test)] mod tests { use super::*; - use editor::test::editor_test_context::assert_state_with_diff; + use editor::{MultiBufferOffset, test::editor_test_context::assert_state_with_diff}; use gpui::{TestAppContext, VisualContext}; use project::{FakeFs, Project}; use serde_json::json; @@ -691,7 +691,11 @@ mod tests { let (unmarked_text, selection_ranges) = marked_text_ranges(editor_text, false); editor.set_text(unmarked_text, window, cx); editor.change_selections(Default::default(), window, cx, |s| { - s.select_ranges(selection_ranges) + s.select_ranges( + selection_ranges + .into_iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)), + ) }); editor diff --git a/crates/go_to_line/src/cursor_position.rs b/crates/go_to_line/src/cursor_position.rs index 286faa0b8d0185bbdb9b488fd8502cb7566dc388..042d9a46b6c76a461e60d9002a2362190e253cd4 100644 --- a/crates/go_to_line/src/cursor_position.rs +++ b/crates/go_to_line/src/cursor_position.rs @@ -1,4 +1,4 @@ -use editor::{Editor, EditorEvent, MultiBufferSnapshot}; +use editor::{Editor, EditorEvent, MBTextSummary, MultiBufferSnapshot}; use gpui::{App, Entity, FocusHandle, Focusable, Styled, Subscription, Task, WeakEntity}; use settings::{RegisterSetting, Settings}; use std::{fmt::Write, num::NonZeroU32, time::Duration}; @@ -55,7 +55,7 @@ impl UserCaretPosition { let line_start = Point::new(selection_end.row, 0); let chars_to_last_position = snapshot - .text_summary_for_range::(line_start..selection_end) + .text_summary_for_range::(line_start..selection_end) .chars as u32; (selection_end.row, chars_to_last_position) }; @@ -116,7 +116,7 @@ impl CursorPosition { for selection in editor.selections.all_adjusted(&snapshot) { let selection_summary = snapshot .buffer_snapshot() - .text_summary_for_range::( + .text_summary_for_range::( selection.start..selection.end, ); cursor_position.selected_count.characters += diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 2e30b91dab833d18f5fc9c35ad7ea4934d197fa8..f43949c0051f56559388203e387a540b8c593467 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -159,7 +159,7 @@ pub fn new_journal_entry(workspace: &Workspace, window: &mut Window, cx: &mut Ap cx, |s| s.select_ranges([len..len]), ); - if len > 0 { + if len.0 > 0 { editor.insert("\n\n", window, cx); } editor.insert(&entry_heading, window, cx); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index f46fc0db0a171349456b2c29a1c9fff556daee2d..95db651350a2e1c703ce0ab52c77f075a83a0500 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2083,7 +2083,7 @@ impl Buffer { } /// Gets a [`Subscription`] that tracks all of the changes to the buffer's text. - pub fn subscribe(&mut self) -> Subscription { + pub fn subscribe(&mut self) -> Subscription { self.text.subscribe() } diff --git a/crates/language_tools/src/lsp_log_view.rs b/crates/language_tools/src/lsp_log_view.rs index d480eadc73b9546e5a59b204b036a3ff88a018c7..9b52912b8ed09ce2dd7a4f2ea26f7106bfd11c31 100644 --- a/crates/language_tools/src/lsp_log_view.rs +++ b/crates/language_tools/src/lsp_log_view.rs @@ -1,6 +1,6 @@ use collections::VecDeque; use copilot::Copilot; -use editor::{Editor, EditorEvent, actions::MoveToEnd, scroll::Autoscroll}; +use editor::{Editor, EditorEvent, MultiBufferOffset, actions::MoveToEnd, scroll::Autoscroll}; use gpui::{ AnyView, App, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window, actions, div, @@ -231,7 +231,7 @@ impl LspLogView { let last_offset = editor.buffer().read(cx).len(cx); let newest_cursor_is_at_end = editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .start >= last_offset; editor.edit( diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index e2a0cd4c33a93b7806710e68abca6404290808ce..885f6bed327c765019ae166e21eab112f884e7dd 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -1,5 +1,5 @@ use command_palette_hooks::CommandPaletteFilter; -use editor::{Anchor, Editor, ExcerptId, SelectionEffects, scroll::Autoscroll}; +use editor::{Anchor, Editor, ExcerptId, MultiBufferOffset, SelectionEffects, scroll::Autoscroll}; use gpui::{ App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, @@ -254,7 +254,7 @@ impl SyntaxTreeView { let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| { let selection_range = editor .selections - .last::(&editor.display_snapshot(cx)) + .last::(&editor.display_snapshot(cx)) .range(); let multi_buffer = editor.buffer().read(cx); let (buffer, range, excerpt_id) = snapshot @@ -308,8 +308,8 @@ impl SyntaxTreeView { // Within the active layer, find the syntax node under the cursor, // and scroll to it. let mut cursor = layer.node().walk(); - while cursor.goto_first_child_for_byte(range.start).is_some() { - if !range.is_empty() && cursor.node().end_byte() == range.start { + while cursor.goto_first_child_for_byte(range.start.0).is_some() { + if !range.is_empty() && cursor.node().end_byte() == range.start.0 { cursor.goto_next_sibling(); } } @@ -317,7 +317,7 @@ impl SyntaxTreeView { // Ascend to the smallest ancestor that contains the range. loop { let node_range = cursor.node().byte_range(); - if node_range.start <= range.start && node_range.end >= range.end { + if node_range.start <= range.start.0 && node_range.end >= range.end.0 { break; } if !cursor.goto_parent() { diff --git a/crates/markdown_preview/src/markdown_preview_view.rs b/crates/markdown_preview/src/markdown_preview_view.rs index f62ff0874df8079f44868dfeaa1ad2fd0348e474..c4d3c033df6395235603837bf0944eeb59d3dfbc 100644 --- a/crates/markdown_preview/src/markdown_preview_view.rs +++ b/crates/markdown_preview/src/markdown_preview_view.rs @@ -4,7 +4,7 @@ use std::{ops::Range, path::PathBuf}; use anyhow::Result; use editor::scroll::Autoscroll; -use editor::{Editor, EditorEvent, SelectionEffects}; +use editor::{Editor, EditorEvent, MultiBufferOffset, SelectionEffects}; use gpui::{ App, ClickEvent, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, IsZero, ListState, ParentElement, Render, RetainAllImageCache, Styled, @@ -281,7 +281,7 @@ impl MarkdownPreviewView { let selection_range = editor.update(cx, |editor, cx| { editor .selections - .last::(&editor.display_snapshot(cx)) + .last::(&editor.display_snapshot(cx)) .range() }); this.selected_block = this.get_block_index_under_cursor(selection_range); @@ -358,7 +358,7 @@ impl MarkdownPreviewView { &self, window: &mut Window, cx: &mut Context, - selection: Range, + selection: Range, ) { if let Some(state) = &self.active_editor { state.editor.update(cx, |editor, cx| { @@ -375,7 +375,7 @@ impl MarkdownPreviewView { /// The absolute path of the file that is currently being previewed. fn get_folder_for_active_editor(editor: &Editor, cx: &App) -> Option { - if let Some(file) = editor.file_at(0, cx) { + if let Some(file) = editor.file_at(MultiBufferOffset(0), cx) { if let Some(file) = file.as_local() { file.abs_path(cx).parent().map(|p| p.to_path_buf()) } else { @@ -386,9 +386,9 @@ impl MarkdownPreviewView { } } - fn get_block_index_under_cursor(&self, selection_range: Range) -> usize { + fn get_block_index_under_cursor(&self, selection_range: Range) -> usize { let mut block_index = None; - let cursor = selection_range.start; + let cursor = selection_range.start.0; let mut last_end = 0; if let Some(content) = &self.contents { @@ -524,7 +524,15 @@ impl Render for MarkdownPreviewView { if e.checked() { "[x]" } else { "[ ]" }; editor.edit( - vec![(e.source_range(), task_marker)], + vec![( + MultiBufferOffset( + e.source_range().start, + ) + ..MultiBufferOffset( + e.source_range().end, + ), + task_marker, + )], cx, ); }); @@ -564,7 +572,8 @@ impl Render for MarkdownPreviewView { this.move_cursor_to_block( window, cx, - source_range.start..source_range.start, + MultiBufferOffset(source_range.start) + ..MultiBufferOffset(source_range.start), ); } }, diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index d5009172084d6d683f722a8ad2aa5b8b21ae0493..57b5244b3f276265c31f1431701a2bd7d8e59aef 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -1,8 +1,10 @@ +use crate::{MultiBufferDimension, MultiBufferOffset, MultiBufferOffsetUtf16}; + use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint}; -use language::{OffsetUtf16, Point, TextDimension}; +use language::Point; use std::{ cmp::Ordering, - ops::{Range, Sub}, + ops::{AddAssign, Range, Sub}, }; use sum_tree::Bias; use text::BufferId; @@ -162,7 +164,11 @@ impl Anchor { pub fn summary(&self, snapshot: &MultiBufferSnapshot) -> D where - D: TextDimension + Ord + Sub, + D: MultiBufferDimension + + Ord + + Sub + + AddAssign, + D::TextDimension: Sub + Ord, { snapshot.summary_for_anchor(self) } @@ -182,10 +188,10 @@ impl Anchor { } impl ToOffset for Anchor { - fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize { + fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset { self.summary(snapshot) } - fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 { self.summary(snapshot) } } @@ -203,7 +209,7 @@ pub trait AnchorRangeExt { fn cmp(&self, other: &Range, buffer: &MultiBufferSnapshot) -> Ordering; fn includes(&self, other: &Range, buffer: &MultiBufferSnapshot) -> bool; fn overlaps(&self, other: &Range, buffer: &MultiBufferSnapshot) -> bool; - fn to_offset(&self, content: &MultiBufferSnapshot) -> Range; + fn to_offset(&self, content: &MultiBufferSnapshot) -> Range; fn to_point(&self, content: &MultiBufferSnapshot) -> Range; } @@ -223,7 +229,7 @@ impl AnchorRangeExt for Range { self.end.cmp(&other.start, buffer).is_ge() && self.start.cmp(&other.end, buffer).is_le() } - fn to_offset(&self, content: &MultiBufferSnapshot) -> Range { + fn to_offset(&self, content: &MultiBufferSnapshot) -> Range { self.start.to_offset(content)..self.end.to_offset(content) } @@ -231,6 +237,3 @@ impl AnchorRangeExt for Range { self.start.to_point(content)..self.end.to_point(content) } } - -#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)] -pub struct Offset(pub usize); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 43def73ae257e29f007ef56fb181e03432023edb..c1a2fed7e2a253d3469944a7f2c4fa2275c8abd4 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -7,7 +7,7 @@ mod transaction; use self::transaction::History; -pub use anchor::{Anchor, AnchorRangeExt, Offset}; +pub use anchor::{Anchor, AnchorRangeExt}; pub use position::{TypedOffset, TypedPoint, TypedRow}; use anyhow::{Result, anyhow}; @@ -43,13 +43,13 @@ use std::{ io, iter::{self, FromIterator}, mem, - ops::{Range, RangeBounds, Sub}, + ops::{self, AddAssign, Range, RangeBounds, Sub}, rc::Rc, str, sync::Arc, time::Duration, }; -use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, Summary, TreeMap}; +use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap}; use text::{ BufferId, Edit, LineIndent, TextSummary, locator::Locator, @@ -78,7 +78,7 @@ pub struct MultiBuffer { paths_by_excerpt: HashMap, /// Mapping from buffer IDs to their diff states diffs: HashMap, - subscriptions: Topic, + subscriptions: Topic, /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`] singleton: bool, /// The history of the multi-buffer. @@ -138,7 +138,7 @@ pub struct MultiBufferDiffHunk { /// The excerpt that contains the diff hunk. pub excerpt_id: ExcerptId, /// The range within the buffer's diff base that this hunk corresponds to. - pub diff_base_byte_range: Range, + pub diff_base_byte_range: Range, /// Whether or not this hunk also appears in the 'secondary diff'. pub secondary_status: DiffHunkSecondaryStatus, } @@ -159,7 +159,7 @@ impl MultiBufferDiffHunk { } pub fn is_created_file(&self) -> bool { - self.diff_base_byte_range == (0..0) + self.diff_base_byte_range == (BufferOffset(0)..BufferOffset(0)) && self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX) } @@ -183,7 +183,7 @@ impl MultiBufferRow { pub const MAX: Self = Self(u32::MAX); } -impl std::ops::Add for MultiBufferRow { +impl ops::Add for MultiBufferRow { type Output = Self; fn add(self, rhs: usize) -> Self::Output { @@ -191,9 +191,305 @@ impl std::ops::Add for MultiBufferRow { } } +pub trait MultiBufferDimension: 'static + Copy + Default + std::fmt::Debug { + type TextDimension: TextDimension; + fn from_summary(summary: &MBTextSummary) -> Self; + + fn add_text_dim(&mut self, summary: &Self::TextDimension); + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary); +} + +// todo(lw): MultiBufferPoint +impl MultiBufferDimension for Point { + type TextDimension = Point; + fn from_summary(summary: &MBTextSummary) -> Self { + summary.lines + } + + fn add_text_dim(&mut self, other: &Self::TextDimension) { + *self += *other; + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + *self += summary.lines; + } +} + +// todo(lw): MultiBufferPointUtf16 +impl MultiBufferDimension for PointUtf16 { + type TextDimension = PointUtf16; + fn from_summary(summary: &MBTextSummary) -> Self { + summary.lines_utf16() + } + + fn add_text_dim(&mut self, other: &Self::TextDimension) { + *self += *other; + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + *self += summary.lines_utf16(); + } +} + +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)] +pub struct MultiBufferOffset(pub usize); + +impl fmt::Display for MultiBufferOffset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl rand::distr::uniform::SampleUniform for MultiBufferOffset { + type Sampler = MultiBufferOffsetUniformSampler; +} + +pub struct MultiBufferOffsetUniformSampler { + sampler: rand::distr::uniform::UniformUsize, +} + +impl rand::distr::uniform::UniformSampler for MultiBufferOffsetUniformSampler { + type X = MultiBufferOffset; + + fn new(low_b: B1, high_b: B2) -> Result + where + B1: rand::distr::uniform::SampleBorrow + Sized, + B2: rand::distr::uniform::SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + let sampler = rand::distr::uniform::UniformUsize::new(low.0, high.0); + sampler.map(|sampler| MultiBufferOffsetUniformSampler { sampler }) + } + + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new_inclusive(low_b: B1, high_b: B2) -> Result + where + B1: rand::distr::uniform::SampleBorrow + Sized, + B2: rand::distr::uniform::SampleBorrow + Sized, + { + let low = *low_b.borrow(); + let high = *high_b.borrow(); + let sampler = rand::distr::uniform::UniformUsize::new_inclusive(low.0, high.0); + sampler.map(|sampler| MultiBufferOffsetUniformSampler { sampler }) + } + + fn sample(&self, rng: &mut R) -> Self::X { + MultiBufferOffset(self.sampler.sample(rng)) + } +} +impl MultiBufferDimension for MultiBufferOffset { + type TextDimension = usize; + fn from_summary(summary: &MBTextSummary) -> Self { + summary.len + } + + fn add_text_dim(&mut self, other: &Self::TextDimension) { + self.0 += *other; + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + *self += summary.len; + } +} +impl MultiBufferDimension for MultiBufferOffsetUtf16 { + type TextDimension = OffsetUtf16; + fn from_summary(summary: &MBTextSummary) -> Self { + MultiBufferOffsetUtf16(summary.len_utf16) + } + + fn add_text_dim(&mut self, other: &Self::TextDimension) { + self.0 += *other; + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + self.0 += summary.len_utf16; + } +} + +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)] +pub struct BufferOffset(pub usize); + +impl TextDimension for BufferOffset { + fn from_text_summary(summary: &TextSummary) -> Self { + BufferOffset(usize::from_text_summary(summary)) + } + fn from_chunk(chunk: rope::ChunkSlice) -> Self { + BufferOffset(usize::from_chunk(chunk)) + } + fn add_assign(&mut self, other: &Self) { + TextDimension::add_assign(&mut self.0, &other.0); + } +} +impl<'a> sum_tree::Dimension<'a, rope::ChunkSummary> for BufferOffset { + fn zero(cx: ()) -> Self { + BufferOffset(>::zero(cx)) + } + + fn add_summary(&mut self, summary: &'a rope::ChunkSummary, cx: ()) { + usize::add_summary(&mut self.0, summary, cx); + } +} + +impl Sub for BufferOffset { + type Output = usize; + + fn sub(self, other: BufferOffset) -> Self::Output { + self.0 - other.0 + } +} + +impl AddAssign> for BufferOffset { + fn add_assign(&mut self, other: DimensionPair) { + self.0 += other.key; + } +} + +impl language::ToPoint for BufferOffset { + fn to_point(&self, snapshot: &text::BufferSnapshot) -> Point { + self.0.to_point(snapshot) + } +} + +impl language::ToPointUtf16 for BufferOffset { + fn to_point_utf16(&self, snapshot: &text::BufferSnapshot) -> PointUtf16 { + self.0.to_point_utf16(snapshot) + } +} + +impl language::ToOffset for BufferOffset { + fn to_offset(&self, snapshot: &text::BufferSnapshot) -> usize { + self.0.to_offset(snapshot) + } +} + +impl language::ToOffsetUtf16 for BufferOffset { + fn to_offset_utf16(&self, snapshot: &text::BufferSnapshot) -> OffsetUtf16 { + self.0.to_offset_utf16(snapshot) + } +} + +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] +pub struct MultiBufferOffsetUtf16(pub OffsetUtf16); + +impl ops::Add for MultiBufferOffsetUtf16 { + type Output = MultiBufferOffsetUtf16; + + fn add(self, rhs: usize) -> Self::Output { + MultiBufferOffsetUtf16(OffsetUtf16(self.0.0 + rhs)) + } +} + +impl AddAssign for MultiBufferOffsetUtf16 { + fn add_assign(&mut self, rhs: OffsetUtf16) { + self.0 += rhs; + } +} + +impl AddAssign for MultiBufferOffsetUtf16 { + fn add_assign(&mut self, rhs: usize) { + self.0.0 += rhs; + } +} + +impl Sub for MultiBufferOffsetUtf16 { + type Output = OffsetUtf16; + + fn sub(self, other: MultiBufferOffsetUtf16) -> Self::Output { + self.0 - other.0 + } +} + +#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] +pub struct BufferOffsetUtf16(pub OffsetUtf16); + +impl MultiBufferOffset { + const ZERO: Self = Self(0); + pub fn saturating_sub(self, other: MultiBufferOffset) -> usize { + self.0.saturating_sub(other.0) + } + pub fn saturating_sub_usize(self, other: usize) -> MultiBufferOffset { + MultiBufferOffset(self.0.saturating_sub(other)) + } +} + +impl ops::Sub for MultiBufferOffset { + type Output = usize; + + fn sub(self, other: MultiBufferOffset) -> Self::Output { + self.0 - other.0 + } +} + +impl ops::Sub for MultiBufferOffset { + type Output = Self; + + fn sub(self, other: usize) -> Self::Output { + MultiBufferOffset(self.0 - other) + } +} + +impl ops::SubAssign for MultiBufferOffset { + fn sub_assign(&mut self, other: usize) { + self.0 -= other; + } +} + +impl ops::Add for BufferOffset { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + BufferOffset(self.0 + rhs) + } +} + +impl ops::AddAssign for BufferOffset { + fn add_assign(&mut self, other: usize) { + self.0 += other; + } +} + +impl ops::Add for MultiBufferOffset { + type Output = Self; + + fn add(self, rhs: usize) -> Self::Output { + MultiBufferOffset(self.0 + rhs) + } +} + +impl ops::AddAssign for MultiBufferOffset { + fn add_assign(&mut self, other: usize) { + self.0 += other; + } +} + +impl ops::Add for MultiBufferOffset { + type Output = Self; + + fn add(self, rhs: isize) -> Self::Output { + MultiBufferOffset((self.0 as isize + rhs) as usize) + } +} + +impl ops::Add for MultiBufferOffset { + type Output = Self; + + fn add(self, rhs: MultiBufferOffset) -> Self::Output { + MultiBufferOffset(self.0 + rhs.0) + } +} + +impl ops::AddAssign for MultiBufferOffset { + fn add_assign(&mut self, other: MultiBufferOffset) { + self.0 += other.0; + } +} + pub trait ToOffset: 'static + fmt::Debug { - fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; - fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; + fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset; + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16; } pub trait ToPoint: 'static + fmt::Debug { @@ -255,7 +551,7 @@ pub struct MultiBufferSnapshot { #[derive(Debug, Clone)] enum DiffTransform { BufferContent { - summary: TextSummary, + summary: MBTextSummary, inserted_hunk_info: Option, }, DeletedHunk { @@ -370,10 +666,12 @@ struct Excerpt { #[derive(Clone)] pub struct MultiBufferExcerpt<'a> { excerpt: &'a Excerpt, - diff_transforms: sum_tree::Cursor<'a, 'static, DiffTransform, DiffTransforms>, - offset: usize, - excerpt_offset: ExcerptDimension, - buffer_offset: usize, + diff_transforms: + sum_tree::Cursor<'a, 'static, DiffTransform, DiffTransforms>, + offset: MultiBufferOffset, + // todo unsure about this type + excerpt_offset: MultiBufferOffset, + buffer_offset: BufferOffset, } #[derive(Clone, Debug)] @@ -408,13 +706,153 @@ pub struct ExcerptSummary { /// The location of the last [`Excerpt`] being summarized excerpt_locator: Locator, widest_line_number: u32, - text: TextSummary, + text: MBTextSummary, } #[derive(Debug, Clone)] pub struct DiffTransformSummary { - input: TextSummary, - output: TextSummary, + input: MBTextSummary, + output: MBTextSummary, +} + +/// Summary of a string of text. +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct MBTextSummary { + /// Length in bytes. + pub len: MultiBufferOffset, + /// Length in UTF-8. + pub chars: usize, + /// Length in UTF-16 code units + pub len_utf16: OffsetUtf16, + /// A point representing the number of lines and the length of the last line. + /// + /// In other words, it marks the point after the last byte in the text, (if + /// EOF was a character, this would be its position). + pub lines: Point, + /// How many `char`s are in the first line + pub first_line_chars: u32, + /// How many `char`s are in the last line + pub last_line_chars: u32, + /// How many UTF-16 code units are in the last line + pub last_line_len_utf16: u32, + /// The row idx of the longest row + pub longest_row: u32, + /// How many `char`s are in the longest row + pub longest_row_chars: u32, +} + +impl From for MBTextSummary { + fn from(summary: TextSummary) -> Self { + MBTextSummary { + len: MultiBufferOffset(summary.len), + chars: summary.chars, + len_utf16: summary.len_utf16, + lines: summary.lines, + first_line_chars: summary.first_line_chars, + last_line_chars: summary.last_line_chars, + last_line_len_utf16: summary.last_line_len_utf16, + longest_row: summary.longest_row, + longest_row_chars: summary.longest_row_chars, + } + } +} +impl From<&str> for MBTextSummary { + fn from(text: &str) -> Self { + MBTextSummary::from(TextSummary::from(text)) + } +} + +impl MultiBufferDimension for MBTextSummary { + type TextDimension = TextSummary; + + fn from_summary(summary: &MBTextSummary) -> Self { + *summary + } + + fn add_text_dim(&mut self, summary: &Self::TextDimension) { + *self += *summary; + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + *self += *summary; + } +} + +impl AddAssign for MBTextSummary { + fn add_assign(&mut self, other: MBTextSummary) { + let joined_chars = self.last_line_chars + other.first_line_chars; + if joined_chars > self.longest_row_chars { + self.longest_row = self.lines.row; + self.longest_row_chars = joined_chars; + } + if other.longest_row_chars > self.longest_row_chars { + self.longest_row = self.lines.row + other.longest_row; + self.longest_row_chars = other.longest_row_chars; + } + + if self.lines.row == 0 { + self.first_line_chars += other.first_line_chars; + } + + if other.lines.row == 0 { + self.last_line_chars += other.first_line_chars; + self.last_line_len_utf16 += other.last_line_len_utf16; + } else { + self.last_line_chars = other.last_line_chars; + self.last_line_len_utf16 = other.last_line_len_utf16; + } + + self.chars += other.chars; + self.len += other.len; + self.len_utf16 += other.len_utf16; + self.lines += other.lines; + } +} + +impl AddAssign for MBTextSummary { + fn add_assign(&mut self, other: TextSummary) { + *self += MBTextSummary::from(other); + } +} + +impl MBTextSummary { + pub fn lines_utf16(&self) -> PointUtf16 { + PointUtf16 { + row: self.lines.row, + column: self.last_line_len_utf16, + } + } +} + +impl MultiBufferDimension for DimensionPair +where + K: MultiBufferDimension, + V: MultiBufferDimension, +{ + type TextDimension = DimensionPair; + + fn from_summary(summary: &MBTextSummary) -> Self { + Self { + key: K::from_summary(summary), + value: Some(V::from_summary(summary)), + } + } + + fn add_text_dim(&mut self, summary: &Self::TextDimension) { + self.key.add_text_dim(&summary.key); + if let Some(value) = &mut self.value { + if let Some(other_value) = summary.value.as_ref() { + value.add_text_dim(other_value); + } + } + } + + fn add_mb_text_summary(&mut self, summary: &MBTextSummary) { + self.key.add_mb_text_summary(summary); + if let Some(value) = &mut self.value { + value.add_mb_text_summary(summary); + } + } } #[derive(Clone)] @@ -422,53 +860,54 @@ pub struct MultiBufferRows<'a> { point: Point, is_empty: bool, is_singleton: bool, - cursor: MultiBufferCursor<'a, Point>, + cursor: MultiBufferCursor<'a, Point, Point>, } pub struct MultiBufferChunks<'a> { excerpts: Cursor<'a, 'static, Excerpt, ExcerptOffset>, - diff_transforms: Cursor<'a, 'static, DiffTransform, Dimensions>, + diff_transforms: + Cursor<'a, 'static, DiffTransform, Dimensions>, diffs: &'a TreeMap, diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>, buffer_chunk: Option>, - range: Range, + range: Range, excerpt_offset_range: Range, excerpt_chunks: Option>, language_aware: bool, } pub struct ReversedMultiBufferChunks<'a> { - cursor: MultiBufferCursor<'a, usize>, + cursor: MultiBufferCursor<'a, MultiBufferOffset, BufferOffset>, current_chunks: Option>, - start: usize, - offset: usize, + start: MultiBufferOffset, + offset: MultiBufferOffset, } pub struct MultiBufferBytes<'a> { - range: Range, - cursor: MultiBufferCursor<'a, usize>, + range: Range, + cursor: MultiBufferCursor<'a, MultiBufferOffset, BufferOffset>, excerpt_bytes: Option>, has_trailing_newline: bool, chunk: &'a [u8], } pub struct ReversedMultiBufferBytes<'a> { - range: Range, + range: Range, chunks: ReversedMultiBufferChunks<'a>, chunk: &'a [u8], } #[derive(Clone)] -struct DiffTransforms { - output_dimension: OutputDimension, - excerpt_dimension: ExcerptDimension, +struct DiffTransforms { + output_dimension: OutputDimension, + excerpt_dimension: ExcerptDimension, } -impl<'a, D: TextDimension> Dimension<'a, DiffTransformSummary> for DiffTransforms { +impl<'a, MBD: MultiBufferDimension> Dimension<'a, DiffTransformSummary> for DiffTransforms { fn zero(cx: ::Context<'_>) -> Self { Self { output_dimension: OutputDimension::zero(cx), - excerpt_dimension: as Dimension<'a, DiffTransformSummary>>::zero( + excerpt_dimension: as Dimension<'a, DiffTransformSummary>>::zero( cx, ), } @@ -485,21 +924,21 @@ impl<'a, D: TextDimension> Dimension<'a, DiffTransformSummary> for DiffTransform } #[derive(Clone)] -struct MultiBufferCursor<'a, D: TextDimension> { - excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension>, - diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms>, +struct MultiBufferCursor<'a, MBD, BD> { + excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension>, + diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms>, diffs: &'a TreeMap, - cached_region: Option>, + cached_region: Option>, } #[derive(Clone)] -struct MultiBufferRegion<'a, D: TextDimension> { +struct MultiBufferRegion<'a, MBD, BD> { buffer: &'a BufferSnapshot, is_main_buffer: bool, diff_hunk_status: Option, excerpt: &'a Excerpt, - buffer_range: Range, - range: Range, + buffer_range: Range, + range: Range, has_trailing_newline: bool, } @@ -511,7 +950,7 @@ struct ExcerptChunks<'a> { #[derive(Debug)] struct BufferEdit { - range: Range, + range: Range, new_text: Arc, is_insertion: bool, original_indent_column: Option, @@ -693,7 +1132,7 @@ impl MultiBuffer { self.singleton } - pub fn subscribe(&mut self) -> Subscription { + pub fn subscribe(&mut self) -> Subscription { self.subscriptions.subscribe() } @@ -711,7 +1150,7 @@ impl MultiBuffer { // The `is_empty` signature doesn't match what clippy expects #[allow(clippy::len_without_is_empty)] - pub fn len(&self, cx: &App) -> usize { + pub fn len(&self, cx: &App) -> MultiBufferOffset { self.read(cx).len() } @@ -750,7 +1189,7 @@ impl MultiBuffer { // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. fn edit_internal( this: &mut MultiBuffer, - edits: Vec<(Range, Arc)>, + edits: Vec<(Range, Arc)>, mut autoindent_mode: Option, cx: &mut Context, ) { @@ -849,13 +1288,13 @@ impl MultiBuffer { } fn convert_edits_to_buffer_edits( - edits: Vec<(Range, Arc)>, + edits: Vec<(Range, Arc)>, snapshot: &MultiBufferSnapshot, original_indent_columns: &[Option], ) -> (HashMap>, Vec) { let mut buffer_edits: HashMap> = Default::default(); let mut edited_excerpt_ids = Vec::new(); - let mut cursor = snapshot.cursor::(); + let mut cursor = snapshot.cursor::(); for (ix, (range, new_text)) in edits.into_iter().enumerate() { let original_indent_column = original_indent_columns.get(ix).copied().flatten(); @@ -994,7 +1433,7 @@ impl MultiBuffer { fn autoindent_ranges_internal( this: &mut MultiBuffer, - edits: Vec<(Range, Arc)>, + edits: Vec<(Range, Arc)>, cx: &mut Context, ) { let (buffer_edits, edited_excerpt_ids) = @@ -1005,7 +1444,7 @@ impl MultiBuffer { buffer_ids.push(buffer_id); edits.sort_unstable_by_key(|edit| edit.range.start); - let mut ranges: Vec> = Vec::new(); + let mut ranges: Vec> = Vec::new(); for edit in edits { if let Some(last_range) = ranges.last_mut() && edit.range.start <= last_range.end @@ -1243,7 +1682,7 @@ impl MultiBuffer { let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right); prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone(); - let edit_start = ExcerptOffset::new(new_excerpts.summary().text.len); + let edit_start = ExcerptOffset::new(new_excerpts.summary().text.len.0); new_excerpts.update_last( |excerpt| { excerpt.has_trailing_newline = true; @@ -1287,7 +1726,7 @@ impl MultiBuffer { new_excerpt_ids.push(ExcerptIdMapping { id, locator }, ()); } - let edit_end = ExcerptOffset::new(new_excerpts.summary().text.len); + let edit_end = ExcerptOffset::new(new_excerpts.summary().text.len.0); let suffix = cursor.suffix(); let changed_trailing_excerpt = suffix.is_empty(); @@ -1345,7 +1784,7 @@ impl MultiBuffer { show_headers: _, } = self.snapshot.get_mut(); let start = ExcerptOffset::new(0); - let prev_len = ExcerptOffset::new(excerpts.summary().text.len); + let prev_len = ExcerptOffset::new(excerpts.summary().text.len.0); *excerpts = Default::default(); *trailing_excerpt_update_count += 1; *is_dirty = false; @@ -1416,7 +1855,7 @@ impl MultiBuffer { if let Some(excerpt) = excerpts.item() && excerpt.locator == *locator { - let excerpt_start = excerpts.start().1.clone(); + let excerpt_start = excerpts.start().1; let excerpt_end = ExcerptDimension(excerpt_start.0 + excerpt.text_summary.lines); diff_transforms.seek_forward(&excerpt_start, Bias::Left); @@ -1459,7 +1898,7 @@ impl MultiBuffer { let snapshot = self.read(cx); let offset = position.to_offset(&snapshot); - let mut cursor = snapshot.cursor::(); + let mut cursor = snapshot.cursor::(); cursor.seek(&offset); cursor .excerpt() @@ -1487,7 +1926,7 @@ impl MultiBuffer { &self, point: T, cx: &App, - ) -> Option<(Entity, usize)> { + ) -> Option<(Entity, BufferOffset)> { let snapshot = self.read(cx); let (buffer, offset) = snapshot.point_to_buffer_offset(point)?; Some(( @@ -1631,7 +2070,7 @@ impl MultiBuffer { // Push an edit for the removal of this run of excerpts. let old_end = cursor.start().1; - let new_start = ExcerptOffset::new(new_excerpts.summary().text.len); + let new_start = ExcerptOffset::new(new_excerpts.summary().text.len.0); edits.push(Edit { old: old_start..old_end, new: new_start..new_start, @@ -1855,7 +2294,7 @@ impl MultiBuffer { let buffer = buffer.read(cx); language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx) }) - .unwrap_or_else(move || self.language_settings_at(0, cx)) + .unwrap_or_else(move || self.language_settings_at(MultiBufferOffset::default(), cx)) } pub fn language_settings_at<'a, T: ToOffset>( @@ -2007,7 +2446,7 @@ impl MultiBuffer { pub fn single_hunk_is_expanded(&self, range: Range, cx: &App) -> bool { let snapshot = self.read(cx); - let mut cursor = snapshot.diff_transforms.cursor::(()); + let mut cursor = snapshot.diff_transforms.cursor::(()); let offset_range = range.to_offset(&snapshot); cursor.seek(&offset_range.start, Bias::Left); while let Some(item) = cursor.item() { @@ -2024,13 +2463,13 @@ impl MultiBuffer { pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range], cx: &App) -> bool { let snapshot = self.read(cx); - let mut cursor = snapshot.diff_transforms.cursor::(()); + let mut cursor = snapshot.diff_transforms.cursor::(()); for range in ranges { let range = range.to_point(&snapshot); let start = snapshot.point_to_offset(Point::new(range.start.row, 0)); let end = snapshot.point_to_offset(Point::new(range.end.row + 1, 0)); - let start = start.saturating_sub(1); - let end = snapshot.len().min(end + 1); + let start = start.saturating_sub_usize(1); + let end = snapshot.len().min(end + 1usize); cursor.seek(&start, Bias::Right); while let Some(item) = cursor.item() { if *cursor.start() >= end { @@ -2149,7 +2588,7 @@ impl MultiBuffer { .buffer .text_summary_for_range(excerpt.range.context.clone()); - let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len); + let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len.0); let old_start_offset = cursor.start().1; let new_text_len = ExcerptOffset::new(excerpt.text_summary.len); let edit = Edit { @@ -2255,7 +2694,7 @@ impl MultiBuffer { .buffer .text_summary_for_range(excerpt.range.context.clone()); - let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len); + let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len.0); let old_start_offset = cursor.start().1; let new_text_len = ExcerptOffset::new(excerpt.text_summary.len); let edit = Edit { @@ -2328,7 +2767,7 @@ impl MultiBuffer { buffers: &HashMap, diffs: &HashMap, cx: &App, - ) -> Vec> { + ) -> Vec> { let MultiBufferSnapshot { excerpts, diffs: buffer_diff, @@ -2417,7 +2856,7 @@ impl MultiBuffer { .map(|edit| { let excerpt_old_start = cursor.start().1; let excerpt_new_start = - ExcerptOffset::new(new_excerpts.summary().text.len); + ExcerptOffset::new(new_excerpts.summary().text.len.0); let old_start = excerpt_old_start + ExcerptOffset::new(edit.old.start); let old_end = excerpt_old_start + ExcerptOffset::new(edit.old.end); let new_start = excerpt_new_start + ExcerptOffset::new(edit.new.start); @@ -2456,7 +2895,7 @@ impl MultiBuffer { snapshot: &mut MultiBufferSnapshot, excerpt_edits: Vec>, change_kind: DiffChangeKind, - ) -> Vec> { + ) -> Vec> { if excerpt_edits.is_empty() { return vec![]; } @@ -2464,7 +2903,7 @@ impl MultiBuffer { let mut excerpts = snapshot.excerpts.cursor::(()); let mut old_diff_transforms = snapshot .diff_transforms - .cursor::>(()); + .cursor::>(()); let mut new_diff_transforms = SumTree::default(); let mut old_expanded_hunks = HashSet::default(); let mut output_edits = Vec::new(); @@ -2496,7 +2935,8 @@ impl MultiBuffer { // Compute the start of the edit in output coordinates. let edit_start_overshoot = (edit.old.start - old_diff_transforms.start().0).value; let edit_old_start = old_diff_transforms.start().1 + edit_start_overshoot; - let edit_new_start = (edit_old_start as isize + output_delta) as usize; + let edit_new_start = + MultiBufferOffset((edit_old_start.0 as isize + output_delta) as usize); let changed_diff_hunks = Self::recompute_diff_transforms_for_edit( &edit, @@ -2588,7 +3028,10 @@ impl MultiBuffer { fn recompute_diff_transforms_for_edit( edit: &Edit>, excerpts: &mut Cursor>, - old_diff_transforms: &mut Cursor, usize>>, + old_diff_transforms: &mut Cursor< + DiffTransform, + Dimensions, MultiBufferOffset>, + >, new_diff_transforms: &mut SumTree, end_of_current_insert: &mut Option<(TypedOffset, DiffTransformHunkInfo)>, old_expanded_hunks: &mut HashSet, @@ -2807,7 +3250,7 @@ impl MultiBuffer { continue; } let summary_to_add = old_snapshot - .text_summary_for_excerpt_offset_range::(start_offset..end_offset); + .text_summary_for_excerpt_offset_range::(start_offset..end_offset); if !Self::extend_last_buffer_content_transform( new_transforms, @@ -2828,7 +3271,7 @@ impl MultiBuffer { fn extend_last_buffer_content_transform( new_transforms: &mut SumTree, new_inserted_hunk_info: Option, - summary_to_add: TextSummary, + summary_to_add: MBTextSummary, ) -> bool { let mut did_extend = false; new_transforms.update_last( @@ -2914,14 +3357,14 @@ impl MultiBuffer { use util::RandomCharIter; let snapshot = self.read(cx); - let mut edits: Vec<(Range, Arc)> = Vec::new(); + let mut edits: Vec<(Range, Arc)> = Vec::new(); let mut last_end = None; for _ in 0..edit_count { if last_end.is_some_and(|last_end| last_end >= snapshot.len()) { break; } - let new_start = last_end.map_or(0, |last_end| last_end + 1); + let new_start = last_end.map_or(MultiBufferOffset::ZERO, |last_end| last_end + 1usize); let end = snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right); let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right); @@ -3078,18 +3521,21 @@ impl EventEmitter for MultiBuffer {} impl MultiBufferSnapshot { pub fn text(&self) -> String { - self.chunks(0..self.len(), false) + self.chunks(MultiBufferOffset::ZERO..self.len(), false) .map(|chunk| chunk.text) .collect() } pub fn reversed_chars_at(&self, position: T) -> impl Iterator + '_ { - self.reversed_chunks_in_range(0..position.to_offset(self)) + self.reversed_chunks_in_range(MultiBufferOffset::ZERO..position.to_offset(self)) .flat_map(|c| c.chars().rev()) } - fn reversed_chunks_in_range(&self, range: Range) -> ReversedMultiBufferChunks<'_> { - let mut cursor = self.cursor::(); + fn reversed_chunks_in_range( + &self, + range: Range, + ) -> ReversedMultiBufferChunks<'_> { + let mut cursor = self.cursor::(); cursor.seek(&range.end); let current_chunks = cursor.region().as_ref().map(|region| { let start_overshoot = range.start.saturating_sub(region.range.start); @@ -3173,7 +3619,8 @@ impl MultiBufferSnapshot { buffer_id: excerpt.buffer_id, excerpt_id: excerpt.id, buffer_range: hunk.buffer_range.clone(), - diff_base_byte_range: hunk.diff_base_byte_range.clone(), + diff_base_byte_range: BufferOffset(hunk.diff_base_byte_range.start) + ..BufferOffset(hunk.diff_base_byte_range.end), secondary_status: hunk.secondary_status, }) }) @@ -3184,7 +3631,7 @@ impl MultiBufferSnapshot { range: Range, ) -> impl Iterator + '_ { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&range.start); std::iter::from_fn(move || { let region = cursor.region()?; @@ -3201,7 +3648,7 @@ impl MultiBufferSnapshot { range: Range, ) -> impl Iterator + '_ { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&range.start); std::iter::from_fn(move || { let region = cursor.region()?; @@ -3218,21 +3665,21 @@ impl MultiBufferSnapshot { pub fn ranges_to_buffer_ranges( &self, ranges: impl Iterator>, - ) -> impl Iterator, ExcerptId)> { + ) -> impl Iterator, ExcerptId)> { ranges.flat_map(|range| self.range_to_buffer_ranges(range).into_iter()) } pub fn range_to_buffer_ranges( &self, range: Range, - ) -> Vec<(&BufferSnapshot, Range, ExcerptId)> { + ) -> Vec<(&BufferSnapshot, Range, ExcerptId)> { let start = range.start.to_offset(self); let end = range.end.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&start); - let mut result: Vec<(&BufferSnapshot, Range, ExcerptId)> = Vec::new(); + let mut result: Vec<(&BufferSnapshot, Range, ExcerptId)> = Vec::new(); while let Some(region) = cursor.region() { if region.range.start > end { break; @@ -3264,11 +3711,18 @@ impl MultiBufferSnapshot { pub fn range_to_buffer_ranges_with_deleted_hunks( &self, range: Range, - ) -> impl Iterator, ExcerptId, Option)> + '_ { + ) -> impl Iterator< + Item = ( + &BufferSnapshot, + Range, + ExcerptId, + Option, + ), + > + '_ { let start = range.start.to_offset(self); let end = range.end.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&start); std::iter::from_fn(move || { @@ -3314,18 +3768,25 @@ impl MultiBufferSnapshot { /// /// The returned iterator yields each of these metadata items, paired with its range in /// multi-buffer coordinates. - fn lift_buffer_metadata<'a, D, M, I>( + fn lift_buffer_metadata<'a, MBD, M, I>( &'a self, - query_range: Range, - get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range) -> Option, - ) -> impl Iterator, M, &'a Excerpt)> + 'a + query_range: Range, + get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range) -> Option, + ) -> impl Iterator, M, &'a Excerpt)> + 'a where - I: Iterator, M)> + 'a, - D: TextDimension + Ord + Sub, + I: Iterator, M)> + 'a, + MBD: MultiBufferDimension + + Ord + + Sub + + ops::Add + + ops::AddAssign, + MBD::TextDimension: Sub + + ops::Add + + AddAssign + + Ord, { - let max_position = D::from_text_summary(&self.text_summary()); let mut current_excerpt_metadata: Option<(ExcerptId, I)> = None; - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); // Find the excerpt and buffer offset where the given range ends. cursor.seek(&query_range.end); @@ -3336,9 +3797,9 @@ impl MultiBufferSnapshot { let overshoot = if query_range.end > region.range.start { query_range.end - region.range.start } else { - D::default() + ::default() }; - buffer_end.add_assign(&overshoot); + buffer_end = buffer_end + overshoot; range_end = Some((region.excerpt.id, buffer_end)); break; } @@ -3348,7 +3809,7 @@ impl MultiBufferSnapshot { cursor.seek(&query_range.start); if let Some(region) = cursor.region().filter(|region| !region.is_main_buffer) - && region.range.start > D::zero(()) + && region.range.start > MBD::default() { cursor.prev() } @@ -3373,13 +3834,17 @@ impl MultiBufferSnapshot { buffer_start = region.buffer_range.start; if query_range.start > region.range.start { let overshoot = query_range.start - region.range.start; - buffer_start.add_assign(&overshoot); + buffer_start = buffer_start + overshoot; } buffer_start = buffer_start.min(region.buffer_range.end); } else { buffer_start = cursor.main_buffer_position()?; }; - let mut buffer_end = excerpt.range.context.end.summary::(&excerpt.buffer); + let mut buffer_end = excerpt + .range + .context + .end + .summary::(&excerpt.buffer); if let Some((end_excerpt_id, end_buffer_offset)) = range_end && excerpt.id == end_excerpt_id { @@ -3397,7 +3862,7 @@ impl MultiBufferSnapshot { { // Find the multibuffer regions that contain the start and end of // the metadata item's range. - if metadata_buffer_range.start > D::default() { + if metadata_buffer_range.start > ::default() { while let Some(region) = cursor.region() { if region.is_main_buffer && (region.buffer_range.end >= metadata_buffer_range.start @@ -3426,19 +3891,19 @@ impl MultiBufferSnapshot { if start_region.is_main_buffer && metadata_buffer_range.start > region_buffer_start { - start_position - .add_assign(&(metadata_buffer_range.start - region_buffer_start)); + start_position = + start_position + (metadata_buffer_range.start - region_buffer_start); start_position = start_position.min(start_region.range.end); } - let mut end_position = max_position; + let mut end_position = self.max_position(); if let Some(end_region) = &end_region { end_position = end_region.range.start; debug_assert!(end_region.is_main_buffer); let region_buffer_start = end_region.buffer_range.start; if metadata_buffer_range.end > region_buffer_start { - end_position - .add_assign(&(metadata_buffer_range.end - region_buffer_start)); + end_position = + end_position + (metadata_buffer_range.end - region_buffer_start); } end_position = end_position.min(end_region.range.end); } @@ -3464,7 +3929,9 @@ impl MultiBufferSnapshot { pub fn diff_hunk_before(&self, position: T) -> Option { let offset = position.to_offset(self); - let mut cursor = self.cursor::>(); + let mut cursor = self + .cursor::, DimensionPair>( + ); cursor.seek(&DimensionPair { key: offset, value: None, @@ -3540,7 +4007,7 @@ impl MultiBufferSnapshot { &self, start: T, scope_context: Option, - ) -> (Range, Option) { + ) -> (Range, Option) { let mut start = start.to_offset(self); let mut end = start; let mut next_chars = self.chars_at(start).peekable(); @@ -3599,12 +4066,16 @@ impl MultiBufferSnapshot { } } - pub fn len(&self) -> usize { + pub fn len(&self) -> MultiBufferOffset { self.diff_transforms.summary().output.len } + pub fn max_position(&self) -> MBD { + MBD::from_summary(&self.text_summary()) + } + pub fn is_empty(&self) -> bool { - self.excerpts.summary().text.len == 0 + self.diff_transforms.summary().output.len == MultiBufferOffset(0) } pub fn widest_line_number(&self) -> u32 { @@ -3614,7 +4085,7 @@ impl MultiBufferSnapshot { pub fn bytes_in_range(&self, range: Range) -> MultiBufferBytes<'_> { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut excerpts = self.cursor::(); + let mut excerpts = self.cursor::(); excerpts.seek(&range.start); let mut chunk; @@ -3622,8 +4093,8 @@ impl MultiBufferSnapshot { let excerpt_bytes; if let Some(region) = excerpts.region() { let mut bytes = region.buffer.bytes_in_range( - region.buffer_range.start + range.start - region.range.start - ..(region.buffer_range.start + range.end - region.range.start) + region.buffer_range.start + (range.start - region.range.start) + ..(region.buffer_range.start + (range.end - region.range.start)) .min(region.buffer_range.end), ); chunk = bytes.next().unwrap_or(&[][..]); @@ -3663,7 +4134,7 @@ impl MultiBufferSnapshot { } pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> { - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&Point::new(start_row.0, 0)); let mut result = MultiBufferRows { point: Point::new(0, 0), @@ -3682,7 +4153,7 @@ impl MultiBufferSnapshot { ) -> MultiBufferChunks<'_> { let mut chunks = MultiBufferChunks { excerpt_offset_range: ExcerptOffset::new(0)..ExcerptOffset::new(0), - range: 0..0, + range: MultiBufferOffset::ZERO..MultiBufferOffset::ZERO, excerpts: self.excerpts.cursor(()), diff_transforms: self.diff_transforms.cursor(()), diffs: &self.diffs, @@ -3696,7 +4167,7 @@ impl MultiBufferSnapshot { chunks } - pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize { + pub fn clip_offset(&self, offset: MultiBufferOffset, bias: Bias) -> MultiBufferOffset { self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset) } @@ -3704,7 +4175,11 @@ impl MultiBufferSnapshot { self.clip_dimension(point, bias, text::BufferSnapshot::clip_point) } - pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 { + pub fn clip_offset_utf16( + &self, + offset: MultiBufferOffsetUtf16, + bias: Bias, + ) -> MultiBufferOffsetUtf16 { self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset_utf16) } @@ -3714,11 +4189,11 @@ impl MultiBufferSnapshot { }) } - pub fn offset_to_point(&self, offset: usize) -> Point { + pub fn offset_to_point(&self, offset: MultiBufferOffset) -> Point { self.convert_dimension(offset, text::BufferSnapshot::offset_to_point) } - pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 { + pub fn offset_to_point_utf16(&self, offset: MultiBufferOffset) -> PointUtf16 { self.convert_dimension(offset, text::BufferSnapshot::offset_to_point_utf16) } @@ -3730,40 +4205,41 @@ impl MultiBufferSnapshot { self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_point) } - pub fn point_to_offset(&self, point: Point) -> usize { + pub fn point_to_offset(&self, point: Point) -> MultiBufferOffset { self.convert_dimension(point, text::BufferSnapshot::point_to_offset) } - pub fn point_to_offset_utf16(&self, point: Point) -> OffsetUtf16 { + pub fn point_to_offset_utf16(&self, point: Point) -> MultiBufferOffsetUtf16 { self.convert_dimension(point, text::BufferSnapshot::point_to_offset_utf16) } - pub fn offset_utf16_to_offset(&self, offset: OffsetUtf16) -> usize { + pub fn offset_utf16_to_offset(&self, offset: MultiBufferOffsetUtf16) -> MultiBufferOffset { self.convert_dimension(offset, text::BufferSnapshot::offset_utf16_to_offset) } - pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 { + pub fn offset_to_offset_utf16(&self, offset: MultiBufferOffset) -> MultiBufferOffsetUtf16 { self.convert_dimension(offset, text::BufferSnapshot::offset_to_offset_utf16) } - pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset(&self, point: PointUtf16) -> MultiBufferOffset { self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset) } - pub fn point_utf16_to_offset_utf16(&self, point: PointUtf16) -> OffsetUtf16 { + pub fn point_utf16_to_offset_utf16(&self, point: PointUtf16) -> MultiBufferOffsetUtf16 { self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset_utf16) } - fn clip_dimension( + fn clip_dimension( &self, - position: D, + position: MBD, bias: Bias, - clip_buffer_position: fn(&text::BufferSnapshot, D, Bias) -> D, - ) -> D + clip_buffer_position: fn(&text::BufferSnapshot, BD, Bias) -> BD, + ) -> MBD where - D: TextDimension + Ord + Sub, + MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<::Output>, + BD: TextDimension + Sub::Output> + AddAssign<::Output>, { - let mut cursor = self.cursor(); + let mut cursor = self.cursor::(); cursor.seek(&position); if let Some(region) = cursor.region() { if position >= region.range.end { @@ -3771,27 +4247,29 @@ impl MultiBufferSnapshot { } let overshoot = position - region.range.start; let mut buffer_position = region.buffer_range.start; - buffer_position.add_assign(&overshoot); + buffer_position += overshoot; let clipped_buffer_position = clip_buffer_position(region.buffer, buffer_position, bias); let mut position = region.range.start; - position.add_assign(&(clipped_buffer_position - region.buffer_range.start)); + position += clipped_buffer_position - region.buffer_range.start; position } else { - D::from_text_summary(&self.text_summary()) + self.max_position() } } - fn convert_dimension( + fn convert_dimension( &self, - key: D1, - convert_buffer_dimension: fn(&text::BufferSnapshot, D1) -> D2, - ) -> D2 + key: MBR1, + convert_buffer_dimension: fn(&text::BufferSnapshot, BR1) -> BR2, + ) -> MBR2 where - D1: TextDimension + Ord + Sub, - D2: TextDimension + Ord + Sub, + MBR1: MultiBufferDimension + Ord + Sub + ops::AddAssign<::Output>, + BR1: TextDimension + Sub::Output> + AddAssign<::Output>, + MBR2: MultiBufferDimension + Ord + Sub + ops::AddAssign<::Output>, + BR2: TextDimension + Sub::Output> + AddAssign<::Output>, { - let mut cursor = self.cursor::>(); + let mut cursor = self.cursor::, DimensionPair>(); cursor.seek(&DimensionPair { key, value: None }); if let Some(region) = cursor.region() { if key >= region.range.end.key { @@ -3802,34 +4280,34 @@ impl MultiBufferSnapshot { let buffer_start_key = region.buffer_range.start.key; let buffer_start_value = region.buffer_range.start.value.unwrap(); let mut buffer_key = buffer_start_key; - buffer_key.add_assign(&(key - start_key)); + buffer_key += key - start_key; let buffer_value = convert_buffer_dimension(region.buffer, buffer_key); let mut result = start_value; - result.add_assign(&(buffer_value - buffer_start_value)); + result += buffer_value - buffer_start_value; result } else { - D2::from_text_summary(&self.text_summary()) + self.max_position() } } pub fn point_to_buffer_offset( &self, point: T, - ) -> Option<(&BufferSnapshot, usize)> { + ) -> Option<(&BufferSnapshot, BufferOffset)> { let offset = point.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&offset); let region = cursor.region()?; let overshoot = offset - region.range.start; let buffer_offset = region.buffer_range.start + overshoot; - if buffer_offset == region.buffer.len() + 1 + if buffer_offset == BufferOffset(region.buffer.len() + 1) && region.has_trailing_newline && !region.is_main_buffer { let main_buffer_position = cursor.main_buffer_position()?; let buffer_snapshot = &cursor.excerpt()?.buffer; return Some((buffer_snapshot, main_buffer_position)); - } else if buffer_offset > region.buffer.len() { + } else if buffer_offset > BufferOffset(region.buffer.len()) { return None; } Some((region.buffer, buffer_offset)) @@ -3839,7 +4317,7 @@ impl MultiBufferSnapshot { &self, point: Point, ) -> Option<(&BufferSnapshot, Point, ExcerptId)> { - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&point); let region = cursor.region()?; let overshoot = point - region.range.start; @@ -3864,7 +4342,7 @@ impl MultiBufferSnapshot { let mut result = BTreeMap::new(); let mut rows_for_excerpt = Vec::new(); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); let mut rows = rows.into_iter().peekable(); let mut prev_row = u32::MAX; let mut prev_language_indent_size = IndentSize::default(); @@ -4000,7 +4478,7 @@ impl MultiBufferSnapshot { &self, row: MultiBufferRow, ) -> Option<(&BufferSnapshot, Range)> { - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); let point = Point::new(row.0, 0); cursor.seek(&point); let region = cursor.region()?; @@ -4023,23 +4501,23 @@ impl MultiBufferSnapshot { MultiBufferRow(self.text_summary().lines.row) } - pub fn text_summary(&self) -> TextSummary { + pub fn text_summary(&self) -> MBTextSummary { self.diff_transforms.summary().output } - pub fn text_summary_for_range(&self, range: Range) -> D + pub fn text_summary_for_range(&self, range: Range) -> MBD where - D: TextDimension, + MBD: MultiBufferDimension + AddAssign, O: ToOffset, { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut cursor = self .diff_transforms - .cursor::>(()); + .cursor::>(()); cursor.seek(&range.start, Bias::Right); let Some(first_transform) = cursor.item() else { - return D::from_text_summary(&TextSummary::default()); + return MBD::from_summary(&MBTextSummary::default()); }; let diff_transform_start = cursor.start().0; @@ -4072,14 +4550,18 @@ impl MultiBufferSnapshot { buffer_end -= 1; } - let mut summary = - base_text.text_summary_for_range::(buffer_start..buffer_end); + let mut summary = base_text + .text_summary_for_range::(buffer_start..buffer_end); if include_trailing_newline { - summary.add_assign(&D::from_text_summary(&TextSummary::newline())) + summary.add_assign(&::from_text_summary( + &TextSummary::newline(), + )) } - summary + let mut result = MBD::default(); + result.add_text_dim(&summary); + result } }; if range.end < diff_transform_end { @@ -4087,9 +4569,11 @@ impl MultiBufferSnapshot { } cursor.next(); - result.add_assign(&D::from_text_summary( - &cursor.summary(&range.end, Bias::Right), - )); + result.add_mb_text_summary( + &cursor + .summary::<_, OutputDimension<_>>(&range.end, Bias::Right) + .0, + ); let Some(last_transform) = cursor.item() else { return result; @@ -4099,7 +4583,7 @@ impl MultiBufferSnapshot { let suffix = match last_transform { DiffTransform::BufferContent { .. } => { let end = cursor.start().1 + ExcerptOffset::new(overshoot); - self.text_summary_for_excerpt_offset_range::(cursor.start().1..end) + self.text_summary_for_excerpt_offset_range::(cursor.start().1..end) } DiffTransform::DeletedHunk { base_text_byte_range, @@ -4112,24 +4596,30 @@ impl MultiBufferSnapshot { panic!("{:?} is in non-existent deleted hunk", range.end) }; - let mut suffix = base_text - .text_summary_for_range::(base_text_byte_range.start..buffer_end); + let mut suffix = base_text.text_summary_for_range::( + base_text_byte_range.start..buffer_end, + ); if *has_trailing_newline && buffer_end == base_text_byte_range.end + 1 { - suffix.add_assign(&D::from_text_summary(&TextSummary::newline())) + suffix.add_assign(&::from_text_summary( + &TextSummary::from("\n"), + )) } - suffix + + let mut result = MBD::default(); + result.add_text_dim(&suffix); + result } }; - result.add_assign(&suffix); + result += suffix; result } - fn text_summary_for_excerpt_offset_range(&self, mut range: Range) -> D + fn text_summary_for_excerpt_offset_range(&self, mut range: Range) -> MBD where - D: TextDimension, + MBD: MultiBufferDimension + AddAssign, { - let mut summary = D::zero(()); + let mut summary = MBD::default(); let mut cursor = self.excerpts.cursor::(()); cursor.seek(&range.start, Bias::Right); if let Some(excerpt) = cursor.item() { @@ -4142,34 +4632,36 @@ impl MultiBufferSnapshot { let start_in_excerpt = excerpt_start + (range.start - *cursor.start()).value; let end_in_excerpt = excerpt_start + (cmp::min(end_before_newline, range.end) - *cursor.start()).value; - summary.add_assign( + summary.add_text_dim( &excerpt .buffer - .text_summary_for_range(start_in_excerpt..end_in_excerpt), + .text_summary_for_range::( + start_in_excerpt..end_in_excerpt, + ), ); if range.end > end_before_newline { - summary.add_assign(&D::from_text_summary(&TextSummary::from("\n"))); + summary.add_mb_text_summary(&MBTextSummary::from(TextSummary::newline())); } cursor.next(); } if range.end > *cursor.start() { - summary.add_assign( - &cursor - .summary::<_, ExcerptDimension>(&range.end, Bias::Right) - .0, - ); + summary += cursor + .summary::<_, ExcerptDimension>(&range.end, Bias::Right) + .0; if let Some(excerpt) = cursor.item() { range.end = cmp::max(*cursor.start(), range.end); let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer); let end_in_excerpt = excerpt_start + (range.end - *cursor.start()).value; - summary.add_assign( + summary.add_text_dim( &excerpt .buffer - .text_summary_for_range(excerpt_start..end_in_excerpt), + .text_summary_for_range::( + excerpt_start..end_in_excerpt, + ), ); } } @@ -4177,24 +4669,28 @@ impl MultiBufferSnapshot { summary } - pub fn summary_for_anchor(&self, anchor: &Anchor) -> D + pub fn summary_for_anchor(&self, anchor: &Anchor) -> MBD where - D: TextDimension + Ord + Sub, + MBD: MultiBufferDimension + + Ord + + Sub + + AddAssign, + MBD::TextDimension: Sub + Ord, { self.summaries_for_anchors([anchor])[0] } - fn resolve_summary_for_anchor( + fn resolve_summary_for_anchor( &self, anchor: &Anchor, - excerpt_position: D, + excerpt_position: MBD, diff_transforms: &mut Cursor< DiffTransform, - Dimensions, OutputDimension>, + Dimensions, OutputDimension>, >, - ) -> D + ) -> MBD where - D: TextDimension + Ord + Sub, + MBD: MultiBufferDimension + Ord + Sub + AddAssign<::Output>, { loop { let transform_end_position = diff_transforms.end().0.0; @@ -4221,10 +4717,11 @@ impl MultiBufferSnapshot { if base_text_offset >= base_text_byte_range.start && base_text_offset <= base_text_byte_range.end { - let position_in_hunk = base_text.text_summary_for_range::( - base_text_byte_range.start..base_text_offset, - ); - position.add_assign(&position_in_hunk); + let position_in_hunk = base_text + .text_summary_for_range::( + base_text_byte_range.start..base_text_offset, + ); + position.add_text_dim(&position_in_hunk); } else if at_transform_end { diff_transforms.next(); continue; @@ -4237,7 +4734,7 @@ impl MultiBufferSnapshot { continue; } let overshoot = excerpt_position - diff_transforms.start().0.0; - position.add_assign(&overshoot); + position += overshoot; } } @@ -4282,16 +4779,20 @@ impl MultiBufferSnapshot { excerpt_id } - pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec + pub fn summaries_for_anchors<'a, MBD, I>(&'a self, anchors: I) -> Vec where - D: TextDimension + Ord + Sub, + MBD: MultiBufferDimension + + Ord + + Sub + + AddAssign, + MBD::TextDimension: Sub + Ord, I: 'a + IntoIterator, { let mut anchors = anchors.into_iter().peekable(); let mut cursor = self.excerpts.cursor::(()); let mut diff_transforms_cursor = self .diff_transforms - .cursor::, OutputDimension>>(()); + .cursor::, OutputDimension>>(()); diff_transforms_cursor.next(); let mut summaries = Vec::new(); @@ -4308,7 +4809,7 @@ impl MultiBufferSnapshot { cursor.prev(); } - let excerpt_start_position = D::from_text_summary(&cursor.start().text); + let excerpt_start_position = MBD::from_summary(&cursor.start().text); if let Some(excerpt) = cursor.item() { if excerpt.id != excerpt_id && excerpt_id != ExcerptId::max() { let position = self.resolve_summary_for_anchor( @@ -4319,19 +4820,26 @@ impl MultiBufferSnapshot { summaries.extend(excerpt_anchors.map(|_| position)); continue; } - let excerpt_buffer_start = - excerpt.range.context.start.summary::(&excerpt.buffer); - let excerpt_buffer_end = excerpt.range.context.end.summary::(&excerpt.buffer); + let excerpt_buffer_start = excerpt + .range + .context + .start + .summary::(&excerpt.buffer); + let excerpt_buffer_end = excerpt + .range + .context + .end + .summary::(&excerpt.buffer); for (buffer_summary, anchor) in excerpt .buffer - .summaries_for_anchors_with_payload::( + .summaries_for_anchors_with_payload::( excerpt_anchors.map(|a| (&a.text_anchor, a)), ) { let summary = cmp::min(excerpt_buffer_end, buffer_summary); let mut position = excerpt_start_position; if summary > excerpt_buffer_start { - position.add_assign(&(summary - excerpt_buffer_start)); + position += summary - excerpt_buffer_start; } if position > diff_transforms_cursor.start().0.0 { @@ -4360,14 +4868,14 @@ impl MultiBufferSnapshot { summaries } - pub fn dimensions_from_points<'a, D>( + pub fn dimensions_from_points<'a, MBD>( &'a self, points: impl 'a + IntoIterator, - ) -> impl 'a + Iterator + ) -> impl 'a + Iterator where - D: TextDimension + Sub, + MBD: MultiBufferDimension + Sub + AddAssign<::Output>, { - let mut cursor = self.cursor::>(); + let mut cursor = self.cursor::, Point>(); cursor.seek(&DimensionPair { key: Point::default(), value: None, @@ -4383,19 +4891,19 @@ impl MultiBufferSnapshot { if let Some(region) = cursor.region() { let overshoot = point - region.range.start.key; - let buffer_point = region.buffer_range.start.key + overshoot; + let buffer_point = region.buffer_range.start + overshoot; let mut position = region.range.start.value.unwrap(); - position.add_assign( + position.add_text_dim( ®ion .buffer - .text_summary_for_range(region.buffer_range.start.key..buffer_point), + .text_summary_for_range(region.buffer_range.start..buffer_point), ); if point == region.range.end.key && region.has_trailing_newline { - position.add_assign(&D::from_text_summary(&TextSummary::newline())); + position.add_mb_text_summary(&MBTextSummary::from(TextSummary::newline())); } Some(position) } else { - Some(D::from_text_summary(&self.text_summary())) + Some(MBD::from_summary(&self.text_summary())) } }) } @@ -4503,7 +5011,7 @@ impl MultiBufferSnapshot { // offset in the excerpts, and whether the position is within a deleted hunk. let mut diff_transforms = self .diff_transforms - .cursor::>(()); + .cursor::>(()); diff_transforms.seek(&offset, Bias::Right); if offset == diff_transforms.start().0 @@ -4651,7 +5159,11 @@ impl MultiBufferSnapshot { .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone())) } - fn cursor(&self) -> MultiBufferCursor<'_, D> { + fn cursor<'a, MBD, BD>(&'a self) -> MultiBufferCursor<'a, MBD, BD> + where + MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<::Output>, + BD: TextDimension + AddAssign<::Output>, + { let excerpts = self.excerpts.cursor(()); let diff_transforms = self.diff_transforms.cursor(()); MultiBufferCursor { @@ -4666,11 +5178,13 @@ impl MultiBufferSnapshot { let start_locator = self.excerpt_locator_for_id(excerpt_id); let mut excerpts = self .excerpts - .cursor::, ExcerptDimension>>(()); + .cursor::, ExcerptDimension>>(()); excerpts.seek(&Some(start_locator), Bias::Left); excerpts.prev(); - let mut diff_transforms = self.diff_transforms.cursor::>(()); + let mut diff_transforms = self + .diff_transforms + .cursor::>(()); diff_transforms.seek(&excerpts.start().1, Bias::Left); if diff_transforms.end().excerpt_dimension < excerpts.start().1 { diff_transforms.next(); @@ -4680,8 +5194,8 @@ impl MultiBufferSnapshot { Some(MultiBufferExcerpt { excerpt, offset: diff_transforms.start().output_dimension.0, - buffer_offset: excerpt.range.context.start.to_offset(&excerpt.buffer), - excerpt_offset: excerpts.start().1.clone(), + buffer_offset: BufferOffset(excerpt.range.context.start.to_offset(&excerpt.buffer)), + excerpt_offset: excerpts.start().1.0, diff_transforms, }) } @@ -4704,7 +5218,7 @@ impl MultiBufferSnapshot { panic!("not supported") } Bound::Unbounded => { - start_offset = 0; + start_offset = MultiBufferOffset::ZERO; Bound::Unbounded } }; @@ -4714,8 +5228,7 @@ impl MultiBufferSnapshot { Bound::Unbounded => Bound::Unbounded, }; let bounds = (start, end); - - let mut cursor = self.cursor::>(); + let mut cursor = self.cursor::, BufferOffset>(); cursor.seek(&DimensionPair { key: start_offset, value: None, @@ -4798,8 +5311,10 @@ impl MultiBufferSnapshot { pub fn innermost_enclosing_bracket_ranges( &self, range: Range, - range_filter: Option<&dyn Fn(&BufferSnapshot, Range, Range) -> bool>, - ) -> Option<(Range, Range)> { + range_filter: Option< + &dyn Fn(&BufferSnapshot, Range, Range) -> bool, + >, + ) -> Option<(Range, Range)> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut excerpt = self.excerpt_containing(range.clone())?; let buffer = excerpt.buffer(); @@ -4807,9 +5322,15 @@ impl MultiBufferSnapshot { // Filter to ranges contained in the excerpt let range_filter = |open: Range, close: Range| -> bool { - excerpt_buffer_range.contains(&open.start) - && excerpt_buffer_range.contains(&close.end) - && range_filter.is_none_or(|filter| filter(buffer, open, close)) + excerpt_buffer_range.contains(&BufferOffset(open.start)) + && excerpt_buffer_range.contains(&BufferOffset(close.end)) + && range_filter.is_none_or(|filter| { + filter( + buffer, + BufferOffset(open.start)..BufferOffset(close.end), + BufferOffset(close.start)..BufferOffset(close.end), + ) + }) }; let (open, close) = excerpt.buffer().innermost_enclosing_bracket_ranges( @@ -4818,8 +5339,8 @@ impl MultiBufferSnapshot { )?; Some(( - excerpt.map_range_from_buffer(open), - excerpt.map_range_from_buffer(close), + excerpt.map_range_from_buffer(BufferOffset(open.start)..BufferOffset(open.end)), + excerpt.map_range_from_buffer(BufferOffset(close.start)..BufferOffset(close.end)), )) } @@ -4828,7 +5349,8 @@ impl MultiBufferSnapshot { pub fn enclosing_bracket_ranges( &self, range: Range, - ) -> Option, Range)> + '_> { + ) -> Option, Range)> + '_> + { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut excerpt = self.excerpt_containing(range.clone())?; @@ -4837,10 +5359,14 @@ impl MultiBufferSnapshot { .buffer() .enclosing_bracket_ranges(excerpt.map_range_to_buffer(range)) .filter_map(move |pair| { - if excerpt.contains_buffer_range(pair.open_range.start..pair.close_range.end) { + let open_range = + BufferOffset(pair.open_range.start)..BufferOffset(pair.open_range.end); + let close_range = + BufferOffset(pair.close_range.start)..BufferOffset(pair.close_range.end); + if excerpt.contains_buffer_range(open_range.start..close_range.end) { Some(( - excerpt.map_range_from_buffer(pair.open_range), - excerpt.map_range_from_buffer(pair.close_range), + excerpt.map_range_from_buffer(open_range), + excerpt.map_range_from_buffer(close_range), )) } else { None @@ -4855,7 +5381,7 @@ impl MultiBufferSnapshot { &self, range: Range, options: TreeSitterOptions, - ) -> impl Iterator, TextObject)> + '_ { + ) -> impl Iterator, TextObject)> + '_ { let range = range.start.to_offset(self)..range.end.to_offset(self); self.excerpt_containing(range.clone()) .map(|mut excerpt| { @@ -4863,6 +5389,7 @@ impl MultiBufferSnapshot { .buffer() .text_object_ranges(excerpt.map_range_to_buffer(range), options) .filter_map(move |(range, text_object)| { + let range = BufferOffset(range.start)..BufferOffset(range.end); if excerpt.contains_buffer_range(range.clone()) { Some((excerpt.map_range_from_buffer(range), text_object)) } else { @@ -4879,7 +5406,8 @@ impl MultiBufferSnapshot { pub fn bracket_ranges( &self, range: Range, - ) -> Option, Range)> + '_> { + ) -> Option, Range)> + '_> + { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut excerpt = self.excerpt_containing(range.clone())?; @@ -4888,11 +5416,14 @@ impl MultiBufferSnapshot { .buffer() .bracket_ranges(excerpt.map_range_to_buffer(range)) .filter_map(move |pair| { - let buffer_range = pair.open_range.start..pair.close_range.end; - if excerpt.contains_buffer_range(buffer_range) { + let open_range = + BufferOffset(pair.open_range.start)..BufferOffset(pair.open_range.end); + let close_range = + BufferOffset(pair.close_range.start)..BufferOffset(pair.close_range.end); + if excerpt.contains_buffer_range(open_range.start..close_range.end) { Some(( - excerpt.map_range_from_buffer(pair.open_range), - excerpt.map_range_from_buffer(pair.close_range), + excerpt.map_range_from_buffer(open_range), + excerpt.map_range_from_buffer(close_range), )) } else { None @@ -4905,7 +5436,7 @@ impl MultiBufferSnapshot { &'a self, range: Range, redaction_enabled: impl Fn(Option<&Arc>) -> bool + 'a, - ) -> impl Iterator> + 'a { + ) -> impl Iterator> + 'a { let range = range.start.to_offset(self)..range.end.to_offset(self); self.lift_buffer_metadata(range, move |buffer, range| { if redaction_enabled(buffer.file()) { @@ -4920,7 +5451,7 @@ impl MultiBufferSnapshot { pub fn runnable_ranges( &self, range: Range, - ) -> impl Iterator + '_ { + ) -> impl Iterator, language::RunnableRange)> + '_ { let range = range.start.to_offset(self)..range.end.to_offset(self); self.lift_buffer_metadata(range, move |buffer, range| { Some( @@ -4933,10 +5464,7 @@ impl MultiBufferSnapshot { .map(|runnable| (runnable.run_range.clone(), runnable)), ) }) - .map(|(run_range, runnable, _)| language::RunnableRange { - run_range, - ..runnable - }) + .map(|(run_range, runnable, _)| (run_range, runnable)) } pub fn line_indents( @@ -4945,7 +5473,7 @@ impl MultiBufferSnapshot { buffer_filter: impl Fn(&BufferSnapshot) -> bool, ) -> impl Iterator { let max_point = self.max_point(); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&Point::new(start_row.0, 0)); iter::from_fn(move || { let mut region = cursor.region()?; @@ -4983,7 +5511,7 @@ impl MultiBufferSnapshot { buffer_filter: impl Fn(&BufferSnapshot) -> bool, ) -> impl Iterator { let max_point = self.max_point(); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&Point::new(end_row.0, 0)); iter::from_fn(move || { let mut region = cursor.region()?; @@ -5290,7 +5818,7 @@ impl MultiBufferSnapshot { cx, ) }) - .unwrap_or_else(move || self.language_settings_at(0, cx)) + .unwrap_or_else(move || self.language_settings_at(MultiBufferOffset::ZERO, cx)) } pub fn language_settings_at<'a, T: ToOffset>( @@ -5350,34 +5878,44 @@ impl MultiBufferSnapshot { buffer_id: BufferId, group_id: usize, ) -> impl Iterator> + '_ { - self.lift_buffer_metadata(Point::zero()..self.max_point(), move |buffer, range| { - if buffer.remote_id() != buffer_id { - return None; - }; - Some( - buffer - .diagnostics_in_range(range, false) - .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id) - .map(move |DiagnosticEntryRef { diagnostic, range }| (range, diagnostic)), - ) - }) + self.lift_buffer_metadata::( + Point::zero()..self.max_point(), + move |buffer, range| { + if buffer.remote_id() != buffer_id { + return None; + }; + Some( + buffer + .diagnostics_in_range(range, false) + .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id) + .map(move |DiagnosticEntryRef { diagnostic, range }| (range, diagnostic)), + ) + }, + ) .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range }) } - pub fn diagnostics_in_range<'a, T>( + pub fn diagnostics_in_range<'a, MBD>( &'a self, - range: Range, - ) -> impl Iterator> + 'a + range: Range, + ) -> impl Iterator> + 'a where - T: 'a + MBD::TextDimension: 'a + text::ToOffset + text::FromAnchor - + TextDimension + + Sub + + fmt::Debug + + ops::Add + + ops::AddAssign + + Ord, + MBD: MultiBufferDimension + Ord - + Sub - + fmt::Debug, + + Sub + + ops::Add + + ops::AddAssign + + 'a, { - self.lift_buffer_metadata(range, move |buffer, buffer_range| { + self.lift_buffer_metadata::(range, move |buffer, buffer_range| { Some( buffer .diagnostics_in_range(buffer_range.start..buffer_range.end, false) @@ -5387,20 +5925,24 @@ impl MultiBufferSnapshot { .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range }) } - pub fn diagnostics_with_buffer_ids_in_range<'a, T>( + pub fn diagnostics_with_buffer_ids_in_range<'a, MBD>( &'a self, - range: Range, - ) -> impl Iterator)> + 'a + range: Range, + ) -> impl Iterator)> + 'a where - T: 'a + MBD: MultiBufferDimension + + Ord + + Sub + + ops::Add + + ops::AddAssign, + MBD::TextDimension: Sub + + ops::Add + text::ToOffset + text::FromAnchor - + TextDimension - + Ord - + Sub - + fmt::Debug, + + AddAssign + + Ord, { - self.lift_buffer_metadata(range, move |buffer, buffer_range| { + self.lift_buffer_metadata::(range, move |buffer, buffer_range| { Some( buffer .diagnostics_in_range(buffer_range.start..buffer_range.end, false) @@ -5413,41 +5955,20 @@ impl MultiBufferSnapshot { pub fn syntax_ancestor( &self, range: Range, - ) -> Option<(tree_sitter::Node<'_>, Range)> { + ) -> Option<(tree_sitter::Node<'_>, Range)> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut excerpt = self.excerpt_containing(range.clone())?; let node = excerpt .buffer() .syntax_ancestor(excerpt.map_range_to_buffer(range))?; let node_range = node.byte_range(); + let node_range = BufferOffset(node_range.start)..BufferOffset(node_range.end); if !excerpt.contains_buffer_range(node_range.clone()) { return None; }; Some((node, excerpt.map_range_from_buffer(node_range))) } - pub fn syntax_next_sibling( - &self, - range: Range, - ) -> Option> { - let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut excerpt = self.excerpt_containing(range.clone())?; - excerpt - .buffer() - .syntax_next_sibling(excerpt.map_range_to_buffer(range)) - } - - pub fn syntax_prev_sibling( - &self, - range: Range, - ) -> Option> { - let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut excerpt = self.excerpt_containing(range.clone())?; - excerpt - .buffer() - .syntax_prev_sibling(excerpt.map_range_to_buffer(range)) - } - pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option> { let (excerpt_id, _, buffer) = self.as_singleton()?; let outline = buffer.outline(theme); @@ -5587,7 +6108,7 @@ impl MultiBufferSnapshot { sought_exact = true; } if sought_exact { - let start = cursor.start().1.clone(); + let start = cursor.start().1; let end = cursor.end().1; let mut diff_transforms = self .diff_transforms @@ -5625,7 +6146,7 @@ impl MultiBufferSnapshot { range: Range, ) -> Option> { let range = range.start.to_offset(self)..range.end.to_offset(self); - let mut cursor = self.cursor::(); + let mut cursor = self.cursor::(); cursor.seek(&range.start); let start_excerpt = cursor.excerpt()?; @@ -5640,7 +6161,7 @@ impl MultiBufferSnapshot { let region = cursor.region()?; let offset = region.range.start; let buffer_offset = start_excerpt.buffer_start_offset(); - let excerpt_offset = cursor.excerpts.start().clone(); + let excerpt_offset = cursor.excerpts.start().0; Some(MultiBufferExcerpt { diff_transforms: cursor.diff_transforms, excerpt: start_excerpt, @@ -5761,7 +6282,11 @@ impl MultiBufferSnapshot { #[cfg(any(test, feature = "test-support"))] impl MultiBufferSnapshot { - pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range { + pub fn random_byte_range( + &self, + start_offset: MultiBufferOffset, + rng: &mut impl rand::Rng, + ) -> Range { let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right); let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right); start..end @@ -5795,7 +6320,7 @@ impl MultiBufferSnapshot { if self.diff_transforms.summary().input != self.excerpts.summary().text { panic!( "incorrect input summary. expected {:?}, got {:?}. transforms: {:+?}", - self.excerpts.summary().text.len, + self.excerpts.summary().text, self.diff_transforms.summary().input, self.diff_transforms.items(()), ); @@ -5819,7 +6344,7 @@ impl MultiBufferSnapshot { self.diff_transforms.items(()) ); } - if summary.len == 0 && !self.is_empty() { + if summary.len == MultiBufferOffset(0) && !self.is_empty() { panic!("empty buffer content transform"); } } @@ -5828,11 +6353,12 @@ impl MultiBufferSnapshot { } } -impl<'a, D> MultiBufferCursor<'a, D> +impl<'a, MBD, BD> MultiBufferCursor<'a, MBD, BD> where - D: TextDimension + Ord + Sub, + MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<::Output>, + BD: TextDimension + AddAssign<::Output>, { - fn seek(&mut self, position: &D) { + fn seek(&mut self, position: &MBD) { self.cached_region.take(); self.diff_transforms .seek(&OutputDimension(*position), Bias::Right); @@ -5842,20 +6368,19 @@ where self.diff_transforms.prev(); } - let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0; + let mut excerpt_position = self.diff_transforms.start().excerpt_dimension; if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() { let overshoot = *position - self.diff_transforms.start().output_dimension.0; - excerpt_position.add_assign(&overshoot); + excerpt_position.0 += overshoot; } - self.excerpts - .seek(&ExcerptDimension(excerpt_position), Bias::Right); - if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 { + self.excerpts.seek(&excerpt_position, Bias::Right); + if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() { self.excerpts.prev(); } } - fn seek_forward(&mut self, position: &D) { + fn seek_forward(&mut self, position: &MBD) { self.cached_region.take(); self.diff_transforms .seek_forward(&OutputDimension(*position), Bias::Right); @@ -5866,14 +6391,13 @@ where } let overshoot = *position - self.diff_transforms.start().output_dimension.0; - let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0; + let mut excerpt_position = self.diff_transforms.start().excerpt_dimension; if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() { - excerpt_position.add_assign(&overshoot); + excerpt_position.0 += overshoot; } - self.excerpts - .seek_forward(&ExcerptDimension(excerpt_position), Bias::Right); - if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 { + self.excerpts.seek_forward(&excerpt_position, Bias::Right); + if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() { self.excerpts.prev(); } } @@ -5949,7 +6473,7 @@ where } } - fn region(&mut self) -> Option> { + fn region(&mut self) -> Option> { if self.cached_region.is_none() { self.cached_region = self.build_region(); } @@ -5991,17 +6515,17 @@ where }) } - fn main_buffer_position(&self) -> Option { + fn main_buffer_position(&self) -> Option { let excerpt = self.excerpts.item()?; let buffer = &excerpt.buffer; - let buffer_context_start = excerpt.range.context.start.summary::(buffer); + let buffer_context_start = excerpt.range.context.start.summary::(buffer); let mut buffer_start = buffer_context_start; - let overshoot = self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0; - buffer_start.add_assign(&overshoot); + let overshoot = self.diff_transforms.end().excerpt_dimension - *self.excerpts.start(); + buffer_start += overshoot.0; Some(buffer_start) } - fn build_region(&self) -> Option> { + fn build_region(&self) -> Option> { let excerpt = self.excerpts.item()?; match self.diff_transforms.item()? { DiffTransform::DeletedHunk { @@ -6014,10 +6538,10 @@ where let diff = self.diffs.get(buffer_id)?; let buffer = diff.base_text(); let mut rope_cursor = buffer.as_rope().cursor(0); - let buffer_start = rope_cursor.summary::(base_text_byte_range.start); - let buffer_range_len = rope_cursor.summary::(base_text_byte_range.end); + let buffer_start = rope_cursor.summary::(base_text_byte_range.start); + let buffer_range_len = rope_cursor.summary::(base_text_byte_range.end); let mut buffer_end = buffer_start; - buffer_end.add_assign(&buffer_range_len); + TextDimension::add_assign(&mut buffer_end, &buffer_range_len); let start = self.diff_transforms.start().output_dimension.0; let end = self.diff_transforms.end().output_dimension.0; Some(MultiBufferRegion { @@ -6036,36 +6560,36 @@ where inserted_hunk_info, .. } => { let buffer = &excerpt.buffer; - let buffer_context_start = excerpt.range.context.start.summary::(buffer); + let buffer_context_start = excerpt.range.context.start.summary::(buffer); let mut start = self.diff_transforms.start().output_dimension.0; let mut buffer_start = buffer_context_start; if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() { let overshoot = - self.excerpts.start().0 - self.diff_transforms.start().excerpt_dimension.0; - start.add_assign(&overshoot); + *self.excerpts.start() - self.diff_transforms.start().excerpt_dimension; + start += overshoot.0; } else { let overshoot = - self.diff_transforms.start().excerpt_dimension.0 - self.excerpts.start().0; - buffer_start.add_assign(&overshoot); + self.diff_transforms.start().excerpt_dimension - *self.excerpts.start(); + buffer_start += overshoot.0; } let mut end; let mut buffer_end; let has_trailing_newline; - if self.diff_transforms.end().excerpt_dimension.0 < self.excerpts.end().0 { + if self.diff_transforms.end().excerpt_dimension < self.excerpts.end() { let overshoot = - self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0; + self.diff_transforms.end().excerpt_dimension - *self.excerpts.start(); end = self.diff_transforms.end().output_dimension.0; buffer_end = buffer_context_start; - buffer_end.add_assign(&overshoot); + buffer_end += overshoot.0; has_trailing_newline = false; } else { let overshoot = - self.excerpts.end().0 - self.diff_transforms.start().excerpt_dimension.0; + self.excerpts.end() - self.diff_transforms.start().excerpt_dimension; end = self.diff_transforms.start().output_dimension.0; - end.add_assign(&overshoot); - buffer_end = excerpt.range.context.end.summary::(buffer); + end += overshoot.0; + buffer_end = excerpt.range.context.end.summary::(buffer); has_trailing_newline = excerpt.has_trailing_newline; }; @@ -6171,12 +6695,12 @@ impl Excerpt { } /// The [`Excerpt`]'s start offset in its [`Buffer`] - fn buffer_start_offset(&self) -> usize { - self.range.context.start.to_offset(&self.buffer) + fn buffer_start_offset(&self) -> BufferOffset { + BufferOffset(self.range.context.start.to_offset(&self.buffer)) } /// The [`Excerpt`]'s end offset in its [`Buffer`] - fn buffer_end_offset(&self) -> usize { + fn buffer_end_offset(&self) -> BufferOffset { self.buffer_start_offset() + self.text_summary.len } } @@ -6210,27 +6734,28 @@ impl<'a> MultiBufferExcerpt<'a> { &self.excerpt.buffer } - pub fn buffer_range(&self) -> Range { + pub fn buffer_range(&self) -> Range { self.buffer_offset - ..self - .excerpt - .range - .context - .end - .to_offset(&self.excerpt.buffer.text) + ..BufferOffset( + self.excerpt + .range + .context + .end + .to_offset(&self.excerpt.buffer.text), + ) } - pub fn start_offset(&self) -> usize { + pub fn start_offset(&self) -> MultiBufferOffset { self.offset } /// Maps an offset within the [`MultiBuffer`] to an offset within the [`Buffer`] - pub fn map_offset_to_buffer(&mut self, offset: usize) -> usize { + pub fn map_offset_to_buffer(&mut self, offset: MultiBufferOffset) -> BufferOffset { self.map_range_to_buffer(offset..offset).start } /// Maps a range within the [`MultiBuffer`] to a range within the [`Buffer`] - pub fn map_range_to_buffer(&mut self, range: Range) -> Range { + pub fn map_range_to_buffer(&mut self, range: Range) -> Range { self.diff_transforms .seek(&OutputDimension(range.start), Bias::Right); let start = self.map_offset_to_buffer_internal(range.start); @@ -6244,47 +6769,52 @@ impl<'a> MultiBufferExcerpt<'a> { start..end } - fn map_offset_to_buffer_internal(&self, offset: usize) -> usize { - let mut excerpt_offset = self.diff_transforms.start().excerpt_dimension.clone(); + fn map_offset_to_buffer_internal(&self, offset: MultiBufferOffset) -> BufferOffset { + let mut excerpt_offset = self.diff_transforms.start().excerpt_dimension; if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() { excerpt_offset.0 += offset - self.diff_transforms.start().output_dimension.0; }; - let offset_in_excerpt = excerpt_offset.0.saturating_sub(self.excerpt_offset.0); + let offset_in_excerpt = excerpt_offset.0.0.saturating_sub(self.excerpt_offset.0); self.buffer_offset + offset_in_excerpt } /// Map an offset within the [`Buffer`] to an offset within the [`MultiBuffer`] - pub fn map_offset_from_buffer(&mut self, buffer_offset: usize) -> usize { + pub fn map_offset_from_buffer(&mut self, buffer_offset: BufferOffset) -> MultiBufferOffset { self.map_range_from_buffer(buffer_offset..buffer_offset) .start } /// Map a range within the [`Buffer`] to a range within the [`MultiBuffer`] - pub fn map_range_from_buffer(&mut self, buffer_range: Range) -> Range { + pub fn map_range_from_buffer( + &mut self, + buffer_range: Range, + ) -> Range { if buffer_range.start < self.buffer_offset { log::warn!( "Attempting to map a range from a buffer offset that starts before the current buffer offset" ); - return buffer_range; + return self.offset..self.offset; } let overshoot = buffer_range.start - self.buffer_offset; - let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot); - self.diff_transforms.seek(&excerpt_offset, Bias::Right); - if excerpt_offset.0 < self.diff_transforms.start().excerpt_dimension.0 { + let excerpt_offset = self.excerpt_offset + overshoot; + let excerpt_seek_dim = ExcerptDimension(excerpt_offset); + self.diff_transforms.seek(&excerpt_seek_dim, Bias::Right); + if excerpt_offset < self.diff_transforms.start().excerpt_dimension.0 { log::warn!( "Attempting to map a range from a buffer offset that starts before the current buffer offset" ); - return buffer_range; + return self.offset..self.offset; } - let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0; + let overshoot = excerpt_offset - self.diff_transforms.start().excerpt_dimension.0; let start = self.diff_transforms.start().output_dimension.0 + overshoot; let end = if buffer_range.end > buffer_range.start { let overshoot = buffer_range.end - self.buffer_offset; - let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot); + let excerpt_offset = self.excerpt_offset + overshoot; + let excerpt_seek_dim = ExcerptDimension(excerpt_offset); self.diff_transforms - .seek_forward(&excerpt_offset, Bias::Right); - let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0; + .seek_forward(&excerpt_seek_dim, Bias::Right); + let overshoot = excerpt_offset - self.diff_transforms.start().excerpt_dimension.0; self.diff_transforms.start().output_dimension.0 + overshoot } else { start @@ -6294,7 +6824,7 @@ impl<'a> MultiBufferExcerpt<'a> { } /// Returns true if the entirety of the given range is in the buffer's excerpt - pub fn contains_buffer_range(&self, range: Range) -> bool { + pub fn contains_buffer_range(&self, range: Range) -> bool { range.start >= self.excerpt.buffer_start_offset() && range.end <= self.excerpt.buffer_end_offset() } @@ -6359,7 +6889,7 @@ impl sum_tree::Item for Excerpt { excerpt_id: self.id, excerpt_locator: self.locator.clone(), widest_line_number: self.max_buffer_row, - text, + text: text.into(), } } } @@ -6400,9 +6930,9 @@ impl sum_tree::Item for DiffTransform { input: *summary, output: *summary, }, - DiffTransform::DeletedHunk { summary, .. } => DiffTransformSummary { - input: TextSummary::default(), - output: *summary, + &DiffTransform::DeletedHunk { summary, .. } => DiffTransformSummary { + input: MBTextSummary::default(), + output: summary.into(), }, } } @@ -6410,15 +6940,15 @@ impl sum_tree::Item for DiffTransform { impl DiffTransformSummary { fn excerpt_len(&self) -> ExcerptOffset { - ExcerptOffset::new(self.input.len) + ExcerptOffset::new(self.input.len.0) } } impl sum_tree::ContextLessSummary for DiffTransformSummary { fn zero() -> Self { DiffTransformSummary { - input: TextSummary::default(), - output: TextSummary::default(), + input: MBTextSummary::default(), + output: MBTextSummary::default(), } } @@ -6446,7 +6976,7 @@ impl sum_tree::ContextLessSummary for ExcerptSummary { fn add_summary(&mut self, summary: &Self) { debug_assert!(summary.excerpt_locator > self.excerpt_locator); self.excerpt_locator = summary.excerpt_locator.clone(); - Summary::add_summary(&mut self.text, &summary.text, ()); + self.text += summary.text; self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number); } } @@ -6457,13 +6987,41 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptOffset { } fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) { - self.value += summary.text.len; + self.value += summary.text.len.0; + } +} +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for MultiBufferOffset { + fn zero((): ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a ExcerptSummary, (): ()) { + *self += summary.text.len; + } +} + +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for MultiBufferOffsetUtf16 { + fn zero((): ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a ExcerptSummary, (): ()) { + self.0 += summary.text.len_utf16; + } +} +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 { + fn zero((): ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a ExcerptSummary, (): ()) { + TextDimension::add_assign(self, &summary.text.lines_utf16()) } } impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for ExcerptOffset { fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering { - Ord::cmp(&self.value, &cursor_location.text.len) + Ord::cmp(&self.value, &cursor_location.text.len.0) } } @@ -6489,15 +7047,25 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptPoint { } } -impl<'a, D: TextDimension + Default> sum_tree::Dimension<'a, ExcerptSummary> - for ExcerptDimension +impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point { + fn zero(_cx: ()) -> Self { + Default::default() + } + + fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) { + TextDimension::add_assign(self, &summary.text.lines); + } +} + +impl<'a, MBD: MultiBufferDimension + Default> sum_tree::Dimension<'a, ExcerptSummary> + for ExcerptDimension { fn zero(_: ()) -> Self { - ExcerptDimension(D::default()) + ExcerptDimension(MBD::default()) } fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) { - self.0.add_assign(&D::from_text_summary(&summary.text)) + MultiBufferDimension::add_mb_text_summary(&mut self.0, &summary.text) } } @@ -6521,19 +7089,59 @@ impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option { } } -#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] struct ExcerptDimension(T); +impl ops::Sub for ExcerptDimension +where + T: ops::Sub, +{ + type Output = ExcerptDimension; + + fn sub(self, other: Self) -> Self::Output { + ExcerptDimension(self.0 - other.0) + } +} + +impl AddAssign for ExcerptDimension +where + T: AddAssign, +{ + fn add_assign(&mut self, other: Self) { + self.0 += other.0; + } +} + #[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)] struct OutputDimension(T); +impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for MultiBufferOffset { + fn zero(_: ()) -> Self { + MultiBufferOffset::ZERO + } + + fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { + *self += summary.output.len; + } +} + +impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for MultiBufferOffsetUtf16 { + fn zero(_: ()) -> Self { + MultiBufferOffsetUtf16(OffsetUtf16(0)) + } + + fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { + self.0 += summary.output.len_utf16; + } +} + impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptOffset { fn zero(_: ()) -> Self { ExcerptOffset::new(0) } fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - self.value += summary.input.len; + self.value += summary.input.len.0; } } @@ -6547,77 +7155,77 @@ impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptPoint { } } -impl sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransformSummary> - for ExcerptDimension +impl sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransformSummary> + for ExcerptDimension +where + MBD: MultiBufferDimension + Ord, { fn cmp(&self, cursor_location: &DiffTransformSummary, _: ()) -> cmp::Ordering { - Ord::cmp(&self.0, &D::from_text_summary(&cursor_location.input)) + Ord::cmp(&self.0, &MBD::from_summary(&cursor_location.input)) } } -impl sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms> - for ExcerptDimension +impl<'a, MBD> sum_tree::SeekTarget<'a, DiffTransformSummary, DiffTransforms> + for ExcerptDimension +where + MBD: MultiBufferDimension + Ord, { - fn cmp(&self, cursor_location: &DiffTransforms, _: ()) -> cmp::Ordering { + fn cmp(&self, cursor_location: &DiffTransforms, _: ()) -> cmp::Ordering { Ord::cmp(&self.0, &cursor_location.excerpt_dimension.0) } } -impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptDimension { +impl<'a, MBD: MultiBufferDimension> sum_tree::Dimension<'a, DiffTransformSummary> + for ExcerptDimension +{ fn zero(_: ()) -> Self { - ExcerptDimension(D::default()) + ExcerptDimension(MBD::default()) } fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - self.0.add_assign(&D::from_text_summary(&summary.input)) + self.0.add_mb_text_summary(&summary.input) } } -impl sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms> - for OutputDimension +impl<'a, MBD> sum_tree::SeekTarget<'a, DiffTransformSummary, DiffTransforms> + for OutputDimension +where + MBD: MultiBufferDimension + Ord, { - fn cmp(&self, cursor_location: &DiffTransforms, _: ()) -> cmp::Ordering { + fn cmp(&self, cursor_location: &DiffTransforms, _: ()) -> cmp::Ordering { Ord::cmp(&self.0, &cursor_location.output_dimension.0) } } -impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for OutputDimension { - fn zero(_: ()) -> Self { - OutputDimension(D::default()) - } - - fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - self.0.add_assign(&D::from_text_summary(&summary.output)) - } -} - -impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for TextSummary { +impl<'a, MBD: MultiBufferDimension> sum_tree::Dimension<'a, DiffTransformSummary> + for OutputDimension +{ fn zero(_: ()) -> Self { - TextSummary::default() + OutputDimension(MBD::default()) } fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - *self += summary.output + self.0.add_mb_text_summary(&summary.output) } } -impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for usize { +impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for Point { fn zero(_: ()) -> Self { - 0 + Point::new(0, 0) } fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - *self += summary.output.len + *self += summary.output.lines } } -impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for Point { +impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for PointUtf16 { fn zero(_: ()) -> Self { - Point::new(0, 0) + PointUtf16::new(0, 0) } fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) { - *self += summary.output.lines + *self += summary.output.lines_utf16() } } @@ -6751,11 +7359,11 @@ impl Iterator for MultiBufferRows<'_> { } impl<'a> MultiBufferChunks<'a> { - pub fn offset(&self) -> usize { + pub fn offset(&self) -> MultiBufferOffset { self.range.start } - pub fn seek(&mut self, range: Range) { + pub fn seek(&mut self, range: Range) { self.diff_transforms.seek(&range.end, Bias::Right); let mut excerpt_end = self.diff_transforms.start().1; if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() { @@ -6859,7 +7467,13 @@ impl<'a> Iterator for MultiBufferChunks<'a> { let diff_transform_start = self.diff_transforms.start().0; let diff_transform_end = self.diff_transforms.end().0; - debug_assert!(self.range.start < diff_transform_end); + debug_assert!( + self.range.start < diff_transform_end, + "{:?} < {:?} of ({1:?}..{2:?})", + self.range.start, + diff_transform_end, + diff_transform_start + ); let diff_transform = self.diff_transforms.item()?; match diff_transform { @@ -6904,9 +7518,9 @@ impl<'a> Iterator for MultiBufferChunks<'a> { .. } => { let base_text_start = - base_text_byte_range.start + self.range.start - diff_transform_start; + base_text_byte_range.start + (self.range.start - diff_transform_start); let base_text_end = - base_text_byte_range.start + self.range.end - diff_transform_start; + base_text_byte_range.start + (self.range.end - diff_transform_start); let base_text_end = base_text_end.min(base_text_byte_range.end); let mut chunks = if let Some((_, mut chunks)) = self @@ -6959,7 +7573,7 @@ impl MultiBufferBytes<'_> { if let Some(region) = self.cursor.region() { let mut excerpt_bytes = region.buffer.bytes_in_range( region.buffer_range.start - ..(region.buffer_range.start + self.range.end - region.range.start) + ..(region.buffer_range.start + (self.range.end - region.range.start)) .min(region.buffer_range.end), ); self.chunk = excerpt_bytes.next().unwrap_or(&[]); @@ -7044,50 +7658,50 @@ impl<'a> Iterator for ExcerptChunks<'a> { } impl ToOffset for Point { - fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset { snapshot.point_to_offset(*self) } - fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 { snapshot.point_to_offset_utf16(*self) } } -impl ToOffset for usize { +impl ToOffset for MultiBufferOffset { #[track_caller] - fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset { assert!( *self <= snapshot.len(), "offset {} is greater than the snapshot.len() {}", - *self, - snapshot.len(), + self.0, + snapshot.len().0, ); *self } - fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 { snapshot.offset_to_offset_utf16(*self) } } -impl ToOffset for OffsetUtf16 { - fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { +impl ToOffset for MultiBufferOffsetUtf16 { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset { snapshot.offset_utf16_to_offset(*self) } - fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 { *self } } impl ToOffset for PointUtf16 { - fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset { snapshot.point_utf16_to_offset(*self) } - fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 { snapshot.point_utf16_to_offset_utf16(*self) } } -impl ToPoint for usize { +impl ToPoint for MultiBufferOffset { fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point { snapshot.offset_to_point(*self) } @@ -7125,15 +7739,17 @@ pub mod debug { use super::*; pub trait ToMultiBufferDebugRanges { - fn to_multi_buffer_debug_ranges(&self, snapshot: &MultiBufferSnapshot) - -> Vec>; + fn to_multi_buffer_debug_ranges( + &self, + snapshot: &MultiBufferSnapshot, + ) -> Vec>; } impl ToMultiBufferDebugRanges for T { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { [self.to_offset(snapshot)].to_multi_buffer_debug_ranges(snapshot) } } @@ -7142,7 +7758,7 @@ pub mod debug { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { [self.start.to_offset(snapshot)..self.end.to_offset(snapshot)] .to_multi_buffer_debug_ranges(snapshot) } @@ -7152,7 +7768,7 @@ pub mod debug { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { self.as_slice().to_multi_buffer_debug_ranges(snapshot) } } @@ -7161,7 +7777,7 @@ pub mod debug { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { self.as_slice().to_multi_buffer_debug_ranges(snapshot) } } @@ -7170,7 +7786,7 @@ pub mod debug { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { self.iter() .map(|item| { let offset = item.to_offset(snapshot); @@ -7184,7 +7800,7 @@ pub mod debug { fn to_multi_buffer_debug_ranges( &self, snapshot: &MultiBufferSnapshot, - ) -> Vec> { + ) -> Vec> { self.iter() .map(|range| range.start.to_offset(snapshot)..range.end.to_offset(snapshot)) .collect() diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index 22c041267f9c78c1f20609b74e2332516639f39b..0151805d065b779569b3a2f8f02157f3ce129295 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/crates/multi_buffer/src/multi_buffer_tests.rs @@ -130,8 +130,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) { assert_eq!( subscription.consume().into_inner(), [Edit { - old: 0..0, - new: 0..10 + old: MultiBufferOffset(0)..MultiBufferOffset(0), + new: MultiBufferOffset(0)..MultiBufferOffset(10) }] ); @@ -148,8 +148,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) { assert_eq!( subscription.consume().into_inner(), [Edit { - old: 10..10, - new: 10..22 + old: MultiBufferOffset(10)..MultiBufferOffset(10), + new: MultiBufferOffset(10)..MultiBufferOffset(22) }] ); @@ -282,8 +282,8 @@ fn test_excerpt_boundaries_and_clipping(cx: &mut App) { assert_eq!( subscription.consume().into_inner(), [Edit { - old: 6..8, - new: 6..7 + old: MultiBufferOffset(6)..MultiBufferOffset(8), + new: MultiBufferOffset(6)..MultiBufferOffset(7) }] ); @@ -925,7 +925,7 @@ fn test_empty_diff_excerpt(cx: &mut TestAppContext) { .next() .unwrap(); - assert_eq!(hunk.diff_base_byte_range.start, 0); + assert_eq!(hunk.diff_base_byte_range.start, BufferOffset(0)); let buf2 = cx.new(|cx| Buffer::local("X", cx)); multibuffer.update(cx, |multibuffer, cx| { @@ -971,10 +971,30 @@ fn test_singleton_multibuffer_anchors(cx: &mut App) { assert_eq!(old_snapshot.text(), "abcd"); assert_eq!(new_snapshot.text(), "XabcdY"); - assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0); - assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1); - assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5); - assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(0)) + .to_offset(&new_snapshot), + MultiBufferOffset(0) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(0)) + .to_offset(&new_snapshot), + MultiBufferOffset(1) + ); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(4)) + .to_offset(&new_snapshot), + MultiBufferOffset(5) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(4)) + .to_offset(&new_snapshot), + MultiBufferOffset(6) + ); } #[gpui::test] @@ -989,12 +1009,28 @@ fn test_multibuffer_anchors(cx: &mut App) { }); let old_snapshot = multibuffer.read(cx).snapshot(cx); - assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0); - assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0); - assert_eq!(Anchor::min().to_offset(&old_snapshot), 0); - assert_eq!(Anchor::min().to_offset(&old_snapshot), 0); - assert_eq!(Anchor::max().to_offset(&old_snapshot), 10); - assert_eq!(Anchor::max().to_offset(&old_snapshot), 10); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(0)) + .to_offset(&old_snapshot), + MultiBufferOffset(0) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(0)) + .to_offset(&old_snapshot), + MultiBufferOffset(0) + ); + assert_eq!(Anchor::min().to_offset(&old_snapshot), MultiBufferOffset(0)); + assert_eq!(Anchor::min().to_offset(&old_snapshot), MultiBufferOffset(0)); + assert_eq!( + Anchor::max().to_offset(&old_snapshot), + MultiBufferOffset(10) + ); + assert_eq!( + Anchor::max().to_offset(&old_snapshot), + MultiBufferOffset(10) + ); buffer_1.update(cx, |buffer, cx| { buffer.edit([(0..0, "W")], None, cx); @@ -1009,16 +1045,66 @@ fn test_multibuffer_anchors(cx: &mut App) { assert_eq!(old_snapshot.text(), "abcd\nefghi"); assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ"); - assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0); - assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1); - assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2); - assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2); - assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3); - assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3); - assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7); - assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8); - assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13); - assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(0)) + .to_offset(&new_snapshot), + MultiBufferOffset(0) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(0)) + .to_offset(&new_snapshot), + MultiBufferOffset(1) + ); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(1)) + .to_offset(&new_snapshot), + MultiBufferOffset(2) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(1)) + .to_offset(&new_snapshot), + MultiBufferOffset(2) + ); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(2)) + .to_offset(&new_snapshot), + MultiBufferOffset(3) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(2)) + .to_offset(&new_snapshot), + MultiBufferOffset(3) + ); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(5)) + .to_offset(&new_snapshot), + MultiBufferOffset(7) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(5)) + .to_offset(&new_snapshot), + MultiBufferOffset(8) + ); + assert_eq!( + old_snapshot + .anchor_before(MultiBufferOffset(10)) + .to_offset(&new_snapshot), + MultiBufferOffset(13) + ); + assert_eq!( + old_snapshot + .anchor_after(MultiBufferOffset(10)) + .to_offset(&new_snapshot), + MultiBufferOffset(14) + ); } #[gpui::test] @@ -1066,26 +1152,30 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) { // The current excerpts are from a different buffer, so we don't attempt to // resolve the old text anchor in the new buffer. assert_eq!( - snapshot_2.summary_for_anchor::(&snapshot_1.anchor_before(2)), - 0 + snapshot_2.summary_for_anchor::( + &snapshot_1.anchor_before(MultiBufferOffset(2)) + ), + MultiBufferOffset(0) ); assert_eq!( - snapshot_2.summaries_for_anchors::(&[ - snapshot_1.anchor_before(2), - snapshot_1.anchor_after(3) + snapshot_2.summaries_for_anchors::(&[ + snapshot_1.anchor_before(MultiBufferOffset(2)), + snapshot_1.anchor_after(MultiBufferOffset(3)) ]), - vec![0, 0] + vec![MultiBufferOffset(0), MultiBufferOffset(0)] ); // Refresh anchors from the old snapshot. The return value indicates that both // anchors lost their original excerpt. - let refresh = - snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]); + let refresh = snapshot_2.refresh_anchors(&[ + snapshot_1.anchor_before(MultiBufferOffset(2)), + snapshot_1.anchor_after(MultiBufferOffset(3)), + ]); assert_eq!( refresh, &[ - (0, snapshot_2.anchor_before(0), false), - (1, snapshot_2.anchor_after(0), false), + (0, snapshot_2.anchor_before(MultiBufferOffset(0)), false), + (1, snapshot_2.anchor_after(MultiBufferOffset(0)), false), ] ); @@ -1112,14 +1202,19 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) { // The third anchor can't be resolved, since its excerpt has been removed, // so it resolves to the same position as its predecessor. let anchors = [ - snapshot_2.anchor_before(0), - snapshot_2.anchor_after(2), - snapshot_2.anchor_after(6), - snapshot_2.anchor_after(14), + snapshot_2.anchor_before(MultiBufferOffset(0)), + snapshot_2.anchor_after(MultiBufferOffset(2)), + snapshot_2.anchor_after(MultiBufferOffset(6)), + snapshot_2.anchor_after(MultiBufferOffset(14)), ]; assert_eq!( - snapshot_3.summaries_for_anchors::(&anchors), - &[0, 2, 9, 13] + snapshot_3.summaries_for_anchors::(&anchors), + &[ + MultiBufferOffset(0), + MultiBufferOffset(2), + MultiBufferOffset(9), + MultiBufferOffset(13) + ] ); let new_anchors = snapshot_3.refresh_anchors(&anchors); @@ -1128,8 +1223,13 @@ fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) { &[(0, true), (1, true), (2, true), (3, true)] ); assert_eq!( - snapshot_3.summaries_for_anchors::(new_anchors.iter().map(|a| &a.1)), - &[0, 2, 7, 13] + snapshot_3.summaries_for_anchors::(new_anchors.iter().map(|a| &a.1)), + &[ + MultiBufferOffset(0), + MultiBufferOffset(2), + MultiBufferOffset(7), + MultiBufferOffset(13) + ] ); } @@ -1371,7 +1471,7 @@ fn test_basic_diff_hunks(cx: &mut TestAppContext) { assert_eq!( snapshot - .diff_hunks_in_range(0..snapshot.len()) + .diff_hunks_in_range(MultiBufferOffset(0)..snapshot.len()) .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0) .collect::>(), &[0..4, 5..7] @@ -2072,7 +2172,7 @@ fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) { assert_eq!( snapshot - .diff_hunks_in_range(0..snapshot.len()) + .diff_hunks_in_range(MultiBufferOffset(0)..snapshot.len()) .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0) .collect::>(), &[0..1, 2..4, 5..7, 9..10, 12..13, 14..17] @@ -2636,14 +2736,16 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { 30..=39 if !reference.excerpts.is_empty() => { let multibuffer = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx)); - let offset = - multibuffer.clip_offset(rng.random_range(0..=multibuffer.len()), Bias::Left); + let offset = multibuffer.clip_offset( + MultiBufferOffset(rng.random_range(0..=multibuffer.len().0)), + Bias::Left, + ); let bias = if rng.random() { Bias::Left } else { Bias::Right }; - log::info!("Creating anchor at {} with bias {:?}", offset, bias); + log::info!("Creating anchor at {} with bias {:?}", offset.0, bias); anchors.push(multibuffer.anchor_at(offset, bias)); anchors.sort_by(|a, b| a.cmp(b, &multibuffer)); } @@ -2796,7 +2898,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx)); let actual_text = snapshot.text(); let actual_boundary_rows = snapshot - .excerpt_boundaries_in_range(0..) + .excerpt_boundaries_in_range(MultiBufferOffset(0)..) .map(|b| b.row) .collect::>(); let actual_row_infos = snapshot.row_infos(MultiBufferRow(0)).collect::>(); @@ -2874,9 +2976,14 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { }) .collect::>() }); - for i in 0..snapshot.len() { - let excerpt = snapshot.excerpt_containing(i..i).unwrap(); - assert_eq!(excerpt.buffer_range(), reference_ranges[&excerpt.id()]); + for i in 0..snapshot.len().0 { + let excerpt = snapshot + .excerpt_containing(MultiBufferOffset(i)..MultiBufferOffset(i)) + .unwrap(); + assert_eq!( + excerpt.buffer_range().start.0..excerpt.buffer_range().end.0, + reference_ranges[&excerpt.id()] + ); } assert_consistent_line_numbers(&snapshot); @@ -2897,7 +3004,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left); let text_for_range = snapshot - .text_for_range(start_ix..end_ix) + .text_for_range(MultiBufferOffset(start_ix)..MultiBufferOffset(end_ix)) .collect::(); assert_eq!( text_for_range, @@ -2906,9 +3013,12 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { start_ix..end_ix ); - let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]); + let expected_summary = + MBTextSummary::from(TextSummary::from(&expected_text[start_ix..end_ix])); assert_eq!( - snapshot.text_summary_for_range::(start_ix..end_ix), + snapshot.text_summary_for_range::( + MultiBufferOffset(start_ix)..MultiBufferOffset(end_ix) + ), expected_summary, "incorrect summary for range {:?}", start_ix..end_ix @@ -2916,12 +3026,12 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { } // Anchor resolution - let summaries = snapshot.summaries_for_anchors::(&anchors); + let summaries = snapshot.summaries_for_anchors::(&anchors); assert_eq!(anchors.len(), summaries.len()); for (anchor, resolved_offset) in anchors.iter().zip(summaries) { assert!(resolved_offset <= snapshot.len()); assert_eq!( - snapshot.summary_for_anchor::(anchor), + snapshot.summary_for_anchor::(anchor), resolved_offset, "anchor: {:?}", anchor @@ -2931,7 +3041,9 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { for _ in 0..10 { let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right); assert_eq!( - snapshot.reversed_chars_at(end_ix).collect::(), + snapshot + .reversed_chars_at(MultiBufferOffset(end_ix)) + .collect::(), expected_text[..end_ix].chars().rev().collect::(), ); } @@ -2941,7 +3053,7 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { let start_ix = rng.random_range(0..=end_ix); assert_eq!( snapshot - .bytes_in_range(start_ix..end_ix) + .bytes_in_range(MultiBufferOffset(start_ix)..MultiBufferOffset(end_ix)) .flatten() .copied() .collect::>(), @@ -2964,8 +3076,13 @@ async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) { let mut text = old_snapshot.text(); for edit in edits { - let new_text: String = snapshot.text_for_range(edit.new.clone()).collect(); - text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text); + let new_text: String = snapshot + .text_for_range(edit.new.start..edit.new.end) + .collect(); + text.replace_range( + edit.new.start.0..edit.new.start.0 + (edit.old.end.0 - edit.old.start.0), + &new_text, + ); } assert_eq!(text.to_string(), snapshot.text()); } @@ -3038,7 +3155,11 @@ fn test_history(cx: &mut App) { // Edit buffer 1 through the multibuffer now += 2 * group_interval; multibuffer.start_transaction_at(now, cx); - multibuffer.edit([(2..2, "C")], None, cx); + multibuffer.edit( + [(MultiBufferOffset(2)..MultiBufferOffset(2), "C")], + None, + cx, + ); multibuffer.end_transaction_at(now, cx); assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678"); @@ -3091,7 +3212,11 @@ fn test_history(cx: &mut App) { // Redo stack gets cleared after an edit. now += 2 * group_interval; multibuffer.start_transaction_at(now, cx); - multibuffer.edit([(0..0, "X")], None, cx); + multibuffer.edit( + [(MultiBufferOffset(0)..MultiBufferOffset(0), "X")], + None, + cx, + ); multibuffer.end_transaction_at(now, cx); assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678"); multibuffer.redo(cx); @@ -3320,7 +3445,7 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { ); assert_eq!(snapshot.max_point(), Point::new(2, 0)); - assert_eq!(snapshot.len(), 8); + assert_eq!(snapshot.len().0, 8); assert_eq!( snapshot @@ -3330,7 +3455,7 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { ); let (_, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap(); - assert_eq!(translated_offset, "one\n".len()); + assert_eq!(translated_offset.0, "one\n".len()); let (_, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); assert_eq!(translated_point, Point::new(1, 0)); @@ -3371,7 +3496,7 @@ fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) { let buffer_1_id = buffer_1.read_with(cx, |buffer_1, _| buffer_1.remote_id()); let (buffer, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap(); assert_eq!(buffer.remote_id(), buffer_1_id); - assert_eq!(translated_offset, "one\n".len()); + assert_eq!(translated_offset.0, "one\n".len()); let (buffer, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap(); assert_eq!(buffer.remote_id(), buffer_1_id); assert_eq!(translated_point, Point::new(1, 0)); @@ -3439,7 +3564,7 @@ fn assert_excerpts_match( fn assert_new_snapshot( multibuffer: &Entity, snapshot: &mut MultiBufferSnapshot, - subscription: &mut Subscription, + subscription: &mut Subscription, cx: &mut TestAppContext, expected_diff: &str, ) { @@ -3462,15 +3587,15 @@ fn assert_new_snapshot( fn check_edits( old_snapshot: &MultiBufferSnapshot, new_snapshot: &MultiBufferSnapshot, - edits: &[Edit], + edits: &[Edit], ) { let mut text = old_snapshot.text(); let new_text = new_snapshot.text(); for edit in edits.iter().rev() { - if !text.is_char_boundary(edit.old.start) - || !text.is_char_boundary(edit.old.end) - || !new_text.is_char_boundary(edit.new.start) - || !new_text.is_char_boundary(edit.new.end) + if !text.is_char_boundary(edit.old.start.0) + || !text.is_char_boundary(edit.old.end.0) + || !new_text.is_char_boundary(edit.new.start.0) + || !new_text.is_char_boundary(edit.new.end.0) { panic!( "invalid edits: {:?}\nold text: {:?}\nnew text: {:?}", @@ -3479,8 +3604,8 @@ fn check_edits( } text.replace_range( - edit.old.start..edit.old.end, - &new_text[edit.new.start..edit.new.end], + edit.old.start.0..edit.old.end.0, + &new_text[edit.new.start.0..edit.new.end.0], ); } @@ -3491,8 +3616,8 @@ fn check_edits( fn assert_chunks_in_ranges(snapshot: &MultiBufferSnapshot) { let full_text = snapshot.text(); for ix in 0..full_text.len() { - let mut chunks = snapshot.chunks(0..snapshot.len(), false); - chunks.seek(ix..snapshot.len()); + let mut chunks = snapshot.chunks(MultiBufferOffset(0)..snapshot.len(), false); + chunks.seek(MultiBufferOffset(ix)..snapshot.len()); let tail = chunks.map(|chunk| chunk.text).collect::(); assert_eq!(tail, &full_text[ix..], "seek to range: {:?}", ix..); } @@ -3522,44 +3647,49 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { let mut offsets = Vec::new(); let mut points = Vec::new(); for offset in 0..=text.len() + 1 { + let offset = MultiBufferOffset(offset); let clipped_left = snapshot.clip_offset(offset, Bias::Left); let clipped_right = snapshot.clip_offset(offset, Bias::Right); assert_eq!( - clipped_left, - text.clip_offset(offset, Bias::Left), + clipped_left.0, + text.clip_offset(offset.0, Bias::Left), "clip_offset({offset:?}, Left)" ); assert_eq!( - clipped_right, - text.clip_offset(offset, Bias::Right), + clipped_right.0, + text.clip_offset(offset.0, Bias::Right), "clip_offset({offset:?}, Right)" ); assert_eq!( snapshot.offset_to_point(clipped_left), - text.offset_to_point(clipped_left), - "offset_to_point({clipped_left})" + text.offset_to_point(clipped_left.0), + "offset_to_point({})", + clipped_left.0 ); assert_eq!( snapshot.offset_to_point(clipped_right), - text.offset_to_point(clipped_right), - "offset_to_point({clipped_right})" + text.offset_to_point(clipped_right.0), + "offset_to_point({})", + clipped_right.0 ); let anchor_after = snapshot.anchor_after(clipped_left); assert_eq!( anchor_after.to_offset(snapshot), clipped_left, - "anchor_after({clipped_left}).to_offset {anchor_after:?}" + "anchor_after({}).to_offset {anchor_after:?}", + clipped_left.0 ); let anchor_before = snapshot.anchor_before(clipped_left); assert_eq!( anchor_before.to_offset(snapshot), clipped_left, - "anchor_before({clipped_left}).to_offset" + "anchor_before({}).to_offset", + clipped_left.0 ); left_anchors.push(anchor_before); right_anchors.push(anchor_after); offsets.push(clipped_left); - points.push(text.offset_to_point(clipped_left)); + points.push(text.offset_to_point(clipped_left.0)); } for row in 0..text.max_point().row { @@ -3578,12 +3708,12 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { "clip_point({point:?}, Right)" ); assert_eq!( - snapshot.point_to_offset(clipped_left), + snapshot.point_to_offset(clipped_left).0, text.point_to_offset(clipped_left), "point_to_offset({clipped_left:?})" ); assert_eq!( - snapshot.point_to_offset(clipped_right), + snapshot.point_to_offset(clipped_right).0, text.point_to_offset(clipped_right), "point_to_offset({clipped_right:?})" ); @@ -3591,7 +3721,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { } assert_eq!( - snapshot.summaries_for_anchors::(&left_anchors), + snapshot.summaries_for_anchors::(&left_anchors), offsets, "left_anchors <-> offsets" ); @@ -3601,7 +3731,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { "left_anchors <-> points" ); assert_eq!( - snapshot.summaries_for_anchors::(&right_anchors), + snapshot.summaries_for_anchors::(&right_anchors), offsets, "right_anchors <-> offsets" ); @@ -3613,7 +3743,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { for (anchors, bias) in [(&left_anchors, Bias::Left), (&right_anchors, Bias::Right)] { for (ix, (offset, anchor)) in offsets.iter().zip(anchors).enumerate() { - if ix > 0 && *offset == 252 && offset > &offsets[ix - 1] { + if ix > 0 && *offset == MultiBufferOffset(252) && offset > &offsets[ix - 1] { let prev_anchor = left_anchors[ix - 1]; assert!( anchor.cmp(&prev_anchor, snapshot).is_gt(), @@ -3632,7 +3762,7 @@ fn assert_position_translation(snapshot: &MultiBufferSnapshot) { } if let Some((buffer, offset)) = snapshot.point_to_buffer_offset(snapshot.max_point()) { - assert!(offset <= buffer.len()); + assert!(offset.0 <= buffer.len()); } if let Some((buffer, point, _)) = snapshot.point_to_buffer_point(snapshot.max_point()) { assert!(point <= buffer.max_point()); @@ -3747,7 +3877,7 @@ fn test_random_chunk_bitmaps(cx: &mut App, mut rng: StdRng) { let snapshot = multibuffer.read(cx).snapshot(cx); - let chunks = snapshot.chunks(0..snapshot.len(), false); + let chunks = snapshot.chunks(MultiBufferOffset(0)..snapshot.len(), false); for chunk in chunks { let chunk_text = chunk.text; @@ -3879,24 +4009,24 @@ fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) { let mut ranges = Vec::new(); for _ in 0..rng.random_range(1..5) { - if snapshot.len() == 0 { + if snapshot.len().0 == 0 { break; } let diff_size = rng.random_range(5..1000); - let mut start = rng.random_range(0..snapshot.len()); + let mut start = rng.random_range(0..snapshot.len().0); while !text.is_char_boundary(start) { start = start.saturating_sub(1); } - let mut end = rng.random_range(start..snapshot.len().min(start + diff_size)); + let mut end = rng.random_range(start..snapshot.len().0.min(start + diff_size)); while !text.is_char_boundary(end) { end = end.saturating_add(1); } - let start_anchor = snapshot.anchor_after(start); - let end_anchor = snapshot.anchor_before(end); + let start_anchor = snapshot.anchor_after(MultiBufferOffset(start)); + let end_anchor = snapshot.anchor_before(MultiBufferOffset(end)); ranges.push(start_anchor..end_anchor); } multibuffer.expand_diff_hunks(ranges, cx); @@ -3905,7 +4035,7 @@ fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) { let snapshot = multibuffer.read(cx).snapshot(cx); - let chunks = snapshot.chunks(0..snapshot.len(), false); + let chunks = snapshot.chunks(MultiBufferOffset(0)..snapshot.len(), false); for chunk in chunks { let chunk_text = chunk.text; diff --git a/crates/multi_buffer/src/transaction.rs b/crates/multi_buffer/src/transaction.rs index 062d25d8233777190113aaa3e6a7f62396cfd08f..a65e394c8f1834a95ccbc70532aa03d2a3e6e34c 100644 --- a/crates/multi_buffer/src/transaction.rs +++ b/crates/multi_buffer/src/transaction.rs @@ -1,14 +1,14 @@ use gpui::{App, Context, Entity}; -use language::{self, Buffer, TextDimension, TransactionId}; +use language::{self, Buffer, TransactionId}; use std::{ collections::HashMap, - ops::{Range, Sub}, + ops::{AddAssign, Range, Sub}, time::{Duration, Instant}, }; use sum_tree::Bias; use text::BufferId; -use crate::BufferState; +use crate::{BufferState, MultiBufferDimension}; use super::{Event, ExcerptSummary, MultiBuffer}; @@ -320,7 +320,11 @@ impl MultiBuffer { cx: &App, ) -> Vec> where - D: TextDimension + Ord + Sub, + D: MultiBufferDimension + + Ord + + Sub + + AddAssign, + D::TextDimension: PartialOrd + Sub, { let Some(transaction) = self.history.transaction(transaction_id) else { return Vec::new(); @@ -336,24 +340,34 @@ impl MultiBuffer { }; let buffer = buffer_state.buffer.read(cx); - for range in buffer.edited_ranges_for_transaction_id::(*buffer_transaction) { + for range in + buffer.edited_ranges_for_transaction_id::(*buffer_transaction) + { for excerpt_id in &buffer_state.excerpts { cursor.seek(excerpt_id, Bias::Left); if let Some(excerpt) = cursor.item() && excerpt.locator == *excerpt_id { - let excerpt_buffer_start = excerpt.range.context.start.summary::(buffer); - let excerpt_buffer_end = excerpt.range.context.end.summary::(buffer); + let excerpt_buffer_start = excerpt + .range + .context + .start + .summary::(buffer); + let excerpt_buffer_end = excerpt + .range + .context + .end + .summary::(buffer); let excerpt_range = excerpt_buffer_start..excerpt_buffer_end; if excerpt_range.contains(&range.start) && excerpt_range.contains(&range.end) { - let excerpt_start = D::from_text_summary(&cursor.start().text); + let excerpt_start = D::from_summary(&cursor.start().text); let mut start = excerpt_start; - start.add_assign(&(range.start - excerpt_buffer_start)); + start += range.start - excerpt_buffer_start; let mut end = excerpt_start; - end.add_assign(&(range.end - excerpt_buffer_start)); + end += range.end - excerpt_buffer_start; ranges.push(start..end); break; diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 7ffbd5ef440996718337d839730a159b1f6593b7..7127627226d3aa55877f067038b69e6e848e1c3a 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -6,7 +6,7 @@ use std::{ use editor::scroll::ScrollOffset; use editor::{Anchor, AnchorRangeExt, Editor, scroll::Autoscroll}; -use editor::{RowHighlightOptions, SelectionEffects}; +use editor::{MultiBufferOffset, RowHighlightOptions, SelectionEffects}; use fuzzy::StringMatch; use gpui::{ App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle, @@ -247,7 +247,7 @@ impl PickerDelegate for OutlineViewDelegate { let buffer = editor.buffer().read(cx).snapshot(cx); let cursor_offset = editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .head(); (buffer, cursor_offset) }); @@ -259,8 +259,8 @@ impl PickerDelegate for OutlineViewDelegate { .map(|(ix, item)| { let range = item.range.to_offset(&buffer); let distance_to_closest_endpoint = cmp::min( - (range.start as isize - cursor_offset as isize).abs(), - (range.end as isize - cursor_offset as isize).abs(), + (range.start.0 as isize - cursor_offset.0 as isize).abs(), + (range.end.0 as isize - cursor_offset.0 as isize).abs(), ); let depth = if range.contains(&cursor_offset) { Some(item.depth) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index b21fd02a05c7fca93f09b436488318fdc3bd33c4..6a7036fce81eee5810dfbc41f57119efd22cfdca 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -7,7 +7,7 @@ use collections::{BTreeSet, HashMap, hash_map}; use command_palette_hooks::CommandPaletteFilter; use db::kvp::KEY_VALUE_STORE; use editor::{ - Editor, EditorEvent, + Editor, EditorEvent, MultiBufferOffset, items::{ entry_diagnostic_aware_icon_decoration_and_color, entry_diagnostic_aware_icon_name_and_color, entry_git_aware_label_color, @@ -1925,7 +1925,9 @@ impl ProjectPanel { self.filename_editor.update(cx, |editor, cx| { editor.set_text(file_name, window, cx); editor.change_selections(Default::default(), window, cx, |s| { - s.select_ranges([selection]) + s.select_ranges([ + MultiBufferOffset(selection.start)..MultiBufferOffset(selection.end) + ]) }); }); self.update_visible_entries(None, true, true, window, cx); diff --git a/crates/project_panel/src/project_panel_tests.rs b/crates/project_panel/src/project_panel_tests.rs index a85ba36c5297d7f40eb08ff42ddf086408a01316..6cf487bf9849a9252abc21504171b8c6bdf7e298 100644 --- a/crates/project_panel/src/project_panel_tests.rs +++ b/crates/project_panel/src/project_panel_tests.rs @@ -1,5 +1,6 @@ use super::*; use collections::HashSet; +use editor::MultiBufferOffset; use gpui::{Empty, Entity, TestAppContext, VisualTestContext, WindowHandle}; use pretty_assertions::assert_eq; use project::FakeFs; @@ -658,7 +659,9 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) { let confirm = panel.update_in(cx, |panel, window, cx| { panel.filename_editor.update(cx, |editor, cx| { - let file_name_selections = editor.selections.all::(&editor.display_snapshot(cx)); + let file_name_selections = editor + .selections + .all::(&editor.display_snapshot(cx)); assert_eq!( file_name_selections.len(), 1, @@ -666,12 +669,13 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) { ); let file_name_selection = &file_name_selections[0]; assert_eq!( - file_name_selection.start, 0, + file_name_selection.start, + MultiBufferOffset(0), "Should select the file name from the start" ); assert_eq!( file_name_selection.end, - "another-filename".len(), + MultiBufferOffset("another-filename".len()), "Should not select file extension" ); @@ -732,11 +736,11 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) { panel.update_in(cx, |panel, window, cx| { panel.filename_editor.update(cx, |editor, cx| { - let file_name_selections = editor.selections.all::(&editor.display_snapshot(cx)); + let file_name_selections = editor.selections.all::(&editor.display_snapshot(cx)); assert_eq!(file_name_selections.len(), 1, "File editing should have a single selection, but got: {file_name_selections:?}"); let file_name_selection = &file_name_selections[0]; - assert_eq!(file_name_selection.start, 0, "Should select the file name from the start"); - assert_eq!(file_name_selection.end, "a-different-filename.tar".len(), "Should not select file extension, but still may select anything up to the last dot.."); + assert_eq!(file_name_selection.start, MultiBufferOffset(0), "Should select the file name from the start"); + assert_eq!(file_name_selection.end, MultiBufferOffset("a-different-filename.tar".len()), "Should not select file extension, but still may select anything up to the last dot.."); }); panel.cancel(&menu::Cancel, window, cx) @@ -1218,7 +1222,9 @@ async fn test_copy_paste(cx: &mut gpui::TestAppContext) { panel.update_in(cx, |panel, window, cx| { panel.filename_editor.update(cx, |editor, cx| { - let file_name_selections = editor.selections.all::(&editor.display_snapshot(cx)); + let file_name_selections = editor + .selections + .all::(&editor.display_snapshot(cx)); assert_eq!( file_name_selections.len(), 1, @@ -1227,12 +1233,12 @@ async fn test_copy_paste(cx: &mut gpui::TestAppContext) { let file_name_selection = &file_name_selections[0]; assert_eq!( file_name_selection.start, - "one".len(), + MultiBufferOffset("one".len()), "Should select the file name disambiguation after the original file name" ); assert_eq!( file_name_selection.end, - "one copy".len(), + MultiBufferOffset("one copy".len()), "Should select the file name disambiguation until the extension" ); }); diff --git a/crates/repl/src/repl_editor.rs b/crates/repl/src/repl_editor.rs index 84293fb27fdac7cfec7c3b7d38ecbc6527345e5b..9e52637ab75c02b14e798600584fe18ca3f55805 100644 --- a/crates/repl/src/repl_editor.rs +++ b/crates/repl/src/repl_editor.rs @@ -4,7 +4,7 @@ use std::ops::Range; use std::sync::Arc; use anyhow::{Context as _, Result}; -use editor::Editor; +use editor::{Editor, MultiBufferOffset}; use gpui::{App, Entity, WeakEntity, Window, prelude::*}; use language::{BufferSnapshot, Language, LanguageName, Point}; use project::{ProjectItem as _, WorktreeId}; @@ -478,7 +478,9 @@ fn get_language(editor: WeakEntity, cx: &mut App) -> Option(&display_snapshot); + let selection = editor + .selections + .newest::(&display_snapshot); display_snapshot .buffer_snapshot() .language_at(selection.head()) diff --git a/crates/rope/src/offset_utf16.rs b/crates/rope/src/offset_utf16.rs index 9a52b3c3f900788262696fe136dcc7d5995b3a5a..1223fbbe3898285cb7da5124f2f5a2cbe45a1f64 100644 --- a/crates/rope/src/offset_utf16.rs +++ b/crates/rope/src/offset_utf16.rs @@ -32,7 +32,6 @@ impl Sub for OffsetUtf16 { type Output = OffsetUtf16; fn sub(self, other: Self) -> Self::Output { - debug_assert!(other <= self); Self(self.0 - other.0) } } diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index a5699554a32b552e395001ded24512e10d645d4b..ad39022c0d6181bd5d5f4fdfc1b84ea4a667340d 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -1534,39 +1534,63 @@ where } } -impl ops::Sub for DimensionPair +impl ops::Sub for DimensionPair where - K: ops::Sub, - V: ops::Sub, + K: ops::Sub, + V: ops::Sub, { - type Output = Self; + type Output = DimensionPair; fn sub(self, rhs: Self) -> Self::Output { - Self { + DimensionPair { key: self.key - rhs.key, value: self.value.zip(rhs.value).map(|(a, b)| a - b), } } } +impl ops::AddAssign> for DimensionPair +where + K: ops::AddAssign, + V: ops::AddAssign, +{ + fn add_assign(&mut self, rhs: DimensionPair) { + self.key += rhs.key; + if let Some(value) = &mut self.value { + if let Some(other_value) = rhs.value { + *value += other_value; + } else { + self.value.take(); + } + } + } +} + +impl std::ops::AddAssign> for Point { + fn add_assign(&mut self, rhs: DimensionPair) { + *self += rhs.key; + } +} + impl cmp::Eq for DimensionPair where K: cmp::Eq {} -impl<'a, K, V> sum_tree::Dimension<'a, ChunkSummary> for DimensionPair +impl<'a, K, V, S> sum_tree::Dimension<'a, S> for DimensionPair where - K: sum_tree::Dimension<'a, ChunkSummary>, - V: sum_tree::Dimension<'a, ChunkSummary>, + S: sum_tree::Summary, + K: sum_tree::Dimension<'a, S>, + V: sum_tree::Dimension<'a, S>, { - fn zero(_cx: ()) -> Self { + fn zero(cx: S::Context<'_>) -> Self { Self { - key: K::zero(_cx), - value: Some(V::zero(_cx)), + key: K::zero(cx), + value: Some(V::zero(cx)), } } - fn add_summary(&mut self, summary: &'a ChunkSummary, _cx: ()) { - self.key.add_summary(summary, _cx); + fn add_summary(&mut self, summary: &'a S, cx: S::Context<'_>) { + self.key.add_summary(summary, cx); if let Some(value) = &mut self.value { - value.add_summary(summary, _cx); + value.add_summary(summary, cx); } } } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index a601f5a683f2c464e792c351c566358212bdf312..0b45455faea1c6cd4474ac630d725ee57e1021f4 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -10,7 +10,7 @@ use any_vec::AnyVec; use anyhow::Context as _; use collections::HashMap; use editor::{ - DisplayPoint, Editor, EditorSettings, + DisplayPoint, Editor, EditorSettings, MultiBufferOffset, actions::{Backtab, Tab}, }; use futures::channel::oneshot; @@ -868,7 +868,11 @@ impl BufferSearchBar { .buffer() .update(cx, |replacement_buffer, cx| { let len = replacement_buffer.len(cx); - replacement_buffer.edit([(0..len, replacement.unwrap())], None, cx); + replacement_buffer.edit( + [(MultiBufferOffset(0)..len, replacement.unwrap())], + None, + cx, + ); }); }); } @@ -892,7 +896,7 @@ impl BufferSearchBar { self.query_editor.update(cx, |query_editor, cx| { query_editor.buffer().update(cx, |query_buffer, cx| { let len = query_buffer.len(cx); - query_buffer.edit([(0..len, query)], None, cx); + query_buffer.edit([(MultiBufferOffset(0)..len, query)], None, cx); }); }); self.set_search_options(options, cx); diff --git a/crates/tasks_ui/src/tasks_ui.rs b/crates/tasks_ui/src/tasks_ui.rs index b0185f98568399326ab0e45ffe713f7f1dc504fb..35c8a2ee220c6dba3732ca0f323bc50eb592ce19 100644 --- a/crates/tasks_ui/src/tasks_ui.rs +++ b/crates/tasks_ui/src/tasks_ui.rs @@ -392,7 +392,7 @@ fn worktree_context(worktree_abs_path: &Path) -> TaskContext { mod tests { use std::{collections::HashMap, sync::Arc}; - use editor::{Editor, SelectionEffects}; + use editor::{Editor, MultiBufferOffset, SelectionEffects}; use gpui::TestAppContext; use language::{Language, LanguageConfig}; use project::{BasicContextProvider, FakeFs, Project, task_store::TaskStore}; @@ -539,7 +539,7 @@ mod tests { // And now, let's select an identifier. editor2.update_in(cx, |editor, window, cx| { editor.change_selections(SelectionEffects::no_scroll(), window, cx, |selections| { - selections.select_ranges([14..18]) + selections.select_ranges([MultiBufferOffset(14)..MultiBufferOffset(18)]) }) }); diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index 349d557fab8c980901149698223cae78739797d8..e355f70c492ff3cdf2632f4e6204723fb05c9235 100644 --- a/crates/text/src/selection.rs +++ b/crates/text/src/selection.rs @@ -130,9 +130,15 @@ impl Selection { } } -impl Selection { +impl Selection { + pub fn len(&self) -> ::Output { + self.end - self.start + } +} + +impl Selection { #[cfg(feature = "test-support")] - pub fn from_offset(offset: usize) -> Self { + pub fn from_offset(offset: T) -> Self { Selection { id: 0, start: offset, @@ -142,7 +148,7 @@ impl Selection { } } - pub fn equals(&self, offset_range: &Range) -> bool { + pub fn equals(&self, offset_range: &Range) -> bool { self.start == offset_range.start && self.end == offset_range.end } } diff --git a/crates/text/src/subscription.rs b/crates/text/src/subscription.rs index 878e8a2cfe0a82782300089881b8cf31b428d2c2..50857a2de4ca2f9a89514a482973a0d14cce2163 100644 --- a/crates/text/src/subscription.rs +++ b/crates/text/src/subscription.rs @@ -6,36 +6,55 @@ use std::{ }; #[derive(Default)] -pub struct Topic(Mutex>>>>); +pub struct Topic(Mutex>>>>); -pub struct Subscription(Arc>>); +pub struct Subscription(Arc>>); -impl Topic { - pub fn subscribe(&mut self) -> Subscription { +impl Topic +where + T: 'static + + Copy + + Ord + + std::ops::Sub + + std::ops::Add + + std::ops::AddAssign + + Default, + TDelta: Ord + Copy, +{ + pub fn subscribe(&mut self) -> Subscription { let subscription = Subscription(Default::default()); self.0.get_mut().push(Arc::downgrade(&subscription.0)); subscription } - pub fn publish(&self, edits: impl Clone + IntoIterator>) { + pub fn publish(&self, edits: impl Clone + IntoIterator>) { publish(&mut self.0.lock(), edits); } - pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator>) { + pub fn publish_mut(&mut self, edits: impl Clone + IntoIterator>) { publish(self.0.get_mut(), edits); } } -impl Subscription { - pub fn consume(&self) -> Patch { +impl Subscription { + pub fn consume(&self) -> Patch { mem::take(&mut *self.0.lock()) } } -fn publish( - subscriptions: &mut Vec>>>, - edits: impl Clone + IntoIterator>, -) { +fn publish( + subscriptions: &mut Vec>>>, + edits: impl Clone + IntoIterator>, +) where + T: 'static + + Copy + + Ord + + std::ops::Sub + + std::ops::Add + + std::ops::AddAssign + + Default, + TDelta: Ord + Copy, +{ subscriptions.retain(|subscription| { if let Some(subscription) = subscription.upgrade() { let mut patch = subscription.lock(); diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 316bdb59faed8438d9664a904c7900491c59376b..e476103879d700dc6121882055bc7e2cabf3ed5a 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -54,7 +54,7 @@ pub struct Buffer { deferred_ops: OperationQueue, deferred_replicas: HashSet, pub lamport_clock: clock::Lamport, - subscriptions: Topic, + subscriptions: Topic, edit_id_resolvers: HashMap>>, wait_for_version_txs: Vec<(clock::Global, oneshot::Sender<()>)>, } @@ -1619,7 +1619,7 @@ impl Buffer { self.edited_ranges_for_edit_ids(&transaction.edit_ids) } - pub fn subscribe(&mut self) -> Subscription { + pub fn subscribe(&mut self) -> Subscription { self.subscriptions.subscribe() } diff --git a/crates/vim/src/helix.rs b/crates/vim/src/helix.rs index e1cc58b89560e46a68e05fe6c8e75dbefb4e3e83..eb0749794adb321d8ce19f8ad5adcf67b9a41bba 100644 --- a/crates/vim/src/helix.rs +++ b/crates/vim/src/helix.rs @@ -6,8 +6,8 @@ mod select; use editor::display_map::DisplaySnapshot; use editor::{ - DisplayPoint, Editor, EditorSettings, HideMouseCursorOrigin, SelectionEffects, ToOffset, - ToPoint, movement, + DisplayPoint, Editor, EditorSettings, HideMouseCursorOrigin, MultiBufferOffset, + SelectionEffects, ToOffset, ToPoint, movement, }; use gpui::actions; use gpui::{Context, Window}; @@ -523,7 +523,7 @@ impl Vim { ..range.end.to_offset(&display_map, Bias::Left); if !byte_range.is_empty() { - let replacement_text = text.repeat(byte_range.len()); + let replacement_text = text.repeat(byte_range.end - byte_range.start); edits.push((byte_range, replacement_text)); } } @@ -620,7 +620,7 @@ impl Vim { self.update_editor(cx, |_, editor, cx| { let newest = editor .selections - .newest::(&editor.display_snapshot(cx)); + .newest::(&editor.display_snapshot(cx)); editor.change_selections(Default::default(), window, cx, |s| s.select(vec![newest])); }); } diff --git a/crates/vim/src/helix/boundary.rs b/crates/vim/src/helix/boundary.rs index a6de926bc5a10415dad6584f3d07476b2bf0e5d7..0c2ebbeef00a306a388756455bfb6ffcd40395e8 100644 --- a/crates/vim/src/helix/boundary.rs +++ b/crates/vim/src/helix/boundary.rs @@ -1,10 +1,7 @@ -use std::{ - cmp::Ordering, - ops::{Deref, DerefMut, Range}, -}; +use std::{cmp::Ordering, ops::Range}; use editor::{ - DisplayPoint, + DisplayPoint, MultiBufferOffset, display_map::{DisplaySnapshot, ToDisplayPoint}, movement, }; @@ -104,8 +101,8 @@ trait BoundedObject { let next_end = self.next_end(map, end_search_start, outer)?; let maybe_next_start = self.next_start(map, start_search_start, outer); if let Some(next_start) = maybe_next_start - && (*next_start < *next_end - || *next_start == *next_end && self.can_be_zero_width(outer)) + && (next_start.0 < next_end.0 + || next_start.0 == next_end.0 && self.can_be_zero_width(outer)) && !self.ambiguous_outer() { let closing = self.close_at_end(next_start, map, outer)?; @@ -133,8 +130,8 @@ trait BoundedObject { let previous_start = self.previous_start(map, start_search_end, outer)?; let maybe_previous_end = self.previous_end(map, end_search_end, outer); if let Some(previous_end) = maybe_previous_end - && (*previous_end > *previous_start - || *previous_end == *previous_start && self.can_be_zero_width(outer)) + && (previous_end.0 > previous_start.0 + || previous_end.0 == previous_start.0 && self.can_be_zero_width(outer)) && !self.ambiguous_outer() { let closing = self.close_at_start(previous_end, map, outer)?; @@ -151,30 +148,22 @@ trait BoundedObject { } } -#[derive(Clone, Copy, PartialEq, Debug)] -struct Offset(usize); -impl Deref for Offset { - type Target = usize; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for Offset { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} +#[derive(Clone, Copy, PartialEq, Debug, PartialOrd, Ord, Eq)] +struct Offset(MultiBufferOffset); impl Offset { fn next(self, map: &DisplaySnapshot) -> Option { - let next = Self(map.buffer_snapshot().clip_offset(*self + 1, Bias::Right)); - (*next > *self).then(|| next) + let next = Self( + map.buffer_snapshot() + .clip_offset(self.0 + 1usize, Bias::Right), + ); + (next.0 > self.0).then(|| next) } fn previous(self, map: &DisplaySnapshot) -> Option { - if *self == 0 { + if self.0 == MultiBufferOffset(0) { return None; } Some(Self( - map.buffer_snapshot().clip_offset(*self - 1, Bias::Left), + map.buffer_snapshot().clip_offset(self.0 - 1, Bias::Left), )) } fn range( @@ -211,7 +200,7 @@ impl HelixTextObject for B { let max_end = self.close_at_end(search_start, map, find_outer)?; let min_start = self.close_at_start(max_end, map, find_outer)?; - (*min_start <= *relative_to.start).then(|| min_start..max_end) + (min_start <= relative_to.start).then(|| min_start..max_end) }) } @@ -279,8 +268,8 @@ fn relative_range( min_start..max_end }; - let start = wanted_range.start.clone().to_display_point(map); - let end = wanted_range.end.clone().to_display_point(map); + let start = wanted_range.start.0.to_display_point(map); + let end = wanted_range.end.0.to_display_point(map); Some(start..end) } @@ -390,7 +379,7 @@ impl ImmediateBoundary { impl BoundedObject for ImmediateBoundary { fn next_start(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option { try_find_boundary(map, from, |left, right| { - let classifier = map.buffer_snapshot().char_classifier_at(*from); + let classifier = map.buffer_snapshot().char_classifier_at(from.0); if outer { self.is_outer_start(left, right, classifier) } else { @@ -400,7 +389,7 @@ impl BoundedObject for ImmediateBoundary { } fn next_end(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option { try_find_boundary(map, from, |left, right| { - let classifier = map.buffer_snapshot().char_classifier_at(*from); + let classifier = map.buffer_snapshot().char_classifier_at(from.0); if outer { self.is_outer_end(left, right, classifier) } else { @@ -410,7 +399,7 @@ impl BoundedObject for ImmediateBoundary { } fn previous_start(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option { try_find_preceding_boundary(map, from, |left, right| { - let classifier = map.buffer_snapshot().char_classifier_at(*from); + let classifier = map.buffer_snapshot().char_classifier_at(from.0); if outer { self.is_outer_start(left, right, classifier) } else { @@ -420,7 +409,7 @@ impl BoundedObject for ImmediateBoundary { } fn previous_end(&self, map: &DisplaySnapshot, from: Offset, outer: bool) -> Option { try_find_preceding_boundary(map, from, |left, right| { - let classifier = map.buffer_snapshot().char_classifier_at(*from); + let classifier = map.buffer_snapshot().char_classifier_at(from.0); if outer { self.is_outer_end(left, right, classifier) } else { @@ -572,7 +561,7 @@ impl FuzzyBoundary { boundary_kind: Boundary, ) -> Option { let generate_boundary_data = |left, right, point: Offset| { - let classifier = map.buffer_snapshot().char_classifier_at(*from); + let classifier = map.buffer_snapshot().char_classifier_at(from.0); let reach_boundary = if outer && boundary_kind == Boundary::Start { self.is_near_potential_outer_start(left, right, &classifier) } else if !outer && boundary_kind == Boundary::Start { @@ -598,9 +587,9 @@ impl FuzzyBoundary { Ordering::Greater => !backward, }); if backward { - boundaries.max_by_key(|boundary| **boundary) + boundaries.max_by_key(|boundary| *boundary) } else { - boundaries.min_by_key(|boundary| **boundary) + boundaries.min_by_key(|boundary| *boundary) } } } @@ -662,15 +651,15 @@ fn try_find_boundary_data( ) -> Option { let mut prev_ch = map .buffer_snapshot() - .reversed_chars_at(*from) + .reversed_chars_at(from.0) .next() .unwrap_or('\0'); - for ch in map.buffer_snapshot().chars_at(*from).chain(['\0']) { + for ch in map.buffer_snapshot().chars_at(from.0).chain(['\0']) { if let Some(boundary_information) = boundary_information(prev_ch, ch, from) { return Some(boundary_information); } - *from += ch.len_utf8(); + from.0 += ch.len_utf8(); prev_ch = ch; } @@ -702,13 +691,21 @@ fn try_find_preceding_boundary_data( mut from: Offset, is_boundary: impl Fn(char, char, Offset) -> Option, ) -> Option { - let mut prev_ch = map.buffer_snapshot().chars_at(*from).next().unwrap_or('\0'); + let mut prev_ch = map + .buffer_snapshot() + .chars_at(from.0) + .next() + .unwrap_or('\0'); - for ch in map.buffer_snapshot().reversed_chars_at(*from).chain(['\0']) { + for ch in map + .buffer_snapshot() + .reversed_chars_at(from.0) + .chain(['\0']) + { if let Some(boundary_information) = is_boundary(ch, prev_ch, from) { return Some(boundary_information); } - from.0 = from.0.saturating_sub(ch.len_utf8()); + from.0.0 = from.0.0.saturating_sub(ch.len_utf8()); prev_ch = ch; } diff --git a/crates/vim/src/helix/duplicate.rs b/crates/vim/src/helix/duplicate.rs index 1b1f10b00b6a7381f22c6ec3be674dc2c085eff6..37796c57aa0b9e27f2d7d786c9b8870e49d5871e 100644 --- a/crates/vim/src/helix/duplicate.rs +++ b/crates/vim/src/helix/duplicate.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use editor::{DisplayPoint, display_map::DisplaySnapshot}; +use editor::{DisplayPoint, MultiBufferOffset, display_map::DisplaySnapshot}; use gpui::Context; use text::Bias; use ui::Window; @@ -111,7 +111,7 @@ fn find_next_valid_duplicate_space( fn display_point_range_to_offset_range( range: &Range, map: &DisplaySnapshot, -) -> Range { +) -> Range { range.start.to_offset(map, Bias::Left)..range.end.to_offset(map, Bias::Right) } diff --git a/crates/vim/src/helix/paste.rs b/crates/vim/src/helix/paste.rs index 67af7650011b0220f4ad05cebb6badf5d0ba7aa7..d91b138853abb07dc10957a4ee1f5af158066e06 100644 --- a/crates/vim/src/helix/paste.rs +++ b/crates/vim/src/helix/paste.rs @@ -125,7 +125,7 @@ impl Vim { s.select_ranges(new_selections.into_iter().map(|(anchor, len)| { let offset = anchor.to_offset(&snapshot); if action.before { - offset.saturating_sub(len)..offset + offset.saturating_sub_usize(len)..offset } else { offset..(offset + len) } diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index fd4171a36bc5baf0dc1cc60efe707fa275e4be81..b0faa7bb068135a3feafc507e4f8a6ed97863e8c 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -1,5 +1,5 @@ use editor::{ - Anchor, Bias, DisplayPoint, Editor, RowExt, ToOffset, ToPoint, + Anchor, Bias, BufferOffset, DisplayPoint, Editor, MultiBufferOffset, RowExt, ToOffset, ToPoint, display_map::{DisplayRow, DisplaySnapshot, FoldPoint, ToDisplayPoint}, movement::{ self, FindRange, TextLayoutDetails, find_boundary, find_preceding_boundary_display_point, @@ -2143,7 +2143,7 @@ pub(crate) fn sentence_backwards( if start_of_next_sentence < start { times = times.saturating_sub(1); } - if times == 0 || offset == 0 { + if times == 0 || offset.0 == 0 { return map.clip_point( start_of_next_sentence .to_offset(&map.buffer_snapshot()) @@ -2207,7 +2207,7 @@ pub(crate) fn sentence_forwards( map.max_point() } -fn next_non_blank(map: &DisplaySnapshot, start: usize) -> usize { +fn next_non_blank(map: &DisplaySnapshot, start: MultiBufferOffset) -> MultiBufferOffset { for (c, o) in map.buffer_chars_at(start) { if c == '\n' || !c.is_whitespace() { return o; @@ -2219,7 +2219,10 @@ fn next_non_blank(map: &DisplaySnapshot, start: usize) -> usize { // given the offset after a ., !, or ? find the start of the next sentence. // if this is not a sentence boundary, returns None. -fn start_of_next_sentence(map: &DisplaySnapshot, end_of_sentence: usize) -> Option { +fn start_of_next_sentence( + map: &DisplaySnapshot, + end_of_sentence: MultiBufferOffset, +) -> Option { let chars = map.buffer_chars_at(end_of_sentence); let mut seen_space = false; @@ -2253,10 +2256,10 @@ fn go_to_line(map: &DisplaySnapshot, display_point: DisplayPoint, line: usize) - .clip_point(Point::new((line - 1) as u32, point.column), Bias::Left), ); let buffer_range = excerpt.buffer_range(); - if offset >= buffer_range.start && offset <= buffer_range.end { + if offset >= buffer_range.start.0 && offset <= buffer_range.end.0 { let point = map .buffer_snapshot() - .offset_to_point(excerpt.map_offset_from_buffer(offset)); + .offset_to_point(excerpt.map_offset_from_buffer(BufferOffset(offset))); return map.clip_point(map.point_to_display_point(point, Bias::Left), Bias::Left); } let mut last_position = None; @@ -2360,6 +2363,9 @@ fn matching_tag(map: &DisplaySnapshot, head: DisplayPoint) -> Option DisplayPoint { + if !map.is_singleton() { + return display_point; + } // https://github.com/vim/vim/blob/1d87e11a1ef201b26ed87585fba70182ad0c468a/runtime/doc/motion.txt#L1200 let display_point = map.clip_at_line_end(display_point); let point = display_point.to_point(map); @@ -2375,9 +2381,10 @@ fn matching(map: &DisplaySnapshot, display_point: DisplayPoint) -> DisplayPoint // Attempt to find the smallest enclosing bracket range that also contains // the offset, which only happens if the cursor is currently in a bracket. let range_filter = |_buffer: &language::BufferSnapshot, - opening_range: Range, - closing_range: Range| { - opening_range.contains(&offset) || closing_range.contains(&offset) + opening_range: Range, + closing_range: Range| { + opening_range.contains(&BufferOffset(offset.0)) + || closing_range.contains(&BufferOffset(offset.0)) }; let bracket_ranges = snapshot @@ -2840,7 +2847,7 @@ fn method_motion( for _ in 0..times { let point = map.display_point_to_point(display_point, Bias::Left); - let offset = point.to_offset(&map.buffer_snapshot()); + let offset = point.to_offset(&map.buffer_snapshot()).0; let range = if direction == Direction::Prev { 0..offset } else { @@ -2869,7 +2876,7 @@ fn method_motion( } else { possibilities.min().unwrap_or(offset) }; - let new_point = map.clip_point(dest.to_display_point(map), Bias::Left); + let new_point = map.clip_point(MultiBufferOffset(dest).to_display_point(map), Bias::Left); if new_point == display_point { break; } @@ -2890,7 +2897,7 @@ fn comment_motion( for _ in 0..times { let point = map.display_point_to_point(display_point, Bias::Left); - let offset = point.to_offset(&map.buffer_snapshot()); + let offset = point.to_offset(&map.buffer_snapshot()).0; let range = if direction == Direction::Prev { 0..offset } else { @@ -2923,7 +2930,7 @@ fn comment_motion( } else { possibilities.min().unwrap_or(offset) }; - let new_point = map.clip_point(dest.to_display_point(map), Bias::Left); + let new_point = map.clip_point(MultiBufferOffset(dest).to_display_point(map), Bias::Left); if new_point == display_point { break; } @@ -2946,7 +2953,7 @@ fn section_motion( .display_point_to_point(display_point, Bias::Left) .to_offset(&map.buffer_snapshot()); let range = if direction == Direction::Prev { - 0..offset + MultiBufferOffset(0)..offset } else { offset..map.buffer_snapshot().len() }; @@ -2977,7 +2984,7 @@ fn section_motion( let relevant = if is_start { range.start } else { range.end }; if direction == Direction::Prev && relevant < offset { Some(relevant) - } else if direction == Direction::Next && relevant > offset + 1 { + } else if direction == Direction::Next && relevant > offset + 1usize { Some(relevant) } else { None @@ -2985,7 +2992,7 @@ fn section_motion( }); let offset = if direction == Direction::Prev { - possibilities.max().unwrap_or(0) + possibilities.max().unwrap_or(MultiBufferOffset(0)) } else { possibilities.min().unwrap_or(map.buffer_snapshot().len()) }; diff --git a/crates/vim/src/normal/increment.rs b/crates/vim/src/normal/increment.rs index 7eadf5053a15f33946031a332cb8a7f2dcb8ed52..a6a76b22aa16d6fd774dc32a8b4988804ad8e42c 100644 --- a/crates/vim/src/normal/increment.rs +++ b/crates/vim/src/normal/increment.rs @@ -211,9 +211,14 @@ fn find_target( let mut pre_char = String::new(); // Backward scan to find the start of the number, but stop at start_offset - for ch in snapshot.reversed_chars_at(offset + if offset < snapshot.len() { 1 } else { 0 }) { + let next_offset = if offset < snapshot.len() { + offset + 1usize + } else { + offset + }; + for ch in snapshot.reversed_chars_at(next_offset) { // Search boundaries - if offset == 0 || ch.is_whitespace() || (need_range && offset <= start_offset) { + if offset.0 == 0 || ch.is_whitespace() || (need_range && offset <= start_offset) { break; } diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index 74a28322d13b6ab0f563e6953f6b1edbfea66740..77305ea783c34e340a7ed840658088f3e6191abb 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -1,4 +1,7 @@ -use editor::{DisplayPoint, RowExt, SelectionEffects, display_map::ToDisplayPoint, movement}; +use editor::{ + DisplayPoint, MultiBufferOffset, RowExt, SelectionEffects, display_map::ToDisplayPoint, + movement, +}; use gpui::{Action, Context, Window}; use language::{Bias, SelectionGoal}; use schemars::JsonSchema; @@ -174,7 +177,10 @@ impl Vim { original_indent_columns.push(original_indent_column); } - let cursor_offset = editor.selections.last::(&display_map).head(); + let cursor_offset = editor + .selections + .last::(&display_map) + .head(); if editor .buffer() .read(cx) diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index f361dd8f274879f067c49bf04c0a73ebbc34be06..2f5ccac07bfe5f6f11b048e317523292dd74294d 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -6,7 +6,7 @@ use crate::{ state::{Mode, Operator}, }; use editor::{ - Bias, DisplayPoint, Editor, ToOffset, + Bias, BufferOffset, DisplayPoint, Editor, MultiBufferOffset, ToOffset, display_map::{DisplaySnapshot, ToDisplayPoint}, movement::{self, FindRange}, }; @@ -81,8 +81,8 @@ pub struct CandidateRange { #[derive(Debug, Clone)] pub struct CandidateWithRanges { candidate: CandidateRange, - open_range: Range, - close_range: Range, + open_range: Range, + close_range: Range, } /// Selects text at the same indentation level. @@ -120,7 +120,7 @@ struct CurlyBrackets { opening: bool, } -fn cover_or_next, Range)>>( +fn cover_or_next, Range)>>( candidates: Option, caret: DisplayPoint, map: &DisplaySnapshot, @@ -128,7 +128,7 @@ fn cover_or_next, Range)>>( let caret_offset = caret.to_offset(map, Bias::Left); let mut covering = vec![]; let mut next_ones = vec![]; - let snapshot = &map.buffer_snapshot(); + let snapshot = map.buffer_snapshot(); if let Some(ranges) = candidates { for (open_range, close_range) in ranges { @@ -171,7 +171,7 @@ fn cover_or_next, Range)>>( if !next_ones.is_empty() { return next_ones.into_iter().min_by_key(|r| { let start = r.candidate.start.to_offset(map, Bias::Left); - (start as isize - caret_offset as isize).abs() + (start.0 as isize - caret_offset.0 as isize).abs() }); } @@ -181,8 +181,8 @@ fn cover_or_next, Range)>>( type DelimiterPredicate = dyn Fn(&BufferSnapshot, usize, usize) -> bool; struct DelimiterRange { - open: Range, - close: Range, + open: Range, + close: Range, } impl DelimiterRange { @@ -221,14 +221,14 @@ fn find_mini_delimiters( .buffer_snapshot() .bracket_ranges(visible_line_range) .map(|ranges| { - ranges.filter_map(move |(open, close)| { + ranges.filter_map(|(open, close)| { // Convert the ranges from multibuffer space to buffer space as // that is what `is_valid_delimiter` expects, otherwise it might // panic as the values might be out of bounds. let buffer_open = excerpt.map_range_to_buffer(open.clone()); let buffer_close = excerpt.map_range_to_buffer(close.clone()); - if is_valid_delimiter(buffer, buffer_open.start, buffer_close.start) { + if is_valid_delimiter(buffer, buffer_open.start.0, buffer_close.start.0) { Some((open, close)) } else { None @@ -252,8 +252,12 @@ fn find_mini_delimiters( Some( DelimiterRange { - open: open_bracket, - close: close_bracket, + open: excerpt.map_range_from_buffer( + BufferOffset(open_bracket.start)..BufferOffset(open_bracket.end), + ), + close: excerpt.map_range_from_buffer( + BufferOffset(close_bracket.start)..BufferOffset(close_bracket.end), + ), } .to_display_range(map, around), ) @@ -899,7 +903,7 @@ pub fn surrounding_html_tag( // Find the most closest to current offset let mut cursor = buffer.syntax_layer_at(offset)?.node().walk(); let mut last_child_node = cursor.node(); - while cursor.goto_first_child_for_byte(offset).is_some() { + while cursor.goto_first_child_for_byte(offset.0).is_some() { last_child_node = cursor.node(); } @@ -916,10 +920,16 @@ pub fn surrounding_html_tag( - range.start.to_offset(map, Bias::Left) <= 1 { - offset <= last_child.end_byte() + offset.0 <= last_child.end_byte() } else { - range.start.to_offset(map, Bias::Left) >= first_child.start_byte() - && range.end.to_offset(map, Bias::Left) <= last_child.start_byte() + 1 + excerpt + .map_offset_to_buffer(range.start.to_offset(map, Bias::Left)) + .0 + >= first_child.start_byte() + && excerpt + .map_offset_to_buffer(range.end.to_offset(map, Bias::Left)) + .0 + <= last_child.start_byte() + 1 }; if open_tag.is_some() && open_tag == close_tag && is_valid { let range = if around { @@ -927,6 +937,7 @@ pub fn surrounding_html_tag( } else { first_child.byte_range().end..last_child.byte_range().start }; + let range = BufferOffset(range.start)..BufferOffset(range.end); if excerpt.contains_buffer_range(range.clone()) { let result = excerpt.map_range_from_buffer(range); return Some( @@ -1093,7 +1104,8 @@ fn text_object( .collect(); matches.sort_by_key(|r| r.end - r.start); if let Some(buffer_range) = matches.first() { - let range = excerpt.map_range_from_buffer(buffer_range.clone()); + let buffer_range = BufferOffset(buffer_range.start)..BufferOffset(buffer_range.end); + let range = excerpt.map_range_from_buffer(buffer_range); return Some(range.start.to_display_point(map)..range.end.to_display_point(map)); } @@ -1113,10 +1125,12 @@ fn text_object( if let Some(buffer_range) = matches.first() && !buffer_range.is_empty() { - let range = excerpt.map_range_from_buffer(buffer_range.clone()); + let buffer_range = BufferOffset(buffer_range.start)..BufferOffset(buffer_range.end); + let range = excerpt.map_range_from_buffer(buffer_range); return Some(range.start.to_display_point(map)..range.end.to_display_point(map)); } - let buffer_range = excerpt.map_range_from_buffer(around_range.clone()); + let around_range = BufferOffset(around_range.start)..BufferOffset(around_range.end); + let buffer_range = excerpt.map_range_from_buffer(around_range); return Some(buffer_range.start.to_display_point(map)..buffer_range.end.to_display_point(map)); } @@ -1134,9 +1148,9 @@ fn argument( fn comma_delimited_range_at( buffer: &BufferSnapshot, - mut offset: usize, + mut offset: BufferOffset, include_comma: bool, - ) -> Option> { + ) -> Option> { // Seek to the first non-whitespace character offset += buffer .chars_at(offset) @@ -1151,7 +1165,7 @@ fn argument( } // If the cursor is outside the brackets, ignore them - if open.start == offset || close.end == offset { + if open.start == offset.0 || close.end == offset.0 { return false; } @@ -1167,7 +1181,7 @@ fn argument( let (open_bracket, close_bracket) = buffer.innermost_enclosing_bracket_ranges(offset..offset, Some(&bracket_filter))?; - let inner_bracket_range = open_bracket.end..close_bracket.start; + let inner_bracket_range = BufferOffset(open_bracket.end)..BufferOffset(close_bracket.start); let layer = buffer.syntax_layer_at(offset)?; let node = layer.node(); @@ -1186,7 +1200,7 @@ fn argument( parent_covers_bracket_range = covers_bracket_range; // Unable to find a child node with a parent that covers the bracket range, so no argument to select - cursor.goto_first_child_for_byte(offset)?; + cursor.goto_first_child_for_byte(offset.0)?; } let mut argument_node = cursor.node(); @@ -1256,7 +1270,7 @@ fn argument( } } - Some(start..end) + Some(BufferOffset(start)..BufferOffset(end)) } let result = comma_delimited_range_at(buffer, excerpt.map_offset_to_buffer(offset), around)?; @@ -1387,7 +1401,7 @@ fn is_possible_sentence_start(character: char) -> bool { const SENTENCE_END_PUNCTUATION: &[char] = &['.', '!', '?']; const SENTENCE_END_FILLERS: &[char] = &[')', ']', '"', '\'']; const SENTENCE_END_WHITESPACE: &[char] = &[' ', '\t', '\n']; -fn is_sentence_end(map: &DisplaySnapshot, offset: usize) -> bool { +fn is_sentence_end(map: &DisplaySnapshot, offset: MultiBufferOffset) -> bool { let mut next_chars = map.buffer_chars_at(offset).peekable(); if let Some((char, _)) = next_chars.next() { // We are at a double newline. This position is a sentence end. diff --git a/crates/vim/src/surrounds.rs b/crates/vim/src/surrounds.rs index 579ab7842096f1e5cb1bb4c70e2fd8f4256355d0..b3f9307aac3df18334cf24a619dc640ccb625e24 100644 --- a/crates/vim/src/surrounds.rs +++ b/crates/vim/src/surrounds.rs @@ -4,7 +4,7 @@ use crate::{ object::{Object, surrounding_markers}, state::Mode, }; -use editor::{Bias, movement}; +use editor::{Bias, MultiBufferOffset, movement}; use gpui::{Context, Window}; use language::BracketPair; @@ -175,7 +175,7 @@ impl Vim { while let Some((ch, offset)) = chars_and_offset.next() { if ch.to_string() == pair.start { let start = offset; - let mut end = start + 1; + let mut end = start + 1usize; if surround && let Some((next_ch, _)) = chars_and_offset.peek() && next_ch.eq(&' ') @@ -193,7 +193,7 @@ impl Vim { while let Some((ch, offset)) = reverse_chars_and_offsets.next() { if ch.to_string() == pair.end { let mut start = offset; - let end = start + 1; + let end = start + 1usize; if surround && let Some((next_ch, _)) = reverse_chars_and_offsets.peek() && next_ch.eq(&' ') @@ -282,7 +282,7 @@ impl Vim { // that the end replacement string does not exceed // this value. Helpful when dealing with newlines. let mut edit_len = 0; - let mut open_range_end = 0; + let mut open_range_end = MultiBufferOffset(0); let mut chars_and_offset = display_map .buffer_chars_at(range.start.to_offset(&display_map, Bias::Left)) .peekable(); @@ -291,7 +291,7 @@ impl Vim { if ch.to_string() == will_replace_pair.start { let mut open_str = pair.start.clone(); let start = offset; - open_range_end = start + 1; + open_range_end = start + 1usize; while let Some((next_ch, _)) = chars_and_offset.next() && next_ch == ' ' { @@ -322,7 +322,7 @@ impl Vim { if ch.to_string() == will_replace_pair.end { let mut close_str = String::new(); let mut start = offset; - let end = start + 1; + let end = start + 1usize; while let Some((next_ch, _)) = reverse_chars_and_offsets.next() && next_ch == ' ' && close_str.len() < edit_len - 1 diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index 3cd0646ff4fc0a6966f12db75b64999e3655ab98..5a98ec47b122e0d1ed7fd1edfc7c5e2265c40d90 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -7,7 +7,7 @@ use std::{sync::Arc, time::Duration}; use collections::HashMap; use command_palette::CommandPalette; use editor::{ - AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, + AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, MultiBufferOffset, actions::{DeleteLine, WrapSelectionsInTag}, code_context_menus::CodeContextMenu, display_map::DisplayRow, @@ -908,6 +908,9 @@ fn assert_pending_input(cx: &mut VimTestContext, expected: &str) { .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot())) .collect::>(), ranges + .iter() + .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end)) + .collect::>() ) }); } @@ -967,7 +970,7 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) { .iter() .map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot())) .collect::>(), - vec![0..1] + vec![MultiBufferOffset(0)..MultiBufferOffset(1)] ) }); cx.executor().advance_clock(Duration::from_millis(500)); diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 14ee4709a74ba68e92f07dd53182416ea93ed6d5..b633c9ef3c5aa13286277d602ec08efc3ab03373 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -21,8 +21,8 @@ mod visual; use collections::HashMap; use editor::{ - Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, SelectionEffects, - ToPoint, + Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, MultiBufferOffset, + SelectionEffects, ToPoint, actions::Paste, movement::{self, FindRange}, }; @@ -1388,7 +1388,7 @@ impl Vim { let newest_selection_empty = editor.update(cx, |editor, cx| { editor .selections - .newest::(&editor.display_snapshot(cx)) + .newest::(&editor.display_snapshot(cx)) .is_empty() }); let editor = editor.read(cx); @@ -1488,7 +1488,7 @@ impl Vim { let snapshot = &editor.snapshot(window, cx); let selection = editor .selections - .newest::(&snapshot.display_snapshot); + .newest::(&snapshot.display_snapshot); let snapshot = snapshot.buffer_snapshot(); let (range, kind) = diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index 0abba86e993a76b6c2a1c18f02d68d72d092e78c..3c6f237435e3924a907e059ed1a878641c287e7e 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use collections::HashMap; use editor::{ - Bias, DisplayPoint, Editor, SelectionEffects, + Bias, DisplayPoint, Editor, MultiBufferOffset, SelectionEffects, display_map::{DisplaySnapshot, ToDisplayPoint}, movement, }; @@ -778,7 +778,7 @@ impl Vim { { let range = row_range.start.to_offset(&display_map, Bias::Right) ..row_range.end.to_offset(&display_map, Bias::Right); - let text = text.repeat(range.len()); + let text = text.repeat(range.end - range.start); edits.push((range, text)); } } @@ -844,8 +844,8 @@ impl Vim { return; }; let vim_is_normal = self.mode == Mode::Normal; - let mut start_selection = 0usize; - let mut end_selection = 0usize; + let mut start_selection = MultiBufferOffset(0); + let mut end_selection = MultiBufferOffset(0); self.update_editor(cx, |_, editor, _| { editor.set_collapse_matches(false); @@ -868,7 +868,7 @@ impl Vim { self.update_editor(cx, |_, editor, cx| { let latest = editor .selections - .newest::(&editor.display_snapshot(cx)); + .newest::(&editor.display_snapshot(cx)); start_selection = latest.start; end_selection = latest.end; }); @@ -891,7 +891,7 @@ impl Vim { self.update_editor(cx, |_, editor, cx| { let latest = editor .selections - .newest::(&editor.display_snapshot(cx)); + .newest::(&editor.display_snapshot(cx)); if vim_is_normal { start_selection = latest.start; end_selection = latest.end; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 18f4a12b5d4abf8a11ae825bed2ad44feb1563ec..e90cf59f38e69be74e67969d230d5532b72dbba7 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2241,7 +2241,9 @@ mod tests { use super::*; use assets::Assets; use collections::HashSet; - use editor::{DisplayPoint, Editor, SelectionEffects, display_map::DisplayRow}; + use editor::{ + DisplayPoint, Editor, MultiBufferOffset, SelectionEffects, display_map::DisplayRow, + }; use gpui::{ Action, AnyWindowHandle, App, AssetSource, BorrowAppContext, SemanticVersion, TestAppContext, UpdateGlobal, VisualTestContext, WindowHandle, actions, @@ -3508,7 +3510,11 @@ mod tests { assert!(!editor.is_dirty(cx)); assert_eq!(editor.title(cx), "untitled"); assert!(Arc::ptr_eq( - &editor.buffer().read(cx).language_at(0, cx).unwrap(), + &editor + .buffer() + .read(cx) + .language_at(MultiBufferOffset(0), cx) + .unwrap(), &languages::PLAIN_TEXT )); editor.handle_input("hi", window, cx); @@ -3542,7 +3548,12 @@ mod tests { assert!(!editor.is_dirty(cx)); assert_eq!(editor.title(cx), "the-new-name.rs"); assert_eq!( - editor.buffer().read(cx).language_at(0, cx).unwrap().name(), + editor + .buffer() + .read(cx) + .language_at(MultiBufferOffset(0), cx) + .unwrap() + .name(), "Rust".into() ); }); @@ -3648,7 +3659,11 @@ mod tests { .update(cx, |_, window, cx| { editor.update(cx, |editor, cx| { assert!(Arc::ptr_eq( - &editor.buffer().read(cx).language_at(0, cx).unwrap(), + &editor + .buffer() + .read(cx) + .language_at(MultiBufferOffset(0), cx) + .unwrap(), &languages::PLAIN_TEXT )); editor.handle_input("hi", window, cx); @@ -3672,7 +3687,12 @@ mod tests { editor.update(cx, |editor, cx| { assert!(!editor.is_dirty(cx)); assert_eq!( - editor.buffer().read(cx).language_at(0, cx).unwrap().name(), + editor + .buffer() + .read(cx) + .language_at(MultiBufferOffset(0), cx) + .unwrap() + .name(), "Rust".into() ) });