Skip to content

Commit

Permalink
feat(lsp): ts language service scopes (denoland#24345)
Browse files Browse the repository at this point in the history
  • Loading branch information
nayeemrmn authored and sbmsr committed Jul 2, 2024
1 parent 962b342 commit 1f87053
Show file tree
Hide file tree
Showing 8 changed files with 1,265 additions and 357 deletions.
15 changes: 13 additions & 2 deletions cli/lsp/code_lens.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use crate::lsp::logging::lsp_warn;

use super::analysis::source_range_to_lsp_range;
use super::config::CodeLensSettings;
use super::language_server;
Expand Down Expand Up @@ -27,6 +29,7 @@ use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
use std::sync::Arc;
use tower_lsp::jsonrpc::Error as LspError;
use tower_lsp::lsp_types as lsp;

static ABSTRACT_MODIFIER: Lazy<Regex> = lazy_regex!(r"\babstract\b");
Expand Down Expand Up @@ -260,7 +263,11 @@ async fn resolve_implementation_code_lens(
data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?,
)
.await?;
.await
.map_err(|err| {
lsp_warn!("{err}");
LspError::internal_error()
})?;
if let Some(implementations) = maybe_implementations {
let mut locations = Vec::new();
for implementation in implementations {
Expand Down Expand Up @@ -357,7 +364,11 @@ async fn resolve_references_code_lens(
data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?,
)
.await?;
.await
.map_err(|err| {
lsp_warn!("Unable to find references: {err}");
LspError::internal_error()
})?;
let locations = get_locations(maybe_referenced_symbols, language_server)?;
let title = if locations.len() == 1 {
"1 reference".to_string()
Expand Down
9 changes: 0 additions & 9 deletions cli/lsp/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,6 @@ impl Default for LspTsConfig {
"esModuleInterop": true,
"experimentalDecorators": false,
"isolatedModules": true,
"jsx": "react",
"lib": ["deno.ns", "deno.window", "deno.unstable"],
"module": "esnext",
"moduleDetection": "force",
Expand Down Expand Up @@ -1569,16 +1568,10 @@ impl ConfigData {

#[derive(Clone, Debug, Default)]
pub struct ConfigTree {
first_folder: Option<ModuleSpecifier>,
scopes: Arc<BTreeMap<ModuleSpecifier, Arc<ConfigData>>>,
}

impl ConfigTree {
pub fn root_ts_config(&self) -> Arc<LspTsConfig> {
let root_data = self.first_folder.as_ref().and_then(|s| self.scopes.get(s));
root_data.map(|d| d.ts_config.clone()).unwrap_or_default()
}

pub fn scope_for_specifier(
&self,
specifier: &ModuleSpecifier,
Expand Down Expand Up @@ -1773,7 +1766,6 @@ impl ConfigTree {
);
}
}
self.first_folder.clone_from(&settings.first_folder);
self.scopes = Arc::new(scopes);
}

Expand All @@ -1790,7 +1782,6 @@ impl ConfigTree {
)
.await,
);
self.first_folder = Some(scope.clone());
self.scopes = Arc::new([(scope, data)].into_iter().collect());
}
}
Expand Down
49 changes: 34 additions & 15 deletions cli/lsp/documents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ impl AssetOrDocument {
}
}

pub fn file_referrer(&self) -> Option<&ModuleSpecifier> {
match self {
AssetOrDocument::Asset(_) => None,
AssetOrDocument::Document(doc) => doc.file_referrer(),
}
}

pub fn scope(&self) -> Option<&ModuleSpecifier> {
match self {
AssetOrDocument::Asset(_) => None,
AssetOrDocument::Document(doc) => doc.scope(),
}
}

pub fn maybe_semantic_tokens(&self) -> Option<lsp::SemanticTokens> {
match self {
AssetOrDocument::Asset(_) => None,
Expand Down Expand Up @@ -605,6 +619,13 @@ impl Document {
self.file_referrer.as_ref()
}

pub fn scope(&self) -> Option<&ModuleSpecifier> {
self
.file_referrer
.as_ref()
.and_then(|r| self.config.tree.scope_for_specifier(r))
}

pub fn content(&self) -> &Arc<str> {
&self.text
}
Expand Down Expand Up @@ -926,9 +947,9 @@ pub struct Documents {
/// The npm package requirements found in npm specifiers.
npm_reqs_by_scope:
Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>>,
/// Gets if any document had a node: specifier such that a @types/node package
/// should be injected.
has_injected_types_node_package: bool,
/// Config scopes that contain a node: specifier such that a @types/node
/// package should be injected.
scopes_with_node_specifier: Arc<HashSet<Option<ModuleSpecifier>>>,
}

impl Documents {
Expand Down Expand Up @@ -1122,10 +1143,10 @@ impl Documents {
self.npm_reqs_by_scope.clone()
}

/// Returns if a @types/node package was injected into the npm
/// resolver based on the state of the documents.
pub fn has_injected_types_node_package(&self) -> bool {
self.has_injected_types_node_package
pub fn scopes_with_node_specifier(
&self,
) -> &Arc<HashSet<Option<ModuleSpecifier>>> {
&self.scopes_with_node_specifier
}

/// Return a document for the specifier.
Expand Down Expand Up @@ -1346,20 +1367,18 @@ impl Documents {
/// document.
fn calculate_npm_reqs_if_dirty(&mut self) {
let mut npm_reqs_by_scope: BTreeMap<_, BTreeSet<_>> = Default::default();
let mut scopes_with_node_builtin_specifier = HashSet::new();
let mut scopes_with_specifier = HashSet::new();
let is_fs_docs_dirty = self.file_system_docs.set_dirty(false);
if !is_fs_docs_dirty && !self.dirty {
return;
}
let mut visit_doc = |doc: &Arc<Document>| {
let scope = doc
.file_referrer()
.and_then(|r| self.config.tree.scope_for_specifier(r));
let scope = doc.scope();
let reqs = npm_reqs_by_scope.entry(scope.cloned()).or_default();
for dependency in doc.dependencies().values() {
if let Some(dep) = dependency.get_code() {
if dep.scheme() == "node" {
scopes_with_node_builtin_specifier.insert(scope.cloned());
scopes_with_specifier.insert(scope.cloned());
}
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
reqs.insert(reference.into_inner().req);
Expand Down Expand Up @@ -1402,15 +1421,15 @@ impl Documents {
// Ensure a @types/node package exists when any module uses a node: specifier.
// Unlike on the command line, here we just add @types/node to the npm package
// requirements since this won't end up in the lockfile.
for scope in scopes_with_node_builtin_specifier {
let reqs = npm_reqs_by_scope.entry(scope).or_default();
for scope in &scopes_with_specifier {
let reqs = npm_reqs_by_scope.entry(scope.clone()).or_default();
if !reqs.iter().any(|r| r.name == "@types/node") {
self.has_injected_types_node_package = true;
reqs.insert(PackageReq::from_str("@types/node").unwrap());
}
}

self.npm_reqs_by_scope = Arc::new(npm_reqs_by_scope);
self.scopes_with_node_specifier = Arc::new(scopes_with_specifier);
self.dirty = false;
}

Expand Down
Loading

0 comments on commit 1f87053

Please sign in to comment.