Repository: denoland/deno
Pull request: fix(lsp): store assigned compiler options for cache files

- Fixes #31315
- Fixes https://github.com/denoland/vscode_deno/issues/1335

Changed files:
filename: cli/lsp/documents.rs
patch: @@ -1033,6 +1033,8 @@ impl WeakDocumentModuleMap {
   }
 }
 
+type ScopeInfo = (Option<Arc<Url>>, CompilerOptionsKey);
+
 #[derive(Debug, Default, Clone)]
 pub struct DocumentModules {
   pub documents: Documents,
@@ -1044,6 +1046,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>>,
 }
 
 impl DocumentModules {
@@ -1071,6 +1074,7 @@ impl DocumentModules {
         .collect(),
     );
     self.dep_info_by_scope = Default::default();
+    self.assigned_scopes = Default::default();
 
     node_resolver::PackageJsonThreadLocalCache::clear();
     NodeResolutionThreadLocalCache::clear();
@@ -1275,12 +1279,19 @@ impl DocumentModules {
       self
         .documents
         .get_for_specifier(&specifier, scope, &self.cache)?;
-    self.module_inner(
+    let module = self.module_inner(
       &document,
       Some(&Arc::new(specifier)),
       scope,
       compiler_options_key,
-    )
+    );
+    if let Some(module) = &module {
+      self.assigned_scopes.insert(
+        document.uri().clone(),
+        (module.scope.clone(), module.compiler_options_key.clone()),
+      );
+    }
+    module
   }
 
   pub fn primary_module(
@@ -1290,6 +1301,16 @@ 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())
+    {
+      return self.module_inner(
+        document,
+        None,
+        scope.as_deref(),
+        Some(&compiler_options_key),
+      );
+    }
     for modules in self.modules_by_scope.values() {
       if let Some(module) = modules.get(document) {
         return Some(module);

filename: tests/integration/lsp_tests.rs
patch: @@ -15960,6 +15960,77 @@ fn lsp_vendor_dir() {
   client.shutdown();
 }
 
+#[test]
+#[timeout(300_000)]
+fn lsp_npm_global_cache_hover() {
+  let context = TestContextBuilder::new()
+    .use_http_server()
+    .use_temp_cwd()
+    .add_npm_env_vars()
+    .build();
+  let temp_dir = context.temp_dir();
+  let file = temp_dir.source_file("main.ts", "import \"npm:chalk@5.0.1\";\n");
+  context.run_deno("check main.ts");
+  let mut client = context.new_lsp_command().build();
+  client.initialize_default();
+  client.did_open_file(&file);
+  let res = client.write_request(
+    "textDocument/definition",
+    json!({
+      "textDocument": { "uri": file.uri() },
+      "position": { "line": 0, "character": 7 },
+    }),
+  );
+  let npm_cache_file_path = context
+    .deno_dir()
+    .path()
+    .canonicalize()
+    .join("npm/localhost_4260/chalk/5.0.1/source/index.d.ts");
+  assert_eq!(
+    res,
+    json!([{
+      "targetUri": npm_cache_file_path.uri_file(),
+      "targetRange": {
+        "start": { "line": 2, "character": 0 },
+        "end": { "line": 318, "character": 0 },
+      },
+      "targetSelectionRange": {
+        "start": { "line": 2, "character": 0 },
+        "end": { "line": 318, "character": 0 },
+      },
+    }]),
+  );
+  client.did_open(json!({
+    "textDocument": {
+      "uri": npm_cache_file_path.uri_file(),
+      "languageId": "typescript",
+      "version": 1,
+      "text": npm_cache_file_path.read_to_string(),
+    },
+  }));
+  let res = client.write_request(
+    "textDocument/hover",
+    json!({
+      "textDocument": { "uri": npm_cache_file_path.uri_file() },
+      "position": { "line": 2, "character": 43 },
+    }),
+  );
+  assert_eq!(
+    res,
+    json!({
+      "contents": {
+        "kind": "markdown",
+        "value": format!("**Resolved Dependency**\n\n**Types**: file&#8203;{}\n", context.deno_dir().path().canonicalize().join("npm/localhost_4260/chalk/5.0.1/source/vendor/supports-color/index.d.ts").url_file().as_str().trim_start_matches("file")),
+      },
+      "range": {
+        "start": { "line": 2, "character": 43 },
+        "end": { "line": 2, "character": 77 },
+      },
+    }),
+  );
+  client.shutdown();
+}
+
 #[test]
 #[timeout(300_000)]
 fn lsp_deno_json_scopes_import_map() {