use super::*;use crate::assemble_excerpts::assemble_excerpt_ranges;use futures::channel::mpsc::UnboundedReceiver;use gpui::TestAppContext;use indoc::indoc;use language::{Point, ToPoint as _, rust_lang};use lsp::FakeLanguageServer;use project::{FakeFs, LocationLink, Project, ProjectPath};use serde_json::json;use settings::SettingsStore;use std::fmt::Write as _;use util::rel_path::rel_path;use util::{path, test::marked_text_ranges}; #[gpui::test]async fn test_edit_prediction_context(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx)); related_excerpt_store.update(cx, |store, cx| { let position = { let buffer = buffer.read(cx); let offset = buffer.text().find("todo").unwrap(); buffer.anchor_before(offset) }; store.set_identifier_line_count(0); store.refresh(buffer.clone(), position, cx); }); cx.executor().advance_clock(DEBOUNCE_DURATION); related_excerpt_store.update(cx, |store, cx| { let excerpts = store.related_files(cx); assert_related_files( &excerpts, &[ ( "root/src/person.rs", &[ indoc! {" pub struct Person { first_name: String, last_name: String, email: String, age: u32, } impl Person { pub fn get_first_name(&self) -> &str { &self.first_name }"}, "}", ], ), ( "root/src/company.rs", &[indoc! {" pub struct Company { owner: Arc<Person>, address: Address, }"}], ), ( "root/src/main.rs", &[ indoc! {" pub struct Session { company: Arc<Company>, } impl Session { pub fn set_company(&mut self, company: Arc<Company>) {"}, indoc! {" } }"}, ], ), ], ); }); let company_buffer = related_excerpt_store.update(cx, |store, cx| { store .related_files_with_buffers(cx) .find(|(file, _)| file.path.to_str() == Some("root/src/company.rs")) .map(|(_, buffer)| buffer) .expect("company.rs buffer not found") }); company_buffer.update(cx, |buffer, cx| { let text = buffer.text(); let insert_pos = text.find("address: Address,").unwrap() + "address: Address,".len(); buffer.edit([(insert_pos..insert_pos, "\n name: String,")], None, cx); }); related_excerpt_store.update(cx, |store, cx| { let excerpts = store.related_files(cx); assert_related_files( &excerpts, &[ ( "root/src/person.rs", &[ indoc! {" pub struct Person { first_name: String, last_name: String, email: String, age: u32, } impl Person { pub fn get_first_name(&self) -> &str { &self.first_name }"}, "}", ], ), ( "root/src/company.rs", &[indoc! {" pub struct Company { owner: Arc<Person>, address: Address, name: String, }"}], ), ( "root/src/main.rs", &[ indoc! {" pub struct Session { company: Arc<Company>, } impl Session { pub fn set_company(&mut self, company: Arc<Company>) {"}, indoc! {" } }"}, ], ), ], ); });} #[gpui::test]async fn test_assemble_excerpts(cx: &mut TestAppContext) { let table = [ ( indoc! {r#" struct User { first_name: String, «last_name»: String, age: u32, email: String, create_at: Instant, } impl User { pub fn first_name(&self) -> String { self.first_name.clone() } pub fn full_name(&self) -> String { « format!("{} {}", self.first_name, self.last_name) » } } "#}, indoc! {r#" struct User { first_name: String, last_name: String, … } impl User { … pub fn full_name(&self) -> String { format!("{} {}", self.first_name, self.last_name) } } "#}, ), ( indoc! {r#" struct «User» { first_name: String, last_name: String, age: u32, } impl User { // methods } "#}, indoc! {r#" struct User { first_name: String, last_name: String, age: u32, } … "#}, ), ( indoc! {r#" trait «FooProvider» { const NAME: &'static str; fn provide_foo(&self, id: usize) -> Foo; fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> { ids.iter() .map(|id| self.provide_foo(*id)) .collect() } fn sync(&self); } "# }, indoc! {r#" trait FooProvider { const NAME: &'static str; fn provide_foo(&self, id: usize) -> Foo; fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> { … } fn sync(&self); } "#}, ), ( indoc! {r#" trait «Something» { fn method1(&self, id: usize) -> Foo; fn method2(&self, ids: &[usize]) -> Vec<Foo> { struct Helper1 { field1: usize, } struct Helper2 { field2: usize, } struct Helper3 { filed2: usize, } } fn sync(&self); } "# }, indoc! {r#" trait Something { fn method1(&self, id: usize) -> Foo; fn method2(&self, ids: &[usize]) -> Vec<Foo> { … } fn sync(&self); } "#}, ), ]; for (input, expected_output) in table { let (input, ranges) = marked_text_ranges(&input, false); let buffer = cx.new(|cx| Buffer::local(input, cx).with_language(rust_lang(), cx)); buffer .read_with(cx, |buffer, _| buffer.parsing_idle()) .await; buffer.read_with(cx, |buffer, _cx| { let ranges: Vec<(Range<Point>, usize)> = ranges .into_iter() .map(|range| (range.to_point(&buffer), 0)) .collect(); let assembled = assemble_excerpt_ranges(&buffer.snapshot(), ranges); let excerpts: Vec<RelatedExcerpt> = assembled .into_iter() .map(|(row_range, order)| { let start = Point::new(row_range.start, 0); let end = Point::new(row_range.end, buffer.line_len(row_range.end)); RelatedExcerpt { row_range, text: buffer.text_for_range(start..end).collect::<String>().into(), order, context_source: ContextSource::Lsp, } }) .collect(); let output = format_excerpts(buffer, &excerpts); assert_eq!(output, expected_output); }); }} #[gpui::test]async fn test_fake_definition_lsp(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text()); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("Address {").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub struct Address {"], cx); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("State::CA").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub enum State {"], cx); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("to_string()").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub fn to_string(&self) -> String {"], cx);} #[gpui::test]async fn test_fake_type_definition_lsp(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text()); // Type definition on a type name returns its own definition // (same as regular definition) let type_defs = project .update(cx, |project, cx| { let offset = buffer_text.find("Address {").expect("Address { not found"); project.type_definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Address {"], cx); // Type definition on a field resolves through the type annotation. // company.rs has `owner: Arc<Person>`, so type-def of `owner` → Person. let (company_buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/company.rs"), cx) }) .await .unwrap(); cx.run_until_parked(); let company_text = company_buffer.read_with(cx, |buffer, _| buffer.text()); let type_defs = project .update(cx, |project, cx| { let offset = company_text.find("owner").expect("owner not found"); project.type_definitions(&company_buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Person {"], cx); // Type definition on another field: `address: Address` → Address. let type_defs = project .update(cx, |project, cx| { let offset = company_text.find("address").expect("address not found"); project.type_definitions(&company_buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Address {"], cx); // Type definition on a lowercase name with no type annotation returns empty. let type_defs = project .update(cx, |project, cx| { let offset = buffer_text.find("main").expect("main not found"); project.type_definitions(&buffer, offset, cx) }) .await; let is_empty = match &type_defs { Ok(Some(defs)) => defs.is_empty(), Ok(None) => true, Err(_) => false, }; assert!(is_empty, "expected no type definitions for `main`");} #[gpui::test]async fn test_type_definitions_in_related_files(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( path!("/root"), json!({ "src": { "config.rs": indoc! {r#" pub struct Config { debug: bool, verbose: bool, } "#}, "widget.rs": indoc! {r#" use super::config::Config; pub struct Widget { config: Config, name: String, } impl Widget { pub fn render(&self) { if self.config.debug { println!("debug mode"); } } } "#}, }, }), ) .await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/widget.rs"), cx) }) .await .unwrap();Lines 1–500 of 1160. Load more
use super::*; use crate::assemble_excerpts::assemble_excerpt_ranges; use futures::channel::mpsc::UnboundedReceiver; use gpui::TestAppContext; use indoc::indoc; use language::{Point, ToPoint as _, rust_lang}; use lsp::FakeLanguageServer; use project::{FakeFs, LocationLink, Project, ProjectPath}; use serde_json::json; use settings::SettingsStore; use std::fmt::Write as _; use util::rel_path::rel_path; use util::{path, test::marked_text_ranges}; #[gpui::test] async fn test_edit_prediction_context(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx)); related_excerpt_store.update(cx, |store, cx| { let position = { let buffer = buffer.read(cx); let offset = buffer.text().find("todo").unwrap(); buffer.anchor_before(offset) }; store.set_identifier_line_count(0); store.refresh(buffer.clone(), position, cx); }); cx.executor().advance_clock(DEBOUNCE_DURATION); related_excerpt_store.update(cx, |store, cx| { let excerpts = store.related_files(cx); assert_related_files( &excerpts, &[ ( "root/src/person.rs", &[ indoc! {" pub struct Person { first_name: String, last_name: String, email: String, age: u32, } impl Person { pub fn get_first_name(&self) -> &str { &self.first_name }"}, "}", ], ), ( "root/src/company.rs", &[indoc! {" pub struct Company { owner: Arc<Person>, address: Address, }"}], ), ( "root/src/main.rs", &[ indoc! {" pub struct Session { company: Arc<Company>, } impl Session { pub fn set_company(&mut self, company: Arc<Company>) {"}, indoc! {" } }"}, ], ), ], ); }); let company_buffer = related_excerpt_store.update(cx, |store, cx| { store .related_files_with_buffers(cx) .find(|(file, _)| file.path.to_str() == Some("root/src/company.rs")) .map(|(_, buffer)| buffer) .expect("company.rs buffer not found") }); company_buffer.update(cx, |buffer, cx| { let text = buffer.text(); let insert_pos = text.find("address: Address,").unwrap() + "address: Address,".len(); buffer.edit([(insert_pos..insert_pos, "\n name: String,")], None, cx); }); related_excerpt_store.update(cx, |store, cx| { let excerpts = store.related_files(cx); assert_related_files( &excerpts, &[ ( "root/src/person.rs", &[ indoc! {" pub struct Person { first_name: String, last_name: String, email: String, age: u32, } impl Person { pub fn get_first_name(&self) -> &str { &self.first_name }"}, "}", ], ), ( "root/src/company.rs", &[indoc! {" pub struct Company { owner: Arc<Person>, address: Address, name: String, }"}], ), ( "root/src/main.rs", &[ indoc! {" pub struct Session { company: Arc<Company>, } impl Session { pub fn set_company(&mut self, company: Arc<Company>) {"}, indoc! {" } }"}, ], ), ], ); }); } #[gpui::test] async fn test_assemble_excerpts(cx: &mut TestAppContext) { let table = [ ( indoc! {r#" struct User { first_name: String, «last_name»: String, age: u32, email: String, create_at: Instant, } impl User { pub fn first_name(&self) -> String { self.first_name.clone() } pub fn full_name(&self) -> String { « format!("{} {}", self.first_name, self.last_name) » } } "#}, indoc! {r#" struct User { first_name: String, last_name: String, … } impl User { … pub fn full_name(&self) -> String { format!("{} {}", self.first_name, self.last_name) } } "#}, ), ( indoc! {r#" struct «User» { first_name: String, last_name: String, age: u32, } impl User { // methods } "#}, indoc! {r#" struct User { first_name: String, last_name: String, age: u32, } … "#}, ), ( indoc! {r#" trait «FooProvider» { const NAME: &'static str; fn provide_foo(&self, id: usize) -> Foo; fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> { ids.iter() .map(|id| self.provide_foo(*id)) .collect() } fn sync(&self); } "# }, indoc! {r#" trait FooProvider { const NAME: &'static str; fn provide_foo(&self, id: usize) -> Foo; fn provide_foo_batched(&self, ids: &[usize]) -> Vec<Foo> { … } fn sync(&self); } "#}, ), ( indoc! {r#" trait «Something» { fn method1(&self, id: usize) -> Foo; fn method2(&self, ids: &[usize]) -> Vec<Foo> { struct Helper1 { field1: usize, } struct Helper2 { field2: usize, } struct Helper3 { filed2: usize, } } fn sync(&self); } "# }, indoc! {r#" trait Something { fn method1(&self, id: usize) -> Foo; fn method2(&self, ids: &[usize]) -> Vec<Foo> { … } fn sync(&self); } "#}, ), ]; for (input, expected_output) in table { let (input, ranges) = marked_text_ranges(&input, false); let buffer = cx.new(|cx| Buffer::local(input, cx).with_language(rust_lang(), cx)); buffer .read_with(cx, |buffer, _| buffer.parsing_idle()) .await; buffer.read_with(cx, |buffer, _cx| { let ranges: Vec<(Range<Point>, usize)> = ranges .into_iter() .map(|range| (range.to_point(&buffer), 0)) .collect(); let assembled = assemble_excerpt_ranges(&buffer.snapshot(), ranges); let excerpts: Vec<RelatedExcerpt> = assembled .into_iter() .map(|(row_range, order)| { let start = Point::new(row_range.start, 0); let end = Point::new(row_range.end, buffer.line_len(row_range.end)); RelatedExcerpt { row_range, text: buffer.text_for_range(start..end).collect::<String>().into(), order, context_source: ContextSource::Lsp, } }) .collect(); let output = format_excerpts(buffer, &excerpts); assert_eq!(output, expected_output); }); } } #[gpui::test] async fn test_fake_definition_lsp(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text()); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("Address {").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub struct Address {"], cx); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("State::CA").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub enum State {"], cx); let definitions = project .update(cx, |project, cx| { let offset = buffer_text.find("to_string()").unwrap(); project.definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&definitions, &["pub fn to_string(&self) -> String {"], cx); } #[gpui::test] async fn test_fake_type_definition_lsp(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree(path!("/root"), test_project_1()).await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) }) .await .unwrap(); let _server = servers.next().await.unwrap(); cx.run_until_parked(); let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text()); // Type definition on a type name returns its own definition // (same as regular definition) let type_defs = project .update(cx, |project, cx| { let offset = buffer_text.find("Address {").expect("Address { not found"); project.type_definitions(&buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Address {"], cx); // Type definition on a field resolves through the type annotation. // company.rs has `owner: Arc<Person>`, so type-def of `owner` → Person. let (company_buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/company.rs"), cx) }) .await .unwrap(); cx.run_until_parked(); let company_text = company_buffer.read_with(cx, |buffer, _| buffer.text()); let type_defs = project .update(cx, |project, cx| { let offset = company_text.find("owner").expect("owner not found"); project.type_definitions(&company_buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Person {"], cx); // Type definition on another field: `address: Address` → Address. let type_defs = project .update(cx, |project, cx| { let offset = company_text.find("address").expect("address not found"); project.type_definitions(&company_buffer, offset, cx) }) .await .unwrap() .unwrap(); assert_definitions(&type_defs, &["pub struct Address {"], cx); // Type definition on a lowercase name with no type annotation returns empty. let type_defs = project .update(cx, |project, cx| { let offset = buffer_text.find("main").expect("main not found"); project.type_definitions(&buffer, offset, cx) }) .await; let is_empty = match &type_defs { Ok(Some(defs)) => defs.is_empty(), Ok(None) => true, Err(_) => false, }; assert!(is_empty, "expected no type definitions for `main`"); } #[gpui::test] async fn test_type_definitions_in_related_files(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( path!("/root"), json!({ "src": { "config.rs": indoc! {r#" pub struct Config { debug: bool, verbose: bool, } "#}, "widget.rs": indoc! {r#" use super::config::Config; pub struct Widget { config: Config, name: String, } impl Widget { pub fn render(&self) { if self.config.debug { println!("debug mode"); } } } "#}, }, }), ) .await; let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let mut servers = setup_fake_lsp(&project, cx); let (buffer, _handle) = project .update(cx, |project, cx| { project.open_local_buffer_with_lsp(path!("/root/src/widget.rs"), cx) }) .await .unwrap();