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}