From b6c7496aea2595fdbfbc04b0ac8dd393f363b569 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 12 Jun 2026 14:27:28 +0200 Subject: [PATCH] multi_buffer: Don't eagerly clone `BufferSnapshot` in `range_to_buffer_ranges` (#59190) Both cloning and dropping of these has quite a bit of overhead (despite them being snapshots), so avoid where possible, especially in display map syncing Release Notes: - N/A or Added/Fixed/Improved ... --- crates/agent_ui/src/completion_provider.rs | 8 +++---- crates/editor/src/completions.rs | 3 +++ crates/editor/src/display_map/block_map.rs | 9 ++++---- crates/editor/src/display_map/tab_map.rs | 13 ++++++------ crates/editor/src/display_map/wrap_map.rs | 7 +------ crates/editor/src/editor.rs | 4 +++- crates/editor/src/input.rs | 2 +- crates/editor/src/items.rs | 2 +- crates/editor/src/split.rs | 6 +++--- crates/file_finder/src/file_finder.rs | 6 +++--- crates/file_finder/src/file_finder_tests.rs | 2 +- crates/fuzzy/src/matcher.rs | 2 +- crates/fuzzy/src/paths.rs | 6 +----- crates/fuzzy_nucleo/src/paths.rs | 4 ++-- crates/inspector_ui/src/div_inspector.rs | 2 +- crates/languages/src/python.rs | 2 +- crates/multi_buffer/src/multi_buffer.rs | 12 ++++------- crates/multi_buffer/src/path_key.rs | 4 ++-- crates/project/src/manifest_tree.rs | 2 +- .../project/src/manifest_tree/server_tree.rs | 2 +- crates/project/src/project.rs | 6 +++--- crates/project/src/task_inventory.rs | 2 +- crates/project/src/toolchain_store.rs | 2 +- crates/project/src/worktree_store.rs | 2 +- crates/repl/src/kernels/mod.rs | 2 +- crates/repl/src/repl_store.rs | 2 +- crates/settings/src/settings_store.rs | 20 ++++++++---------- crates/terminal_view/src/terminal_view.rs | 2 +- crates/util/src/rel_path.rs | 6 ++++++ crates/worktree/src/worktree.rs | 21 +++++++------------ 30 files changed, 78 insertions(+), 85 deletions(-) diff --git a/crates/agent_ui/src/completion_provider.rs b/crates/agent_ui/src/completion_provider.rs index 58b4bb9bbe9..b676bc3c3e8 100644 --- a/crates/agent_ui/src/completion_provider.rs +++ b/crates/agent_ui/src/completion_provider.rs @@ -1245,7 +1245,7 @@ impl PromptCompletionProvider { let path_prefix = if include_root_name { worktree.read(cx).root_name().into() } else { - RelPath::empty().into() + RelPath::empty_arc() }; Match::File(FileMatch { mat: fuzzy::PathMatch { @@ -2143,9 +2143,9 @@ pub(crate) fn search_files( project .worktree_for_id(project_path.worktree_id, cx) .map(|wt| wt.read(cx).root_name().into()) - .unwrap_or_else(|| RelPath::empty().into()) + .unwrap_or_else(|| RelPath::empty_arc()) } else { - RelPath::empty().into() + RelPath::empty_arc() }; FileMatch { @@ -2167,7 +2167,7 @@ pub(crate) fn search_files( let path_prefix: Arc = if include_root_name { worktree.root_name().into() } else { - RelPath::empty().into() + RelPath::empty_arc() }; worktree.entries(false, 0).map(move |entry| FileMatch { mat: PathMatch { diff --git a/crates/editor/src/completions.rs b/crates/editor/src/completions.rs index 07ebf31457a..df00100365e 100644 --- a/crates/editor/src/completions.rs +++ b/crates/editor/src/completions.rs @@ -184,6 +184,9 @@ impl Editor { .range_to_buffer_ranges(visible_range) .into_iter() .filter(|(_, excerpt_visible_range, _)| !excerpt_visible_range.is_empty()) + .map(|(buffer_snapshot, buffer_offset_range, excerpt_range)| { + (buffer_snapshot.clone(), buffer_offset_range, excerpt_range) + }) .collect() } diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 7664ff4d2d7..cfc744c3c24 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -773,6 +773,8 @@ impl BlockMap { edits = self.deferred_edits.take().compose(edits); + let max_point = wrap_snapshot.max_point(); + // Handle changing the last excerpt if it is empty. if buffer.trailing_excerpt_update_count() != self @@ -781,7 +783,6 @@ impl BlockMap { .buffer_snapshot() .trailing_excerpt_update_count() { - let max_point = wrap_snapshot.max_point(); let edit_start = wrap_snapshot.prev_row_boundary(max_point); let edit_end = max_point.row() + WrapRow(1); // this is end of file edits = edits.compose([WrapEdit { @@ -829,7 +830,7 @@ impl BlockMap { let mut my_start = wrap_snapshot.make_wrap_point(my_start, Bias::Left); let mut my_end = wrap_snapshot.make_wrap_point(my_end, Bias::Left); // TODO(split-diff) should use trailing_excerpt_update_count for the second case - if my_end.column() > 0 || my_end == wrap_snapshot.max_point() { + if my_end.column() > 0 || my_end == max_point { *my_end.row_mut() += 1; *my_end.column_mut() = 0; } @@ -837,7 +838,7 @@ impl BlockMap { // Empty edits won't survive Patch::compose, but we still need to make sure // we recompute spacers when we get them. if my_start.row() == my_end.row() { - if my_end.row() <= wrap_snapshot.max_point().row() { + if my_end.row() <= max_point.row() { *my_end.row_mut() += 1; *my_end.column_mut() = 0; } else if my_start.row() > WrapRow(0) { @@ -1006,7 +1007,7 @@ impl BlockMap { }; let end_bound; - let end_block_ix = if new_end > wrap_snapshot.max_point().row() { + let end_block_ix = if new_end > max_point.row() { end_bound = Bound::Unbounded; self.custom_blocks.len() } else { diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 24f0206cccd..2cc9296f034 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -72,6 +72,9 @@ impl TabMap { old_snapshot.tab_size = tab_size; return (old_snapshot.clone(), vec![]); } + + let old_fold_max_point = old_snapshot.fold_snapshot.max_point(); + // Expand each edit to include the next tab on the same line as the edit, // and any subsequent tabs on that line that moved across the tab expansion // boundary. @@ -89,11 +92,9 @@ impl TabMap { // expansion boundary (transitioning between expanded and non-expanded). for fold_edit in &mut fold_edits { let old_end = fold_edit.old.end.to_point(&old_snapshot.fold_snapshot); - let old_end_row_successor_offset = cmp::min( - FoldPoint::new(old_end.row() + 1, 0), - old_snapshot.fold_snapshot.max_point(), - ) - .to_offset(&old_snapshot.fold_snapshot); + let old_end_row_successor_offset = + cmp::min(FoldPoint::new(old_end.row() + 1, 0), old_fold_max_point) + .to_offset(&old_snapshot.fold_snapshot); let new_end = fold_edit.new.end.to_point(&fold_snapshot); let mut offset_from_edit = 0; @@ -211,7 +212,7 @@ impl std::ops::Deref for TabSnapshot { } impl TabSnapshot { - #[ztracing::instrument(skip_all)] + #[inline] pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { &self.fold_snapshot.inlay_snapshot.buffer } diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 44a4689b285..1d495965afd 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -8,7 +8,7 @@ use super::{ use futures_lite::future::yield_now; use gpui::{App, AppContext as _, Context, Entity, Font, LineWrapper, Pixels, Task}; use language::{LanguageAwareStyling, Point}; -use multi_buffer::{MultiBufferSnapshot, RowInfo}; +use multi_buffer::RowInfo; use std::{cmp, collections::VecDeque, mem, ops::Range, sync::LazyLock, time::Duration}; use sum_tree::{Bias, Cursor, Dimensions, SumTree}; use text::Patch; @@ -381,11 +381,6 @@ impl WrapSnapshot { } } - #[ztracing::instrument(skip_all)] - pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot { - self.tab_snapshot.buffer_snapshot() - } - #[ztracing::instrument(skip_all)] fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> WrapPatch { let mut new_transforms; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 70568a66c46..862b7aaf6e8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -534,13 +534,15 @@ pub struct EditorStyle { impl Default for EditorStyle { fn default() -> Self { + static NONE_SYNTAX: std::sync::LazyLock> = + std::sync::LazyLock::new(|| Arc::new(SyntaxTheme::default())); Self { background: Hsla::default(), border: Hsla::default(), local_player: PlayerColor::default(), text: TextStyle::default(), scrollbar_width: Pixels::default(), - syntax: Default::default(), + syntax: NONE_SYNTAX.clone(), // HACK: Status colors don't have a real default. // We should look into removing the status colors from the editor // style and retrieve them directly from the theme. diff --git a/crates/editor/src/input.rs b/crates/editor/src/input.rs index 04e546a6691..bf213ee20a5 100644 --- a/crates/editor/src/input.rs +++ b/crates/editor/src/input.rs @@ -2376,7 +2376,7 @@ impl NewlineConfig { .range_to_buffer_ranges(range.start..range.end) .as_slice() { - [(buffer_snapshot, range, _)] => (buffer_snapshot.clone(), range.clone()), + [(buffer_snapshot, range, _)] => (*buffer_snapshot, range.clone()), _ => return false, }; let pair = { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index a5fc5b2b991..137744d9b1b 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -2255,7 +2255,7 @@ mod tests { #[gpui::test] fn test_path_for_file(cx: &mut App) { let file: Arc = Arc::new(TestFile { - path: RelPath::empty().into(), + path: RelPath::empty_arc(), root_name: String::new(), local_root: None, }); diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 2c3bd5f8dba..5d3943dc9b5 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -187,15 +187,15 @@ fn patches_for_range( where F: Fn(&BufferDiffSnapshot, RangeInclusive, &text::BufferSnapshot) -> Patch, { - struct PendingExcerpt { - source_buffer_snapshot: language::BufferSnapshot, + struct PendingExcerpt<'a> { + source_buffer_snapshot: &'a language::BufferSnapshot, source_excerpt_range: ExcerptRange, buffer_point_range: Range, } let mut result = Vec::new(); let mut current_buffer_id: Option = None; - let mut pending_excerpts: Vec = Vec::new(); + let mut pending_excerpts: Vec> = Vec::new(); let mut union_context_start: Option = None; let mut union_context_end: Option = None; diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index a4feaf29523..717ee23caa1 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -764,7 +764,7 @@ fn matching_history_items<'a>( candidates_paths.remove_entry(&project_path).or_else(|| { candidates_paths.remove_entry(&ProjectPath { worktree_id, - path: RelPath::empty().into_arc(), + path: RelPath::empty_arc(), }) })?; // Key with path_match.path so the deduplication check in push_new_matches @@ -824,7 +824,7 @@ fn project_path_for_search_match( .worktree_for_id(worktree_id, cx) .is_some_and(|worktree| worktree.read(cx).is_single_file()) { - RelPath::empty().into_arc() + RelPath::empty_arc() } else { path_match.path.clone() }; @@ -1457,7 +1457,7 @@ impl FileFinderDelegate { positions: Vec::new(), worktree_id: worktree.read(cx).id().to_usize(), path: relative_path, - path_prefix: RelPath::empty().into(), + path_prefix: RelPath::empty_arc(), is_dir: false, // File finder doesn't support directories distance_to_relative_ancestor: usize::MAX, })); diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index 726251e9dee..5dddaf3760f 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -2641,7 +2641,7 @@ async fn test_single_file_search_result_split_open(cx: &mut gpui::TestAppContext active_editor.read(cx).active_project_path(cx), Some(ProjectPath { worktree_id, - path: RelPath::empty().into_arc(), + path: RelPath::empty_arc(), }), "Should split-open the single-file worktree root with an empty relative path" ); diff --git a/crates/fuzzy/src/matcher.rs b/crates/fuzzy/src/matcher.rs index 102708d2fad..f22c9d3db39 100644 --- a/crates/fuzzy/src/matcher.rs +++ b/crates/fuzzy/src/matcher.rs @@ -605,7 +605,7 @@ mod tests { worktree_id: 0, positions: positions.clone(), path: candidate.path.into(), - path_prefix: RelPath::empty().into(), + path_prefix: RelPath::empty_arc(), distance_to_relative_ancestor: usize::MAX, is_dir: false, }, diff --git a/crates/fuzzy/src/paths.rs b/crates/fuzzy/src/paths.rs index 2f92f05b96a..6420799403b 100644 --- a/crates/fuzzy/src/paths.rs +++ b/crates/fuzzy/src/paths.rs @@ -116,11 +116,7 @@ pub fn match_fixed_path_set( (worktree_root_name, path_prefix_chars, lowercase_pfx) } - None => ( - RelPath::empty().into(), - Default::default(), - Default::default(), - ), + None => (RelPath::empty_arc(), Default::default(), Default::default()), }; matcher.match_candidates( diff --git a/crates/fuzzy_nucleo/src/paths.rs b/crates/fuzzy_nucleo/src/paths.rs index 6aaabfeb50e..037acbc37f0 100644 --- a/crates/fuzzy_nucleo/src/paths.rs +++ b/crates/fuzzy_nucleo/src/paths.rs @@ -207,7 +207,7 @@ fn path_match_helper<'a>( candidate.path.into() }, path_prefix: if root_is_file { - RelPath::empty().into() + RelPath::empty_arc() } else { Arc::clone(path_prefix) }, @@ -239,7 +239,7 @@ pub fn match_fixed_path_set( let root_is_file = worktree_root_name.is_some() && candidates.iter().all(|c| c.path.is_empty()); - let path_prefix = worktree_root_name.unwrap_or_else(|| RelPath::empty().into()); + let path_prefix = worktree_root_name.unwrap_or_else(|| RelPath::empty_arc()); let mut results = Vec::new(); diff --git a/crates/inspector_ui/src/div_inspector.rs b/crates/inspector_ui/src/div_inspector.rs index aa930418bf9..f0415d204c9 100644 --- a/crates/inspector_ui/src/div_inspector.rs +++ b/crates/inspector_ui/src/div_inspector.rs @@ -466,7 +466,7 @@ impl DivInspector { let project_path = worktree.read_with(cx, |worktree, _cx| ProjectPath { worktree_id: worktree.id(), - path: RelPath::empty().into(), + path: RelPath::empty_arc(), }); let buffer = project diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 97483cf3dae..2734c205ac8 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -889,7 +889,7 @@ impl ContextProvider for PythonContextProvider { .as_ref() .and_then(|f| f.path().parent()) .map(Arc::from) - .unwrap_or_else(|| RelPath::empty().into()); + .unwrap_or_else(|| RelPath::empty_arc()); toolchains .active_toolchain(worktree_id, file_path, "Python".into(), cx) diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index dd0c24fb5f8..4b776b2d811 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -3610,7 +3610,7 @@ impl MultiBufferSnapshot { &self, range: Range, ) -> Vec<( - BufferSnapshot, + &BufferSnapshot, Range, ExcerptRange, )> { @@ -3621,7 +3621,7 @@ impl MultiBufferSnapshot { cursor.seek(&start); let mut result: Vec<( - BufferSnapshot, + &BufferSnapshot, Range, ExcerptRange, )> = Vec::new(); @@ -3653,7 +3653,7 @@ impl MultiBufferSnapshot { { prev.1.end = end; } else { - result.push((region.buffer.clone(), start..end, excerpt_range)); + result.push((region.buffer, start..end, excerpt_range)); } } cursor.next(); @@ -3676,11 +3676,7 @@ impl MultiBufferSnapshot { || prev_excerpt.context.start != excerpt_range.context.start }) { - result.push(( - buffer_snapshot.clone(), - buffer_offset..buffer_offset, - excerpt_range, - )); + result.push((buffer_snapshot, buffer_offset..buffer_offset, excerpt_range)); } } diff --git a/crates/multi_buffer/src/path_key.rs b/crates/multi_buffer/src/path_key.rs index 53117a05013..18423a69608 100644 --- a/crates/multi_buffer/src/path_key.rs +++ b/crates/multi_buffer/src/path_key.rs @@ -26,14 +26,14 @@ impl PathKey { pub fn min() -> Self { Self { sort_prefix: None, - path: RelPath::empty().into_arc(), + path: RelPath::empty_arc(), } } pub fn sorted(sort_prefix: u64) -> Self { Self { sort_prefix: Some(sort_prefix), - path: RelPath::empty().into_arc(), + path: RelPath::empty_arc(), } } pub fn with_sort_prefix(sort_prefix: u64, path: Arc) -> Self { diff --git a/crates/project/src/manifest_tree.rs b/crates/project/src/manifest_tree.rs index 70d9cf7c00e..8c28601b544 100644 --- a/crates/project/src/manifest_tree.rs +++ b/crates/project/src/manifest_tree.rs @@ -186,7 +186,7 @@ impl ManifestTree { .and_then(|manifest_name| self.root_for_path(project_path, manifest_name, delegate, cx)) .unwrap_or_else(|| ProjectPath { worktree_id, - path: RelPath::empty().into(), + path: RelPath::empty_arc(), }) } diff --git a/crates/project/src/manifest_tree/server_tree.rs b/crates/project/src/manifest_tree/server_tree.rs index 4756f541d01..44e6d624a1e 100644 --- a/crates/project/src/manifest_tree/server_tree.rs +++ b/crates/project/src/manifest_tree/server_tree.rs @@ -338,7 +338,7 @@ impl LanguageServerTree { .entry(worktree_id) .or_default() .roots - .entry(RelPath::empty().into()) + .entry(RelPath::empty_arc()) .or_default() .entry(node.disposition.server_name.clone()) .or_insert_with(|| (node, BTreeSet::new())) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 061f8265ab3..9ae67a85633 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -453,7 +453,7 @@ impl ProjectPath { pub fn root_path(worktree_id: WorktreeId) -> Self { Self { worktree_id, - path: RelPath::empty().into(), + path: RelPath::empty_arc(), } } @@ -6385,7 +6385,7 @@ impl<'a> fuzzy::PathMatchCandidateSet<'a> for PathMatchCandidateSet { if self.snapshot.root_entry().is_some_and(|e| e.is_file()) || self.include_root_name { self.snapshot.root_name().into() } else { - RelPath::empty().into() + RelPath::empty_arc() } } @@ -6460,7 +6460,7 @@ impl<'a> fuzzy_nucleo::PathMatchCandidateSet<'a> for PathMatchCandidateSet { if self.snapshot.root_entry().is_some_and(|e| e.is_file()) || self.include_root_name { self.snapshot.root_name().into() } else { - RelPath::empty().into() + RelPath::empty_arc() } } fn root_is_file(&self) -> bool { diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 6af516805c7..af48841b26c 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -1202,7 +1202,7 @@ mod tests { let other_worktree = WorktreeId::from_usize(2); let kind = TaskSourceKind::Worktree { id: task_worktree, - directory_in_worktree: RelPath::empty().into_arc(), + directory_in_worktree: RelPath::empty_arc(), id_base: Cow::Borrowed("worktree"), }; diff --git a/crates/project/src/toolchain_store.rs b/crates/project/src/toolchain_store.rs index c72b99c6a11..1539014fb31 100644 --- a/crates/project/src/toolchain_store.rs +++ b/crates/project/src/toolchain_store.rs @@ -265,7 +265,7 @@ impl ToolchainStore { let path = if let Some(path) = envelope.payload.path { RelPath::from_proto(&path)? } else { - RelPath::empty().into() + RelPath::empty_arc() }; Ok(this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx)) })? diff --git a/crates/project/src/worktree_store.rs b/crates/project/src/worktree_store.rs index f544973a548..f66be5c4249 100644 --- a/crates/project/src/worktree_store.rs +++ b/crates/project/src/worktree_store.rs @@ -433,7 +433,7 @@ impl WorktreeStore { Task::ready(Ok((tree, relative_path))) } else { let worktree = self.create_worktree(abs_path, visible, cx); - cx.background_spawn(async move { Ok((worktree.await?, RelPath::empty().into())) }) + cx.background_spawn(async move { Ok((worktree.await?, RelPath::empty_arc())) }) } } diff --git a/crates/repl/src/kernels/mod.rs b/crates/repl/src/kernels/mod.rs index 737893b09e4..f7b59d2ef82 100644 --- a/crates/repl/src/kernels/mod.rs +++ b/crates/repl/src/kernels/mod.rs @@ -426,7 +426,7 @@ pub fn python_env_kernel_specifications( let toolchains = project.read(cx).available_toolchains( ProjectPath { worktree_id, - path: RelPath::empty().into(), + path: RelPath::empty_arc(), }, python_language, cx, diff --git a/crates/repl/src/repl_store.rs b/crates/repl/src/repl_store.rs index b2bf90e99dc..8f5ad104035 100644 --- a/crates/repl/src/repl_store.rs +++ b/crates/repl/src/repl_store.rs @@ -178,7 +178,7 @@ impl ReplStore { let active_toolchain = project.read(cx).active_toolchain( ProjectPath { worktree_id, - path: RelPath::empty().into(), + path: RelPath::empty_arc(), }, LanguageName::new_static("Python"), cx, diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 725e5552873..4fcbfcde47e 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -1169,10 +1169,10 @@ impl SettingsStore { ) -> impl '_ + Iterator, &ProjectSettingsContent)> { self.local_settings .range( - (root_id, RelPath::empty().into()) + (root_id, RelPath::empty_arc()) ..( WorktreeId::from_usize(root_id.to_usize() + 1), - RelPath::empty().into(), + RelPath::empty_arc(), ), ) .map(|((_, path), content)| (path.clone(), &content.project)) @@ -2566,7 +2566,7 @@ mod tests { store .set_user_settings(r#"{"preferred_line_length": 0}"#, cx) .unwrap(); - let local = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + let local = (WorktreeId::from_usize(0), RelPath::empty_arc()); store .set_local_settings( local.0, @@ -2626,7 +2626,7 @@ mod tests { store.register_setting::(); store.register_setting::(); - let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + let local_1 = (WorktreeId::from_usize(0), RelPath::empty_arc()); let local_1_child = ( WorktreeId::from_usize(0), @@ -2638,7 +2638,7 @@ mod tests { .into_arc(), ); - let local_2 = (WorktreeId::from_usize(1), RelPath::empty().into_arc()); + let local_2 = (WorktreeId::from_usize(1), RelPath::empty_arc()); let local_2_child = ( WorktreeId::from_usize(1), RelPath::new( @@ -2759,11 +2759,11 @@ mod tests { let mut store = SettingsStore::new(cx, &test_settings()); store.register_setting::(); - let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc()); + let wt0_root = (WorktreeId::from_usize(0), RelPath::empty_arc()); let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc()); let wt0_child2 = (WorktreeId::from_usize(0), rel_path("child2").into_arc()); - let wt1_root = (WorktreeId::from_usize(1), RelPath::empty().into_arc()); + let wt1_root = (WorktreeId::from_usize(1), RelPath::empty_arc()); let wt1_subdir = (WorktreeId::from_usize(1), rel_path("subdir").into_arc()); fn get(content: &SettingsContent) -> &Option { @@ -2881,15 +2881,13 @@ mod tests { #[test] fn test_file_ord() { - let wt0_root = - SettingsFile::Project((WorktreeId::from_usize(0), RelPath::empty().into_arc())); + let wt0_root = SettingsFile::Project((WorktreeId::from_usize(0), RelPath::empty_arc())); let wt0_child1 = SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child1").into_arc())); let wt0_child2 = SettingsFile::Project((WorktreeId::from_usize(0), rel_path("child2").into_arc())); - let wt1_root = - SettingsFile::Project((WorktreeId::from_usize(1), RelPath::empty().into_arc())); + let wt1_root = SettingsFile::Project((WorktreeId::from_usize(1), RelPath::empty_arc())); let wt1_subdir = SettingsFile::Project((WorktreeId::from_usize(1), rel_path("subdir").into_arc())); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 61646d72d77..9c4321ca691 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -2661,7 +2661,7 @@ mod tests { let entry = cx .update(|cx| { wt.update(cx, |wt, cx| { - wt.create_entry(RelPath::empty().into(), is_dir, None, cx) + wt.create_entry(RelPath::empty_arc(), is_dir, None, cx) }) }) .await diff --git a/crates/util/src/rel_path.rs b/crates/util/src/rel_path.rs index 7b17094e080..382a4bad37b 100644 --- a/crates/util/src/rel_path.rs +++ b/crates/util/src/rel_path.rs @@ -36,6 +36,12 @@ impl RelPath { Self::new_unchecked("") } + /// Creates an empty [`RelPath`]. + pub fn empty_arc() -> Arc { + static EMPTY: std::sync::OnceLock> = std::sync::OnceLock::new(); + EMPTY.get_or_init(|| Arc::from(Self::empty())).clone() + } + /// Converts a path with a given style into a [`RelPath`]. /// /// Returns an error if the path is absolute, or is not valid unicode. diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 3539a9fc882..af365d828fa 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -215,7 +215,7 @@ impl WorkDirectory { fn path_key(&self) -> PathKey { match self { WorkDirectory::InProject { relative_path } => PathKey(relative_path.clone()), - WorkDirectory::AboveProject { .. } => PathKey(RelPath::empty().into()), + WorkDirectory::AboveProject { .. } => PathKey(RelPath::empty_arc()), } } @@ -440,9 +440,7 @@ impl Worktree { abs_path .file_name() .and_then(|f| f.to_str()) - .map_or(RelPath::empty().into(), |f| { - RelPath::unix(f).unwrap().into() - }), + .map_or(RelPath::empty_arc(), |f| RelPath::unix(f).unwrap().into()), abs_path.clone(), PathStyle::local(), ), @@ -471,7 +469,7 @@ impl Worktree { let share_private_files = false; if let Some(metadata) = metadata { let mut entry = Entry::new( - RelPath::empty().into(), + RelPath::empty_arc(), &metadata, ProjectEntryId::new(&next_entry_id), snapshot.root_char_bag, @@ -529,8 +527,7 @@ impl Worktree { cx.new(|cx: &mut Context| { let mut snapshot = Snapshot::new( WorktreeId::from_proto(worktree.id), - RelPath::from_proto(&worktree.root_name) - .unwrap_or_else(|_| RelPath::empty().into()), + RelPath::from_proto(&worktree.root_name).unwrap_or_else(|_| RelPath::empty_arc()), Path::new(&worktree.abs_path).into(), path_style, ); @@ -1552,7 +1549,7 @@ impl LocalWorktree { } } - lowest_ancestor.unwrap_or_else(|| RelPath::empty().into()) + lowest_ancestor.unwrap_or_else(|| RelPath::empty_arc()) } pub fn create_entry( @@ -2058,9 +2055,7 @@ impl LocalWorktree { .as_path() .file_name() .and_then(|f| f.to_str()) - .map_or(RelPath::empty().into(), |f| { - RelPath::unix(f).unwrap().into() - }); + .map_or(RelPath::empty_arc(), |f| RelPath::unix(f).unwrap().into()); self.snapshot.update_abs_path(new_path, root_name); self.restart_background_scanners(cx); } @@ -3698,7 +3693,7 @@ impl Summary for PathSummary { fn zero(cx: Self::Context<'_>) -> Self { Self { - max_path: RelPath::empty().into(), + max_path: RelPath::empty_arc(), item_summary: S::zero(cx), } } @@ -3955,7 +3950,7 @@ pub struct PathKey(pub Arc); impl Default for PathKey { fn default() -> Self { - Self(RelPath::empty().into()) + Self(RelPath::empty_arc()) } }