Repository: denoland/deno
Pull request: fix(lsp): implement remaining tsgo resolver callbacks

- Coupled with https://github.com/denoland/typescript-go/pull/24

Changed files:
filename: cli/lsp/documents.rs
patch: @@ -1296,7 +1296,7 @@ impl DocumentModules {
     module
   }
 
-  pub fn module_for_tsgo_referrer(
+  pub fn module_for_tsgo_document(
     &self,
     uri: &Uri,
     compiler_options_key: &CompilerOptionsKey,
@@ -1314,7 +1314,7 @@ impl DocumentModules {
     self.module(&document, scope.map(|s| s.as_ref()))
   }
 
-  pub fn module_for_tsgo_dependency(
+  pub fn module_for_tsgo_specifier(
     &self,
     specifier: &Url,
     compiler_options_key: &CompilerOptionsKey,
@@ -1855,7 +1855,7 @@ impl DocumentModules {
       specifier = Cow::Owned(s);
     }
     let module =
-      self.module_for_tsgo_dependency(&specifier, compiler_options_key)?;
+      self.module_for_tsgo_specifier(&specifier, compiler_options_key)?;
     if let Some(types) = module
       .types_dependency
       .as_ref()

filename: cli/lsp/tsgo.rs
patch: @@ -5,6 +5,7 @@ use std::collections::HashMap;
 use std::io::BufRead;
 use std::io::Write;
 use std::path::Path;
+use std::path::PathBuf;
 use std::process::Child;
 use std::process::Command;
 use std::process::Stdio;
@@ -13,12 +14,14 @@ use std::sync::Arc;
 use std::sync::atomic::AtomicI64;
 use std::sync::atomic::Ordering;
 
+use deno_ast::MediaType;
 use deno_config::deno_json::CompilerOptions;
 use deno_core::anyhow::anyhow;
 use deno_core::error::AnyError;
 use deno_core::parking_lot::Mutex;
 use deno_core::serde_json;
 use deno_graph::source::Resolver;
+use deno_path_util::url_from_directory_path;
 use deno_resolver::deno_json::CompilerOptionsKey;
 use indexmap::IndexSet;
 use lsp_types::Uri;
@@ -171,6 +174,17 @@ enum TsGoCallbackParams {
     resolution_mode: deno_typescript_go_client_rust::types::ResolutionMode,
     compiler_options_key: CompilerOptionsKey,
   },
+  #[serde(rename_all = "camelCase")]
+  ResolveJsxImportSource {
+    compiler_options_key: CompilerOptionsKey,
+  },
+  #[serde(rename_all = "camelCase")]
+  GetPackageScopeForPath { directory_path: PathBuf },
+  #[serde(rename_all = "camelCase")]
+  GetImpliedNodeFormatForFile {
+    uri: Uri,
+    compiler_options_key: CompilerOptionsKey,
+  },
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)]
@@ -726,6 +740,35 @@ impl TsGoServerInner {
       }
     });
 
+    let capabilities = {
+      let snapshot = snapshot.lock();
+      lsp::ClientCapabilities {
+        text_document: Some(lsp::TextDocumentClientCapabilities {
+          synchronization: None,
+          formatting: None,
+          range_formatting: None,
+          on_type_formatting: None,
+          publish_diagnostics: None,
+          diagnostic: Some(Default::default()),
+          ..snapshot
+            .config
+            .client_capabilities
+            .text_document
+            .clone()
+            .unwrap_or_default()
+        }),
+        workspace: Some(lsp::WorkspaceClientCapabilities {
+          symbol: snapshot
+            .config
+            .client_capabilities
+            .workspace
+            .as_ref()
+            .and_then(|w| w.symbol.clone()),
+          ..Default::default()
+        }),
+        ..Default::default()
+      }
+    };
     let initialize_request = json!({
       "jsonrpc": "2.0",
       "id": 0,
@@ -735,7 +778,7 @@ impl TsGoServerInner {
           "disablePushDiagnostics": true,
         },
         "processId": std::process::id(),
-        "capabilities": {},
+        "capabilities": capabilities,
         "rootUri": null,
         "workspaceFolders": null,
       },
@@ -812,7 +855,7 @@ impl TsGoServerInner {
       } => {
         let referrer_module = snapshot
           .document_modules
-          .module_for_tsgo_referrer(&referrer_uri, &compiler_options_key)
+          .module_for_tsgo_document(&referrer_uri, &compiler_options_key)
           .ok_or_else(|| anyhow!("Referrer module not found"))?;
         let Some((uri, media_type)) = snapshot
           .document_modules
@@ -838,6 +881,65 @@ impl TsGoServerInner {
           "extension": media_type.as_ts_extension(),
         }))
       }
+      TsGoCallbackParams::ResolveJsxImportSource {
+        compiler_options_key,
+      } => {
+        let compiler_options_data = snapshot
+          .compiler_options_resolver
+          .for_key(&compiler_options_key)
+          .unwrap();
+        let specifier = compiler_options_data
+          .jsx_import_source_config
+          .as_ref()
+          .and_then(|c| c.specifier());
+        Ok(json!(specifier))
+      }
+      TsGoCallbackParams::GetPackageScopeForPath { directory_path } => {
+        let Ok(directory_url) = url_from_directory_path(&directory_path) else {
+          return Ok(json!(null));
+        };
+        let scoped_resolver =
+          snapshot.resolver.get_scoped_resolver(Some(&directory_url));
+        let Ok(Some(package_json)) = scoped_resolver
+          .as_pkg_json_resolver()
+          .get_closest_package_json(&directory_path.join("package.json"))
+        else {
+          return Ok(json!(null));
+        };
+        Ok(json!({
+          "packageDirectoryPath": package_json.path.parent(),
+          "packageJsonText": serde_json::to_string(&package_json).unwrap(),
+        }))
+      }
+      TsGoCallbackParams::GetImpliedNodeFormatForFile {
+        uri,
+        compiler_options_key,
+      } => {
+        let referrer_module = snapshot
+          .document_modules
+          .module_for_tsgo_document(&uri, &compiler_options_key)
+          .ok_or_else(|| anyhow!("Module not found"))?;
+        let resolution_mode =
+          match (referrer_module.resolution_mode, referrer_module.media_type) {
+            (
+              _,
+              MediaType::Css
+              | MediaType::Json
+              | MediaType::Html
+              | MediaType::Sql
+              | MediaType::Wasm
+              | MediaType::SourceMap
+              | MediaType::Unknown,
+            ) => deno_typescript_go_client_rust::types::ResolutionMode::None,
+            (ResolutionMode::Import, _) => {
+              deno_typescript_go_client_rust::types::ResolutionMode::ESM
+            }
+            (ResolutionMode::Require, _) => {
+              deno_typescript_go_client_rust::types::ResolutionMode::CommonJS
+            }
+          };
+        Ok(json!(resolution_mode))
+      }
     }
   }
 

filename: cli/tsc/go/tsgo_version.rs
patch: @@ -25,16 +25,16 @@ impl Hashes {
   }
 }
 
-pub const VERSION: &str = "0.1.18";
+pub const VERSION: &str = "0.1.19";
 pub const DOWNLOAD_BASE_URL: &str =
-  "https://github.com/denoland/typescript-go/releases/download/v0.1.18";
+  "https://github.com/denoland/typescript-go/releases/download/v0.1.19";
 pub const HASHES: Hashes = Hashes {
-  windows_x64: "sha256:d66796c57a33b17a4eec565771e3554d86bc1961dfac76ce497313f7143a6824",
-  windows_arm64: "sha256:4be12a26e9854733d56811c1c42e9f27f82f8d010231bd73a790975a30735659",
-  macos_x64: "sha256:db341d3175d69c0d8f9c03b9ddef894969cd4024289ad5431f9ae47c6f18015f",
-  macos_arm64: "sha256:bc9a2d6a7786b2f39c1d444ad58894c96caa70c7fbc1684ad10068d7563d706b",
-  linux_x64: "sha256:9fc51e74189cdbc19b308e3dffd23ec0fa06fb427389049970d78d220916f9e6",
-  linux_arm64: "sha256:06b1129a5e853157c1cae35eb3722287dce6580dc70198041b129eff39c73f89",
+  windows_x64: "sha256:92f2918378fc22d09ad6d1ceb1a88885653362b2906e344f84f82442048c6abf",
+  windows_arm64: "sha256:c63c96f51b21bca8e7a54cc54c1094e664af3aecf214e92dab075067c45db903",
+  macos_x64: "sha256:ec95648702510df312da4247b8825713b5bc71c59a7a2320980d3265287d6ad9",
+  macos_arm64: "sha256:c4df21a13299eefb5b58d5728a08d9e3db3295f8d337915f9609935fb7afdfa1",
+  linux_x64: "sha256:4089d04bf695155a0f7671c95b5b4ad68d2c424f88a924c52e5bc6b22735cec4",
+  linux_arm64: "sha256:ff037938829703d743d9998fc62c62005ba177746bdcf13155a90facc4e873a2",
 };
 
 const _: () = {

filename: tests/integration/lsp_tests.rs
patch: @@ -14270,8 +14270,8 @@ fn lsp_jsx_import_source_package_json_automatic_cache() {
   client.shutdown();
 }
 
-#[test(timeout = 300)]
-fn lsp_jsx_import_source_byonm_preact() {
+#[test(timeout = 300, fork_with_suffix = "_tsgo")]
+fn lsp_jsx_import_source_byonm_preact(use_tsgo: bool) {
   let context = TestContextBuilder::new()
     .use_http_server()
     .use_temp_cwd()
@@ -14300,7 +14300,7 @@ fn lsp_jsx_import_source_byonm_preact() {
   );
   let file = source_file(temp_dir.path().join("file.tsx"), r#"<div></div>;"#);
   context.run_npm("install");
-  let mut client = context.new_lsp_command().build();
+  let mut client = context.new_lsp_command().set_use_tsgo(use_tsgo).build();
   client.initialize_default();
   let diagnostics = client.did_open_file(&file);
   assert_eq!(json!(diagnostics.all()), json!([]));
@@ -14316,7 +14316,11 @@ fn lsp_jsx_import_source_byonm_preact() {
     json!({
       "contents": {
         "kind": "markdown",
-        "value": "```typescript\n(property) JSXInternal.IntrinsicElements.div: JSXInternal.HTMLAttributes<HTMLDivElement>\n```",
+        "value": if use_tsgo {
+          "```tsx\n(property) JSXInternal.IntrinsicElements.div: JSXInternal.HTMLAttributes<HTMLDivElement>\n```\n"
+        } else {
+          "```typescript\n(property) JSXInternal.IntrinsicElements.div: JSXInternal.HTMLAttributes<HTMLDivElement>\n```"
+        },
       },
       "range": {
         "start": { "line": 0, "character": 1 },

filename: tests/util/lib/lsp.rs
patch: @@ -30,7 +30,9 @@ use lsp_types::CodeActionLiteralSupport;
 use lsp_types::CompletionClientCapabilities;
 use lsp_types::CompletionItemCapability;
 use lsp_types::FoldingRangeClientCapabilities;
+use lsp_types::HoverClientCapabilities;
 use lsp_types::InitializeParams;
+use lsp_types::MarkupKind;
 use lsp_types::TextDocumentClientCapabilities;
 use lsp_types::TextDocumentSyncClientCapabilities;
 use lsp_types::Uri;
@@ -266,6 +268,13 @@ impl InitializeParamsBuilder {
               }),
               ..Default::default()
             }),
+            hover: Some(HoverClientCapabilities {
+              content_format: Some(vec![
+                MarkupKind::Markdown,
+                MarkupKind::PlainText,
+              ]),
+              ..Default::default()
+            }),
             diagnostic: Some(Default::default()),
             folding_range: Some(FoldingRangeClientCapabilities {
               line_folding_only: Some(true),