diff options
-rw-r--r-- | cli/lsp/analysis.rs | 21 | ||||
-rw-r--r-- | cli/lsp/code_lens.rs | 7 | ||||
-rw-r--r-- | cli/lsp/completions.rs | 87 | ||||
-rw-r--r-- | cli/lsp/config.rs | 14 | ||||
-rw-r--r-- | cli/lsp/diagnostics.rs | 97 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 155 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 11 |
7 files changed, 181 insertions, 211 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index bbc30043d..03329a8b9 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -1,5 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +use super::documents::Documents; use super::language_server; use super::tsc; @@ -171,14 +172,11 @@ fn code_as_string(code: &Option<lsp::NumberOrString>) -> String { fn check_specifier( specifier: &str, referrer: &ModuleSpecifier, - snapshot: &language_server::StateSnapshot, + documents: &Documents, ) -> Option<String> { for ext in SUPPORTED_EXTENSIONS { let specifier_with_ext = format!("{}{}", specifier, ext); - if snapshot - .documents - .contains_import(&specifier_with_ext, referrer) - { + if documents.contains_import(&specifier_with_ext, referrer) { return Some(specifier_with_ext); } } @@ -190,10 +188,9 @@ fn check_specifier( pub(crate) fn fix_ts_import_changes( referrer: &ModuleSpecifier, changes: &[tsc::FileTextChanges], - language_server: &language_server::Inner, + documents: &Documents, ) -> Result<Vec<tsc::FileTextChanges>, AnyError> { let mut r = Vec::new(); - let snapshot = language_server.snapshot()?; for change in changes { let mut text_changes = Vec::new(); for text_change in &change.text_changes { @@ -205,7 +202,7 @@ pub(crate) fn fix_ts_import_changes( .ok_or_else(|| anyhow!("Missing capture."))? .as_str(); if let Some(new_specifier) = - check_specifier(specifier, referrer, &snapshot) + check_specifier(specifier, referrer, documents) { let new_text = text_change.new_text.replace(specifier, &new_specifier); @@ -234,7 +231,7 @@ pub(crate) fn fix_ts_import_changes( fn fix_ts_import_action( referrer: &ModuleSpecifier, action: &tsc::CodeFixAction, - language_server: &language_server::Inner, + documents: &Documents, ) -> Result<tsc::CodeFixAction, AnyError> { if action.fix_name == "import" { let change = action @@ -251,9 +248,8 @@ fn fix_ts_import_action( .get(1) .ok_or_else(|| anyhow!("Missing capture."))? .as_str(); - let snapshot = language_server.snapshot()?; if let Some(new_specifier) = - check_specifier(specifier, referrer, &snapshot) + check_specifier(specifier, referrer, documents) { let description = action.description.replace(specifier, &new_specifier); let changes = action @@ -622,7 +618,8 @@ impl CodeActionCollection { "The action returned from TypeScript is unsupported.", )); } - let action = fix_ts_import_action(specifier, action, language_server)?; + let action = + fix_ts_import_action(specifier, action, &language_server.documents)?; let edit = ts_changes_to_edit(&action.changes, language_server).await?; let code_action = lsp::CodeAction { title: action.description.clone(), diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index 885e8cb1f..9a07cc21d 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -248,7 +248,7 @@ async fn resolve_implementation_code_lens( data.specifier.clone(), line_index.offset_tsc(code_lens.range.start)?, )); - let snapshot = language_server.snapshot()?; + let snapshot = language_server.snapshot(); let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = language_server.ts_server.request(snapshot, req).await?; if let Some(implementations) = maybe_implementations { @@ -317,7 +317,7 @@ async fn resolve_references_code_lens( data.specifier.clone(), line_index.offset_tsc(code_lens.range.start)?, )); - let snapshot = language_server.snapshot()?; + let snapshot = language_server.snapshot(); let maybe_references: Option<Vec<tsc::ReferenceEntry>> = language_server.ts_server.request(snapshot, req).await?; if let Some(references) = maybe_references { @@ -332,7 +332,8 @@ async fn resolve_references_code_lens( .get_asset_or_document(&reference_specifier) .await?; locations.push( - reference.to_location(asset_or_doc.line_index(), language_server), + reference + .to_location(asset_or_doc.line_index(), &language_server.url_map), ); } let command = if !locations.is_empty() { diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index 00d0631b8..beab247b7 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -1,8 +1,11 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use super::client::Client; +use super::config::ConfigSnapshot; +use super::documents::Documents; use super::language_server; use super::lsp_custom; +use super::registries::ModuleRegistry; use super::tsc; use crate::fs_util::is_supported_ext; @@ -37,18 +40,12 @@ pub struct CompletionItemData { /// a notification to the client. async fn check_auto_config_registry( url_str: &str, - snapshot: &language_server::StateSnapshot, + config: &ConfigSnapshot, client: Client, + module_registries: &ModuleRegistry, ) { // check to see if auto discovery is enabled - if snapshot - .config - .settings - .workspace - .suggest - .imports - .auto_discover - { + if config.settings.workspace.suggest.imports.auto_discover { if let Ok(specifier) = resolve_url(url_str) { let scheme = specifier.scheme(); let path = &specifier[Position::BeforePath..]; @@ -57,26 +54,18 @@ async fn check_auto_config_registry( && url_str.ends_with(path) { // check to see if this origin is already explicitly set - let in_config = snapshot - .config - .settings - .workspace - .suggest - .imports - .hosts - .iter() - .any(|(h, _)| { - resolve_url(h).map(|u| u.origin()) == Ok(specifier.origin()) - }); + let in_config = + config.settings.workspace.suggest.imports.hosts.iter().any( + |(h, _)| { + resolve_url(h).map(|u| u.origin()) == Ok(specifier.origin()) + }, + ); // if it isn't in the configuration, we will check to see if it supports // suggestions and send a notification to the client. if !in_config { let origin = specifier.origin().ascii_serialization(); - let suggestions = snapshot - .module_registries - .check_origin(&origin) - .await - .is_ok(); + let suggestions = + module_registries.check_origin(&origin).await.is_ok(); // we are only sending registry state when enabled now, but changing // the custom notification would make older versions of the plugin // incompatible. @@ -133,10 +122,12 @@ fn to_narrow_lsp_range( pub(crate) async fn get_import_completions( specifier: &ModuleSpecifier, position: &lsp::Position, - state_snapshot: &language_server::StateSnapshot, + config: &ConfigSnapshot, client: Client, + module_registries: &ModuleRegistry, + documents: &Documents, ) -> Option<lsp::CompletionResponse> { - let document = state_snapshot.documents.get(specifier)?; + let document = documents.get(specifier)?; let (text, _, range) = document.get_maybe_dependency(position)?; let range = to_narrow_lsp_range(&document.text_info(), &range); // completions for local relative modules @@ -147,25 +138,19 @@ pub(crate) async fn get_import_completions( })) } else if !text.is_empty() { // completion of modules from a module registry or cache - check_auto_config_registry(&text, state_snapshot, client).await; + check_auto_config_registry(&text, config, client, module_registries).await; let offset = if position.character > range.start.character { (position.character - range.start.character) as usize } else { 0 }; - let maybe_list = state_snapshot - .module_registries + let maybe_list = module_registries .get_completions(&text, offset, &range, |specifier| { - state_snapshot.documents.contains_specifier(specifier) + documents.contains_specifier(specifier) }) .await; let list = maybe_list.unwrap_or_else(|| lsp::CompletionList { - items: get_workspace_completions( - specifier, - &text, - &range, - state_snapshot, - ), + items: get_workspace_completions(specifier, &text, &range, documents), is_incomplete: false, }); Some(lsp::CompletionResponse::List(list)) @@ -182,9 +167,8 @@ pub(crate) async fn get_import_completions( }) .collect(); let mut is_incomplete = false; - if let Some(origin_items) = state_snapshot - .module_registries - .get_origin_completions(&text, &range) + if let Some(origin_items) = + module_registries.get_origin_completions(&text, &range) { is_incomplete = origin_items.is_incomplete; items.extend(origin_items.items); @@ -302,10 +286,9 @@ fn get_workspace_completions( specifier: &ModuleSpecifier, current: &str, range: &lsp::Range, - state_snapshot: &language_server::StateSnapshot, + documents: &Documents, ) -> Vec<lsp::CompletionItem> { - let workspace_specifiers = state_snapshot - .documents + let workspace_specifiers = documents .documents(false, true) .into_iter() .map(|d| d.specifier().clone()) @@ -454,11 +437,11 @@ mod tests { use std::sync::Arc; use tempfile::TempDir; - fn mock_state_snapshot( + fn mock_documents( fixtures: &[(&str, &str, i32, LanguageId)], source_fixtures: &[(&str, &str)], location: &Path, - ) -> language_server::StateSnapshot { + ) -> Documents { let mut documents = Documents::new(location); for (specifier, source, version, language_id) in fixtures { let specifier = @@ -482,19 +465,16 @@ mod tests { "source could not be setup" ); } - language_server::StateSnapshot { - documents, - ..Default::default() - } + documents } fn setup( documents: &[(&str, &str, i32, LanguageId)], sources: &[(&str, &str)], - ) -> language_server::StateSnapshot { + ) -> Documents { let temp_dir = TempDir::new().expect("could not create temp dir"); let location = temp_dir.path().join("deps"); - mock_state_snapshot(documents, sources, &location) + mock_documents(documents, sources, &location) } #[test] @@ -653,7 +633,7 @@ mod tests { character: 21, }, }; - let state_snapshot = setup( + let documents = setup( &[ ( "file:///a/b/c.ts", @@ -665,8 +645,7 @@ mod tests { ], &[("https://deno.land/x/a/b/c.ts", "console.log(1);\n")], ); - let actual = - get_workspace_completions(&specifier, "h", &range, &state_snapshot); + let actual = get_workspace_completions(&specifier, "h", &range, &documents); assert_eq!( actual, vec![lsp::CompletionItem { diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 7be4135d2..89924a22a 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -194,7 +194,6 @@ impl WorkspaceSettings { #[derive(Debug, Clone, Default)] pub struct ConfigSnapshot { pub client_capabilities: ClientCapabilities, - pub root_uri: Option<Url>, pub settings: Settings, pub workspace_folders: Option<Vec<lsp::WorkspaceFolder>>, } @@ -224,7 +223,6 @@ pub struct Settings { #[derive(Debug)] pub struct Config { pub client_capabilities: ClientCapabilities, - pub root_uri: Option<Url>, settings: Arc<RwLock<Settings>>, tx: mpsc::Sender<ConfigRequest>, pub workspace_folders: Option<Vec<WorkspaceFolder>>, @@ -326,7 +324,6 @@ impl Config { Self { client_capabilities: ClientCapabilities::default(), - root_uri: None, settings, tx, workspace_folders: None, @@ -345,15 +342,10 @@ impl Config { Ok(()) } - pub fn snapshot(&self) -> Result<ConfigSnapshot, AnyError> { - Ok(ConfigSnapshot { + pub fn snapshot(&self) -> Arc<ConfigSnapshot> { + Arc::new(ConfigSnapshot { client_capabilities: self.client_capabilities.clone(), - root_uri: self.root_uri.clone(), - settings: self - .settings - .try_read() - .ok_or_else(|| anyhow!("Error reading settings."))? - .clone(), + settings: self.settings.read().clone(), workspace_folders: self.workspace_folders.clone(), }) } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index a3f6a65af..853a8da73 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -2,6 +2,7 @@ use super::analysis; use super::client::Client; +use super::config::ConfigSnapshot; use super::documents; use super::documents::Documents; use super::language_server; @@ -9,6 +10,7 @@ use super::performance::Performance; use super::tsc; use super::tsc::TsServer; +use crate::config_file::LintConfig; use crate::diagnostics; use deno_ast::MediaType; @@ -193,11 +195,20 @@ impl DiagnosticsServer { dirty = false; debounce_timer.as_mut().reset(Instant::now() + NEVER); - let snapshot = language_server.lock().await.snapshot().unwrap(); + let (snapshot, config, maybe_lint_config) = { + let language_server = language_server.lock().await; + ( + language_server.snapshot(), + language_server.config.snapshot(), + language_server.maybe_lint_config.clone(), + ) + }; update_diagnostics( &client, collection.clone(), snapshot, + config, + maybe_lint_config, &ts_server, performance.clone(), ).await; @@ -326,10 +337,11 @@ fn ts_json_to_diagnostics( async fn generate_lint_diagnostics( snapshot: &language_server::StateSnapshot, collection: Arc<Mutex<DiagnosticCollection>>, + config: &ConfigSnapshot, + maybe_lint_config: Option<LintConfig>, ) -> Result<DiagnosticVec, AnyError> { let documents = snapshot.documents.documents(true, true); - let workspace_settings = snapshot.config.settings.workspace.clone(); - let maybe_lint_config = snapshot.maybe_lint_config.clone(); + let workspace_settings = config.settings.workspace.clone(); tokio::task::spawn(async move { let mut diagnostics_vec = Vec::new(); @@ -539,13 +551,14 @@ fn diagnose_dependency( /// dependencies on the local file system or in the DENO_DIR cache. async fn generate_deps_diagnostics( snapshot: Arc<language_server::StateSnapshot>, + config: Arc<ConfigSnapshot>, collection: Arc<Mutex<DiagnosticCollection>>, ) -> Result<DiagnosticVec, AnyError> { tokio::task::spawn(async move { let mut diagnostics_vec = Vec::new(); for document in snapshot.documents.documents(true, true) { - if !snapshot.config.specifier_enabled(document.specifier()) { + if !config.specifier_enabled(document.specifier()) { continue; } let version = document.maybe_lsp_version(); @@ -590,11 +603,12 @@ async fn publish_diagnostics( client: &Client, collection: &mut DiagnosticCollection, snapshot: &language_server::StateSnapshot, + config: &ConfigSnapshot, ) { if let Some(changes) = collection.take_changes() { for specifier in changes { let mut diagnostics: Vec<lsp::Diagnostic> = - if snapshot.config.settings.workspace.lint { + if config.settings.workspace.lint { collection .get(&specifier, DiagnosticSource::DenoLint) .cloned() @@ -602,7 +616,7 @@ async fn publish_diagnostics( } else { Vec::new() }; - if snapshot.config.specifier_enabled(&specifier) { + if config.specifier_enabled(&specifier) { diagnostics.extend( collection .get(&specifier, DiagnosticSource::TypeScript) @@ -629,6 +643,8 @@ async fn update_diagnostics( client: &Client, collection: Arc<Mutex<DiagnosticCollection>>, snapshot: Arc<language_server::StateSnapshot>, + config: Arc<ConfigSnapshot>, + maybe_lint_config: Option<LintConfig>, ts_server: &tsc::TsServer, performance: Arc<Performance>, ) { @@ -637,18 +653,23 @@ async fn update_diagnostics( let lint = async { let mark = performance.mark("update_diagnostics_lint", None::<()>); let collection = collection.clone(); - let diagnostics = generate_lint_diagnostics(&snapshot, collection.clone()) - .await - .map_err(|err| { - error!("Error generating lint diagnostics: {}", err); - }) - .unwrap_or_default(); + let diagnostics = generate_lint_diagnostics( + &snapshot, + collection.clone(), + &config, + maybe_lint_config, + ) + .await + .map_err(|err| { + error!("Error generating lint diagnostics: {}", err); + }) + .unwrap_or_default(); let mut collection = collection.lock().await; for diagnostic_record in diagnostics { collection.set(DiagnosticSource::DenoLint, diagnostic_record); } - publish_diagnostics(client, &mut collection, &snapshot).await; + publish_diagnostics(client, &mut collection, &snapshot, &config).await; performance.measure(mark); }; @@ -666,25 +687,28 @@ async fn update_diagnostics( for diagnostic_record in diagnostics { collection.set(DiagnosticSource::TypeScript, diagnostic_record); } - publish_diagnostics(client, &mut collection, &snapshot).await; + publish_diagnostics(client, &mut collection, &snapshot, &config).await; performance.measure(mark); }; let deps = async { let mark = performance.mark("update_diagnostics_deps", None::<()>); let collection = collection.clone(); - let diagnostics = - generate_deps_diagnostics(snapshot.clone(), collection.clone()) - .await - .map_err(|err| { - error!("Error generating Deno diagnostics: {}", err); - }) - .unwrap_or_default(); + let diagnostics = generate_deps_diagnostics( + snapshot.clone(), + config.clone(), + collection.clone(), + ) + .await + .map_err(|err| { + error!("Error generating Deno diagnostics: {}", err); + }) + .unwrap_or_default(); let mut collection = collection.lock().await; for diagnostic_record in diagnostics { collection.set(DiagnosticSource::Deno, diagnostic_record); } - publish_diagnostics(client, &mut collection, &snapshot).await; + publish_diagnostics(client, &mut collection, &snapshot, &config).await; performance.measure(mark); }; @@ -719,7 +743,14 @@ mod tests { Arc::new(source.to_string()), ); } - let config = ConfigSnapshot { + StateSnapshot { + documents, + ..Default::default() + } + } + + fn mock_config() -> ConfigSnapshot { + ConfigSnapshot { settings: Settings { workspace: WorkspaceSettings { enable: true, @@ -729,27 +760,28 @@ mod tests { ..Default::default() }, ..Default::default() - }; - StateSnapshot { - config, - documents, - ..Default::default() } } fn setup( sources: &[(&str, &str, i32, LanguageId)], - ) -> (StateSnapshot, Arc<Mutex<DiagnosticCollection>>, PathBuf) { + ) -> ( + StateSnapshot, + Arc<Mutex<DiagnosticCollection>>, + PathBuf, + ConfigSnapshot, + ) { let temp_dir = TempDir::new().expect("could not create temp dir"); let location = temp_dir.path().join("deps"); let state_snapshot = mock_state_snapshot(sources, &location); let collection = Arc::new(Mutex::new(DiagnosticCollection::default())); - (state_snapshot, collection, location) + let config = mock_config(); + (state_snapshot, collection, location, config) } #[tokio::test] async fn test_generate_lint_diagnostics() { - let (snapshot, collection, _) = setup(&[( + let (snapshot, collection, _, config) = setup(&[( "file:///a.ts", r#"import * as b from "./b.ts"; @@ -759,7 +791,8 @@ console.log(a); 1, LanguageId::TypeScript, )]); - let result = generate_lint_diagnostics(&snapshot, collection).await; + let result = + generate_lint_diagnostics(&snapshot, collection, &config, None).await; assert!(result.is_ok()); let diagnostics = result.unwrap(); assert_eq!(diagnostics.len(), 1); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index be4b23ff7..e33ffc93a 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -74,15 +74,12 @@ const CACHE_PATH: &str = "deps"; #[derive(Debug, Clone)] pub struct LanguageServer(Arc<tokio::sync::Mutex<Inner>>); +/// Snapshot of the state used by TSC. #[derive(Debug, Default)] pub(crate) struct StateSnapshot { pub assets: AssetsSnapshot, - pub config: ConfigSnapshot, pub documents: Documents, - pub maybe_lint_config: Option<LintConfig>, - pub maybe_fmt_config: Option<FmtConfig>, - pub module_registries: registries::ModuleRegistry, - pub url_map: urls::LspUrlMap, + pub root_uri: Option<Url>, } #[derive(Debug)] @@ -97,7 +94,7 @@ pub(crate) struct Inner { diagnostics_server: diagnostics::DiagnosticsServer, /// The collection of documents that the server is currently handling, either /// on disk or "open" within the client. - documents: Documents, + pub(crate) documents: Documents, /// Handles module registries, which allow discovery of modules module_registries: registries::ModuleRegistry, /// The path to the module registries cache @@ -111,7 +108,7 @@ pub(crate) struct Inner { /// options. maybe_config_file: Option<ConfigFile>, /// An optional configuration for linter which has been taken from specified config file. - maybe_lint_config: Option<LintConfig>, + pub(crate) maybe_lint_config: Option<LintConfig>, /// An optional configuration for formatter which has been taken from specified config file. maybe_fmt_config: Option<FmtConfig>, /// An optional import map which is used to resolve modules. @@ -120,6 +117,8 @@ pub(crate) struct Inner { maybe_import_map_uri: Option<Url>, /// A collection of measurements which instrument that performance of the LSP. performance: Arc<Performance>, + /// Root provided by the initialization parameters. + root_uri: Option<Url>, /// A memoized version of fixable diagnostic codes retrieved from TypeScript. ts_fixable_diagnostics: Vec<String>, /// An abstraction that handles interactions with TypeScript. @@ -169,6 +168,7 @@ impl Inner { maybe_import_map_uri: None, module_registries, module_registries_location, + root_uri: None, performance, ts_fixable_diagnostics: Default::default(), ts_server, @@ -248,12 +248,8 @@ impl Inner { self .assets .get_cached(specifier) - .map(|maybe_asset| { - maybe_asset - .as_ref() - .map(|asset| AssetOrDocument::Asset(asset.clone())) - }) .flatten() + .map(AssetOrDocument::Asset) } else { self.documents.get(specifier).map(AssetOrDocument::Document) } @@ -275,7 +271,7 @@ impl Inner { let navigation_tree: tsc::NavigationTree = self .ts_server .request( - self.snapshot()?, + self.snapshot(), tsc::RequestMethod::GetNavigationTree(specifier.clone()), ) .await?; @@ -302,7 +298,7 @@ impl Inner { /// If there's no config file specified in settings returns `None`. fn get_config_file(&self) -> Result<Option<ConfigFile>, AnyError> { let workspace_settings = self.config.get_workspace_settings(); - let maybe_root_uri = self.config.root_uri.clone(); + let maybe_root_uri = self.root_uri.clone(); let maybe_config = workspace_settings.config; if let Some(config_str) = &maybe_config { if !config_str.is_empty() { @@ -387,39 +383,24 @@ impl Inner { Ok(()) } - pub(crate) fn snapshot(&self) -> LspResult<Arc<StateSnapshot>> { - Ok(Arc::new(StateSnapshot { + pub(crate) fn snapshot(&self) -> Arc<StateSnapshot> { + Arc::new(StateSnapshot { assets: self.assets.snapshot(), - config: self.config.snapshot().map_err(|err| { - error!("{}", err); - LspError::internal_error() - })?, documents: self.documents.clone(), - maybe_lint_config: self.maybe_lint_config.clone(), - maybe_fmt_config: self.maybe_fmt_config.clone(), - module_registries: self.module_registries.clone(), - // it's ok to get an Arc to the url map because the url_map is a cache - // that does not change once the mapping for a specifier is set - url_map: self.url_map.clone(), - })) + root_uri: self.root_uri.clone(), + }) } pub fn update_cache(&mut self) -> Result<(), AnyError> { let mark = self.performance.mark("update_cache", None::<()>); self.performance.measure(mark); self.maybe_cache_server = None; - let (maybe_cache, maybe_root_uri) = { - let config = &self.config; - ( - config.get_workspace_settings().cache, - config.root_uri.clone(), - ) - }; + let maybe_cache = self.config.get_workspace_settings().cache; let maybe_cache_path = if let Some(cache_str) = &maybe_cache { lsp_log!("Setting cache path from: \"{}\"", cache_str); let cache_url = if let Ok(url) = Url::from_file_path(cache_str) { Ok(url) - } else if let Some(root_uri) = &maybe_root_uri { + } else if let Some(root_uri) = &self.root_uri { let root_path = fs_util::specifier_to_file_path(root_uri)?; let cache_path = root_path.join(cache_str); Url::from_file_path(cache_path).map_err(|_| { @@ -459,13 +440,7 @@ impl Inner { pub async fn update_import_map(&mut self) -> Result<(), AnyError> { let mark = self.performance.mark("update_import_map", None::<()>); self.maybe_cache_server = None; - let (maybe_import_map, maybe_root_uri) = { - let config = &self.config; - ( - config.get_workspace_settings().import_map, - config.root_uri.clone(), - ) - }; + let maybe_import_map = self.config.get_workspace_settings().import_map; if let Some(import_map_str) = &maybe_import_map { lsp_log!("Setting import map from: \"{}\"", import_map_str); let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str) @@ -475,7 +450,7 @@ impl Inner { Url::parse(import_map_str).map_err(|_| { anyhow!("Bad data url for import map: {:?}", import_map_str) }) - } else if let Some(root_uri) = &maybe_root_uri { + } else if let Some(root_uri) = &self.root_uri { let root_path = fs_util::specifier_to_file_path(root_uri)?; let import_map_path = root_path.join(import_map_str); Url::from_file_path(import_map_path).map_err(|_| { @@ -598,7 +573,7 @@ impl Inner { } let _ok: bool = self .ts_server - .request(self.snapshot()?, tsc::RequestMethod::Configure(tsconfig)) + .request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig)) .await?; self.performance.measure(mark); Ok(()) @@ -646,20 +621,19 @@ impl Inner { } { - let config = &mut self.config; // sometimes this root uri may not have a trailing slash, so force it to - config.root_uri = params + self.root_uri = params .root_uri .map(|s| self.url_map.normalize_url(&s)) .map(fs_util::ensure_directory_specifier); if let Some(value) = params.initialization_options { - config.set_workspace_settings(value).map_err(|err| { + self.config.set_workspace_settings(value).map_err(|err| { error!("Cannot set workspace settings: {}", err); LspError::internal_error() })?; } - config.update_capabilities(¶ms.capabilities); + self.config.update_capabilities(¶ms.capabilities); } self.update_debug_flag(); @@ -677,7 +651,7 @@ impl Inner { if capabilities.code_action_provider.is_some() { let fixable_diagnostics: Vec<String> = self .ts_server - .request(self.snapshot()?, tsc::RequestMethod::GetSupportedCodeFixes) + .request(self.snapshot(), tsc::RequestMethod::GetSupportedCodeFixes) .await .map_err(|err| { error!("Unable to get fixable diagnostics: {}", err); @@ -1136,7 +1110,7 @@ impl Inner { )); let maybe_quick_info: Option<tsc::QuickInfo> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Unable to get quick info: {}", err); @@ -1216,7 +1190,7 @@ impl Inner { codes, )); let actions: Vec<tsc::CodeFixAction> = - match self.ts_server.request(self.snapshot()?, req).await { + match self.ts_server.request(self.snapshot(), req).await { Ok(items) => items, Err(err) => { // sometimes tsc reports errors when retrieving code actions @@ -1289,7 +1263,7 @@ impl Inner { )); let refactor_infos: Vec<tsc::ApplicableRefactorInfo> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1336,6 +1310,7 @@ impl Inner { let result = if kind.as_str().starts_with(CodeActionKind::QUICKFIX.as_str()) { + let snapshot = self.snapshot(); let code_action_data: CodeActionData = from_value(data).map_err(|err| { error!("Unable to decode code action data: {}", err); @@ -1347,7 +1322,7 @@ impl Inner { )); let combined_code_actions: tsc::CombinedCodeActions = self .ts_server - .request(self.snapshot()?, req) + .request(snapshot.clone(), req) .await .map_err(|err| { error!("Unable to get combined fix from TypeScript: {}", err); @@ -1362,7 +1337,7 @@ impl Inner { fix_ts_import_changes( &code_action_data.specifier, &combined_code_actions.changes, - self, + &self.documents, ) .map_err(|err| { error!("Unable to remap changes: {}", err); @@ -1379,6 +1354,7 @@ impl Inner { })?; code_action } else if kind.as_str().starts_with(CodeActionKind::REFACTOR.as_str()) { + let snapshot = self.snapshot(); let mut code_action = params.clone(); let action_data: refactor::RefactorCodeActionData = from_value(data) .map_err(|err| { @@ -1396,11 +1372,8 @@ impl Inner { action_data.refactor_name.clone(), action_data.action_name.clone(), )); - let refactor_edit_info: tsc::RefactorEditInfo = self - .ts_server - .request(self.snapshot()?, req) - .await - .map_err(|err| { + let refactor_edit_info: tsc::RefactorEditInfo = + self.ts_server.request(snapshot, req).await.map_err(|err| { error!("Failed to request to tsserver {}", err); LspError::invalid_request() })?; @@ -1506,7 +1479,7 @@ impl Inner { )); let maybe_document_highlights: Option<Vec<tsc::DocumentHighlights>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Unable to get document highlights from TypeScript: {}", err); @@ -1549,7 +1522,7 @@ impl Inner { )); let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Unable to get references from TypeScript: {}", err); @@ -1571,7 +1544,8 @@ impl Inner { self.get_asset_or_document(&reference_specifier).await?; asset_or_doc.line_index() }; - results.push(reference.to_location(reference_line_index, self)); + results + .push(reference.to_location(reference_line_index, &self.url_map)); } self.performance.measure(mark); @@ -1604,7 +1578,7 @@ impl Inner { )); let maybe_definition: Option<tsc::DefinitionInfoAndBoundSpan> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Unable to get definition from TypeScript: {}", err); @@ -1644,7 +1618,7 @@ impl Inner { }; let maybe_definition_info: Option<Vec<tsc::DefinitionInfo>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Unable to get type definition from TypeScript: {}", err); @@ -1688,12 +1662,13 @@ impl Inner { // 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 // other completions come from. - let snapshot = self.snapshot()?; let response = if let Some(response) = completions::get_import_completions( &specifier, ¶ms.text_document_position.position, - &snapshot, + &self.config.snapshot(), self.client.clone(), + &self.module_registries, + &self.documents, ) .await { @@ -1722,11 +1697,9 @@ impl Inner { trigger_character, }, )); - let maybe_completion_info: Option<tsc::CompletionInfo> = self - .ts_server - .request(self.snapshot()?, req) - .await - .map_err(|err| { + let snapshot = self.snapshot(); + let maybe_completion_info: Option<tsc::CompletionInfo> = + self.ts_server.request(snapshot, req).await.map_err(|err| { error!("Unable to get completion info from TypeScript: {}", err); LspError::internal_error() })?; @@ -1762,14 +1735,13 @@ impl Inner { })?; if let Some(data) = data.tsc { let req = tsc::RequestMethod::GetCompletionDetails(data.into()); - let maybe_completion_info: Option<tsc::CompletionEntryDetails> = self - .ts_server - .request(self.snapshot()?, req) - .await - .map_err(|err| { - error!("Unable to get completion info from TypeScript: {}", err); - LspError::internal_error() - })?; + let maybe_completion_info: Option<tsc::CompletionEntryDetails> = + self.ts_server.request(self.snapshot(), req).await.map_err( + |err| { + error!("Unable to get completion info from TypeScript: {}", err); + LspError::internal_error() + }, + )?; if let Some(completion_info) = maybe_completion_info { completion_info.as_completion_item(¶ms) } else { @@ -1817,7 +1789,7 @@ impl Inner { )); let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1859,7 +1831,7 @@ impl Inner { let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let outlining_spans: Vec<tsc::OutliningSpan> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1907,7 +1879,7 @@ impl Inner { )); let incoming_calls: Vec<tsc::CallHierarchyIncomingCall> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1915,7 +1887,6 @@ impl Inner { })?; let maybe_root_path_owned = self - .config .root_uri .as_ref() .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); @@ -1956,7 +1927,7 @@ impl Inner { )); let outgoing_calls: Vec<tsc::CallHierarchyOutgoingCall> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1964,7 +1935,6 @@ impl Inner { })?; let maybe_root_path_owned = self - .config .root_uri .as_ref() .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); @@ -2011,7 +1981,7 @@ impl Inner { let maybe_one_or_many: Option<tsc::OneOrMany<tsc::CallHierarchyItem>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2020,7 +1990,6 @@ impl Inner { let response = if let Some(one_or_many) = maybe_one_or_many { let maybe_root_path_owned = self - .config .root_uri .as_ref() .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); @@ -2087,7 +2056,7 @@ impl Inner { let maybe_locations: Option<Vec<tsc::RenameLocation>> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2173,7 +2142,7 @@ impl Inner { let selection_range: tsc::SelectionRange = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2211,7 +2180,7 @@ impl Inner { )); let semantic_classification: tsc::Classifications = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2254,7 +2223,7 @@ impl Inner { )); let semantic_classification: tsc::Classifications = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2307,7 +2276,7 @@ impl Inner { )); let maybe_signature_help_items: Option<tsc::SignatureHelpItems> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed to request to tsserver: {}", err); @@ -2339,7 +2308,7 @@ impl Inner { let navigate_to_items: Vec<tsc::NavigateToItem> = self .ts_server - .request(self.snapshot()?, req) + .request(self.snapshot(), req) .await .map_err(|err| { error!("Failed request to tsserver: {}", err); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index c61f7c215..268646506 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -14,6 +14,7 @@ use super::semantic_tokens; use super::semantic_tokens::SemanticTokensBuilder; use super::text; use super::text::LineIndex; +use super::urls::LspUrlMap; use super::urls::INVALID_SPECIFIER; use crate::config_file::TsConfig; @@ -265,13 +266,13 @@ impl Assets { specifier: &ModuleSpecifier, // todo(dsherret): this shouldn't be a parameter, but instead retrieved via // a constructor dependency - get_snapshot: impl Fn() -> LspResult<Arc<StateSnapshot>>, + get_snapshot: impl Fn() -> Arc<StateSnapshot>, ) -> LspResult<Option<AssetDocument>> { // 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()?) + let maybe_asset = get_asset(specifier, &self.ts_server, get_snapshot()) .await .map_err(|err| { error!("Error getting asset {}: {}", specifier, err); @@ -1528,12 +1529,11 @@ impl ReferenceEntry { pub(crate) fn to_location( &self, line_index: Arc<LineIndex>, - language_server: &language_server::Inner, + url_map: &LspUrlMap, ) -> lsp::Location { let specifier = normalize_specifier(&self.document_span.file_name) .unwrap_or_else(|_| INVALID_SPECIFIER.clone()); - let uri = language_server - .url_map + let uri = url_map .normalize_specifier(&specifier) .unwrap_or_else(|_| INVALID_SPECIFIER.clone()); lsp::Location { @@ -2628,7 +2628,6 @@ fn start( state_snapshot: &StateSnapshot, ) -> Result<(), AnyError> { let root_uri = state_snapshot - .config .root_uri .clone() .unwrap_or_else(|| Url::parse("cache:///").unwrap()); |