diff options
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 493 |
1 files changed, 177 insertions, 316 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 73d028e76..10a69fadb 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -39,6 +39,7 @@ use super::diagnostics; use super::diagnostics::DiagnosticSource; use super::documents::to_hover_text; use super::documents::to_lsp_range; +use super::documents::AssetOrDocument; use super::documents::Documents; use super::documents::LanguageId; use super::lsp_custom; @@ -47,7 +48,6 @@ use super::performance::Performance; use super::refactor; use super::registries; use super::text; -use super::text::LineIndex; use super::tsc; use super::tsc::AssetDocument; use super::tsc::Assets; @@ -171,71 +171,82 @@ impl Inner { } } - /// Searches assets, open documents and external sources for a line_index, - /// which might be performed asynchronously, hydrating in memory caches for - /// subsequent requests. - pub(crate) async fn get_line_index( + /// Searches assets and open documents which might be performed asynchronously, + /// hydrating in memory caches for subsequent requests. + pub(crate) async fn get_asset_or_document( &mut self, - specifier: ModuleSpecifier, - ) -> Result<Arc<LineIndex>, AnyError> { - let mark = self - .performance - .mark("get_line_index", Some(json!({ "specifier": specifier }))); - let result = if specifier.scheme() == "asset" { - if let Some(asset) = self.get_asset(&specifier).await? { - Ok(asset.line_index) - } else { - Err(anyhow!("asset is missing: {}", specifier)) - } - } else if let Some(line_index) = self.documents.line_index(&specifier) { - Ok(line_index) - } else { - Err(anyhow!("Unable to find line index for: {}", specifier)) - }; - self.performance.measure(mark); - result + specifier: &ModuleSpecifier, + ) -> LspResult<AssetOrDocument> { + 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, + ) } - /// Only searches already cached assets and documents for a line index. If - /// the line index cannot be found, `None` is returned. - pub fn get_line_index_sync( - &self, + /// Searches assets and open documents which might be performed asynchronously, + /// hydrating in memory caches for subsequent requests. + pub(crate) async fn get_maybe_asset_or_document( + &mut self, specifier: &ModuleSpecifier, - ) -> Option<Arc<LineIndex>> { + ) -> LspResult<Option<AssetOrDocument>> { let mark = self.performance.mark( - "get_line_index_sync", + "get_maybe_asset_or_document", Some(json!({ "specifier": specifier })), ); - let maybe_line_index = if specifier.scheme() == "asset" { - if let Some(Some(asset)) = self.assets.get(specifier) { - Some(asset.line_index.clone()) - } else { - None - } + let result = if specifier.scheme() == "asset" { + self.get_asset(specifier).await?.map(AssetOrDocument::Asset) } else { - self.documents.line_index(specifier) + self.documents.get(specifier).map(AssetOrDocument::Document) }; self.performance.measure(mark); - maybe_line_index + Ok(result) } - // TODO(@kitsonk) we really should find a better way to just return the - // content as a `&str`, or be able to get the byte at a particular offset - // which is all that this API that is consuming it is trying to do at the - // moment - /// Searches already cached assets and documents and returns its text - /// content. If not found, `None` is returned. - pub(crate) fn get_text_content( + /// Only searches already cached assets and documents. If + /// the asset or document cannot be found an error is returned. + pub(crate) fn get_cached_asset_or_document( &self, specifier: &ModuleSpecifier, - ) -> Option<Arc<String>> { + ) -> LspResult<AssetOrDocument> { + 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(crate) fn get_maybe_cached_asset_or_document( + &self, + specifier: &ModuleSpecifier, + ) -> Option<AssetOrDocument> { if specifier.scheme() == "asset" { self .assets .get(specifier) - .map(|o| o.clone().map(|a| a.text))? + .map(|maybe_asset| { + maybe_asset + .as_ref() + .map(|asset| AssetOrDocument::Asset(asset.clone())) + }) + .flatten() } else { - self.documents.content(specifier) + self.documents.get(specifier).map(AssetOrDocument::Document) } } @@ -247,33 +258,30 @@ impl Inner { "get_navigation_tree", Some(json!({ "specifier": specifier })), ); - let maybe_navigation_tree = if specifier.scheme() == "asset" { - self.assets.get_navigation_tree(specifier) - } else { - self.documents.get_navigation_tree(specifier) - }; - let navigation_tree = if let Some(navigation_tree) = maybe_navigation_tree { - navigation_tree - } else { - let navigation_tree: tsc::NavigationTree = self - .ts_server - .request( - self.snapshot()?, - tsc::RequestMethod::GetNavigationTree(specifier.clone()), - ) - .await?; - let navigation_tree = Arc::new(navigation_tree); - if specifier.scheme() == "asset" { - self - .assets - .set_navigation_tree(specifier, navigation_tree.clone())?; + let asset_or_doc = self.get_cached_asset_or_document(specifier)?; + let navigation_tree = + if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() { + navigation_tree } else { - self - .documents - .set_navigation_tree(specifier, navigation_tree.clone())?; - } - navigation_tree - }; + let navigation_tree: tsc::NavigationTree = self + .ts_server + .request( + self.snapshot()?, + tsc::RequestMethod::GetNavigationTree(specifier.clone()), + ) + .await?; + let navigation_tree = Arc::new(navigation_tree); + if specifier.scheme() == "asset" { + self + .assets + .set_navigation_tree(specifier, navigation_tree.clone())?; + } else { + self + .documents + .set_navigation_tree(specifier, navigation_tree.clone())?; + } + navigation_tree + }; self.performance.measure(mark); Ok(navigation_tree) } @@ -335,7 +343,11 @@ impl Inner { | MediaType::Dts ) } else { - self.documents.is_diagnosable(specifier) + self + .documents + .get(specifier) + .map(|d| d.is_diagnosable()) + .unwrap_or(false) } } @@ -594,22 +606,20 @@ impl Inner { Ok(()) } - pub(crate) fn document_version( - &self, - specifier: &ModuleSpecifier, - ) -> Option<i32> { - self.documents.lsp_version(specifier) - } - async fn get_asset( &mut self, specifier: &ModuleSpecifier, - ) -> Result<Option<AssetDocument>, AnyError> { + ) -> LspResult<Option<AssetDocument>> { if let Some(maybe_asset) = self.assets.get(specifier) { Ok(maybe_asset.clone()) } else { let maybe_asset = - tsc::get_asset(specifier, &self.ts_server, self.snapshot()?).await?; + tsc::get_asset(specifier, &self.ts_server, self.snapshot()?) + .await + .map_err(|err| { + error!("Error getting asset {}: {}", specifier, err); + LspError::internal_error() + })?; self.assets.insert(specifier.clone(), maybe_asset.clone()); Ok(maybe_asset) } @@ -783,14 +793,14 @@ impl Inner { ); } let content = Arc::new(params.text_document.text); - self.documents.open( + let document = self.documents.open( specifier.clone(), params.text_document.version, params.text_document.language_id.parse().unwrap(), content, ); - if self.is_diagnosable(&specifier) { + if document.is_diagnosable() { self .diagnostics_server .invalidate(self.documents.dependents(&specifier)) @@ -811,8 +821,8 @@ impl Inner { params.text_document.version, params.content_changes, ) { - Ok(()) => { - if self.is_diagnosable(&specifier) { + Ok(document) => { + if document.is_diagnosable() { self .diagnostics_server .invalidate(self.documents.dependents(&specifier)) @@ -974,16 +984,8 @@ impl Inner { } let mark = self.performance.mark("document_symbol", Some(¶ms)); - - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_document = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_document.line_index(); let req = tsc::RequestMethod::GetNavigationTree(specifier); let navigation_tree: tsc::NavigationTree = self @@ -1014,9 +1016,10 @@ impl Inner { params: DocumentFormattingParams, ) -> LspResult<Option<Vec<TextEdit>>> { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); - if !self.documents.is_formattable(&specifier) { - return Ok(None); - } + let document = match self.documents.get(&specifier) { + Some(doc) if doc.is_open() => doc, + _ => return Ok(None), + }; let mark = self.performance.mark("formatting", Some(¶ms)); let file_path = if let Ok(file_path) = params.text_document.uri.to_file_path() { @@ -1031,34 +1034,23 @@ impl Inner { Default::default() }; - let content = self.documents.content(&specifier).map_or_else( - || { - Err(LspError::invalid_params( - "The specified file could not be found in memory.", - )) - }, - Ok, - )?; - let line_index = self.documents.line_index(&specifier).unwrap(); - let maybe_parsed_source = self.documents.parsed_source(&specifier); - let text_edits = tokio::task::spawn_blocking(move || { - let format_result = match maybe_parsed_source { + let format_result = match document.maybe_parsed_source() { Some(Ok(parsed_source)) => { format_parsed_source(&parsed_source, fmt_options) } Some(Err(err)) => Err(err.to_string()), None => { // it's not a js/ts file, so attempt to format its contents - format_file(&file_path, content.as_str(), fmt_options) + format_file(&file_path, document.content().as_str(), fmt_options) } }; match format_result { Ok(new_text) => Some(text::get_edits( - content.as_str(), + document.content().as_str(), &new_text, - line_index.as_ref(), + document.line_index().as_ref(), )), Err(err) => { // TODO(lucacasonato): handle error properly @@ -1094,14 +1086,17 @@ impl Inner { } let mark = self.performance.mark("hover", Some(¶ms)); - let hover = if let Some((_, dep, range)) = - self.documents.get_maybe_dependency( - &specifier, - ¶ms.text_document_position_params.position, - ) { - let maybe_types_dependency = - self.documents.get_maybe_types_for_dependency(&dep); - let value = match (&dep.maybe_code, &dep.maybe_type, &maybe_types_dependency) { + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let hover = if let Some((_, dep, range)) = asset_or_doc + .get_maybe_dependency(¶ms.text_document_position_params.position) + { + let dep_maybe_types_dependency = dep + .get_code() + .map(|s| self.documents.get(s)) + .flatten() + .map(|d| d.maybe_types_dependency()) + .flatten(); + let value = match (&dep.maybe_code, &dep.maybe_type, &dep_maybe_types_dependency) { (Some(code_dep), Some(type_dep), None) => format!( "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", to_hover_text(code_dep), @@ -1136,15 +1131,7 @@ impl Inner { range: Some(to_lsp_range(&range)), }) } else { - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetQuickInfo(( specifier, line_index.offset_tsc(params.text_document_position_params.position)?, @@ -1176,7 +1163,8 @@ impl Inner { let mark = self.performance.mark("code_action", Some(¶ms)); let mut all_actions = CodeActionResponse::new(); - let line_index = self.get_line_index_sync(&specifier).unwrap(); + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); // QuickFix let fixable_diagnostics: Vec<&Diagnostic> = params @@ -1266,12 +1254,8 @@ impl Inner { .add_deno_lint_ignore_action( &specifier, diagnostic, - self.documents.text_info(&specifier), - self - .documents - .parsed_source(&specifier) - .map(|r| r.ok()) - .flatten(), + asset_or_doc.document().map(|d| d.text_info()), + asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten(), ) .map_err(|err| { error!("Unable to fix lint error: {}", err); @@ -1400,8 +1384,9 @@ impl Inner { error!("Unable to decode code action data: {}", err); LspError::invalid_params("The CodeAction's data is invalid.") })?; - let line_index = - self.get_line_index_sync(&action_data.specifier).unwrap(); + let asset_or_doc = + self.get_cached_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; let req = tsc::RequestMethod::GetEditsForRefactor(( @@ -1449,25 +1434,15 @@ impl Inner { } let mark = self.performance.mark("code_lens", Some(¶ms)); + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; let navigation_tree = self.get_navigation_tree(&specifier).await.map_err(|err| { error!("Error getting code lenses for \"{}\": {}", specifier, err); LspError::internal_error() })?; - let parsed_source = self - .documents - .parsed_source(&specifier) - .map(|r| r.ok()) - .flatten(); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let parsed_source = + asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten(); + let line_index = asset_or_doc.line_index(); let code_lenses = code_lens::collect( &specifier, parsed_source, @@ -1520,15 +1495,8 @@ impl Inner { } let mark = self.performance.mark("document_highlight", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let files_to_search = vec![specifier.clone()]; let req = tsc::RequestMethod::GetDocumentHighlights(( specifier, @@ -1572,15 +1540,8 @@ impl Inner { } let mark = self.performance.mark("references", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetReferences(( specifier, line_index.offset_tsc(params.text_document_position.position)?, @@ -1602,10 +1563,9 @@ impl Inner { } let reference_specifier = resolve_url(&reference.document_span.file_name).unwrap(); - // TODO(lucacasonato): handle error correctly - let line_index = - self.get_line_index(reference_specifier).await.unwrap(); - results.push(reference.to_location(line_index, self)); + let asset_or_doc = + self.get_asset_or_document(&reference_specifier).await?; + results.push(reference.to_location(asset_or_doc.line_index(), self)); } self.performance.measure(mark); @@ -1630,15 +1590,8 @@ impl Inner { } let mark = self.performance.mark("goto_definition", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetDefinition(( specifier, line_index.offset_tsc(params.text_document_position_params.position)?, @@ -1676,6 +1629,7 @@ impl Inner { } let mark = self.performance.mark("completion", Some(¶ms)); + let asset_or_doc = self.get_cached_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 @@ -1690,15 +1644,7 @@ impl Inner { { Some(response) } else { - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let line_index = asset_or_doc.line_index(); let trigger_character = if let Some(context) = ¶ms.context { context.trigger_character.clone() } else { @@ -1801,15 +1747,8 @@ impl Inner { } let mark = self.performance.mark("goto_implementation", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetImplementation(( specifier, @@ -1854,15 +1793,7 @@ impl Inner { } let mark = self.performance.mark("folding_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let outlining_spans: Vec<tsc::OutliningSpan> = self @@ -1875,20 +1806,13 @@ impl Inner { })?; let response = if !outlining_spans.is_empty() { - let text_content = - self.get_text_content(&specifier).ok_or_else(|| { - LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - )) - })?; Some( outlining_spans .iter() .map(|span| { span.to_folding_range( - line_index.clone(), - text_content.as_str().as_bytes(), + asset_or_doc.line_index(), + asset_or_doc.text().as_str().as_bytes(), self.config.client_capabilities.line_folding_only, ) }) @@ -1913,15 +1837,8 @@ impl Inner { } let mark = self.performance.mark("incoming_calls", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls(( specifier.clone(), @@ -1969,15 +1886,8 @@ impl Inner { } let mark = self.performance.mark("outgoing_calls", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls(( specifier.clone(), @@ -2030,15 +1940,8 @@ impl Inner { let mark = self .performance .mark("prepare_call_hierarchy", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::PrepareCallHierarchy(( specifier.clone(), @@ -2109,15 +2012,8 @@ impl Inner { } let mark = self.performance.mark("rename", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::FindRenameLocations { specifier, @@ -2204,15 +2100,8 @@ impl Inner { } let mark = self.performance.mark("selection_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let mut selection_ranges = Vec::<SelectionRange>::new(); for position in params.positions { @@ -2249,15 +2138,8 @@ impl Inner { } let mark = self.performance.mark("semantic_tokens_full", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetEncodedSemanticClassifications(( specifier.clone(), @@ -2300,15 +2182,8 @@ impl Inner { let mark = self .performance .mark("semantic_tokens_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let start = line_index.offset_tsc(params.range.start)?; let length = line_index.offset_tsc(params.range.end)? - start; @@ -2350,15 +2225,8 @@ impl Inner { } let mark = self.performance.mark("signature_help", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let options = if let Some(context) = params.context { tsc::SignatureHelpItemsOptions { trigger_reason: Some(tsc::SignatureHelpTriggerReason { @@ -2689,7 +2557,12 @@ impl Inner { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); let contents = if specifier.as_str() == "deno:/status.md" { let mut contents = String::new(); - let mut documents_specifiers = self.documents.specifiers(false, false); + let mut documents_specifiers = self + .documents + .documents(false, false) + .into_iter() + .map(|d| d.specifier().clone()) + .collect::<Vec<_>>(); documents_specifiers.sort(); let measures = self.performance.to_vec(); let workspace_settings = self.config.get_workspace_settings(); @@ -2743,27 +2616,15 @@ impl Inner { } Some(contents) } else { - match specifier.scheme() { - "asset" => { - if let Some(asset) = self - .get_asset(&specifier) - .await - .map_err(|_| LspError::internal_error())? - { - Some(asset.text.to_string()) - } else { - error!("Missing asset: {}", specifier); - None - } - } - _ => { - if let Some(source) = self.documents.content(&specifier) { - Some(source.to_string()) - } else { - error!("The cached source was not found: {}", specifier); - None - } - } + let asset_or_doc = self + .get_maybe_asset_or_document(&specifier) + .await + .map_err(|_| LspError::internal_error())?; + if let Some(asset_or_doc) = asset_or_doc { + Some(asset_or_doc.text().to_string()) + } else { + error!("The source was not found: {}", specifier); + None } }; self.performance.measure(mark); |