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() ) });