summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs260
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(&params.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;
+ }
}