From 772201449713fbefad6c42b9ce545a5bb2d7499b Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 30 Mar 2023 17:47:53 -0400 Subject: [PATCH] fix(lsp): include all diagnosable documents on initialize (#17979) Closes https://github.com/denoland/vscode_deno/issues/797 Closes https://github.com/denoland/deno/issues/11190 Closes https://github.com/denoland/vscode_deno/issues/811 Closes https://github.com/denoland/vscode_deno/issues/761 Closes https://github.com/denoland/vscode_deno/issues/585 Closes https://github.com/denoland/vscode_deno/issues/561 Closes https://github.com/denoland/vscode_deno/issues/410 --- cli/cache/http_cache.rs | 3 +- cli/lsp/config.rs | 105 ++++++++ cli/lsp/documents.rs | 259 +++++++++++++++++-- cli/lsp/language_server.rs | 1 + cli/lsp/tsc.rs | 32 +-- cli/tests/integration/lsp_tests.rs | 385 +++++++++++++++++++++------- cli/tests/integration/repl_tests.rs | 92 +++++-- cli/tests/integration/run_tests.rs | 150 +++++------ cli/tests/integration/task_tests.rs | 11 +- cli/tests/integration/test_tests.rs | 5 +- test_util/src/builders.rs | 7 +- test_util/src/lib.rs | 10 +- 12 files changed, 813 insertions(+), 247 deletions(-) diff --git a/cli/cache/http_cache.rs b/cli/cache/http_cache.rs index 2e784765e2..b10c597561 100644 --- a/cli/cache/http_cache.rs +++ b/cli/cache/http_cache.rs @@ -11,7 +11,6 @@ use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; use deno_core::url::Url; -use log::error; use std::fs; use std::fs::File; use std::io; @@ -42,7 +41,7 @@ fn base_url_to_filename(url: &Url) -> Option { } "data" | "blob" => (), scheme => { - error!("Don't know how to create cache name for scheme: {}", scheme); + log::debug!("Don't know how to create cache name for scheme: {}", scheme); return None; } }; diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 60b975a44b..418ffdc483 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -485,6 +485,31 @@ impl Config { .unwrap_or_else(|| self.settings.workspace.enable) } + /// Gets the root directories or file paths based on the workspace config. + pub fn enabled_root_urls(&self) -> Vec { + let mut urls: Vec = Vec::new(); + + if !self.settings.workspace.enable && self.enabled_paths.is_empty() { + // do not return any urls when disabled + return urls; + } + + for (workspace, enabled_paths) in &self.enabled_paths { + if !enabled_paths.is_empty() { + urls.extend(enabled_paths.iter().cloned()); + } else { + urls.push(workspace.clone()); + } + } + if urls.is_empty() { + if let Some(root_dir) = &self.root_uri { + urls.push(root_dir.clone()) + } + } + sort_and_remove_non_leaf_urls(&mut urls); + urls + } + pub fn specifier_code_lens_test(&self, specifier: &ModuleSpecifier) -> bool { let value = self .settings @@ -621,6 +646,21 @@ impl Config { } } +/// Removes any URLs that are a descendant of another URL in the collection. +fn sort_and_remove_non_leaf_urls(dirs: &mut Vec) { + if dirs.is_empty() { + return; + } + + dirs.sort(); + for i in (0..dirs.len() - 1).rev() { + let prev = &dirs[i + 1]; + if prev.as_str().starts_with(dirs[i].as_str()) { + dirs.remove(i + 1); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -785,4 +825,69 @@ mod tests { WorkspaceSettings::default() ); } + + #[test] + fn test_sort_and_remove_non_leaf_urls() { + fn run_test(dirs: Vec<&str>, expected_output: Vec<&str>) { + let mut dirs = dirs + .into_iter() + .map(|dir| Url::parse(dir).unwrap()) + .collect(); + sort_and_remove_non_leaf_urls(&mut dirs); + let dirs: Vec<_> = dirs.iter().map(|dir| dir.as_str()).collect(); + assert_eq!(dirs, expected_output); + } + + run_test( + vec![ + "file:///test/asdf/test/asdf/", + "file:///test/asdf/", + "file:///test/asdf/", + "file:///testing/456/893/", + "file:///testing/456/893/test/", + ], + vec!["file:///test/asdf/", "file:///testing/456/893/"], + ); + run_test(vec![], vec![]); + } + + #[test] + fn config_enabled_root_urls() { + let mut config = Config::new(); + let root_dir = Url::parse("file:///example/").unwrap(); + config.root_uri = Some(root_dir.clone()); + config.settings.workspace.enable = false; + config.settings.workspace.enable_paths = Vec::new(); + assert_eq!(config.enabled_root_urls(), vec![]); + + config.settings.workspace.enable = true; + assert_eq!(config.enabled_root_urls(), vec![root_dir]); + + config.settings.workspace.enable = false; + let root_dir1 = Url::parse("file:///root1/").unwrap(); + let root_dir2 = Url::parse("file:///root2/").unwrap(); + let root_dir3 = Url::parse("file:///root3/").unwrap(); + config.enabled_paths = HashMap::from([ + ( + root_dir1.clone(), + vec![ + root_dir1.join("sub_dir").unwrap(), + root_dir1.join("sub_dir/other").unwrap(), + root_dir1.join("test.ts").unwrap(), + ], + ), + (root_dir2.clone(), vec![root_dir2.join("other.ts").unwrap()]), + (root_dir3.clone(), vec![]), + ]); + + assert_eq!( + config.enabled_root_urls(), + vec![ + root_dir1.join("sub_dir").unwrap(), + root_dir1.join("test.ts").unwrap(), + root_dir2.join("other.ts").unwrap(), + root_dir3 + ] + ); + } } diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index aa47faf623..0c27893a79 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -43,11 +43,13 @@ use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::PackageJson; use deno_runtime::permissions::PermissionsContainer; use indexmap::IndexMap; +use lsp::Url; use once_cell::sync::Lazy; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; use std::fs; +use std::fs::ReadDir; use std::ops::Range; use std::path::Path; use std::path::PathBuf; @@ -775,18 +777,6 @@ impl FileSystemDocuments { self.docs.insert(specifier.clone(), doc.clone()); Some(doc) } - - pub fn refresh_dependencies( - &mut self, - resolver: &dyn deno_graph::source::Resolver, - ) { - for doc in self.docs.values_mut() { - if let Some(new_doc) = doc.maybe_with_new_resolver(resolver) { - *doc = new_doc; - } - } - self.dirty = true; - } } fn get_document_path( @@ -1166,6 +1156,7 @@ impl Documents { pub fn update_config( &mut self, + root_urls: Vec, maybe_import_map: Option>, maybe_config_file: Option<&ConfigFile>, maybe_package_json: Option<&PackageJson>, @@ -1173,11 +1164,18 @@ impl Documents { npm_resolution: NpmResolution, ) { fn calculate_resolver_config_hash( + root_urls: &[Url], maybe_import_map: Option<&import_map::ImportMap>, maybe_jsx_config: Option<&JsxImportSourceConfig>, maybe_package_json_deps: Option<&PackageJsonDeps>, ) -> u64 { let mut hasher = FastInsecureHasher::default(); + hasher.write_hashable(&{ + // ensure these are sorted (they should be, but this is a safeguard) + let mut root_urls = root_urls.to_vec(); + root_urls.sort_unstable(); + root_urls + }); if let Some(import_map) = maybe_import_map { hasher.write_str(&import_map.to_json()); hasher.write_str(import_map.base_url().as_str()); @@ -1193,6 +1191,7 @@ impl Documents { let maybe_jsx_config = maybe_config_file.and_then(|cf| cf.to_maybe_jsx_import_source_config()); let new_resolver_config_hash = calculate_resolver_config_hash( + &root_urls, maybe_import_map.as_deref(), maybe_jsx_config.as_ref(), maybe_package_json_deps.as_ref(), @@ -1232,21 +1231,51 @@ impl Documents { // only refresh the dependencies if the underlying configuration has changed if self.resolver_config_hash != new_resolver_config_hash { - self.refresh_dependencies(); + self.refresh_dependencies(root_urls); self.resolver_config_hash = new_resolver_config_hash; } self.dirty = true; } - fn refresh_dependencies(&mut self) { + fn refresh_dependencies(&mut self, root_urls: Vec) { let resolver = self.resolver.as_graph_resolver(); for doc in self.open_docs.values_mut() { if let Some(new_doc) = doc.maybe_with_new_resolver(resolver) { *doc = new_doc; } } - self.file_system_docs.lock().refresh_dependencies(resolver); + + // update the file system documents + let mut fs_docs = self.file_system_docs.lock(); + let mut not_found_docs = + fs_docs.docs.keys().cloned().collect::>(); + let open_docs = &mut self.open_docs; + + for specifier in PreloadDocumentFinder::from_root_urls(&root_urls) { + // mark this document as having been found + not_found_docs.remove(&specifier); + + if !open_docs.contains_key(&specifier) + && !fs_docs.docs.contains_key(&specifier) + { + fs_docs.refresh_document(&self.cache, resolver, &specifier); + } else { + // update the existing entry to have the new resolver + if let Some(doc) = fs_docs.docs.get_mut(&specifier) { + if let Some(new_doc) = doc.maybe_with_new_resolver(resolver) { + *doc = new_doc; + } + } + } + } + + // clean up and remove any documents that weren't found + for uri in not_found_docs { + fs_docs.docs.remove(&uri); + } + + fs_docs.dirty = true; } /// Iterate through the documents, building a map where the key is a unique @@ -1478,12 +1507,150 @@ fn analyze_module( } } +/// Iterator that finds documents that can be preloaded into +/// the LSP on startup. +struct PreloadDocumentFinder { + pending_dirs: Vec, + pending_files: Vec, + current_entries: Option, +} + +impl PreloadDocumentFinder { + pub fn from_root_urls(root_urls: &Vec) -> Self { + let mut finder = PreloadDocumentFinder { + pending_dirs: Default::default(), + pending_files: Default::default(), + current_entries: Default::default(), + }; + for root_url in root_urls { + if let Ok(path) = root_url.to_file_path() { + if path.is_dir() { + finder.pending_dirs.push(path); + } else { + finder.pending_files.push(path); + } + } + } + finder + } + + fn read_next_file_entry(&mut self) -> Option { + fn is_discoverable_dir(dir_path: &Path) -> bool { + if let Some(dir_name) = dir_path.file_name() { + let dir_name = dir_name.to_string_lossy().to_lowercase(); + !matches!(dir_name.as_str(), "node_modules" | ".git") + } else { + false + } + } + + if let Some(mut entries) = self.current_entries.take() { + while let Some(entry) = entries.next() { + if let Ok(entry) = entry { + let path = entry.path(); + if let Ok(file_type) = entry.file_type() { + if file_type.is_dir() && is_discoverable_dir(&path) { + self.pending_dirs.push(path); + } else if file_type.is_file() { + if let Some(specifier) = Self::get_valid_specifier(&path) { + // restore the next entries for next time + self.current_entries = Some(entries); + return Some(specifier); + } + } + } + } + } + } + + None + } + + fn get_valid_specifier(path: &Path) -> Option { + fn is_discoverable_file(file_path: &Path) -> bool { + // Don't auto-discover minified files as they are likely to be very large + // and likely not to have dependencies on code outside them that would + // be useful in the LSP + if let Some(file_name) = file_path.file_name() { + let file_name = file_name.to_string_lossy().to_lowercase(); + !file_name.as_str().contains(".min.") + } else { + false + } + } + + fn is_discoverable_media_type(media_type: MediaType) -> bool { + match media_type { + MediaType::JavaScript + | MediaType::Jsx + | MediaType::Mjs + | MediaType::Cjs + | MediaType::TypeScript + | MediaType::Mts + | MediaType::Cts + | MediaType::Dts + | MediaType::Dmts + | MediaType::Dcts + | MediaType::Tsx => true, + MediaType::Json // ignore because json never depends on other files + | MediaType::Wasm + | MediaType::SourceMap + | MediaType::TsBuildInfo + | MediaType::Unknown => false, + } + } + + let media_type = MediaType::from_path(path); + if is_discoverable_media_type(media_type) && is_discoverable_file(path) { + if let Ok(specifier) = ModuleSpecifier::from_file_path(path) { + return Some(specifier); + } + } + None + } + + fn queue_next_file_entries(&mut self) { + debug_assert!(self.current_entries.is_none()); + while let Some(dir_path) = self.pending_dirs.pop() { + if let Ok(entries) = fs::read_dir(&dir_path) { + self.current_entries = Some(entries); + break; + } + } + } +} + +impl Iterator for PreloadDocumentFinder { + type Item = ModuleSpecifier; + + fn next(&mut self) -> Option { + // drain the pending files + while let Some(path) = self.pending_files.pop() { + if let Some(specifier) = Self::get_valid_specifier(&path) { + return Some(specifier); + } + } + + // then go through the current entries and directories + while !self.pending_dirs.is_empty() || self.current_entries.is_some() { + match self.read_next_file_entry() { + Some(entry) => return Some(entry), + None => { + self.queue_next_file_entries(); + } + } + } + None + } +} + #[cfg(test)] mod tests { use crate::npm::NpmResolution; use super::*; use import_map::ImportMap; + use pretty_assertions::assert_eq; use test_util::TempDir; fn setup(temp_dir: &TempDir) -> (Documents, PathBuf) { @@ -1616,6 +1783,7 @@ console.log(b, "hello deno"); .unwrap(); documents.update_config( + vec![], Some(Arc::new(import_map)), None, None, @@ -1655,6 +1823,7 @@ console.log(b, "hello deno"); .unwrap(); documents.update_config( + vec![], Some(Arc::new(import_map)), None, None, @@ -1676,4 +1845,64 @@ console.log(b, "hello deno"); ); } } + + #[test] + pub fn test_pre_load_document_finder() { + let temp_dir = TempDir::new(); + temp_dir.create_dir_all("root1/node_modules/"); + temp_dir.write("root1/node_modules/mod.ts", ""); // no, node_modules + + temp_dir.create_dir_all("root1/sub_dir"); + temp_dir.create_dir_all("root1/.git"); + temp_dir.create_dir_all("root1/file.ts"); // no, directory + temp_dir.write("root1/mod1.ts", ""); // yes + temp_dir.write("root1/mod2.js", ""); // yes + temp_dir.write("root1/mod3.tsx", ""); // yes + temp_dir.write("root1/mod4.d.ts", ""); // yes + temp_dir.write("root1/mod5.jsx", ""); // yes + temp_dir.write("root1/mod6.mjs", ""); // yes + temp_dir.write("root1/mod7.mts", ""); // yes + temp_dir.write("root1/mod8.d.mts", ""); // yes + temp_dir.write("root1/other.json", ""); // no, json + temp_dir.write("root1/other.txt", ""); // no, text file + temp_dir.write("root1/other.wasm", ""); // no, don't load wasm + temp_dir.write("root1/sub_dir/mod.ts", ""); // yes + temp_dir.write("root1/sub_dir/data.min.ts", ""); // no, minified file + temp_dir.write("root1/.git/main.ts", ""); // no, .git folder + + temp_dir.create_dir_all("root2/folder"); + temp_dir.write("root2/file1.ts", ""); // yes, provided + temp_dir.write("root2/file2.ts", ""); // no, not provided + temp_dir.write("root2/folder/main.ts", ""); // yes, provided + + temp_dir.create_dir_all("root3/"); + temp_dir.write("root3/mod.ts", ""); // no, not provided + + let mut urls = PreloadDocumentFinder::from_root_urls(&vec![ + temp_dir.uri().join("root1/").unwrap(), + temp_dir.uri().join("root2/file1.ts").unwrap(), + temp_dir.uri().join("root2/folder/").unwrap(), + ]) + .collect::>(); + + // order doesn't matter + urls.sort(); + + assert_eq!( + urls, + vec![ + temp_dir.uri().join("root1/mod1.ts").unwrap(), + temp_dir.uri().join("root1/mod2.js").unwrap(), + temp_dir.uri().join("root1/mod3.tsx").unwrap(), + temp_dir.uri().join("root1/mod4.d.ts").unwrap(), + temp_dir.uri().join("root1/mod5.jsx").unwrap(), + temp_dir.uri().join("root1/mod6.mjs").unwrap(), + temp_dir.uri().join("root1/mod7.mts").unwrap(), + temp_dir.uri().join("root1/mod8.d.mts").unwrap(), + temp_dir.uri().join("root1/sub_dir/mod.ts").unwrap(), + temp_dir.uri().join("root2/file1.ts").unwrap(), + temp_dir.uri().join("root2/folder/main.ts").unwrap(), + ] + ); + } } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 164c9734f8..754b5c95cd 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -1138,6 +1138,7 @@ impl Inner { fn refresh_documents_config(&mut self) { self.documents.update_config( + self.config.enabled_root_urls(), self.maybe_import_map.clone(), self.maybe_config_file.as_ref(), self.maybe_package_json.as_ref(), diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index e846cc4963..f02668910a 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -2803,9 +2803,9 @@ fn op_respond(state: &mut OpState, args: Response) -> bool { fn op_script_names(state: &mut OpState) -> Vec { let state = state.borrow_mut::(); let documents = &state.state_snapshot.documents; - let open_docs = documents.documents(DocumentsFilter::OpenDiagnosable); - let mut result = Vec::new(); + let all_docs = documents.documents(DocumentsFilter::AllDiagnosable); let mut seen = HashSet::new(); + let mut result = Vec::new(); if documents.has_injected_types_node_package() { // ensure this is first so it resolves the node types first @@ -2822,23 +2822,17 @@ fn op_script_names(state: &mut OpState) -> Vec { } // finally include the documents and all their dependencies - for doc in &open_docs { - let specifier = doc.specifier(); - if seen.insert(specifier.as_str()) { - result.push(specifier.to_string()); - } - } - - // and then all their dependencies (do this after to avoid exists calls) - for doc in &open_docs { - for dep in doc.dependencies().values() { - if let Some(specifier) = dep.get_type().or_else(|| dep.get_code()) { - if seen.insert(specifier.as_str()) { - // only include dependencies we know to exist otherwise typescript will error - if documents.exists(specifier) { - result.push(specifier.to_string()); - } - } + for doc in &all_docs { + let specifiers = std::iter::once(doc.specifier()).chain( + doc + .dependencies() + .values() + .filter_map(|dep| dep.get_type().or_else(|| dep.get_code())), + ); + for specifier in specifiers { + if seen.insert(specifier.as_str()) && documents.exists(specifier) { + // only include dependencies we know to exist otherwise typescript will error + result.push(specifier.to_string()); } } } diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index 35c115d564..15f0ea4900 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -11,21 +11,21 @@ use std::fs; use std::process::Stdio; use test_util::deno_cmd_with_deno_dir; use test_util::env_vars_for_npm_tests; -use test_util::lsp::LspClientBuilder; use test_util::testdata_path; use test_util::TestContextBuilder; use tower_lsp::lsp_types as lsp; #[test] fn lsp_startup_shutdown() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.shutdown(); } #[test] fn lsp_init_tsconfig() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -58,7 +58,7 @@ fn lsp_init_tsconfig() { #[test] fn lsp_tsconfig_types() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -98,7 +98,8 @@ fn lsp_tsconfig_types() { #[test] fn lsp_tsconfig_bad_config_path() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder .set_config("bad_tsconfig.json") @@ -123,7 +124,7 @@ fn lsp_tsconfig_bad_config_path() { #[test] fn lsp_triple_slash_types() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); let a_dts = "// deno-lint-ignore-file no-var\ndeclare var a: string;"; temp_dir.write("a.d.ts", a_dts); @@ -146,7 +147,7 @@ fn lsp_triple_slash_types() { #[test] fn lsp_import_map() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); let import_map = r#"{ "imports": { @@ -205,7 +206,7 @@ fn lsp_import_map() { #[test] fn lsp_import_map_data_url() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}"); @@ -230,7 +231,7 @@ fn lsp_import_map_data_url() { #[test] fn lsp_import_map_config_file() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( "deno.import_map.jsonc", @@ -297,7 +298,7 @@ fn lsp_import_map_config_file() { #[test] fn lsp_import_map_embedded_in_config_file() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( "deno.embedded_import_map.jsonc", @@ -358,7 +359,7 @@ fn lsp_import_map_embedded_in_config_file() { #[test] fn lsp_deno_task() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( "deno.jsonc", @@ -393,7 +394,7 @@ fn lsp_deno_task() { #[test] fn lsp_import_assertions() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}"); @@ -509,7 +510,7 @@ fn lsp_import_assertions() { #[test] fn lsp_import_map_import_completions() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( "import-map.json", @@ -649,7 +650,8 @@ fn lsp_import_map_import_completions() { #[test] fn lsp_hover() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -690,7 +692,8 @@ fn lsp_hover() { #[test] fn lsp_hover_asset() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -747,7 +750,7 @@ fn lsp_hover_asset() { #[test] fn lsp_hover_disabled() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.set_deno_enable(false); @@ -779,7 +782,7 @@ fn lsp_hover_disabled() { #[test] fn lsp_inlay_hints() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.enable_inlay_hints(); @@ -882,7 +885,8 @@ fn lsp_inlay_hints() { #[test] fn lsp_inlay_hints_not_enabled() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -929,11 +933,11 @@ fn lsp_inlay_hints_not_enabled() { #[test] fn lsp_workspace_enable_paths() { fn run_test(use_trailing_slash: bool) { - let context = TestContextBuilder::new().build(); - // we aren't actually writing anything to the tempdir in this test, but we - // just need a legitimate file path on the host system so that logic that - // tries to convert to and from the fs paths works on all env + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); + temp_dir.create_dir_all("worker"); + temp_dir.write("worker/shared.ts", "export const a = 1"); + temp_dir.write("worker/other.ts", "import { a } from './shared.ts';\na;"); let root_specifier = temp_dir.uri(); @@ -985,7 +989,11 @@ fn lsp_workspace_enable_paths() { "uri": root_specifier.join("./worker/file.ts").unwrap(), "languageId": "typescript", "version": 1, - "text": "console.log(Date.now());\n" + "text": concat!( + "console.log(Date.now());\n", + "import { a } from './shared.ts';\n", + "a;\n", + ), } })); @@ -1072,6 +1080,56 @@ fn lsp_workspace_enable_paths() { }) ); + // check that the file system documents were auto-discovered + // via the enabled paths + let res = client.write_request( + "textDocument/references", + json!({ + "textDocument": { + "uri": root_specifier.join("./worker/file.ts").unwrap(), + }, + "position": { "line": 2, "character": 0 }, + "context": { + "includeDeclaration": true + } + }), + ); + + assert_eq!( + res, + json!([{ + "uri": root_specifier.join("./worker/file.ts").unwrap(), + "range": { + "start": { "line": 1, "character": 9 }, + "end": { "line": 1, "character": 10 } + } + }, { + "uri": root_specifier.join("./worker/file.ts").unwrap(), + "range": { + "start": { "line": 2, "character": 0 }, + "end": { "line": 2, "character": 1 } + } + }, { + "uri": root_specifier.join("./worker/shared.ts").unwrap(), + "range": { + "start": { "line": 0, "character": 13 }, + "end": { "line": 0, "character": 14 } + } + }, { + "uri": root_specifier.join("./worker/other.ts").unwrap(), + "range": { + "start": { "line": 0, "character": 9 }, + "end": { "line": 0, "character": 10 } + } + }, { + "uri": root_specifier.join("./worker/other.ts").unwrap(), + "range": { + "start": { "line": 1, "character": 0 }, + "end": { "line": 1, "character": 1 } + } + }]) + ); + client.shutdown(); } @@ -1081,7 +1139,8 @@ fn lsp_workspace_enable_paths() { #[test] fn lsp_hover_unstable_disabled() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -1120,7 +1179,8 @@ fn lsp_hover_unstable_disabled() { #[test] fn lsp_hover_unstable_enabled() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.set_unstable(true); }); @@ -1163,7 +1223,8 @@ fn lsp_hover_unstable_enabled() { #[test] fn lsp_hover_change_mbc() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -1228,7 +1289,7 @@ fn lsp_hover_change_mbc() { #[test] fn lsp_hover_closed_document() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write("a.ts", r#"export const a = "a";"#); temp_dir.write("b.ts", r#"export * from "./a.ts";"#); @@ -1320,7 +1381,10 @@ fn lsp_hover_closed_document() { #[test] fn lsp_hover_dependency() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ @@ -1466,7 +1530,8 @@ fn lsp_hover_dependency() { // unable to resolve dependencies when there was an invalid syntax in the module #[test] fn lsp_hover_deps_preserved_when_invalid_parse() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -1557,7 +1622,10 @@ fn lsp_hover_deps_preserved_when_invalid_parse() { #[test] fn lsp_hover_typescript_types() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( @@ -1610,7 +1678,8 @@ fn lsp_hover_typescript_types() { #[test] fn lsp_hover_jsdoc_symbol_link() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -1660,7 +1729,8 @@ fn lsp_hover_jsdoc_symbol_link() { #[test] fn lsp_goto_type_definition() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -1702,7 +1772,8 @@ fn lsp_goto_type_definition() { #[test] fn lsp_call_hierarchy() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -1831,7 +1902,8 @@ fn lsp_call_hierarchy() { #[test] fn lsp_large_doc_changes() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let large_file_text = fs::read_to_string(testdata_path().join("lsp").join("large_file.txt")) @@ -1932,7 +2004,8 @@ fn lsp_large_doc_changes() { #[test] fn lsp_document_symbol() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2131,7 +2204,8 @@ fn lsp_document_symbol() { #[test] fn lsp_folding_range() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2177,7 +2251,8 @@ fn lsp_folding_range() { #[test] fn lsp_rename() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2229,7 +2304,8 @@ fn lsp_rename() { #[test] fn lsp_selection_range() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2306,7 +2382,8 @@ fn lsp_selection_range() { #[test] fn lsp_semantic_tokens() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2365,7 +2442,8 @@ fn lsp_semantic_tokens() { #[test] fn lsp_code_lens() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -2539,7 +2617,8 @@ fn lsp_code_lens() { #[test] fn lsp_code_lens_impl() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -2685,7 +2764,8 @@ fn lsp_code_lens_impl() { #[test] fn lsp_code_lens_test() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.disable_testing_api().set_code_lens(None); }); @@ -2940,7 +3020,8 @@ fn lsp_code_lens_test() { #[test] fn lsp_code_lens_test_disabled() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.disable_testing_api().set_code_lens(Some(json!({ "implementations": true, @@ -2980,7 +3061,8 @@ fn lsp_code_lens_test_disabled() { #[test] fn lsp_code_lens_non_doc_nav_tree() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -3037,7 +3119,8 @@ fn lsp_code_lens_non_doc_nav_tree() { #[test] fn lsp_nav_tree_updates() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -3177,7 +3260,8 @@ fn lsp_nav_tree_updates() { #[test] fn lsp_find_references() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -3285,7 +3369,8 @@ fn lsp_find_references() { #[test] fn lsp_signature_help() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -3405,7 +3490,8 @@ fn lsp_signature_help() { #[test] fn lsp_code_actions() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -3592,7 +3678,8 @@ fn lsp_code_actions() { #[test] fn lsp_code_actions_deno_cache() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client.did_open(json!({ "textDocument": { @@ -3681,7 +3768,8 @@ fn lsp_code_actions_deno_cache() { #[test] fn lsp_code_actions_deno_cache_npm() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client.did_open(json!({ "textDocument": { @@ -3765,7 +3853,8 @@ fn lsp_code_actions_deno_cache_npm() { #[test] fn lsp_code_actions_imports() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -4005,7 +4094,8 @@ export class DuckConfig { #[test] fn lsp_code_actions_refactor() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -4214,7 +4304,8 @@ fn lsp_code_actions_refactor() { #[test] fn lsp_code_actions_refactor_no_disabled_support() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.with_capabilities(|c| { let doc = c.text_document.as_mut().unwrap(); @@ -4283,7 +4374,8 @@ fn lsp_code_actions_refactor_no_disabled_support() { #[test] fn lsp_code_actions_deadlock() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let large_file_text = fs::read_to_string(testdata_path().join("lsp").join("large_file.txt")) @@ -4406,7 +4498,8 @@ fn lsp_code_actions_deadlock() { #[test] fn lsp_completions() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -4464,7 +4557,8 @@ fn lsp_completions() { #[test] fn lsp_completions_private_fields() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -4488,7 +4582,8 @@ fn lsp_completions_private_fields() { #[test] fn lsp_completions_optional() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -4570,7 +4665,8 @@ fn lsp_completions_optional() { #[test] fn lsp_completions_auto_import() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -4654,7 +4750,8 @@ fn lsp_completions_auto_import() { #[test] fn lsp_completions_snippet() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( json!({ @@ -4748,7 +4845,8 @@ fn lsp_completions_snippet() { #[test] fn lsp_completions_no_snippet() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.with_capabilities(|c| { let doc = c.text_document.as_mut().unwrap(); @@ -4802,7 +4900,10 @@ fn lsp_completions_no_snippet() { #[test] fn lsp_completions_npm() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( @@ -4938,7 +5039,10 @@ fn lsp_completions_npm() { #[test] fn lsp_npm_specifier_unopened_file() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); @@ -5017,7 +5121,10 @@ fn lsp_npm_specifier_unopened_file() { #[test] fn lsp_completions_node_specifier() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client.did_open(json!({ @@ -5238,7 +5345,10 @@ fn lsp_completions_node_specifier() { #[test] fn lsp_completions_registry() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.add_test_server_suggestions(); @@ -5301,7 +5411,10 @@ fn lsp_completions_registry() { #[test] fn lsp_completions_registry_empty() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder.add_test_server_suggestions(); @@ -5361,7 +5474,10 @@ fn lsp_completions_registry_empty() { #[test] fn lsp_auto_discover_registry() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ @@ -5394,7 +5510,10 @@ fn lsp_auto_discover_registry() { #[test] fn lsp_cache_location() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let temp_dir = context.temp_dir(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { @@ -5483,7 +5602,10 @@ fn lsp_cache_location() { /// and cache files. #[test] fn lsp_tls_cert() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize(|builder| { builder @@ -5570,7 +5692,10 @@ fn lsp_tls_cert() { #[test] fn lsp_diagnostics_warn_redirect() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( @@ -5646,7 +5771,10 @@ fn lsp_diagnostics_warn_redirect() { #[test] fn lsp_redirect_quick_fix() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open( @@ -5730,7 +5858,8 @@ fn lsp_redirect_quick_fix() { #[test] fn lsp_diagnostics_deprecated() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client.did_open(json!({ "textDocument": { @@ -5776,7 +5905,8 @@ fn lsp_diagnostics_deprecated() { #[test] fn lsp_diagnostics_deno_types() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client .did_open(json!({ @@ -5803,7 +5933,8 @@ fn lsp_diagnostics_deno_types() { #[test] fn lsp_diagnostics_refresh_dependents() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -5891,7 +6022,8 @@ struct PerformanceAverages { #[test] fn lsp_performance() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -5943,7 +6075,8 @@ fn lsp_performance() { #[test] fn lsp_format_no_changes() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -5972,7 +6105,8 @@ fn lsp_format_no_changes() { #[test] fn lsp_format_error() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6000,7 +6134,8 @@ fn lsp_format_error() { #[test] fn lsp_format_mbc() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6055,7 +6190,7 @@ fn lsp_format_mbc() { #[test] fn lsp_format_exclude_with_config() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -6108,7 +6243,7 @@ fn lsp_format_exclude_with_config() { #[test] fn lsp_format_exclude_default_config() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -6161,7 +6296,8 @@ fn lsp_format_exclude_default_config() { #[test] fn lsp_format_json() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6216,7 +6352,8 @@ fn lsp_format_json() { #[test] fn lsp_json_no_diagnostics() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6253,7 +6390,8 @@ fn lsp_json_no_diagnostics() { #[test] fn lsp_format_markdown() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6300,7 +6438,7 @@ fn lsp_format_markdown() { #[test] fn lsp_format_with_config() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( "deno.fmt.jsonc", @@ -6407,7 +6545,8 @@ fn lsp_format_with_config() { #[test] fn lsp_markdown_no_diagnostics() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6444,7 +6583,10 @@ fn lsp_markdown_no_diagnostics() { #[test] fn lsp_configuration_did_change() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ @@ -6537,7 +6679,8 @@ fn lsp_configuration_did_change() { #[test] fn lsp_workspace_symbol() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6631,7 +6774,8 @@ fn lsp_workspace_symbol() { #[test] fn lsp_code_actions_ignore_lint() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6754,7 +6898,8 @@ fn lsp_code_actions_ignore_lint() { /// This test exercises updating an existing deno-lint-ignore-file comment. #[test] fn lsp_code_actions_update_ignore_lint() { - let mut client = LspClientBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ "textDocument": { @@ -6879,7 +7024,7 @@ console.log(snake_case); #[test] fn lsp_lint_with_config() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -6920,7 +7065,7 @@ fn lsp_lint_with_config() { #[test] fn lsp_lint_exclude_with_config() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); temp_dir.write( @@ -6961,7 +7106,10 @@ fn lsp_lint_exclude_with_config() { #[test] fn lsp_jsx_import_source_pragma() { - let context = TestContextBuilder::new().use_http_server().build(); + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .build(); let mut client = context.new_lsp_command().build(); client.initialize_default(); client.did_open(json!({ @@ -7058,7 +7206,7 @@ struct TestRunResponseParams { #[test] fn lsp_testing_api() { - let context = TestContextBuilder::new().build(); + let context = TestContextBuilder::new().use_temp_cwd().build(); let temp_dir = context.temp_dir(); let contents = r#" @@ -7216,3 +7364,56 @@ Deno.test({ client.shutdown(); } + +#[test] +fn lsp_closed_file_find_references() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + temp_dir.write("./mod.ts", "export const a = 5;"); + temp_dir.write( + "./mod.test.ts", + "import { a } from './mod.ts'; console.log(a);", + ); + let temp_dir_url = temp_dir.uri(); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir_url.join("mod.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#"export const a = 5;"# + } + })); + let res = client.write_request( + "textDocument/references", + json!({ + "textDocument": { + "uri": temp_dir_url.join("mod.ts").unwrap(), + }, + "position": { "line": 0, "character": 13 }, + "context": { + "includeDeclaration": false + } + }), + ); + + assert_eq!( + res, + json!([{ + "uri": temp_dir_url.join("mod.test.ts").unwrap(), + "range": { + "start": { "line": 0, "character": 9 }, + "end": { "line": 0, "character": 10 } + } + }, { + "uri": temp_dir_url.join("mod.test.ts").unwrap(), + "range": { + "start": { "line": 0, "character": 42 }, + "end": { "line": 0, "character": 43 } + } + }]) + ); + + client.shutdown(); +} diff --git a/cli/tests/integration/repl_tests.rs b/cli/tests/integration/repl_tests.rs index 73c8918f9d..c3e7c42d65 100644 --- a/cli/tests/integration/repl_tests.rs +++ b/cli/tests/integration/repl_tests.rs @@ -131,24 +131,43 @@ fn pty_complete_expression() { #[test] fn pty_complete_imports() { - util::with_pty(&["repl", "-A"], |mut console| { - // single quotes - console.write_line_raw("import './run/001_hel\t'"); - console.expect("Hello World"); - // double quotes - console.write_line_raw("import { output } from \"./run/045_out\t\""); - console.expect("\"./run/045_output.ts\""); - console.write_line_raw("output('testing output');"); - console.expect("testing output"); - }); + let context = TestContextBuilder::default().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + temp_dir.create_dir_all("subdir"); + temp_dir.write("./subdir/my_file.ts", ""); + temp_dir.create_dir_all("run"); + temp_dir.write("./run/hello.ts", "console.log('Hello World');"); + temp_dir.write( + "./run/output.ts", + r#"export function output(text: string) { + console.log(text); +} +"#, + ); + context + .new_command() + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + // single quotes + console.write_line_raw("import './run/hel\t'"); + console.expect("Hello World"); + // double quotes + console.write_line_raw("import { output } from \"./run/out\t\""); + console.expect("\"./run/output.ts\""); + console.write_line_raw("output('testing output');"); + console.expect("testing output"); + }); // ensure when the directory changes that the suggestions come from the cwd - util::with_pty(&["repl", "-A"], |mut console| { - console.write_line("Deno.chdir('./subdir');"); - console.expect("undefined"); - console.write_line_raw("import '../run/001_hel\t'"); - console.expect("Hello World"); - }); + context + .new_command() + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + console.write_line("Deno.chdir('./subdir');"); + console.expect("undefined"); + console.write_line_raw("import '../run/he\t'"); + console.expect("Hello World"); + }); } #[test] @@ -283,10 +302,15 @@ fn let_redeclaration() { #[test] fn repl_cwd() { - util::with_pty(&["repl", "-A"], |mut console| { - console.write_line("Deno.cwd()"); - console.expect("testdata"); - }); + let context = TestContextBuilder::default().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + context + .new_command() + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + console.write_line("Deno.cwd()"); + console.expect(temp_dir.path().file_name().unwrap().to_str().unwrap()); + }); } #[test] @@ -384,18 +408,30 @@ fn multiline() { #[test] fn import() { - util::with_pty(&["repl", "-A"], |mut console| { - console.write_line("import('./subdir/auto_print_hello.ts')"); - console.expect("hello!"); - }); + let context = TestContextBuilder::default() + .use_copy_temp_dir("./subdir") + .build(); + context + .new_command() + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + console.write_line("import('./subdir/auto_print_hello.ts')"); + console.expect("hello!"); + }); } #[test] fn import_declarations() { - util::with_pty(&["repl", "-A"], |mut console| { - console.write_line("import './subdir/auto_print_hello.ts'"); - console.expect("hello!"); - }); + let context = TestContextBuilder::default() + .use_copy_temp_dir("./subdir") + .build(); + context + .new_command() + .args_vec(["repl", "-A"]) + .with_pty(|mut console| { + console.write_line("import './subdir/auto_print_hello.ts'"); + console.expect("hello!"); + }); } #[test] diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 003bc59fc5..b661e135ae 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -14,6 +14,7 @@ use trust_dns_client::serialize::txt::Lexer; use trust_dns_client::serialize::txt::Parser; use util::assert_contains; use util::env_vars_for_npm_tests_no_sync_download; +use util::TestContext; use util::TestContextBuilder; itest!(stdout_write_all { @@ -571,9 +572,10 @@ itest!(_089_run_allow_list { #[test] fn _090_run_permissions_request() { - util::with_pty( - &["run", "--quiet", "run/090_run_permissions_request.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/090_run_permissions_request.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests run access to \"ls\".\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -592,15 +594,15 @@ fn _090_run_permissions_request() { console.expect("Denied run access to \"cat\"."); console.expect("granted"); console.expect("denied"); - }, - ); + }); } #[test] fn _090_run_permissions_request_sync() { - util::with_pty( - &["run", "--quiet", "run/090_run_permissions_request_sync.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/090_run_permissions_request_sync.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests run access to \"ls\".\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -619,15 +621,15 @@ fn _090_run_permissions_request_sync() { console.expect("Denied run access to \"cat\"."); console.expect("granted"); console.expect("denied"); - }, - ); + }); } #[test] fn permissions_prompt_allow_all() { - util::with_pty( - &["run", "--quiet", "run/permissions_prompt_allow_all.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all.ts"]) + .with_pty(|mut console| { // "run" permissions console.expect(concat!( "┌ ⚠️ Deno requests run access to \"FOO\".\r\n", @@ -697,9 +699,10 @@ fn permissions_prompt_allow_all() { #[test] fn permissions_prompt_allow_all_2() { - util::with_pty( - &["run", "--quiet", "run/permissions_prompt_allow_all_2.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all_2.ts"]) + .with_pty(|mut console| { // "env" permissions console.expect(concat!( "┌ ⚠️ Deno requests env access to \"FOO\".\r\n", @@ -728,15 +731,15 @@ fn permissions_prompt_allow_all_2() { )); console.write_line_raw("A"); console.expect("✅ Granted all read access."); - }, - ); + }); } #[test] fn permissions_prompt_allow_all_lowercase_a() { - util::with_pty( - &["run", "--quiet", "run/permissions_prompt_allow_all.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/permissions_prompt_allow_all.ts"]) + .with_pty(|mut console| { // "run" permissions console.expect(concat!( "┌ ⚠️ Deno requests run access to \"FOO\".\r\n", @@ -746,8 +749,7 @@ fn permissions_prompt_allow_all_lowercase_a() { )); console.write_line_raw("a"); console.expect("Unrecognized option."); - }, - ); + }); } itest!(_091_use_define_for_class_fields { @@ -2141,6 +2143,7 @@ fn dont_cache_on_check_fail() { mod permissions { use test_util as util; + use util::TestContext; // TODO(bartlomieju): remove --unstable once Deno.Command is stabilized #[test] @@ -2503,9 +2506,10 @@ mod permissions { #[test] fn _061_permissions_request() { - util::with_pty( - &["run", "--quiet", "run/061_permissions_request.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/061_permissions_request.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests read access to \"foo\".\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -2523,15 +2527,15 @@ mod permissions { console.expect("granted"); console.expect("prompt"); console.expect("denied"); - }, - ); + }); } #[test] fn _061_permissions_request_sync() { - util::with_pty( - &["run", "--quiet", "run/061_permissions_request_sync.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/061_permissions_request_sync.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests read access to \"foo\".\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -2549,15 +2553,15 @@ mod permissions { console.expect("granted"); console.expect("prompt"); console.expect("denied"); - }, - ); + }); } #[test] fn _062_permissions_request_global() { - util::with_pty( - &["run", "--quiet", "run/062_permissions_request_global.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/062_permissions_request_global.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests read access.\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -2571,19 +2575,15 @@ mod permissions { .expect("PermissionStatus { state: \"granted\", onchange: null }"); console .expect("PermissionStatus { state: \"granted\", onchange: null }"); - }, - ); + }); } #[test] fn _062_permissions_request_global_sync() { - util::with_pty( - &[ - "run", - "--quiet", - "run/062_permissions_request_global_sync.ts", - ], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "run/062_permissions_request_global_sync.ts"]) + .with_pty(|mut console| { console.expect(concat!( "┌ ⚠️ Deno requests read access.\r\n", "├ Requested by `Deno.permissions.query()` API.\r\n", @@ -2597,8 +2597,7 @@ mod permissions { .expect("PermissionStatus { state: \"granted\", onchange: null }"); console .expect("PermissionStatus { state: \"granted\", onchange: null }"); - }, - ); + }); } itest!(_063_permissions_revoke { @@ -2623,9 +2622,10 @@ mod permissions { #[test] fn _066_prompt() { - util::with_pty( - &["run", "--quiet", "--unstable", "run/066_prompt.ts"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["run", "--quiet", "--unstable", "run/066_prompt.ts"]) + .with_pty(|mut console| { console.expect("What is your name? [Jane Doe] "); console.write_line_raw("John Doe"); console.expect("Your name is John Doe."); @@ -2658,8 +2658,7 @@ mod permissions { console.expect("What is EOF? "); console.write_line(""); console.expect("Your answer is null"); - }, - ); + }); } itest!(dynamic_import_permissions_remote_remote { @@ -2715,28 +2714,31 @@ itest!(byte_order_mark { #[test] fn issue9750() { - util::with_pty(&["run", "--prompt", "run/issue9750.js"], |mut console| { - console.expect("Enter 'yy':"); - console.write_line_raw("yy"); - console.expect(concat!( - "┌ ⚠️ Deno requests env access.\r\n", - "├ Requested by `Deno.permissions.query()` API.\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("n"); - console.expect("Denied env access."); - console.expect(concat!( - "┌ ⚠️ Deno requests env access to \"SECRET\".\r\n", - "├ Run again with --allow-env to bypass this prompt.\r\n", - "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", - )); - console.write_line_raw("n"); - console.expect_all(&[ - "Denied env access to \"SECRET\".", - "PermissionDenied: Requires env access to \"SECRET\", run again with the --allow-env flag", - ]); - }); + TestContext::default() + .new_command() + .args_vec(["run", "--prompt", "run/issue9750.js"]) + .with_pty(|mut console| { + console.expect("Enter 'yy':"); + console.write_line_raw("yy"); + console.expect(concat!( + "┌ ⚠️ Deno requests env access.\r\n", + "├ Requested by `Deno.permissions.query()` API.\r\n", + "├ Run again with --allow-env to bypass this prompt.\r\n", + "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", + )); + console.write_line_raw("n"); + console.expect("Denied env access."); + console.expect(concat!( + "┌ ⚠️ Deno requests env access to \"SECRET\".\r\n", + "├ Run again with --allow-env to bypass this prompt.\r\n", + "└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)", + )); + console.write_line_raw("n"); + console.expect_all(&[ + "Denied env access to \"SECRET\".", + "PermissionDenied: Requires env access to \"SECRET\", run again with the --allow-env flag", + ]); + }); } // Regression test for https://github.com/denoland/deno/issues/11451. diff --git a/cli/tests/integration/task_tests.rs b/cli/tests/integration/task_tests.rs index f090deff5d..45b091b083 100644 --- a/cli/tests/integration/task_tests.rs +++ b/cli/tests/integration/task_tests.rs @@ -4,6 +4,7 @@ // These tests are intended to only test integration. use test_util::env_vars_for_npm_tests; +use test_util::TestContext; itest!(task_no_args { args: "task -q --config task/deno_json/deno.json", @@ -53,12 +54,12 @@ itest!(task_non_existent { #[test] fn task_emoji() { // this bug only appears when using a pty/tty - test_util::with_pty( - &["task", "--config", "task/deno_json/deno.json", "echo_emoji"], - |mut console| { + TestContext::default() + .new_command() + .args_vec(["task", "--config", "task/deno_json/deno.json", "echo_emoji"]) + .with_pty(|mut console| { console.expect("Task echo_emoji echo 🔥\r\n🔥"); - }, - ); + }); } itest!(task_boolean_logic { diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs index 3a7f37db8b..8e24045301 100644 --- a/cli/tests/integration/test_tests.rs +++ b/cli/tests/integration/test_tests.rs @@ -406,10 +406,9 @@ fn file_protocol() { .unwrap() .to_string(); - let context = TestContext::default(); - context + TestContext::default() .new_command() - .args_vec(vec!["test".to_string(), file_url]) + .args_vec(["test", file_url.as_str()]) .run() .assert_matches_file("test/file_protocol.out"); } diff --git a/test_util/src/builders.rs b/test_util/src/builders.rs index 4997dac2ce..0a0f2244fd 100644 --- a/test_util/src/builders.rs +++ b/test_util/src/builders.rs @@ -228,8 +228,11 @@ impl TestCommandBuilder { self } - pub fn args_vec(&mut self, args: Vec) -> &mut Self { - self.args_vec = args; + pub fn args_vec, I: IntoIterator>( + &mut self, + args: I, + ) -> &mut Self { + self.args_vec = args.into_iter().map(|a| a.as_ref().to_string()).collect(); self } diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index c844e594f1..5c6bc97f7a 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -2093,8 +2093,7 @@ impl<'a> CheckOutputIntegrationTest<'a> { command_builder.args(self.args); } if !self.args_vec.is_empty() { - command_builder - .args_vec(self.args_vec.iter().map(|a| a.to_string()).collect()); + command_builder.args_vec(self.args_vec.clone()); } if let Some(input) = &self.input { command_builder.stdin(input); @@ -2167,11 +2166,8 @@ pub fn pattern_match(pattern: &str, s: &str, wildcard: &str) -> bool { } pub fn with_pty(deno_args: &[&str], action: impl FnMut(Pty)) { - let context = TestContextBuilder::default().build(); - context - .new_command() - .args_vec(deno_args.iter().map(ToString::to_string).collect()) - .with_pty(action); + let context = TestContextBuilder::default().use_temp_cwd().build(); + context.new_command().args_vec(deno_args).with_pty(action); } pub struct WrkOutput {