edit_prediction_context_tests.rs

  1use super::*;
  2use futures::channel::mpsc::UnboundedReceiver;
  3use gpui::TestAppContext;
  4use indoc::indoc;
  5use language::{Language, LanguageConfig, LanguageMatcher, Point, ToPoint as _, tree_sitter_rust};
  6use lsp::FakeLanguageServer;
  7use project::{FakeFs, LocationLink, Project};
  8use serde_json::json;
  9use settings::SettingsStore;
 10use std::sync::Arc;
 11use util::path;
 12
 13#[gpui::test]
 14async fn test_edit_prediction_context(cx: &mut TestAppContext) {
 15    init_test(cx);
 16    let fs = FakeFs::new(cx.executor());
 17    fs.insert_tree(path!("/root"), test_project_1()).await;
 18
 19    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
 20    let mut servers = setup_fake_lsp(&project, cx);
 21
 22    let (buffer, _handle) = project
 23        .update(cx, |project, cx| {
 24            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
 25        })
 26        .await
 27        .unwrap();
 28
 29    let _server = servers.next().await.unwrap();
 30    cx.run_until_parked();
 31
 32    let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx));
 33    related_excerpt_store.update(cx, |store, cx| {
 34        let position = {
 35            let buffer = buffer.read(cx);
 36            let offset = buffer.text().find("todo").unwrap();
 37            buffer.anchor_before(offset)
 38        };
 39
 40        store.refresh(buffer.clone(), position, cx);
 41    });
 42
 43    cx.executor().advance_clock(DEBOUNCE_DURATION);
 44    related_excerpt_store.update(cx, |store, _| {
 45        let excerpts = store.related_files();
 46        assert_related_files(
 47            &excerpts,
 48            &[
 49                (
 50                    "src/company.rs",
 51                    &[indoc! {"
 52                        pub struct Company {
 53                            owner: Arc<Person>,
 54                            address: Address,
 55                        }"}],
 56                ),
 57                (
 58                    "src/main.rs",
 59                    &[
 60                        indoc! {"
 61                        pub struct Session {
 62                            company: Arc<Company>,
 63                        }
 64
 65                        impl Session {
 66                            pub fn set_company(&mut self, company: Arc<Company>) {"},
 67                        indoc! {"
 68                            }
 69                        }"},
 70                    ],
 71                ),
 72                (
 73                    "src/person.rs",
 74                    &[
 75                        indoc! {"
 76                        impl Person {
 77                            pub fn get_first_name(&self) -> &str {
 78                                &self.first_name
 79                            }"},
 80                        "}",
 81                    ],
 82                ),
 83            ],
 84        );
 85    });
 86}
 87
 88#[gpui::test]
 89async fn test_fake_definition_lsp(cx: &mut TestAppContext) {
 90    init_test(cx);
 91
 92    let fs = FakeFs::new(cx.executor());
 93    fs.insert_tree(path!("/root"), test_project_1()).await;
 94
 95    let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await;
 96    let mut servers = setup_fake_lsp(&project, cx);
 97
 98    let (buffer, _handle) = project
 99        .update(cx, |project, cx| {
100            project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx)
101        })
102        .await
103        .unwrap();
104
105    let _server = servers.next().await.unwrap();
106    cx.run_until_parked();
107
108    let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text());
109
110    let definitions = project
111        .update(cx, |project, cx| {
112            let offset = buffer_text.find("Address {").unwrap();
113            project.definitions(&buffer, offset, cx)
114        })
115        .await
116        .unwrap()
117        .unwrap();
118    assert_definitions(&definitions, &["pub struct Address {"], cx);
119
120    let definitions = project
121        .update(cx, |project, cx| {
122            let offset = buffer_text.find("State::CA").unwrap();
123            project.definitions(&buffer, offset, cx)
124        })
125        .await
126        .unwrap()
127        .unwrap();
128    assert_definitions(&definitions, &["pub enum State {"], cx);
129
130    let definitions = project
131        .update(cx, |project, cx| {
132            let offset = buffer_text.find("to_string()").unwrap();
133            project.definitions(&buffer, offset, cx)
134        })
135        .await
136        .unwrap()
137        .unwrap();
138    assert_definitions(&definitions, &["pub fn to_string(&self) -> String {"], cx);
139}
140
141fn init_test(cx: &mut TestAppContext) {
142    let settings_store = cx.update(|cx| SettingsStore::test(cx));
143    cx.set_global(settings_store);
144    env_logger::try_init().ok();
145}
146
147fn setup_fake_lsp(
148    project: &Entity<Project>,
149    cx: &mut TestAppContext,
150) -> UnboundedReceiver<FakeLanguageServer> {
151    let (language_registry, fs) = project.read_with(cx, |project, _| {
152        (project.languages().clone(), project.fs().clone())
153    });
154    let language = rust_lang();
155    language_registry.add(language.clone());
156    fake_definition_lsp::register_fake_definition_server(&language_registry, language, fs)
157}
158
159fn test_project_1() -> serde_json::Value {
160    let person_rs = indoc! {r#"
161        pub struct Person {
162            first_name: String,
163            last_name: String,
164            email: String,
165            age: u32,
166        }
167
168        impl Person {
169            pub fn get_first_name(&self) -> &str {
170                &self.first_name
171            }
172
173            pub fn get_last_name(&self) -> &str {
174                &self.last_name
175            }
176
177            pub fn get_email(&self) -> &str {
178                &self.email
179            }
180
181            pub fn get_age(&self) -> u32 {
182                self.age
183            }
184        }
185    "#};
186
187    let address_rs = indoc! {r#"
188        pub struct Address {
189            street: String,
190            city: String,
191            state: State,
192            zip: u32,
193        }
194
195        pub enum State {
196            CA,
197            OR,
198            WA,
199            TX,
200            // ...
201        }
202
203        impl Address {
204            pub fn get_street(&self) -> &str {
205                &self.street
206            }
207
208            pub fn get_city(&self) -> &str {
209                &self.city
210            }
211
212            pub fn get_state(&self) -> State {
213                self.state
214            }
215
216            pub fn get_zip(&self) -> u32 {
217                self.zip
218            }
219        }
220    "#};
221
222    let company_rs = indoc! {r#"
223        use super::person::Person;
224        use super::address::Address;
225
226        pub struct Company {
227            owner: Arc<Person>,
228            address: Address,
229        }
230
231        impl Company {
232            pub fn get_owner(&self) -> &Person {
233                &self.owner
234            }
235
236            pub fn get_address(&self) -> &Address {
237                &self.address
238            }
239
240            pub fn to_string(&self) -> String {
241                format!("{} ({})", self.owner.first_name, self.address.city)
242            }
243        }
244    "#};
245
246    let main_rs = indoc! {r#"
247        use std::sync::Arc;
248        use super::person::Person;
249        use super::address::Address;
250        use super::company::Company;
251
252        pub struct Session {
253            company: Arc<Company>,
254        }
255
256        impl Session {
257            pub fn set_company(&mut self, company: Arc<Company>) {
258                self.company = company;
259                if company.owner != self.company.owner {
260                    log("new owner", company.owner.get_first_name()); todo();
261                }
262            }
263        }
264
265        fn main() {
266            let company = Company {
267                owner: Arc::new(Person {
268                    first_name: "John".to_string(),
269                    last_name: "Doe".to_string(),
270                    email: "john@example.com".to_string(),
271                    age: 30,
272                }),
273                address: Address {
274                    street: "123 Main St".to_string(),
275                    city: "Anytown".to_string(),
276                    state: State::CA,
277                    zip: 12345,
278                },
279            };
280
281            println!("Company: {}", company.to_string());
282        }
283    "#};
284
285    json!({
286        "src": {
287            "person.rs": person_rs,
288            "address.rs": address_rs,
289            "company.rs": company_rs,
290            "main.rs": main_rs,
291        },
292    })
293}
294
295fn assert_related_files(actual_files: &[RelatedFile], expected_files: &[(&str, &[&str])]) {
296    let actual_files = actual_files
297        .iter()
298        .map(|file| {
299            let excerpts = file
300                .excerpts
301                .iter()
302                .map(|excerpt| excerpt.text.to_string())
303                .collect::<Vec<_>>();
304            (file.path.path.as_unix_str(), excerpts)
305        })
306        .collect::<Vec<_>>();
307    let expected_excerpts = expected_files
308        .iter()
309        .map(|(path, texts)| {
310            (
311                *path,
312                texts
313                    .iter()
314                    .map(|line| line.to_string())
315                    .collect::<Vec<_>>(),
316            )
317        })
318        .collect::<Vec<_>>();
319    pretty_assertions::assert_eq!(actual_files, expected_excerpts)
320}
321
322fn assert_definitions(definitions: &[LocationLink], first_lines: &[&str], cx: &mut TestAppContext) {
323    let actual_first_lines = definitions
324        .iter()
325        .map(|definition| {
326            definition.target.buffer.read_with(cx, |buffer, _| {
327                let mut start = definition.target.range.start.to_point(&buffer);
328                start.column = 0;
329                let end = Point::new(start.row, buffer.line_len(start.row));
330                buffer
331                    .text_for_range(start..end)
332                    .collect::<String>()
333                    .trim()
334                    .to_string()
335            })
336        })
337        .collect::<Vec<String>>();
338
339    assert_eq!(actual_first_lines, first_lines);
340}
341
342pub(crate) fn rust_lang() -> Arc<Language> {
343    Arc::new(
344        Language::new(
345            LanguageConfig {
346                name: "Rust".into(),
347                matcher: LanguageMatcher {
348                    path_suffixes: vec!["rs".to_string()],
349                    first_line_pattern: None,
350                },
351                ..Default::default()
352            },
353            Some(tree_sitter_rust::LANGUAGE.into()),
354        )
355        .with_highlights_query(include_str!("../../languages/src/rust/highlights.scm"))
356        .unwrap()
357        .with_outline_query(include_str!("../../languages/src/rust/outline.scm"))
358        .unwrap(),
359    )
360}