summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/lsp/analysis.rs13
-rw-r--r--cli/lsp/completions.rs10
-rw-r--r--cli/lsp/config.rs347
-rw-r--r--cli/lsp/diagnostics.rs15
-rw-r--r--cli/lsp/documents.rs12
-rw-r--r--cli/lsp/language_server.rs110
-rw-r--r--cli/lsp/tsc.rs72
7 files changed, 271 insertions, 308 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index cd7d45928..ce5d0c7f4 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -43,7 +43,6 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::Path;
-use std::sync::Arc;
use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::Position;
use tower_lsp::lsp_types::Range;
@@ -217,7 +216,7 @@ fn code_as_string(code: &Option<lsp::NumberOrString>) -> String {
/// Rewrites imports in quick fixes and code changes to be Deno specific.
pub struct TsResponseImportMapper<'a> {
documents: &'a Documents,
- maybe_import_map: Option<Arc<ImportMap>>,
+ maybe_import_map: Option<&'a ImportMap>,
node_resolver: Option<&'a CliNodeResolver>,
npm_resolver: Option<&'a dyn CliNpmResolver>,
}
@@ -225,7 +224,7 @@ pub struct TsResponseImportMapper<'a> {
impl<'a> TsResponseImportMapper<'a> {
pub fn new(
documents: &'a Documents,
- maybe_import_map: Option<Arc<ImportMap>>,
+ maybe_import_map: Option<&'a ImportMap>,
node_resolver: Option<&'a CliNodeResolver>,
npm_resolver: Option<&'a dyn CliNpmResolver>,
) -> Self {
@@ -270,7 +269,7 @@ impl<'a> TsResponseImportMapper<'a> {
let sub_path = (export != ".").then_some(export);
let mut req = None;
req = req.or_else(|| {
- let import_map = self.maybe_import_map.as_ref()?;
+ let import_map = self.maybe_import_map?;
for entry in import_map.entries_for_referrer(referrer) {
let Some(value) = entry.raw_value else {
continue;
@@ -297,7 +296,7 @@ impl<'a> TsResponseImportMapper<'a> {
JsrPackageNvReference::new(nv_ref).to_string()
};
let specifier = ModuleSpecifier::parse(&spec_str).ok()?;
- if let Some(import_map) = &self.maybe_import_map {
+ if let Some(import_map) = self.maybe_import_map {
if let Some(result) = import_map.lookup(&specifier, referrer) {
return Some(result);
}
@@ -316,7 +315,7 @@ impl<'a> TsResponseImportMapper<'a> {
// check if any pkg reqs match what is found in an import map
if !pkg_reqs.is_empty() {
let sub_path = self.resolve_package_path(specifier);
- if let Some(import_map) = &self.maybe_import_map {
+ if let Some(import_map) = self.maybe_import_map {
let pkg_reqs = pkg_reqs.iter().collect::<HashSet<_>>();
let mut matches = Vec::new();
for entry in import_map.entries_for_referrer(referrer) {
@@ -358,7 +357,7 @@ impl<'a> TsResponseImportMapper<'a> {
}
// check if the import map has this specifier
- if let Some(import_map) = &self.maybe_import_map {
+ if let Some(import_map) = self.maybe_import_map {
if let Some(result) = import_map.lookup(specifier, referrer) {
return Some(result);
}
diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs
index 2186949fb..164b3b8c3 100644
--- a/cli/lsp/completions.rs
+++ b/cli/lsp/completions.rs
@@ -32,7 +32,6 @@ use deno_semver::package::PackageNv;
use import_map::ImportMap;
use once_cell::sync::Lazy;
use regex::Regex;
-use std::sync::Arc;
use tower_lsp::lsp_types as lsp;
static FILE_PROTO_RE: Lazy<Regex> =
@@ -155,7 +154,7 @@ pub async fn get_import_completions(
jsr_search_api: &CliJsrSearchApi,
npm_search_api: &CliNpmSearchApi,
documents: &Documents,
- maybe_import_map: Option<Arc<ImportMap>>,
+ maybe_import_map: Option<&ImportMap>,
) -> Option<lsp::CompletionResponse> {
let document = documents.get(specifier)?;
let (text, _, range) = document.get_maybe_dependency(position)?;
@@ -164,7 +163,7 @@ pub async fn get_import_completions(
specifier,
&text,
&range,
- maybe_import_map.clone(),
+ maybe_import_map,
documents,
) {
// completions for import map specifiers
@@ -238,7 +237,7 @@ pub async fn get_import_completions(
.collect();
let mut is_incomplete = false;
if let Some(import_map) = maybe_import_map {
- items.extend(get_base_import_map_completions(import_map.as_ref()));
+ items.extend(get_base_import_map_completions(import_map));
}
if let Some(origin_items) =
module_registries.get_origin_completions(&text, &range)
@@ -301,7 +300,7 @@ fn get_import_map_completions(
specifier: &ModuleSpecifier,
text: &str,
range: &lsp::Range,
- maybe_import_map: Option<Arc<ImportMap>>,
+ maybe_import_map: Option<&ImportMap>,
documents: &Documents,
) -> Option<lsp::CompletionList> {
if !text.is_empty() {
@@ -809,6 +808,7 @@ mod tests {
use deno_graph::Range;
use std::collections::HashMap;
use std::path::Path;
+ use std::sync::Arc;
use test_util::TempDir;
fn mock_documents(
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index e80851429..1a707c44c 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -731,7 +731,7 @@ pub struct ConfigSnapshot {
pub client_capabilities: ClientCapabilities,
pub settings: Settings,
pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>,
- pub tree: Arc<ConfigTree>,
+ pub tree: ConfigTree,
}
impl ConfigSnapshot {
@@ -745,7 +745,7 @@ impl ConfigSnapshot {
/// Determine if the provided specifier is enabled or not.
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
let config_file = self.tree.config_file_for_specifier(specifier);
- if let Some(cf) = &config_file {
+ if let Some(cf) = config_file {
if let Ok(files) = cf.to_files_config() {
if !files.matches_specifier(specifier) {
return false;
@@ -781,10 +781,6 @@ pub struct Settings {
}
impl Settings {
- pub fn first_root_uri(&self) -> Option<&ModuleSpecifier> {
- self.first_folder.as_ref()
- }
-
/// Returns `None` if the value should be deferred to the presence of a
/// `deno.json` file.
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> Option<bool> {
@@ -793,7 +789,7 @@ impl Settings {
return Some(true);
};
let (settings, mut folder_uri) = self.get_for_specifier(specifier);
- folder_uri = folder_uri.or_else(|| self.first_root_uri());
+ folder_uri = folder_uri.or(self.first_folder.as_ref());
let mut disable_paths = vec![];
let mut enable_paths = None;
if let Some(folder_uri) = folder_uri {
@@ -879,7 +875,7 @@ pub struct Config {
pub client_capabilities: ClientCapabilities,
pub settings: Settings,
pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>,
- pub tree: Arc<ConfigTree>,
+ pub tree: ConfigTree,
}
impl Config {
@@ -997,7 +993,7 @@ impl Config {
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
let config_file = self.tree.config_file_for_specifier(specifier);
- if let Some(cf) = &config_file {
+ if let Some(cf) = config_file {
if let Ok(files) = cf.to_files_config() {
if !files.matches_specifier(specifier) {
return false;
@@ -1086,23 +1082,51 @@ impl Config {
}
}
-pub fn default_ts_config() -> TsConfig {
- TsConfig::new(json!({
- "allowJs": true,
- "esModuleInterop": true,
- "experimentalDecorators": false,
- "isolatedModules": true,
- "jsx": "react",
- "lib": ["deno.ns", "deno.window", "deno.unstable"],
- "module": "esnext",
- "moduleDetection": "force",
- "noEmit": true,
- "resolveJsonModule": true,
- "strict": true,
- "target": "esnext",
- "useDefineForClassFields": true,
- "useUnknownInCatchVariables": false,
- }))
+#[derive(Debug, Serialize)]
+pub struct LspTsConfig {
+ #[serde(flatten)]
+ inner: TsConfig,
+}
+
+impl Default for LspTsConfig {
+ fn default() -> Self {
+ Self {
+ inner: TsConfig::new(json!({
+ "allowJs": true,
+ "esModuleInterop": true,
+ "experimentalDecorators": false,
+ "isolatedModules": true,
+ "jsx": "react",
+ "lib": ["deno.ns", "deno.window", "deno.unstable"],
+ "module": "esnext",
+ "moduleDetection": "force",
+ "noEmit": true,
+ "resolveJsonModule": true,
+ "strict": true,
+ "target": "esnext",
+ "useDefineForClassFields": true,
+ "useUnknownInCatchVariables": false,
+ })),
+ }
+ }
+}
+
+impl LspTsConfig {
+ pub fn new(config_file: Option<&ConfigFile>) -> Self {
+ let mut ts_config = Self::default();
+ if let Some(config_file) = config_file {
+ match config_file.to_compiler_options() {
+ Ok((value, maybe_ignored_options)) => {
+ ts_config.inner.merge(&value);
+ if let Some(ignored_options) = maybe_ignored_options {
+ lsp_warn!("{}", ignored_options);
+ }
+ }
+ Err(err) => lsp_warn!("{}", err),
+ }
+ }
+ ts_config
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -1120,7 +1144,7 @@ pub struct ConfigData {
pub fmt_options: Arc<FmtOptions>,
pub lint_options: Arc<LintOptions>,
pub lint_rules: Arc<ConfiguredRules>,
- pub ts_config: Arc<TsConfig>,
+ pub ts_config: Arc<LspTsConfig>,
pub node_modules_dir: Option<PathBuf>,
pub vendor_dir: Option<PathBuf>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>,
@@ -1242,18 +1266,7 @@ impl ConfigData {
.unwrap_or_default();
let lint_rules =
get_configured_rules(lint_options.rules.clone(), config_file.as_ref());
- let mut ts_config = default_ts_config();
- if let Some(config_file) = &config_file {
- match config_file.to_compiler_options() {
- Ok((value, maybe_ignored_options)) => {
- ts_config.merge(&value);
- if let Some(ignored_options) = maybe_ignored_options {
- lsp_warn!("{}", ignored_options);
- }
- }
- Err(err) => lsp_warn!("{}", err),
- }
- }
+ let ts_config = LspTsConfig::new(config_file.as_ref());
let node_modules_dir =
config_file.as_ref().and_then(resolve_node_modules_dir);
let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path());
@@ -1425,206 +1438,191 @@ impl ConfigData {
}
}
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct ConfigTree {
- root: Mutex<Option<(ModuleSpecifier, Arc<ConfigData>)>>,
+ first_folder: Option<ModuleSpecifier>,
+ scopes: Arc<BTreeMap<ModuleSpecifier, ConfigData>>,
}
impl ConfigTree {
- pub fn root_data(&self) -> Option<Arc<ConfigData>> {
- self.root.lock().as_ref().map(|(_, d)| d.clone())
+ pub fn root_data(&self) -> Option<&ConfigData> {
+ self.first_folder.as_ref().and_then(|s| self.scopes.get(s))
}
- pub fn root_config_file(&self) -> Option<Arc<ConfigFile>> {
+ pub fn root_ts_config(&self) -> Arc<LspTsConfig> {
self
- .root
- .lock()
- .as_ref()
- .and_then(|(_, d)| d.config_file.clone())
+ .root_data()
+ .map(|d| d.ts_config.clone())
+ .unwrap_or_default()
}
- pub fn root_ts_config(&self) -> Arc<TsConfig> {
- self
- .root
- .lock()
- .as_ref()
- .map(|(_, d)| d.ts_config.clone())
- .unwrap_or_else(|| Arc::new(default_ts_config()))
+ pub fn root_vendor_dir(&self) -> Option<&PathBuf> {
+ self.root_data().and_then(|d| d.vendor_dir.as_ref())
}
- pub fn root_vendor_dir(&self) -> Option<PathBuf> {
- self
- .root
- .lock()
- .as_ref()
- .and_then(|(_, d)| d.vendor_dir.clone())
+ pub fn root_lockfile(&self) -> Option<&Arc<Mutex<Lockfile>>> {
+ self.root_data().and_then(|d| d.lockfile.as_ref())
}
- pub fn root_lockfile(&self) -> Option<Arc<Mutex<Lockfile>>> {
- self
- .root
- .lock()
- .as_ref()
- .and_then(|(_, d)| d.lockfile.clone())
+ pub fn root_import_map(&self) -> Option<&Arc<ImportMap>> {
+ self.root_data().and_then(|d| d.import_map.as_ref())
}
pub fn scope_for_specifier(
&self,
- _specifier: &ModuleSpecifier,
- ) -> Option<ModuleSpecifier> {
- self.root.lock().as_ref().map(|r| r.0.clone())
+ specifier: &ModuleSpecifier,
+ ) -> Option<&ModuleSpecifier> {
+ self
+ .scopes
+ .keys()
+ .rfind(|s| specifier.as_str().starts_with(s.as_str()))
+ .or(self.first_folder.as_ref())
}
pub fn data_for_specifier(
&self,
- _specifier: &ModuleSpecifier,
- ) -> Option<Arc<ConfigData>> {
- self.root_data()
+ specifier: &ModuleSpecifier,
+ ) -> Option<&ConfigData> {
+ self
+ .scope_for_specifier(specifier)
+ .and_then(|s| self.scopes.get(s))
}
- pub fn data_by_scope(&self) -> BTreeMap<ModuleSpecifier, Arc<ConfigData>> {
- self.root.lock().iter().cloned().collect()
+ pub fn data_by_scope(&self) -> &Arc<BTreeMap<ModuleSpecifier, ConfigData>> {
+ &self.scopes
}
pub fn config_file_for_specifier(
&self,
- _specifier: &ModuleSpecifier,
- ) -> Option<Arc<ConfigFile>> {
- self.root_config_file()
- }
-
- pub fn has_config_file_for_specifier(
- &self,
- _specifier: &ModuleSpecifier,
- ) -> bool {
+ specifier: &ModuleSpecifier,
+ ) -> Option<&Arc<ConfigFile>> {
self
- .root
- .lock()
- .as_ref()
- .map(|(_, d)| d.config_file.is_some())
- .unwrap_or(false)
+ .data_for_specifier(specifier)
+ .and_then(|d| d.config_file.as_ref())
}
- pub fn config_files(&self) -> Vec<Arc<ConfigFile>> {
- self.root_config_file().into_iter().collect()
+ pub fn config_files(&self) -> Vec<&Arc<ConfigFile>> {
+ self
+ .scopes
+ .iter()
+ .filter_map(|(_, d)| d.config_file.as_ref())
+ .collect()
}
- pub fn package_jsons(&self) -> Vec<Arc<PackageJson>> {
+ pub fn package_jsons(&self) -> Vec<&Arc<PackageJson>> {
self
- .root
- .lock()
- .as_ref()
- .and_then(|(_, d)| d.package_json.clone())
- .into_iter()
+ .scopes
+ .iter()
+ .filter_map(|(_, d)| d.package_json.as_ref())
.collect()
}
pub fn fmt_options_for_specifier(
&self,
- _specifier: &ModuleSpecifier,
+ specifier: &ModuleSpecifier,
) -> Arc<FmtOptions> {
self
- .root
- .lock()
- .as_ref()
- .map(|(_, d)| d.fmt_options.clone())
+ .data_for_specifier(specifier)
+ .map(|d| d.fmt_options.clone())
.unwrap_or_default()
}
- pub fn lockfile_for_specifier(
+ /// Returns (scope_uri, type).
+ pub fn watched_file_type(
&self,
- _specifier: &ModuleSpecifier,
- ) -> Option<Arc<Mutex<Lockfile>>> {
- self.root_lockfile()
+ specifier: &ModuleSpecifier,
+ ) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
+ for (scope_uri, data) in self.scopes.iter() {
+ if let Some(typ) = data.watched_files.get(specifier) {
+ return Some((scope_uri, *typ));
+ }
+ }
+ None
}
- pub fn import_map_for_specifier(
- &self,
- _specifier: &ModuleSpecifier,
- ) -> Option<Arc<ImportMap>> {
+ pub fn is_watched_file(&self, specifier: &ModuleSpecifier) -> bool {
+ if specifier.path().ends_with("/deno.json")
+ || specifier.path().ends_with("/deno.jsonc")
+ || specifier.path().ends_with("/package.json")
+ {
+ return true;
+ }
self
- .root
- .lock()
- .as_ref()
- .and_then(|(_, d)| d.import_map.clone())
+ .scopes
+ .values()
+ .any(|data| data.watched_files.contains_key(specifier))
}
pub async fn refresh(
- &self,
+ &mut self,
settings: &Settings,
- root_uri: &ModuleSpecifier,
workspace_files: &BTreeSet<ModuleSpecifier>,
file_fetcher: &FileFetcher,
) {
lsp_log!("Refreshing configuration tree...");
- let mut root = None;
- if let Some(config_path) = &settings.unscoped.config {
- if let Ok(config_uri) = root_uri.join(config_path) {
- root = Some((
- root_uri.clone(),
- Arc::new(
- ConfigData::load(
- Some(&config_uri),
- root_uri,
+ let mut scopes = BTreeMap::new();
+ for (folder_uri, ws_settings) in &settings.by_workspace_folder {
+ let mut ws_settings = ws_settings.as_ref();
+ if Some(folder_uri) == settings.first_folder.as_ref() {
+ ws_settings = ws_settings.or(Some(&settings.unscoped));
+ }
+ if let Some(ws_settings) = ws_settings {
+ if let Some(config_path) = &ws_settings.config {
+ if let Ok(config_uri) = folder_uri.join(config_path) {
+ scopes.insert(
+ folder_uri.clone(),
+ ConfigData::load(
+ Some(&config_uri),
+ folder_uri,
+ settings,
+ Some(file_fetcher),
+ )
+ .await,
+ );
+ }
+ }
+ }
+ }
+
+ for specifier in workspace_files {
+ if specifier.path().ends_with("/deno.json")
+ || specifier.path().ends_with("/deno.jsonc")
+ {
+ if let Ok(scope) = specifier.join(".") {
+ let entry = scopes.entry(scope.clone());
+ #[allow(clippy::map_entry)]
+ if matches!(entry, std::collections::btree_map::Entry::Vacant(_)) {
+ let data = ConfigData::load(
+ Some(specifier),
+ &scope,
settings,
Some(file_fetcher),
)
- .await,
- ),
- ));
+ .await;
+ entry.or_insert(data);
+ }
+ }
}
- } else {
- let get_uri_if_exists = |name| {
- let uri = root_uri.join(name).ok();
- uri.filter(|s| workspace_files.contains(s))
- };
- let config_uri = get_uri_if_exists("deno.jsonc")
- .or_else(|| get_uri_if_exists("deno.json"));
- root = Some((
- root_uri.clone(),
- Arc::new(
- ConfigData::load(
- config_uri.as_ref(),
- root_uri,
- settings,
- Some(file_fetcher),
- )
- .await,
- ),
- ));
}
- *self.root.lock() = root;
- }
- /// Returns (scope_uri, type).
- pub fn watched_file_type(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<(ModuleSpecifier, ConfigWatchedFileType)> {
- if let Some((scope_uri, data)) = &*self.root.lock() {
- if let Some(typ) = data.watched_files.get(specifier) {
- return Some((scope_uri.clone(), *typ));
+ for folder_uri in settings.by_workspace_folder.keys() {
+ if !scopes
+ .keys()
+ .any(|s| folder_uri.as_str().starts_with(s.as_str()))
+ {
+ scopes.insert(
+ folder_uri.clone(),
+ ConfigData::load(None, folder_uri, settings, Some(file_fetcher))
+ .await,
+ );
}
}
- None
- }
-
- pub fn is_watched_file(&self, specifier: &ModuleSpecifier) -> bool {
- if specifier.path().ends_with("/deno.json")
- || specifier.path().ends_with("/deno.jsonc")
- || specifier.path().ends_with("/package.json")
- {
- return true;
- }
- self
- .root
- .lock()
- .as_ref()
- .is_some_and(|(_, d)| d.watched_files.contains_key(specifier))
+ self.first_folder = settings.first_folder.clone();
+ self.scopes = Arc::new(scopes);
}
#[cfg(test)]
- pub async fn inject_config_file(&self, config_file: ConfigFile) {
+ pub async fn inject_config_file(&mut self, config_file: ConfigFile) {
let scope = config_file.specifier.join(".").unwrap();
let data = ConfigData::load_inner(
Some(config_file),
@@ -1633,7 +1631,8 @@ impl ConfigTree {
None,
)
.await;
- *self.root.lock() = Some((scope, Arc::new(data)));
+ self.first_folder = Some(scope.clone());
+ self.scopes = Arc::new([(scope, data)].into_iter().collect());
}
}
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index e518f4c6e..ac1f522d7 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -811,7 +811,7 @@ fn generate_lint_diagnostics(
let (lint_options, lint_rules) = config
.tree
.scope_for_specifier(document.specifier())
- .and_then(|s| config_data_by_scope.get(&s))
+ .and_then(|s| config_data_by_scope.get(s))
.map(|d| (d.lint_options.clone(), d.lint_rules.clone()))
.unwrap_or_default();
diagnostics_vec.push(DiagnosticRecord {
@@ -1452,8 +1452,8 @@ fn diagnose_dependency(
}
}
- let import_map = snapshot.config.tree.import_map_for_specifier(referrer);
- if let Some(import_map) = &import_map {
+ let import_map = snapshot.config.tree.root_import_map();
+ if let Some(import_map) = import_map {
if let Resolution::Ok(resolved) = &dependency.maybe_code {
if let Some(to) = import_map.lookup(&resolved.specifier, referrer) {
if dependency_key != to {
@@ -1502,7 +1502,7 @@ fn diagnose_dependency(
},
dependency.is_dynamic,
dependency.maybe_attribute_type.as_deref(),
- import_map.as_deref(),
+ import_map.map(|i| i.as_ref()),
)
.iter()
.flat_map(|diag| {
@@ -1525,7 +1525,7 @@ fn diagnose_dependency(
&dependency.maybe_type,
dependency.is_dynamic,
dependency.maybe_attribute_type.as_deref(),
- import_map.as_deref(),
+ import_map.map(|i| i.as_ref()),
)
.iter()
.map(|diag| diag.to_lsp_diagnostic(&range)),
@@ -1614,7 +1614,7 @@ mod tests {
(*source).into(),
);
}
- let config = Config::new_with_roots([resolve_url("file:///").unwrap()]);
+ let mut config = Config::new_with_roots([resolve_url("file:///").unwrap()]);
if let Some((base_url, json_string)) = maybe_import_map {
let base_url = resolve_url(base_url).unwrap();
let config_file = ConfigFile::new(
@@ -1689,8 +1689,7 @@ let c: number = "a";
let snapshot = Arc::new(snapshot);
let cache =
Arc::new(GlobalHttpCache::new(cache_location, RealDenoCacheEnv));
- let ts_server =
- TsServer::new(Default::default(), cache, Default::default());
+ let ts_server = TsServer::new(Default::default(), cache);
ts_server.start(None);
// test enabled
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index d207330ad..0e9b6c3c3 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -1309,14 +1309,12 @@ impl Documents {
workspace_files: &BTreeSet<ModuleSpecifier>,
) {
let config_data = config.tree.root_data();
- let config_file =
- config_data.as_ref().and_then(|d| d.config_file.as_deref());
+ let config_file = config_data.and_then(|d| d.config_file.as_deref());
self.resolver = Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
node_resolver,
npm_resolver,
package_json_deps_provider: Arc::new(PackageJsonDepsProvider::new(
config_data
- .as_ref()
.and_then(|d| d.package_json.as_ref())
.map(|package_json| {
package_json::get_local_package_json_version_reqs(package_json)
@@ -1324,10 +1322,8 @@ impl Documents {
)),
maybe_jsx_import_source_config: config_file
.and_then(|cf| cf.to_maybe_jsx_import_source_config().ok().flatten()),
- maybe_import_map: config_data.as_ref().and_then(|d| d.import_map.clone()),
- maybe_vendor_dir: config_data
- .as_ref()
- .and_then(|d| d.vendor_dir.as_ref()),
+ maybe_import_map: config_data.and_then(|d| d.import_map.clone()),
+ maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
bare_node_builtins_enabled: config_file
.map(|config| config.has_unstable("bare-node-builtins"))
.unwrap_or(false),
@@ -1338,7 +1334,7 @@ impl Documents {
}));
self.jsr_resolver = Arc::new(JsrCacheResolver::new(
self.cache.clone(),
- config.tree.root_lockfile(),
+ config.tree.root_lockfile().cloned(),
));
self.redirect_resolver =
Arc::new(RedirectResolver::new(self.cache.clone()));
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index a5923a84a..8348bd95f 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -334,11 +334,7 @@ impl LanguageServer {
// do as much as possible in a read, then do a write outside
let maybe_prepare_cache_result = {
let inner = self.0.read().await; // ensure dropped
- match inner.prepare_cache(
- specifiers,
- referrer.clone(),
- force_global_cache,
- ) {
+ match inner.prepare_cache(specifiers, referrer, force_global_cache) {
Ok(maybe_cache_result) => maybe_cache_result,
Err(err) => {
lsp_warn!("Error preparing caching: {:#}", err);
@@ -370,7 +366,7 @@ impl LanguageServer {
}
{
let mut inner = self.0.write().await;
- let lockfile = inner.config.tree.lockfile_for_specifier(&referrer);
+ let lockfile = inner.config.tree.root_lockfile().cloned();
inner.documents.refresh_jsr_resolver(lockfile);
inner.refresh_npm_specifiers().await;
}
@@ -516,11 +512,8 @@ impl Inner {
let cache_metadata = cache::CacheMetadata::new(deps_http_cache.clone());
let performance = Arc::new(Performance::default());
let config = Config::default();
- let ts_server = Arc::new(TsServer::new(
- performance.clone(),
- deps_http_cache.clone(),
- config.tree.clone(),
- ));
+ let ts_server =
+ Arc::new(TsServer::new(performance.clone(), deps_http_cache.clone()));
let diagnostics_state = Arc::new(DiagnosticsState::default());
let diagnostics_server = DiagnosticsServer::new(
client.clone(),
@@ -765,7 +758,10 @@ impl Inner {
));
let maybe_local_cache =
self.config.tree.root_vendor_dir().map(|local_path| {
- Arc::new(LocalLspHttpCache::new(local_path, global_cache.clone()))
+ Arc::new(LocalLspHttpCache::new(
+ local_path.clone(),
+ global_cache.clone(),
+ ))
});
let cache: Arc<dyn HttpCache> = maybe_local_cache
.clone()
@@ -1154,42 +1150,33 @@ impl Inner {
async fn refresh_config_tree(&mut self) {
let file_fetcher = self.create_file_fetcher(CacheSetting::RespectHeaders);
- if let Some(root_uri) = self.config.root_uri() {
- self
- .config
- .tree
- .refresh(
- &self.config.settings,
- root_uri,
- &self.workspace_files,
- &file_fetcher,
- )
- .await;
- for config_file in self.config.tree.config_files() {
- if let Ok((compiler_options, _)) = config_file.to_compiler_options() {
- if let Some(compiler_options_obj) = compiler_options.as_object() {
- if let Some(jsx_import_source) =
- compiler_options_obj.get("jsxImportSource")
- {
- if let Some(jsx_import_source) = jsx_import_source.as_str() {
- let specifiers = vec![Url::parse(&format!(
- "data:application/typescript;base64,{}",
- base64::engine::general_purpose::STANDARD.encode(format!(
- "import '{jsx_import_source}/jsx-runtime';"
- ))
- ))
- .unwrap()];
- let referrer = config_file.specifier.clone();
- self.task_queue.queue_task(Box::new(|ls: LanguageServer| {
- spawn(async move {
- if let Err(err) =
- ls.cache(specifiers, referrer, false).await
- {
- lsp_warn!("{:#}", err);
- }
- });
- }));
- }
+ self
+ .config
+ .tree
+ .refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
+ .await;
+ for config_file in self.config.tree.config_files() {
+ if let Ok((compiler_options, _)) = config_file.to_compiler_options() {
+ if let Some(compiler_options_obj) = compiler_options.as_object() {
+ if let Some(jsx_import_source) =
+ compiler_options_obj.get("jsxImportSource")
+ {
+ if let Some(jsx_import_source) = jsx_import_source.as_str() {
+ let specifiers = vec![Url::parse(&format!(
+ "data:application/typescript;base64,{}",
+ base64::engine::general_purpose::STANDARD
+ .encode(format!("import '{jsx_import_source}/jsx-runtime';"))
+ ))
+ .unwrap()];
+ let referrer = config_file.specifier.clone();
+ self.task_queue.queue_task(Box::new(|ls: LanguageServer| {
+ spawn(async move {
+ if let Err(err) = ls.cache(specifiers, referrer, false).await
+ {
+ lsp_warn!("{:#}", err);
+ }
+ });
+ }));
}
}
}
@@ -1383,7 +1370,7 @@ impl Inner {
_ => return None,
};
Some(lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: t.0,
+ scope_uri: t.0.clone(),
file_uri: e.uri.clone(),
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
e.typ,
@@ -1407,7 +1394,7 @@ impl Inner {
_ => return None,
};
Some(lsp_custom::DenoConfigurationChangeEvent {
- scope_uri: t.0,
+ scope_uri: t.0.clone(),
file_uri: e.uri.clone(),
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
e.typ,
@@ -2010,11 +1997,11 @@ impl Inner {
pub fn get_ts_response_import_mapper(
&self,
- referrer: &ModuleSpecifier,
+ _referrer: &ModuleSpecifier,
) -> TsResponseImportMapper {
TsResponseImportMapper::new(
&self.documents,
- self.config.tree.import_map_for_specifier(referrer),
+ self.config.tree.root_import_map().map(|i| i.as_ref()),
self.npm.node_resolver.as_deref(),
self.npm.resolver.as_deref(),
)
@@ -2327,7 +2314,7 @@ impl Inner {
&self.jsr_search_api,
&self.npm.search_api,
&self.documents,
- self.config.tree.import_map_for_specifier(&specifier),
+ self.config.tree.root_import_map().map(|i| i.as_ref()),
)
.await;
}
@@ -3112,7 +3099,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
}
let mut config_events = vec![];
- for (scope_uri, config_data) in ls.config.tree.data_by_scope() {
+ for (scope_uri, config_data) in ls.config.tree.data_by_scope().iter() {
if let Some(config_file) = &config_data.config_file {
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
scope_uri: scope_uri.clone(),
@@ -3493,7 +3480,7 @@ impl Inner {
let mark = self
.performance
.mark_with_args("lsp.cache", (&specifiers, &referrer));
- let config_data = self.config.tree.data_for_specifier(&referrer);
+ let config_data = self.config.tree.root_data();
let roots = if !specifiers.is_empty() {
specifiers
} else {
@@ -3508,7 +3495,7 @@ impl Inner {
unsafely_ignore_certificate_errors: workspace_settings
.unsafely_ignore_certificate_errors
.clone(),
- import_map_path: config_data.as_ref().and_then(|d| {
+ import_map_path: config_data.and_then(|d| {
if d.import_map_from_settings {
return Some(d.import_map.as_ref()?.base_url().to_string());
}
@@ -3516,7 +3503,6 @@ impl Inner {
}),
node_modules_dir: Some(
config_data
- .as_ref()
.and_then(|d| d.node_modules_dir.as_ref())
.is_some(),
),
@@ -3525,13 +3511,9 @@ impl Inner {
..Default::default()
},
self.initial_cwd.clone(),
- config_data
- .as_ref()
- .and_then(|d| d.config_file.as_deref().cloned()),
- config_data.as_ref().and_then(|d| d.lockfile.clone()),
- config_data
- .as_ref()
- .and_then(|d| d.package_json.as_deref().cloned()),
+ config_data.and_then(|d| d.config_file.as_deref().cloned()),
+ config_data.and_then(|d| d.lockfile.clone()),
+ config_data.and_then(|d| d.package_json.as_deref().cloned()),
force_global_cache,
)?;
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 63c08331d..fbc712a56 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -3,7 +3,6 @@
use super::analysis::CodeActionData;
use super::code_lens;
use super::config;
-use super::config::ConfigTree;
use super::documents::AssetOrDocument;
use super::documents::DocumentsFilter;
use super::language_server;
@@ -222,7 +221,6 @@ pub struct TsServer {
sender: mpsc::UnboundedSender<Request>,
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
specifier_map: Arc<TscSpecifierMap>,
- config_tree: Arc<ConfigTree>,
inspector_server: Mutex<Option<Arc<InspectorServer>>>,
}
@@ -240,11 +238,7 @@ impl std::fmt::Debug for TsServer {
}
impl TsServer {
- pub fn new(
- performance: Arc<Performance>,
- cache: Arc<dyn HttpCache>,
- config_tree: Arc<ConfigTree>,
- ) -> Self {
+ pub fn new(performance: Arc<Performance>, cache: Arc<dyn HttpCache>) -> Self {
let (tx, request_rx) = mpsc::unbounded_channel::<Request>();
Self {
performance,
@@ -252,7 +246,6 @@ impl TsServer {
sender: tx,
receiver: Mutex::new(Some(request_rx)),
specifier_map: Arc::new(TscSpecifierMap::new()),
- config_tree,
inspector_server: Mutex::new(None),
}
}
@@ -275,7 +268,6 @@ impl TsServer {
let performance = self.performance.clone();
let cache = self.cache.clone();
let specifier_map = self.specifier_map.clone();
- let config_tree = self.config_tree.clone();
let _join_handle = thread::spawn(move || {
run_tsc_thread(
receiver,
@@ -283,7 +275,6 @@ impl TsServer {
cache.clone(),
specifier_map.clone(),
maybe_inspector_server,
- config_tree,
)
});
}
@@ -3884,7 +3875,6 @@ struct State {
response: Option<Response>,
state_snapshot: Arc<StateSnapshot>,
specifier_map: Arc<TscSpecifierMap>,
- config_tree: Arc<ConfigTree>,
token: CancellationToken,
}
@@ -3892,7 +3882,6 @@ impl State {
fn new(
state_snapshot: Arc<StateSnapshot>,
specifier_map: Arc<TscSpecifierMap>,
- config_tree: Arc<ConfigTree>,
performance: Arc<Performance>,
) -> Self {
Self {
@@ -3901,7 +3890,6 @@ impl State {
response: None,
state_snapshot,
specifier_map,
- config_tree,
token: Default::default(),
}
}
@@ -4120,7 +4108,7 @@ fn op_script_version(
fn op_ts_config(state: &mut OpState) -> serde_json::Value {
let state = state.borrow_mut::<State>();
let mark = state.performance.mark("tsc.op.op_ts_config");
- let r = json!(state.config_tree.root_ts_config());
+ let r = json!(state.state_snapshot.config.tree.root_ts_config());
state.performance.measure(mark);
r
}
@@ -4141,19 +4129,13 @@ fn run_tsc_thread(
cache: Arc<dyn HttpCache>,
specifier_map: Arc<TscSpecifierMap>,
maybe_inspector_server: Option<Arc<InspectorServer>>,
- config_tree: Arc<ConfigTree>,
) {
let has_inspector_server = maybe_inspector_server.is_some();
// Create and setup a JsRuntime based on a snapshot. It is expected that the
// supplied snapshot is an isolate that contains the TypeScript language
// server.
let mut tsc_runtime = JsRuntime::new(RuntimeOptions {
- extensions: vec![deno_tsc::init_ops(
- performance,
- cache,
- specifier_map,
- config_tree,
- )],
+ extensions: vec![deno_tsc::init_ops(performance, cache, specifier_map)],
startup_snapshot: Some(tsc::compiler_snapshot()),
inspector: maybe_inspector_server.is_some(),
..Default::default()
@@ -4227,7 +4209,6 @@ deno_core::extension!(deno_tsc,
performance: Arc<Performance>,
cache: Arc<dyn HttpCache>,
specifier_map: Arc<TscSpecifierMap>,
- config_tree: Arc<ConfigTree>,
},
state = |state, options| {
state.put(State::new(
@@ -4239,7 +4220,6 @@ deno_core::extension!(deno_tsc,
npm: None,
}),
options.specifier_map,
- options.config_tree,
options.performance,
));
},
@@ -4507,7 +4487,10 @@ impl UserPreferences {
language_settings.preferences.use_aliases_for_renames,
),
// Only use workspace settings for quote style if there's no `deno.json`.
- quote_preference: if config.tree.has_config_file_for_specifier(specifier)
+ quote_preference: if config
+ .tree
+ .config_file_for_specifier(specifier)
+ .is_some()
{
base_preferences.quote_preference
} else {
@@ -4650,12 +4633,14 @@ fn request(
#[cfg(test)]
mod tests {
+
use super::*;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::cache::RealDenoCacheEnv;
use crate::http_util::HeadersMap;
use crate::lsp::cache::CacheMetadata;
+ use crate::lsp::config::ConfigSnapshot;
use crate::lsp::config::WorkspaceSettings;
use crate::lsp::documents::Documents;
use crate::lsp::documents::LanguageId;
@@ -4664,9 +4649,10 @@ mod tests {
use std::path::Path;
use test_util::TempDir;
- fn mock_state_snapshot(
+ async fn mock_state_snapshot(
fixtures: &[(&str, &str, i32, LanguageId)],
location: &Path,
+ ts_config: Value,
) -> StateSnapshot {
let cache = Arc::new(GlobalHttpCache::new(
location.to_path_buf(),
@@ -4683,11 +4669,26 @@ mod tests {
(*source).into(),
);
}
+ let mut config = ConfigSnapshot::default();
+ config
+ .tree
+ .inject_config_file(
+ deno_config::ConfigFile::new(
+ &json!({
+ "compilerOptions": ts_config,
+ })
+ .to_string(),
+ resolve_url("file:///deno.json").unwrap(),
+ &deno_config::ParseOptions::default(),
+ )
+ .unwrap(),
+ )
+ .await;
StateSnapshot {
documents,
assets: Default::default(),
cache_metadata: CacheMetadata::new(cache),
- config: Default::default(),
+ config: Arc::new(config),
npm: None,
}
}
@@ -4700,23 +4701,10 @@ mod tests {
let location = temp_dir.path().join("deps").to_path_buf();
let cache =
Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv));
- let snapshot = Arc::new(mock_state_snapshot(sources, &location));
+ let snapshot =
+ Arc::new(mock_state_snapshot(sources, &location, config).await);
let performance = Arc::new(Performance::default());
- let config_tree = Arc::new(ConfigTree::default());
- config_tree
- .inject_config_file(
- deno_config::ConfigFile::new(
- &json!({
- "compilerOptions": config,
- })
- .to_string(),
- resolve_url("file:///deno.json").unwrap(),
- &deno_config::ParseOptions::default(),
- )
- .unwrap(),
- )
- .await;
- let ts_server = TsServer::new(performance, cache.clone(), config_tree);
+ let ts_server = TsServer::new(performance, cache.clone());
ts_server.start(None);
(ts_server, snapshot, cache)
}