diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 9e21248a1d..e04077d64e 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -35,7 +35,7 @@ use gpui::{ StatefulInteractiveElement, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext, }; -use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _}; +use language::{language_settings::SoftWrap, Buffer, BufferId, LanguageRegistry, ToOffset as _}; use project::Project; use search::{buffer_search::DivRegistrar, BufferSearchBar}; use semantic_index::{SemanticIndex, SemanticIndexStatus}; @@ -1414,7 +1414,7 @@ impl Conversation { ) -> Self { let markdown = language_registry.language_for_name("Markdown"); let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), ""); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), ""); buffer.set_language_registry(language_registry); cx.spawn(|buffer, mut cx| async move { let markdown = markdown.await?; @@ -1515,7 +1515,11 @@ impl Conversation { let mut message_anchors = Vec::new(); let mut next_message_id = MessageId(0); let buffer = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), saved_conversation.text); + let mut buffer = Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + saved_conversation.text, + ); for message in saved_conversation.messages { message_anchors.push(MessageAnchor { id: message.id, diff --git a/crates/assistant/src/codegen.rs b/crates/assistant/src/codegen.rs index 8af3010f40..d592ce88ae 100644 --- a/crates/assistant/src/codegen.rs +++ b/crates/assistant/src/codegen.rs @@ -365,7 +365,9 @@ mod tests { use futures::stream::{self}; use gpui::{Context, TestAppContext}; use indoc::indoc; - use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point}; + use language::{ + language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point, + }; use rand::prelude::*; use serde::Serialize; use settings::SettingsStore; @@ -394,8 +396,9 @@ mod tests { } } "}; - let buffer = - cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let range = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); @@ -460,8 +463,9 @@ mod tests { le } "}; - let buffer = - cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let position = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); @@ -525,8 +529,9 @@ mod tests { " \n", "}\n" // ); - let buffer = - cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let position = buffer.read_with(cx, |buffer, cx| { let snapshot = buffer.snapshot(cx); diff --git a/crates/assistant/src/prompts.rs b/crates/assistant/src/prompts.rs index c9614a4851..9042fda5be 100644 --- a/crates/assistant/src/prompts.rs +++ b/crates/assistant/src/prompts.rs @@ -178,7 +178,9 @@ pub(crate) mod tests { use gpui::{AppContext, Context}; use indoc::indoc; - use language::{language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, Point}; + use language::{ + language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point, + }; use settings::SettingsStore; pub(crate) fn rust_lang() -> Language { @@ -253,8 +255,9 @@ pub(crate) mod tests { } } "}; - let buffer = - cx.new_model(|cx| Buffer::new(0, 0, text).with_language(Arc::new(rust_lang()), cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(1).unwrap(), text).with_language(Arc::new(rust_lang()), cx) + }); let snapshot = buffer.read(cx).snapshot(); assert_eq!( diff --git a/crates/channel/src/channel_buffer.rs b/crates/channel/src/channel_buffer.rs index e87954edf6..dc63c55d15 100644 --- a/crates/channel/src/channel_buffer.rs +++ b/crates/channel/src/channel_buffer.rs @@ -9,6 +9,7 @@ use rpc::{ TypedEnvelope, }; use std::{sync::Arc, time::Duration}; +use text::BufferId; use util::ResultExt; pub const ACKNOWLEDGE_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(250); @@ -53,7 +54,7 @@ impl ChannelBuffer { channel_id: channel.id, }) .await?; - + let buffer_id = BufferId::new(response.buffer_id)?; let base_text = response.base_text; let operations = response .operations @@ -63,12 +64,7 @@ impl ChannelBuffer { let buffer = cx.new_model(|cx| { let capability = channel_store.read(cx).channel_capability(channel.id); - language::Buffer::remote( - response.buffer_id, - response.replica_id as u16, - capability, - base_text, - ) + language::Buffer::remote(buffer_id, response.replica_id as u16, capability, base_text) })?; buffer.update(&mut cx, |buffer, cx| buffer.apply_ops(operations, cx))??; @@ -107,7 +103,7 @@ impl ChannelBuffer { } } - pub fn remote_id(&self, cx: &AppContext) -> u64 { + pub fn remote_id(&self, cx: &AppContext) -> BufferId { self.buffer.read(cx).remote_id() } @@ -210,7 +206,7 @@ impl ChannelBuffer { pub fn acknowledge_buffer_version(&mut self, cx: &mut ModelContext<'_, ChannelBuffer>) { let buffer = self.buffer.read(cx); let version = buffer.version(); - let buffer_id = buffer.remote_id(); + let buffer_id = buffer.remote_id().into(); let client = self.client.clone(); let epoch = self.epoch(); diff --git a/crates/collab/src/db/queries/buffers.rs b/crates/collab/src/db/queries/buffers.rs index 59b8f8d01f..b42e26e465 100644 --- a/crates/collab/src/db/queries/buffers.rs +++ b/crates/collab/src/db/queries/buffers.rs @@ -693,7 +693,7 @@ impl Database { return Ok(()); } - let mut text_buffer = text::Buffer::new(0, 0, base_text); + let mut text_buffer = text::Buffer::new(0, text::BufferId::new(1).unwrap(), base_text); text_buffer .apply_ops(operations.into_iter().filter_map(operation_from_wire)) .unwrap(); diff --git a/crates/collab/src/db/tests/buffer_tests.rs b/crates/collab/src/db/tests/buffer_tests.rs index 2eb8e15301..0d7438b517 100644 --- a/crates/collab/src/db/tests/buffer_tests.rs +++ b/crates/collab/src/db/tests/buffer_tests.rs @@ -67,7 +67,7 @@ async fn test_channel_buffers(db: &Arc) { .await .unwrap(); - let mut buffer_a = Buffer::new(0, 0, "".to_string()); + let mut buffer_a = Buffer::new(0, text::BufferId::new(0).unwrap(), "".to_string()); let mut operations = Vec::new(); operations.push(buffer_a.edit([(0..0, "hello world")])); operations.push(buffer_a.edit([(5..5, ", cruel")])); @@ -90,7 +90,11 @@ async fn test_channel_buffers(db: &Arc) { .await .unwrap(); - let mut buffer_b = Buffer::new(0, 0, buffer_response_b.base_text); + let mut buffer_b = Buffer::new( + 0, + text::BufferId::new(0).unwrap(), + buffer_response_b.base_text, + ); buffer_b .apply_ops(buffer_response_b.operations.into_iter().map(|operation| { let operation = proto::deserialize_operation(operation).unwrap(); @@ -223,7 +227,11 @@ async fn test_channel_buffers_last_operations(db: &Database) { .unwrap(), ); - text_buffers.push(Buffer::new(0, 0, "".to_string())); + text_buffers.push(Buffer::new( + 0, + text::BufferId::new(1).unwrap(), + "".to_string(), + )); } let operations = db @@ -270,7 +278,7 @@ async fn test_channel_buffers_last_operations(db: &Database) { db.join_channel_buffer(buffers[1].channel_id, user_id, connection_id) .await .unwrap(); - text_buffers[1] = Buffer::new(1, 0, "def".to_string()); + text_buffers[1] = Buffer::new(1, text::BufferId::new(1).unwrap(), "def".to_string()); update_buffer( buffers[1].channel_id, user_id, diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index 6345bdb794..6b2816b6f3 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -1023,12 +1023,15 @@ async fn get_copilot_lsp(http: Arc) -> anyhow::Result { mod tests { use super::*; use gpui::TestAppContext; + use language::BufferId; #[gpui::test(iterations = 10)] async fn test_buffer_management(cx: &mut TestAppContext) { let (copilot, mut lsp) = Copilot::fake(cx); - let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Hello")); + let buffer_1 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "Hello") + }); let buffer_1_uri: lsp::Url = format!("buffer://{}", buffer_1.entity_id().as_u64()) .parse() .unwrap(); @@ -1046,7 +1049,13 @@ mod tests { } ); - let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "Goodbye")); + let buffer_2 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "Goodbye", + ) + }); let buffer_2_uri: lsp::Url = format!("buffer://{}", buffer_2.entity_id().as_u64()) .parse() .unwrap(); @@ -1235,7 +1244,7 @@ mod tests { fn buffer_reloaded( &self, - _: u64, + _: BufferId, _: &clock::Global, _: language::RopeFingerprint, _: language::LineEnding, diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 7f29a7d04f..6d81f3e37c 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1007,6 +1007,7 @@ pub mod tests { use settings::SettingsStore; use smol::stream::StreamExt; use std::{env, sync::Arc}; + use text::BufferId; use theme::{LoadThemes, SyntaxTheme}; use util::test::{marked_text_ranges, sample_text}; use Bias::*; @@ -1467,7 +1468,8 @@ pub mod tests { cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap()))); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) }); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); @@ -1553,7 +1555,8 @@ pub mod tests { cx.update(|cx| init_test(cx, |_| {})); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) }); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); @@ -1620,7 +1623,8 @@ pub mod tests { let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) }); cx.condition(&buffer, |buf, _| !buf.is_parsing()).await; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a572729e76..bc7061524e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -109,7 +109,7 @@ use std::{ }; pub use sum_tree::Bias; use sum_tree::TreeMap; -use text::{OffsetUtf16, Rope}; +use text::{BufferId, OffsetUtf16, Rope}; use theme::{ observe_buffer_font_size_adjustment, ActiveTheme, PlayerColor, StatusColors, SyntaxTheme, ThemeColors, ThemeSettings, @@ -1289,19 +1289,37 @@ impl InlayHintRefreshReason { impl Editor { pub fn single_line(cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new())); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + String::new(), + ) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::SingleLine, buffer, None, cx) } pub fn multi_line(cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new())); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + String::new(), + ) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::Full, buffer, None, cx) } pub fn auto_height(max_lines: usize, cx: &mut ViewContext) -> Self { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), String::new())); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + String::new(), + ) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); Self::new(EditorMode::AutoHeight { max_lines }, buffer, None, cx) } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 8c31a2bcdf..a039d27098 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -39,7 +39,8 @@ fn test_edit_events(cx: &mut TestAppContext) { init_test(cx, |_| {}); let buffer = cx.new_model(|cx| { - let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "123456"); + let mut buffer = + language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456"); buffer.set_group_interval(Duration::from_secs(1)); buffer }); @@ -154,7 +155,9 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) { init_test(cx, |_| {}); let mut now = Instant::now(); - let buffer = cx.new_model(|cx| language::Buffer::new(0, cx.entity_id().as_u64(), "123456")); + let buffer = cx.new_model(|cx| { + language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "123456") + }); let group_interval = buffer.update(cx, |buffer, _| buffer.transaction_group_interval()); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let editor = cx.add_window(|cx| build_editor(buffer.clone(), cx)); @@ -225,7 +228,8 @@ fn test_ime_composition(cx: &mut TestAppContext) { init_test(cx, |_| {}); let buffer = cx.new_model(|cx| { - let mut buffer = language::Buffer::new(0, cx.entity_id().as_u64(), "abcde"); + let mut buffer = + language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcde"); // Ensure automatic grouping doesn't occur. buffer.set_group_interval(Duration::ZERO); buffer @@ -629,7 +633,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) { // Ensure we don't panic when navigation data contains invalid anchors *and* points. let mut invalid_anchor = editor.scroll_manager.anchor().anchor; - invalid_anchor.text_anchor.buffer_id = Some(999); + invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok(); let invalid_point = Point::new(9999, 0); editor.navigate( Box::new(NavigationData { @@ -2342,11 +2346,20 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) { )); let toml_buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n").with_language(toml_language, cx) + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "a = 1\nb = 2\n", + ) + .with_language(toml_language, cx) }); let rust_buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), "const c: usize = 3;\n") - .with_language(rust_language, cx) + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "const c: usize = 3;\n", + ) + .with_language(rust_language, cx) }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -3984,8 +3997,10 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx - .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); @@ -4149,8 +4164,10 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) { let text = "fn a() {}"; - let buffer = cx - .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); editor @@ -4713,8 +4730,10 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx - .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); view.condition::(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) @@ -4862,8 +4881,10 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) { "# .unindent(); - let buffer = cx - .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (editor, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); editor @@ -6095,7 +6116,13 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) { fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(3, 4, 'a'), + ) + }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); multibuffer.push_excerpts( @@ -6179,7 +6206,13 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { primary: None, } }); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), initial_text)); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + initial_text, + ) + }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); multibuffer.push_excerpts(buffer, excerpt_ranges, cx); @@ -6237,7 +6270,13 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) { fn test_refresh_selections(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(3, 4, 'a'), + ) + }); let mut excerpt1_id = None; let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -6322,7 +6361,13 @@ fn test_refresh_selections(cx: &mut TestAppContext) { fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) { init_test(cx, |_| {}); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(3, 4, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(3, 4, 'a'), + ) + }); let mut excerpt1_id = None; let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); @@ -6417,8 +6462,10 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) { "{{} }\n", // ); - let buffer = cx - .new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) + }); let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx)); let (view, cx) = cx.add_window_view(|cx| build_editor(buffer, cx)); view.condition::(cx, |view, cx| !view.buffer.read(cx).is_parsing(cx)) @@ -7498,8 +7545,20 @@ async fn test_copilot_multibuffer(executor: BackgroundExecutor, cx: &mut gpui::T let (copilot, copilot_lsp) = Copilot::fake(cx); _ = cx.update(|cx| cx.set_global(copilot)); - let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a = 1\nb = 2\n")); - let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "c = 3\nd = 4\n")); + let buffer_1 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "a = 1\nb = 2\n", + ) + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "c = 3\nd = 4\n", + ) + }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, ReadWrite); multibuffer.push_excerpts( diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index c4c18aefbf..71e039a843 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -28,7 +28,7 @@ use collections::{hash_map, HashMap, HashSet}; use language::language_settings::InlayHintSettings; use smol::lock::Semaphore; use sum_tree::Bias; -use text::{ToOffset, ToPoint}; +use text::{BufferId, ToOffset, ToPoint}; use util::post_inc; pub struct InlayHintCache { @@ -50,7 +50,7 @@ struct TasksForRanges { struct CachedExcerptHints { version: usize, buffer_version: Global, - buffer_id: u64, + buffer_id: BufferId, ordered_hints: Vec, hints_by_id: HashMap, } @@ -93,7 +93,7 @@ struct ExcerptHintsUpdate { #[derive(Debug, Clone, Copy)] struct ExcerptQuery { - buffer_id: u64, + buffer_id: BufferId, excerpt_id: ExcerptId, cache_version: usize, invalidate: InvalidationStrategy, @@ -553,7 +553,7 @@ impl InlayHintCache { /// Queries a certain hint from the cache for extra data via the LSP resolve request. pub(super) fn spawn_hint_resolve( &self, - buffer_id: u64, + buffer_id: BufferId, excerpt_id: ExcerptId, id: InlayId, cx: &mut ViewContext<'_, Editor>, diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 11ea07ff5c..f26c4361b7 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -30,7 +30,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use text::Selection; +use text::{BufferId, Selection}; use theme::Theme; use ui::{h_flex, prelude::*, Label}; use util::{paths::PathExt, paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt}; @@ -73,12 +73,14 @@ impl FollowableItem for Editor { .iter() .map(|excerpt| excerpt.buffer_id) .collect::>(); - let buffers = project.update(cx, |project, cx| { - buffer_ids - .iter() - .map(|id| project.open_buffer_by_id(*id, cx)) - .collect::>() - }); + let buffers = project + .update(cx, |project, cx| { + buffer_ids + .iter() + .map(|id| BufferId::new(*id).map(|id| project.open_buffer_by_id(id, cx))) + .collect::>>() + }) + .ok()?; let pane = pane.downgrade(); Some(cx.spawn(|mut cx| async move { @@ -109,10 +111,12 @@ impl FollowableItem for Editor { MultiBuffer::new(replica_id, project.read(cx).capability()); let mut excerpts = state.excerpts.into_iter().peekable(); while let Some(excerpt) = excerpts.peek() { - let buffer_id = excerpt.buffer_id; + let Ok(buffer_id) = BufferId::new(excerpt.buffer_id) else { + continue; + }; let buffer_excerpts = iter::from_fn(|| { let excerpt = excerpts.peek()?; - (excerpt.buffer_id == buffer_id) + (excerpt.buffer_id == u64::from(buffer_id)) .then(|| excerpts.next().unwrap()) }); let buffer = @@ -189,7 +193,7 @@ impl FollowableItem for Editor { .excerpts() .map(|(id, buffer, range)| proto::Excerpt { id: id.to_proto(), - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), context_start: Some(serialize_text_anchor(&range.context.start)), context_end: Some(serialize_text_anchor(&range.context.end)), primary_start: range @@ -336,9 +340,9 @@ async fn update_editor_from_message( let inserted_excerpt_buffers = project.update(cx, |project, cx| { inserted_excerpt_buffer_ids .into_iter() - .map(|id| project.open_buffer_by_id(id, cx)) - .collect::>() - })?; + .map(|id| BufferId::new(id).map(|id| project.open_buffer_by_id(id, cx))) + .collect::>>() + })??; let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?; // Update the editor's excerpts. @@ -362,7 +366,7 @@ async fn update_editor_from_message( let Some(previous_excerpt_id) = insertion.previous_excerpt_id else { continue; }; - let buffer_id = excerpt.buffer_id; + let buffer_id = BufferId::new(excerpt.buffer_id)?; let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else { continue; }; @@ -370,7 +374,7 @@ async fn update_editor_from_message( let adjacent_excerpts = iter::from_fn(|| { let insertion = insertions.peek()?; if insertion.previous_excerpt_id.is_none() - && insertion.excerpt.as_ref()?.buffer_id == buffer_id + && insertion.excerpt.as_ref()?.buffer_id == u64::from(buffer_id) { insertions.next()?.excerpt } else { @@ -395,8 +399,9 @@ async fn update_editor_from_message( } multibuffer.remove_excerpts(removed_excerpt_ids, cx); - }); - })?; + Result::<(), anyhow::Error>::Ok(()) + }) + })??; // Deserialize the editor state. let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| { @@ -450,13 +455,13 @@ async fn update_editor_from_message( } fn serialize_excerpt( - buffer_id: u64, + buffer_id: BufferId, id: &ExcerptId, range: &ExcerptRange, ) -> Option { Some(proto::Excerpt { id: id.to_proto(), - buffer_id, + buffer_id: buffer_id.into(), context_start: Some(serialize_text_anchor(&range.context.start)), context_end: Some(serialize_text_anchor(&range.context.end)), primary_start: range diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 0cf8ac7440..71c2cceac1 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -522,6 +522,7 @@ mod tests { use language::Capability; use project::Project; use settings::SettingsStore; + use text::BufferId; use util::post_inc; #[gpui::test] @@ -822,8 +823,13 @@ mod tests { let font = font("Helvetica"); - let buffer = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abc\ndefg\nhijkl\nmn")); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "abc\ndefg\nhijkl\nmn", + ) + }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); multibuffer.push_excerpts( diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 6e83bab220..07b0240f60 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -314,7 +314,7 @@ mod tests { use std::assert_eq; use super::*; - use text::Buffer; + use text::{Buffer, BufferId}; use unindent::Unindent as _; #[test] @@ -333,7 +333,7 @@ mod tests { " .unindent(); - let mut buffer = Buffer::new(0, 0, buffer_text); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text); let mut diff = BufferDiff::new(); smol::block_on(diff.update(&diff_base, &buffer)); assert_hunks( @@ -393,7 +393,7 @@ mod tests { " .unindent(); - let buffer = Buffer::new(0, 0, buffer_text); + let buffer = Buffer::new(0, BufferId::new(1).unwrap(), buffer_text); let mut diff = BufferDiff::new(); smol::block_on(diff.update(&diff_base, &buffer)); assert_eq!(diff.hunks(&buffer).count(), 8); diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 64d15e6856..7a658d1b03 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -15,7 +15,7 @@ use crate::{ }, CodeLabel, LanguageScope, Outline, }; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Context, Result}; pub use clock::ReplicaId; use futures::channel::oneshot; use gpui::{AppContext, EventEmitter, HighlightStyle, ModelContext, Task, TaskLabel}; @@ -44,10 +44,10 @@ use sum_tree::TreeMap; use text::operation_queue::OperationQueue; use text::*; pub use text::{ - Anchor, Bias, Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Edit, OffsetRangeExt, - OffsetUtf16, Patch, Point, PointUtf16, Rope, RopeFingerprint, Selection, SelectionGoal, - Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, ToPointUtf16, - Transaction, TransactionId, Unclipped, + Anchor, Bias, Buffer as TextBuffer, BufferId, BufferSnapshot as TextBufferSnapshot, Edit, + OffsetRangeExt, OffsetUtf16, Patch, Point, PointUtf16, Rope, RopeFingerprint, Selection, + SelectionGoal, Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, + ToPointUtf16, Transaction, TransactionId, Unclipped, }; use theme::SyntaxTheme; #[cfg(any(test, feature = "test-support"))] @@ -396,7 +396,7 @@ pub trait LocalFile: File { /// Called when the buffer is reloaded from disk. fn buffer_reloaded( &self, - buffer_id: u64, + buffer_id: BufferId, version: &clock::Global, fingerprint: RopeFingerprint, line_ending: LineEnding, @@ -517,7 +517,7 @@ pub enum CharKind { impl Buffer { /// Create a new buffer with the given base text. - pub fn new>(replica_id: ReplicaId, id: u64, base_text: T) -> Self { + pub fn new>(replica_id: ReplicaId, id: BufferId, base_text: T) -> Self { Self::build( TextBuffer::new(replica_id, id, base_text.into()), None, @@ -528,7 +528,7 @@ impl Buffer { /// Create a new buffer that is a replica of a remote buffer. pub fn remote( - remote_id: u64, + remote_id: BufferId, replica_id: ReplicaId, capability: Capability, base_text: String, @@ -549,7 +549,9 @@ impl Buffer { message: proto::BufferState, file: Option>, ) -> Result { - let buffer = TextBuffer::new(replica_id, message.id, message.base_text); + let buffer_id = BufferId::new(message.id) + .with_context(|| anyhow!("Could not deserialize buffer_id"))?; + let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text); let mut this = Self::build( buffer, message.diff_base.map(|text| text.into_boxed_str().into()), @@ -572,7 +574,7 @@ impl Buffer { /// Serialize the buffer's state to a protobuf message. pub fn to_proto(&self) -> proto::BufferState { proto::BufferState { - id: self.remote_id(), + id: self.remote_id().into(), file: self.file.as_ref().map(|f| f.to_proto()), base_text: self.base_text().to_string(), diff_base: self.diff_base.as_ref().map(|h| h.to_string()), diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 6079f1de93..05cec88126 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -18,7 +18,7 @@ use std::{ time::{Duration, Instant}, }; use text::network::Network; -use text::LineEnding; +use text::{BufferId, LineEnding}; use text::{Point, ToPoint}; use unindent::Unindent as _; use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter}; @@ -43,8 +43,12 @@ fn test_line_endings(cx: &mut gpui::AppContext) { init_settings(cx, |_| {}); cx.new_model(|cx| { - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "one\r\ntwo\rthree") - .with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "one\r\ntwo\rthree", + ) + .with_language(Arc::new(rust_lang()), cx); assert_eq!(buffer.text(), "one\ntwo\nthree"); assert_eq!(buffer.line_ending(), LineEnding::Windows); @@ -138,8 +142,10 @@ fn test_edit_events(cx: &mut gpui::AppContext) { let buffer_1_events = Arc::new(Mutex::new(Vec::new())); let buffer_2_events = Arc::new(Mutex::new(Vec::new())); - let buffer1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcdef")); - let buffer2 = cx.new_model(|cx| Buffer::new(1, cx.entity_id().as_u64(), "abcdef")); + let buffer1 = cx + .new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); + let buffer2 = cx + .new_model(|cx| Buffer::new(1, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef")); let buffer1_ops = Arc::new(Mutex::new(Vec::new())); buffer1.update(cx, { let buffer1_ops = buffer1_ops.clone(); @@ -218,7 +224,8 @@ fn test_edit_events(cx: &mut gpui::AppContext) { #[gpui::test] async fn test_apply_diff(cx: &mut TestAppContext) { let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n"; - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = + cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3))); let text = "a\nccc\ndddd\nffffff\n"; @@ -250,7 +257,8 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) { ] .join("\n"); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = + cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); // Spawn a task to format the buffer's whitespace. // Pause so that the foratting task starts running. @@ -315,7 +323,8 @@ async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) { async fn test_reparse(cx: &mut gpui::TestAppContext) { let text = "fn a() {}"; let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx) }); // Wait for the initial text to parse @@ -443,8 +452,8 @@ async fn test_reparse(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_resetting_language(cx: &mut gpui::TestAppContext) { let buffer = cx.new_model(|cx| { - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), "{}").with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "{}") + .with_language(Arc::new(rust_lang()), cx); buffer.set_sync_parse_timeout(Duration::ZERO); buffer }); @@ -493,7 +502,8 @@ async fn test_outline(cx: &mut gpui::TestAppContext) { .unindent(); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx) }); let outline = buffer .update(cx, |buffer, _| buffer.snapshot().outline(None)) @@ -579,7 +589,8 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) { .unindent(); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx) }); let outline = buffer .update(cx, |buffer, _| buffer.snapshot().outline(None)) @@ -617,7 +628,8 @@ async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) { .unindent(); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(language), cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(language), cx) }); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); @@ -661,7 +673,8 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) { .unindent(); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx) + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx) }); let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot()); @@ -883,8 +896,8 @@ fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: & fn test_range_for_syntax_ancestor(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() { b(|c| {}) }"; - let buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); let snapshot = buffer.snapshot(); assert_eq!( @@ -924,8 +937,8 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() {}"; - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); assert_eq!(buffer.text(), "fn a() {\n \n}"); @@ -967,8 +980,8 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) { cx.new_model(|cx| { let text = "fn a() {}"; - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx); assert_eq!(buffer.text(), "fn a() {\n\t\n}"); @@ -1007,10 +1020,9 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC init_settings(cx, |_| {}); cx.new_model(|cx| { - let entity_id = cx.entity_id(); let mut buffer = Buffer::new( 0, - entity_id.as_u64(), + BufferId::new(cx.entity_id().as_u64()).unwrap(), " fn a() { c; @@ -1085,7 +1097,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC let mut buffer = Buffer::new( 0, - cx.entity_id().as_u64(), + BufferId::new(cx.entity_id().as_u64()).unwrap(), " fn a() { b(); @@ -1150,7 +1162,7 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap cx.new_model(|cx| { let mut buffer = Buffer::new( 0, - cx.entity_id().as_u64(), + BufferId::new(cx.entity_id().as_u64()).unwrap(), " fn a() { i @@ -1212,7 +1224,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) { cx.new_model(|cx| { let mut buffer = Buffer::new( 0, - cx.entity_id().as_u64(), + BufferId::new(cx.entity_id().as_u64()).unwrap(), " fn a() {} " @@ -1268,8 +1280,8 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) { cx.new_model(|cx| { let text = "a\nb"; - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); buffer.edit( [(0..1, "\n"), (2..3, "\n")], Some(AutoindentMode::EachLine), @@ -1295,8 +1307,8 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) { " .unindent(); - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); buffer.edit( [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")], Some(AutoindentMode::EachLine), @@ -1333,8 +1345,8 @@ fn test_autoindent_block_mode(cx: &mut AppContext) { } "# .unindent(); - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); // When this text was copied, both of the quotation marks were at the same // indent level, but the indentation of the first line was not included in @@ -1419,8 +1431,8 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex } "# .unindent(); - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), text).with_language(Arc::new(rust_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(Arc::new(rust_lang()), cx); // The original indent columns are not known, so this text is // auto-indented in a block as if the first line was copied in @@ -1499,17 +1511,18 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) { " .unindent(); - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text).with_language( - Arc::new(Language::new( - LanguageConfig { - name: "Markdown".into(), - auto_indent_using_last_non_empty_line: false, - ..Default::default() - }, - Some(tree_sitter_json::language()), - )), - cx, - ); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language( + Arc::new(Language::new( + LanguageConfig { + name: "Markdown".into(), + auto_indent_using_last_non_empty_line: false, + ..Default::default() + }, + Some(tree_sitter_json::language()), + )), + cx, + ); buffer.edit( [(Point::new(3, 0)..Point::new(3, 0), "\n")], Some(AutoindentMode::EachLine), @@ -1575,7 +1588,7 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) { false, ); - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text); buffer.set_language_registry(language_registry); buffer.set_language(Some(html_language), cx); buffer.edit( @@ -1611,8 +1624,8 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) { }); cx.new_model(|cx| { - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), "").with_language(Arc::new(ruby_lang()), cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") + .with_language(Arc::new(ruby_lang()), cx); let text = r#" class C @@ -1713,8 +1726,8 @@ fn test_language_scope_at_with_javascript(cx: &mut AppContext) { "# .unindent(); - let buffer = - Buffer::new(0, cx.entity_id().as_u64(), &text).with_language(Arc::new(language), cx); + let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), &text) + .with_language(Arc::new(language), cx); let snapshot = buffer.snapshot(); let config = snapshot.language_scope_at(0).unwrap(); @@ -1831,8 +1844,12 @@ fn test_language_scope_at_with_rust(cx: &mut AppContext) { "# .unindent(); - let buffer = Buffer::new(0, cx.entity_id().as_u64(), text.clone()) - .with_language(Arc::new(language), cx); + let buffer = Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + text.clone(), + ) + .with_language(Arc::new(language), cx); let snapshot = buffer.snapshot(); // By default, all brackets are enabled @@ -1876,7 +1893,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) { language_registry.add(Arc::new(html_lang())); language_registry.add(Arc::new(erb_lang())); - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), text); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text); buffer.set_language_registry(language_registry.clone()); buffer.set_language( language_registry @@ -1911,7 +1928,7 @@ fn test_serialization(cx: &mut gpui::AppContext) { let mut now = Instant::now(); let buffer1 = cx.new_model(|cx| { - let mut buffer = Buffer::new(0, cx.entity_id().as_u64(), "abc"); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abc"); buffer.edit([(3..3, "D")], None, cx); now += Duration::from_secs(1); @@ -1966,8 +1983,13 @@ fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) { let mut replica_ids = Vec::new(); let mut buffers = Vec::new(); let network = Arc::new(Mutex::new(Network::new(rng.clone()))); - let base_buffer = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text.as_str())); + let base_buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + base_text.as_str(), + ) + }); for i in 0..rng.gen_range(min_peers..=max_peers) { let buffer = cx.new_model(|cx| { @@ -2475,8 +2497,12 @@ fn assert_bracket_pairs( ) { let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false); let buffer = cx.new_model(|cx| { - Buffer::new(0, cx.entity_id().as_u64(), expected_text.clone()) - .with_language(Arc::new(language), cx) + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + expected_text.clone(), + ) + .with_language(Arc::new(language), cx) }); let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot()); diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 45136ba1d7..eec2dcbb29 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -241,7 +241,7 @@ pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor { Bias::Left => proto::Bias::Left as i32, Bias::Right => proto::Bias::Right as i32, }, - buffer_id: anchor.buffer_id, + buffer_id: anchor.buffer_id.map(Into::into), } } @@ -420,6 +420,11 @@ pub fn deserialize_diagnostics( /// Deserializes an [`Anchor`] from the RPC representation. pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { + let buffer_id = if let Some(id) = anchor.buffer_id { + Some(BufferId::new(id).ok()?) + } else { + None + }; Some(Anchor { timestamp: clock::Lamport { replica_id: anchor.replica_id as ReplicaId, @@ -430,7 +435,7 @@ pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { proto::Bias::Left => Bias::Left, proto::Bias::Right => Bias::Right, }, - buffer_id: anchor.buffer_id, + buffer_id, }) } diff --git a/crates/language/src/syntax_map/syntax_map_tests.rs b/crates/language/src/syntax_map/syntax_map_tests.rs index 8b9169d1cc..abf52c64e5 100644 --- a/crates/language/src/syntax_map/syntax_map_tests.rs +++ b/crates/language/src/syntax_map/syntax_map_tests.rs @@ -2,7 +2,7 @@ use super::*; use crate::LanguageConfig; use rand::rngs::StdRng; use std::{env, ops::Range, sync::Arc}; -use text::Buffer; +use text::{Buffer, BufferId}; use tree_sitter::Node; use unindent::Unindent as _; use util::test::marked_text_ranges; @@ -86,7 +86,7 @@ fn test_syntax_map_layers_for_range() { let mut buffer = Buffer::new( 0, - 0, + BufferId::new(1).unwrap(), r#" fn a() { assert_eq!( @@ -185,7 +185,7 @@ fn test_dynamic_language_injection() { let mut buffer = Buffer::new( 0, - 0, + BufferId::new(1).unwrap(), r#" This is a code block: @@ -860,7 +860,7 @@ fn test_random_edits( .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); - let mut buffer = Buffer::new(0, 0, text); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), text); let mut syntax_map = SyntaxMap::new(); syntax_map.set_language_registry(registry.clone()); @@ -1040,7 +1040,7 @@ fn test_edit_sequence(language_name: &str, steps: &[&str]) -> (Buffer, SyntaxMap .now_or_never() .unwrap() .unwrap(); - let mut buffer = Buffer::new(0, 0, Default::default()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), Default::default()); let mut mutated_syntax_map = SyntaxMap::new(); mutated_syntax_map.set_language_registry(registry.clone()); diff --git a/crates/multi_buffer/src/anchor.rs b/crates/multi_buffer/src/anchor.rs index 39a8182da1..877f74c21b 100644 --- a/crates/multi_buffer/src/anchor.rs +++ b/crates/multi_buffer/src/anchor.rs @@ -5,10 +5,11 @@ use std::{ ops::{Range, Sub}, }; use sum_tree::Bias; +use text::BufferId; #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] pub struct Anchor { - pub buffer_id: Option, + pub buffer_id: Option, pub excerpt_id: ExcerptId, pub text_anchor: text::Anchor, } diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index d83a547dcd..0f90612358 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -33,7 +33,7 @@ use sum_tree::{Bias, Cursor, SumTree}; use text::{ locator::Locator, subscription::{Subscription, Topic}, - Edit, TextSummary, + BufferId, Edit, TextSummary, }; use theme::SyntaxTheme; use util::post_inc; @@ -48,7 +48,7 @@ pub struct ExcerptId(usize); pub struct MultiBuffer { snapshot: RefCell, - buffers: RefCell>, + buffers: RefCell>, next_excerpt_id: usize, subscriptions: Topic, singleton: bool, @@ -101,7 +101,7 @@ struct History { #[derive(Clone)] struct Transaction { id: TransactionId, - buffer_transactions: HashMap, + buffer_transactions: HashMap, first_edit_at: Instant, last_edit_at: Instant, suppress_grouping: bool, @@ -161,7 +161,7 @@ pub struct ExcerptBoundary { struct Excerpt { id: ExcerptId, locator: Locator, - buffer_id: u64, + buffer_id: BufferId, buffer: BufferSnapshot, range: ExcerptRange, max_buffer_row: u32, @@ -366,7 +366,7 @@ impl MultiBuffer { offset: T, theme: Option<&SyntaxTheme>, cx: &AppContext, - ) -> Option<(u64, Vec>)> { + ) -> Option<(BufferId, Vec>)> { self.read(cx).symbols_containing(offset, theme) } @@ -412,7 +412,7 @@ impl MultiBuffer { is_insertion: bool, original_indent_column: u32, } - let mut buffer_edits: HashMap> = Default::default(); + let mut buffer_edits: HashMap> = Default::default(); let mut edited_excerpt_ids = Vec::new(); let mut cursor = snapshot.excerpts.cursor::(); for (ix, (range, new_text)) in edits.enumerate() { @@ -514,7 +514,7 @@ impl MultiBuffer { // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. fn tail( this: &mut MultiBuffer, - buffer_edits: HashMap>, + buffer_edits: HashMap>, autoindent_mode: Option, edited_excerpt_ids: Vec, cx: &mut ModelContext, @@ -720,7 +720,7 @@ impl MultiBuffer { cursor_shape: CursorShape, cx: &mut ModelContext, ) { - let mut selections_by_buffer: HashMap>> = + let mut selections_by_buffer: HashMap>> = Default::default(); let snapshot = self.read(cx); let mut cursor = snapshot.excerpts.cursor::>(); @@ -1440,7 +1440,7 @@ impl MultiBuffer { .collect() } - pub fn buffer(&self, buffer_id: u64) -> Option> { + pub fn buffer(&self, buffer_id: BufferId) -> Option> { self.buffers .borrow() .get(&buffer_id) @@ -1661,7 +1661,8 @@ impl MultiBuffer { #[cfg(any(test, feature = "test-support"))] impl MultiBuffer { pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> Model { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = cx + .new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)); cx.new_model(|cx| Self::singleton(buffer, cx)) } @@ -1671,7 +1672,9 @@ impl MultiBuffer { ) -> Model { let multi = cx.new_model(|_| Self::new(0, Capability::ReadWrite)); for (text, ranges) in excerpts { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text)); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + }); let excerpt_ranges = ranges.into_iter().map(|range| ExcerptRange { context: range, primary: None, @@ -1760,7 +1763,9 @@ impl MultiBuffer { if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) { let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() { let text = RandomCharIter::new(&mut *rng).take(10).collect::(); - buffers.push(cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), text))); + buffers.push(cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + })); let buffer = buffers.last().unwrap().read(cx); log::info!( "Creating new buffer {} with text: {:?}", @@ -1987,7 +1992,7 @@ impl MultiBufferSnapshot { (start..end, word_kind) } - pub fn as_singleton(&self) -> Option<(&ExcerptId, u64, &BufferSnapshot)> { + pub fn as_singleton(&self) -> Option<(&ExcerptId, BufferId, &BufferSnapshot)> { if self.singleton { self.excerpts .iter() @@ -3209,7 +3214,7 @@ impl MultiBufferSnapshot { &self, offset: T, theme: Option<&SyntaxTheme>, - ) -> Option<(u64, Vec>)> { + ) -> Option<(BufferId, Vec>)> { let anchor = self.anchor_before(offset); let excerpt_id = anchor.excerpt_id; let excerpt = self.excerpt(excerpt_id)?; @@ -3249,7 +3254,7 @@ impl MultiBufferSnapshot { } } - pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option { + pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option { Some(self.excerpt(excerpt_id)?.buffer_id) } @@ -3387,7 +3392,7 @@ impl History { fn end_transaction( &mut self, now: Instant, - buffer_transactions: HashMap, + buffer_transactions: HashMap, ) -> bool { assert_ne!(self.transaction_depth, 0); self.transaction_depth -= 1; @@ -3561,7 +3566,7 @@ impl Excerpt { fn new( id: ExcerptId, locator: Locator, - buffer_id: u64, + buffer_id: BufferId, buffer: BufferSnapshot, range: ExcerptRange, has_trailing_newline: bool, @@ -4154,8 +4159,13 @@ mod tests { #[gpui::test] fn test_singleton(cx: &mut AppContext) { - let buffer = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(6, 6, 'a'), + ) + }); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let snapshot = multibuffer.read(cx).snapshot(cx); @@ -4182,7 +4192,8 @@ mod tests { #[gpui::test] fn test_remote(cx: &mut AppContext) { - let host_buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "a")); + let host_buffer = + cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "a")); let guest_buffer = cx.new_model(|cx| { let state = host_buffer.read(cx).to_proto(); let ops = cx @@ -4213,10 +4224,20 @@ mod tests { #[gpui::test] fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) { - let buffer_1 = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'a'))); - let buffer_2 = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(6, 6, 'g'))); + let buffer_1 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(6, 6, 'a'), + ) + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(6, 6, 'g'), + ) + }); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let events = Arc::new(RwLock::new(Vec::::new())); @@ -4449,10 +4470,20 @@ mod tests { #[gpui::test] fn test_excerpt_events(cx: &mut AppContext) { - let buffer_1 = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'a'))); - let buffer_2 = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(10, 3, 'm'))); + let buffer_1 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(10, 3, 'a'), + ) + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(10, 3, 'm'), + ) + }); let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); @@ -4557,8 +4588,13 @@ mod tests { #[gpui::test] fn test_push_excerpts_with_context_lines(cx: &mut AppContext) { - let buffer = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(20, 3, 'a'), + ) + }); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { multibuffer.push_excerpts_with_context_lines( @@ -4594,8 +4630,13 @@ mod tests { #[gpui::test] async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) { - let buffer = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), sample_text(20, 3, 'a'))); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + sample_text(20, 3, 'a'), + ) + }); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| { let snapshot = buffer.read(cx); @@ -4641,7 +4682,9 @@ mod tests { #[gpui::test] fn test_singleton_multibuffer_anchors(cx: &mut AppContext) { - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); + let buffer = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") + }); let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx)); let old_snapshot = multibuffer.read(cx).snapshot(cx); buffer.update(cx, |buffer, cx| { @@ -4661,8 +4704,12 @@ mod tests { #[gpui::test] fn test_multibuffer_anchors(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); - let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "efghi")); + let buffer_1 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "efghi") + }); let multibuffer = cx.new_model(|cx| { let mut multibuffer = MultiBuffer::new(0, Capability::ReadWrite); multibuffer.push_excerpts( @@ -4719,9 +4766,16 @@ mod tests { #[gpui::test] fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) { - let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "abcd")); - let buffer_2 = - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "ABCDEFGHIJKLMNOP")); + let buffer_1 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcd") + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + "ABCDEFGHIJKLMNOP", + ) + }); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); // Create an insertion id in buffer 1 that doesn't exist in buffer 2. @@ -4932,9 +4986,13 @@ mod tests { let base_text = util::RandomCharIter::new(&mut rng) .take(10) .collect::(); - buffers.push( - cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), base_text)), - ); + buffers.push(cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + base_text, + ) + })); buffers.last().unwrap() } else { buffers.choose(&mut rng).unwrap() @@ -5276,8 +5334,12 @@ mod tests { let test_settings = SettingsStore::test(cx); cx.set_global(test_settings); - let buffer_1 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "1234")); - let buffer_2 = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), "5678")); + let buffer_1 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "1234") + }); + let buffer_2 = cx.new_model(|cx| { + Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "5678") + }); let multibuffer = cx.new_model(|_| MultiBuffer::new(0, Capability::ReadWrite)); let group_interval = multibuffer.read(cx).history.group_interval; multibuffer.update(cx, |multibuffer, cx| { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 5536b4d84d..20bcd4b7b3 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -21,7 +21,7 @@ use lsp::{ OneOf, ServerCapabilities, }; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; -use text::LineEnding; +use text::{BufferId, LineEnding}; pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions { lsp::FormattingOptions { @@ -84,7 +84,7 @@ pub trait LspCommand: 'static + Sized + Send { cx: AsyncAppContext, ) -> Result; - fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64; + fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result; } pub(crate) struct PrepareRename { @@ -205,7 +205,7 @@ impl LspCommand for PrepareRename { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PrepareRename { proto::PrepareRename { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -274,8 +274,8 @@ impl LspCommand for PrepareRename { } } - fn buffer_id_from_proto(message: &proto::PrepareRename) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::PrepareRename) -> Result { + BufferId::new(message.buffer_id) } } @@ -332,7 +332,7 @@ impl LspCommand for PerformRename { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::PerformRename { proto::PerformRename { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -393,8 +393,8 @@ impl LspCommand for PerformRename { .await } - fn buffer_id_from_proto(message: &proto::PerformRename) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::PerformRename) -> Result { + BufferId::new(message.buffer_id) } } @@ -437,7 +437,7 @@ impl LspCommand for GetDefinition { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition { proto::GetDefinition { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -486,8 +486,8 @@ impl LspCommand for GetDefinition { location_links_from_proto(message.links, project, cx).await } - fn buffer_id_from_proto(message: &proto::GetDefinition) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetDefinition) -> Result { + BufferId::new(message.buffer_id) } } @@ -538,7 +538,7 @@ impl LspCommand for GetTypeDefinition { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition { proto::GetTypeDefinition { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -587,8 +587,8 @@ impl LspCommand for GetTypeDefinition { location_links_from_proto(message.links, project, cx).await } - fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetTypeDefinition) -> Result { + BufferId::new(message.buffer_id) } } @@ -617,9 +617,10 @@ async fn location_links_from_proto( for link in proto_links { let origin = match link.origin { Some(origin) => { + let buffer_id = BufferId::new(origin.buffer_id)?; let buffer = project .update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(origin.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await?; let start = origin @@ -642,9 +643,10 @@ async fn location_links_from_proto( }; let target = link.target.ok_or_else(|| anyhow!("missing target"))?; + let buffer_id = BufferId::new(target.buffer_id)?; let buffer = project .update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(target.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await?; let start = target @@ -761,7 +763,9 @@ fn location_links_to_proto( .into_iter() .map(|definition| { let origin = definition.origin.map(|origin| { - let buffer_id = project.create_buffer_for_peer(&origin.buffer, peer_id, cx); + let buffer_id = project + .create_buffer_for_peer(&origin.buffer, peer_id, cx) + .into(); proto::Location { start: Some(serialize_anchor(&origin.range.start)), end: Some(serialize_anchor(&origin.range.end)), @@ -769,7 +773,9 @@ fn location_links_to_proto( } }); - let buffer_id = project.create_buffer_for_peer(&definition.target.buffer, peer_id, cx); + let buffer_id = project + .create_buffer_for_peer(&definition.target.buffer, peer_id, cx) + .into(); let target = proto::Location { start: Some(serialize_anchor(&definition.target.range.start)), end: Some(serialize_anchor(&definition.target.range.end)), @@ -859,7 +865,7 @@ impl LspCommand for GetReferences { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetReferences { proto::GetReferences { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -901,7 +907,7 @@ impl LspCommand for GetReferences { proto::Location { start: Some(serialize_anchor(&definition.range.start)), end: Some(serialize_anchor(&definition.range.end)), - buffer_id, + buffer_id: buffer_id.into(), } }) .collect(); @@ -917,9 +923,10 @@ impl LspCommand for GetReferences { ) -> Result> { let mut locations = Vec::new(); for location in message.locations { + let buffer_id = BufferId::new(location.buffer_id)?; let target_buffer = project .update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(location.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await?; let start = location @@ -941,8 +948,8 @@ impl LspCommand for GetReferences { Ok(locations) } - fn buffer_id_from_proto(message: &proto::GetReferences) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetReferences) -> Result { + BufferId::new(message.buffer_id) } } @@ -1007,7 +1014,7 @@ impl LspCommand for GetDocumentHighlights { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDocumentHighlights { proto::GetDocumentHighlights { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -1092,8 +1099,8 @@ impl LspCommand for GetDocumentHighlights { Ok(highlights) } - fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetDocumentHighlights) -> Result { + BufferId::new(message.buffer_id) } } @@ -1195,7 +1202,7 @@ impl LspCommand for GetHover { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> Self::ProtoRequest { proto::GetHover { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -1308,8 +1315,8 @@ impl LspCommand for GetHover { })) } - fn buffer_id_from_proto(message: &Self::ProtoRequest) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &Self::ProtoRequest) -> Result { + BufferId::new(message.buffer_id) } } @@ -1492,7 +1499,7 @@ impl LspCommand for GetCompletions { let anchor = buffer.anchor_after(self.position); proto::GetCompletions { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor(&anchor)), version: serialize_version(&buffer.version()), } @@ -1556,8 +1563,8 @@ impl LspCommand for GetCompletions { future::try_join_all(completions).await } - fn buffer_id_from_proto(message: &proto::GetCompletions) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result { + BufferId::new(message.buffer_id) } } @@ -1630,7 +1637,7 @@ impl LspCommand for GetCodeActions { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCodeActions { proto::GetCodeActions { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), start: Some(language::proto::serialize_anchor(&self.range.start)), end: Some(language::proto::serialize_anchor(&self.range.end)), version: serialize_version(&buffer.version()), @@ -1695,8 +1702,8 @@ impl LspCommand for GetCodeActions { .collect() } - fn buffer_id_from_proto(message: &proto::GetCodeActions) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::GetCodeActions) -> Result { + BufferId::new(message.buffer_id) } } @@ -1768,7 +1775,7 @@ impl LspCommand for OnTypeFormatting { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::OnTypeFormatting { proto::OnTypeFormatting { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -1831,8 +1838,8 @@ impl LspCommand for OnTypeFormatting { Ok(Some(language::proto::deserialize_transaction(transaction)?)) } - fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::OnTypeFormatting) -> Result { + BufferId::new(message.buffer_id) } } @@ -2291,7 +2298,7 @@ impl LspCommand for InlayHints { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::InlayHints { proto::InlayHints { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), start: Some(language::proto::serialize_anchor(&self.range.start)), end: Some(language::proto::serialize_anchor(&self.range.end)), version: serialize_version(&buffer.version()), @@ -2358,7 +2365,7 @@ impl LspCommand for InlayHints { Ok(hints) } - fn buffer_id_from_proto(message: &proto::InlayHints) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::InlayHints) -> Result { + BufferId::new(message.buffer_id) } } diff --git a/crates/project/src/lsp_ext_command.rs b/crates/project/src/lsp_ext_command.rs index 683e5087cc..a35fdd152f 100644 --- a/crates/project/src/lsp_ext_command.rs +++ b/crates/project/src/lsp_ext_command.rs @@ -1,13 +1,13 @@ use std::{path::Path, sync::Arc}; -use anyhow::Context; +use anyhow::{Context, Result}; use async_trait::async_trait; use gpui::{AppContext, AsyncAppContext, Model}; use language::{point_to_lsp, proto::deserialize_anchor, Buffer}; use lsp::{LanguageServer, LanguageServerId}; use rpc::proto::{self, PeerId}; use serde::{Deserialize, Serialize}; -use text::{PointUtf16, ToPointUtf16}; +use text::{BufferId, PointUtf16, ToPointUtf16}; use crate::{lsp_command::LspCommand, Project}; @@ -83,7 +83,7 @@ impl LspCommand for ExpandMacro { fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro { proto::LspExtExpandMacro { project_id, - buffer_id: buffer.remote_id(), + buffer_id: buffer.remote_id().into(), position: Some(language::proto::serialize_anchor( &buffer.anchor_before(self.position), )), @@ -131,7 +131,7 @@ impl LspCommand for ExpandMacro { }) } - fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> u64 { - message.buffer_id + fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> Result { + BufferId::new(message.buffer_id) } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4f7fe8302a..9b1a59ce85 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -12,7 +12,7 @@ mod project_tests; #[cfg(test)] mod worktree_tests; -use anyhow::{anyhow, Context as _, Result}; +use anyhow::{anyhow, bail, Context as _, Result}; use client::{proto, Client, Collaborator, TypedEnvelope, UserStore}; use clock::ReplicaId; use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}; @@ -81,7 +81,7 @@ use std::{ time::{Duration, Instant}, }; use terminals::Terminals; -use text::Anchor; +use text::{Anchor, BufferId}; use util::{ debug_panic, defer, http::HttpClient, merge_json_value_into, paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _, @@ -120,9 +120,9 @@ pub struct Project { collaborators: HashMap, client_subscriptions: Vec, _subscriptions: Vec, - next_buffer_id: u64, + next_buffer_id: BufferId, opened_buffer: (watch::Sender<()>, watch::Receiver<()>), - shared_buffers: HashMap>, + shared_buffers: HashMap>, #[allow(clippy::type_complexity)] loading_buffers_by_path: HashMap< ProjectPath, @@ -131,14 +131,14 @@ pub struct Project { #[allow(clippy::type_complexity)] loading_local_worktrees: HashMap, Shared, Arc>>>>, - opened_buffers: HashMap, - local_buffer_ids_by_path: HashMap, - local_buffer_ids_by_entry_id: HashMap, + opened_buffers: HashMap, + local_buffer_ids_by_path: HashMap, + local_buffer_ids_by_entry_id: HashMap, /// A mapping from a buffer ID to None means that we've started waiting for an ID but haven't finished loading it. /// Used for re-issuing buffer requests when peers temporarily disconnect - incomplete_remote_buffers: HashMap>>, - buffer_snapshots: HashMap>>, // buffer_id -> server_id -> vec of snapshots - buffers_being_formatted: HashSet, + incomplete_remote_buffers: HashMap>>, + buffer_snapshots: HashMap>>, // buffer_id -> server_id -> vec of snapshots + buffers_being_formatted: HashSet, buffers_needing_diff: HashSet>, git_diff_debouncer: DelayedDebounced, nonce: u128, @@ -210,7 +210,7 @@ struct LspBufferSnapshot { /// Message ordered with respect to buffer operations enum BufferOrderedMessage { Operation { - buffer_id: u64, + buffer_id: BufferId, operation: proto::Operation, }, LanguageServerUpdate { @@ -224,7 +224,7 @@ enum LocalProjectUpdate { WorktreesChanged, CreateBufferForPeer { peer_id: proto::PeerId, - buffer_id: u64, + buffer_id: BufferId, }, } @@ -636,7 +636,7 @@ impl Project { worktrees: Vec::new(), buffer_ordered_messages_tx: tx, collaborators: Default::default(), - next_buffer_id: 0, + next_buffer_id: BufferId::new(1).unwrap(), opened_buffers: Default::default(), shared_buffers: Default::default(), incomplete_remote_buffers: Default::default(), @@ -722,7 +722,7 @@ impl Project { worktrees: Vec::new(), buffer_ordered_messages_tx: tx, loading_buffers_by_path: Default::default(), - next_buffer_id: 0, + next_buffer_id: BufferId::default(), opened_buffer: watch::channel(), shared_buffers: Default::default(), incomplete_remote_buffers: Default::default(), @@ -997,7 +997,7 @@ impl Project { cx.notify(); } - pub fn buffer_for_id(&self, remote_id: u64) -> Option> { + pub fn buffer_for_id(&self, remote_id: BufferId) -> Option> { self.opened_buffers .get(&remote_id) .and_then(|buffer| buffer.upgrade()) @@ -1479,7 +1479,7 @@ impl Project { variant: Some( proto::create_buffer_for_peer::Variant::Chunk( proto::BufferChunk { - buffer_id, + buffer_id: buffer_id.into(), operations: chunk, is_last, }, @@ -1713,7 +1713,7 @@ impl Project { if self.is_remote() { return Err(anyhow!("creating buffers as a guest is not supported yet")); } - let id = post_inc(&mut self.next_buffer_id); + let id = self.next_buffer_id.next(); let buffer = cx.new_model(|cx| { Buffer::new(self.replica_id(), id, text) .with_language(language.unwrap_or_else(|| language::PLAIN_TEXT.clone()), cx) @@ -1814,7 +1814,7 @@ impl Project { worktree: &Model, cx: &mut ModelContext, ) -> Task>> { - let buffer_id = post_inc(&mut self.next_buffer_id); + let buffer_id = self.next_buffer_id.next(); let load_buffer = worktree.update(cx, |worktree, cx| { let worktree = worktree.as_local_mut().unwrap(); worktree.load_buffer(buffer_id, path, cx) @@ -1845,8 +1845,9 @@ impl Project { path: path_string, }) .await?; + let buffer_id = BufferId::new(response.buffer_id)?; this.update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(response.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await }) @@ -1895,7 +1896,7 @@ impl Project { pub fn open_buffer_by_id( &mut self, - id: u64, + id: BufferId, cx: &mut ModelContext, ) -> Task>> { if let Some(buffer) = self.buffer_for_id(id) { @@ -1903,11 +1904,12 @@ impl Project { } else if self.is_local() { Task::ready(Err(anyhow!("buffer {} does not exist", id))) } else if let Some(project_id) = self.remote_id() { - let request = self - .client - .request(proto::OpenBufferById { project_id, id }); + let request = self.client.request(proto::OpenBufferById { + project_id, + id: id.into(), + }); cx.spawn(move |this, mut cx| async move { - let buffer_id = request.await?.buffer_id; + let buffer_id = BufferId::new(request.await?.buffer_id)?; this.update(&mut cx, |this, cx| { this.wait_for_remote_buffer(buffer_id, cx) })? @@ -2223,7 +2225,7 @@ impl Project { let mut operations_by_buffer_id = HashMap::default(); async fn flush_operations( this: &WeakModel, - operations_by_buffer_id: &mut HashMap>, + operations_by_buffer_id: &mut HashMap>, needs_resync_with_host: &mut bool, is_local: bool, cx: &mut AsyncAppContext, @@ -2232,7 +2234,7 @@ impl Project { let request = this.update(cx, |this, _| { let project_id = this.remote_id()?; Some(this.client.request(proto::UpdateBuffer { - buffer_id, + buffer_id: buffer_id.into(), project_id, operations, })) @@ -4078,7 +4080,9 @@ impl Project { buffer_ids: remote_buffers .iter() .filter_map(|buffer| { - buffer.update(&mut cx, |buffer, _| buffer.remote_id()).ok() + buffer + .update(&mut cx, |buffer, _| buffer.remote_id().into()) + .ok() }) .collect(), }) @@ -4324,7 +4328,7 @@ impl Project { buffer_ids: buffers .iter() .map(|buffer| { - buffer.update(&mut cx, |buffer, _| buffer.remote_id()) + buffer.update(&mut cx, |buffer, _| buffer.remote_id().into()) }) .collect::>()?, }) @@ -4720,8 +4724,9 @@ impl Project { }); cx.spawn(move |this, mut cx| async move { let response = request.await?; + let buffer_id = BufferId::new(response.buffer_id)?; this.update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(response.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await }) @@ -5047,7 +5052,7 @@ impl Project { let response = client .request(proto::ApplyCompletionAdditionalEdits { project_id, - buffer_id, + buffer_id: buffer_id.into(), completion: Some(language::proto::serialize_completion(&completion)), }) .await?; @@ -5179,7 +5184,7 @@ impl Project { let client = self.client.clone(); let request = proto::ApplyCodeAction { project_id, - buffer_id: buffer_handle.read(cx).remote_id(), + buffer_id: buffer_handle.read(cx).remote_id().into(), action: Some(language::proto::serialize_code_action(&action)), }; cx.spawn(move |this, mut cx| async move { @@ -5242,7 +5247,7 @@ impl Project { let client = self.client.clone(); let request = proto::OnTypeFormatting { project_id, - buffer_id: buffer.read(cx).remote_id(), + buffer_id: buffer.read(cx).remote_id().into(), position: Some(serialize_anchor(&position)), trigger, version: serialize_version(&buffer.read(cx).version()), @@ -5531,7 +5536,7 @@ impl Project { let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end); let range_start = range.start; let range_end = range.end; - let buffer_id = buffer.remote_id(); + let buffer_id = buffer.remote_id().into(); let buffer_version = buffer.version().clone(); let lsp_request = InlayHints { range }; @@ -5624,7 +5629,7 @@ impl Project { let client = self.client.clone(); let request = proto::ResolveInlayHint { project_id, - buffer_id: buffer_handle.read(cx).remote_id(), + buffer_id: buffer_handle.read(cx).remote_id().into(), language_server_id: server_id.0 as u64, hint: Some(InlayHints::project_to_proto_hint(hint.clone())), }; @@ -5659,9 +5664,10 @@ impl Project { let response = request.await?; let mut result = HashMap::default(); for location in response.locations { + let buffer_id = BufferId::new(location.buffer_id)?; let target_buffer = this .update(&mut cx, |this, cx| { - this.wait_for_remote_buffer(location.buffer_id, cx) + this.wait_for_remote_buffer(buffer_id, cx) })? .await?; let start = location @@ -6555,7 +6561,7 @@ impl Project { self.client .send(proto::UpdateBufferFile { project_id, - buffer_id: buffer_id as u64, + buffer_id: buffer_id.into(), file: Some(new_file.to_proto()), }) .log_err(); @@ -6721,7 +6727,7 @@ impl Project { for (buffer, diff_base) in diff_bases_by_buffer { let buffer_id = buffer.update(&mut cx, |buffer, cx| { buffer.set_diff_base(diff_base.clone(), cx); - buffer.remote_id() + buffer.remote_id().into() })?; if let Some(project_id) = remote_id { client @@ -7353,7 +7359,7 @@ impl Project { ) -> Result { this.update(&mut cx, |this, cx| { let payload = envelope.payload.clone(); - let buffer_id = payload.buffer_id; + let buffer_id = BufferId::new(payload.buffer_id)?; let ops = payload .operations .into_iter() @@ -7404,7 +7410,7 @@ impl Project { as Arc); } - let buffer_id = state.id; + let buffer_id = BufferId::new(state.id)?; let buffer = cx.new_model(|_| { Buffer::from_proto(this.replica_id(), this.capability(), state, buffer_file) .unwrap() @@ -7413,9 +7419,10 @@ impl Project { .insert(buffer_id, Some(buffer)); } proto::create_buffer_for_peer::Variant::Chunk(chunk) => { + let buffer_id = BufferId::new(chunk.buffer_id)?; let buffer = this .incomplete_remote_buffers - .get(&chunk.buffer_id) + .get(&buffer_id) .cloned() .flatten() .ok_or_else(|| { @@ -7432,7 +7439,7 @@ impl Project { buffer.update(cx, |buffer, cx| buffer.apply_ops(operations, cx))?; if chunk.is_last { - this.incomplete_remote_buffers.remove(&chunk.buffer_id); + this.incomplete_remote_buffers.remove(&buffer_id); this.register_buffer(&buffer, cx)?; } } @@ -7450,6 +7457,7 @@ impl Project { ) -> Result<()> { this.update(&mut cx, |this, cx| { let buffer_id = envelope.payload.buffer_id; + let buffer_id = BufferId::new(buffer_id)?; let diff_base = envelope.payload.diff_base; if let Some(buffer) = this .opened_buffers @@ -7475,6 +7483,7 @@ impl Project { mut cx: AsyncAppContext, ) -> Result<()> { let buffer_id = envelope.payload.buffer_id; + let buffer_id = BufferId::new(buffer_id)?; this.update(&mut cx, |this, cx| { let payload = envelope.payload.clone(); @@ -7509,7 +7518,7 @@ impl Project { _: Arc, mut cx: AsyncAppContext, ) -> Result { - let buffer_id = envelope.payload.buffer_id; + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let (project_id, buffer) = this.update(&mut cx, |this, _cx| { let project_id = this.remote_id().ok_or_else(|| anyhow!("not connected"))?; let buffer = this @@ -7530,7 +7539,7 @@ impl Project { .await?; Ok(buffer.update(&mut cx, |buffer, _| proto::BufferSaved { project_id, - buffer_id, + buffer_id: buffer_id.into(), version: serialize_version(buffer.saved_version()), mtime: Some(buffer.saved_mtime().into()), fingerprint: language::proto::serialize_fingerprint(buffer.saved_version_fingerprint()), @@ -7547,9 +7556,10 @@ impl Project { let reload = this.update(&mut cx, |this, cx| { let mut buffers = HashSet::default(); for buffer_id in &envelope.payload.buffer_ids { + let buffer_id = BufferId::new(*buffer_id)?; buffers.insert( this.opened_buffers - .get(buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?, ); @@ -7580,12 +7590,12 @@ impl Project { this.update(&mut cx, |this, cx| { let Some(guest_id) = envelope.original_sender_id else { error!("missing original_sender_id on SynchronizeBuffers request"); - return; + bail!("missing original_sender_id on SynchronizeBuffers request"); }; this.shared_buffers.entry(guest_id).or_default().clear(); for buffer in envelope.payload.buffers { - let buffer_id = buffer.id; + let buffer_id = BufferId::new(buffer.id)?; let remote_version = language::proto::deserialize_version(&buffer.version); if let Some(buffer) = this.buffer_for_id(buffer_id) { this.shared_buffers @@ -7595,7 +7605,7 @@ impl Project { let buffer = buffer.read(cx); response.buffers.push(proto::BufferVersion { - id: buffer_id, + id: buffer_id.into(), version: language::proto::serialize_version(&buffer.version), }); @@ -7605,7 +7615,7 @@ impl Project { client .send(proto::UpdateBufferFile { project_id, - buffer_id: buffer_id as u64, + buffer_id: buffer_id.into(), file: Some(file.to_proto()), }) .log_err(); @@ -7614,7 +7624,7 @@ impl Project { client .send(proto::UpdateDiffBase { project_id, - buffer_id: buffer_id as u64, + buffer_id: buffer_id.into(), diff_base: buffer.diff_base().map(Into::into), }) .log_err(); @@ -7622,7 +7632,7 @@ impl Project { client .send(proto::BufferReloaded { project_id, - buffer_id, + buffer_id: buffer_id.into(), version: language::proto::serialize_version(buffer.saved_version()), mtime: Some(buffer.saved_mtime().into()), fingerprint: language::proto::serialize_fingerprint( @@ -7642,7 +7652,7 @@ impl Project { client .request(proto::UpdateBuffer { project_id, - buffer_id, + buffer_id: buffer_id.into(), operations: chunk, }) .await?; @@ -7654,7 +7664,8 @@ impl Project { .detach(); } } - })?; + Ok(()) + })??; Ok(response) } @@ -7669,9 +7680,10 @@ impl Project { let format = this.update(&mut cx, |this, cx| { let mut buffers = HashSet::default(); for buffer_id in &envelope.payload.buffer_ids { + let buffer_id = BufferId::new(*buffer_id)?; buffers.insert( this.opened_buffers - .get(buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?, ); @@ -7696,11 +7708,12 @@ impl Project { mut cx: AsyncAppContext, ) -> Result { let (buffer, completion) = this.update(&mut cx, |this, cx| { + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let buffer = this .opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) - .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?; + .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?; let language = buffer.read(cx).language(); let completion = language::proto::deserialize_completion( envelope @@ -7774,9 +7787,10 @@ impl Project { .ok_or_else(|| anyhow!("invalid action"))?, )?; let apply_code_action = this.update(&mut cx, |this, cx| { + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let buffer = this .opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?; Ok::<_, anyhow::Error>(this.apply_code_action(buffer, action, false, cx)) @@ -7798,11 +7812,12 @@ impl Project { mut cx: AsyncAppContext, ) -> Result { let on_type_formatting = this.update(&mut cx, |this, cx| { + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let buffer = this .opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) - .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id))?; + .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id))?; let position = envelope .payload .position @@ -7830,9 +7845,10 @@ impl Project { mut cx: AsyncAppContext, ) -> Result { let sender_id = envelope.original_sender_id()?; + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let buffer = this.update(&mut cx, |this, _| { this.opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id)) })??; @@ -7886,10 +7902,11 @@ impl Project { let hint = InlayHints::proto_to_project_hint(proto_hint) .context("resolved proto inlay hint conversion")?; let buffer = this.update(&mut cx, |this, _cx| { + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; this.opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) - .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id)) + .ok_or_else(|| anyhow!("unknown buffer id {}", buffer_id)) })??; let response_hint = this .update(&mut cx, |project, cx| { @@ -7930,7 +7947,7 @@ impl Project { ::Result: Send, { let sender_id = envelope.original_sender_id()?; - let buffer_id = T::buffer_id_from_proto(&envelope.payload); + let buffer_id = T::buffer_id_from_proto(&envelope.payload)?; let buffer_handle = this.update(&mut cx, |this, _cx| { this.opened_buffers .get(&buffer_id) @@ -7995,7 +8012,7 @@ impl Project { let start = serialize_anchor(&range.start); let end = serialize_anchor(&range.end); let buffer_id = this.update(&mut cx, |this, cx| { - this.create_buffer_for_peer(&buffer, peer_id, cx) + this.create_buffer_for_peer(&buffer, peer_id, cx).into() })?; locations.push(proto::Location { buffer_id, @@ -8037,7 +8054,7 @@ impl Project { Ok(proto::OpenBufferForSymbolResponse { buffer_id: this.update(&mut cx, |this, cx| { - this.create_buffer_for_peer(&buffer, peer_id, cx) + this.create_buffer_for_peer(&buffer, peer_id, cx).into() })?, }) } @@ -8057,14 +8074,13 @@ impl Project { mut cx: AsyncAppContext, ) -> Result { let peer_id = envelope.original_sender_id()?; + let buffer_id = BufferId::new(envelope.payload.id)?; let buffer = this - .update(&mut cx, |this, cx| { - this.open_buffer_by_id(envelope.payload.id, cx) - })? + .update(&mut cx, |this, cx| this.open_buffer_by_id(buffer_id, cx))? .await?; this.update(&mut cx, |this, cx| { Ok(proto::OpenBufferResponse { - buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx), + buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(), }) })? } @@ -8090,7 +8106,7 @@ impl Project { let buffer = open_buffer.await?; this.update(&mut cx, |this, cx| { Ok(proto::OpenBufferResponse { - buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx), + buffer_id: this.create_buffer_for_peer(&buffer, peer_id, cx).into(), }) })? } @@ -8108,7 +8124,7 @@ impl Project { for (buffer, transaction) in project_transaction.0 { serialized_transaction .buffer_ids - .push(self.create_buffer_for_peer(&buffer, peer_id, cx)); + .push(self.create_buffer_for_peer(&buffer, peer_id, cx).into()); serialized_transaction .transactions .push(language::proto::serialize_transaction(&transaction)); @@ -8126,6 +8142,7 @@ impl Project { let mut project_transaction = ProjectTransaction::default(); for (buffer_id, transaction) in message.buffer_ids.into_iter().zip(message.transactions) { + let buffer_id = BufferId::new(buffer_id)?; let buffer = this .update(&mut cx, |this, cx| { this.wait_for_remote_buffer(buffer_id, cx) @@ -8158,7 +8175,7 @@ impl Project { buffer: &Model, peer_id: proto::PeerId, cx: &mut AppContext, - ) -> u64 { + ) -> BufferId { let buffer_id = buffer.read(cx).remote_id(); if let ProjectClientState::Shared { updates_tx, .. } = &self.client_state { updates_tx @@ -8170,7 +8187,7 @@ impl Project { fn wait_for_remote_buffer( &mut self, - id: u64, + id: BufferId, cx: &mut ModelContext, ) -> Task>> { let mut opened_buffer_rx = self.opened_buffer.1.clone(); @@ -8239,7 +8256,7 @@ impl Project { .filter_map(|(id, buffer)| { let buffer = buffer.upgrade()?; Some(proto::BufferVersion { - id: *id, + id: (*id).into(), version: language::proto::serialize_version(&buffer.read(cx).version), }) }) @@ -8265,7 +8282,12 @@ impl Project { .into_iter() .map(|buffer| { let client = client.clone(); - let buffer_id = buffer.id; + let buffer_id = match BufferId::new(buffer.id) { + Ok(id) => id, + Err(e) => { + return Task::ready(Err(e)); + } + }; let remote_version = language::proto::deserialize_version(&buffer.version); if let Some(buffer) = this.buffer_for_id(buffer_id) { let operations = @@ -8276,7 +8298,7 @@ impl Project { client .request(proto::UpdateBuffer { project_id, - buffer_id, + buffer_id: buffer_id.into(), operations: chunk, }) .await?; @@ -8294,7 +8316,10 @@ impl Project { // creates these buffers for us again to unblock any waiting futures. for id in incomplete_buffer_ids { cx.background_executor() - .spawn(client.request(proto::OpenBufferById { project_id, id })) + .spawn(client.request(proto::OpenBufferById { + project_id, + id: id.into(), + })) .detach(); } @@ -8436,6 +8461,7 @@ impl Project { ) -> Result<()> { let fingerprint = deserialize_fingerprint(&envelope.payload.fingerprint)?; let version = deserialize_version(&envelope.payload.version); + let buffer_id = BufferId::new(envelope.payload.buffer_id)?; let mtime = envelope .payload .mtime @@ -8445,11 +8471,11 @@ impl Project { this.update(&mut cx, |this, cx| { let buffer = this .opened_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .or_else(|| { this.incomplete_remote_buffers - .get(&envelope.payload.buffer_id) + .get(&buffer_id) .and_then(|b| b.clone()) }); if let Some(buffer) = buffer { @@ -8478,14 +8504,15 @@ impl Project { .mtime .ok_or_else(|| anyhow!("missing mtime"))? .into(); + let buffer_id = BufferId::new(payload.buffer_id)?; this.update(&mut cx, |this, cx| { let buffer = this .opened_buffers - .get(&payload.buffer_id) + .get(&buffer_id) .and_then(|buffer| buffer.upgrade()) .or_else(|| { this.incomplete_remote_buffers - .get(&payload.buffer_id) + .get(&buffer_id) .cloned() .flatten() }); diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 3ece157ba5..84bb0aa7a6 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -62,6 +62,7 @@ use std::{ time::{Duration, SystemTime}, }; use sum_tree::{Bias, Edit, SeekTarget, SumTree, TreeMap, TreeSet}; +use text::BufferId; use util::{ paths::{PathMatcher, HOME}, ResultExt, @@ -672,7 +673,7 @@ impl LocalWorktree { pub(crate) fn load_buffer( &mut self, - id: u64, + id: BufferId, path: &Path, cx: &mut ModelContext, ) -> Task>> { @@ -1043,7 +1044,7 @@ impl LocalWorktree { let buffer = buffer_handle.read(cx); let rpc = self.client.clone(); - let buffer_id = buffer.remote_id(); + let buffer_id: u64 = buffer.remote_id().into(); let project_id = self.share.as_ref().map(|share| share.project_id); let text = buffer.as_rope().clone(); @@ -1481,7 +1482,7 @@ impl RemoteWorktree { cx: &mut ModelContext, ) -> Task> { let buffer = buffer_handle.read(cx); - let buffer_id = buffer.remote_id(); + let buffer_id = buffer.remote_id().into(); let version = buffer.version(); let rpc = self.client.clone(); let project_id = self.project_id; @@ -2840,7 +2841,7 @@ impl language::LocalFile for File { fn buffer_reloaded( &self, - buffer_id: u64, + buffer_id: BufferId, version: &clock::Global, fingerprint: RopeFingerprint, line_ending: LineEnding, @@ -2853,7 +2854,7 @@ impl language::LocalFile for File { .client .send(proto::BufferReloaded { project_id, - buffer_id, + buffer_id: buffer_id.into(), version: serialize_version(version), mtime: Some(mtime.into()), fingerprint: serialize_fingerprint(fingerprint), diff --git a/crates/project/src/worktree_tests.rs b/crates/project/src/worktree_tests.rs index fbf8b74d62..26a180e5f9 100644 --- a/crates/project/src/worktree_tests.rs +++ b/crates/project/src/worktree_tests.rs @@ -21,6 +21,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; +use text::BufferId; use util::{http::FakeHttpClient, test::temp_tree, ResultExt}; #[gpui::test] @@ -511,9 +512,11 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) { let prev_read_dir_count = fs.read_dir_call_count(); let buffer = tree .update(cx, |tree, cx| { - tree.as_local_mut() - .unwrap() - .load_buffer(0, "one/node_modules/b/b1.js".as_ref(), cx) + tree.as_local_mut().unwrap().load_buffer( + BufferId::new(1).unwrap(), + "one/node_modules/b/b1.js".as_ref(), + cx, + ) }) .await .unwrap(); @@ -553,9 +556,11 @@ async fn test_open_gitignored_files(cx: &mut TestAppContext) { let prev_read_dir_count = fs.read_dir_call_count(); let buffer = tree .update(cx, |tree, cx| { - tree.as_local_mut() - .unwrap() - .load_buffer(0, "one/node_modules/a/a2.js".as_ref(), cx) + tree.as_local_mut().unwrap().load_buffer( + BufferId::new(1).unwrap(), + "one/node_modules/a/a2.js".as_ref(), + cx, + ) }) .await .unwrap(); diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index ad08636852..bf01347ce8 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -1007,7 +1007,7 @@ mod tests { use super::*; use editor::{DisplayPoint, Editor}; use gpui::{Context, Hsla, TestAppContext, VisualTestContext}; - use language::Buffer; + use language::{Buffer, BufferId}; use smol::stream::StreamExt as _; use unindent::Unindent as _; @@ -1029,7 +1029,7 @@ mod tests { let buffer = cx.new_model(|cx| { Buffer::new( 0, - cx.entity_id().as_u64(), + BufferId::new(cx.entity_id().as_u64()).unwrap(), r#" A regular expression (shortened as regex or regexp;[1] also referred to as rational expression[2][3]) is a sequence of characters that specifies a search @@ -1385,7 +1385,13 @@ mod tests { expected_query_matches_count > 1, "Should pick a query with multiple results" ); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), buffer_text)); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + buffer_text, + ) + }); let window = cx.add_window(|_| ()); let editor = window.build_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx)); @@ -1581,7 +1587,13 @@ mod tests { for "find" or "find and replace" operations on strings, or for input validation. "# .unindent(); - let buffer = cx.new_model(|cx| Buffer::new(0, cx.entity_id().as_u64(), buffer_text)); + let buffer = cx.new_model(|cx| { + Buffer::new( + 0, + BufferId::new(cx.entity_id().as_u64()).unwrap(), + buffer_text, + ) + }); let cx = cx.add_empty_window(); let editor = cx.new_view(|cx| Editor::for_buffer(buffer.clone(), None, cx)); diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index a65e3753d4..db44a34030 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,6 +1,6 @@ use crate::{ - locator::Locator, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, ToPoint, - ToPointUtf16, + locator::Locator, BufferId, BufferSnapshot, Point, PointUtf16, TextDimension, ToOffset, + ToPoint, ToPointUtf16, }; use anyhow::Result; use std::{cmp::Ordering, fmt::Debug, ops::Range}; @@ -11,7 +11,7 @@ pub struct Anchor { pub timestamp: clock::Lamport, pub offset: usize, pub bias: Bias, - pub buffer_id: Option, + pub buffer_id: Option, } impl Anchor { diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index 7e26e0a296..ca1e81b1c3 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -18,7 +18,7 @@ fn init_logger() { #[test] fn test_edit() { - let mut buffer = Buffer::new(0, 0, "abc".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "abc".into()); assert_eq!(buffer.text(), "abc"); buffer.edit([(3..3, "def")]); assert_eq!(buffer.text(), "abcdef"); @@ -42,7 +42,7 @@ fn test_random_edits(mut rng: StdRng) { let mut reference_string = RandomCharIter::new(&mut rng) .take(reference_string_len) .collect::(); - let mut buffer = Buffer::new(0, 0, reference_string.clone()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), reference_string.clone()); LineEnding::normalize(&mut reference_string); buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200))); @@ -164,7 +164,7 @@ fn test_line_endings() { LineEnding::Windows ); - let mut buffer = Buffer::new(0, 0, "one\r\ntwo\rthree".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "one\r\ntwo\rthree".into()); assert_eq!(buffer.text(), "one\ntwo\nthree"); assert_eq!(buffer.line_ending(), LineEnding::Windows); buffer.check_invariants(); @@ -178,7 +178,7 @@ fn test_line_endings() { #[test] fn test_line_len() { - let mut buffer = Buffer::new(0, 0, "".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "".into()); buffer.edit([(0..0, "abcd\nefg\nhij")]); buffer.edit([(12..12, "kl\nmno")]); buffer.edit([(18..18, "\npqrs\n")]); @@ -195,7 +195,7 @@ fn test_line_len() { #[test] fn test_common_prefix_at_position() { let text = "a = str; b = δα"; - let buffer = Buffer::new(0, 0, text.into()); + let buffer = Buffer::new(0, BufferId::new(1).unwrap(), text.into()); let offset1 = offset_after(text, "str"); let offset2 = offset_after(text, "δα"); @@ -243,7 +243,11 @@ fn test_common_prefix_at_position() { #[test] fn test_text_summary_for_range() { - let buffer = Buffer::new(0, 0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz".into()); + let buffer = Buffer::new( + 0, + BufferId::new(1).unwrap(), + "ab\nefg\nhklm\nnopqrs\ntuvwxyz".into(), + ); assert_eq!( buffer.text_summary_for_range::(1..3), TextSummary { @@ -313,7 +317,7 @@ fn test_text_summary_for_range() { #[test] fn test_chars_at() { - let mut buffer = Buffer::new(0, 0, "".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "".into()); buffer.edit([(0..0, "abcd\nefgh\nij")]); buffer.edit([(12..12, "kl\nmno")]); buffer.edit([(18..18, "\npqrs")]); @@ -335,7 +339,7 @@ fn test_chars_at() { assert_eq!(chars.collect::(), "PQrs"); // Regression test: - let mut buffer = Buffer::new(0, 0, "".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "".into()); buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]); buffer.edit([(60..60, "\n")]); @@ -345,7 +349,7 @@ fn test_chars_at() { #[test] fn test_anchors() { - let mut buffer = Buffer::new(0, 0, "".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "".into()); buffer.edit([(0..0, "abc")]); let left_anchor = buffer.anchor_before(2); let right_anchor = buffer.anchor_after(2); @@ -463,7 +467,7 @@ fn test_anchors() { #[test] fn test_anchors_at_start_and_end() { - let mut buffer = Buffer::new(0, 0, "".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "".into()); let before_start_anchor = buffer.anchor_before(0); let after_end_anchor = buffer.anchor_after(0); @@ -486,7 +490,7 @@ fn test_anchors_at_start_and_end() { #[test] fn test_undo_redo() { - let mut buffer = Buffer::new(0, 0, "1234".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "1234".into()); // Set group interval to zero so as to not group edits in the undo stack. buffer.set_group_interval(Duration::from_secs(0)); @@ -523,7 +527,7 @@ fn test_undo_redo() { #[test] fn test_history() { let mut now = Instant::now(); - let mut buffer = Buffer::new(0, 0, "123456".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "123456".into()); buffer.set_group_interval(Duration::from_millis(300)); let transaction_1 = buffer.start_transaction_at(now).unwrap(); @@ -590,7 +594,7 @@ fn test_history() { #[test] fn test_finalize_last_transaction() { let now = Instant::now(); - let mut buffer = Buffer::new(0, 0, "123456".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "123456".into()); buffer.start_transaction_at(now); buffer.edit([(2..4, "cd")]); @@ -625,7 +629,7 @@ fn test_finalize_last_transaction() { #[test] fn test_edited_ranges_for_transaction() { let now = Instant::now(); - let mut buffer = Buffer::new(0, 0, "1234567".into()); + let mut buffer = Buffer::new(0, BufferId::new(1).unwrap(), "1234567".into()); buffer.start_transaction_at(now); buffer.edit([(2..4, "cd")]); @@ -664,9 +668,9 @@ fn test_edited_ranges_for_transaction() { fn test_concurrent_edits() { let text = "abcdef"; - let mut buffer1 = Buffer::new(1, 0, text.into()); - let mut buffer2 = Buffer::new(2, 0, text.into()); - let mut buffer3 = Buffer::new(3, 0, text.into()); + let mut buffer1 = Buffer::new(1, BufferId::new(1).unwrap(), text.into()); + let mut buffer2 = Buffer::new(2, BufferId::new(1).unwrap(), text.into()); + let mut buffer3 = Buffer::new(3, BufferId::new(1).unwrap(), text.into()); let buf1_op = buffer1.edit([(1..2, "12")]); assert_eq!(buffer1.text(), "a12cdef"); @@ -705,7 +709,7 @@ fn test_random_concurrent_edits(mut rng: StdRng) { let mut network = Network::new(rng.clone()); for i in 0..peers { - let mut buffer = Buffer::new(i as ReplicaId, 0, base_text.clone()); + let mut buffer = Buffer::new(i as ReplicaId, BufferId::new(1).unwrap(), base_text.clone()); buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200)); buffers.push(buffer); replica_ids.push(i as u16); diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index acc170a508..9ce9efe7e0 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -26,6 +26,7 @@ pub use selection::*; use std::{ borrow::Cow, cmp::{self, Ordering, Reverse}, + fmt::Display, future::Future, iter::Iterator, ops::{self, Deref, Range, Sub}, @@ -59,10 +60,39 @@ pub struct Buffer { wait_for_version_txs: Vec<(clock::Global, oneshot::Sender<()>)>, } +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, PartialOrd, Ord, Eq)] +pub struct BufferId(u64); + +impl Display for BufferId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl BufferId { + /// Returns Err if `id` is outside of BufferId domain. + pub fn new(id: u64) -> anyhow::Result { + Ok(Self(id)) + } + /// Increments this buffer id, returning the old value. + /// So that's a post-increment operator in disguise. + pub fn next(&mut self) -> Self { + let old = *self; + self.0 += 1; + old + } +} +impl From for u64 { + fn from(id: BufferId) -> Self { + id.0 + } +} + #[derive(Clone)] pub struct BufferSnapshot { replica_id: ReplicaId, - remote_id: u64, + remote_id: BufferId, visible_text: Rope, deleted_text: Rope, line_ending: LineEnding, @@ -369,7 +399,7 @@ struct Edits<'a, D: TextDimension, F: FnMut(&FragmentSummary) -> bool> { old_end: D, new_end: D, range: Range<(&'a Locator, usize)>, - buffer_id: u64, + buffer_id: BufferId, } #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -478,7 +508,7 @@ pub struct UndoOperation { } impl Buffer { - pub fn new(replica_id: u16, remote_id: u64, mut base_text: String) -> Buffer { + pub fn new(replica_id: u16, remote_id: BufferId, mut base_text: String) -> Buffer { let line_ending = LineEnding::detect(&base_text); LineEnding::normalize(&mut base_text); @@ -545,7 +575,7 @@ impl Buffer { self.lamport_clock.replica_id } - pub fn remote_id(&self) -> u64 { + pub fn remote_id(&self) -> BufferId { self.remote_id } @@ -1590,7 +1620,7 @@ impl BufferSnapshot { &self.visible_text } - pub fn remote_id(&self) -> u64 { + pub fn remote_id(&self) -> BufferId { self.remote_id } diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index e325c529a7..da964efd21 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -69,14 +69,14 @@ mod test { use crate::{test::VimTestContext, Vim}; use editor::Editor; use gpui::{Context, Entity, VisualTestContext}; - use language::Buffer; + use language::{Buffer, BufferId}; // regression test for blur called with a different active editor #[gpui::test] async fn test_blur_focus(cx: &mut gpui::TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n")); + let buffer = cx.new_model(|_| Buffer::new(0, BufferId::new(1).unwrap(), "a = 1\nb = 2\n")); let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx)); let editor2 = cx .update(|cx| { @@ -111,7 +111,7 @@ mod test { let mut cx1 = VisualTestContext::from_window(cx.window, &cx); let editor1 = cx.editor.clone(); - let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n")); + let buffer = cx.new_model(|_| Buffer::new(0, BufferId::new(1).unwrap(), "a = 1\nb = 2\n")); let (editor2, cx2) = cx.add_window_view(|cx| Editor::for_buffer(buffer, None, cx)); editor2.update(cx2, |_, cx| { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 57af9f5365..655ac044c1 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -278,6 +278,7 @@ mod tests { use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use settings::SettingsStore; use std::num::NonZeroU32; + use text::BufferId; #[gpui::test] async fn test_c_autoindent(cx: &mut TestAppContext) { @@ -295,8 +296,8 @@ mod tests { let language = crate::languages::language("c", tree_sitter_c::language(), None).await; cx.new_model(|cx| { - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") + .with_language(language, cx); // empty function buffer.edit([(0..0, "int main() {}")], None, cx); diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 437e3a2d87..216957df66 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -181,6 +181,7 @@ mod tests { use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer}; use settings::SettingsStore; use std::num::NonZeroU32; + use text::BufferId; #[gpui::test] async fn test_python_autoindent(cx: &mut TestAppContext) { @@ -199,8 +200,8 @@ mod tests { }); cx.new_model(|cx| { - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") + .with_language(language, cx); let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext| { let ix = buffer.len(); buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index c8ca6fc1f7..ffbe706fe8 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -297,6 +297,7 @@ mod tests { use gpui::{Context, Hsla, TestAppContext}; use language::language_settings::AllLanguageSettings; use settings::SettingsStore; + use text::BufferId; use theme::SyntaxTheme; #[gpui::test] @@ -509,8 +510,8 @@ mod tests { let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await; cx.new_model(|cx| { - let mut buffer = - Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx); + let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "") + .with_language(language, cx); // indent between braces buffer.set_text("fn a() {}", cx); diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 5d04e31f6b..08dc21965b 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -349,6 +349,7 @@ async fn get_cached_eslint_server_binary( #[cfg(test)] mod tests { use gpui::{Context, TestAppContext}; + use text::BufferId; use unindent::Unindent; #[gpui::test] @@ -376,7 +377,8 @@ mod tests { .unindent(); let buffer = cx.new_model(|cx| { - language::Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx) + language::Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text) + .with_language(language, cx) }); let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); assert_eq!(