| @@ -1,6 +1,6 @@ |
| 1 | mod project_panel_settings; | 1 | mod project_panel_settings; |
| 2 | use client::{ErrorCode, ErrorExt}; | 2 | use client::{ErrorCode, ErrorExt}; |
| 3 | use settings::Settings; | 3 | use settings::{Settings, SettingsStore}; |
| 4 | | 4 | |
| 5 | use db::kvp::KEY_VALUE_STORE; | 5 | use db::kvp::KEY_VALUE_STORE; |
| 6 | use editor::{actions::Cancel, items::entry_git_aware_label_color, scroll::Autoscroll, Editor}; | 6 | use editor::{actions::Cancel, items::entry_git_aware_label_color, scroll::Autoscroll, Editor}; |
| @@ -24,6 +24,7 @@ use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings}; · 17 unmodified lines |
| 24 | use serde::{Deserialize, Serialize}; | 24 | use serde::{Deserialize, Serialize}; |
| 25 | use std::{ | 25 | use std::{ |
| 26 | cmp::Ordering, | 26 | cmp::Ordering, |
| | 27 | collections::HashSet, |
| 27 | ffi::OsStr, | 28 | ffi::OsStr, |
| 28 | ops::Range, | 29 | ops::Range, |
| 29 | path::{Path, PathBuf}, | 30 | path::{Path, PathBuf}, |
| @@ -50,6 +51,7 @@ pub struct ProjectPanel { · 20 unmodified lines |
| 50 | visible_entries: Vec<(WorktreeId, Vec<Entry>)>, | 51 | visible_entries: Vec<(WorktreeId, Vec<Entry>)>, |
| 51 | last_worktree_root_id: Option<ProjectEntryId>, | 52 | last_worktree_root_id: Option<ProjectEntryId>, |
| 52 | expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>, | 53 | expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>, |
| | 54 | unfolded_dir_ids: HashSet<ProjectEntryId>, |
| 53 | selection: Option<Selection>, | 55 | selection: Option<Selection>, |
| 54 | context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>, | 56 | context_menu: Option<(View<ContextMenu>, Point<Pixels>, Subscription)>, |
| 55 | edit_state: Option<EditState>, | 57 | edit_state: Option<EditState>, |
| @@ -133,6 +135,8 @@ actions!( · 77 unmodified lines |
| 133 | OpenPermanent, | 135 | OpenPermanent, |
| 134 | ToggleFocus, | 136 | ToggleFocus, |
| 135 | NewSearchInDirectory, | 137 | NewSearchInDirectory, |
| | 138 | UnfoldDirectory, |
| | 139 | FoldDirectory, |
| 136 | ] | 140 | ] |
| 137 | ); | 141 | ); |
| 138 | | 142 | |
| @@ -235,6 +239,16 @@ impl ProjectPanel { · 96 unmodified lines |
| 235 | }) | 239 | }) |
| 236 | .detach(); | 240 | .detach(); |
| 237 | | 241 | |
| | 242 | let mut project_panel_settings = *ProjectPanelSettings::get_global(cx); |
| | 243 | cx.observe_global::<SettingsStore>(move |_, cx| { |
| | 244 | let new_settings = *ProjectPanelSettings::get_global(cx); |
| | 245 | if project_panel_settings != new_settings { |
| | 246 | project_panel_settings = new_settings; |
| | 247 | cx.notify(); |
| | 248 | } |
| | 249 | }) |
| | 250 | .detach(); |
| | 251 | |
| 238 | let mut this = Self { | 252 | let mut this = Self { |
| 239 | project: project.clone(), | 253 | project: project.clone(), |
| 240 | fs: workspace.app_state().fs.clone(), | 254 | fs: workspace.app_state().fs.clone(), |
| @@ -243,6 +257,7 @@ impl ProjectPanel { · 2 unmodified lines |
| 243 | visible_entries: Default::default(), | 257 | visible_entries: Default::default(), |
| 244 | last_worktree_root_id: Default::default(), | 258 | last_worktree_root_id: Default::default(), |
| 245 | expanded_dir_ids: Default::default(), | 259 | expanded_dir_ids: Default::default(), |
| | 260 | unfolded_dir_ids: Default::default(), |
| 246 | selection: None, | 261 | selection: None, |
| 247 | edit_state: None, | 262 | edit_state: None, |
| 248 | context_menu: None, | 263 | context_menu: None, |
| @@ -403,8 +418,11 @@ impl ProjectPanel { · 154 unmodified lines |
| 403 | }); | 418 | }); |
| 404 | | 419 | |
| 405 | if let Some((worktree, entry)) = self.selected_entry(cx) { | 420 | if let Some((worktree, entry)) = self.selected_entry(cx) { |
| | 421 | let auto_fold_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs; |
| 406 | let is_root = Some(entry) == worktree.root_entry(); | 422 | let is_root = Some(entry) == worktree.root_entry(); |
| 407 | let is_dir = entry.is_dir(); | 423 | let is_dir = entry.is_dir(); |
| | 424 | let is_foldable = auto_fold_dirs && self.is_foldable(entry, worktree); |
| | 425 | let is_unfoldable = auto_fold_dirs && self.is_unfoldable(entry, worktree); |
| 408 | let worktree_id = worktree.id(); | 426 | let worktree_id = worktree.id(); |
| 409 | let is_local = project.is_local(); | 427 | let is_local = project.is_local(); |
| 410 | let is_read_only = project.is_read_only(); | 428 | let is_read_only = project.is_read_only(); |
| @@ -430,6 +448,12 @@ impl ProjectPanel { · 19 unmodified lines |
| 430 | menu.separator() | 448 | menu.separator() |
| 431 | .action("Find in Folder…", Box::new(NewSearchInDirectory)) | 449 | .action("Find in Folder…", Box::new(NewSearchInDirectory)) |
| 432 | }) | 450 | }) |
| | 451 | .when(is_unfoldable, |menu| { |
| | 452 | menu.action("Unfold Directory", Box::new(UnfoldDirectory)) |
| | 453 | }) |
| | 454 | .when(is_foldable, |menu| { |
| | 455 | menu.action("Fold Directory", Box::new(FoldDirectory)) |
| | 456 | }) |
| 433 | .separator() | 457 | .separator() |
| 434 | .action("Cut", Box::new(Cut)) | 458 | .action("Cut", Box::new(Cut)) |
| 435 | .action("Copy", Box::new(Copy)) | 459 | .action("Copy", Box::new(Copy)) |
| @@ -482,6 +506,37 @@ impl ProjectPanel { · 46 unmodified lines |
| 482 | cx.notify(); | 506 | cx.notify(); |
| 483 | } | 507 | } |
| 484 | | 508 | |
| | 509 | fn is_unfoldable(&self, entry: &Entry, worktree: &Worktree) -> bool { |
| | 510 | if !entry.is_dir() || self.unfolded_dir_ids.contains(&entry.id) { |
| | 511 | return false; |
| | 512 | } |
| | 513 | |
| | 514 | if let Some(parent_path) = entry.path.parent() { |
| | 515 | let snapshot = worktree.snapshot(); |
| | 516 | let mut child_entries = snapshot.child_entries(&parent_path); |
| | 517 | if let Some(child) = child_entries.next() { |
| | 518 | if child_entries.next().is_none() { |
| | 519 | return child.kind.is_dir(); |
| | 520 | } |
| | 521 | } |
| | 522 | }; |
| | 523 | false |
| | 524 | } |
| | 525 | |
| | 526 | fn is_foldable(&self, entry: &Entry, worktree: &Worktree) -> bool { |
| | 527 | if entry.is_dir() { |
| | 528 | let snapshot = worktree.snapshot(); |
| | 529 | |
| | 530 | let mut child_entries = snapshot.child_entries(&entry.path); |
| | 531 | if let Some(child) = child_entries.next() { |
| | 532 | if child_entries.next().is_none() { |
| | 533 | return child.kind.is_dir(); |
| | 534 | } |
| | 535 | } |
| | 536 | } |
| | 537 | false |
| | 538 | } |
| | 539 | |
| 485 | fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) { | 540 | fn expand_selected_entry(&mut self, _: &ExpandSelectedEntry, cx: &mut ViewContext<Self>) { |
| 486 | if let Some((worktree, entry)) = self.selected_entry(cx) { | 541 | if let Some((worktree, entry)) = self.selected_entry(cx) { |
| 487 | if entry.is_dir() { | 542 | if entry.is_dir() { |
| @@ -859,6 +914,59 @@ impl ProjectPanel { · 371 unmodified lines |
| 859 | }); | 914 | }); |
| 860 | } | 915 | } |
| 861 | | 916 | |
| | 917 | fn unfold_directory(&mut self, _: &UnfoldDirectory, cx: &mut ViewContext<Self>) { |
| | 918 | if let Some((worktree, entry)) = self.selected_entry(cx) { |
| | 919 | self.unfolded_dir_ids.insert(entry.id); |
| | 920 | |
| | 921 | let snapshot = worktree.snapshot(); |
| | 922 | let mut parent_path = entry.path.parent(); |
| | 923 | while let Some(path) = parent_path { |
| | 924 | if let Some(parent_entry) = worktree.entry_for_path(path) { |
| | 925 | let mut children_iter = snapshot.child_entries(path); |
| | 926 | |
| | 927 | if children_iter.by_ref().take(2).count() > 1 { |
| | 928 | break; |
| | 929 | } |
| | 930 | |
| | 931 | self.unfolded_dir_ids.insert(parent_entry.id); |
| | 932 | parent_path = path.parent(); |
| | 933 | } else { |
| | 934 | break; |
| | 935 | } |
| | 936 | } |
| | 937 | |
| | 938 | self.update_visible_entries(None, cx); |
| | 939 | self.autoscroll(cx); |
| | 940 | cx.notify(); |
| | 941 | } |
| | 942 | } |
| | 943 | |
| | 944 | fn fold_directory(&mut self, _: &FoldDirectory, cx: &mut ViewContext<Self>) { |
| | 945 | if let Some((worktree, entry)) = self.selected_entry(cx) { |
| | 946 | self.unfolded_dir_ids.remove(&entry.id); |
| | 947 | |
| | 948 | let snapshot = worktree.snapshot(); |
| | 949 | let mut path = &*entry.path; |
| | 950 | loop { |
| | 951 | let mut child_entries_iter = snapshot.child_entries(path); |
| | 952 | if let Some(child) = child_entries_iter.next() { |
| | 953 | if child_entries_iter.next().is_none() && child.is_dir() { |
| | 954 | self.unfolded_dir_ids.remove(&child.id); |
| | 955 | path = &*child.path; |
| | 956 | } else { |
| | 957 | break; |
| | 958 | } |
| | 959 | } else { |
| | 960 | break; |
| | 961 | } |
| | 962 | } |
| | 963 | |
| | 964 | self.update_visible_entries(None, cx); |
| | 965 | self.autoscroll(cx); |
| | 966 | cx.notify(); |
| | 967 | } |
| | 968 | } |
| | 969 | |
| 862 | fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) { | 970 | fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) { |
| 863 | if let Some(selection) = self.selection { | 971 | if let Some(selection) = self.selection { |
| 864 | let (mut worktree_ix, mut entry_ix, _) = | 972 | let (mut worktree_ix, mut entry_ix, _) = |
| @@ -1153,6 +1261,7 @@ impl ProjectPanel { · 288 unmodified lines |
| 1153 | new_selected_entry: Option<(WorktreeId, ProjectEntryId)>, | 1261 | new_selected_entry: Option<(WorktreeId, ProjectEntryId)>, |
| 1154 | cx: &mut ViewContext<Self>, | 1262 | cx: &mut ViewContext<Self>, |
| 1155 | ) { | 1263 | ) { |
| | 1264 | let auto_collapse_dirs = ProjectPanelSettings::get_global(cx).auto_fold_dirs; |
| 1156 | let project = self.project.read(cx); | 1265 | let project = self.project.read(cx); |
| 1157 | self.last_worktree_root_id = project | 1266 | self.last_worktree_root_id = project |
| 1158 | .visible_worktrees(cx) | 1267 | .visible_worktrees(cx) |
| @@ -1194,8 +1303,25 @@ impl ProjectPanel { · 35 unmodified lines |
| 1194 | | 1303 | |
| 1195 | let mut visible_worktree_entries = Vec::new(); | 1304 | let mut visible_worktree_entries = Vec::new(); |
| 1196 | let mut entry_iter = snapshot.entries(true); | 1305 | let mut entry_iter = snapshot.entries(true); |
| 1197 | | | |
| 1198 | while let Some(entry) = entry_iter.entry() { | 1306 | while let Some(entry) = entry_iter.entry() { |
| | 1307 | if auto_collapse_dirs |
| | 1308 | && entry.kind.is_dir() |
| | 1309 | && !self.unfolded_dir_ids.contains(&entry.id) |
| | 1310 | { |
| | 1311 | if let Some(root_path) = snapshot.root_entry() { |
| | 1312 | let mut child_entries = snapshot.child_entries(&entry.path); |
| | 1313 | if let Some(child) = child_entries.next() { |
| | 1314 | if entry.path != root_path.path |
| | 1315 | && child_entries.next().is_none() |
| | 1316 | && child.kind.is_dir() |
| | 1317 | { |
| | 1318 | entry_iter.advance(); |
| | 1319 | continue; |
| | 1320 | } |
| | 1321 | } |
| | 1322 | } |
| | 1323 | } |
| | 1324 | |
| 1199 | visible_worktree_entries.push(entry.clone()); | 1325 | visible_worktree_entries.push(entry.clone()); |
| 1200 | if Some(entry.id) == new_entry_parent_id { | 1326 | if Some(entry.id) == new_entry_parent_id { |
| 1201 | visible_worktree_entries.push(Entry { | 1327 | visible_worktree_entries.push(Entry { |
| @@ -1367,16 +1493,32 @@ impl ProjectPanel { · 165 unmodified lines |
| 1367 | } | 1493 | } |
| 1368 | }; | 1494 | }; |
| 1369 | | 1495 | |
| 1370 | let mut details = EntryDetails { | 1496 | let (depth, difference) = ProjectPanel::calculate_depth_and_difference( |
| 1371 | filename: entry | 1497 | entry, |
| | 1498 | visible_worktree_entries, |
| | 1499 | ); |
| | 1500 | |
| | 1501 | let filename = match difference { |
| | 1502 | diff if diff > 1 => entry |
| 1372 | .path | 1503 | .path |
| 1373 | .file_name() | 1504 | .iter() |
| 1374 | .unwrap_or(root_name) | 1505 | .skip(entry.path.components().count() - diff) |
| 1375 | .to_string_lossy() | 1506 | .collect::<PathBuf>() |
| | 1507 | .to_str() |
| | 1508 | .unwrap_or_default() |
| 1376 | .to_string(), | 1509 | .to_string(), |
| | 1510 | _ => entry |
| | 1511 | .path |
| | 1512 | .file_name() |
| | 1513 | .map(|name| name.to_string_lossy().into_owned()) |
| | 1514 | .unwrap_or_else(|| root_name.to_string_lossy().to_string()), |
| | 1515 | }; |
| | 1516 | |
| | 1517 | let mut details = EntryDetails { |
| | 1518 | filename, |
| 1377 | icon, | 1519 | icon, |
| 1378 | path: entry.path.clone(), | 1520 | path: entry.path.clone(), |
| 1379 | depth: entry.path.components().count(), | 1521 | depth, |
| 1380 | kind: entry.kind, | 1522 | kind: entry.kind, |
| 1381 | is_ignored: entry.is_ignored, | 1523 | is_ignored: entry.is_ignored, |
| 1382 | is_expanded, | 1524 | is_expanded, |
| @@ -1420,6 +1562,45 @@ impl ProjectPanel { · 37 unmodified lines |
| 1420 | } | 1562 | } |
| 1421 | } | 1563 | } |
| 1422 | | 1564 | |
| | 1565 | fn calculate_depth_and_difference( |
| | 1566 | entry: &Entry, |
| | 1567 | visible_worktree_entries: &Vec<Entry>, |
| | 1568 | ) -> (usize, usize) { |
| | 1569 | let visible_worktree_paths: HashSet<Arc<Path>> = visible_worktree_entries |
| | 1570 | .iter() |
| | 1571 | .map(|e| e.path.clone()) |
| | 1572 | .collect(); |
| | 1573 | |
| | 1574 | let (depth, difference) = entry |
| | 1575 | .path |
| | 1576 | .ancestors() |
| | 1577 | .skip(1) // Skip the entry itself |
| | 1578 | .find_map(|ancestor| { |
| | 1579 | if visible_worktree_paths.contains(ancestor) { |
| | 1580 | let parent_entry = visible_worktree_entries |
| | 1581 | .iter() |
| | 1582 | .find(|&e| &*e.path == ancestor) |
| | 1583 | .unwrap(); |
| | 1584 | |
| | 1585 | let entry_path_components_count = entry.path.components().count(); |
| | 1586 | let parent_path_components_count = parent_entry.path.components().count(); |
| | 1587 | let difference = entry_path_components_count - parent_path_components_count; |
| | 1588 | let depth = parent_entry |
| | 1589 | .path |
| | 1590 | .ancestors() |
| | 1591 | .skip(1) |
| | 1592 | .filter(|ancestor| visible_worktree_paths.contains(*ancestor)) |
| | 1593 | .count(); |
| | 1594 | Some((depth + 1, difference)) |
| | 1595 | } else { |
| | 1596 | None |
| | 1597 | } |
| | 1598 | }) |
| | 1599 | .unwrap_or((0, 0)); |
| | 1600 | |
| | 1601 | (depth, difference) |
| | 1602 | } |
| | 1603 | |
| 1423 | fn render_entry( | 1604 | fn render_entry( |
| 1424 | &self, | 1605 | &self, |
| 1425 | entry_id: ProjectEntryId, | 1606 | entry_id: ProjectEntryId, |
| @@ -1572,6 +1753,8 @@ impl Render for ProjectPanel { · 146 unmodified lines |
| 1572 | .on_action(cx.listener(Self::copy_path)) | 1753 | .on_action(cx.listener(Self::copy_path)) |
| 1573 | .on_action(cx.listener(Self::copy_relative_path)) | 1754 | .on_action(cx.listener(Self::copy_relative_path)) |
| 1574 | .on_action(cx.listener(Self::new_search_in_directory)) | 1755 | .on_action(cx.listener(Self::new_search_in_directory)) |
| | 1756 | .on_action(cx.listener(Self::unfold_directory)) |
| | 1757 | .on_action(cx.listener(Self::fold_directory)) |
| 1575 | .when(!project.is_read_only(), |el| { | 1758 | .when(!project.is_read_only(), |el| { |
| 1576 | el.on_action(cx.listener(Self::new_file)) | 1759 | el.on_action(cx.listener(Self::new_file)) |
| 1577 | .on_action(cx.listener(Self::new_directory)) | 1760 | .on_action(cx.listener(Self::new_directory)) |
| @@ -1983,6 +2166,125 @@ mod tests { · 405 unmodified lines |
| 1983 | ); | 2166 | ); |
| 1984 | } | 2167 | } |
| 1985 | | 2168 | |
| | 2169 | #[gpui::test] |
| | 2170 | async fn test_auto_collapse_dir_paths(cx: &mut gpui::TestAppContext) { |
| | 2171 | init_test(cx); |
| | 2172 | |
| | 2173 | let fs = FakeFs::new(cx.executor().clone()); |
| | 2174 | fs.insert_tree( |
| | 2175 | "/root1", |
| | 2176 | json!({ |
| | 2177 | "dir_1": { |
| | 2178 | "nested_dir_1": { |
| | 2179 | "nested_dir_2": { |
| | 2180 | "nested_dir_3": { |
| | 2181 | "file_a.java": "// File contents", |
| | 2182 | "file_b.java": "// File contents", |
| | 2183 | "file_c.java": "// File contents", |
| | 2184 | "nested_dir_4": { |
| | 2185 | "nested_dir_5": { |
| | 2186 | "file_d.java": "// File contents", |
| | 2187 | } |
| | 2188 | } |
| | 2189 | } |
| | 2190 | } |
| | 2191 | } |
| | 2192 | } |
| | 2193 | }), |
| | 2194 | ) |
| | 2195 | .await; |
| | 2196 | fs.insert_tree( |
| | 2197 | "/root2", |
| | 2198 | json!({ |
| | 2199 | "dir_2": { |
| | 2200 | "file_1.java": "// File contents", |
| | 2201 | } |
| | 2202 | }), |
| | 2203 | ) |
| | 2204 | .await; |
| | 2205 | |
| | 2206 | let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; |
| | 2207 | let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); |
| | 2208 | let cx = &mut VisualTestContext::from_window(*workspace, cx); |
| | 2209 | cx.update(|cx| { |
| | 2210 | let settings = *ProjectPanelSettings::get_global(cx); |
| | 2211 | ProjectPanelSettings::override_global( |
| | 2212 | ProjectPanelSettings { |
| | 2213 | auto_fold_dirs: true, |
| | 2214 | ..settings |
| | 2215 | }, |
| | 2216 | cx, |
| | 2217 | ); |
| | 2218 | }); |
| | 2219 | let panel = workspace |
| | 2220 | .update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)) |
| | 2221 | .unwrap(); |
| | 2222 | assert_eq!( |
| | 2223 | visible_entries_as_strings(&panel, 0..10, cx), |
| | 2224 | &[ |
| | 2225 | "v root1", |
| | 2226 | " > dir_1/nested_dir_1/nested_dir_2/nested_dir_3", |
| | 2227 | "v root2", |
| | 2228 | " > dir_2", |
| | 2229 | ] |
| | 2230 | ); |
| | 2231 | |
| | 2232 | toggle_expand_dir( |
| | 2233 | &panel, |
| | 2234 | "root1/dir_1/nested_dir_1/nested_dir_2/nested_dir_3", |
| | 2235 | cx, |
| | 2236 | ); |
| | 2237 | assert_eq!( |
| | 2238 | visible_entries_as_strings(&panel, 0..10, cx), |
| | 2239 | &[ |
| | 2240 | "v root1", |
| | 2241 | " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3 <== selected", |
| | 2242 | " > nested_dir_4/nested_dir_5", |
| | 2243 | " file_a.java", |
| | 2244 | " file_b.java", |
| | 2245 | " file_c.java", |
| | 2246 | "v root2", |
| | 2247 | " > dir_2", |
| | 2248 | ] |
| | 2249 | ); |
| | 2250 | |
| | 2251 | toggle_expand_dir( |
| | 2252 | &panel, |
| | 2253 | "root1/dir_1/nested_dir_1/nested_dir_2/nested_dir_3/nested_dir_4/nested_dir_5", |
| | 2254 | cx, |
| | 2255 | ); |
| | 2256 | assert_eq!( |
| | 2257 | visible_entries_as_strings(&panel, 0..10, cx), |
| | 2258 | &[ |
| | 2259 | "v root1", |
| | 2260 | " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3", |
| | 2261 | " v nested_dir_4/nested_dir_5 <== selected", |
| | 2262 | " file_d.java", |
| | 2263 | " file_a.java", |
| | 2264 | " file_b.java", |
| | 2265 | " file_c.java", |
| | 2266 | "v root2", |
| | 2267 | " > dir_2", |
| | 2268 | ] |
| | 2269 | ); |
| | 2270 | toggle_expand_dir(&panel, "root2/dir_2", cx); |
| | 2271 | assert_eq!( |
| | 2272 | visible_entries_as_strings(&panel, 0..10, cx), |
| | 2273 | &[ |
| | 2274 | "v root1", |
| | 2275 | " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3", |
| | 2276 | " v nested_dir_4/nested_dir_5", |
| | 2277 | " file_d.java", |
| | 2278 | " file_a.java", |
| | 2279 | " file_b.java", |
| | 2280 | " file_c.java", |
| | 2281 | "v root2", |
| | 2282 | " v dir_2 <== selected", |
| | 2283 | " file_1.java", |
| | 2284 | ] |
| | 2285 | ); |
| | 2286 | } |
| | 2287 | |
| 1986 | #[gpui::test(iterations = 30)] | 2288 | #[gpui::test(iterations = 30)] |
| 1987 | async fn test_editing_files(cx: &mut gpui::TestAppContext) { | 2289 | async fn test_editing_files(cx: &mut gpui::TestAppContext) { |
| 1988 | init_test(cx); | 2290 | init_test(cx); |