Skip to repository content753 lines · 26.6 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T06:52:07.799Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
bookmark_store.rs
1use std::{path::Path, sync::Arc};
2
3use collections::BTreeMap;
4use gpui::{Entity, TestAppContext};
5use language::Buffer;
6use project::{Project, bookmark_store::SerializedBookmark};
7use serde_json::json;
8use util::path;
9
10mod integration {
11 use super::*;
12 use fs::Fs as _;
13
14 fn init_test(cx: &mut TestAppContext) {
15 cx.update(|cx| {
16 let settings_store = settings::SettingsStore::test(cx);
17 cx.set_global(settings_store);
18 release_channel::init(semver::Version::new(0, 0, 0), cx);
19 });
20 }
21
22 fn project_path(path: &str) -> Arc<Path> {
23 Arc::from(Path::new(path))
24 }
25
26 fn serialized_bookmark(row: u32) -> SerializedBookmark {
27 SerializedBookmark {
28 row,
29 label: String::new(),
30 }
31 }
32
33 async fn open_buffer(
34 project: &Entity<Project>,
35 path: &str,
36 cx: &mut TestAppContext,
37 ) -> Entity<Buffer> {
38 project
39 .update(cx, |project, cx| {
40 project.open_local_buffer(Path::new(path), cx)
41 })
42 .await
43 .unwrap()
44 }
45
46 fn add_bookmarks(
47 project: &Entity<Project>,
48 buffer: &Entity<Buffer>,
49 rows: &[u32],
50 cx: &mut TestAppContext,
51 ) {
52 let buffer = buffer.clone();
53 project.update(cx, |project, cx| {
54 let bookmark_store = project.bookmark_store();
55 let snapshot = buffer.read(cx).snapshot();
56 for &row in rows {
57 let anchor = snapshot.anchor_after(text::Point::new(row, 0));
58 bookmark_store.update(cx, |store, cx| {
59 store.toggle_bookmark(buffer.clone(), anchor, String::new(), cx);
60 });
61 }
62 });
63 }
64
65 fn add_labeled_bookmark(
66 project: &Entity<Project>,
67 buffer: &Entity<Buffer>,
68 row: u32,
69 label: &str,
70 cx: &mut TestAppContext,
71 ) {
72 let buffer = buffer.clone();
73 project.update(cx, |project, cx| {
74 let bookmark_store = project.bookmark_store();
75 let snapshot = buffer.read(cx).snapshot();
76 let anchor = snapshot.anchor_after(text::Point::new(row, 0));
77 bookmark_store.update(cx, |store, cx| {
78 store.toggle_bookmark(buffer.clone(), anchor, label.to_string(), cx);
79 });
80 });
81 }
82
83 fn get_all_bookmarks(
84 project: &Entity<Project>,
85 cx: &mut TestAppContext,
86 ) -> BTreeMap<Arc<Path>, Vec<SerializedBookmark>> {
87 project.read_with(cx, |project, cx| {
88 project
89 .bookmark_store()
90 .read(cx)
91 .all_serialized_bookmarks(cx)
92 })
93 }
94
95 fn build_serialized(
96 entries: &[(&str, &[u32])],
97 ) -> BTreeMap<Arc<Path>, Vec<SerializedBookmark>> {
98 let mut map = BTreeMap::new();
99 for &(path_str, rows) in entries {
100 let path = project_path(path_str);
101 map.insert(
102 path.clone(),
103 rows.iter().map(|&row| serialized_bookmark(row)).collect(),
104 );
105 }
106 map
107 }
108
109 async fn restore_bookmarks(
110 project: &Entity<Project>,
111 serialized: BTreeMap<Arc<Path>, Vec<SerializedBookmark>>,
112 cx: &mut TestAppContext,
113 ) {
114 project
115 .update(cx, |project, cx| {
116 project.bookmark_store().update(cx, |store, cx| {
117 store.load_serialized_bookmarks(serialized, cx)
118 })
119 })
120 .await
121 .expect("with_serialized_bookmarks should succeed");
122 }
123
124 fn clear_bookmarks(project: &Entity<Project>, cx: &mut TestAppContext) {
125 project.update(cx, |project, cx| {
126 project.bookmark_store().update(cx, |store, cx| {
127 store.clear_bookmarks(cx);
128 });
129 });
130 }
131
132 fn assert_bookmark_rows(
133 bookmarks: &BTreeMap<Arc<Path>, Vec<SerializedBookmark>>,
134 path: &str,
135 expected_rows: &[u32],
136 ) {
137 let path = project_path(path);
138 let file_bookmarks = bookmarks
139 .get(&path)
140 .unwrap_or_else(|| panic!("Expected bookmarks for {}", path.display()));
141 let rows: Vec<u32> = file_bookmarks.iter().map(|b| b.row).collect();
142 assert_eq!(rows, expected_rows, "Bookmark rows for {}", path.display());
143 }
144
145 fn assert_bookmark_labels(
146 bookmarks: &BTreeMap<Arc<Path>, Vec<SerializedBookmark>>,
147 path: &str,
148 expected: &[(u32, &str)],
149 ) {
150 let path = project_path(path);
151 let file_bookmarks = bookmarks
152 .get(&path)
153 .unwrap_or_else(|| panic!("Expected bookmarks for {}", path.display()));
154 let actual: Vec<_> = file_bookmarks
155 .iter()
156 .map(|bookmark| (bookmark.row, bookmark.label.as_str()))
157 .collect();
158 assert_eq!(actual, expected, "Bookmark labels for {}", path.display());
159 }
160
161 #[gpui::test]
162 async fn test_all_serialized_bookmarks_empty(cx: &mut TestAppContext) {
163 init_test(cx);
164 cx.executor().allow_parking();
165
166 let fs = fs::FakeFs::new(cx.executor());
167 fs.insert_tree(path!("/project"), json!({"file1.rs": "line1\nline2\n"}))
168 .await;
169
170 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
171 assert!(get_all_bookmarks(&project, cx).is_empty());
172 }
173
174 #[gpui::test]
175 async fn test_all_serialized_bookmarks_single_file(cx: &mut TestAppContext) {
176 init_test(cx);
177 cx.executor().allow_parking();
178
179 let fs = fs::FakeFs::new(cx.executor());
180 fs.insert_tree(
181 path!("/project"),
182 json!({"file1.rs": "line1\nline2\nline3\nline4\nline5\n"}),
183 )
184 .await;
185
186 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
187 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
188
189 add_bookmarks(&project, &buffer, &[0, 2], cx);
190
191 let bookmarks = get_all_bookmarks(&project, cx);
192 assert_eq!(bookmarks.len(), 1);
193 assert_bookmark_rows(&bookmarks, path!("/project/file1.rs"), &[0, 2]);
194 }
195
196 #[gpui::test]
197 async fn test_all_serialized_bookmarks_includes_labels(cx: &mut TestAppContext) {
198 init_test(cx);
199 cx.executor().allow_parking();
200
201 let fs = fs::FakeFs::new(cx.executor());
202 fs.insert_tree(
203 path!("/project"),
204 json!({"file1.rs": "line1\nline2\nline3\n"}),
205 )
206 .await;
207
208 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
209 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
210
211 add_labeled_bookmark(&project, &buffer, 0, "first", cx);
212 add_labeled_bookmark(&project, &buffer, 2, " keeps inner spaces ", cx);
213
214 let bookmarks = get_all_bookmarks(&project, cx);
215 assert_bookmark_labels(
216 &bookmarks,
217 path!("/project/file1.rs"),
218 &[(0, "first"), (2, " keeps inner spaces ")],
219 );
220 }
221
222 #[gpui::test]
223 async fn test_all_serialized_bookmarks_multiple_files(cx: &mut TestAppContext) {
224 init_test(cx);
225 cx.executor().allow_parking();
226
227 let fs = fs::FakeFs::new(cx.executor());
228 fs.insert_tree(
229 path!("/project"),
230 json!({
231 "file1.rs": "line1\nline2\nline3\n",
232 "file2.rs": "lineA\nlineB\nlineC\nlineD\n",
233 "file3.rs": "single line"
234 }),
235 )
236 .await;
237
238 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
239 let buffer1 = open_buffer(&project, path!("/project/file1.rs"), cx).await;
240 let buffer2 = open_buffer(&project, path!("/project/file2.rs"), cx).await;
241 let _buffer3 = open_buffer(&project, path!("/project/file3.rs"), cx).await;
242
243 add_bookmarks(&project, &buffer1, &[1], cx);
244 add_bookmarks(&project, &buffer2, &[0, 3], cx);
245
246 let bookmarks = get_all_bookmarks(&project, cx);
247 assert_eq!(bookmarks.len(), 2);
248 assert_bookmark_rows(&bookmarks, path!("/project/file1.rs"), &[1]);
249 assert_bookmark_rows(&bookmarks, path!("/project/file2.rs"), &[0, 3]);
250 assert!(
251 !bookmarks.contains_key(&project_path(path!("/project/file3.rs"))),
252 "file3.rs should have no bookmarks"
253 );
254 }
255
256 #[gpui::test]
257 async fn test_all_serialized_bookmarks_after_toggle_off(cx: &mut TestAppContext) {
258 init_test(cx);
259 cx.executor().allow_parking();
260
261 let fs = fs::FakeFs::new(cx.executor());
262 fs.insert_tree(
263 path!("/project"),
264 json!({"file1.rs": "line1\nline2\nline3\n"}),
265 )
266 .await;
267
268 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
269 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
270
271 add_bookmarks(&project, &buffer, &[1], cx);
272 assert_eq!(get_all_bookmarks(&project, cx).len(), 1);
273
274 // Toggle same row again to remove it
275 add_bookmarks(&project, &buffer, &[1], cx);
276 assert!(get_all_bookmarks(&project, cx).is_empty());
277 }
278
279 #[gpui::test]
280 async fn test_all_serialized_bookmarks_with_clear(cx: &mut TestAppContext) {
281 init_test(cx);
282 cx.executor().allow_parking();
283
284 let fs = fs::FakeFs::new(cx.executor());
285 fs.insert_tree(
286 path!("/project"),
287 json!({
288 "file1.rs": "line1\nline2\nline3\n",
289 "file2.rs": "lineA\nlineB\n"
290 }),
291 )
292 .await;
293
294 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
295 let buffer1 = open_buffer(&project, path!("/project/file1.rs"), cx).await;
296 let buffer2 = open_buffer(&project, path!("/project/file2.rs"), cx).await;
297
298 add_bookmarks(&project, &buffer1, &[0], cx);
299 add_bookmarks(&project, &buffer2, &[1], cx);
300 assert_eq!(get_all_bookmarks(&project, cx).len(), 2);
301
302 clear_bookmarks(&project, cx);
303 assert!(get_all_bookmarks(&project, cx).is_empty());
304 }
305
306 #[gpui::test]
307 async fn test_all_serialized_bookmarks_returns_sorted_by_path(cx: &mut TestAppContext) {
308 init_test(cx);
309 cx.executor().allow_parking();
310
311 let fs = fs::FakeFs::new(cx.executor());
312 fs.insert_tree(
313 path!("/project"),
314 json!({"b.rs": "line1\n", "a.rs": "line1\n", "c.rs": "line1\n"}),
315 )
316 .await;
317
318 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
319 let buffer_b = open_buffer(&project, path!("/project/b.rs"), cx).await;
320 let buffer_a = open_buffer(&project, path!("/project/a.rs"), cx).await;
321 let buffer_c = open_buffer(&project, path!("/project/c.rs"), cx).await;
322
323 add_bookmarks(&project, &buffer_b, &[0], cx);
324 add_bookmarks(&project, &buffer_a, &[0], cx);
325 add_bookmarks(&project, &buffer_c, &[0], cx);
326
327 let paths: Vec<_> = get_all_bookmarks(&project, cx).keys().cloned().collect();
328 assert_eq!(
329 paths,
330 [
331 project_path(path!("/project/a.rs")),
332 project_path(path!("/project/b.rs")),
333 project_path(path!("/project/c.rs")),
334 ]
335 );
336 }
337
338 #[gpui::test]
339 async fn test_all_serialized_bookmarks_deduplicates_same_row(cx: &mut TestAppContext) {
340 init_test(cx);
341 cx.executor().allow_parking();
342
343 let fs = fs::FakeFs::new(cx.executor());
344 fs.insert_tree(
345 path!("/project"),
346 json!({"file1.rs": "line1\nline2\nline3\nline4\n"}),
347 )
348 .await;
349
350 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
351 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
352
353 add_bookmarks(&project, &buffer, &[1, 2], cx);
354
355 let bookmarks = get_all_bookmarks(&project, cx);
356 assert_bookmark_rows(&bookmarks, path!("/project/file1.rs"), &[1, 2]);
357
358 // Verify no duplicates
359 let rows: Vec<u32> = bookmarks
360 .get(&project_path(path!("/project/file1.rs")))
361 .unwrap()
362 .iter()
363 .map(|b| b.row)
364 .collect();
365 let mut deduped = rows.clone();
366 deduped.dedup();
367 assert_eq!(rows, deduped);
368 }
369
370 #[gpui::test]
371 async fn test_with_serialized_bookmarks_restores_bookmarks(cx: &mut TestAppContext) {
372 init_test(cx);
373 cx.executor().allow_parking();
374
375 let fs = fs::FakeFs::new(cx.executor());
376 fs.insert_tree(
377 path!("/project"),
378 json!({
379 "file1.rs": "line1\nline2\nline3\nline4\nline5\n",
380 "file2.rs": "aaa\nbbb\nccc\n"
381 }),
382 )
383 .await;
384
385 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
386
387 let serialized = build_serialized(&[
388 (path!("/project/file1.rs"), &[0, 3]),
389 (path!("/project/file2.rs"), &[1]),
390 ]);
391
392 restore_bookmarks(&project, serialized, cx).await;
393
394 let restored = get_all_bookmarks(&project, cx);
395 assert_eq!(restored.len(), 2);
396 assert_bookmark_rows(&restored, path!("/project/file1.rs"), &[0, 3]);
397 assert_bookmark_rows(&restored, path!("/project/file2.rs"), &[1]);
398 }
399
400 #[gpui::test]
401 async fn test_with_serialized_bookmarks_skips_out_of_range_rows(cx: &mut TestAppContext) {
402 init_test(cx);
403 cx.executor().allow_parking();
404
405 let fs = fs::FakeFs::new(cx.executor());
406 // 3 lines: rows 0, 1, 2
407 fs.insert_tree(
408 path!("/project"),
409 json!({"file1.rs": "line1\nline2\nline3"}),
410 )
411 .await;
412
413 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
414
415 let serialized = build_serialized(&[(path!("/project/file1.rs"), &[1, 100, 2])]);
416 restore_bookmarks(&project, serialized, cx).await;
417
418 // Before resolution, unloaded bookmarks are stored as-is
419 let unresolved = get_all_bookmarks(&project, cx);
420 assert_bookmark_rows(&unresolved, path!("/project/file1.rs"), &[1, 2, 100]);
421
422 // Open the buffer to trigger lazy resolution
423 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
424 project.update(cx, |project, cx| {
425 let buffer_snapshot = buffer.read(cx).snapshot();
426 project.bookmark_store().update(cx, |store, cx| {
427 store.bookmarks_for_buffer(
428 buffer.clone(),
429 buffer_snapshot.anchor_before(0)
430 ..buffer_snapshot.anchor_after(buffer_snapshot.len()),
431 &buffer_snapshot,
432 cx,
433 );
434 });
435 });
436
437 // After resolution, out-of-range rows are filtered
438 let restored = get_all_bookmarks(&project, cx);
439 assert_bookmark_rows(&restored, path!("/project/file1.rs"), &[1, 2]);
440 }
441
442 #[gpui::test]
443 async fn test_with_serialized_bookmarks_skips_empty_entries(cx: &mut TestAppContext) {
444 init_test(cx);
445 cx.executor().allow_parking();
446
447 let fs = fs::FakeFs::new(cx.executor());
448 fs.insert_tree(
449 path!("/project"),
450 json!({"file1.rs": "line1\nline2\n", "file2.rs": "aaa\nbbb\n"}),
451 )
452 .await;
453
454 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
455
456 let mut serialized = build_serialized(&[(path!("/project/file1.rs"), &[0])]);
457 serialized.insert(project_path(path!("/project/file2.rs")), vec![]);
458
459 restore_bookmarks(&project, serialized, cx).await;
460
461 let restored = get_all_bookmarks(&project, cx);
462 assert_eq!(restored.len(), 1);
463 assert!(restored.contains_key(&project_path(path!("/project/file1.rs"))));
464 assert!(!restored.contains_key(&project_path(path!("/project/file2.rs"))));
465 }
466
467 #[gpui::test]
468 async fn test_with_serialized_bookmarks_all_out_of_range_produces_no_entry(
469 cx: &mut TestAppContext,
470 ) {
471 init_test(cx);
472 cx.executor().allow_parking();
473
474 let fs = fs::FakeFs::new(cx.executor());
475 fs.insert_tree(path!("/project"), json!({"tiny.rs": "x"}))
476 .await;
477
478 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
479
480 let serialized = build_serialized(&[(path!("/project/tiny.rs"), &[5, 10])]);
481 restore_bookmarks(&project, serialized, cx).await;
482
483 // Before resolution, unloaded bookmarks are stored as-is
484 let unresolved = get_all_bookmarks(&project, cx);
485 assert_eq!(unresolved.len(), 1);
486
487 // Open the buffer to trigger lazy resolution
488 let buffer = open_buffer(&project, path!("/project/tiny.rs"), cx).await;
489 project.update(cx, |project, cx| {
490 let buffer_snapshot = buffer.read(cx).snapshot();
491 project.bookmark_store().update(cx, |store, cx| {
492 store.bookmarks_for_buffer(
493 buffer.clone(),
494 buffer_snapshot.anchor_before(0)
495 ..buffer_snapshot.anchor_after(buffer_snapshot.len()),
496 &buffer_snapshot,
497 cx,
498 );
499 });
500 });
501
502 // After resolution, all out-of-range rows are filtered away
503 assert!(get_all_bookmarks(&project, cx).is_empty());
504 }
505
506 #[gpui::test]
507 async fn test_with_serialized_bookmarks_replaces_existing(cx: &mut TestAppContext) {
508 init_test(cx);
509 cx.executor().allow_parking();
510
511 let fs = fs::FakeFs::new(cx.executor());
512 fs.insert_tree(
513 path!("/project"),
514 json!({"file1.rs": "aaa\nbbb\nccc\nddd\n"}),
515 )
516 .await;
517
518 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
519 let buffer = open_buffer(&project, path!("/project/file1.rs"), cx).await;
520
521 add_bookmarks(&project, &buffer, &[0], cx);
522 assert_bookmark_rows(
523 &get_all_bookmarks(&project, cx),
524 path!("/project/file1.rs"),
525 &[0],
526 );
527
528 // Restoring different bookmarks should replace, not merge
529 let serialized = build_serialized(&[(path!("/project/file1.rs"), &[2, 3])]);
530 restore_bookmarks(&project, serialized, cx).await;
531
532 let after = get_all_bookmarks(&project, cx);
533 assert_eq!(after.len(), 1);
534 assert_bookmark_rows(&after, path!("/project/file1.rs"), &[2, 3]);
535 }
536
537 #[gpui::test]
538 async fn test_serialize_deserialize_round_trip(cx: &mut TestAppContext) {
539 init_test(cx);
540 cx.executor().allow_parking();
541
542 let fs = fs::FakeFs::new(cx.executor());
543 fs.insert_tree(
544 path!("/project"),
545 json!({
546 "alpha.rs": "fn main() {\n println!(\"hello\");\n return;\n}\n",
547 "beta.rs": "use std::io;\nfn read() {}\nfn write() {}\n"
548 }),
549 )
550 .await;
551
552 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
553 let buffer_alpha = open_buffer(&project, path!("/project/alpha.rs"), cx).await;
554 let buffer_beta = open_buffer(&project, path!("/project/beta.rs"), cx).await;
555
556 add_bookmarks(&project, &buffer_alpha, &[0, 2, 3], cx);
557 add_bookmarks(&project, &buffer_beta, &[1], cx);
558
559 // Serialize
560 let serialized = get_all_bookmarks(&project, cx);
561 assert_eq!(serialized.len(), 2);
562 assert_bookmark_rows(&serialized, path!("/project/alpha.rs"), &[0, 2, 3]);
563 assert_bookmark_rows(&serialized, path!("/project/beta.rs"), &[1]);
564
565 // Clear and restore
566 clear_bookmarks(&project, cx);
567 assert!(get_all_bookmarks(&project, cx).is_empty());
568
569 restore_bookmarks(&project, serialized, cx).await;
570
571 let restored = get_all_bookmarks(&project, cx);
572 assert_eq!(restored.len(), 2);
573 assert_bookmark_rows(&restored, path!("/project/alpha.rs"), &[0, 2, 3]);
574 assert_bookmark_rows(&restored, path!("/project/beta.rs"), &[1]);
575 }
576
577 #[gpui::test]
578 async fn test_round_trip_preserves_bookmarks_after_file_edit(cx: &mut TestAppContext) {
579 init_test(cx);
580 cx.executor().allow_parking();
581
582 let fs = fs::FakeFs::new(cx.executor());
583 fs.insert_tree(
584 path!("/project"),
585 json!({"file.rs": "aaa\nbbb\nccc\nddd\neee\n"}),
586 )
587 .await;
588
589 let project = Project::test(fs, [path!("/project").as_ref()], cx).await;
590 let buffer = open_buffer(&project, path!("/project/file.rs"), cx).await;
591
592 add_bookmarks(&project, &buffer, &[1, 3], cx);
593
594 // Insert a line at the beginning, shifting bookmarks down by 1
595 buffer.update(cx, |buffer, cx| {
596 buffer.edit([(0..0, "new_first_line\n")], None, cx);
597 });
598
599 let serialized = get_all_bookmarks(&project, cx);
600 assert_bookmark_rows(&serialized, path!("/project/file.rs"), &[2, 4]);
601
602 // Clear and restore
603 clear_bookmarks(&project, cx);
604 restore_bookmarks(&project, serialized, cx).await;
605
606 let restored = get_all_bookmarks(&project, cx);
607 assert_bookmark_rows(&restored, path!("/project/file.rs"), &[2, 4]);
608 }
609
610 #[gpui::test]
611 async fn test_file_deletion_removes_bookmarks(cx: &mut TestAppContext) {
612 init_test(cx);
613 cx.executor().allow_parking();
614
615 let fs = fs::FakeFs::new(cx.executor());
616 fs.insert_tree(
617 path!("/project"),
618 json!({
619 "file1.rs": "aaa\nbbb\nccc\n",
620 "file2.rs": "ddd\neee\nfff\n"
621 }),
622 )
623 .await;
624
625 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
626 let buffer1 = open_buffer(&project, path!("/project/file1.rs"), cx).await;
627 let buffer2 = open_buffer(&project, path!("/project/file2.rs"), cx).await;
628
629 add_bookmarks(&project, &buffer1, &[0, 2], cx);
630 add_bookmarks(&project, &buffer2, &[1], cx);
631 assert_eq!(get_all_bookmarks(&project, cx).len(), 2);
632
633 // Delete file1.rs
634 fs.remove_file(path!("/project/file1.rs").as_ref(), Default::default())
635 .await
636 .unwrap();
637 cx.executor().run_until_parked();
638
639 // file1.rs bookmarks should be gone, file2.rs bookmarks preserved
640 let bookmarks = get_all_bookmarks(&project, cx);
641 assert_eq!(bookmarks.len(), 1);
642 assert!(!bookmarks.contains_key(&project_path(path!("/project/file1.rs"))));
643 assert_bookmark_rows(&bookmarks, path!("/project/file2.rs"), &[1]);
644 }
645
646 #[gpui::test]
647 async fn test_deleting_all_bookmarked_files_clears_store(cx: &mut TestAppContext) {
648 init_test(cx);
649 cx.executor().allow_parking();
650
651 let fs = fs::FakeFs::new(cx.executor());
652 fs.insert_tree(
653 path!("/project"),
654 json!({
655 "file1.rs": "aaa\nbbb\n",
656 "file2.rs": "ccc\nddd\n"
657 }),
658 )
659 .await;
660
661 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
662 let buffer1 = open_buffer(&project, path!("/project/file1.rs"), cx).await;
663 let buffer2 = open_buffer(&project, path!("/project/file2.rs"), cx).await;
664
665 add_bookmarks(&project, &buffer1, &[0], cx);
666 add_bookmarks(&project, &buffer2, &[1], cx);
667 assert_eq!(get_all_bookmarks(&project, cx).len(), 2);
668
669 // Delete both files
670 fs.remove_file(path!("/project/file1.rs").as_ref(), Default::default())
671 .await
672 .unwrap();
673 fs.remove_file(path!("/project/file2.rs").as_ref(), Default::default())
674 .await
675 .unwrap();
676 cx.executor().run_until_parked();
677
678 assert!(get_all_bookmarks(&project, cx).is_empty());
679 }
680
681 #[gpui::test]
682 async fn test_file_rename_re_keys_bookmarks(cx: &mut TestAppContext) {
683 init_test(cx);
684 cx.executor().allow_parking();
685
686 let fs = fs::FakeFs::new(cx.executor());
687 fs.insert_tree(path!("/project"), json!({"old_name.rs": "aaa\nbbb\nccc\n"}))
688 .await;
689
690 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
691 let buffer = open_buffer(&project, path!("/project/old_name.rs"), cx).await;
692
693 add_bookmarks(&project, &buffer, &[0, 2], cx);
694 assert_bookmark_rows(
695 &get_all_bookmarks(&project, cx),
696 path!("/project/old_name.rs"),
697 &[0, 2],
698 );
699
700 // Rename the file
701 fs.rename(
702 path!("/project/old_name.rs").as_ref(),
703 path!("/project/new_name.rs").as_ref(),
704 Default::default(),
705 )
706 .await
707 .unwrap();
708 cx.executor().run_until_parked();
709
710 let bookmarks = get_all_bookmarks(&project, cx);
711 assert_eq!(bookmarks.len(), 1);
712 assert!(!bookmarks.contains_key(&project_path(path!("/project/old_name.rs"))));
713 assert_bookmark_rows(&bookmarks, path!("/project/new_name.rs"), &[0, 2]);
714 }
715
716 #[gpui::test]
717 async fn test_file_rename_preserves_other_bookmarks(cx: &mut TestAppContext) {
718 init_test(cx);
719 cx.executor().allow_parking();
720
721 let fs = fs::FakeFs::new(cx.executor());
722 fs.insert_tree(
723 path!("/project"),
724 json!({
725 "rename_me.rs": "aaa\nbbb\n",
726 "untouched.rs": "ccc\nddd\neee\n"
727 }),
728 )
729 .await;
730
731 let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await;
732 let buffer_rename = open_buffer(&project, path!("/project/rename_me.rs"), cx).await;
733 let buffer_other = open_buffer(&project, path!("/project/untouched.rs"), cx).await;
734
735 add_bookmarks(&project, &buffer_rename, &[1], cx);
736 add_bookmarks(&project, &buffer_other, &[0, 2], cx);
737
738 fs.rename(
739 path!("/project/rename_me.rs").as_ref(),
740 path!("/project/renamed.rs").as_ref(),
741 Default::default(),
742 )
743 .await
744 .unwrap();
745 cx.executor().run_until_parked();
746
747 let bookmarks = get_all_bookmarks(&project, cx);
748 assert_eq!(bookmarks.len(), 2);
749 assert_bookmark_rows(&bookmarks, path!("/project/renamed.rs"), &[1]);
750 assert_bookmark_rows(&bookmarks, path!("/project/untouched.rs"), &[0, 2]);
751 }
752}
753