Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:56:21.965Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

edit_prediction_context_tests.rs

1161 lines · 37.3 KB · rust
1use super::*;
2use crate::assemble_excerpts::assemble_excerpt_ranges;
3use futures::channel::mpsc::UnboundedReceiver;
4use gpui::TestAppContext;
5use indoc::indoc;
6use language::{Point, ToPoint as _, rust_lang};
7use lsp::FakeLanguageServer;
8use project::{FakeFs, LocationLink, Project, ProjectPath};
9use serde_json::json;
10use settings::SettingsStore;
11use std::fmt::Write as _;
12use util::rel_path::rel_path;
13use util::{path, test::marked_text_ranges};
14
15#[gpui::test]
16async fn test_edit_prediction_context(cx: &mut TestAppContext) {
17    init_test(cx);
18    let fs = FakeFs::new(cx.executor());
19    fs.insert_tree(path!("/root"), test_project_1()).await;
20
21    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
22    let mut servers = setup_fake_lsp(&project, cx);
23
24    let (buffer, _handle) = project
25        .update(cx, |project, cx| {
26            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
27        })
28        .await
29        .unwrap();
30
31    let _server = servers.next().await.unwrap();
32    cx.run_until_parked();
33
34    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
35    related_excerpt_store.update(cx, |store, cx| {
36        let position = {
37            let buffer = buffer.read(cx);
38            let offset = buffer.text().find("todo").unwrap();
39            buffer.anchor_before(offset)
40        };
41
42        store.set_identifier_line_count(0);
43        store.refresh(buffer.clone(), position, cx);
44    });
45
46    cx.executor().advance_clock(DEBOUNCE_DURATION);
47    related_excerpt_store.update(cx, |store, cx| {
48        let excerpts = store.related_files(cx);
49        assert_related_files(
50            &excerpts,
51            &[
52                (
53                    "root/src/person.rs",
54                    &[
55                        indoc! {"
56                        pub struct Person {
57                            first_name: String,
58                            last_name: String,
59                            email: String,
60                            age: u32,
61                        }
62
63                        impl Person {
64                            pub fn get_first_name(&self) -> &str {
65                                &self.first_name
66                            }"},
67                        "}",
68                    ],
69                ),
70                (
71                    "root/src/company.rs",
72                    &[indoc! {"
73                        pub struct Company {
74                            owner: Arc<Person>,
75                            address: Address,
76                        }"}],
77                ),
78                (
79                    "root/src/main.rs",
80                    &[
81                        indoc! {"
82                        pub struct Session {
83                            company: Arc<Company>,
84                        }
85
86                        impl Session {
87                            pub fn set_company(&mut self, company: Arc<Company>) {"},
88                        indoc! {"
89                            }
90                        }"},
91                    ],
92                ),
93            ],
94        );
95    });
96
97    let company_buffer = related_excerpt_store.update(cx, |store, cx| {
98        store
99            .related_files_with_buffers(cx)
100            .find(|(file, _)| file.path.to_str() == Some("root/src/company.rs"))
101            .map(|(_, buffer)| buffer)
102            .expect("company.rs buffer not found")
103    });
104
105    company_buffer.update(cx, |buffer, cx| {
106        let text = buffer.text();
107        let insert_pos = text.find("address: Address,").unwrap() + "address: Address,".len();
108        buffer.edit([(insert_pos..insert_pos, "\n    name: String,")], None, cx);
109    });
110
111    related_excerpt_store.update(cx, |store, cx| {
112        let excerpts = store.related_files(cx);
113        assert_related_files(
114            &excerpts,
115            &[
116                (
117                    "root/src/person.rs",
118                    &[
119                        indoc! {"
120                        pub struct Person {
121                            first_name: String,
122                            last_name: String,
123                            email: String,
124                            age: u32,
125                        }
126
127                        impl Person {
128                            pub fn get_first_name(&self) -> &str {
129                                &self.first_name
130                            }"},
131                        "}",
132                    ],
133                ),
134                (
135                    "root/src/company.rs",
136                    &[indoc! {"
137                        pub struct Company {
138                            owner: Arc<Person>,
139                            address: Address,
140                            name: String,
141                        }"}],
142                ),
143                (
144                    "root/src/main.rs",
145                    &[
146                        indoc! {"
147                        pub struct Session {
148                            company: Arc<Company>,
149                        }
150
151                        impl Session {
152                            pub fn set_company(&mut self, company: Arc<Company>) {"},
153                        indoc! {"
154                            }
155                        }"},
156                    ],
157                ),
158            ],
159        );
160    });
161}
162
163#[gpui::test]
164async fn test_assemble_excerpts(cx: &mut TestAppContext) {
165    let table = [
166        (
167            indoc! {r#"
168                struct User {
169                    first_name: String,
170                    «last_name»: String,
171                    age: u32,
172                    email: String,
173                    create_at: Instant,
174                }
175
176                impl User {
177                    pub fn first_name(&self) -> String {
178                        self.first_name.clone()
179                    }
180
181                    pub fn full_name(&self) -> String {
182                «        format!("{} {}", self.first_name, self.last_name)
183                »    }
184                }
185            "#},
186            indoc! {r#"
187                struct User {
188                    first_name: String,
189                    last_name: String,
190
191                }
192
193                impl User {
194
195                    pub fn full_name(&self) -> String {
196                        format!("{} {}", self.first_name, self.last_name)
197                    }
198                }
199            "#},
200        ),
201        (
202            indoc! {r#"
203                struct «User» {
204                    first_name: String,
205                    last_name: String,
206                    age: u32,
207                }
208
209                impl User {
210                    // methods
211                }
212            "#},
213            indoc! {r#"
214                struct User {
215                    first_name: String,
216                    last_name: String,
217                    age: u32,
218                }
219
220            "#},
221        ),
222        (
223            indoc! {r#"
224                trait «FooProvider» {
225                    const NAME: &'static str;
226
227                    fn provide_foo(&self, id: usize) -> Foo;
228
229                    fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> {
230                            ids.iter()
231                            .map(|id| self.provide_foo(*id))
232                            .collect()
233                    }
234
235                    fn sync(&self);
236                }
237                "#
238            },
239            indoc! {r#"
240                trait FooProvider {
241                    const NAME: &'static str;
242
243                    fn provide_foo(&self, id: usize) -> Foo;
244
245                    fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> {
246
247                    }
248
249                    fn sync(&self);
250                }
251            "#},
252        ),
253        (
254            indoc! {r#"
255                trait «Something» {
256                    fn method1(&self, id: usize) -> Foo;
257
258                    fn method2(&self, ids: &[usize]) -> Vec<Foo> {
259                            struct Helper1 {
260                            field1: usize,
261                            }
262
263                            struct Helper2 {
264                            field2: usize,
265                            }
266
267                            struct Helper3 {
268                            filed2: usize,
269                        }
270                    }
271
272                    fn sync(&self);
273                }
274                "#
275            },
276            indoc! {r#"
277                trait Something {
278                    fn method1(&self, id: usize) -> Foo;
279
280                    fn method2(&self, ids: &[usize]) -> Vec<Foo> {
281
282                    }
283
284                    fn sync(&self);
285                }
286            "#},
287        ),
288    ];
289
290    for (input, expected_output) in table {
291        let (input, ranges) = marked_text_ranges(&input, false);
292        let buffer = cx.new(|cx| Buffer::local(input, cx).with_language(rust_lang(), cx));
293        buffer
294            .read_with(cx, |buffer, _| buffer.parsing_idle())
295            .await;
296        buffer.read_with(cx, |buffer, _cx| {
297            let ranges: Vec<(Range<Point>, usize)> = ranges
298                .into_iter()
299                .map(|range| (range.to_point(&buffer), 0))
300                .collect();
301
302            let assembled = assemble_excerpt_ranges(&buffer.snapshot(), ranges);
303            let excerpts: Vec<RelatedExcerpt> = assembled
304                .into_iter()
305                .map(|(row_range, order)| {
306                    let start = Point::new(row_range.start, 0);
307                    let end = Point::new(row_range.end, buffer.line_len(row_range.end));
308                    RelatedExcerpt {
309                        row_range,
310                        text: buffer.text_for_range(start..end).collect::<String>().into(),
311                        order,
312                        context_source: ContextSource::Lsp,
313                    }
314                })
315                .collect();
316
317            let output = format_excerpts(buffer, &excerpts);
318            assert_eq!(output, expected_output);
319        });
320    }
321}
322
323#[gpui::test]
324async fn test_fake_definition_lsp(cx: &mut TestAppContext) {
325    init_test(cx);
326
327    let fs = FakeFs::new(cx.executor());
328    fs.insert_tree(path!("/root"), test_project_1()).await;
329
330    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
331    let mut servers = setup_fake_lsp(&project, cx);
332
333    let (buffer, _handle) = project
334        .update(cx, |project, cx| {
335            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
336        })
337        .await
338        .unwrap();
339
340    let _server = servers.next().await.unwrap();
341    cx.run_until_parked();
342
343    let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text());
344
345    let definitions = project
346        .update(cx, |project, cx| {
347            let offset = buffer_text.find("Address {").unwrap();
348            project.definitions(&buffer, offset, cx)
349        })
350        .await
351        .unwrap()
352        .unwrap();
353    assert_definitions(&definitions, &["pub struct Address {"], cx);
354
355    let definitions = project
356        .update(cx, |project, cx| {
357            let offset = buffer_text.find("State::CA").unwrap();
358            project.definitions(&buffer, offset, cx)
359        })
360        .await
361        .unwrap()
362        .unwrap();
363    assert_definitions(&definitions, &["pub enum State {"], cx);
364
365    let definitions = project
366        .update(cx, |project, cx| {
367            let offset = buffer_text.find("to_string()").unwrap();
368            project.definitions(&buffer, offset, cx)
369        })
370        .await
371        .unwrap()
372        .unwrap();
373    assert_definitions(&definitions, &["pub fn to_string(&self) -> String {"], cx);
374}
375
376#[gpui::test]
377async fn test_fake_type_definition_lsp(cx: &mut TestAppContext) {
378    init_test(cx);
379
380    let fs = FakeFs::new(cx.executor());
381    fs.insert_tree(path!("/root"), test_project_1()).await;
382
383    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
384    let mut servers = setup_fake_lsp(&project, cx);
385
386    let (buffer, _handle) = project
387        .update(cx, |project, cx| {
388            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
389        })
390        .await
391        .unwrap();
392
393    let _server = servers.next().await.unwrap();
394    cx.run_until_parked();
395
396    let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text());
397
398    // Type definition on a type name returns its own definition
399    // (same as regular definition)
400    let type_defs = project
401        .update(cx, |project, cx| {
402            let offset = buffer_text.find("Address {").expect("Address { not found");
403            project.type_definitions(&buffer, offset, cx)
404        })
405        .await
406        .unwrap()
407        .unwrap();
408    assert_definitions(&type_defs, &["pub struct Address {"], cx);
409
410    // Type definition on a field resolves through the type annotation.
411    // company.rs has `owner: Arc<Person>`, so type-def of `owner` → Person.
412    let (company_buffer, _handle) = project
413        .update(cx, |project, cx| {
414            project.open_local_buffer_with_lsp(path!("/root/src/company.rs"), cx)
415        })
416        .await
417        .unwrap();
418    cx.run_until_parked();
419
420    let company_text = company_buffer.read_with(cx, |buffer, _| buffer.text());
421    let type_defs = project
422        .update(cx, |project, cx| {
423            let offset = company_text.find("owner").expect("owner not found");
424            project.type_definitions(&company_buffer, offset, cx)
425        })
426        .await
427        .unwrap()
428        .unwrap();
429    assert_definitions(&type_defs, &["pub struct Person {"], cx);
430
431    // Type definition on another field: `address: Address` → Address.
432    let type_defs = project
433        .update(cx, |project, cx| {
434            let offset = company_text.find("address").expect("address not found");
435            project.type_definitions(&company_buffer, offset, cx)
436        })
437        .await
438        .unwrap()
439        .unwrap();
440    assert_definitions(&type_defs, &["pub struct Address {"], cx);
441
442    // Type definition on a lowercase name with no type annotation returns empty.
443    let type_defs = project
444        .update(cx, |project, cx| {
445            let offset = buffer_text.find("main").expect("main not found");
446            project.type_definitions(&buffer, offset, cx)
447        })
448        .await;
449    let is_empty = match &type_defs {
450        Ok(Some(defs)) => defs.is_empty(),
451        Ok(None) => true,
452        Err(_) => false,
453    };
454    assert!(is_empty, "expected no type definitions for `main`");
455}
456
457#[gpui::test]
458async fn test_type_definitions_in_related_files(cx: &mut TestAppContext) {
459    init_test(cx);
460    let fs = FakeFs::new(cx.executor());
461    fs.insert_tree(
462        path!("/root"),
463        json!({
464            "src": {
465                "config.rs": indoc! {r#"
466                    pub struct Config {
467                        debug: bool,
468                        verbose: bool,
469                    }
470                "#},
471                "widget.rs": indoc! {r#"
472                    use super::config::Config;
473
474                    pub struct Widget {
475                        config: Config,
476                        name: String,
477                    }
478
479                    impl Widget {
480                        pub fn render(&self) {
481                            if self.config.debug {
482                                println!("debug mode");
483                            }
484                        }
485                    }
486                "#},
487            },
488        }),
489    )
490    .await;
491
492    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
493    let mut servers = setup_fake_lsp(&project, cx);
494
495    let (buffer, _handle) = project
496        .update(cx, |project, cx| {
497            project.open_local_buffer_with_lsp(path!("/root/src/widget.rs"), cx)
498        })
499        .await
500        .unwrap();
501
502    let _server = servers.next().await.unwrap();
503    cx.run_until_parked();
504
505    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
506    related_excerpt_store.update(cx, |store, cx| {
507        let position = {
508            let buffer = buffer.read(cx);
509            let offset = buffer
510                .text()
511                .find("self.config.debug")
512                .expect("self.config.debug not found");
513            buffer.anchor_before(offset)
514        };
515
516        store.set_identifier_line_count(0);
517        store.refresh(buffer.clone(), position, cx);
518    });
519
520    cx.executor().advance_clock(DEBOUNCE_DURATION);
521    // config.rs appears ONLY because the fake LSP resolves the type annotation
522    // `config: Config` to `pub struct Config` via GotoTypeDefinition.
523    // widget.rs appears from regular definitions of Widget / render.
524    related_excerpt_store.update(cx, |store, cx| {
525        let excerpts = store.related_files(cx);
526        assert_related_files(
527            &excerpts,
528            &[
529                (
530                    "root/src/config.rs",
531                    &[indoc! {"
532                        pub struct Config {
533                            debug: bool,
534                            verbose: bool,
535                        }"}],
536                ),
537                (
538                    "root/src/widget.rs",
539                    &[
540                        indoc! {"
541                        pub struct Widget {
542                            config: Config,
543                            name: String,
544                        }
545
546                        impl Widget {
547                            pub fn render(&self) {"},
548                        indoc! {"
549                            }
550                        }"},
551                    ],
552                ),
553            ],
554        );
555    });
556}
557
558#[gpui::test]
559async fn test_type_definition_deduplication(cx: &mut TestAppContext) {
560    init_test(cx);
561    let fs = FakeFs::new(cx.executor());
562
563    // In this project the only identifier near the cursor whose type definition
564    // resolves is `TypeA`, and its GotoTypeDefinition returns the exact same
565    // location as GotoDefinition. After the definitions and type definitions are
566    // merged and deduped, the type-definition location is dropped, so it
567    // contributes nothing extra to the related-file output (the target location
568    // appears only once).
569    fs.insert_tree(
570        path!("/root"),
571        json!({
572            "src": {
573                "types.rs": indoc! {r#"
574                    pub struct TypeA {
575                        value: i32,
576                    }
577
578                    pub struct TypeB {
579                        label: String,
580                    }
581                "#},
582                "main.rs": indoc! {r#"
583                    use super::types::TypeA;
584
585                    fn work() {
586                        let item: TypeA = unimplemented!();
587                        println!("{}", item.value);
588                    }
589                "#},
590            },
591        }),
592    )
593    .await;
594
595    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
596    let mut servers = setup_fake_lsp(&project, cx);
597
598    let (buffer, _handle) = project
599        .update(cx, |project, cx| {
600            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
601        })
602        .await
603        .unwrap();
604
605    let _server = servers.next().await.unwrap();
606    cx.run_until_parked();
607
608    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
609    related_excerpt_store.update(cx, |store, cx| {
610        let position = {
611            let buffer = buffer.read(cx);
612            let offset = buffer.text().find("let item").expect("let item not found");
613            buffer.anchor_before(offset)
614        };
615
616        store.set_identifier_line_count(0);
617        store.refresh(buffer.clone(), position, cx);
618    });
619
620    cx.executor().advance_clock(DEBOUNCE_DURATION);
621    // types.rs appears because `TypeA` has a regular definition there.
622    // `item`'s type definition also resolves to TypeA in types.rs, but
623    // deduplication removes it since it points to the same location.
624    // TypeB should NOT appear because nothing references it.
625    related_excerpt_store.update(cx, |store, cx| {
626        let excerpts = store.related_files(cx);
627        assert_related_files(
628            &excerpts,
629            &[
630                (
631                    "root/src/types.rs",
632                    &[indoc! {"
633                        pub struct TypeA {
634                            value: i32,
635                        }"}],
636                ),
637                ("root/src/main.rs", &["fn work() {", "}"]),
638            ],
639        );
640    });
641}
642
643#[gpui::test]
644async fn test_edit_prediction_filters_raw_definitions_before_opening_buffers(
645    cx: &mut TestAppContext,
646) {
647    init_test(cx);
648    let fs = FakeFs::new(cx.executor());
649    fs.insert_tree(
650        path!("/root"),
651        json!({
652            "src": {
653                "main.rs": indoc! {"
654                    // fake-definition-lsp-extra target /root/src/large.rs 0 0 0 129
655                    // fake-definition-lsp-extra target /root/src/valid.rs 0 3 0 9
656                    // fake-definition-lsp-extra target /outside.rs 0 3 0 10
657                    fn main() {
658                        target();
659                    }
660                "},
661                "valid.rs": "fn target() {}\n",
662                "large.rs": format!("{}\n", "a".repeat(MAX_TARGET_LEN + 16)),
663            },
664        }),
665    )
666    .await;
667    fs.insert_file(path!("/outside.rs"), "fn outside() {}\n".into())
668        .await;
669
670    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
671    let mut servers = setup_fake_lsp(&project, cx);
672
673    let (buffer, _handle) = project
674        .update(cx, |project, cx| {
675            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
676        })
677        .await
678        .unwrap();
679
680    let _fake_language_server = servers.next().await.unwrap();
681    cx.run_until_parked();
682
683    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
684    related_excerpt_store.update(cx, |store, cx| {
685        let position = {
686            let buffer = buffer.read(cx);
687            let offset = buffer
688                .text()
689                .find("target();")
690                .expect("target call not found");
691            buffer.anchor_before(offset)
692        };
693
694        store.set_identifier_line_count(0);
695        store.refresh(buffer.clone(), position, cx);
696    });
697
698    cx.executor().advance_clock(DEBOUNCE_DURATION);
699    related_excerpt_store.update(cx, |store, cx| {
700        assert_related_files(
701            &store.related_files(cx),
702            &[
703                ("root/src/valid.rs", &["fn target() {}"]),
704                ("root/src/main.rs", &["fn main() {\n    target();\n}"]),
705            ],
706        );
707    });
708
709    let worktree_id = buffer.read_with(cx, |buffer, cx| {
710        buffer.file().expect("buffer has file").worktree_id(cx)
711    });
712    let valid_path = ProjectPath {
713        worktree_id,
714        path: rel_path("src/valid.rs").into(),
715    };
716    project.read_with(cx, |project, cx| {
717        assert!(project.get_open_buffer(&valid_path, cx).is_some());
718        assert_eq!(project.worktrees(cx).count(), 1);
719    });
720}
721
722#[gpui::test]
723async fn test_definitions_ranked_by_cursor_proximity(cx: &mut TestAppContext) {
724    init_test(cx);
725    let fs = FakeFs::new(cx.executor());
726
727    // helpers.rs has an impl block whose body exceeds the test
728    // MAX_OUTLINE_ITEM_BODY_SIZE (24 bytes), so assemble_excerpt_ranges
729    // splits it into header + individual children + closing brace. main.rs
730    // references two of the three methods on separate lines at varying
731    // distances from the cursor. This exercises:
732    //   1. File ordering by closest identifier rank.
733    //   2. Per-excerpt ordering within a file — child excerpts carry the rank
734    //      of the identifier that discovered them.
735    //   3. Parent excerpt (impl header / closing brace) inheriting the minimum
736    //      order of its children.
737    fs.insert_tree(
738        path!("/root"),
739        json!({
740            "src": {
741                "helpers.rs": indoc! {r#"
742                    pub struct Helpers {
743                        value: i32,
744                    }
745
746                    impl Helpers {
747                        pub fn alpha(&self) -> i32 {
748                            let intermediate = self.value;
749                            intermediate + 1
750                        }
751
752                        pub fn beta(&self) -> i32 {
753                            let intermediate = self.value;
754                            intermediate + 2
755                        }
756
757                        pub fn gamma(&self) -> i32 {
758                            let intermediate = self.value;
759                            intermediate + 3
760                        }
761                    }
762                "#},
763                "main.rs": indoc! {r#"
764                    use super::helpers::Helpers;
765
766                    fn process(h: Helpers) {
767                        let a = h.alpha();
768                        let b = h.gamma();
769                    }
770                "#},
771            },
772        }),
773    )
774    .await;
775
776    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
777    let mut servers = setup_fake_lsp(&project, cx);
778
779    let (buffer, _handle) = project
780        .update(cx, |project, cx| {
781            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
782        })
783        .await
784        .unwrap();
785
786    let _server = servers.next().await.unwrap();
787    cx.run_until_parked();
788
789    // Place cursor on "h.alpha()". `alpha` is at distance 0, `gamma` is
790    // farther below. Both resolve to methods inside `impl Helpers` in
791    // helpers.rs. The impl header and closing brace excerpts should inherit
792    // the min order of their children (alpha's order).
793    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
794    related_excerpt_store.update(cx, |store, cx| {
795        let position = {
796            let buffer = buffer.read(cx);
797            let offset = buffer.text().find("h.alpha()").unwrap();
798            buffer.anchor_before(offset)
799        };
800
801        store.set_identifier_line_count(1);
802        store.refresh(buffer.clone(), position, cx);
803    });
804
805    cx.executor().advance_clock(DEBOUNCE_DURATION);
806    related_excerpt_store.update(cx, |store, cx| {
807        let files = store.related_files(cx);
808
809        // helpers.rs has 4 excerpts: the struct+impl header merged with
810        // the alpha method header (order 1 from alpha), alpha's closing
811        // brace (order 1), gamma's method header (order 6), and the
812        // gamma+impl closing brace (order 1, inherited from alpha which
813        // is also a child of the impl).
814        let alpha_order = 1;
815        let gamma_order = 6;
816        assert_related_files_with_orders(
817            &files,
818            &[
819                (
820                    "root/src/helpers.rs",
821                    &[
822                        (
823                            indoc! {"
824                            pub struct Helpers {
825                                value: i32,
826                            }
827
828                            impl Helpers {
829                                pub fn alpha(&self) -> i32 {"},
830                            alpha_order,
831                        ),
832                        ("    }", alpha_order),
833                        ("    pub fn gamma(&self) -> i32 {", gamma_order),
834                        (
835                            indoc! {"
836                                }
837                            }"},
838                            alpha_order,
839                        ),
840                    ],
841                ),
842                (
843                    "root/src/main.rs",
844                    &[("fn process(h: Helpers) {", 8), ("}", 8)],
845                ),
846            ],
847        );
848    });
849
850    // Now move cursor to "h.gamma()" — gamma becomes closest, reranking the
851    // excerpts so that the gamma method excerpt has the best order and the
852    // alpha method excerpt has a worse order.
853    related_excerpt_store.update(cx, |store, cx| {
854        let position = {
855            let buffer = buffer.read(cx);
856            let offset = buffer.text().find("h.gamma()").unwrap();
857            buffer.anchor_before(offset)
858        };
859
860        store.set_identifier_line_count(1);
861        store.refresh(buffer.clone(), position, cx);
862    });
863
864    cx.executor().advance_clock(DEBOUNCE_DURATION);
865    related_excerpt_store.update(cx, |store, cx| {
866        let files = store.related_files(cx);
867
868        // Now gamma is closest. The alpha method excerpts carry alpha's
869        // rank (3), and the gamma method excerpts carry gamma's rank (1).
870        // The impl closing brace merges with gamma's closing brace and
871        // inherits gamma's order (the best child).
872        let alpha_order = 3;
873        let gamma_order = 1;
874        assert_related_files_with_orders(
875            &files,
876            &[
877                (
878                    "root/src/helpers.rs",
879                    &[
880                        (
881                            indoc! {"
882                            pub struct Helpers {
883                                value: i32,
884                            }
885
886                            impl Helpers {
887                                pub fn alpha(&self) -> i32 {"},
888                            alpha_order,
889                        ),
890                        ("    }", alpha_order),
891                        ("    pub fn gamma(&self) -> i32 {", gamma_order),
892                        (
893                            indoc! {"
894                                }
895                            }"},
896                            gamma_order,
897                        ),
898                    ],
899                ),
900                (
901                    "root/src/main.rs",
902                    &[("fn process(h: Helpers) {", 8), ("}", 8)],
903                ),
904            ],
905        );
906    });
907}
908
909fn init_test(cx: &mut TestAppContext) {
910    let settings_store = cx.update(|cx| SettingsStore::test(cx));
911    cx.set_global(settings_store);
912    env_logger::try_init().ok();
913}
914
915fn setup_fake_lsp(
916    project: &Entity<Project>,
917    cx: &mut TestAppContext,
918) -> UnboundedReceiver<FakeLanguageServer> {
919    let (language_registry, fs) = project.read_with(cx, |project, _| {
920        (project.languages().clone(), project.fs().clone())
921    });
922    let language = rust_lang();
923    language_registry.add(language.clone());
924    fake_definition_lsp::register_fake_definition_server(&language_registry, language, fs)
925}
926
927fn test_project_1() -> serde_json::Value {
928    let person_rs = indoc! {r#"
929        pub struct Person {
930            first_name: String,
931            last_name: String,
932            email: String,
933            age: u32,
934        }
935
936        impl Person {
937            pub fn get_first_name(&self) -> &str {
938                &self.first_name
939            }
940
941            pub fn get_last_name(&self) -> &str {
942                &self.last_name
943            }
944
945            pub fn get_email(&self) -> &str {
946                &self.email
947            }
948
949            pub fn get_age(&self) -> u32 {
950                self.age
951            }
952        }
953    "#};
954
955    let address_rs = indoc! {r#"
956        pub struct Address {
957            street: String,
958            city: String,
959            state: State,
960            zip: u32,
961        }
962
963        pub enum State {
964            CA,
965            OR,
966            WA,
967            TX,
968            // ...
969        }
970
971        impl Address {
972            pub fn get_street(&self) -> &str {
973                &self.street
974            }
975
976            pub fn get_city(&self) -> &str {
977                &self.city
978            }
979
980            pub fn get_state(&self) -> State {
981                self.state
982            }
983
984            pub fn get_zip(&self) -> u32 {
985                self.zip
986            }
987        }
988    "#};
989
990    let company_rs = indoc! {r#"
991        use super::person::Person;
992        use super::address::Address;
993
994        pub struct Company {
995            owner: Arc<Person>,
996            address: Address,
997        }
998
999        impl Company {
1000            pub fn get_owner(&self) -> &Person {
1001                &self.owner
1002            }
1003
1004            pub fn get_address(&self) -> &Address {
1005                &self.address
1006            }
1007
1008            pub fn to_string(&self) -> String {
1009                format!("{} ({})", self.owner.first_name, self.address.city)
1010            }
1011        }
1012    "#};
1013
1014    let main_rs = indoc! {r#"
1015        use std::sync::Arc;
1016        use super::person::Person;
1017        use super::address::Address;
1018        use super::company::Company;
1019
1020        pub struct Session {
1021            company: Arc<Company>,
1022        }
1023
1024        impl Session {
1025            pub fn set_company(&mut self, company: Arc<Company>) {
1026                self.company = company;
1027                if company.owner != self.company.owner {
1028                    log("new owner", company.owner.get_first_name()); todo();
1029                }
1030            }
1031        }
1032
1033        fn main() {
1034            let company = Company {
1035                owner: Arc::new(Person {
1036                    first_name: "John".to_string(),
1037                    last_name: "Doe".to_string(),
1038                    email: "john@example.com".to_string(),
1039                    age: 30,
1040                }),
1041                address: Address {
1042                    street: "123 Main St".to_string(),
1043                    city: "Anytown".to_string(),
1044                    state: State::CA,
1045                    zip: 12345,
1046                },
1047            };
1048
1049            println!("Company: {}", company.to_string());
1050        }
1051    "#};
1052
1053    json!({
1054        "src": {
1055            "person.rs": person_rs,
1056            "address.rs": address_rs,
1057            "company.rs": company_rs,
1058            "main.rs": main_rs,
1059        },
1060    })
1061}
1062
1063fn assert_related_files(actual_files: &[RelatedFile], expected_files: &[(&str, &[&str])]) {
1064    let expected_with_orders: Vec<(&str, Vec<(&str, usize)>)> = expected_files
1065        .iter()
1066        .map(|(path, texts)| (*path, texts.iter().map(|text| (*text, 0)).collect()))
1067        .collect();
1068    let expected_refs: Vec<(&str, &[(&str, usize)])> = expected_with_orders
1069        .iter()
1070        .map(|(path, excerpts)| (*path, excerpts.as_slice()))
1071        .collect();
1072    assert_related_files_impl(actual_files, &expected_refs, false)
1073}
1074
1075fn assert_related_files_with_orders(
1076    actual_files: &[RelatedFile],
1077    expected_files: &[(&str, &[(&str, usize)])],
1078) {
1079    assert_related_files_impl(actual_files, expected_files, true)
1080}
1081
1082fn assert_related_files_impl(
1083    actual_files: &[RelatedFile],
1084    expected_files: &[(&str, &[(&str, usize)])],
1085    check_orders: bool,
1086) {
1087    let actual: Vec<(&str, Vec<(String, usize)>)> = actual_files
1088        .iter()
1089        .map(|file| {
1090            let excerpts = file
1091                .excerpts
1092                .iter()
1093                .map(|excerpt| {
1094                    let order = if check_orders { excerpt.order } else { 0 };
1095                    (excerpt.text.to_string(), order)
1096                })
1097                .collect();
1098            (file.path.to_str().unwrap(), excerpts)
1099        })
1100        .collect();
1101    let expected: Vec<(&str, Vec<(String, usize)>)> = expected_files
1102        .iter()
1103        .map(|(path, excerpts)| {
1104            (
1105                *path,
1106                excerpts
1107                    .iter()
1108                    .map(|(text, order)| (text.to_string(), *order))
1109                    .collect(),
1110            )
1111        })
1112        .collect();
1113    pretty_assertions::assert_eq!(actual, expected)
1114}
1115
1116#[track_caller]
1117fn assert_definitions(definitions: &[LocationLink], first_lines: &[&str], cx: &mut TestAppContext) {
1118    let actual_first_lines = definitions
1119        .iter()
1120        .map(|definition| {
1121            definition.target.buffer.read_with(cx, |buffer, _| {
1122                let mut start = definition.target.range.start.to_point(&buffer);
1123                start.column = 0;
1124                let end = Point::new(start.row, buffer.line_len(start.row));
1125                buffer
1126                    .text_for_range(start..end)
1127                    .collect::<String>()
1128                    .trim()
1129                    .to_string()
1130            })
1131        })
1132        .collect::<Vec<String>>();
1133
1134    assert_eq!(actual_first_lines, first_lines);
1135}
1136
1137fn format_excerpts(buffer: &Buffer, excerpts: &[RelatedExcerpt]) -> String {
1138    let mut output = String::new();
1139    let file_line_count = buffer.max_point().row;
1140    let mut current_row = 0;
1141    for excerpt in excerpts {
1142        if excerpt.text.is_empty() {
1143            continue;
1144        }
1145        if current_row < excerpt.row_range.start {
1146            writeln!(&mut output, "…").unwrap();
1147        }
1148        current_row = excerpt.row_range.start;
1149
1150        for line in excerpt.text.to_string().lines() {
1151            output.push_str(line);
1152            output.push('\n');
1153            current_row += 1;
1154        }
1155    }
1156    if current_row < file_line_count {
1157        writeln!(&mut output, "…").unwrap();
1158    }
1159    output
1160}
1161
Served at tenant.openagents/omega Member data and write actions are omitted.