From c0e3b6096d37e9a4243c7ad461487db291c824fa Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 25 Apr 2022 11:23:24 -0400 Subject: [PATCH] refactor(lsp): store all the assets in Rust when initializing (#14367) --- cli/lsp/analysis.rs | 9 +- cli/lsp/code_lens.rs | 10 +- cli/lsp/language_server.rs | 203 +++++++++++-------------------- cli/lsp/tsc.rs | 234 +++++++++++++++--------------------- cli/tsc/99_main_compiler.js | 17 ++- cli/tsc/compiler.d.ts | 7 +- 6 files changed, 186 insertions(+), 294 deletions(-) diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 2c70799513..bbce1966b9 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -323,14 +323,13 @@ fn is_preferred( /// Convert changes returned from a TypeScript quick fix action into edits /// for an LSP CodeAction. -pub async fn ts_changes_to_edit( +pub fn ts_changes_to_edit( changes: &[tsc::FileTextChanges], language_server: &language_server::Inner, ) -> Result, AnyError> { let mut text_document_edits = Vec::new(); for change in changes { - let text_document_edit = - change.to_text_document_edit(language_server).await?; + let text_document_edit = change.to_text_document_edit(language_server)?; text_document_edits.push(text_document_edit); } Ok(Some(lsp::WorkspaceEdit { @@ -539,7 +538,7 @@ impl CodeActionCollection { } /// Add a TypeScript code fix action to the code actions collection. - pub async fn add_ts_fix_action( + pub fn add_ts_fix_action( &mut self, specifier: &ModuleSpecifier, action: &tsc::CodeFixAction, @@ -561,7 +560,7 @@ impl CodeActionCollection { } let action = fix_ts_import_action(specifier, action, &language_server.documents)?; - let edit = ts_changes_to_edit(&action.changes, language_server).await?; + let edit = ts_changes_to_edit(&action.changes, language_server)?; let code_action = lsp::CodeAction { title: action.description.clone(), kind: Some(lsp::CodeActionKind::QUICKFIX), diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index a80f9bbb07..725051e3c9 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -241,8 +241,7 @@ async fn resolve_implementation_code_lens( data: CodeLensData, language_server: &language_server::Inner, ) -> Result { - let asset_or_doc = - language_server.get_cached_asset_or_document(&data.specifier)?; + let asset_or_doc = language_server.get_asset_or_document(&data.specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetImplementation(( data.specifier.clone(), @@ -311,7 +310,7 @@ async fn resolve_references_code_lens( language_server: &language_server::Inner, ) -> Result { let asset_or_document = - language_server.get_cached_asset_or_document(&data.specifier)?; + language_server.get_asset_or_document(&data.specifier)?; let line_index = asset_or_document.line_index(); let req = tsc::RequestMethod::GetReferences(( data.specifier.clone(), @@ -328,9 +327,8 @@ async fn resolve_references_code_lens( } let reference_specifier = resolve_url(&reference.document_span.file_name)?; - let asset_or_doc = language_server - .get_asset_or_document(&reference_specifier) - .await?; + let asset_or_doc = + language_server.get_asset_or_document(&reference_specifier)?; locations.push( reference .to_location(asset_or_doc.line_index(), &language_server.url_map), diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 1b9eeed3dd..9b82aea272 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -197,7 +197,7 @@ impl LanguageServer { match params.map(serde_json::from_value) { Some(Ok(params)) => Ok(Some( serde_json::to_value( - self.0.lock().await.virtual_text_document(params).await?, + self.0.lock().await.virtual_text_document(params)?, ) .map_err(|err| { error!( @@ -261,80 +261,30 @@ impl Inner { } } - /// Searches assets and open documents which might be performed asynchronously, - /// hydrating in memory caches for subsequent requests. - pub async fn get_asset_or_document( + /// Searches assets and documents for the provided + /// specifier erroring if it doesn't exist. + pub fn get_asset_or_document( &self, specifier: &ModuleSpecifier, ) -> LspResult { - self - .get_maybe_asset_or_document(specifier) - .await? - .map_or_else( - || { - Err(LspError::invalid_params(format!( - "Unable to find asset or document for: {}", - specifier - ))) - }, - Ok, - ) + self.get_maybe_asset_or_document(specifier).map_or_else( + || { + Err(LspError::invalid_params(format!( + "Unable to find asset or document for: {}", + specifier + ))) + }, + Ok, + ) } - /// Searches assets and open documents which might be performed asynchronously, - /// hydrating in memory caches for subsequent requests. - pub async fn get_maybe_asset_or_document( - &self, - specifier: &ModuleSpecifier, - ) -> LspResult> { - let mark = self.performance.mark( - "get_maybe_asset_or_document", - Some(json!({ "specifier": specifier })), - ); - let result = if specifier.scheme() == "asset" { - self - .assets - .get(specifier, || self.snapshot()) - .await? - .map(AssetOrDocument::Asset) - } else { - self.documents.get(specifier).map(AssetOrDocument::Document) - }; - self.performance.measure(mark); - Ok(result) - } - - /// Only searches already cached assets and documents. If - /// the asset or document cannot be found an error is returned. - pub fn get_cached_asset_or_document( - &self, - specifier: &ModuleSpecifier, - ) -> LspResult { - self - .get_maybe_cached_asset_or_document(specifier) - .map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - ) - } - - /// Only searches already cached assets and documents. If - /// the asset or document cannot be found, `None` is returned. - pub fn get_maybe_cached_asset_or_document( + /// Searches assets and documents for the provided specifier. + pub fn get_maybe_asset_or_document( &self, specifier: &ModuleSpecifier, ) -> Option { if specifier.scheme() == "asset" { - self - .assets - .get_cached(specifier) - .flatten() - .map(AssetOrDocument::Asset) + self.assets.get(specifier).map(AssetOrDocument::Asset) } else { self.documents.get(specifier).map(AssetOrDocument::Document) } @@ -348,7 +298,7 @@ impl Inner { "get_navigation_tree", Some(json!({ "specifier": specifier })), ); - let asset_or_doc = self.get_cached_asset_or_document(specifier)?; + let asset_or_doc = self.get_asset_or_document(specifier)?; let navigation_tree = if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() { navigation_tree @@ -826,6 +776,8 @@ impl Inner { self.maybe_config_file.as_ref(), ); + self.assets.intitialize(self.snapshot()).await; + self.performance.measure(mark); Ok(InitializeResult { capabilities, @@ -1094,7 +1046,7 @@ impl Inner { } let mark = self.performance.mark("document_symbol", Some(¶ms)); - let asset_or_document = self.get_cached_asset_or_document(&specifier)?; + let asset_or_document = self.get_asset_or_document(&specifier)?; let line_index = asset_or_document.line_index(); let navigation_tree = @@ -1199,7 +1151,7 @@ impl Inner { } let mark = self.performance.mark("hover", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let hover = if let Some((_, dep, range)) = asset_or_doc .get_maybe_dependency(¶ms.text_document_position_params.position) { @@ -1285,7 +1237,7 @@ impl Inner { let mark = self.performance.mark("code_action", Some(¶ms)); let mut all_actions = CodeActionResponse::new(); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); // QuickFix @@ -1345,7 +1297,6 @@ impl Inner { for action in actions { code_actions .add_ts_fix_action(&specifier, &action, diagnostic, self) - .await .map_err(|err| { error!("Unable to convert fix: {}", err); LspError::internal_error() @@ -1485,14 +1436,13 @@ impl Inner { LspError::internal_error() })? } else { - combined_code_actions.changes.clone() + combined_code_actions.changes }; let mut code_action = params.clone(); - code_action.edit = - ts_changes_to_edit(&changes, self).await.map_err(|err| { - error!("Unable to convert changes to edits: {}", err); - LspError::internal_error() - })?; + code_action.edit = ts_changes_to_edit(&changes, self).map_err(|err| { + error!("Unable to convert changes to edits: {}", err); + LspError::internal_error() + })?; code_action } else if kind.as_str().starts_with(CodeActionKind::REFACTOR.as_str()) { let snapshot = self.snapshot(); @@ -1502,8 +1452,7 @@ impl Inner { error!("Unable to decode code action data: {}", err); LspError::invalid_params("The CodeAction's data is invalid.") })?; - let asset_or_doc = - self.get_cached_asset_or_document(&action_data.specifier)?; + let asset_or_doc = self.get_asset_or_document(&action_data.specifier)?; let line_index = asset_or_doc.line_index(); let start = line_index.offset_tsc(action_data.range.start)?; let length = line_index.offset_tsc(action_data.range.end)? - start; @@ -1549,7 +1498,7 @@ impl Inner { } let mark = self.performance.mark("code_lens", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let navigation_tree = self.get_navigation_tree(&specifier).await.map_err(|err| { error!("Error getting code lenses for \"{}\": {}", specifier, err); @@ -1609,7 +1558,7 @@ impl Inner { } let mark = self.performance.mark("document_highlight", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let files_to_search = vec![specifier.clone()]; let req = tsc::RequestMethod::GetDocumentHighlights(( @@ -1653,7 +1602,7 @@ impl Inner { } let mark = self.performance.mark("references", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetReferences(( specifier.clone(), @@ -1680,7 +1629,7 @@ impl Inner { line_index.clone() } else { let asset_or_doc = - self.get_asset_or_document(&reference_specifier).await?; + self.get_asset_or_document(&reference_specifier)?; asset_or_doc.line_index() }; results @@ -1709,7 +1658,7 @@ impl Inner { } let mark = self.performance.mark("goto_definition", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetDefinition(( specifier, @@ -1748,7 +1697,7 @@ impl Inner { } let mark = self.performance.mark("goto_definition", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetTypeDefinition { specifier, @@ -1767,8 +1716,7 @@ impl Inner { let response = if let Some(definition_info) = maybe_definition_info { let mut location_links = Vec::new(); for info in definition_info { - if let Some(link) = - info.document_span.to_link(line_index.clone(), self).await + if let Some(link) = info.document_span.to_link(line_index.clone(), self) { location_links.push(link); } @@ -1796,7 +1744,7 @@ impl Inner { } let mark = self.performance.mark("completion", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; // Import specifiers are something wholly internal to Deno, so for // completions, we will use internal logic and if there are completions // for imports, we will return those and not send a message into tsc, where @@ -1920,7 +1868,7 @@ impl Inner { } let mark = self.performance.mark("goto_implementation", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetImplementation(( @@ -1939,9 +1887,7 @@ impl Inner { let result = if let Some(implementations) = maybe_implementations { let mut links = Vec::new(); for implementation in implementations { - if let Some(link) = - implementation.to_link(line_index.clone(), self).await - { + if let Some(link) = implementation.to_link(line_index.clone(), self) { links.push(link) } } @@ -1966,7 +1912,7 @@ impl Inner { } let mark = self.performance.mark("folding_range", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let outlining_spans: Vec = self @@ -2010,7 +1956,7 @@ impl Inner { } let mark = self.performance.mark("incoming_calls", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls(( @@ -2033,13 +1979,10 @@ impl Inner { .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); let mut resolved_items = Vec::::new(); for item in incoming_calls.iter() { - if let Some(resolved) = item - .try_resolve_call_hierarchy_incoming_call( - self, - maybe_root_path_owned.as_deref(), - ) - .await - { + if let Some(resolved) = item.try_resolve_call_hierarchy_incoming_call( + self, + maybe_root_path_owned.as_deref(), + ) { resolved_items.push(resolved); } } @@ -2059,7 +2002,7 @@ impl Inner { } let mark = self.performance.mark("outgoing_calls", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls(( @@ -2082,14 +2025,11 @@ impl Inner { .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); let mut resolved_items = Vec::::new(); for item in outgoing_calls.iter() { - if let Some(resolved) = item - .try_resolve_call_hierarchy_outgoing_call( - line_index.clone(), - self, - maybe_root_path_owned.as_deref(), - ) - .await - { + if let Some(resolved) = item.try_resolve_call_hierarchy_outgoing_call( + line_index.clone(), + self, + maybe_root_path_owned.as_deref(), + ) { resolved_items.push(resolved); } } @@ -2113,7 +2053,7 @@ impl Inner { let mark = self .performance .mark("prepare_call_hierarchy", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::PrepareCallHierarchy(( @@ -2139,25 +2079,19 @@ impl Inner { let mut resolved_items = Vec::::new(); match one_or_many { tsc::OneOrMany::One(item) => { - if let Some(resolved) = item - .try_resolve_call_hierarchy_item( - self, - maybe_root_path_owned.as_deref(), - ) - .await - { + if let Some(resolved) = item.try_resolve_call_hierarchy_item( + self, + maybe_root_path_owned.as_deref(), + ) { resolved_items.push(resolved) } } tsc::OneOrMany::Many(items) => { for item in items.iter() { - if let Some(resolved) = item - .try_resolve_call_hierarchy_item( - self, - maybe_root_path_owned.as_deref(), - ) - .await - { + if let Some(resolved) = item.try_resolve_call_hierarchy_item( + self, + maybe_root_path_owned.as_deref(), + ) { resolved_items.push(resolved); } } @@ -2185,7 +2119,7 @@ impl Inner { } let mark = self.performance.mark("rename", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::FindRenameLocations { @@ -2235,7 +2169,7 @@ impl Inner { } let mark = self.performance.mark("selection_range", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let mut selection_ranges = Vec::::new(); @@ -2273,7 +2207,7 @@ impl Inner { } let mark = self.performance.mark("semantic_tokens_full", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetEncodedSemanticClassifications(( @@ -2317,7 +2251,7 @@ impl Inner { let mark = self .performance .mark("semantic_tokens_range", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let start = line_index.offset_tsc(params.range.start)?; @@ -2360,7 +2294,7 @@ impl Inner { } let mark = self.performance.mark("signature_help", Some(¶ms)); - let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let asset_or_doc = self.get_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let options = if let Some(context) = params.context { tsc::SignatureHelpItemsOptions { @@ -2425,7 +2359,7 @@ impl Inner { } else { let mut symbol_information = Vec::new(); for item in navigate_to_items { - if let Some(info) = item.to_symbol_information(self).await { + if let Some(info) = item.to_symbol_information(self) { symbol_information.push(info); } } @@ -2918,7 +2852,7 @@ impl Inner { Ok(Some(json!(true))) } - async fn virtual_text_document( + fn virtual_text_document( &mut self, params: lsp_custom::VirtualTextDocumentParams, ) -> LspResult> { @@ -2987,10 +2921,7 @@ impl Inner { } Some(contents) } else { - let asset_or_doc = self - .get_maybe_asset_or_document(&specifier) - .await - .map_err(|_| LspError::internal_error())?; + let asset_or_doc = self.get_maybe_asset_or_document(&specifier); if let Some(asset_or_doc) = asset_or_doc { Some(asset_or_doc.text().to_string()) } else { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 6b8a149c2f..0557179b69 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -43,7 +43,6 @@ use deno_core::ModuleSpecifier; use deno_core::OpState; use deno_core::RuntimeOptions; use deno_runtime::tokio_util::create_basic_runtime; -use log::error; use log::warn; use once_cell::sync::Lazy; use regex::Captures; @@ -215,7 +214,7 @@ impl AssetDocument { } } -type AssetsMap = HashMap>; +type AssetsMap = HashMap; fn new_assets_map() -> Arc> { let assets = tsc::STATIC_ASSETS @@ -224,7 +223,7 @@ fn new_assets_map() -> Arc> { let url_str = format!("asset:///{}", k); let specifier = resolve_url(&url_str).unwrap(); let asset = AssetDocument::new(specifier.clone(), v); - (specifier, Some(asset)) + (specifier, asset) }) .collect(); Arc::new(Mutex::new(assets)) @@ -245,10 +244,7 @@ impl AssetsSnapshot { self.0.lock().contains_key(k) } - pub fn get_cached( - &self, - k: &ModuleSpecifier, - ) -> Option> { + pub fn get(&self, k: &ModuleSpecifier) -> Option { self.0.lock().get(k).cloned() } } @@ -269,47 +265,25 @@ impl Assets { } } + /// Initializes with the assets in the isolate. + pub async fn intitialize(&self, state_snapshot: Arc) { + let assets = get_isolate_assets(&self.ts_server, state_snapshot).await; + let mut assets_map = self.assets.lock(); + for asset in assets { + if !assets_map.contains_key(asset.specifier()) { + assets_map.insert(asset.specifier().clone(), asset); + } + } + } + pub fn snapshot(&self) -> AssetsSnapshot { // it's ok to not make a complete copy for snapshotting purposes // because assets are static AssetsSnapshot(self.assets.clone()) } - pub fn get_cached( - &self, - k: &ModuleSpecifier, - ) -> Option> { - self.assets.lock().get(k).cloned() - } - - pub async fn get( - &self, - specifier: &ModuleSpecifier, - // todo(dsherret): this shouldn't be a parameter, but instead retrieved via - // a constructor dependency - get_snapshot: impl Fn() -> Arc, - ) -> LspResult> { - // Race conditions are ok to happen here since the assets are static - if let Some(maybe_asset) = self.get_cached(specifier) { - Ok(maybe_asset) - } else { - let maybe_asset = get_asset(specifier, &self.ts_server, get_snapshot()) - .await - .map_err(|err| { - error!("Error getting asset {}: {}", specifier, err); - LspError::internal_error() - })?; - // if another thread has inserted into the cache, return the asset - // that already exists in the cache so that we don't store duplicate - // assets in memory anywhere - let mut assets = self.assets.lock(); - if let Some(maybe_asset) = assets.get(specifier) { - Ok(maybe_asset.clone()) - } else { - assets.insert(specifier.clone(), maybe_asset.clone()); - Ok(maybe_asset) - } - } + pub fn get(&self, specifier: &ModuleSpecifier) -> Option { + self.assets.lock().get(specifier).cloned() } pub fn cache_navigation_tree( @@ -318,38 +292,44 @@ impl Assets { navigation_tree: Arc, ) -> Result<(), AnyError> { let mut assets = self.assets.lock(); - let maybe_doc = assets + let doc = assets .get_mut(specifier) .ok_or_else(|| anyhow!("Missing asset."))?; - let doc = maybe_doc - .as_mut() - .ok_or_else(|| anyhow!("Cannot get doc mutable"))?; *doc = doc.with_navigation_tree(navigation_tree); Ok(()) } } -/// Optionally returns an internal asset, first checking for any static assets -/// in Rust, then checking any previously retrieved static assets from the -/// isolate, and then finally, the tsc isolate itself. -async fn get_asset( - specifier: &ModuleSpecifier, +/// Get all the assets stored in the tsc isolate. +async fn get_isolate_assets( ts_server: &TsServer, state_snapshot: Arc, -) -> Result, AnyError> { - let specifier_str = specifier.to_string().replace("asset:///", ""); - if let Some(text) = tsc::get_asset(&specifier_str) { - let maybe_asset = Some(AssetDocument::new(specifier.clone(), text)); - Ok(maybe_asset) - } else { - let res = ts_server - .request(state_snapshot, RequestMethod::GetAsset(specifier.clone())) - .await?; - let maybe_text: Option = serde_json::from_value(res)?; - let maybe_asset = - maybe_text.map(|text| AssetDocument::new(specifier.clone(), text)); - Ok(maybe_asset) +) -> Vec { + let res: Value = ts_server + .request(state_snapshot, RequestMethod::GetAssets) + .await + .unwrap(); + let response_assets = match res { + Value::Array(value) => value, + _ => unreachable!(), + }; + let mut assets = Vec::with_capacity(response_assets.len()); + + for asset in response_assets { + let mut obj = match asset { + Value::Object(obj) => obj, + _ => unreachable!(), + }; + let specifier_str = obj.get("specifier").unwrap().as_str().unwrap(); + let specifier = ModuleSpecifier::parse(specifier_str).unwrap(); + let text = match obj.remove("text").unwrap() { + Value::String(text) => text, + _ => unreachable!(), + }; + assets.push(AssetDocument::new(specifier, text)); } + + assets } fn get_tag_body_text( @@ -829,16 +809,14 @@ pub struct DocumentSpan { } impl DocumentSpan { - pub async fn to_link( + pub fn to_link( &self, line_index: Arc, language_server: &language_server::Inner, ) -> Option { let target_specifier = normalize_specifier(&self.file_name).ok()?; - let target_asset_or_doc = language_server - .get_asset_or_document(&target_specifier) - .await - .ok()?; + let target_asset_or_doc = + language_server.get_maybe_asset_or_document(&target_specifier)?; let target_line_index = target_asset_or_doc.line_index(); let target_uri = language_server .url_map @@ -883,7 +861,7 @@ impl DocumentSpan { ) -> Option { let specifier = normalize_specifier(&self.file_name).ok()?; let asset_or_doc = - language_server.get_maybe_cached_asset_or_document(&specifier)?; + language_server.get_maybe_asset_or_document(&specifier)?; let line_index = asset_or_doc.line_index(); let range = self.text_span.to_range(line_index); let mut target = language_server @@ -927,15 +905,13 @@ pub struct NavigateToItem { } impl NavigateToItem { - pub async fn to_symbol_information( + pub fn to_symbol_information( &self, language_server: &mut language_server::Inner, ) -> Option { let specifier = normalize_specifier(&self.file_name).ok()?; - let asset_or_doc = language_server - .get_asset_or_document(&specifier) - .await - .ok()?; + let asset_or_doc = + language_server.get_asset_or_document(&specifier).ok()?; let line_index = asset_or_doc.line_index(); let uri = language_server .url_map @@ -1148,15 +1124,12 @@ impl ImplementationLocation { } } - pub async fn to_link( + pub fn to_link( &self, line_index: Arc, language_server: &language_server::Inner, ) -> Option { - self - .document_span - .to_link(line_index, language_server) - .await + self.document_span.to_link(line_index, language_server) } } @@ -1185,8 +1158,7 @@ impl RenameLocations { for location in self.locations.iter() { let specifier = normalize_specifier(&location.document_span.file_name)?; let uri = language_server.url_map.normalize_specifier(&specifier)?; - let asset_or_doc = - language_server.get_asset_or_document(&specifier).await?; + let asset_or_doc = language_server.get_asset_or_document(&specifier)?; // ensure TextDocumentEdit for `location.file_name`. if text_document_edit_map.get(&uri).is_none() { @@ -1276,7 +1248,6 @@ impl DefinitionInfoAndBoundSpan { if let Some(link) = di .document_span .to_link(line_index.clone(), language_server) - .await { location_links.push(link); } @@ -1345,13 +1316,12 @@ pub struct FileTextChanges { } impl FileTextChanges { - pub async fn to_text_document_edit( + pub fn to_text_document_edit( &self, language_server: &language_server::Inner, ) -> Result { let specifier = normalize_specifier(&self.file_name)?; - let asset_or_doc = - language_server.get_asset_or_document(&specifier).await?; + let asset_or_doc = language_server.get_asset_or_document(&specifier)?; let edits = self .text_changes .iter() @@ -1359,22 +1329,21 @@ impl FileTextChanges { .collect(); Ok(lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { - uri: specifier.clone(), + uri: specifier, version: asset_or_doc.document_lsp_version(), }, edits, }) } - pub async fn to_text_document_change_ops( + pub fn to_text_document_change_ops( &self, language_server: &language_server::Inner, ) -> Result, AnyError> { let mut ops = Vec::::new(); let specifier = normalize_specifier(&self.file_name)?; let maybe_asset_or_document = if !self.is_new_file.unwrap_or(false) { - let asset_or_doc = - language_server.get_asset_or_document(&specifier).await?; + let asset_or_doc = language_server.get_asset_or_document(&specifier)?; Some(asset_or_doc) } else { None @@ -1404,7 +1373,7 @@ impl FileTextChanges { .collect(); ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { - uri: specifier.clone(), + uri: specifier, version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()), }, edits, @@ -1609,7 +1578,7 @@ impl RefactorEditInfo { ) -> Result, AnyError> { let mut all_ops = Vec::::new(); for edit in self.edits.iter() { - let ops = edit.to_text_document_change_ops(language_server).await?; + let ops = edit.to_text_document_change_ops(language_server)?; all_ops.extend(ops); } @@ -1700,16 +1669,14 @@ pub struct CallHierarchyItem { } impl CallHierarchyItem { - pub async fn try_resolve_call_hierarchy_item( + pub fn try_resolve_call_hierarchy_item( &self, language_server: &language_server::Inner, maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.file).ok()?; - let target_asset_or_doc = language_server - .get_asset_or_document(&target_specifier) - .await - .ok()?; + let target_asset_or_doc = + language_server.get_maybe_asset_or_document(&target_specifier)?; Some(self.to_call_hierarchy_item( target_asset_or_doc.line_index(), @@ -1801,16 +1768,14 @@ pub struct CallHierarchyIncomingCall { } impl CallHierarchyIncomingCall { - pub async fn try_resolve_call_hierarchy_incoming_call( + pub fn try_resolve_call_hierarchy_incoming_call( &self, language_server: &language_server::Inner, maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.from.file).ok()?; - let target_asset_or_doc = language_server - .get_asset_or_document(&target_specifier) - .await - .ok()?; + let target_asset_or_doc = + language_server.get_maybe_asset_or_document(&target_specifier)?; Some(lsp::CallHierarchyIncomingCall { from: self.from.to_call_hierarchy_item( @@ -1835,17 +1800,15 @@ pub struct CallHierarchyOutgoingCall { } impl CallHierarchyOutgoingCall { - pub async fn try_resolve_call_hierarchy_outgoing_call( + pub fn try_resolve_call_hierarchy_outgoing_call( &self, line_index: Arc, language_server: &language_server::Inner, maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.to.file).ok()?; - let target_asset_or_doc = language_server - .get_asset_or_document(&target_specifier) - .await - .ok()?; + let target_asset_or_doc = + language_server.get_maybe_asset_or_document(&target_specifier)?; Some(lsp::CallHierarchyOutgoingCall { to: self.to.to_call_hierarchy_item( @@ -2612,9 +2575,7 @@ fn op_get_length( let state = state.borrow_mut::(); let mark = state.performance.mark("op_get_length", Some(&args)); let specifier = state.normalize_specifier(args.specifier)?; - let r = if let Some(Some(asset)) = - state.state_snapshot.assets.get_cached(&specifier) - { + let r = if let Some(asset) = state.state_snapshot.assets.get(&specifier) { Ok(asset.length()) } else { cache_snapshot(state, &specifier, args.version.clone())?; @@ -2645,8 +2606,8 @@ fn op_get_text( let state = state.borrow_mut::(); let mark = state.performance.mark("op_get_text", Some(&args)); let specifier = state.normalize_specifier(args.specifier)?; - let maybe_asset = state.state_snapshot.assets.get_cached(&specifier); - let content = if let Some(Some(content)) = &maybe_asset { + let maybe_asset = state.state_snapshot.assets.get(&specifier); + let content = if let Some(content) = &maybe_asset { content.text_str() } else { cache_snapshot(state, &specifier, args.version.clone())?; @@ -2978,8 +2939,7 @@ pub enum RequestMethod { find_in_comments: bool, provide_prefix_and_suffix_text_for_rename: bool, }, - /// Retrieve the text of an assets that exists in memory in the isolate. - GetAsset(ModuleSpecifier), + GetAssets, /// Retrieve the possible refactor info for a range of a file. GetApplicableRefactors((ModuleSpecifier, TextSpan, String)), /// Retrieve the refactor edit info for a range. @@ -3060,10 +3020,9 @@ impl RequestMethod { "providePrefixAndSuffixTextForRename": provide_prefix_and_suffix_text_for_rename }) } - RequestMethod::GetAsset(specifier) => json!({ + RequestMethod::GetAssets => json!({ "id": id, - "method": "getAsset", - "specifier": specifier, + "method": "getAssets", }), RequestMethod::GetApplicableRefactors((specifier, span, kind)) => json!({ "id": id, @@ -3743,31 +3702,32 @@ mod tests { } #[test] - fn test_request_asset() { + fn test_request_assets() { let temp_dir = TempDir::new(); - let (mut runtime, state_snapshot, _) = setup( - &temp_dir, - false, - json!({ - "target": "esnext", - "module": "esnext", - "lib": ["deno.ns", "deno.window"], - "noEmit": true, - }), - &[], - ); - let specifier = - resolve_url("asset:///lib.esnext.d.ts").expect("could not resolve url"); + let (mut runtime, state_snapshot, _) = + setup(&temp_dir, false, json!({}), &[]); let result = request( &mut runtime, state_snapshot, - RequestMethod::GetAsset(specifier), + RequestMethod::GetAssets, Default::default(), - ); - assert!(result.is_ok()); - let response: Option = - serde_json::from_value(result.unwrap()).unwrap(); - assert!(response.is_some()); + ) + .unwrap(); + let assets = result.as_array().unwrap(); + + // You might have found this assertion starts failing after upgrading TypeScript. + // Just update the new number of assets (declaration files) for this number. + assert_eq!(assets.len(), 66); + + // get some notification when the size of the assets grows + let mut total_size = 0; + for asset in assets { + let obj = asset.as_object().unwrap(); + let text = obj.get("text").unwrap().as_str().unwrap(); + total_size += text.len(); + } + assert!(total_size > 0); + assert!(total_size < 2_000_000); // currently as of TS 4.6, it's 0.7MB } #[test] diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index c124703a8c..68f4f37f62 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -611,12 +611,17 @@ delete Object.prototype.__proto__; ), ); } - case "getAsset": { - const sourceFile = host.getSourceFile( - request.specifier, - ts.ScriptTarget.ESNext, - ); - return respond(id, sourceFile && sourceFile.text); + case "getAssets": { + const assets = []; + for (const sourceFile of sourceFileCache.values()) { + if (sourceFile.fileName.startsWith(ASSETS)) { + assets.push({ + specifier: sourceFile.fileName, + text: sourceFile.text, + }); + } + } + return respond(id, assets); } case "getApplicableRefactors": { return respond( diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index 946b234867..e844848842 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -46,7 +46,7 @@ declare global { type LanguageServerRequest = | ConfigureRequest | FindRenameLocationsRequest - | GetAsset + | GetAssets | GetApplicableRefactors | GetEditsForRefactor | GetCodeFixes @@ -91,9 +91,8 @@ declare global { providePrefixAndSuffixTextForRename: boolean; } - interface GetAsset extends BaseLanguageServerRequest { - method: "getAsset"; - specifier: string; + interface GetAssets extends BaseLanguageServerRequest { + method: "getAssets"; } interface GetApplicableRefactors extends BaseLanguageServerRequest {