mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
feat(lsp): respect nested deno.json for fmt and lint config (#23159)
This commit is contained in:
parent
3b9fd1af80
commit
2b1c6e172e
9 changed files with 480 additions and 316 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,185 +1438,103 @@ 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 package_jsons(&self) -> Vec<Arc<PackageJson>> {
|
||||
pub fn config_files(&self) -> Vec<&Arc<ConfigFile>> {
|
||||
self
|
||||
.root
|
||||
.lock()
|
||||
.as_ref()
|
||||
.and_then(|(_, d)| d.package_json.clone())
|
||||
.into_iter()
|
||||
.scopes
|
||||
.iter()
|
||||
.filter_map(|(_, d)| d.config_file.as_ref())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn package_jsons(&self) -> Vec<&Arc<PackageJson>> {
|
||||
self
|
||||
.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(
|
||||
&self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
) -> Option<Arc<Mutex<Lockfile>>> {
|
||||
self.root_lockfile()
|
||||
}
|
||||
|
||||
pub fn import_map_for_specifier(
|
||||
&self,
|
||||
_specifier: &ModuleSpecifier,
|
||||
) -> Option<Arc<ImportMap>> {
|
||||
self
|
||||
.root
|
||||
.lock()
|
||||
.as_ref()
|
||||
.and_then(|(_, d)| d.import_map.clone())
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
&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,
|
||||
settings,
|
||||
Some(file_fetcher),
|
||||
)
|
||||
.await,
|
||||
),
|
||||
));
|
||||
}
|
||||
} 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() {
|
||||
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
|
||||
for (scope_uri, data) in self.scopes.iter() {
|
||||
if let Some(typ) = data.watched_files.get(specifier) {
|
||||
return Some((scope_uri.clone(), *typ));
|
||||
return Some((scope_uri, *typ));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -1617,14 +1548,81 @@ impl ConfigTree {
|
|||
return true;
|
||||
}
|
||||
self
|
||||
.root
|
||||
.lock()
|
||||
.as_ref()
|
||||
.is_some_and(|(_, d)| d.watched_files.contains_key(specifier))
|
||||
.scopes
|
||||
.values()
|
||||
.any(|data| data.watched_files.contains_key(specifier))
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
workspace_files: &BTreeSet<ModuleSpecifier>,
|
||||
file_fetcher: &FileFetcher,
|
||||
) {
|
||||
lsp_log!("Refreshing configuration tree...");
|
||||
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;
|
||||
entry.or_insert(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1061,6 +1061,7 @@ fn lsp_did_change_deno_configuration_notification() {
|
|||
}],
|
||||
}))
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1098,6 +1099,7 @@ fn lsp_deno_task() {
|
|||
}
|
||||
])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1110,6 +1112,7 @@ fn lsp_reload_import_registries_command() {
|
|||
json!({ "command": "deno.reloadImportRegistries" }),
|
||||
);
|
||||
assert_eq!(res, json!(true));
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1598,6 +1601,7 @@ fn lsp_inlay_hints() {
|
|||
}
|
||||
])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1645,6 +1649,7 @@ fn lsp_inlay_hints_not_enabled() {
|
|||
}),
|
||||
);
|
||||
assert_eq!(res, json!(null));
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2409,6 +2414,7 @@ fn lsp_hover_dependency() {
|
|||
}
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
// This tests for a regression covered by denoland/deno#12753 where the lsp was
|
||||
|
@ -4822,6 +4828,7 @@ fn test_lsp_code_actions_ordering() {
|
|||
},
|
||||
])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -4851,6 +4858,7 @@ fn lsp_status_file() {
|
|||
);
|
||||
let res = res.as_str().unwrap().to_string();
|
||||
assert!(res.starts_with("# Deno Language Server Status"));
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -5598,6 +5606,7 @@ fn lsp_cache_then_definition() {
|
|||
},
|
||||
}]),
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6355,6 +6364,7 @@ fn lsp_quote_style_from_workspace_settings() {
|
|||
},
|
||||
}]),
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6812,6 +6822,7 @@ fn lsp_completions_auto_import() {
|
|||
]
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7063,6 +7074,7 @@ fn lsp_npm_completions_auto_import_and_quick_fix_no_import_map() {
|
|||
}
|
||||
}])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7102,6 +7114,7 @@ fn lsp_semantic_tokens_for_disabled_module() {
|
|||
"data": [0, 6, 9, 7, 9, 0, 15, 9, 7, 8],
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7538,6 +7551,7 @@ fn lsp_completions_auto_import_and_quick_fix_with_import_map() {
|
|||
]
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7633,6 +7647,7 @@ fn lsp_completions_snippet() {
|
|||
"insertTextFormat": 2
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7688,6 +7703,7 @@ fn lsp_completions_no_snippet() {
|
|||
]
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7919,6 +7935,7 @@ fn lsp_npm_specifier_unopened_file() {
|
|||
assert!(!list.is_incomplete);
|
||||
assert_eq!(list.items.len(), 63);
|
||||
assert!(list.items.iter().any(|i| i.label == "ansi256"));
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -11432,6 +11449,193 @@ fn lsp_vendor_dir() {
|
|||
|
||||
client.shutdown();
|
||||
}
|
||||
#[test]
|
||||
fn lsp_deno_json_scopes_fmt_config() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let temp_dir = context.temp_dir();
|
||||
temp_dir.create_dir_all("project1");
|
||||
temp_dir.write(
|
||||
"project1/deno.json",
|
||||
json!({
|
||||
"fmt": {
|
||||
"semiColons": false,
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.create_dir_all("project2");
|
||||
temp_dir.write(
|
||||
"project2/deno.json",
|
||||
json!({
|
||||
"fmt": {
|
||||
"singleQuote": true,
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "console.log(\"\");\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/formatting",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
},
|
||||
"options": {
|
||||
"tabSize": 2,
|
||||
"insertSpaces": true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([{
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 15 },
|
||||
"end": { "line": 0, "character": 16 },
|
||||
},
|
||||
"newText": "",
|
||||
}])
|
||||
);
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "console.log(\"\");\n",
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/formatting",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
},
|
||||
"options": {
|
||||
"tabSize": 2,
|
||||
"insertSpaces": true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([{
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 12 },
|
||||
"end": { "line": 0, "character": 14 },
|
||||
},
|
||||
"newText": "''",
|
||||
}])
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_deno_json_scopes_lint_config() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let temp_dir = context.temp_dir();
|
||||
temp_dir.create_dir_all("project1");
|
||||
temp_dir.write(
|
||||
"project1/deno.json",
|
||||
json!({
|
||||
"lint": {
|
||||
"rules": {
|
||||
"include": ["camelcase"],
|
||||
},
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
temp_dir.create_dir_all("project2");
|
||||
temp_dir.write(
|
||||
"project2/deno.json",
|
||||
json!({
|
||||
"lint": {
|
||||
"rules": {
|
||||
"include": ["ban-untagged-todo"],
|
||||
},
|
||||
},
|
||||
})
|
||||
.to_string(),
|
||||
);
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": r#"
|
||||
// TODO: Unused var
|
||||
const snake_case_var = 1;
|
||||
console.log(snake_case_var);
|
||||
"#,
|
||||
},
|
||||
}));
|
||||
assert_eq!(
|
||||
json!(diagnostics.messages_with_source("deno-lint")),
|
||||
json!({
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
"diagnostics": [{
|
||||
"range": {
|
||||
"start": { "line": 2, "character": 14 },
|
||||
"end": { "line": 2, "character": 28 },
|
||||
},
|
||||
"severity": 2,
|
||||
"code": "camelcase",
|
||||
"source": "deno-lint",
|
||||
"message": "Identifier 'snake_case_var' is not in camel case.\nConsider renaming `snake_case_var` to `snakeCaseVar`",
|
||||
}],
|
||||
"version": 1,
|
||||
})
|
||||
);
|
||||
client.write_notification(
|
||||
"textDocument/didClose",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project1/file.ts").unwrap(),
|
||||
},
|
||||
}),
|
||||
);
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": r#"
|
||||
// TODO: Unused var
|
||||
const snake_case_var = 1;
|
||||
console.log(snake_case_var);
|
||||
"#,
|
||||
},
|
||||
}));
|
||||
assert_eq!(
|
||||
json!(diagnostics.messages_with_source("deno-lint")),
|
||||
json!({
|
||||
"uri": temp_dir.uri().join("project2/file.ts").unwrap(),
|
||||
"diagnostics": [{
|
||||
"range": {
|
||||
"start": { "line": 1, "character": 8 },
|
||||
"end": { "line": 1, "character": 27 },
|
||||
},
|
||||
"severity": 2,
|
||||
"code": "ban-untagged-todo",
|
||||
"source": "deno-lint",
|
||||
"message": "TODO should be tagged with (@username) or (#issue)\nAdd a user tag or issue reference to the TODO comment, e.g. TODO(@djones), TODO(djones), TODO(#123)",
|
||||
}],
|
||||
"version": 1,
|
||||
})
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_import_unstable_bare_node_builtins_auto_discovered() {
|
||||
|
@ -11995,7 +12199,7 @@ C.test();
|
|||
}))
|
||||
.all();
|
||||
|
||||
assert_eq!(diagnostics.len(), 0);
|
||||
assert_eq!(json!(diagnostics), json!([]));
|
||||
|
||||
client.shutdown();
|
||||
}
|
||||
|
|
|
@ -225,9 +225,6 @@ impl TestContextBuilder {
|
|||
}
|
||||
|
||||
let deno_exe = deno_exe_path();
|
||||
self
|
||||
.diagnostic_logger
|
||||
.writeln(format!("deno_exe path {}", deno_exe));
|
||||
|
||||
let http_server_guard = if self.use_http_server {
|
||||
Some(Rc::new(http_server()))
|
||||
|
|
Loading…
Reference in a new issue