diff options
| author | David Sherret <dsherret@users.noreply.github.com> | 2023-03-11 11:43:45 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-11 11:43:45 -0500 |
| commit | 8db853514caae431a0ce91360d854c1b7f3c405f (patch) | |
| tree | 4173976ce6c836b715af22c9aeb9f336debb8544 /cli/lsp | |
| parent | e4430400ced48529730a8752c547b613a23c76ce (diff) | |
fix(check): regression where config "types" entries caused type checking errors (#18124)
Closes #18117
Closes #18121 (this is just over 10ms faster in a directory one up from
the root folder)
cc @nayeemrmn
Diffstat (limited to 'cli/lsp')
| -rw-r--r-- | cli/lsp/documents.rs | 13 | ||||
| -rw-r--r-- | cli/lsp/language_server.rs | 2 | ||||
| -rw-r--r-- | cli/lsp/tsc.rs | 103 |
3 files changed, 48 insertions, 70 deletions
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 893a30103..d49cd0b1f 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -818,7 +818,7 @@ pub struct Documents { resolver_config_hash: u64, /// Any imports to the context supplied by configuration files. This is like /// the imports into the a module graph in CLI. - imports: Arc<HashMap<ModuleSpecifier, GraphImport>>, + imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, /// A resolver that takes into account currently loaded import map and JSX /// settings. resolver: CliGraphResolver, @@ -851,6 +851,14 @@ impl Documents { } } + pub fn module_graph_imports(&self) -> impl Iterator<Item = &ModuleSpecifier> { + self + .imports + .values() + .flat_map(|i| i.dependencies.values()) + .flat_map(|value| value.get_type().or_else(|| value.get_code())) + } + /// "Open" a document from the perspective of the editor, meaning that /// requests for information from the document will come from the in-memory /// representation received from the language server client, versus reading @@ -946,7 +954,6 @@ impl Documents { /// Return `true` if the specifier can be resolved to a document. pub fn exists(&self, specifier: &ModuleSpecifier) -> bool { - // keep this fast because it's used by op_exists, which is a hot path in tsc let specifier = self.specifier_resolver.resolve(specifier); if let Some(specifier) = specifier { if self.open_docs.contains_key(&specifier) { @@ -1239,7 +1246,7 @@ impl Documents { }) .collect() } else { - HashMap::new() + IndexMap::new() }, ); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index ee91e045b..357d77ade 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -97,7 +97,6 @@ pub struct StateSnapshot { pub cache_metadata: cache::CacheMetadata, pub documents: Documents, pub maybe_import_map: Option<Arc<ImportMap>>, - pub root_uri: Option<Url>, pub maybe_npm_resolver: Option<NpmPackageResolver>, } @@ -576,7 +575,6 @@ impl Inner { documents: self.documents.clone(), maybe_import_map: self.maybe_import_map.clone(), maybe_npm_resolver: Some(self.npm_resolver.snapshotted()), - root_uri: self.config.root_uri.clone(), }) } diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 9a0c0dabe..79c1817e2 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -109,8 +109,7 @@ impl TsServer { while let Some((req, state_snapshot, tx, token)) = rx.recv().await { if !started { // TODO(@kitsonk) need to reflect the debug state of the lsp here - start(&mut ts_runtime, false, &state_snapshot) - .expect("could not start tsc"); + start(&mut ts_runtime, false).unwrap(); started = true; } let value = request(&mut ts_runtime, state_snapshot, req, token); @@ -2660,24 +2659,6 @@ struct SpecifierArgs { } #[op] -fn op_exists(state: &mut OpState, args: SpecifierArgs) -> bool { - let state = state.borrow_mut::<State>(); - // we don't measure the performance of op_exists anymore because as of TS 4.5 - // it is noisy with all the checking for custom libs, that we can't see the - // forrest for the trees as well as it compounds any lsp performance - // challenges, opening a single document in the editor causes some 3k worth - // of op_exists requests... :omg: - let specifier = match state.normalize_specifier(&args.specifier) { - Ok(url) => url, - // sometimes tsc tries to query invalid specifiers, especially when - // something else isn't quite right, so instead of bubbling up the error - // back to tsc, we simply swallow it and say the file doesn't exist - Err(_) => return false, - }; - state.state_snapshot.documents.exists(&specifier) -} - -#[op] fn op_is_cancelled(state: &mut OpState) -> bool { let state = state.borrow_mut::<State>(); state.token.is_cancelled() @@ -2768,15 +2749,45 @@ fn op_script_names(state: &mut OpState) -> Vec<String> { let state = state.borrow_mut::<State>(); let documents = &state.state_snapshot.documents; let open_docs = documents.documents(true, true); - - let mut result = Vec::with_capacity(open_docs.len() + 1); + let mut result = Vec::new(); + let mut seen = HashSet::new(); if documents.has_injected_types_node_package() { // ensure this is first so it resolves the node types first - result.push("asset:///node_types.d.ts".to_string()); + let specifier = "asset:///node_types.d.ts"; + result.push(specifier.to_string()); + seen.insert(specifier); + } + + // inject these next because they're global + for import in documents.module_graph_imports() { + if seen.insert(import.as_str()) { + result.push(import.to_string()); + } + } + + // 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()); + } + } + } + } } - result.extend(open_docs.into_iter().map(|d| d.specifier().to_string())); result } @@ -2812,7 +2823,6 @@ fn js_runtime(performance: Arc<Performance>) -> JsRuntime { fn init_extension(performance: Arc<Performance>) -> Extension { Extension::builder("deno_tsc") .ops(vec![ - op_exists::decl(), op_is_cancelled::decl(), op_is_node_file::decl(), op_load::decl(), @@ -2832,16 +2842,8 @@ fn init_extension(performance: Arc<Performance>) -> Extension { /// Instruct a language server runtime to start the language server and provide /// it with a minimal bootstrap configuration. -fn start( - runtime: &mut JsRuntime, - debug: bool, - state_snapshot: &StateSnapshot, -) -> Result<(), AnyError> { - let root_uri = state_snapshot - .root_uri - .clone() - .unwrap_or_else(|| Url::parse("cache:///").unwrap()); - let init_config = json!({ "debug": debug, "rootUri": root_uri }); +fn start(runtime: &mut JsRuntime, debug: bool) -> Result<(), AnyError> { + let init_config = json!({ "debug": debug }); let init_src = format!("globalThis.serverInit({init_config});"); runtime.execute_script(&located_script_name!(), &init_src)?; @@ -3495,8 +3497,7 @@ mod tests { let location = temp_dir.path().join("deps"); let state_snapshot = Arc::new(mock_state_snapshot(sources, &location)); let mut runtime = js_runtime(Default::default()); - start(&mut runtime, debug, &state_snapshot) - .expect("could not start server"); + start(&mut runtime, debug).unwrap(); let ts_config = TsConfig::new(config); assert_eq!( request( @@ -4039,34 +4040,6 @@ mod tests { } #[test] - fn test_op_exists() { - let temp_dir = TempDir::new(); - let (mut rt, state_snapshot, _) = setup( - &temp_dir, - false, - json!({ - "target": "esnext", - "module": "esnext", - "lib": ["deno.ns", "deno.window"], - "noEmit": true, - }), - &[], - ); - let performance = Arc::new(Performance::default()); - let state = State::new(state_snapshot, performance); - let op_state = rt.op_state(); - let mut op_state = op_state.borrow_mut(); - op_state.put(state); - let actual = op_exists::call( - &mut op_state, - SpecifierArgs { - specifier: "/error/unknown:something/index.d.ts".to_string(), - }, - ); - assert!(!actual); - } - - #[test] fn test_completion_entry_filter_text() { let fixture = CompletionEntry { kind: ScriptElementKind::MemberVariableElement, |
