diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-12-22 21:21:18 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-22 21:21:18 +1100 |
commit | 097c3379ba8a5dce5d9a73771693205d8178792d (patch) | |
tree | 085b775e016f219782455d923f8bdf924916d4e0 /cli/lsp/language_server.rs | |
parent | b091c6c8c9cf33cc27b25560feaeea1eb23dd345 (diff) |
feat(lsp): support the unstable setting (#8851)
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 260 |
1 files changed, 179 insertions, 81 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 0a9d81bf3..7834cab7f 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -89,49 +89,34 @@ impl LanguageServer { config.settings.enable } - pub async fn update_import_map(&self) -> Result<(), AnyError> { - let (maybe_import_map, maybe_root_uri) = { - let config = self.config.read().unwrap(); - (config.settings.import_map.clone(), config.root_uri.clone()) - }; - if let Some(import_map_str) = &maybe_import_map { - info!("update import map"); - let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str) + pub async fn get_line_index( + &self, + specifier: ModuleSpecifier, + ) -> Result<Vec<u32>, AnyError> { + let line_index = if specifier.as_url().scheme() == "asset" { + let state_snapshot = self.snapshot(); + if let Some(source) = + tsc::get_asset(&specifier, &self.ts_server, &state_snapshot).await? { - Ok(url) - } else if let Some(root_uri) = &maybe_root_uri { - let root_path = root_uri - .to_file_path() - .map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?; - let import_map_path = root_path.join(import_map_str); - Url::from_file_path(import_map_path).map_err(|_| { - anyhow!("Bad file path for import map: {:?}", import_map_str) - }) + text::index_lines(&source) } else { - Err(anyhow!( - "The path to the import map (\"{}\") is not resolvable.", - import_map_str - )) - }?; - let import_map_path = import_map_url - .to_file_path() - .map_err(|_| anyhow!("Bad file path."))?; - let import_map_json = - fs::read_to_string(import_map_path).await.map_err(|err| { - anyhow!( - "Failed to load the import map at: {}. [{}]", - import_map_url, - err - ) - })?; - let import_map = - ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?; - *self.maybe_import_map_uri.write().unwrap() = Some(import_map_url); - *self.maybe_import_map.write().unwrap() = Some(import_map); + return Err(anyhow!("asset source missing: {}", specifier)); + } } else { - *self.maybe_import_map.write().unwrap() = None; - } - Ok(()) + let file_cache = self.file_cache.read().unwrap(); + if let Some(file_id) = file_cache.lookup(&specifier) { + let file_text = file_cache.get_contents(file_id)?; + text::index_lines(&file_text) + } else { + let mut sources = self.sources.write().unwrap(); + if let Some(line_index) = sources.get_line_index(&specifier) { + line_index + } else { + return Err(anyhow!("source for specifier not found: {}", specifier)); + } + } + }; + Ok(line_index) } async fn prepare_diagnostics(&self) -> Result<(), AnyError> { @@ -260,34 +245,76 @@ impl LanguageServer { } } - pub async fn get_line_index( - &self, - specifier: ModuleSpecifier, - ) -> Result<Vec<u32>, AnyError> { - let line_index = if specifier.as_url().scheme() == "asset" { - let state_snapshot = self.snapshot(); - if let Some(source) = - tsc::get_asset(&specifier, &self.ts_server, &state_snapshot).await? + pub async fn update_import_map(&self) -> Result<(), AnyError> { + let (maybe_import_map, maybe_root_uri) = { + let config = self.config.read().unwrap(); + (config.settings.import_map.clone(), config.root_uri.clone()) + }; + if let Some(import_map_str) = &maybe_import_map { + info!("update import map"); + let import_map_url = if let Ok(url) = Url::from_file_path(import_map_str) { - text::index_lines(&source) + Ok(url) + } else if let Some(root_uri) = &maybe_root_uri { + let root_path = root_uri + .to_file_path() + .map_err(|_| anyhow!("Bad root_uri: {}", root_uri))?; + let import_map_path = root_path.join(import_map_str); + Url::from_file_path(import_map_path).map_err(|_| { + anyhow!("Bad file path for import map: {:?}", import_map_str) + }) } else { - return Err(anyhow!("asset source missing: {}", specifier)); - } + Err(anyhow!( + "The path to the import map (\"{}\") is not resolvable.", + import_map_str + )) + }?; + let import_map_path = import_map_url + .to_file_path() + .map_err(|_| anyhow!("Bad file path."))?; + let import_map_json = + fs::read_to_string(import_map_path).await.map_err(|err| { + anyhow!( + "Failed to load the import map at: {}. [{}]", + import_map_url, + err + ) + })?; + let import_map = + ImportMap::from_json(&import_map_url.to_string(), &import_map_json)?; + *self.maybe_import_map_uri.write().unwrap() = Some(import_map_url); + *self.maybe_import_map.write().unwrap() = Some(import_map); } else { - let file_cache = self.file_cache.read().unwrap(); - if let Some(file_id) = file_cache.lookup(&specifier) { - let file_text = file_cache.get_contents(file_id)?; - text::index_lines(&file_text) - } else { - let mut sources = self.sources.write().unwrap(); - if let Some(line_index) = sources.get_line_index(&specifier) { - line_index - } else { - return Err(anyhow!("source for specifier not found: {}", specifier)); - } + *self.maybe_import_map.write().unwrap() = None; + } + Ok(()) + } + + async fn update_tsconfig(&self) -> Result<(), AnyError> { + let mut tsconfig = TsConfig::new(json!({ + "allowJs": true, + "experimentalDecorators": true, + "isolatedModules": true, + "lib": ["deno.ns", "deno.window"], + "module": "esnext", + "noEmit": true, + "strict": true, + "target": "esnext", + })); + { + let config = self.config.read().unwrap(); + if config.settings.unstable { + let unstable_libs = json!({ + "lib": ["deno.ns", "deno.window", "deno.unstable"] + }); + tsconfig.merge(&unstable_libs); } - }; - Ok(line_index) + } + self + .ts_server + .request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig)) + .await?; + Ok(()) } } @@ -331,23 +358,9 @@ impl lspower::LanguageServer for LanguageServer { config.update_capabilities(¶ms.capabilities); } - // TODO(@kitsonk) need to make this configurable, respect unstable - let ts_config = TsConfig::new(json!({ - "allowJs": true, - "experimentalDecorators": true, - "isolatedModules": true, - "lib": ["deno.ns", "deno.window"], - "module": "esnext", - "noEmit": true, - "strict": true, - "target": "esnext", - })); - // TODO(lucacasonato): handle error correctly - self - .ts_server - .request(self.snapshot(), tsc::RequestMethod::Configure(ts_config)) - .await - .unwrap(); + if let Err(err) = self.update_tsconfig().await { + warn!("Updating tsconfig has errored: {}", err); + } Ok(InitializeResult { capabilities, @@ -502,6 +515,12 @@ impl lspower::LanguageServer for LanguageServer { .show_message(MessageType::Warning, err.to_string()) .await; } + if let Err(err) = self.update_tsconfig().await { + self + .client + .show_message(MessageType::Warning, err.to_string()) + .await; + } } _ => error!("received empty extension settings from the client"), } @@ -1014,4 +1033,83 @@ mod tests { ]); harness.run().await; } + + #[tokio::test] + async fn test_hover_unstable_disabled() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ("did_open_notification_unstable.json", LspResponse::None), + ( + "hover_request.json", + LspResponse::Request( + 2, + json!({ + "contents": [ + { + "language": "typescript", + "value": "any" + } + ], + "range": { + "start": { + "line": 0, + "character": 17 + }, + "end": { + "line": 0, + "character": 28 + } + } + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } + + #[tokio::test] + async fn test_hover_unstable_enabled() { + let mut harness = LspTestHarness::new(vec![ + ("initialize_request_unstable.json", LspResponse::RequestAny), + ("initialized_notification.json", LspResponse::None), + ("did_open_notification_unstable.json", LspResponse::None), + ( + "hover_request.json", + LspResponse::Request( + 2, + json!({ + "contents": [ + { + "language": "typescript", + "value": "const Deno.permissions: Deno.Permissions" + }, + "**UNSTABLE**: Under consideration to move to `navigator.permissions` to\nmatch web API. It could look like `navigator.permissions.query({ name: Deno.symbols.read })`." + ], + "range": { + "start": { + "line": 0, + "character": 17 + }, + "end": { + "line": 0, + "character": 28 + } + } + }), + ), + ), + ( + "shutdown_request.json", + LspResponse::Request(3, json!(null)), + ), + ("exit_notification.json", LspResponse::None), + ]); + harness.run().await; + } } |