2024-01-01 14:58:21 -05:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2020-12-07 05:46:39 -05:00
2023-09-21 01:46:39 -04:00
use deno_ast ::MediaType ;
2024-07-19 15:56:07 -04:00
use deno_config ::deno_json ::DenoJsonCache ;
use deno_config ::deno_json ::FmtConfig ;
use deno_config ::deno_json ::FmtOptionsConfig ;
use deno_config ::deno_json ::LintConfig ;
2024-08-30 17:58:24 -04:00
use deno_config ::deno_json ::NodeModulesDirMode ;
2024-07-19 15:56:07 -04:00
use deno_config ::deno_json ::TestConfig ;
use deno_config ::deno_json ::TsConfig ;
2024-07-18 18:16:35 -04:00
use deno_config ::fs ::DenoConfigFs ;
use deno_config ::fs ::RealDenoConfigFs ;
use deno_config ::glob ::FilePatterns ;
use deno_config ::glob ::PathOrPatternSet ;
use deno_config ::workspace ::CreateResolverOptions ;
use deno_config ::workspace ::PackageJsonDepResolution ;
use deno_config ::workspace ::SpecifiedImportMap ;
use deno_config ::workspace ::VendorEnablement ;
use deno_config ::workspace ::Workspace ;
2024-07-19 15:56:07 -04:00
use deno_config ::workspace ::WorkspaceCache ;
use deno_config ::workspace ::WorkspaceDirectory ;
use deno_config ::workspace ::WorkspaceDirectoryEmptyOptions ;
2024-07-18 18:16:35 -04:00
use deno_config ::workspace ::WorkspaceDiscoverOptions ;
use deno_config ::workspace ::WorkspaceResolver ;
use deno_core ::anyhow ::anyhow ;
use deno_core ::error ::AnyError ;
use deno_core ::parking_lot ::Mutex ;
2023-09-21 01:46:39 -04:00
use deno_core ::serde ::de ::DeserializeOwned ;
2020-12-07 05:46:39 -05:00
use deno_core ::serde ::Deserialize ;
2021-07-20 21:50:43 -04:00
use deno_core ::serde ::Serialize ;
2020-12-07 05:46:39 -05:00
use deno_core ::serde_json ;
2024-03-26 11:52:20 -04:00
use deno_core ::serde_json ::json ;
2020-12-07 05:46:39 -05:00
use deno_core ::serde_json ::Value ;
2024-08-23 20:21:21 -04:00
use deno_core ::url ::Url ;
2021-05-09 21:16:04 -04:00
use deno_core ::ModuleSpecifier ;
2024-07-19 15:56:07 -04:00
use deno_lint ::linter ::LintConfig as DenoLintConfig ;
2024-05-23 17:26:23 -04:00
use deno_npm ::npm_rc ::ResolvedNpmRc ;
2024-07-23 17:34:46 -04:00
use deno_package_json ::PackageJsonCache ;
2024-09-28 07:55:01 -04:00
use deno_path_util ::url_to_file_path ;
2024-03-26 11:52:20 -04:00
use deno_runtime ::deno_node ::PackageJson ;
2024-06-11 16:06:43 -04:00
use indexmap ::IndexSet ;
2024-05-29 16:31:09 -04:00
use lsp_types ::ClientCapabilities ;
2021-05-20 05:56:48 -04:00
use std ::collections ::BTreeMap ;
2024-10-31 06:52:43 -04:00
use std ::collections ::BTreeSet ;
2021-04-08 21:27:27 -04:00
use std ::collections ::HashMap ;
2024-09-10 19:20:03 -04:00
use std ::ops ::Deref ;
use std ::ops ::DerefMut ;
2024-07-18 18:16:35 -04:00
use std ::path ::Path ;
2023-07-10 17:45:09 -04:00
use std ::path ::PathBuf ;
2021-05-20 05:56:48 -04:00
use std ::sync ::Arc ;
2022-04-03 00:17:30 -04:00
use tower_lsp ::lsp_types as lsp ;
2021-12-15 13:23:43 -05:00
2024-07-18 18:16:35 -04:00
use super ::logging ::lsp_log ;
2024-10-16 17:43:26 -04:00
use super ::lsp_custom ;
use super ::urls ::url_to_uri ;
2024-07-18 18:16:35 -04:00
use crate ::args ::discover_npmrc_from_workspace ;
use crate ::args ::has_flag_env_var ;
use crate ::args ::CliLockfile ;
2024-10-02 16:17:39 -04:00
use crate ::args ::CliLockfileReadFromPathOptions ;
2024-07-18 18:16:35 -04:00
use crate ::args ::ConfigFile ;
2024-07-25 09:07:59 -04:00
use crate ::args ::LintFlags ;
use crate ::args ::LintOptions ;
2024-07-18 18:16:35 -04:00
use crate ::cache ::FastInsecureHasher ;
use crate ::file_fetcher ::FileFetcher ;
use crate ::lsp ::logging ::lsp_warn ;
2024-09-28 19:17:48 -04:00
use crate ::resolver ::CliSloppyImportsResolver ;
use crate ::resolver ::SloppyImportsCachedFs ;
2024-07-25 09:07:59 -04:00
use crate ::tools ::lint ::CliLinter ;
use crate ::tools ::lint ::CliLinterOptions ;
use crate ::tools ::lint ::LintRuleProvider ;
2024-07-18 18:16:35 -04:00
use crate ::util ::fs ::canonicalize_path_maybe_not_exists ;
2021-05-09 21:16:04 -04:00
pub const SETTINGS_SECTION : & str = " deno " ;
2021-06-07 07:38:07 -04:00
fn is_true ( ) -> bool {
true
}
2024-09-10 19:20:03 -04:00
/// Wrapper that defaults if it fails to deserialize. Good for individual
/// settings.
#[ derive(Debug, Default, Clone, Eq, PartialEq) ]
pub struct SafeValue < T > {
inner : T ,
}
impl < ' de , T : Default + for < ' de2 > Deserialize < ' de2 > > Deserialize < ' de >
for SafeValue < T >
{
fn deserialize < D > ( deserializer : D ) -> Result < Self , D ::Error >
where
D : serde ::Deserializer < ' de > ,
{
Ok ( Self {
inner : Deserialize ::deserialize ( deserializer ) . unwrap_or_default ( ) ,
} )
}
}
impl < T : Serialize > Serialize for SafeValue < T > {
fn serialize < S > ( & self , serializer : S ) -> Result < S ::Ok , S ::Error >
where
S : serde ::Serializer ,
{
self . inner . serialize ( serializer )
}
}
impl < T > Deref for SafeValue < T > {
type Target = T ;
fn deref ( & self ) -> & Self ::Target {
& self . inner
}
}
impl < T > DerefMut for SafeValue < T > {
fn deref_mut ( & mut self ) -> & mut T {
& mut self . inner
}
}
impl < T > SafeValue < T > {
pub fn as_deref ( & self ) -> & T {
& self . inner
}
}
2021-07-20 21:50:43 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2021-01-31 22:30:41 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct CodeLensSettings {
2021-02-08 05:45:10 -05:00
/// Flag for providing implementation code lenses.
#[ serde(default) ]
pub implementations : bool ,
/// Flag for providing reference code lenses.
2021-01-31 22:30:41 -05:00
#[ serde(default) ]
pub references : bool ,
/// Flag for providing reference code lens on all functions. For this to have
/// an impact, the `references` flag needs to be `true`.
#[ serde(default) ]
pub references_all_functions : bool ,
2021-06-07 07:38:07 -04:00
/// Flag for providing test code lens on `Deno.test` statements. There is
/// also the `test_args` setting, but this is not used by the server.
#[ serde(default = " is_true " ) ]
pub test : bool ,
2021-01-31 22:30:41 -05:00
}
2021-03-15 18:01:41 -04:00
impl Default for CodeLensSettings {
fn default ( ) -> Self {
Self {
implementations : false ,
references : false ,
references_all_functions : false ,
2021-06-07 07:38:07 -04:00
test : true ,
2021-03-15 18:01:41 -04:00
}
}
}
2023-09-21 01:46:39 -04:00
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct DenoCompletionSettings {
#[ serde(default) ]
pub imports : ImportCompletionSettings ,
}
2023-09-25 22:54:07 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ClassMemberSnippets {
#[ serde(default = " is_true " ) ]
pub enabled : bool ,
}
impl Default for ClassMemberSnippets {
fn default ( ) -> Self {
Self { enabled : true }
}
}
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct ObjectLiteralMethodSnippets {
#[ serde(default = " is_true " ) ]
pub enabled : bool ,
}
impl Default for ObjectLiteralMethodSnippets {
fn default ( ) -> Self {
Self { enabled : true }
}
}
2021-07-20 21:50:43 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2021-03-15 18:01:41 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct CompletionSettings {
#[ serde(default) ]
pub complete_function_calls : bool ,
2021-06-01 07:53:08 -04:00
#[ serde(default = " is_true " ) ]
2023-09-25 22:54:07 -04:00
pub include_automatic_optional_chain_completions : bool ,
#[ serde(default = " is_true " ) ]
pub include_completions_for_import_statements : bool ,
#[ serde(default = " is_true " ) ]
2021-03-15 18:01:41 -04:00
pub names : bool ,
2021-06-01 07:53:08 -04:00
#[ serde(default = " is_true " ) ]
2021-03-15 18:01:41 -04:00
pub paths : bool ,
2021-06-01 07:53:08 -04:00
#[ serde(default = " is_true " ) ]
2021-03-15 18:01:41 -04:00
pub auto_imports : bool ,
2023-09-25 22:54:07 -04:00
#[ serde(default = " is_true " ) ]
pub enabled : bool ,
#[ serde(default) ]
pub class_member_snippets : ClassMemberSnippets ,
#[ serde(default) ]
pub object_literal_method_snippets : ObjectLiteralMethodSnippets ,
2021-03-15 18:01:41 -04:00
}
impl Default for CompletionSettings {
fn default ( ) -> Self {
Self {
complete_function_calls : false ,
2023-09-25 22:54:07 -04:00
include_automatic_optional_chain_completions : true ,
include_completions_for_import_statements : true ,
2021-03-15 18:01:41 -04:00
names : true ,
paths : true ,
auto_imports : true ,
2023-09-25 22:54:07 -04:00
enabled : true ,
class_member_snippets : Default ::default ( ) ,
object_literal_method_snippets : Default ::default ( ) ,
2021-04-08 21:27:27 -04:00
}
}
}
2022-10-15 22:39:43 -04:00
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsSettings {
#[ serde(default) ]
pub parameter_names : InlayHintsParamNamesOptions ,
#[ serde(default) ]
pub parameter_types : InlayHintsParamTypesOptions ,
#[ serde(default) ]
pub variable_types : InlayHintsVarTypesOptions ,
#[ serde(default) ]
pub property_declaration_types : InlayHintsPropDeclTypesOptions ,
#[ serde(default) ]
pub function_like_return_types : InlayHintsFuncLikeReturnTypesOptions ,
#[ serde(default) ]
pub enum_member_values : InlayHintsEnumMemberValuesOptions ,
}
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsParamNamesOptions {
#[ serde(default) ]
pub enabled : InlayHintsParamNamesEnabled ,
#[ serde(default = " is_true " ) ]
pub suppress_when_argument_matches_name : bool ,
}
impl Default for InlayHintsParamNamesOptions {
fn default ( ) -> Self {
Self {
enabled : InlayHintsParamNamesEnabled ::None ,
suppress_when_argument_matches_name : true ,
}
}
}
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub enum InlayHintsParamNamesEnabled {
None ,
Literals ,
All ,
}
impl Default for InlayHintsParamNamesEnabled {
fn default ( ) -> Self {
Self ::None
}
}
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsParamTypesOptions {
#[ serde(default) ]
pub enabled : bool ,
}
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsVarTypesOptions {
#[ serde(default) ]
pub enabled : bool ,
#[ serde(default = " is_true " ) ]
2022-10-28 14:48:14 -04:00
pub suppress_when_type_matches_name : bool ,
2022-10-15 22:39:43 -04:00
}
impl Default for InlayHintsVarTypesOptions {
fn default ( ) -> Self {
Self {
enabled : false ,
2022-10-28 14:48:14 -04:00
suppress_when_type_matches_name : true ,
2022-10-15 22:39:43 -04:00
}
}
}
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsPropDeclTypesOptions {
#[ serde(default) ]
pub enabled : bool ,
}
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsFuncLikeReturnTypesOptions {
#[ serde(default) ]
pub enabled : bool ,
}
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct InlayHintsEnumMemberValuesOptions {
#[ serde(default) ]
pub enabled : bool ,
}
2021-07-20 21:50:43 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2021-04-08 21:27:27 -04:00
#[ serde(rename_all = " camelCase " ) ]
pub struct ImportCompletionSettings {
2021-06-01 07:53:08 -04:00
/// A flag that indicates if non-explicitly set origins should be checked for
/// supporting import suggestions.
#[ serde(default = " is_true " ) ]
pub auto_discover : bool ,
/// A map of origins which have had explicitly set if import suggestions are
/// enabled.
2021-04-08 21:27:27 -04:00
#[ serde(default) ]
pub hosts : HashMap < String , bool > ,
}
impl Default for ImportCompletionSettings {
fn default ( ) -> Self {
Self {
2021-06-01 07:53:08 -04:00
auto_discover : true ,
2021-04-08 21:27:27 -04:00
hosts : HashMap ::default ( ) ,
2021-03-15 18:01:41 -04:00
}
}
}
2022-03-29 18:59:27 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct TestingSettings {
/// A vector of arguments which should be used when running the tests for
/// a workspace.
#[ serde(default) ]
pub args : Vec < String > ,
}
impl Default for TestingSettings {
fn default ( ) -> Self {
Self {
args : vec ! [ " --allow-all " . to_string ( ) , " --no-check " . to_string ( ) ] ,
}
}
}
2022-05-16 07:10:08 -04:00
fn default_to_true ( ) -> bool {
true
}
2023-05-11 17:17:14 -04:00
fn default_document_preload_limit ( ) -> usize {
1000
}
2023-01-03 10:59:48 -05:00
fn empty_string_none < ' de , D : serde ::Deserializer < ' de > > (
d : D ,
) -> Result < Option < String > , D ::Error > {
let o : Option < String > = Option ::deserialize ( d ) ? ;
Ok ( o . filter ( | s | ! s . is_empty ( ) ) )
}
2023-09-25 22:54:07 -04:00
#[ derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " kebab-case " ) ]
pub enum ImportModuleSpecifier {
NonRelative ,
ProjectRelative ,
Relative ,
Shortest ,
}
impl Default for ImportModuleSpecifier {
fn default ( ) -> Self {
Self ::Shortest
}
}
#[ derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " kebab-case " ) ]
pub enum JsxAttributeCompletionStyle {
Auto ,
Braces ,
None ,
}
impl Default for JsxAttributeCompletionStyle {
fn default ( ) -> Self {
Self ::Auto
}
}
2023-10-16 21:51:42 -04:00
#[ derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " kebab-case " ) ]
pub enum QuoteStyle {
Auto ,
Double ,
Single ,
}
impl Default for QuoteStyle {
fn default ( ) -> Self {
Self ::Auto
}
}
impl From < & FmtOptionsConfig > for QuoteStyle {
fn from ( config : & FmtOptionsConfig ) -> Self {
match config . single_quote {
Some ( true ) = > QuoteStyle ::Single ,
_ = > QuoteStyle ::Double ,
}
}
}
2023-09-25 22:54:07 -04:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct LanguagePreferences {
#[ serde(default) ]
pub import_module_specifier : ImportModuleSpecifier ,
#[ serde(default) ]
pub jsx_attribute_completion_style : JsxAttributeCompletionStyle ,
#[ serde(default) ]
pub auto_import_file_exclude_patterns : Vec < String > ,
#[ serde(default = " is_true " ) ]
pub use_aliases_for_renames : bool ,
2023-10-16 21:51:42 -04:00
#[ serde(default) ]
pub quote_style : QuoteStyle ,
2024-10-25 13:35:09 -04:00
#[ serde(default) ]
pub prefer_type_only_auto_imports : bool ,
2023-09-25 22:54:07 -04:00
}
impl Default for LanguagePreferences {
fn default ( ) -> Self {
LanguagePreferences {
import_module_specifier : Default ::default ( ) ,
jsx_attribute_completion_style : Default ::default ( ) ,
auto_import_file_exclude_patterns : vec ! [ ] ,
use_aliases_for_renames : true ,
2023-10-16 21:51:42 -04:00
quote_style : Default ::default ( ) ,
2024-10-25 13:35:09 -04:00
prefer_type_only_auto_imports : false ,
2023-09-25 22:54:07 -04:00
}
}
}
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct UpdateImportsOnFileMoveOptions {
#[ serde(default) ]
pub enabled : UpdateImportsOnFileMoveEnabled ,
}
#[ derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " kebab-case " ) ]
pub enum UpdateImportsOnFileMoveEnabled {
Always ,
Prompt ,
Never ,
}
impl Default for UpdateImportsOnFileMoveEnabled {
fn default ( ) -> Self {
Self ::Prompt
}
}
2023-09-21 01:46:39 -04:00
#[ derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
pub struct LanguageWorkspaceSettings {
#[ serde(default) ]
pub inlay_hints : InlayHintsSettings ,
#[ serde(default) ]
2023-09-25 22:54:07 -04:00
pub preferences : LanguagePreferences ,
#[ serde(default) ]
2023-09-21 01:46:39 -04:00
pub suggest : CompletionSettings ,
2023-09-25 22:54:07 -04:00
#[ serde(default) ]
pub update_imports_on_file_move : UpdateImportsOnFileMoveOptions ,
2023-09-21 01:46:39 -04:00
}
2023-12-21 20:04:02 -05:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
#[ serde(rename_all = " camelCase " ) ]
#[ serde(untagged) ]
pub enum InspectSetting {
Bool ( bool ) ,
String ( String ) ,
}
impl Default for InspectSetting {
fn default ( ) -> Self {
InspectSetting ::Bool ( false )
}
}
impl InspectSetting {
pub fn to_address ( & self ) -> Option < String > {
match self {
InspectSetting ::Bool ( false ) = > None ,
InspectSetting ::Bool ( true ) = > Some ( " 127.0.0.1:9222 " . to_string ( ) ) ,
InspectSetting ::String ( s ) = > Some ( s . clone ( ) ) ,
}
}
}
2021-05-09 21:16:04 -04:00
/// Deno language server specific settings that are applied to a workspace.
2023-01-03 10:59:48 -05:00
#[ derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq) ]
2020-12-07 05:46:39 -05:00
#[ serde(rename_all = " camelCase " ) ]
pub struct WorkspaceSettings {
2021-05-09 21:16:04 -04:00
/// A flag that indicates if Deno is enabled for the workspace.
2023-09-01 16:13:13 -04:00
pub enable : Option < bool > ,
2021-05-09 21:16:04 -04:00
2023-09-13 12:30:27 -04:00
/// A list of paths, using the root_uri as a base that should be Deno
/// disabled.
#[ serde(default) ]
pub disable_paths : Vec < String > ,
2022-03-20 21:33:37 -04:00
/// A list of paths, using the root_uri as a base that should be Deno enabled.
2023-09-09 10:04:21 -04:00
pub enable_paths : Option < Vec < String > > ,
2022-03-20 21:33:37 -04:00
2021-07-27 17:25:09 -04:00
/// An option that points to a path string of the path to utilise as the
/// cache/DENO_DIR for the language server.
2023-01-03 10:59:48 -05:00
#[ serde(default, deserialize_with = " empty_string_none " ) ]
2021-07-27 17:25:09 -04:00
pub cache : Option < String > ,
2023-09-24 12:59:42 -04:00
/// Cache local modules and their dependencies on `textDocument/didSave`
/// notifications corresponding to them.
#[ serde(default) ]
pub cache_on_save : bool ,
2022-01-23 19:27:52 -05:00
/// Override the default stores used to validate certificates. This overrides
/// the environment variable `DENO_TLS_CA_STORE` if present.
pub certificate_stores : Option < Vec < String > > ,
2021-05-10 12:16:39 -04:00
/// An option that points to a path string of the config file to apply to
2021-05-09 21:16:04 -04:00
/// code within the workspace.
2023-01-03 10:59:48 -05:00
#[ serde(default, deserialize_with = " empty_string_none " ) ]
2020-12-07 05:46:39 -05:00
pub config : Option < String > ,
2021-05-09 21:16:04 -04:00
/// An option that points to a path string of the import map to apply to the
/// code within the workspace.
2023-01-03 10:59:48 -05:00
#[ serde(default, deserialize_with = " empty_string_none " ) ]
2020-12-07 05:46:39 -05:00
pub import_map : Option < String > ,
2021-05-09 21:16:04 -04:00
/// Code lens specific settings for the workspace.
2021-03-15 18:01:41 -04:00
#[ serde(default) ]
pub code_lens : CodeLensSettings ,
2021-05-09 21:16:04 -04:00
2021-05-11 00:54:10 -04:00
/// A flag that indicates if internal debug logging should be made available.
#[ serde(default) ]
pub internal_debug : bool ,
2021-01-04 16:52:20 -05:00
2023-12-21 20:04:02 -05:00
#[ serde(default) ]
pub internal_inspect : InspectSetting ,
2023-12-08 12:04:56 -05:00
/// Write logs to a file in a project-local directory.
#[ serde(default) ]
pub log_file : bool ,
2021-05-09 21:16:04 -04:00
/// A flag that indicates if linting is enabled for the workspace.
2022-05-16 07:10:08 -04:00
#[ serde(default = " default_to_true " ) ]
2020-12-07 05:46:39 -05:00
pub lint : bool ,
2021-05-09 21:16:04 -04:00
2023-05-11 17:17:14 -04:00
/// Limits the number of files that can be preloaded by the language server.
#[ serde(default = " default_document_preload_limit " ) ]
pub document_preload_limit : usize ,
2021-05-11 00:54:10 -04:00
#[ serde(default) ]
2023-09-21 01:46:39 -04:00
pub suggest : DenoCompletionSettings ,
2021-05-11 00:54:10 -04:00
2022-03-29 18:59:27 -04:00
/// Testing settings for the workspace.
#[ serde(default) ]
pub testing : TestingSettings ,
2022-01-23 19:27:52 -05:00
/// An option which sets the cert file to use when attempting to fetch remote
/// resources. This overrides `DENO_CERT` if present.
2023-01-03 10:59:48 -05:00
#[ serde(default, deserialize_with = " empty_string_none " ) ]
2022-01-23 19:27:52 -05:00
pub tls_certificate : Option < String > ,
/// An option, if set, will unsafely ignore certificate errors when fetching
/// remote resources.
#[ serde(default) ]
pub unsafely_ignore_certificate_errors : Option < Vec < String > > ,
2021-01-04 16:52:20 -05:00
#[ serde(default) ]
2024-09-10 19:20:03 -04:00
pub unstable : SafeValue < Vec < String > > ,
2023-09-21 01:46:39 -04:00
#[ serde(default) ]
pub javascript : LanguageWorkspaceSettings ,
#[ serde(default) ]
pub typescript : LanguageWorkspaceSettings ,
2020-12-07 05:46:39 -05:00
}
2023-01-03 10:59:48 -05:00
impl Default for WorkspaceSettings {
fn default ( ) -> Self {
WorkspaceSettings {
2023-09-01 16:13:13 -04:00
enable : None ,
2023-09-13 12:30:27 -04:00
disable_paths : vec ! [ ] ,
2023-09-09 10:04:21 -04:00
enable_paths : None ,
2023-01-03 10:59:48 -05:00
cache : None ,
2023-09-24 12:59:42 -04:00
cache_on_save : false ,
2023-01-03 10:59:48 -05:00
certificate_stores : None ,
config : None ,
import_map : None ,
code_lens : Default ::default ( ) ,
internal_debug : false ,
2023-12-21 20:04:02 -05:00
internal_inspect : Default ::default ( ) ,
2023-12-08 12:04:56 -05:00
log_file : false ,
2023-01-03 10:59:48 -05:00
lint : true ,
2023-05-11 17:17:14 -04:00
document_preload_limit : default_document_preload_limit ( ) ,
2023-01-03 10:59:48 -05:00
suggest : Default ::default ( ) ,
testing : Default ::default ( ) ,
tls_certificate : None ,
unsafely_ignore_certificate_errors : None ,
2024-09-10 19:20:03 -04:00
unstable : Default ::default ( ) ,
2023-09-21 01:46:39 -04:00
javascript : Default ::default ( ) ,
typescript : Default ::default ( ) ,
2023-01-03 10:59:48 -05:00
}
}
}
2021-01-31 22:30:41 -05:00
impl WorkspaceSettings {
2023-09-21 01:46:39 -04:00
pub fn from_raw_settings (
deno : Value ,
javascript : Value ,
typescript : Value ,
) -> Self {
fn parse_or_default < T : Default + DeserializeOwned > (
value : Value ,
description : & str ,
) -> T {
if value . is_null ( ) {
return T ::default ( ) ;
}
match serde_json ::from_value ( value ) {
Ok ( v ) = > v ,
Err ( err ) = > {
lsp_warn! ( " Couldn't parse {description}: {err} " ) ;
T ::default ( )
}
}
}
let deno_inlay_hints =
deno . as_object ( ) . and_then ( | o | o . get ( " inlayHints " ) . cloned ( ) ) ;
let deno_suggest = deno . as_object ( ) . and_then ( | o | o . get ( " suggest " ) . cloned ( ) ) ;
let mut settings : Self = parse_or_default ( deno , " settings under \" deno \" " ) ;
settings . javascript =
parse_or_default ( javascript , " settings under \" javascript \" " ) ;
settings . typescript =
parse_or_default ( typescript , " settings under \" typescript \" " ) ;
if let Some ( inlay_hints ) = deno_inlay_hints {
let inlay_hints : InlayHintsSettings =
parse_or_default ( inlay_hints , " settings under \" deno.inlayHints \" " ) ;
if inlay_hints . parameter_names . enabled ! = Default ::default ( ) {
lsp_warn! ( " \" deno.inlayHints.parameterNames.enabled \" is deprecated. Instead use \" javascript.inlayHints.parameterNames.enabled \" and \" typescript.inlayHints.parameterNames.enabled \" . " ) ;
settings . javascript . inlay_hints . parameter_names . enabled =
inlay_hints . parameter_names . enabled . clone ( ) ;
settings . typescript . inlay_hints . parameter_names . enabled =
inlay_hints . parameter_names . enabled ;
}
if ! inlay_hints
. parameter_names
. suppress_when_argument_matches_name
{
lsp_warn! ( " \" deno.inlayHints.parameterNames.suppressWhenArgumentMatchesName \" is deprecated. Instead use \" javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName \" and \" typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName \" . " ) ;
settings
. javascript
. inlay_hints
. parameter_names
. suppress_when_argument_matches_name = inlay_hints
. parameter_names
. suppress_when_argument_matches_name ;
settings
. typescript
. inlay_hints
. parameter_names
. suppress_when_argument_matches_name = inlay_hints
. parameter_names
. suppress_when_argument_matches_name ;
}
if inlay_hints . parameter_types . enabled {
lsp_warn! ( " \" deno.inlayHints.parameterTypes.enabled \" is deprecated. Instead use \" javascript.inlayHints.parameterTypes.enabled \" and \" typescript.inlayHints.parameterTypes.enabled \" . " ) ;
settings . javascript . inlay_hints . parameter_types . enabled =
inlay_hints . parameter_types . enabled ;
settings . typescript . inlay_hints . parameter_types . enabled =
inlay_hints . parameter_types . enabled ;
}
if inlay_hints . variable_types . enabled {
lsp_warn! ( " \" deno.inlayHints.variableTypes.enabled \" is deprecated. Instead use \" javascript.inlayHints.variableTypes.enabled \" and \" typescript.inlayHints.variableTypes.enabled \" . " ) ;
settings . javascript . inlay_hints . variable_types . enabled =
inlay_hints . variable_types . enabled ;
settings . typescript . inlay_hints . variable_types . enabled =
inlay_hints . variable_types . enabled ;
}
if ! inlay_hints . variable_types . suppress_when_type_matches_name {
lsp_warn! ( " \" deno.inlayHints.variableTypes.suppressWhenTypeMatchesName \" is deprecated. Instead use \" javascript.inlayHints.variableTypes.suppressWhenTypeMatchesName \" and \" typescript.inlayHints.variableTypes.suppressWhenTypeMatchesName \" . " ) ;
settings
. javascript
. inlay_hints
. variable_types
. suppress_when_type_matches_name =
inlay_hints . variable_types . suppress_when_type_matches_name ;
settings
. typescript
. inlay_hints
. variable_types
. suppress_when_type_matches_name =
inlay_hints . variable_types . suppress_when_type_matches_name ;
}
if inlay_hints . property_declaration_types . enabled {
lsp_warn! ( " \" deno.inlayHints.propertyDeclarationTypes.enabled \" is deprecated. Instead use \" javascript.inlayHints.propertyDeclarationTypes.enabled \" and \" typescript.inlayHints.propertyDeclarationTypes.enabled \" . " ) ;
settings
. javascript
. inlay_hints
. property_declaration_types
. enabled = inlay_hints . property_declaration_types . enabled ;
settings
. typescript
. inlay_hints
. property_declaration_types
. enabled = inlay_hints . property_declaration_types . enabled ;
}
if inlay_hints . function_like_return_types . enabled {
lsp_warn! ( " \" deno.inlayHints.functionLikeReturnTypes.enabled \" is deprecated. Instead use \" javascript.inlayHints.functionLikeReturnTypes.enabled \" and \" typescript.inlayHints.functionLikeReturnTypes.enabled \" . " ) ;
settings
. javascript
. inlay_hints
. function_like_return_types
. enabled = inlay_hints . function_like_return_types . enabled ;
settings
. typescript
. inlay_hints
. function_like_return_types
. enabled = inlay_hints . function_like_return_types . enabled ;
}
if inlay_hints . enum_member_values . enabled {
lsp_warn! ( " \" deno.inlayHints.enumMemberValues.enabled \" is deprecated. Instead use \" javascript.inlayHints.enumMemberValues.enabled \" and \" typescript.inlayHints.enumMemberValues.enabled \" . " ) ;
settings . javascript . inlay_hints . enum_member_values . enabled =
inlay_hints . enum_member_values . enabled ;
settings . typescript . inlay_hints . enum_member_values . enabled =
inlay_hints . enum_member_values . enabled ;
}
}
if let Some ( suggest ) = deno_suggest {
let suggest : CompletionSettings =
parse_or_default ( suggest , " settings under \" deno.suggest \" " ) ;
if suggest . complete_function_calls {
lsp_warn! ( " \" deno.suggest.completeFunctionCalls \" is deprecated. Instead use \" javascript.suggest.completeFunctionCalls \" and \" typescript.suggest.completeFunctionCalls \" . " ) ;
settings . javascript . suggest . complete_function_calls =
suggest . complete_function_calls ;
settings . typescript . suggest . complete_function_calls =
suggest . complete_function_calls ;
}
if ! suggest . names {
lsp_warn! ( " \" deno.suggest.names \" is deprecated. Instead use \" javascript.suggest.names \" and \" typescript.suggest.names \" . " ) ;
settings . javascript . suggest . names = suggest . names ;
settings . typescript . suggest . names = suggest . names ;
}
if ! suggest . paths {
lsp_warn! ( " \" deno.suggest.paths \" is deprecated. Instead use \" javascript.suggest.paths \" and \" typescript.suggest.paths \" . " ) ;
settings . javascript . suggest . paths = suggest . paths ;
settings . typescript . suggest . paths = suggest . paths ;
}
if ! suggest . auto_imports {
lsp_warn! ( " \" deno.suggest.autoImports \" is deprecated. Instead use \" javascript.suggest.autoImports \" and \" typescript.suggest.autoImports \" . " ) ;
settings . javascript . suggest . auto_imports = suggest . auto_imports ;
settings . typescript . suggest . auto_imports = suggest . auto_imports ;
}
}
settings
}
pub fn from_initialization_options ( options : Value ) -> Self {
let deno = options ;
let javascript = deno
. as_object ( )
. and_then ( | o | o . get ( " javascript " ) . cloned ( ) )
. unwrap_or_default ( ) ;
let typescript = deno
. as_object ( )
. and_then ( | o | o . get ( " typescript " ) . cloned ( ) )
. unwrap_or_default ( ) ;
Self ::from_raw_settings ( deno , javascript , typescript )
}
2021-01-31 22:30:41 -05:00
}
2021-03-09 21:41:35 -05:00
#[ derive(Debug, Default, Clone) ]
2021-05-20 05:56:48 -04:00
pub struct Settings {
2024-06-13 15:57:14 -04:00
pub unscoped : Arc < WorkspaceSettings > ,
pub by_workspace_folder :
BTreeMap < ModuleSpecifier , Option < Arc < WorkspaceSettings > > > ,
2024-03-31 16:39:23 -04:00
pub first_folder : Option < ModuleSpecifier > ,
2023-10-24 16:27:27 -04:00
}
impl Settings {
2024-03-21 00:29:52 -04:00
/// 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 > {
2024-09-28 07:55:01 -04:00
let Ok ( path ) = url_to_file_path ( specifier ) else {
2024-03-21 00:29:52 -04:00
// Non-file URLs are not disabled by these settings.
return Some ( true ) ;
} ;
let ( settings , mut folder_uri ) = self . get_for_specifier ( specifier ) ;
2024-04-02 18:02:50 -04:00
folder_uri = folder_uri . or ( self . first_folder . as_ref ( ) ) ;
2024-03-21 00:29:52 -04:00
let mut disable_paths = vec! [ ] ;
let mut enable_paths = None ;
if let Some ( folder_uri ) = folder_uri {
2024-09-28 07:55:01 -04:00
if let Ok ( folder_path ) = url_to_file_path ( folder_uri ) {
2024-03-21 00:29:52 -04:00
disable_paths = settings
. disable_paths
. iter ( )
. map ( | p | folder_path . join ( p ) )
. collect ::< Vec < _ > > ( ) ;
enable_paths = settings . enable_paths . as_ref ( ) . map ( | enable_paths | {
enable_paths
. iter ( )
. map ( | p | folder_path . join ( p ) )
. collect ::< Vec < _ > > ( )
} ) ;
}
}
if disable_paths . iter ( ) . any ( | p | path . starts_with ( p ) ) {
Some ( false )
} else if let Some ( enable_paths ) = & enable_paths {
for enable_path in enable_paths {
if path . starts_with ( enable_path ) {
return Some ( true ) ;
}
}
Some ( false )
} else {
settings . enable
}
}
2023-10-24 16:27:27 -04:00
pub fn get_unscoped ( & self ) -> & WorkspaceSettings {
& self . unscoped
}
pub fn get_for_specifier (
& self ,
specifier : & ModuleSpecifier ,
) -> ( & WorkspaceSettings , Option < & ModuleSpecifier > ) {
2024-09-28 07:55:01 -04:00
let Ok ( path ) = url_to_file_path ( specifier ) else {
2024-06-11 13:14:36 -04:00
return ( & self . unscoped , self . first_folder . as_ref ( ) ) ;
2023-10-24 16:27:27 -04:00
} ;
2024-03-21 00:29:52 -04:00
for ( folder_uri , settings ) in self . by_workspace_folder . iter ( ) . rev ( ) {
if let Some ( settings ) = settings {
2024-09-28 07:55:01 -04:00
let Ok ( folder_path ) = url_to_file_path ( folder_uri ) else {
2023-10-24 16:27:27 -04:00
continue ;
} ;
if path . starts_with ( folder_path ) {
return ( settings , Some ( folder_uri ) ) ;
}
}
}
2024-06-11 13:14:36 -04:00
( & self . unscoped , self . first_folder . as_ref ( ) )
2023-10-24 16:27:27 -04:00
}
2024-03-21 00:29:52 -04:00
pub fn enable_settings_hash ( & self ) -> u64 {
2024-05-29 14:38:18 -04:00
let mut hasher = FastInsecureHasher ::new_without_deno_version ( ) ;
2024-03-21 00:29:52 -04:00
let unscoped = self . get_unscoped ( ) ;
hasher . write_hashable ( unscoped . enable ) ;
hasher . write_hashable ( & unscoped . enable_paths ) ;
hasher . write_hashable ( & unscoped . disable_paths ) ;
hasher . write_hashable ( unscoped . document_preload_limit ) ;
for ( folder_uri , settings ) in & self . by_workspace_folder {
hasher . write_hashable ( folder_uri ) ;
hasher . write_hashable (
settings
. as_ref ( )
. map ( | s | ( & s . enable , & s . enable_paths , & s . disable_paths ) ) ,
) ;
}
2024-03-31 16:39:23 -04:00
hasher . write_hashable ( & self . first_folder ) ;
2024-03-21 00:29:52 -04:00
hasher . finish ( )
}
2021-05-20 05:56:48 -04:00
}
2024-04-19 21:00:03 -04:00
#[ derive(Clone, Debug, Default) ]
2020-12-07 05:46:39 -05:00
pub struct Config {
2024-06-13 15:57:14 -04:00
pub client_capabilities : Arc < ClientCapabilities > ,
pub settings : Arc < Settings > ,
pub workspace_folders : Arc < Vec < ( ModuleSpecifier , lsp ::WorkspaceFolder ) > > ,
2024-04-02 18:02:50 -04:00
pub tree : ConfigTree ,
2020-12-07 05:46:39 -05:00
}
impl Config {
2023-09-09 10:04:21 -04:00
#[ cfg(test) ]
2024-08-23 20:21:21 -04:00
pub fn new_with_roots ( root_urls : impl IntoIterator < Item = Url > ) -> Self {
use super ::urls ::url_to_uri ;
2024-03-26 11:52:20 -04:00
let mut config = Self ::default ( ) ;
2024-03-21 00:29:52 -04:00
let mut folders = vec! [ ] ;
2024-08-23 20:21:21 -04:00
for root_url in root_urls {
2024-08-28 00:15:48 -04:00
let root_uri = url_to_uri ( & root_url ) . unwrap ( ) ;
2024-08-23 20:21:21 -04:00
let name = root_url . path_segments ( ) . and_then ( | s | s . last ( ) ) ;
2024-03-21 00:29:52 -04:00
let name = name . unwrap_or_default ( ) . to_string ( ) ;
folders . push ( (
2024-08-23 20:21:21 -04:00
root_url ,
2024-03-21 00:29:52 -04:00
lsp ::WorkspaceFolder {
uri : root_uri ,
name ,
} ,
) ) ;
}
config . set_workspace_folders ( folders ) ;
2023-09-09 10:04:21 -04:00
config
}
2024-03-21 00:29:52 -04:00
pub fn set_workspace_folders (
& mut self ,
folders : Vec < ( ModuleSpecifier , lsp ::WorkspaceFolder ) > ,
) {
2024-06-13 15:57:14 -04:00
self . settings = Arc ::new ( Settings {
unscoped : self . settings . unscoped . clone ( ) ,
by_workspace_folder : folders
. iter ( )
. map ( | ( s , _ ) | ( s . clone ( ) , None ) )
. collect ( ) ,
first_folder : folders . first ( ) . map ( | ( s , _ ) | s . clone ( ) ) ,
} ) ;
self . workspace_folders = Arc ::new ( folders ) ;
2024-03-21 00:29:52 -04:00
}
2023-10-24 16:27:27 -04:00
pub fn set_workspace_settings (
& mut self ,
unscoped : WorkspaceSettings ,
2024-03-21 00:29:52 -04:00
folder_settings : Vec < ( ModuleSpecifier , WorkspaceSettings ) > ,
2023-10-24 16:27:27 -04:00
) {
2024-06-13 15:57:14 -04:00
let mut by_folder = folder_settings . into_iter ( ) . collect ::< HashMap < _ , _ > > ( ) ;
self . settings = Arc ::new ( Settings {
unscoped : Arc ::new ( unscoped ) ,
by_workspace_folder : self
. settings
. by_workspace_folder
. keys ( )
. map ( | s | ( s . clone ( ) , by_folder . remove ( s ) . map ( Arc ::new ) ) )
. collect ( ) ,
first_folder : self . settings . first_folder . clone ( ) ,
} ) ;
2023-10-24 16:27:27 -04:00
}
pub fn workspace_settings ( & self ) -> & WorkspaceSettings {
self . settings . get_unscoped ( )
}
pub fn workspace_settings_for_specifier (
& self ,
specifier : & ModuleSpecifier ,
) -> & WorkspaceSettings {
self . settings . get_for_specifier ( specifier ) . 0
}
pub fn language_settings_for_specifier (
& self ,
specifier : & ModuleSpecifier ,
) -> Option < & LanguageWorkspaceSettings > {
let workspace_settings = self . workspace_settings_for_specifier ( specifier ) ;
match MediaType ::from_specifier ( specifier ) {
MediaType ::JavaScript
| MediaType ::Jsx
| MediaType ::Mjs
| MediaType ::Cjs = > Some ( & workspace_settings . javascript ) ,
MediaType ::TypeScript
| MediaType ::Mts
| MediaType ::Cts
| MediaType ::Dts
| MediaType ::Dmts
| MediaType ::Dcts
| MediaType ::Tsx = > Some ( & workspace_settings . typescript ) ,
MediaType ::Json
| MediaType ::Wasm
| MediaType ::TsBuildInfo
| MediaType ::SourceMap
| MediaType ::Unknown = > None ,
}
}
/// Determine if any inlay hints are enabled. This allows short circuiting
/// when there are no inlay hints enabled.
pub fn enabled_inlay_hints_for_specifier (
& self ,
specifier : & ModuleSpecifier ,
) -> bool {
let Some ( settings ) = self . language_settings_for_specifier ( specifier ) else {
return false ;
} ;
! matches! (
settings . inlay_hints . parameter_names . enabled ,
InlayHintsParamNamesEnabled ::None
) | | settings . inlay_hints . parameter_types . enabled
| | settings . inlay_hints . variable_types . enabled
| | settings . inlay_hints . property_declaration_types . enabled
| | settings . inlay_hints . function_like_return_types . enabled
| | settings . inlay_hints . enum_member_values . enabled
}
2023-09-09 10:04:21 -04:00
pub fn root_uri ( & self ) -> Option < & Url > {
2024-01-01 17:22:48 -05:00
self . workspace_folders . first ( ) . map ( | p | & p . 0 )
2023-09-09 10:04:21 -04:00
}
2021-05-09 21:16:04 -04:00
pub fn specifier_enabled ( & self , specifier : & ModuleSpecifier ) -> bool {
2024-07-18 18:16:35 -04:00
let data = self . tree . data_for_specifier ( specifier ) ;
if let Some ( data ) = & data {
if let Ok ( path ) = specifier . to_file_path ( ) {
if data . exclude_files . matches_path ( & path ) {
2024-03-21 00:29:52 -04:00
return false ;
}
}
}
self
. settings
. specifier_enabled ( specifier )
2024-07-18 18:16:35 -04:00
. unwrap_or_else ( | | data . and_then ( | d | d . maybe_deno_json ( ) ) . is_some ( ) )
2021-06-07 07:38:07 -04:00
}
2023-09-09 14:37:01 -04:00
pub fn specifier_enabled_for_test (
& self ,
specifier : & ModuleSpecifier ,
) -> bool {
2024-07-18 18:16:35 -04:00
if let Some ( data ) = self . tree . data_for_specifier ( specifier ) {
if ! data . test_config . files . matches_specifier ( specifier ) {
return false ;
2023-09-09 14:37:01 -04:00
}
}
2024-03-21 00:29:52 -04:00
self . specifier_enabled ( specifier )
2023-03-30 17:47:53 -04:00
}
2023-12-08 12:04:56 -05:00
pub fn log_file ( & self ) -> bool {
self . settings . unscoped . log_file
}
2023-12-21 20:04:02 -05:00
pub fn internal_inspect ( & self ) -> & InspectSetting {
& self . settings . unscoped . internal_inspect
}
2024-05-29 16:31:09 -04:00
pub fn set_client_capabilities (
2020-12-07 05:46:39 -05:00
& mut self ,
2024-05-29 16:31:09 -04:00
client_capabilities : ClientCapabilities ,
2020-12-07 05:46:39 -05:00
) {
2024-06-13 15:57:14 -04:00
self . client_capabilities = Arc ::new ( client_capabilities ) ;
2024-05-29 16:31:09 -04:00
}
2021-01-04 16:52:20 -05:00
2024-05-29 16:31:09 -04:00
pub fn workspace_capable ( & self ) -> bool {
self . client_capabilities . workspace . is_some ( )
}
2021-04-02 02:21:07 -04:00
2024-05-29 16:31:09 -04:00
pub fn workspace_configuration_capable ( & self ) -> bool {
( | | self . client_capabilities . workspace . as_ref ( ) ? . configuration ) ( )
. unwrap_or ( false )
}
pub fn did_change_watched_files_capable ( & self ) -> bool {
( | | {
let workspace = self . client_capabilities . workspace . as_ref ( ) ? ;
let did_change_watched_files =
workspace . did_change_watched_files . as_ref ( ) ? ;
did_change_watched_files . dynamic_registration
} ) ( )
. unwrap_or ( false )
}
pub fn will_rename_files_capable ( & self ) -> bool {
( | | {
let workspace = self . client_capabilities . workspace . as_ref ( ) ? ;
let file_operations = workspace . file_operations . as_ref ( ) ? ;
file_operations . dynamic_registration . filter ( | d | * d ) ? ;
file_operations . will_rename
} ) ( )
. unwrap_or ( false )
}
pub fn line_folding_only_capable ( & self ) -> bool {
( | | {
let text_document = self . client_capabilities . text_document . as_ref ( ) ? ;
text_document . folding_range . as_ref ( ) ? . line_folding_only
} ) ( )
. unwrap_or ( false )
}
pub fn code_action_disabled_capable ( & self ) -> bool {
( | | {
let text_document = self . client_capabilities . text_document . as_ref ( ) ? ;
text_document . code_action . as_ref ( ) ? . disabled_support
} ) ( )
. unwrap_or ( false )
}
pub fn snippet_support_capable ( & self ) -> bool {
( | | {
let text_document = self . client_capabilities . text_document . as_ref ( ) ? ;
let completion = text_document . completion . as_ref ( ) ? ;
completion . completion_item . as_ref ( ) ? . snippet_support
} ) ( )
. unwrap_or ( false )
}
pub fn testing_api_capable ( & self ) -> bool {
( | | {
let experimental = self . client_capabilities . experimental . as_ref ( ) ? ;
experimental . get ( " testingApi " ) ? . as_bool ( )
} ) ( )
. unwrap_or ( false )
2020-12-07 05:46:39 -05:00
}
2021-05-09 21:16:04 -04:00
}
2024-04-02 18:02:50 -04:00
#[ 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 ,
" lib " : [ " deno.ns " , " deno.window " , " deno.unstable " ] ,
" module " : " esnext " ,
" moduleDetection " : " force " ,
" noEmit " : true ,
2024-09-18 09:49:30 -04:00
" noImplicitOverride " : true ,
2024-04-02 18:02:50 -04:00
" resolveJsonModule " : true ,
" strict " : true ,
" target " : " esnext " ,
" useDefineForClassFields " : true ,
2024-05-29 20:09:16 -04:00
" jsx " : " react " ,
" jsxFactory " : " React.createElement " ,
" jsxFragmentFactory " : " React.Fragment " ,
2024-04-02 18:02:50 -04:00
} ) ) ,
}
}
}
impl LspTsConfig {
2024-04-30 20:44:20 -04:00
pub fn new ( config_file : Option < & ConfigFile > ) -> Self {
2024-04-02 18:02:50 -04:00
let mut ts_config = Self ::default ( ) ;
2024-04-11 16:55:27 -04:00
match ts_config . inner . merge_tsconfig_from_config_file ( config_file ) {
Ok ( Some ( ignored_options ) ) = > lsp_warn! ( " {} " , ignored_options ) ,
Err ( err ) = > lsp_warn! ( " {} " , err ) ,
_ = > { }
2024-04-02 18:02:50 -04:00
}
ts_config
}
2024-03-26 11:52:20 -04:00
}
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub enum ConfigWatchedFileType {
DenoJson ,
Lockfile ,
2024-07-18 18:16:35 -04:00
NpmRc ,
2024-03-26 11:52:20 -04:00
PackageJson ,
ImportMap ,
}
/// Contains the config file and dependent information.
#[ derive(Debug, Clone) ]
pub struct ConfigData {
2024-07-18 18:16:35 -04:00
pub scope : Arc < ModuleSpecifier > ,
2024-08-09 03:50:37 -04:00
pub canonicalized_scope : Option < Arc < ModuleSpecifier > > ,
2024-07-19 15:56:07 -04:00
pub member_dir : Arc < WorkspaceDirectory > ,
pub fmt_config : Arc < FmtConfig > ,
pub lint_config : Arc < LintConfig > ,
pub test_config : Arc < TestConfig > ,
2024-07-18 18:16:35 -04:00
pub exclude_files : Arc < PathOrPatternSet > ,
2024-07-25 09:07:59 -04:00
pub linter : Arc < CliLinter > ,
2024-04-02 18:02:50 -04:00
pub ts_config : Arc < LspTsConfig > ,
2024-04-05 11:18:48 -04:00
pub byonm : bool ,
2024-03-26 11:52:20 -04:00
pub node_modules_dir : Option < PathBuf > ,
pub vendor_dir : Option < PathBuf > ,
2024-06-28 20:18:21 -04:00
pub lockfile : Option < Arc < CliLockfile > > ,
2024-05-23 17:26:23 -04:00
pub npmrc : Option < Arc < ResolvedNpmRc > > ,
2024-07-18 18:16:35 -04:00
pub resolver : Arc < WorkspaceResolver > ,
2024-09-28 19:17:48 -04:00
pub sloppy_imports_resolver : Option < Arc < CliSloppyImportsResolver > > ,
2024-07-18 18:16:35 -04:00
pub import_map_from_settings : Option < ModuleSpecifier > ,
2024-10-31 06:52:43 -04:00
pub unstable : BTreeSet < String > ,
2024-03-26 11:52:20 -04:00
watched_files : HashMap < ModuleSpecifier , ConfigWatchedFileType > ,
}
impl ConfigData {
2024-07-19 15:56:07 -04:00
#[ allow(clippy::too_many_arguments) ]
2024-03-26 11:52:20 -04:00
async fn load (
2024-07-18 18:16:35 -04:00
specified_config : Option < & Path > ,
2024-03-26 11:52:20 -04:00
scope : & ModuleSpecifier ,
settings : & Settings ,
2024-07-18 18:16:35 -04:00
file_fetcher : & Arc < FileFetcher > ,
// sync requirement is because the lsp requires sync
cached_deno_config_fs : & ( dyn DenoConfigFs + Sync ) ,
deno_json_cache : & ( dyn DenoJsonCache + Sync ) ,
pkg_json_cache : & ( dyn PackageJsonCache + Sync ) ,
2024-07-19 15:56:07 -04:00
workspace_cache : & ( dyn WorkspaceCache + Sync ) ,
2024-03-26 11:52:20 -04:00
) -> Self {
2024-07-18 18:16:35 -04:00
let scope = Arc ::new ( scope . clone ( ) ) ;
let discover_result = match scope . to_file_path ( ) {
Ok ( scope_dir_path ) = > {
let paths = [ scope_dir_path ] ;
2024-07-19 15:56:07 -04:00
WorkspaceDirectory ::discover (
2024-07-18 18:16:35 -04:00
match specified_config {
Some ( config_path ) = > {
deno_config ::workspace ::WorkspaceDiscoverStart ::ConfigFile (
config_path ,
)
}
None = > {
deno_config ::workspace ::WorkspaceDiscoverStart ::Paths ( & paths )
}
} ,
& WorkspaceDiscoverOptions {
fs : cached_deno_config_fs ,
additional_config_file_names : & [ ] ,
deno_json_cache : Some ( deno_json_cache ) ,
pkg_json_cache : Some ( pkg_json_cache ) ,
2024-07-19 15:56:07 -04:00
workspace_cache : Some ( workspace_cache ) ,
2024-07-18 18:16:35 -04:00
discover_pkg_json : ! has_flag_env_var ( " DENO_NO_PACKAGE_JSON " ) ,
config_parse_options : Default ::default ( ) ,
maybe_vendor_override : None ,
} ,
)
. map ( Arc ::new )
. map_err ( AnyError ::from )
}
Err ( ( ) ) = > Err ( anyhow! ( " Scope '{}' was not a directory path. " , scope ) ) ,
} ;
match discover_result {
2024-07-19 15:56:07 -04:00
Ok ( member_dir ) = > {
Self ::load_inner ( member_dir , scope , settings , Some ( file_fetcher ) ) . await
2024-07-18 18:16:35 -04:00
}
Err ( err ) = > {
lsp_warn! ( " Couldn't open workspace \" {} \" : {} " , scope . as_str ( ) , err ) ;
2024-07-19 15:56:07 -04:00
let member_dir =
Arc ::new ( WorkspaceDirectory ::empty ( WorkspaceDirectoryEmptyOptions {
root_dir : scope . clone ( ) ,
use_vendor_dir : VendorEnablement ::Disable ,
} ) ) ;
2024-07-18 18:16:35 -04:00
let mut data = Self ::load_inner (
2024-07-19 15:56:07 -04:00
member_dir ,
2024-07-18 18:16:35 -04:00
scope . clone ( ) ,
settings ,
Some ( file_fetcher ) ,
)
. await ;
// check if any of these need to be added to the workspace
let files = [
(
scope . join ( " deno.json " ) . unwrap ( ) ,
ConfigWatchedFileType ::DenoJson ,
) ,
(
scope . join ( " deno.jsonc " ) . unwrap ( ) ,
ConfigWatchedFileType ::DenoJson ,
) ,
(
scope . join ( " package.json " ) . unwrap ( ) ,
ConfigWatchedFileType ::PackageJson ,
) ,
] ;
for ( url , file_type ) in files {
let Some ( file_path ) = url . to_file_path ( ) . ok ( ) else {
continue ;
} ;
if file_path . exists ( ) {
data . watched_files . insert ( url . clone ( ) , file_type ) ;
let canonicalized_specifier =
canonicalize_path_maybe_not_exists ( & file_path )
. ok ( )
. and_then ( | p | ModuleSpecifier ::from_file_path ( p ) . ok ( ) ) ;
if let Some ( specifier ) = canonicalized_specifier {
data . watched_files . insert ( specifier , file_type ) ;
}
2024-03-26 11:52:20 -04:00
}
}
2024-07-18 18:16:35 -04:00
data
2024-03-26 11:52:20 -04:00
}
}
}
async fn load_inner (
2024-07-19 15:56:07 -04:00
member_dir : Arc < WorkspaceDirectory > ,
2024-07-18 18:16:35 -04:00
scope : Arc < ModuleSpecifier > ,
2024-03-26 11:52:20 -04:00
settings : & Settings ,
2024-06-03 17:17:08 -04:00
file_fetcher : Option < & Arc < FileFetcher > > ,
2024-03-26 11:52:20 -04:00
) -> Self {
2024-07-18 18:16:35 -04:00
let ( settings , workspace_folder ) = settings . get_for_specifier ( & scope ) ;
let mut watched_files = HashMap ::with_capacity ( 10 ) ;
let mut add_watched_file =
| specifier : ModuleSpecifier , file_type : ConfigWatchedFileType | {
let maybe_canonicalized = specifier
. to_file_path ( )
. ok ( )
. and_then ( | p | canonicalize_path_maybe_not_exists ( & p ) . ok ( ) )
. and_then ( | p | ModuleSpecifier ::from_file_path ( p ) . ok ( ) ) ;
if let Some ( canonicalized ) = maybe_canonicalized {
if canonicalized ! = specifier {
watched_files . entry ( canonicalized ) . or_insert ( file_type ) ;
}
}
watched_files . entry ( specifier ) . or_insert ( file_type ) ;
} ;
2024-03-26 11:52:20 -04:00
2024-08-09 03:50:37 -04:00
let canonicalized_scope = ( | | {
let path = scope . to_file_path ( ) . ok ( ) ? ;
let path = canonicalize_path_maybe_not_exists ( & path ) . ok ( ) ? ;
let specifier = ModuleSpecifier ::from_directory_path ( path ) . ok ( ) ? ;
if specifier = = * scope {
return None ;
}
Some ( Arc ::new ( specifier ) )
} ) ( ) ;
2024-07-19 15:56:07 -04:00
if let Some ( deno_json ) = member_dir . maybe_deno_json ( ) {
2024-07-18 18:16:35 -04:00
lsp_log! (
" Resolved Deno configuration file: \" {} \" " ,
deno_json . specifier
) ;
add_watched_file (
deno_json . specifier . clone ( ) ,
ConfigWatchedFileType ::DenoJson ,
) ;
2024-04-24 16:14:01 -04:00
}
2024-07-19 15:56:07 -04:00
if let Some ( pkg_json ) = member_dir . maybe_pkg_json ( ) {
2024-07-18 18:16:35 -04:00
lsp_log! ( " Resolved package.json: \" {} \" " , pkg_json . specifier ( ) ) ;
2024-04-30 20:44:20 -04:00
2024-07-18 18:16:35 -04:00
add_watched_file (
pkg_json . specifier ( ) ,
ConfigWatchedFileType ::PackageJson ,
) ;
}
2024-05-29 20:09:16 -04:00
2024-07-18 18:16:35 -04:00
// todo(dsherret): cache this so we don't load this so many times
2024-07-19 15:56:07 -04:00
let npmrc = discover_npmrc_from_workspace ( & member_dir . workspace )
2024-07-18 18:16:35 -04:00
. inspect ( | ( _ , path ) | {
if let Some ( path ) = path {
lsp_log! ( " Resolved .npmrc: \" {} \" " , path . display ( ) ) ;
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( path ) {
add_watched_file ( specifier , ConfigWatchedFileType ::NpmRc ) ;
}
2024-07-08 17:31:27 -04:00
}
} )
2024-07-18 18:16:35 -04:00
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read .npmrc for \" {scope} \" : {err} " ) ;
} )
. map ( | ( r , _ ) | r )
. ok ( ) ;
2024-07-19 15:56:07 -04:00
let default_file_pattern_base =
scope . to_file_path ( ) . unwrap_or_else ( | _ | PathBuf ::from ( " / " ) ) ;
2024-07-18 18:16:35 -04:00
let fmt_config = Arc ::new (
2024-07-19 15:56:07 -04:00
member_dir
. to_fmt_config ( FilePatterns ::new_with_base ( member_dir . dir_path ( ) ) )
2024-07-18 18:16:35 -04:00
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read formatter configuration: {} " , err )
} )
. ok ( )
2024-07-19 15:56:07 -04:00
. unwrap_or_else ( | | {
FmtConfig ::new_with_base ( default_file_pattern_base . clone ( ) )
2024-07-18 18:16:35 -04:00
} ) ,
) ;
let lint_config = Arc ::new (
2024-07-19 15:56:07 -04:00
member_dir
. to_lint_config ( FilePatterns ::new_with_base ( member_dir . dir_path ( ) ) )
2024-07-18 18:16:35 -04:00
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read lint configuration: {} " , err )
} )
. ok ( )
2024-07-19 15:56:07 -04:00
. unwrap_or_else ( | | {
LintConfig ::new_with_base ( default_file_pattern_base . clone ( ) )
2024-07-18 18:16:35 -04:00
} ) ,
) ;
2024-07-25 09:07:59 -04:00
2024-07-18 18:16:35 -04:00
let test_config = Arc ::new (
2024-07-19 15:56:07 -04:00
member_dir
. to_test_config ( FilePatterns ::new_with_base ( member_dir . dir_path ( ) ) )
2024-07-18 18:16:35 -04:00
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read test configuration: {} " , err )
} )
. ok ( )
2024-07-19 15:56:07 -04:00
. unwrap_or_else ( | | {
TestConfig ::new_with_base ( default_file_pattern_base . clone ( ) )
2024-07-18 18:16:35 -04:00
} ) ,
) ;
let exclude_files = Arc ::new (
2024-07-19 15:56:07 -04:00
member_dir
. workspace
2024-07-18 18:16:35 -04:00
. resolve_config_excludes ( )
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read config excludes: {} " , err )
} )
. ok ( )
. unwrap_or_default ( ) ,
) ;
2024-03-26 11:52:20 -04:00
2024-07-19 15:56:07 -04:00
let ts_config = LspTsConfig ::new (
member_dir . workspace . root_deno_json ( ) . map ( | c | c . as_ref ( ) ) ,
) ;
2024-07-18 18:16:35 -04:00
let deno_lint_config =
if ts_config . inner . 0. get ( " jsx " ) . and_then ( | v | v . as_str ( ) ) = = Some ( " react " )
{
let default_jsx_factory =
ts_config . inner . 0. get ( " jsxFactory " ) . and_then ( | v | v . as_str ( ) ) ;
let default_jsx_fragment_factory = ts_config
. inner
. 0
. get ( " jsxFragmentFactory " )
. and_then ( | v | v . as_str ( ) ) ;
2024-07-19 15:56:07 -04:00
DenoLintConfig {
2024-07-18 18:16:35 -04:00
default_jsx_factory : default_jsx_factory . map ( String ::from ) ,
default_jsx_fragment_factory : default_jsx_fragment_factory
. map ( String ::from ) ,
}
} else {
2024-07-19 15:56:07 -04:00
DenoLintConfig {
2024-07-18 18:16:35 -04:00
default_jsx_factory : None ,
default_jsx_fragment_factory : None ,
}
} ;
2024-07-19 15:56:07 -04:00
let vendor_dir = member_dir . workspace . vendor_dir_path ( ) . cloned ( ) ;
2024-07-18 18:16:35 -04:00
// todo(dsherret): add caching so we don't load this so many times
2024-07-19 15:56:07 -04:00
let lockfile = resolve_lockfile_from_workspace ( & member_dir ) . map ( Arc ::new ) ;
2024-03-26 11:52:20 -04:00
if let Some ( lockfile ) = & lockfile {
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( & lockfile . filename )
{
2024-07-18 18:16:35 -04:00
add_watched_file ( specifier , ConfigWatchedFileType ::Lockfile ) ;
2024-03-26 11:52:20 -04:00
}
}
2024-07-18 18:16:35 -04:00
2024-09-04 11:39:30 -04:00
let node_modules_dir =
member_dir . workspace . node_modules_dir ( ) . unwrap_or_default ( ) ;
2024-08-30 17:58:24 -04:00
let byonm = match node_modules_dir {
Some ( mode ) = > mode = = NodeModulesDirMode ::Manual ,
2024-09-03 05:18:37 -04:00
None = > member_dir . workspace . root_pkg_json ( ) . is_some ( ) ,
2024-08-30 17:58:24 -04:00
} ;
2024-07-18 18:16:35 -04:00
if byonm {
lsp_log! ( " Enabled 'bring your own node_modules'. " ) ;
2024-03-26 11:52:20 -04:00
}
2024-07-19 15:56:07 -04:00
let node_modules_dir =
resolve_node_modules_dir ( & member_dir . workspace , byonm ) ;
2024-03-26 11:52:20 -04:00
2024-07-18 18:16:35 -04:00
// Mark the import map as a watched file
2024-07-19 15:56:07 -04:00
if let Some ( import_map_specifier ) = member_dir
. workspace
2024-09-23 14:46:50 -04:00
. to_import_map_path ( )
2024-07-19 15:56:07 -04:00
. ok ( )
. flatten ( )
2024-09-23 14:46:50 -04:00
. and_then ( | path | Url ::from_file_path ( path ) . ok ( ) )
2024-07-18 18:16:35 -04:00
{
add_watched_file (
import_map_specifier . clone ( ) ,
ConfigWatchedFileType ::ImportMap ,
) ;
}
// attempt to create a resolver for the workspace
let pkg_json_dep_resolution = if byonm {
PackageJsonDepResolution ::Disabled
} else {
// todo(dsherret): this should be false for nodeModulesDir: true
PackageJsonDepResolution ::Enabled
} ;
let mut import_map_from_settings = {
2024-07-19 15:56:07 -04:00
let is_config_import_map = member_dir
2024-07-18 18:16:35 -04:00
. maybe_deno_json ( )
. map ( | c | c . is_an_import_map ( ) | | c . json . import_map . is_some ( ) )
. or_else ( | | {
2024-07-19 15:56:07 -04:00
member_dir
. workspace
2024-07-18 18:16:35 -04:00
. root_deno_json ( )
. map ( | c | c . is_an_import_map ( ) | | c . json . import_map . is_some ( ) )
} )
. unwrap_or ( false ) ;
if is_config_import_map {
None
} else {
settings . import_map . as_ref ( ) . and_then ( | import_map_str | {
Url ::parse ( import_map_str )
. ok ( )
. or_else ( | | workspace_folder ? . join ( import_map_str ) . ok ( ) )
} )
2024-03-26 11:52:20 -04:00
}
2024-07-18 18:16:35 -04:00
} ;
let specified_import_map = {
2024-07-19 15:56:07 -04:00
let is_config_import_map = member_dir
2024-07-18 18:16:35 -04:00
. maybe_deno_json ( )
. map ( | c | c . is_an_import_map ( ) | | c . json . import_map . is_some ( ) )
. or_else ( | | {
2024-07-19 15:56:07 -04:00
member_dir
. workspace
2024-07-18 18:16:35 -04:00
. root_deno_json ( )
. map ( | c | c . is_an_import_map ( ) | | c . json . import_map . is_some ( ) )
} )
. unwrap_or ( false ) ;
if is_config_import_map {
import_map_from_settings = None ;
2024-03-26 11:52:20 -04:00
}
2024-07-18 18:16:35 -04:00
if let Some ( import_map_url ) = & import_map_from_settings {
add_watched_file (
import_map_url . clone ( ) ,
ConfigWatchedFileType ::ImportMap ,
) ;
// spawn due to the lsp's `Send` requirement
2024-09-16 16:39:37 -04:00
let fetch_result =
deno_core ::unsync ::spawn ( {
let file_fetcher = file_fetcher . cloned ( ) . unwrap ( ) ;
let import_map_url = import_map_url . clone ( ) ;
async move {
file_fetcher . fetch_bypass_permissions ( & import_map_url ) . await
}
} )
. await
. unwrap ( ) ;
2024-07-18 18:16:35 -04:00
let value_result = fetch_result . and_then ( | f | {
serde_json ::from_slice ::< Value > ( & f . source ) . map_err ( | e | e . into ( ) )
} ) ;
match value_result {
Ok ( value ) = > Some ( SpecifiedImportMap {
base_url : import_map_url . clone ( ) ,
value ,
} ) ,
2024-03-26 11:52:20 -04:00
Err ( err ) = > {
lsp_warn! (
2024-07-18 18:16:35 -04:00
" Couldn't read import map \" {} \" : {} " ,
import_map_url . as_str ( ) ,
2024-03-26 11:52:20 -04:00
err
) ;
2024-07-18 18:16:35 -04:00
import_map_from_settings = None ;
None
2024-03-26 11:52:20 -04:00
}
}
2024-07-18 18:16:35 -04:00
} else {
None
2024-03-26 11:52:20 -04:00
}
2024-07-08 17:31:27 -04:00
} ;
2024-09-23 14:46:50 -04:00
let resolver = member_dir
. workspace
. create_resolver (
CreateResolverOptions {
pkg_json_dep_resolution ,
specified_import_map ,
} ,
| path | Ok ( std ::fs ::read_to_string ( path ) ? ) ,
2024-07-18 18:16:35 -04:00
)
2024-09-23 14:46:50 -04:00
. inspect_err ( | err | {
lsp_warn! (
" Failed to load resolver: {} " ,
err // will contain the specifier
) ;
} )
. ok ( )
. unwrap_or_else ( | | {
// create a dummy resolver
WorkspaceResolver ::new_raw (
scope . clone ( ) ,
None ,
member_dir . workspace . resolver_jsr_pkgs ( ) . collect ( ) ,
member_dir . workspace . package_jsons ( ) . cloned ( ) . collect ( ) ,
pkg_json_dep_resolution ,
)
} ) ;
2024-07-18 18:16:35 -04:00
if ! resolver . diagnostics ( ) . is_empty ( ) {
lsp_warn! (
" Import map diagnostics: \n {} " ,
resolver
. diagnostics ( )
. iter ( )
. map ( | d | format! ( " - {d} " ) )
. collect ::< Vec < _ > > ( )
. join ( " \n " )
) ;
}
2024-10-31 06:52:43 -04:00
let unstable = member_dir
. workspace
. unstable_features ( )
. iter ( )
. chain ( settings . unstable . as_deref ( ) )
. cloned ( )
. collect ::< BTreeSet < _ > > ( ) ;
2024-07-25 09:07:59 -04:00
let unstable_sloppy_imports = std ::env ::var ( " DENO_UNSTABLE_SLOPPY_IMPORTS " )
. is_ok ( )
2024-10-31 06:52:43 -04:00
| | unstable . contains ( " sloppy-imports " ) ;
2024-07-25 09:07:59 -04:00
let sloppy_imports_resolver = unstable_sloppy_imports . then ( | | {
2024-09-28 19:17:48 -04:00
Arc ::new ( CliSloppyImportsResolver ::new (
SloppyImportsCachedFs ::new_without_stat_cache ( Arc ::new (
deno_runtime ::deno_fs ::RealFs ,
) ) ,
) )
2024-07-25 09:07:59 -04:00
} ) ;
let resolver = Arc ::new ( resolver ) ;
let lint_rule_provider = LintRuleProvider ::new (
sloppy_imports_resolver . clone ( ) ,
Some ( resolver . clone ( ) ) ,
) ;
let linter = Arc ::new ( CliLinter ::new ( CliLinterOptions {
configured_rules : lint_rule_provider . resolve_lint_rules (
LintOptions ::resolve ( ( * lint_config ) . clone ( ) , & LintFlags ::default ( ) )
. rules ,
member_dir . maybe_deno_json ( ) . map ( | c | c . as_ref ( ) ) ,
) ,
fix : false ,
deno_lint_config ,
} ) ) ;
2024-06-10 12:03:17 -04:00
2024-03-26 11:52:20 -04:00
ConfigData {
2024-07-18 18:16:35 -04:00
scope ,
2024-08-09 03:50:37 -04:00
canonicalized_scope ,
2024-07-19 15:56:07 -04:00
member_dir ,
2024-07-25 09:07:59 -04:00
resolver ,
sloppy_imports_resolver ,
2024-07-18 18:16:35 -04:00
fmt_config ,
2024-05-29 20:09:16 -04:00
lint_config ,
2024-07-18 18:16:35 -04:00
test_config ,
2024-07-25 09:07:59 -04:00
linter ,
2024-07-18 18:16:35 -04:00
exclude_files ,
2024-03-26 11:52:20 -04:00
ts_config : Arc ::new ( ts_config ) ,
2024-04-05 11:18:48 -04:00
byonm ,
2024-03-26 11:52:20 -04:00
node_modules_dir ,
vendor_dir ,
2024-07-08 17:31:27 -04:00
lockfile ,
2024-05-29 23:16:15 -04:00
npmrc ,
2024-03-31 16:39:23 -04:00
import_map_from_settings ,
2024-10-31 06:52:43 -04:00
unstable ,
2024-03-26 11:52:20 -04:00
watched_files ,
}
}
2024-07-18 18:16:35 -04:00
2024-07-19 15:56:07 -04:00
pub fn maybe_deno_json (
& self ,
) -> Option < & Arc < deno_config ::deno_json ::ConfigFile > > {
self . member_dir . maybe_deno_json ( )
2024-07-18 18:16:35 -04:00
}
2024-07-23 17:34:46 -04:00
pub fn maybe_pkg_json ( & self ) -> Option < & Arc < deno_package_json ::PackageJson > > {
2024-07-19 15:56:07 -04:00
self . member_dir . maybe_pkg_json ( )
2024-07-18 18:16:35 -04:00
}
2024-08-09 03:50:37 -04:00
pub fn scope_contains_specifier ( & self , specifier : & ModuleSpecifier ) -> bool {
specifier . as_str ( ) . starts_with ( self . scope . as_str ( ) )
| | self
. canonicalized_scope
. as_ref ( )
. map ( | s | specifier . as_str ( ) . starts_with ( s . as_str ( ) ) )
. unwrap_or ( false )
}
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
#[ derive(Clone, Debug, Default) ]
2024-03-26 11:52:20 -04:00
pub struct ConfigTree {
2024-06-17 16:54:23 -04:00
scopes : Arc < BTreeMap < ModuleSpecifier , Arc < ConfigData > > > ,
2024-03-26 11:52:20 -04:00
}
impl ConfigTree {
pub fn scope_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
) -> Option < & ModuleSpecifier > {
self
. scopes
2024-08-09 03:50:37 -04:00
. iter ( )
. rfind ( | ( _ , d ) | d . scope_contains_specifier ( specifier ) )
. map ( | ( s , _ ) | s )
2024-03-26 11:52:20 -04:00
}
pub fn data_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
2024-06-17 16:54:23 -04:00
) -> Option < & Arc < ConfigData > > {
2024-04-02 18:02:50 -04:00
self
. scope_for_specifier ( specifier )
. and_then ( | s | self . scopes . get ( s ) )
2024-03-26 11:52:20 -04:00
}
2024-06-17 16:54:23 -04:00
pub fn data_by_scope (
& self ,
) -> & Arc < BTreeMap < ModuleSpecifier , Arc < ConfigData > > > {
2024-04-02 18:02:50 -04:00
& self . scopes
2024-03-26 11:52:20 -04:00
}
2024-07-19 15:56:07 -04:00
pub fn workspace_dir_for_specifier (
2024-03-26 11:52:20 -04:00
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
2024-07-19 15:56:07 -04:00
) -> Option < & WorkspaceDirectory > {
self
. data_for_specifier ( specifier )
. map ( | d | d . member_dir . as_ref ( ) )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn config_files ( & self ) -> Vec < & Arc < ConfigFile > > {
self
. scopes
. iter ( )
2024-07-18 18:16:35 -04:00
. filter_map ( | ( _ , d ) | d . maybe_deno_json ( ) )
2024-04-02 18:02:50 -04:00
. collect ( )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn package_jsons ( & self ) -> Vec < & Arc < PackageJson > > {
2024-03-26 11:52:20 -04:00
self
2024-04-02 18:02:50 -04:00
. scopes
. iter ( )
2024-07-18 18:16:35 -04:00
. filter_map ( | ( _ , d ) | d . maybe_pkg_json ( ) )
2024-03-26 11:52:20 -04:00
. collect ( )
}
2024-07-18 18:16:35 -04:00
pub fn fmt_config_for_specifier (
2024-03-26 11:52:20 -04:00
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
2024-07-18 18:16:35 -04:00
) -> Arc < FmtConfig > {
2024-03-26 11:52:20 -04:00
self
2024-04-02 18:02:50 -04:00
. data_for_specifier ( specifier )
2024-07-18 18:16:35 -04:00
. map ( | d | d . fmt_config . clone ( ) )
2024-07-19 15:56:07 -04:00
. unwrap_or_else ( | | Arc ::new ( FmtConfig ::new_with_base ( PathBuf ::from ( " / " ) ) ) )
2024-03-26 11:52:20 -04:00
}
2024-10-16 17:43:26 -04:00
/// Returns (scope_url, type).
2024-04-02 18:02:50 -04:00
pub fn watched_file_type (
2024-03-26 11:52:20 -04:00
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
) -> Option < ( & ModuleSpecifier , ConfigWatchedFileType ) > {
2024-10-16 17:43:26 -04:00
for ( scope_url , data ) in self . scopes . iter ( ) {
2024-04-02 18:02:50 -04:00
if let Some ( typ ) = data . watched_files . get ( specifier ) {
2024-10-16 17:43:26 -04:00
return Some ( ( scope_url , * typ ) ) ;
2024-04-02 18:02:50 -04:00
}
}
None
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn is_watched_file ( & self , specifier : & ModuleSpecifier ) -> bool {
2024-09-04 08:22:30 -04:00
let path = specifier . path ( ) ;
if path . ends_with ( " /deno.json " )
| | path . ends_with ( " /deno.jsonc " )
| | path . ends_with ( " /package.json " )
| | path . ends_with ( " /node_modules/.package-lock.json " )
| | path . ends_with ( " /node_modules/.yarn-integrity.json " )
| | path . ends_with ( " /node_modules/.modules.yaml " )
| | path . ends_with ( " /node_modules/.deno/.setup-cache.bin " )
2024-04-02 18:02:50 -04:00
{
return true ;
}
2024-03-26 11:52:20 -04:00
self
2024-04-02 18:02:50 -04:00
. scopes
. values ( )
. any ( | data | data . watched_files . contains_key ( specifier ) )
2024-03-26 11:52:20 -04:00
}
2024-10-16 17:43:26 -04:00
pub fn to_did_refresh_params (
& self ,
) -> lsp_custom ::DidRefreshDenoConfigurationTreeNotificationParams {
let data = self
. scopes
. values ( )
. filter_map ( | data | {
let workspace_root_scope_uri =
Some ( data . member_dir . workspace . root_dir ( ) )
. filter ( | s | * s ! = data . member_dir . dir_url ( ) )
. and_then ( | s | url_to_uri ( s ) . ok ( ) ) ;
Some ( lsp_custom ::DenoConfigurationData {
scope_uri : url_to_uri ( & data . scope ) . ok ( ) ? ,
deno_json : data . maybe_deno_json ( ) . and_then ( | c | {
if workspace_root_scope_uri . is_some ( )
& & Some ( & c . specifier )
= = data
. member_dir
. workspace
. root_deno_json ( )
. map ( | c | & c . specifier )
{
return None ;
}
Some ( lsp ::TextDocumentIdentifier {
uri : url_to_uri ( & c . specifier ) . ok ( ) ? ,
} )
} ) ,
package_json : data . maybe_pkg_json ( ) . and_then ( | p | {
Some ( lsp ::TextDocumentIdentifier {
uri : url_to_uri ( & p . specifier ( ) ) . ok ( ) ? ,
} )
} ) ,
workspace_root_scope_uri ,
} )
} )
. collect ( ) ;
lsp_custom ::DidRefreshDenoConfigurationTreeNotificationParams { data }
}
2024-03-26 11:52:20 -04:00
pub async fn refresh (
2024-04-02 18:02:50 -04:00
& mut self ,
2024-03-26 11:52:20 -04:00
settings : & Settings ,
2024-06-11 16:06:43 -04:00
workspace_files : & IndexSet < ModuleSpecifier > ,
2024-06-03 17:17:08 -04:00
file_fetcher : & Arc < FileFetcher > ,
2024-03-26 11:52:20 -04:00
) {
lsp_log! ( " Refreshing configuration tree... " ) ;
2024-07-18 18:16:35 -04:00
// since we're resolving a workspace multiple times in different
// folders, we want to cache all the lookups and config files across
// ConfigData::load calls
let cached_fs = CachedDenoConfigFs ::default ( ) ;
let deno_json_cache = DenoJsonMemCache ::default ( ) ;
let pkg_json_cache = PackageJsonMemCache ::default ( ) ;
2024-07-19 15:56:07 -04:00
let workspace_cache = WorkspaceMemCache ::default ( ) ;
2024-04-02 18:02:50 -04:00
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 {
2024-08-05 20:10:02 -04:00
let config_file_path = ( | | {
let config_setting = ws_settings . config . as_ref ( ) ? ;
let config_uri = folder_uri . join ( config_setting ) . ok ( ) ? ;
2024-09-28 07:55:01 -04:00
url_to_file_path ( & config_uri ) . ok ( )
2024-08-05 20:10:02 -04:00
} ) ( ) ;
if config_file_path . is_some ( ) | | ws_settings . import_map . is_some ( ) {
scopes . insert (
folder_uri . clone ( ) ,
Arc ::new (
ConfigData ::load (
config_file_path . as_deref ( ) ,
folder_uri ,
settings ,
file_fetcher ,
& cached_fs ,
& deno_json_cache ,
& pkg_json_cache ,
& workspace_cache ,
)
. await ,
) ,
) ;
2024-04-02 18:02:50 -04:00
}
}
}
for specifier in workspace_files {
2024-06-10 12:03:17 -04:00
if ! ( specifier . path ( ) . ends_with ( " /deno.json " )
2024-07-18 18:16:35 -04:00
| | specifier . path ( ) . ends_with ( " /deno.jsonc " )
| | specifier . path ( ) . ends_with ( " /package.json " ) )
2024-04-02 18:02:50 -04:00
{
2024-06-10 12:03:17 -04:00
continue ;
}
let Ok ( scope ) = specifier . join ( " . " ) else {
continue ;
} ;
if scopes . contains_key ( & scope ) {
continue ;
}
2024-07-18 18:16:35 -04:00
let data = Arc ::new (
ConfigData ::load (
None ,
& scope ,
settings ,
file_fetcher ,
& cached_fs ,
& deno_json_cache ,
& pkg_json_cache ,
2024-07-19 15:56:07 -04:00
& workspace_cache ,
2024-07-18 18:16:35 -04:00
)
. await ,
) ;
scopes . insert ( scope , data . clone ( ) ) ;
2024-07-19 15:56:07 -04:00
for ( member_scope , _ ) in data . member_dir . workspace . config_folders ( ) {
2024-07-18 18:16:35 -04:00
if scopes . contains_key ( member_scope ) {
continue ;
2024-04-02 18:02:50 -04:00
}
2024-07-18 18:16:35 -04:00
let member_data = ConfigData ::load (
None ,
member_scope ,
settings ,
file_fetcher ,
& cached_fs ,
& deno_json_cache ,
& pkg_json_cache ,
2024-07-19 15:56:07 -04:00
& workspace_cache ,
2024-07-18 18:16:35 -04:00
)
. await ;
scopes . insert ( member_scope . as_ref ( ) . clone ( ) , Arc ::new ( member_data ) ) ;
2024-03-26 11:52:20 -04:00
}
}
2024-04-02 18:02:50 -04:00
self . scopes = Arc ::new ( scopes ) ;
2024-03-26 11:52:20 -04:00
}
#[ cfg(test) ]
2024-04-02 18:02:50 -04:00
pub async fn inject_config_file ( & mut self , config_file : ConfigFile ) {
2024-03-26 11:52:20 -04:00
let scope = config_file . specifier . join ( " . " ) . unwrap ( ) ;
2024-07-18 18:16:35 -04:00
let json_text = serde_json ::to_string ( & config_file . json ) . unwrap ( ) ;
let test_fs = deno_runtime ::deno_fs ::InMemoryFs ::default ( ) ;
2024-09-28 07:55:01 -04:00
let config_path = url_to_file_path ( & config_file . specifier ) . unwrap ( ) ;
2024-07-18 18:16:35 -04:00
test_fs . setup_text_files ( vec! [ (
config_path . to_string_lossy ( ) . to_string ( ) ,
json_text ,
) ] ) ;
2024-07-19 15:56:07 -04:00
let workspace_dir = Arc ::new (
WorkspaceDirectory ::discover (
2024-07-18 18:16:35 -04:00
deno_config ::workspace ::WorkspaceDiscoverStart ::ConfigFile (
& config_path ,
) ,
& deno_config ::workspace ::WorkspaceDiscoverOptions {
2024-07-23 17:34:46 -04:00
fs : & crate ::args ::deno_json ::DenoConfigFsAdapter ( & test_fs ) ,
2024-07-18 18:16:35 -04:00
.. Default ::default ( )
} ,
)
. unwrap ( ) ,
) ;
2024-06-17 16:54:23 -04:00
let data = Arc ::new (
ConfigData ::load_inner (
2024-07-19 15:56:07 -04:00
workspace_dir ,
2024-07-18 18:16:35 -04:00
Arc ::new ( scope . clone ( ) ) ,
2024-06-17 16:54:23 -04:00
& Default ::default ( ) ,
None ,
)
. await ,
) ;
2024-07-18 18:16:35 -04:00
assert! ( data . maybe_deno_json ( ) . is_some ( ) ) ;
2024-04-02 18:02:50 -04:00
self . scopes = Arc ::new ( [ ( scope , data ) ] . into_iter ( ) . collect ( ) ) ;
2024-03-26 11:52:20 -04:00
}
}
2024-07-18 18:16:35 -04:00
fn resolve_lockfile_from_workspace (
2024-07-19 15:56:07 -04:00
workspace : & WorkspaceDirectory ,
2024-06-28 20:18:21 -04:00
) -> Option < CliLockfile > {
2024-07-19 15:56:07 -04:00
let lockfile_path = match workspace . workspace . resolve_lockfile_path ( ) {
2023-07-10 17:45:09 -04:00
Ok ( Some ( value ) ) = > value ,
Ok ( None ) = > return None ,
Err ( err ) = > {
lsp_warn! ( " Error resolving lockfile: {:#} " , err ) ;
return None ;
}
} ;
2024-08-20 10:55:47 -04:00
let frozen = workspace
. workspace
. root_deno_json ( )
. and_then ( | c | c . to_lock_config ( ) . ok ( ) . flatten ( ) . map ( | c | c . frozen ( ) ) )
. unwrap_or ( false ) ;
resolve_lockfile_from_path ( lockfile_path , frozen )
2023-07-10 17:45:09 -04:00
}
2024-04-05 11:18:48 -04:00
fn resolve_node_modules_dir (
2024-07-18 18:16:35 -04:00
workspace : & Workspace ,
2024-04-05 11:18:48 -04:00
byonm : bool ,
) -> Option < PathBuf > {
2023-07-10 17:45:09 -04:00
// For the language server, require an explicit opt-in via the
// `nodeModulesDir: true` setting in the deno.json file. This is to
// reduce the chance of modifying someone's node_modules directory
// without them having asked us to do so.
2024-09-04 11:39:30 -04:00
let node_modules_mode = workspace . node_modules_dir ( ) . ok ( ) . flatten ( ) ;
2024-08-30 17:58:24 -04:00
let explicitly_disabled = node_modules_mode = = Some ( NodeModulesDirMode ::None ) ;
2023-08-06 21:56:56 -04:00
if explicitly_disabled {
return None ;
}
2024-08-30 17:58:24 -04:00
let enabled = byonm
| | node_modules_mode
. map ( | m | m . uses_node_modules_dir ( ) )
. unwrap_or ( false )
| | workspace . vendor_dir_path ( ) . is_some ( ) ;
2024-08-30 13:58:58 -04:00
2023-08-06 21:56:56 -04:00
if ! enabled {
2023-07-10 17:45:09 -04:00
return None ;
}
2024-07-18 18:16:35 -04:00
let node_modules_dir = workspace
2024-07-19 15:56:07 -04:00
. root_dir ( )
2024-07-18 18:16:35 -04:00
. to_file_path ( )
. ok ( ) ?
. join ( " node_modules " ) ;
2023-07-10 17:45:09 -04:00
canonicalize_path_maybe_not_exists ( & node_modules_dir ) . ok ( )
}
2024-08-20 10:55:47 -04:00
fn resolve_lockfile_from_path (
lockfile_path : PathBuf ,
frozen : bool ,
) -> Option < CliLockfile > {
2024-10-02 16:17:39 -04:00
match CliLockfile ::read_from_path ( CliLockfileReadFromPathOptions {
file_path : lockfile_path ,
frozen ,
skip_write : false ,
} ) {
2023-07-20 14:05:52 -04:00
Ok ( value ) = > {
2024-06-10 12:03:17 -04:00
if value . filename . exists ( ) {
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( & value . filename )
{
lsp_log! ( " Resolved lockfile: \" {} \" " , specifier ) ;
}
2023-07-20 14:05:52 -04:00
}
2023-07-21 09:12:26 -04:00
Some ( value )
2023-07-20 14:05:52 -04:00
}
2023-07-10 17:45:09 -04:00
Err ( err ) = > {
lsp_warn! ( " Error loading lockfile: {:#} " , err ) ;
None
}
}
}
2024-07-18 18:16:35 -04:00
// todo(dsherret): switch to RefCell once the lsp no longer requires Sync
#[ derive(Default) ]
struct DenoJsonMemCache ( Mutex < HashMap < PathBuf , Arc < ConfigFile > > > ) ;
2024-07-19 15:56:07 -04:00
impl deno_config ::deno_json ::DenoJsonCache for DenoJsonMemCache {
2024-07-18 18:16:35 -04:00
fn get ( & self , path : & Path ) -> Option < Arc < ConfigFile > > {
self . 0. lock ( ) . get ( path ) . cloned ( )
}
fn set ( & self , path : PathBuf , data : Arc < ConfigFile > ) {
self . 0. lock ( ) . insert ( path , data ) ;
}
}
#[ derive(Default) ]
struct PackageJsonMemCache ( Mutex < HashMap < PathBuf , Arc < PackageJson > > > ) ;
2024-07-23 17:34:46 -04:00
impl deno_package_json ::PackageJsonCache for PackageJsonMemCache {
2024-07-18 18:16:35 -04:00
fn get ( & self , path : & Path ) -> Option < Arc < PackageJson > > {
self . 0. lock ( ) . get ( path ) . cloned ( )
}
fn set ( & self , path : PathBuf , data : Arc < PackageJson > ) {
self . 0. lock ( ) . insert ( path , data ) ;
}
}
2024-07-19 15:56:07 -04:00
#[ derive(Default) ]
struct WorkspaceMemCache ( Mutex < HashMap < PathBuf , Arc < Workspace > > > ) ;
impl deno_config ::workspace ::WorkspaceCache for WorkspaceMemCache {
fn get ( & self , dir_path : & Path ) -> Option < Arc < Workspace > > {
self . 0. lock ( ) . get ( dir_path ) . cloned ( )
}
fn set ( & self , dir_path : PathBuf , workspace : Arc < Workspace > ) {
self . 0. lock ( ) . insert ( dir_path , workspace ) ;
}
}
2024-07-18 18:16:35 -04:00
#[ derive(Default) ]
struct CachedFsItems < T : Clone > {
items : HashMap < PathBuf , Result < T , std ::io ::Error > > ,
}
impl < T : Clone > CachedFsItems < T > {
pub fn get (
& mut self ,
path : & Path ,
action : impl FnOnce ( & Path ) -> Result < T , std ::io ::Error > ,
) -> Result < T , std ::io ::Error > {
let value = if let Some ( value ) = self . items . get ( path ) {
value
} else {
let value = action ( path ) ;
// just in case this gets really large for some reason
if self . items . len ( ) = = 16_384 {
return value ;
}
self . items . insert ( path . to_owned ( ) , value ) ;
self . items . get ( path ) . unwrap ( )
} ;
value
. as_ref ( )
. map ( | v | ( * v ) . clone ( ) )
. map_err ( | e | std ::io ::Error ::new ( e . kind ( ) , e . to_string ( ) ) )
}
}
#[ derive(Default) ]
struct InnerData {
stat_calls : CachedFsItems < deno_config ::fs ::FsMetadata > ,
read_to_string_calls : CachedFsItems < String > ,
}
#[ derive(Default) ]
struct CachedDenoConfigFs ( Mutex < InnerData > ) ;
impl DenoConfigFs for CachedDenoConfigFs {
fn stat_sync (
& self ,
path : & Path ,
) -> Result < deno_config ::fs ::FsMetadata , std ::io ::Error > {
self
. 0
. lock ( )
. stat_calls
. get ( path , | path | RealDenoConfigFs . stat_sync ( path ) )
}
fn read_to_string_lossy (
& self ,
path : & Path ,
) -> Result < String , std ::io ::Error > {
self
. 0
. lock ( )
. read_to_string_calls
. get ( path , | path | RealDenoConfigFs . read_to_string_lossy ( path ) )
}
fn read_dir (
& self ,
path : & Path ,
) -> Result < Vec < deno_config ::fs ::FsDirEntry > , std ::io ::Error > {
2024-07-19 15:56:07 -04:00
// no need to cache these because the workspace cache will ensure
// we only do read_dir calls once (read_dirs are only used for
// npm workspace resolution)
RealDenoConfigFs . read_dir ( path )
2024-07-18 18:16:35 -04:00
}
}
2021-05-09 21:16:04 -04:00
#[ cfg(test) ]
mod tests {
2024-07-19 15:56:07 -04:00
use deno_config ::deno_json ::ConfigParseOptions ;
2021-05-09 21:16:04 -04:00
use deno_core ::resolve_url ;
2023-09-21 01:46:39 -04:00
use deno_core ::serde_json ;
2021-05-09 21:16:04 -04:00
use deno_core ::serde_json ::json ;
2023-04-01 15:10:30 -04:00
use pretty_assertions ::assert_eq ;
2021-05-09 21:16:04 -04:00
2024-07-19 15:56:07 -04:00
use super ::* ;
2021-05-09 21:16:04 -04:00
#[ test ]
fn test_config_specifier_enabled ( ) {
2023-09-09 10:04:21 -04:00
let root_uri = resolve_url ( " file:/// " ) . unwrap ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri ] ) ;
2021-05-09 21:16:04 -04:00
let specifier = resolve_url ( " file:///a.ts " ) . unwrap ( ) ;
assert! ( ! config . specifier_enabled ( & specifier ) ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( {
2021-05-09 21:16:04 -04:00
" enable " : true
} ) )
2023-09-21 01:46:39 -04:00
. unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2021-05-09 21:16:04 -04:00
assert! ( config . specifier_enabled ( & specifier ) ) ;
}
2021-06-01 07:53:08 -04:00
2022-03-20 21:33:37 -04:00
#[ test ]
fn test_config_snapshot_specifier_enabled ( ) {
2023-09-09 10:04:21 -04:00
let root_uri = resolve_url ( " file:/// " ) . unwrap ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri ] ) ;
2022-03-20 21:33:37 -04:00
let specifier = resolve_url ( " file:///a.ts " ) . unwrap ( ) ;
assert! ( ! config . specifier_enabled ( & specifier ) ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( {
2022-03-20 21:33:37 -04:00
" enable " : true
} ) )
2023-09-21 01:46:39 -04:00
. unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2024-04-27 16:35:41 -04:00
assert! ( config . specifier_enabled ( & specifier ) ) ;
2022-03-20 21:33:37 -04:00
}
#[ test ]
fn test_config_specifier_enabled_path ( ) {
2023-09-09 10:04:21 -04:00
let root_uri = resolve_url ( " file:///project/ " ) . unwrap ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri ] ) ;
2022-03-20 21:33:37 -04:00
let specifier_a = resolve_url ( " file:///project/worker/a.ts " ) . unwrap ( ) ;
let specifier_b = resolve_url ( " file:///project/other/b.ts " ) . unwrap ( ) ;
assert! ( ! config . specifier_enabled ( & specifier_a ) ) ;
assert! ( ! config . specifier_enabled ( & specifier_b ) ) ;
2023-09-09 10:04:21 -04:00
let workspace_settings =
serde_json ::from_str ( r # "{ "enablePaths": ["worker"] }"# ) . unwrap ( ) ;
2024-03-21 00:29:52 -04:00
config . set_workspace_settings ( workspace_settings , vec! [ ] ) ;
2022-03-20 21:33:37 -04:00
assert! ( config . specifier_enabled ( & specifier_a ) ) ;
assert! ( ! config . specifier_enabled ( & specifier_b ) ) ;
}
2023-09-13 12:30:27 -04:00
#[ test ]
fn test_config_specifier_disabled_path ( ) {
let root_uri = resolve_url ( " file:///root/ " ) . unwrap ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri . clone ( ) ] ) ;
2024-06-13 15:57:14 -04:00
config . set_workspace_settings (
WorkspaceSettings {
enable : Some ( true ) ,
enable_paths : Some ( vec! [ " mod1.ts " . to_string ( ) , " mod2.ts " . to_string ( ) ] ) ,
disable_paths : vec ! [ " mod2.ts " . to_string ( ) ] ,
.. Default ::default ( )
} ,
vec! [ ] ,
) ;
2023-09-13 12:30:27 -04:00
assert! ( config . specifier_enabled ( & root_uri . join ( " mod1.ts " ) . unwrap ( ) ) ) ;
assert! ( ! config . specifier_enabled ( & root_uri . join ( " mod2.ts " ) . unwrap ( ) ) ) ;
assert! ( ! config . specifier_enabled ( & root_uri . join ( " mod3.ts " ) . unwrap ( ) ) ) ;
}
2021-06-01 07:53:08 -04:00
#[ test ]
fn test_set_workspace_settings_defaults ( ) {
2024-03-26 11:52:20 -04:00
let mut config = Config ::default ( ) ;
2024-03-21 00:29:52 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( { } ) ) . unwrap ( ) ,
vec! [ ] ,
) ;
2021-06-01 07:53:08 -04:00
assert_eq! (
2023-05-11 17:17:14 -04:00
config . workspace_settings ( ) . clone ( ) ,
2021-06-01 07:53:08 -04:00
WorkspaceSettings {
2023-09-01 16:13:13 -04:00
enable : None ,
2023-09-13 12:30:27 -04:00
disable_paths : vec ! [ ] ,
2023-09-09 10:04:21 -04:00
enable_paths : None ,
2021-07-27 17:25:09 -04:00
cache : None ,
2023-09-24 12:59:42 -04:00
cache_on_save : false ,
2022-01-23 19:27:52 -05:00
certificate_stores : None ,
2021-06-01 07:53:08 -04:00
config : None ,
import_map : None ,
code_lens : CodeLensSettings {
implementations : false ,
references : false ,
references_all_functions : false ,
2021-06-07 07:38:07 -04:00
test : true ,
2021-06-01 07:53:08 -04:00
} ,
internal_debug : false ,
2023-12-21 20:04:02 -05:00
internal_inspect : InspectSetting ::Bool ( false ) ,
2023-12-08 12:04:56 -05:00
log_file : false ,
2022-05-16 07:10:08 -04:00
lint : true ,
2023-05-11 17:17:14 -04:00
document_preload_limit : 1_000 ,
2023-09-21 01:46:39 -04:00
suggest : DenoCompletionSettings {
2021-06-01 07:53:08 -04:00
imports : ImportCompletionSettings {
auto_discover : true ,
hosts : HashMap ::new ( ) ,
}
} ,
2022-03-29 18:59:27 -04:00
testing : TestingSettings {
args : vec ! [ " --allow-all " . to_string ( ) , " --no-check " . to_string ( ) ] ,
} ,
2022-01-23 19:27:52 -05:00
tls_certificate : None ,
unsafely_ignore_certificate_errors : None ,
2024-09-10 19:20:03 -04:00
unstable : Default ::default ( ) ,
2023-09-21 01:46:39 -04:00
javascript : LanguageWorkspaceSettings {
inlay_hints : InlayHintsSettings {
parameter_names : InlayHintsParamNamesOptions {
enabled : InlayHintsParamNamesEnabled ::None ,
suppress_when_argument_matches_name : true
} ,
parameter_types : InlayHintsParamTypesOptions { enabled : false } ,
variable_types : InlayHintsVarTypesOptions {
enabled : false ,
suppress_when_type_matches_name : true
} ,
property_declaration_types : InlayHintsPropDeclTypesOptions {
enabled : false
} ,
function_like_return_types : InlayHintsFuncLikeReturnTypesOptions {
enabled : false
} ,
enum_member_values : InlayHintsEnumMemberValuesOptions {
enabled : false
} ,
} ,
2023-09-25 22:54:07 -04:00
preferences : LanguagePreferences {
import_module_specifier : ImportModuleSpecifier ::Shortest ,
jsx_attribute_completion_style : JsxAttributeCompletionStyle ::Auto ,
auto_import_file_exclude_patterns : vec ! [ ] ,
use_aliases_for_renames : true ,
2023-10-16 21:51:42 -04:00
quote_style : QuoteStyle ::Auto ,
2024-10-25 13:35:09 -04:00
prefer_type_only_auto_imports : false ,
2023-09-25 22:54:07 -04:00
} ,
2023-09-21 01:46:39 -04:00
suggest : CompletionSettings {
complete_function_calls : false ,
2023-09-25 22:54:07 -04:00
include_automatic_optional_chain_completions : true ,
include_completions_for_import_statements : true ,
2023-09-21 01:46:39 -04:00
names : true ,
paths : true ,
auto_imports : true ,
2023-09-25 22:54:07 -04:00
enabled : true ,
class_member_snippets : ClassMemberSnippets { enabled : true } ,
object_literal_method_snippets : ObjectLiteralMethodSnippets {
enabled : true ,
} ,
2023-09-21 01:46:39 -04:00
} ,
2023-09-25 22:54:07 -04:00
update_imports_on_file_move : UpdateImportsOnFileMoveOptions {
enabled : UpdateImportsOnFileMoveEnabled ::Prompt
}
2023-09-21 01:46:39 -04:00
} ,
typescript : LanguageWorkspaceSettings {
inlay_hints : InlayHintsSettings {
parameter_names : InlayHintsParamNamesOptions {
enabled : InlayHintsParamNamesEnabled ::None ,
suppress_when_argument_matches_name : true
} ,
parameter_types : InlayHintsParamTypesOptions { enabled : false } ,
variable_types : InlayHintsVarTypesOptions {
enabled : false ,
suppress_when_type_matches_name : true
} ,
property_declaration_types : InlayHintsPropDeclTypesOptions {
enabled : false
} ,
function_like_return_types : InlayHintsFuncLikeReturnTypesOptions {
enabled : false
} ,
enum_member_values : InlayHintsEnumMemberValuesOptions {
enabled : false
} ,
} ,
2023-09-25 22:54:07 -04:00
preferences : LanguagePreferences {
import_module_specifier : ImportModuleSpecifier ::Shortest ,
jsx_attribute_completion_style : JsxAttributeCompletionStyle ::Auto ,
auto_import_file_exclude_patterns : vec ! [ ] ,
use_aliases_for_renames : true ,
2023-10-16 21:51:42 -04:00
quote_style : QuoteStyle ::Auto ,
2024-10-25 13:35:09 -04:00
prefer_type_only_auto_imports : false ,
2023-09-25 22:54:07 -04:00
} ,
2023-09-21 01:46:39 -04:00
suggest : CompletionSettings {
complete_function_calls : false ,
2023-09-25 22:54:07 -04:00
include_automatic_optional_chain_completions : true ,
include_completions_for_import_statements : true ,
2023-09-21 01:46:39 -04:00
names : true ,
paths : true ,
auto_imports : true ,
2023-09-25 22:54:07 -04:00
enabled : true ,
class_member_snippets : ClassMemberSnippets { enabled : true } ,
object_literal_method_snippets : ObjectLiteralMethodSnippets {
enabled : true ,
} ,
2023-09-21 01:46:39 -04:00
} ,
2023-09-25 22:54:07 -04:00
update_imports_on_file_move : UpdateImportsOnFileMoveOptions {
enabled : UpdateImportsOnFileMoveEnabled ::Prompt
}
2023-09-21 01:46:39 -04:00
} ,
2021-06-01 07:53:08 -04:00
}
) ;
}
2023-01-03 10:59:48 -05:00
#[ test ]
fn test_empty_cache ( ) {
2024-03-26 11:52:20 -04:00
let mut config = Config ::default ( ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( { " cache " : " " } ) ) . unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2023-01-03 10:59:48 -05:00
assert_eq! (
2023-05-11 17:17:14 -04:00
config . workspace_settings ( ) . clone ( ) ,
2023-01-03 10:59:48 -05:00
WorkspaceSettings ::default ( )
) ;
}
#[ test ]
fn test_empty_import_map ( ) {
2024-03-26 11:52:20 -04:00
let mut config = Config ::default ( ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( { " import_map " : " " } ) ) . unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2023-01-03 10:59:48 -05:00
assert_eq! (
2023-05-11 17:17:14 -04:00
config . workspace_settings ( ) . clone ( ) ,
2023-01-03 10:59:48 -05:00
WorkspaceSettings ::default ( )
) ;
}
#[ test ]
fn test_empty_tls_certificate ( ) {
2024-03-26 11:52:20 -04:00
let mut config = Config ::default ( ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( { " tls_certificate " : " " } ) ) . unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2023-01-03 10:59:48 -05:00
assert_eq! (
2023-05-11 17:17:14 -04:00
config . workspace_settings ( ) . clone ( ) ,
2023-01-03 10:59:48 -05:00
WorkspaceSettings ::default ( )
) ;
}
#[ test ]
fn test_empty_config ( ) {
2024-03-26 11:52:20 -04:00
let mut config = Config ::default ( ) ;
2023-09-21 01:46:39 -04:00
config . set_workspace_settings (
serde_json ::from_value ( json! ( { " config " : " " } ) ) . unwrap ( ) ,
2024-03-21 00:29:52 -04:00
vec! [ ] ,
2023-09-21 01:46:39 -04:00
) ;
2023-01-03 10:59:48 -05:00
assert_eq! (
2023-05-11 17:17:14 -04:00
config . workspace_settings ( ) . clone ( ) ,
2023-01-03 10:59:48 -05:00
WorkspaceSettings ::default ( )
) ;
}
2023-03-30 17:47:53 -04:00
2024-03-26 11:52:20 -04:00
#[ tokio::test ]
async fn config_enable_via_config_file_detection ( ) {
2024-07-18 18:16:35 -04:00
let root_uri = root_dir ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri . clone ( ) ] ) ;
2023-09-18 15:58:16 -04:00
assert! ( ! config . specifier_enabled ( & root_uri ) ) ;
2023-09-01 16:13:13 -04:00
2024-03-26 11:52:20 -04:00
config
. tree
. inject_config_file (
2024-03-27 17:14:27 -04:00
ConfigFile ::new (
" {} " ,
root_uri . join ( " deno.json " ) . unwrap ( ) ,
2024-07-19 15:56:07 -04:00
& ConfigParseOptions ::default ( ) ,
2024-03-27 17:14:27 -04:00
)
. unwrap ( ) ,
2024-03-26 11:52:20 -04:00
)
. await ;
2023-09-18 15:58:16 -04:00
assert! ( config . specifier_enabled ( & root_uri ) ) ;
2023-09-01 16:13:13 -04:00
}
2023-09-09 14:37:01 -04:00
2023-09-12 09:36:50 -04:00
// Regression test for https://github.com/denoland/vscode_deno/issues/917.
#[ test ]
fn config_specifier_enabled_matches_by_path_component ( ) {
2024-07-18 18:16:35 -04:00
let root_uri = root_dir ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri . clone ( ) ] ) ;
2024-06-13 15:57:14 -04:00
config . set_workspace_settings (
WorkspaceSettings {
enable_paths : Some ( vec! [ " mo " . to_string ( ) ] ) ,
.. Default ::default ( )
} ,
vec! [ ] ,
) ;
2023-09-12 09:36:50 -04:00
assert! ( ! config . specifier_enabled ( & root_uri . join ( " mod.ts " ) . unwrap ( ) ) ) ;
}
2024-03-26 11:52:20 -04:00
#[ tokio::test ]
async fn config_specifier_enabled_for_test ( ) {
2024-07-18 18:16:35 -04:00
let root_uri = root_dir ( ) ;
2024-03-21 00:29:52 -04:00
let mut config = Config ::new_with_roots ( vec! [ root_uri . clone ( ) ] ) ;
2024-06-13 15:57:14 -04:00
let mut settings = WorkspaceSettings {
enable : Some ( true ) ,
enable_paths : Some ( vec! [ " mod1.ts " . to_string ( ) , " mod2.ts " . to_string ( ) ] ) ,
disable_paths : vec ! [ " mod2.ts " . to_string ( ) ] ,
.. Default ::default ( )
} ;
config . set_workspace_settings ( settings . clone ( ) , vec! [ ] ) ;
2023-09-09 14:37:01 -04:00
assert! (
config . specifier_enabled_for_test ( & root_uri . join ( " mod1.ts " ) . unwrap ( ) )
) ;
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod2.ts " ) . unwrap ( ) )
) ;
2023-09-13 12:30:27 -04:00
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod3.ts " ) . unwrap ( ) )
) ;
2024-06-13 15:57:14 -04:00
settings . enable_paths = None ;
config . set_workspace_settings ( settings , vec! [ ] ) ;
2023-09-09 14:37:01 -04:00
2024-03-26 11:52:20 -04:00
config
. tree
. inject_config_file (
ConfigFile ::new (
& json! ( {
" exclude " : [ " mod2.ts " ] ,
" test " : {
" exclude " : [ " mod3.ts " ] ,
} ,
} )
. to_string ( ) ,
root_uri . join ( " deno.json " ) . unwrap ( ) ,
2024-07-19 15:56:07 -04:00
& ConfigParseOptions ::default ( ) ,
2024-03-26 11:52:20 -04:00
)
. unwrap ( ) ,
2023-09-09 14:37:01 -04:00
)
2024-03-26 11:52:20 -04:00
. await ;
2023-09-09 14:37:01 -04:00
assert! (
config . specifier_enabled_for_test ( & root_uri . join ( " mod1.ts " ) . unwrap ( ) )
) ;
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod2.ts " ) . unwrap ( ) )
) ;
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod3.ts " ) . unwrap ( ) )
) ;
2024-03-26 11:52:20 -04:00
config
. tree
. inject_config_file (
ConfigFile ::new (
& json! ( {
" test " : {
" include " : [ " mod1.ts " ] ,
} ,
} )
. to_string ( ) ,
root_uri . join ( " deno.json " ) . unwrap ( ) ,
2024-07-19 15:56:07 -04:00
& ConfigParseOptions ::default ( ) ,
2024-03-26 11:52:20 -04:00
)
. unwrap ( ) ,
2023-09-09 14:37:01 -04:00
)
2024-03-26 11:52:20 -04:00
. await ;
2023-09-09 14:37:01 -04:00
2024-03-26 11:52:20 -04:00
config
. tree
. inject_config_file (
ConfigFile ::new (
& json! ( {
" test " : {
" exclude " : [ " mod2.ts " ] ,
" include " : [ " mod2.ts " ] ,
} ,
} )
. to_string ( ) ,
root_uri . join ( " deno.json " ) . unwrap ( ) ,
2024-07-19 15:56:07 -04:00
& ConfigParseOptions ::default ( ) ,
2024-03-26 11:52:20 -04:00
)
. unwrap ( ) ,
2023-09-09 14:37:01 -04:00
)
2024-03-26 11:52:20 -04:00
. await ;
2023-09-09 14:37:01 -04:00
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod1.ts " ) . unwrap ( ) )
) ;
assert! (
! config . specifier_enabled_for_test ( & root_uri . join ( " mod2.ts " ) . unwrap ( ) )
) ;
}
2024-07-18 18:16:35 -04:00
fn root_dir ( ) -> Url {
if cfg! ( windows ) {
Url ::parse ( " file://C:/root/ " ) . unwrap ( )
} else {
Url ::parse ( " file:///root/ " ) . unwrap ( )
}
}
2020-12-07 05:46:39 -05:00
}