Repository: denoland/deno
Pull request: fix(lsp): tsgo scope attribution for asset files

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

Changed files:
filename: cli/lsp/documents.rs
patch: @@ -1159,7 +1159,7 @@ pub struct DocumentModules {
   dep_info_by_scope: once_cell::sync::OnceCell<Arc<DepInfoByScope>>,
   modules_unscoped: Arc<WeakDocumentModuleMap>,
   modules_by_scope: Arc<BTreeMap<Arc<Url>, Arc<WeakDocumentModuleMap>>>,
-  assigned_scopes: Arc<DashMap<Arc<Uri>, ScopeInfo>>,
+  assigned_scopes: Arc<RwLock<HashMap<Arc<Uri>, ScopeInfo>>>,
 }
 
 impl DocumentModules {
@@ -1294,6 +1294,15 @@ impl DocumentModules {
     self.documents.remove_server_doc(&module.uri);
   }
 
+  pub fn assign_scopes(
+    &self,
+    entries: impl IntoIterator<
+      Item = (Arc<Uri>, (Option<Arc<Url>>, CompilerOptionsKey)),
+    >,
+  ) {
+    self.assigned_scopes.write().extend(entries);
+  }
+
   fn infer_specifier(&self, document: &Document) -> Option<Arc<Url>> {
     if let Some(document) = document.server() {
       match &document.kind {
@@ -1400,7 +1409,7 @@ impl DocumentModules {
       compiler_options_key,
     );
     if let Some(module) = &module {
-      self.assigned_scopes.insert(
+      self.assigned_scopes.write().insert(
         document.uri().clone(),
         (module.scope.clone(), module.compiler_options_key.clone()),
       );
@@ -1458,9 +1467,9 @@ impl DocumentModules {
     if let Some(scope) = self.primary_scope(document.uri()) {
       return self.module(document, scope.map(|s| s.as_ref()));
     }
-    if let Some((scope, compiler_options_key)) =
-      self.assigned_scopes.get(document.uri()).map(|e| e.clone())
-    {
+    let assigned_scope =
+      self.assigned_scopes.read().get(document.uri()).cloned();
+    if let Some((scope, compiler_options_key)) = assigned_scope {
       return self.module_inner(
         document,
         None,

filename: cli/lsp/tsgo.rs
patch: @@ -310,13 +310,19 @@ impl TsGoWorkspaceConfig {
   }
 }
 
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
 #[serde(rename_all = "camelCase")]
 struct TsGoWorkspaceChange {
   file_changes: Vec<TsGoFileChange>,
   new_configuration: Option<Arc<TsGoWorkspaceConfig>>,
 }
 
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct TsGoWorkspaceInfo {
+  compiler_options_keys_for_assets: HashMap<Uri, CompilerOptionsKey>,
+}
+
 #[derive(Debug)]
 struct PendingChangeTracker {
   file_changes: Vec<TsGoFileChange>,
@@ -419,11 +425,12 @@ enum TsGoRequest {
   WorkspaceSymbol { query: String },
 }
 
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
 #[serde(rename_all = "camelCase")]
-struct TsGoRequestParams {
-  request: TsGoRequest,
-  workspace_change: Option<TsGoWorkspaceChange>,
+enum TsGoRequestParams {
+  ApplyWorkspaceChange(TsGoWorkspaceChange),
+  #[serde(untagged)]
+  Request(TsGoRequest),
 }
 
 /// Returns if the project files were changed.
@@ -1117,22 +1124,18 @@ impl TsGoServerInner {
 
   async fn request<R>(
     &self,
-    request: TsGoRequest,
+    params: TsGoRequestParams,
     token: &CancellationToken,
   ) -> Result<R, AnyError>
   where
     R: DeserializeOwned,
   {
-    let workspace_change = self.pending_change_tracker.lock().take_change();
-    let (_read, _write) = if workspace_change.is_some() {
-      (None, Some(self.pending_change_lock.write().await))
-    } else {
-      (Some(self.pending_change_lock.read().await), None)
-    };
-    let params = TsGoRequestParams {
-      request,
-      workspace_change,
-    };
+    let (_read, _write) =
+      if matches!(&params, TsGoRequestParams::ApplyWorkspaceChange(_)) {
+        (None, Some(self.pending_change_lock.write().await))
+      } else {
+        (Some(self.pending_change_lock.read().await), None)
+      };
 
     let request_id = self.next_request_id.fetch_add(1, Ordering::SeqCst);
 
@@ -1187,7 +1190,7 @@ impl TsGoServerInner {
 pub struct TsGoServer {
   deno_dir: DenoDir,
   http_client_provider: Arc<HttpClientProvider>,
-  inner: tokio::sync::OnceCell<TsGoServerInner>,
+  inner: tokio::sync::OnceCell<Arc<TsGoServerInner>>,
 }
 
 impl TsGoServer {
@@ -1202,7 +1205,7 @@ impl TsGoServer {
     }
   }
 
-  async fn inner(&self, snapshot: Arc<StateSnapshot>) -> &TsGoServerInner {
+  async fn inner(&self, snapshot: Arc<StateSnapshot>) -> &Arc<TsGoServerInner> {
     self
       .inner
       .get_or_init(async || {
@@ -1212,7 +1215,7 @@ impl TsGoServer {
         )
         .await
         .unwrap();
-        TsGoServerInner::init(tsgo_path, snapshot).await
+        Arc::new(TsGoServerInner::init(tsgo_path, snapshot).await)
       })
       .await
   }
@@ -1247,8 +1250,36 @@ impl TsGoServer {
   where
     R: DeserializeOwned,
   {
-    let inner = self.inner(snapshot).await;
-    inner.request(request, token).await
+    let inner = self.inner(snapshot.clone()).await;
+    let inner_clone = inner.clone();
+    tokio::task::spawn(async move {
+      let workspace_change =
+        inner_clone.pending_change_tracker.lock().take_change();
+      if let Some(workspace_change) = workspace_change {
+        let workspace_info = inner_clone
+          .request::<TsGoWorkspaceInfo>(
+            TsGoRequestParams::ApplyWorkspaceChange(workspace_change),
+            &Default::default(),
+          )
+          .await
+          .unwrap();
+        let asset_scopes = workspace_info
+          .compiler_options_keys_for_assets
+          .into_iter()
+          .map(|(asset_uri, compiler_options_key)| {
+            (
+              Arc::new(normalize_uri(&asset_uri)),
+              (None, compiler_options_key),
+            )
+          });
+        snapshot.document_modules.assign_scopes(asset_scopes);
+      }
+    })
+    .await
+    .unwrap();
+    inner
+      .request(TsGoRequestParams::Request(request), token)
+      .await
   }
 
   pub async fn get_ambient_modules(

filename: cli/tsc/go/tsgo_version.rs
patch: @@ -25,16 +25,16 @@ impl Hashes {
   }
 }
 
-pub const VERSION: &str = "0.1.26";
+pub const VERSION: &str = "0.1.27";
 pub const DOWNLOAD_BASE_URL: &str =
-  "https://github.com/denoland/typescript-go/releases/download/v0.1.26";
+  "https://github.com/denoland/typescript-go/releases/download/v0.1.27";
 pub const HASHES: Hashes = Hashes {
-  windows_x64: "sha256:a0c4b9cfa3ca77c91ddd5fae04ae0f1f63eeb9a8eb10150016e9adc32d4eb459",
-  windows_arm64: "sha256:94f08fa3e66f88e62a46d4ff10f29a2ef382297dac380273474f1fb76d6de58f",
-  macos_x64: "sha256:6c3f2168df400c4dd225cecc98e37c4ad1562d240f183afd72fb6796547e104d",
-  macos_arm64: "sha256:e0efb302eedc80408f42e3d97f795d8d7af90e8fb99aa5b32e847d28987301c1",
-  linux_x64: "sha256:551a454dde34276f0e26193e17e2e0cc8c75f3faeff0aee326d3a9f7f5a2e121",
-  linux_arm64: "sha256:65158be2f0fcb15cf81ce7ca83fb9eb3155a3c248730103a4d081d5ae55cfbc9",
+  windows_x64: "sha256:7326fb1f8607fa44f8fb713062f74e1bd714570458d3761fd7212f04f3fa9ac4",
+  windows_arm64: "sha256:e9a18b6abedb5376cc8849664341b3666f555a7658b1ce6a41cd01554e845e86",
+  macos_x64: "sha256:652f47daf184c595f7813bbe4e01fdba6b059a65f8d58dd49af41d06a48fce2b",
+  macos_arm64: "sha256:09805e0f37384156304485cea24eb07046febbbca083d259f29943ae7e2e9f7e",
+  linux_x64: "sha256:a023ef6b892d5f5b98c8666cea4f904a2c46cfd100e9ecfb94464ff0254badf1",
+  linux_arm64: "sha256:f5bfd8c49d10d9e68aa4f7f3adb20560f8427fd8d33402cc1a907580bf5737e9",
 };
 
 const _: () = {

filename: tests/integration/lsp_tests.rs
patch: @@ -1656,13 +1656,13 @@ fn lsp_hover(use_tsgo: bool) {
   client.shutdown();
 }
 
-#[test(timeout = 300)]
-fn lsp_hover_asset() {
+#[test(timeout = 300, fork_with_suffix = "_tsgo")]
+fn lsp_hover_asset(use_tsgo: bool) {
   let context = TestContextBuilder::new().use_temp_cwd().build();
   let temp_dir = context.temp_dir();
   temp_dir.write("deno.json", json!({}).to_string());
   let file = temp_dir.source_file("file.ts", "console.log(Date.now());\n");
-  let mut client = context.new_lsp_command().build();
+  let mut client = context.new_lsp_command().set_use_tsgo(use_tsgo).build();
   client.initialize_default();
   client.did_open_file(&file);
   let res = client.write_request(
@@ -1676,16 +1676,32 @@ fn lsp_hover_asset() {
   );
   assert_eq!(
     res,
-    json!({
-      "contents": {
-        "kind": "markdown",
-        "value": "```tsx\ninterface Date\n```\nEnables basic storage and retrieval of dates and times.\n\n*@category* — Temporal\n\n\n*@experimental*",
-      },
-      "range": {
-        "start": { "line": 111, "character": 10, },
-        "end": { "line": 111, "character": 14, }
-      }
-    })
+    // TODO(nayeemrmn): The wrong doc and position is returned by tsgo. Seems
+    // to be treating `interface Date` like `declare var Date` in a different
+    // lib asset.
+    if use_tsgo {
+      json!({
+        "contents": {
+          "kind": "markdown",
+          "value": "```tsx\nvar Date: DateConstructor\ninterface Date\n```\n",
+        },
+        "range": {
+          "start": { "line": 111, "character": 12, },
+          "end": { "line": 111, "character": 16, }
+        }
+      })
+    } else {
+      json!({
+        "contents": {
+          "kind": "markdown",
+          "value": "```tsx\ninterface Date\n```\nEnables basic storage and retrieval of dates and times.\n\n*@category* — Temporal\n\n\n*@experimental*",
+        },
+        "range": {
+          "start": { "line": 111, "character": 10, },
+          "end": { "line": 111, "character": 14, }
+        }
+      })
+    },
   );
   client.shutdown();
 }