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
2022-03-20 21:33:37 -04:00
use super ::logging ::lsp_log ;
2023-07-10 17:45:09 -04:00
use crate ::args ::ConfigFile ;
2024-03-26 11:52:20 -04:00
use crate ::args ::FmtOptions ;
use crate ::args ::LintOptions ;
2024-03-21 00:29:52 -04:00
use crate ::cache ::FastInsecureHasher ;
2024-03-26 11:52:20 -04:00
use crate ::file_fetcher ::FileFetcher ;
2023-07-10 17:45:09 -04:00
use crate ::lsp ::logging ::lsp_warn ;
2024-03-26 11:52:20 -04:00
use crate ::tools ::lint ::get_configured_rules ;
use crate ::tools ::lint ::ConfiguredRules ;
2023-07-10 17:45:09 -04:00
use crate ::util ::fs ::canonicalize_path_maybe_not_exists ;
2022-11-28 17:28:54 -05:00
use crate ::util ::path ::specifier_to_file_path ;
2023-09-21 01:46:39 -04:00
use deno_ast ::MediaType ;
2023-10-16 21:51:42 -04:00
use deno_config ::FmtOptionsConfig ;
2024-03-26 11:52:20 -04:00
use deno_config ::TsConfig ;
use deno_core ::anyhow ::anyhow ;
2023-07-10 17:45:09 -04:00
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 ;
2021-05-09 21:16:04 -04:00
use deno_core ::ModuleSpecifier ;
2023-07-10 17:45:09 -04:00
use deno_lockfile ::Lockfile ;
2024-03-26 11:52:20 -04:00
use deno_runtime ::deno_node ::PackageJson ;
use deno_runtime ::permissions ::PermissionsContainer ;
use import_map ::ImportMap ;
2023-03-15 10:34:23 -04:00
use lsp ::Url ;
2021-05-20 05:56:48 -04:00
use std ::collections ::BTreeMap ;
2024-03-26 11:52:20 -04:00
use std ::collections ::BTreeSet ;
2021-04-08 21:27:27 -04:00
use std ::collections ::HashMap ;
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
2021-05-09 21:16:04 -04:00
pub const SETTINGS_SECTION : & str = " deno " ;
2020-12-07 05:46:39 -05:00
#[ derive(Debug, Clone, Default) ]
pub struct ClientCapabilities {
2021-08-09 19:56:34 -04:00
pub code_action_disabled_support : bool ,
pub line_folding_only : bool ,
2022-10-14 08:04:38 -04:00
pub snippet_support : bool ,
2020-12-07 05:46:39 -05:00
pub status_notification : bool ,
2022-03-29 18:59:27 -04:00
/// The client provides the `experimental.testingApi` capability, which is
/// built around VSCode's testing API. It indicates that the server should
/// send notifications about tests discovered in modules.
pub testing_api : bool ,
2021-01-04 16:52:20 -05:00
pub workspace_configuration : bool ,
pub workspace_did_change_watched_files : bool ,
2023-08-25 20:50:47 -04:00
pub workspace_will_rename_files : bool ,
2020-12-07 05:46:39 -05:00
}
2021-06-07 07:38:07 -04:00
fn is_true ( ) -> bool {
true
}
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 ,
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 ( ) ,
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) ]
2020-12-07 05:46:39 -05:00
pub unstable : bool ,
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 ,
unstable : false ,
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-05-20 05:56:48 -04:00
#[ derive(Debug, Clone, Default) ]
pub struct ConfigSnapshot {
pub client_capabilities : ClientCapabilities ,
pub settings : Settings ,
2023-09-09 10:04:21 -04:00
pub workspace_folders : Vec < ( ModuleSpecifier , lsp ::WorkspaceFolder ) > ,
2024-04-02 18:02:50 -04:00
pub tree : ConfigTree ,
2021-05-20 05:56:48 -04:00
}
impl ConfigSnapshot {
2023-10-24 16:27:27 -04:00
pub fn workspace_settings_for_specifier (
& self ,
specifier : & ModuleSpecifier ,
) -> & WorkspaceSettings {
self . settings . get_for_specifier ( specifier ) . 0
}
2022-03-20 21:33:37 -04:00
/// Determine if the provided specifier is enabled or not.
2021-05-20 05:56:48 -04:00
pub fn specifier_enabled ( & self , specifier : & ModuleSpecifier ) -> bool {
2024-03-26 11:52:20 -04:00
let config_file = self . tree . config_file_for_specifier ( specifier ) ;
2024-04-02 18:02:50 -04:00
if let Some ( cf ) = config_file {
2024-03-21 00:29:52 -04:00
if let Ok ( files ) = cf . to_files_config ( ) {
if ! files . matches_specifier ( specifier ) {
return false ;
}
}
}
self
. settings
. specifier_enabled ( specifier )
2024-03-26 11:52:20 -04:00
. unwrap_or_else ( | | config_file . is_some ( ) )
2021-05-20 05:56:48 -04:00
}
2023-09-09 14:37:01 -04:00
pub fn specifier_enabled_for_test (
& self ,
specifier : & ModuleSpecifier ,
) -> bool {
2024-03-26 11:52:20 -04:00
if let Some ( cf ) = self . tree . config_file_for_specifier ( specifier ) {
2023-09-09 14:37:01 -04:00
if let Some ( options ) = cf . to_test_config ( ) . ok ( ) . flatten ( ) {
if ! options . files . matches_specifier ( specifier ) {
return false ;
}
}
}
2024-01-15 19:15:39 -05:00
self . specifier_enabled ( specifier )
2023-09-09 14:37:01 -04:00
}
2021-05-20 05:56:48 -04:00
}
2021-03-09 21:41:35 -05:00
#[ derive(Debug, Default, Clone) ]
2021-05-20 05:56:48 -04:00
pub struct Settings {
2023-10-24 16:27:27 -04:00
pub unscoped : WorkspaceSettings ,
2024-03-21 00:29:52 -04:00
pub by_workspace_folder : BTreeMap < ModuleSpecifier , Option < 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 > {
let Ok ( path ) = specifier_to_file_path ( specifier ) else {
// 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 {
if let Ok ( folder_path ) = specifier_to_file_path ( folder_uri ) {
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 > ) {
let Ok ( path ) = specifier_to_file_path ( specifier ) else {
return ( & self . unscoped , None ) ;
} ;
2024-03-21 00:29:52 -04:00
for ( folder_uri , settings ) in self . by_workspace_folder . iter ( ) . rev ( ) {
let mut settings = settings . as_ref ( ) ;
2024-03-31 16:39:23 -04:00
if self . first_folder . as_ref ( ) = = Some ( folder_uri ) {
2024-03-21 00:29:52 -04:00
settings = settings . or ( Some ( & self . unscoped ) ) ;
}
if let Some ( settings ) = settings {
2023-10-24 16:27:27 -04:00
let Ok ( folder_path ) = specifier_to_file_path ( folder_uri ) else {
continue ;
} ;
if path . starts_with ( folder_path ) {
return ( settings , Some ( folder_uri ) ) ;
}
}
}
( & self . unscoped , None )
}
2024-03-21 00:29:52 -04:00
pub fn enable_settings_hash ( & self ) -> u64 {
let mut hasher = FastInsecureHasher ::default ( ) ;
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-03-26 11:52:20 -04:00
#[ derive(Debug, Default) ]
2020-12-07 05:46:39 -05:00
pub struct Config {
pub client_capabilities : ClientCapabilities ,
2024-03-21 00:29:52 -04:00
pub settings : Settings ,
2023-09-09 10:04:21 -04:00
pub workspace_folders : 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-03-21 00:29:52 -04:00
pub fn new_with_roots ( root_uris : impl IntoIterator < Item = Url > ) -> Self {
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! [ ] ;
for root_uri in root_uris {
let name = root_uri . path_segments ( ) . and_then ( | s | s . last ( ) ) ;
let name = name . unwrap_or_default ( ) . to_string ( ) ;
folders . push ( (
root_uri . clone ( ) ,
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 ) > ,
) {
self . settings . by_workspace_folder =
folders . iter ( ) . map ( | ( s , _ ) | ( s . clone ( ) , None ) ) . collect ( ) ;
2024-03-31 16:39:23 -04:00
self . settings . first_folder = folders . first ( ) . map ( | ( s , _ ) | s . clone ( ) ) ;
2024-03-21 00:29:52 -04:00
self . workspace_folders = folders ;
}
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-03-21 00:29:52 -04:00
self . settings . unscoped = unscoped ;
for ( folder_uri , settings ) in folder_settings . into_iter ( ) {
if let Some ( settings_ ) =
self . settings . by_workspace_folder . get_mut ( & folder_uri )
{
* settings_ = Some ( settings ) ;
}
}
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
}
2022-01-19 17:10:14 -05:00
pub fn snapshot ( & self ) -> Arc < ConfigSnapshot > {
Arc ::new ( ConfigSnapshot {
2021-05-20 05:56:48 -04:00
client_capabilities : self . client_capabilities . clone ( ) ,
2022-01-25 10:30:38 -05:00
settings : self . settings . clone ( ) ,
2023-09-09 10:04:21 -04:00
workspace_folders : self . workspace_folders . clone ( ) ,
2024-03-26 11:52:20 -04:00
tree : self . tree . clone ( ) ,
2021-05-20 05:56:48 -04:00
} )
2021-05-09 21:16:04 -04:00
}
pub fn specifier_enabled ( & self , specifier : & ModuleSpecifier ) -> bool {
2024-03-26 11:52:20 -04:00
let config_file = self . tree . config_file_for_specifier ( specifier ) ;
2024-04-02 18:02:50 -04:00
if let Some ( cf ) = config_file {
2024-03-21 00:29:52 -04:00
if let Ok ( files ) = cf . to_files_config ( ) {
if ! files . matches_specifier ( specifier ) {
return false ;
}
}
}
self
. settings
. specifier_enabled ( specifier )
. unwrap_or_else ( | | config_file . 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-03-26 11:52:20 -04:00
if let Some ( cf ) = self . tree . config_file_for_specifier ( specifier ) {
2023-09-09 14:37:01 -04:00
if let Some ( options ) = cf . to_test_config ( ) . ok ( ) . flatten ( ) {
if ! options . files . matches_specifier ( specifier ) {
return false ;
}
}
}
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
}
2020-12-07 05:46:39 -05:00
pub fn update_capabilities (
& mut self ,
2021-01-29 14:34:33 -05:00
capabilities : & lsp ::ClientCapabilities ,
2020-12-07 05:46:39 -05:00
) {
if let Some ( experimental ) = & capabilities . experimental {
2021-06-15 13:22:28 -04:00
self . client_capabilities . status_notification = experimental
. get ( " statusNotification " )
. and_then ( | it | it . as_bool ( ) )
2022-03-29 18:59:27 -04:00
= = Some ( true ) ;
self . client_capabilities . testing_api =
experimental . get ( " testingApi " ) . and_then ( | it | it . as_bool ( ) )
= = Some ( true ) ;
2020-12-07 05:46:39 -05:00
}
2021-01-04 16:52:20 -05:00
if let Some ( workspace ) = & capabilities . workspace {
self . client_capabilities . workspace_configuration =
workspace . configuration . unwrap_or ( false ) ;
self . client_capabilities . workspace_did_change_watched_files = workspace
. did_change_watched_files
. and_then ( | it | it . dynamic_registration )
. unwrap_or ( false ) ;
2023-08-25 20:50:47 -04:00
if let Some ( file_operations ) = & workspace . file_operations {
if let Some ( true ) = file_operations . dynamic_registration {
self . client_capabilities . workspace_will_rename_files =
file_operations . will_rename . unwrap_or ( false ) ;
}
}
2021-01-04 16:52:20 -05:00
}
2021-04-02 02:21:07 -04:00
if let Some ( text_document ) = & capabilities . text_document {
self . client_capabilities . line_folding_only = text_document
. folding_range
. as_ref ( )
. and_then ( | it | it . line_folding_only )
. unwrap_or ( false ) ;
2021-08-09 19:56:34 -04:00
self . client_capabilities . code_action_disabled_support = text_document
. code_action
. as_ref ( )
. and_then ( | it | it . disabled_support )
. unwrap_or ( false ) ;
2022-10-14 08:04:38 -04:00
self . client_capabilities . snippet_support =
if let Some ( completion ) = & text_document . completion {
completion
. completion_item
. as_ref ( )
. and_then ( | it | it . snippet_support )
. unwrap_or ( false )
} else {
false
} ;
2021-04-02 02:21:07 -04:00
}
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 ,
" jsx " : " react " ,
" lib " : [ " deno.ns " , " deno.window " , " deno.unstable " ] ,
" module " : " esnext " ,
" moduleDetection " : " force " ,
" noEmit " : true ,
" resolveJsonModule " : true ,
" strict " : true ,
" target " : " esnext " ,
" useDefineForClassFields " : true ,
" useUnknownInCatchVariables " : false ,
} ) ) ,
}
}
}
impl LspTsConfig {
pub fn new ( config_file : Option < & ConfigFile > ) -> Self {
let mut ts_config = Self ::default ( ) ;
if let Some ( config_file ) = config_file {
match config_file . to_compiler_options ( ) {
Ok ( ( value , maybe_ignored_options ) ) = > {
ts_config . inner . merge ( & value ) ;
if let Some ( ignored_options ) = maybe_ignored_options {
lsp_warn! ( " {} " , ignored_options ) ;
}
}
Err ( err ) = > lsp_warn! ( " {} " , err ) ,
}
}
ts_config
}
2024-03-26 11:52:20 -04:00
}
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub enum ConfigWatchedFileType {
DenoJson ,
Lockfile ,
PackageJson ,
ImportMap ,
}
/// Contains the config file and dependent information.
#[ derive(Debug, Clone) ]
pub struct ConfigData {
pub config_file : Option < Arc < ConfigFile > > ,
pub fmt_options : Arc < FmtOptions > ,
pub lint_options : Arc < LintOptions > ,
pub lint_rules : Arc < ConfiguredRules > ,
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 > ,
pub lockfile : Option < Arc < Mutex < Lockfile > > > ,
pub package_json : Option < Arc < PackageJson > > ,
pub import_map : Option < Arc < ImportMap > > ,
2024-03-31 16:39:23 -04:00
pub import_map_from_settings : bool ,
2024-03-26 11:52:20 -04:00
watched_files : HashMap < ModuleSpecifier , ConfigWatchedFileType > ,
}
impl ConfigData {
async fn load (
config_file_specifier : Option < & ModuleSpecifier > ,
scope : & ModuleSpecifier ,
settings : & Settings ,
file_fetcher : Option < & FileFetcher > ,
) -> Self {
if let Some ( specifier ) = config_file_specifier {
2024-03-27 17:14:27 -04:00
match ConfigFile ::from_specifier (
specifier . clone ( ) ,
& deno_config ::ParseOptions ::default ( ) ,
) {
2024-03-26 11:52:20 -04:00
Ok ( config_file ) = > {
lsp_log! (
" Resolved Deno configuration file: \" {} \" " ,
config_file . specifier . as_str ( )
) ;
Self ::load_inner ( Some ( config_file ) , scope , settings , file_fetcher )
. await
}
Err ( err ) = > {
lsp_warn! (
" Couldn't read Deno configuration file \" {} \" : {} " ,
specifier . as_str ( ) ,
err
) ;
let mut data =
Self ::load_inner ( None , scope , settings , file_fetcher ) . await ;
data
. watched_files
. insert ( specifier . clone ( ) , ConfigWatchedFileType ::DenoJson ) ;
let canonicalized_specifier = 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 ( specifier ) = canonicalized_specifier {
data
. watched_files
. insert ( specifier , ConfigWatchedFileType ::DenoJson ) ;
}
data
}
}
} else {
Self ::load_inner ( None , scope , settings , file_fetcher ) . await
}
}
async fn load_inner (
config_file : Option < ConfigFile > ,
scope : & ModuleSpecifier ,
settings : & Settings ,
file_fetcher : Option < & FileFetcher > ,
) -> Self {
let ( settings , workspace_folder ) = settings . get_for_specifier ( scope ) ;
let mut watched_files = HashMap ::with_capacity ( 6 ) ;
if let Some ( config_file ) = & config_file {
watched_files
. entry ( config_file . specifier . clone ( ) )
. or_insert ( ConfigWatchedFileType ::DenoJson ) ;
}
let config_file_canonicalized_specifier = config_file
. as_ref ( )
. and_then ( | c | c . 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 ( specifier ) = config_file_canonicalized_specifier {
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::DenoJson ) ;
}
// Resolve some config file fields ahead of time
let fmt_options = config_file
. as_ref ( )
. and_then ( | config_file | {
config_file
. to_fmt_config ( )
. and_then ( | o | {
let base_path = config_file
. specifier
. to_file_path ( )
. map_err ( | _ | anyhow! ( " Invalid base path. " ) ) ? ;
FmtOptions ::resolve ( o , None , & base_path )
} )
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read formatter configuration: {} " , err )
} )
. ok ( )
} )
. unwrap_or_default ( ) ;
let lint_options = config_file
. as_ref ( )
. and_then ( | config_file | {
config_file
. to_lint_config ( )
. and_then ( | o | {
let base_path = config_file
. specifier
. to_file_path ( )
. map_err ( | _ | anyhow! ( " Invalid base path. " ) ) ? ;
LintOptions ::resolve ( o , None , & base_path )
} )
. inspect_err ( | err | {
lsp_warn! ( " Couldn't read lint configuration: {} " , err )
} )
. ok ( )
} )
. unwrap_or_default ( ) ;
let lint_rules =
get_configured_rules ( lint_options . rules . clone ( ) , config_file . as_ref ( ) ) ;
2024-04-02 18:02:50 -04:00
let ts_config = LspTsConfig ::new ( config_file . as_ref ( ) ) ;
2024-03-26 11:52:20 -04:00
let vendor_dir = config_file . as_ref ( ) . and_then ( | c | c . vendor_dir_path ( ) ) ;
// Load lockfile
let lockfile = config_file . as_ref ( ) . and_then ( resolve_lockfile_from_config ) ;
if let Some ( lockfile ) = & lockfile {
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( & lockfile . filename )
{
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::Lockfile ) ;
}
}
let lockfile_canonicalized_specifier = lockfile
. as_ref ( )
. and_then ( | lockfile | {
canonicalize_path_maybe_not_exists ( & lockfile . filename ) . ok ( )
} )
. and_then ( | p | ModuleSpecifier ::from_file_path ( p ) . ok ( ) ) ;
if let Some ( specifier ) = lockfile_canonicalized_specifier {
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::Lockfile ) ;
}
// Load package.json
let mut package_json = None ;
if let Ok ( path ) = specifier_to_file_path ( scope ) {
let path = path . join ( " package.json " ) ;
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( & path ) {
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::PackageJson ) ;
}
let package_json_canonicalized_specifier =
canonicalize_path_maybe_not_exists ( & path )
. ok ( )
. and_then ( | p | ModuleSpecifier ::from_file_path ( p ) . ok ( ) ) ;
if let Some ( specifier ) = package_json_canonicalized_specifier {
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::PackageJson ) ;
}
if let Ok ( source ) = std ::fs ::read_to_string ( & path ) {
match PackageJson ::load_from_string ( path . clone ( ) , source ) {
Ok ( result ) = > {
lsp_log! ( " Resolved package.json: \" {} \" " , path . display ( ) ) ;
package_json = Some ( result ) ;
}
Err ( err ) = > {
lsp_warn! (
" Couldn't read package.json \" {} \" : {} " ,
path . display ( ) ,
err
) ;
}
}
}
}
2024-04-05 11:18:48 -04:00
let byonm = std ::env ::var ( " DENO_UNSTABLE_BYONM " ) . is_ok ( )
| | config_file
. as_ref ( )
. map ( | c | c . has_unstable ( " byonm " ) )
. unwrap_or ( false )
| | ( std ::env ::var ( " DENO_FUTURE " ) . is_ok ( )
& & package_json . is_some ( )
& & config_file
. as_ref ( )
. map ( | c | c . json . node_modules_dir . is_none ( ) )
. unwrap_or ( true ) ) ;
if byonm {
lsp_log! ( " Enabled 'bring your own node_modules'. " ) ;
}
let node_modules_dir = config_file
. as_ref ( )
. and_then ( | c | resolve_node_modules_dir ( c , byonm ) ) ;
2024-03-26 11:52:20 -04:00
// Load import map
let mut import_map = None ;
let mut import_map_value = None ;
let mut import_map_specifier = None ;
2024-03-31 16:39:23 -04:00
let mut import_map_from_settings = false ;
2024-03-26 11:52:20 -04:00
if let Some ( config_file ) = & config_file {
if config_file . is_an_import_map ( ) {
import_map_value = Some ( config_file . to_import_map_value_from_imports ( ) ) ;
import_map_specifier = Some ( config_file . specifier . clone ( ) ) ;
} else if let Ok ( Some ( specifier ) ) = config_file . to_import_map_specifier ( )
{
import_map_specifier = Some ( specifier ) ;
}
}
2024-03-31 16:39:23 -04:00
import_map_specifier = import_map_specifier . or_else ( | | {
let import_map_str = settings . import_map . as_ref ( ) ? ;
let specifier = Url ::parse ( import_map_str )
. ok ( )
. or_else ( | | workspace_folder ? . join ( import_map_str ) . ok ( ) ) ? ;
import_map_from_settings = true ;
Some ( specifier )
} ) ;
2024-03-26 11:52:20 -04:00
if let Some ( specifier ) = & import_map_specifier {
if let Ok ( path ) = specifier_to_file_path ( specifier ) {
watched_files
. entry ( specifier . clone ( ) )
. or_insert ( ConfigWatchedFileType ::ImportMap ) ;
let import_map_canonicalized_specifier =
canonicalize_path_maybe_not_exists ( & path )
. ok ( )
. and_then ( | p | ModuleSpecifier ::from_file_path ( p ) . ok ( ) ) ;
if let Some ( specifier ) = import_map_canonicalized_specifier {
watched_files
. entry ( specifier )
. or_insert ( ConfigWatchedFileType ::ImportMap ) ;
}
}
if import_map_value . is_none ( ) {
if let Some ( file_fetcher ) = file_fetcher {
let fetch_result = file_fetcher
. fetch ( specifier , PermissionsContainer ::allow_all ( ) )
. await ;
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 ) = > {
import_map_value = Some ( value ) ;
}
Err ( err ) = > {
lsp_warn! (
" Couldn't read import map \" {} \" : {} " ,
specifier . as_str ( ) ,
err
) ;
}
}
}
}
}
if let ( Some ( value ) , Some ( specifier ) ) =
( import_map_value , import_map_specifier )
{
match import_map ::parse_from_value ( specifier . clone ( ) , value ) {
Ok ( result ) = > {
if config_file . as_ref ( ) . map ( | c | & c . specifier ) = = Some ( & specifier ) {
lsp_log! ( " Resolved import map from configuration file " ) ;
} else {
lsp_log! ( " Resolved import map: \" {} \" " , specifier . as_str ( ) ) ;
}
if ! result . diagnostics . is_empty ( ) {
lsp_warn! (
" Import map diagnostics: \n {} " ,
result
. diagnostics
. iter ( )
. map ( | d | format! ( " - {d} " ) )
. collect ::< Vec < _ > > ( )
. join ( " \n " )
) ;
}
import_map = Some ( result . import_map ) ;
}
Err ( err ) = > {
lsp_warn! (
" Couldn't read import map \" {} \" : {} " ,
specifier . as_str ( ) ,
err
) ;
}
}
}
ConfigData {
config_file : config_file . map ( Arc ::new ) ,
fmt_options : Arc ::new ( fmt_options ) ,
lint_options : Arc ::new ( lint_options ) ,
lint_rules : Arc ::new ( lint_rules ) ,
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 ,
lockfile : lockfile . map ( Mutex ::new ) . map ( Arc ::new ) ,
package_json : package_json . map ( Arc ::new ) ,
import_map : import_map . map ( Arc ::new ) ,
2024-03-31 16:39:23 -04:00
import_map_from_settings ,
2024-03-26 11:52:20 -04:00
watched_files ,
}
}
}
2024-04-02 18:02:50 -04:00
#[ derive(Clone, Debug, Default) ]
2024-03-26 11:52:20 -04:00
pub struct ConfigTree {
2024-04-02 18:02:50 -04:00
first_folder : Option < ModuleSpecifier > ,
scopes : Arc < BTreeMap < ModuleSpecifier , ConfigData > > ,
2024-03-26 11:52:20 -04:00
}
impl ConfigTree {
2024-04-02 18:02:50 -04:00
pub fn root_data ( & self ) -> Option < & ConfigData > {
self . first_folder . as_ref ( ) . and_then ( | s | self . scopes . get ( s ) )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn root_ts_config ( & self ) -> Arc < LspTsConfig > {
2024-03-26 11:52:20 -04:00
self
2024-04-02 18:02:50 -04:00
. root_data ( )
. map ( | d | d . ts_config . clone ( ) )
. unwrap_or_default ( )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn root_vendor_dir ( & self ) -> Option < & PathBuf > {
self . root_data ( ) . and_then ( | d | d . vendor_dir . as_ref ( ) )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn root_lockfile ( & self ) -> Option < & Arc < Mutex < Lockfile > > > {
self . root_data ( ) . and_then ( | d | d . lockfile . as_ref ( ) )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn root_import_map ( & self ) -> Option < & Arc < ImportMap > > {
self . root_data ( ) . and_then ( | d | d . import_map . as_ref ( ) )
2024-03-26 11:52:20 -04:00
}
pub fn scope_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
) -> Option < & ModuleSpecifier > {
self
. scopes
. keys ( )
. rfind ( | s | specifier . as_str ( ) . starts_with ( s . as_str ( ) ) )
. or ( self . first_folder . as_ref ( ) )
2024-03-26 11:52:20 -04:00
}
pub fn data_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
) -> Option < & ConfigData > {
self
. scope_for_specifier ( specifier )
. and_then ( | s | self . scopes . get ( s ) )
2024-03-26 11:52:20 -04:00
}
2024-04-02 18:02:50 -04:00
pub fn data_by_scope ( & self ) -> & Arc < BTreeMap < ModuleSpecifier , ConfigData > > {
& self . scopes
2024-03-26 11:52:20 -04:00
}
pub fn config_file_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
) -> Option < & Arc < ConfigFile > > {
2024-03-26 11:52:20 -04:00
self
2024-04-02 18:02:50 -04:00
. data_for_specifier ( specifier )
. and_then ( | d | d . config_file . 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 ( )
. filter_map ( | ( _ , d ) | d . config_file . as_ref ( ) )
. 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 ( )
. filter_map ( | ( _ , d ) | d . package_json . as_ref ( ) )
2024-03-26 11:52:20 -04:00
. collect ( )
}
pub fn fmt_options_for_specifier (
& self ,
2024-04-02 18:02:50 -04:00
specifier : & ModuleSpecifier ,
2024-03-26 11:52:20 -04:00
) -> Arc < FmtOptions > {
self
2024-04-02 18:02:50 -04:00
. data_for_specifier ( specifier )
. map ( | d | d . fmt_options . clone ( ) )
2024-03-26 11:52:20 -04:00
. unwrap_or_default ( )
}
2024-04-02 18:02:50 -04:00
/// Returns (scope_uri, type).
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 ) > {
for ( scope_uri , data ) in self . scopes . iter ( ) {
if let Some ( typ ) = data . watched_files . get ( specifier ) {
return Some ( ( scope_uri , * typ ) ) ;
}
}
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 {
if specifier . path ( ) . ends_with ( " /deno.json " )
| | specifier . path ( ) . ends_with ( " /deno.jsonc " )
| | specifier . path ( ) . ends_with ( " /package.json " )
{
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
}
pub async fn refresh (
2024-04-02 18:02:50 -04:00
& mut self ,
2024-03-26 11:52:20 -04:00
settings : & Settings ,
workspace_files : & BTreeSet < ModuleSpecifier > ,
file_fetcher : & FileFetcher ,
) {
lsp_log! ( " Refreshing configuration tree... " ) ;
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 {
if let Some ( config_path ) = & ws_settings . config {
if let Ok ( config_uri ) = folder_uri . join ( config_path ) {
scopes . insert (
folder_uri . clone ( ) ,
ConfigData ::load (
Some ( & config_uri ) ,
folder_uri ,
settings ,
Some ( file_fetcher ) ,
)
. await ,
) ;
}
}
}
}
for specifier in workspace_files {
if specifier . path ( ) . ends_with ( " /deno.json " )
| | specifier . path ( ) . ends_with ( " /deno.jsonc " )
{
if let Ok ( scope ) = specifier . join ( " . " ) {
let entry = scopes . entry ( scope . clone ( ) ) ;
#[ allow(clippy::map_entry) ]
if matches! ( entry , std ::collections ::btree_map ::Entry ::Vacant ( _ ) ) {
let data = ConfigData ::load (
Some ( specifier ) ,
& scope ,
2024-03-26 11:52:20 -04:00
settings ,
Some ( file_fetcher ) ,
)
2024-04-02 18:02:50 -04:00
. await ;
entry . or_insert ( data ) ;
}
}
2024-03-26 11:52:20 -04:00
}
}
2024-04-02 18:02:50 -04:00
for folder_uri in settings . by_workspace_folder . keys ( ) {
if ! scopes
. keys ( )
. any ( | s | folder_uri . as_str ( ) . starts_with ( s . as_str ( ) ) )
{
scopes . insert (
folder_uri . clone ( ) ,
ConfigData ::load ( None , folder_uri , settings , Some ( file_fetcher ) )
. await ,
) ;
2024-03-26 11:52:20 -04:00
}
}
2024-04-02 18:02:50 -04:00
self . first_folder = settings . first_folder . clone ( ) ;
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 ( ) ;
let data = ConfigData ::load_inner (
Some ( config_file ) ,
& scope ,
& Default ::default ( ) ,
None ,
)
. await ;
2024-04-02 18:02:50 -04:00
self . first_folder = Some ( scope . clone ( ) ) ;
self . scopes = Arc ::new ( [ ( scope , data ) ] . into_iter ( ) . collect ( ) ) ;
2024-03-26 11:52:20 -04:00
}
}
2023-07-21 09:12:26 -04:00
fn resolve_lockfile_from_config ( config_file : & ConfigFile ) -> Option < Lockfile > {
2023-07-10 17:45:09 -04:00
let lockfile_path = match config_file . resolve_lockfile_path ( ) {
Ok ( Some ( value ) ) = > value ,
Ok ( None ) = > return None ,
Err ( err ) = > {
lsp_warn! ( " Error resolving lockfile: {:#} " , err ) ;
return None ;
}
} ;
resolve_lockfile_from_path ( lockfile_path )
}
2024-04-05 11:18:48 -04:00
fn resolve_node_modules_dir (
config_file : & ConfigFile ,
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-02-24 00:21:09 -05:00
let explicitly_disabled = config_file . json . node_modules_dir = = Some ( false ) ;
2023-08-06 21:56:56 -04:00
if explicitly_disabled {
return None ;
}
2024-04-05 11:18:48 -04:00
let enabled = byonm
| | config_file . json . node_modules_dir = = Some ( true )
2024-02-24 00:21:09 -05:00
| | config_file . json . vendor = = Some ( true ) ;
2023-08-06 21:56:56 -04:00
if ! enabled {
2023-07-10 17:45:09 -04:00
return None ;
}
if config_file . specifier . scheme ( ) ! = " file " {
return None ;
}
let file_path = config_file . specifier . to_file_path ( ) . ok ( ) ? ;
let node_modules_dir = file_path . parent ( ) ? . join ( " node_modules " ) ;
canonicalize_path_maybe_not_exists ( & node_modules_dir ) . ok ( )
}
2023-07-21 09:12:26 -04:00
fn resolve_lockfile_from_path ( lockfile_path : PathBuf ) -> Option < Lockfile > {
2023-07-10 17:45:09 -04:00
match Lockfile ::new ( lockfile_path , false ) {
2023-07-20 14:05:52 -04:00
Ok ( value ) = > {
if let Ok ( specifier ) = ModuleSpecifier ::from_file_path ( & value . filename ) {
2024-03-26 11:52:20 -04:00
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
}
}
}
2021-05-09 21:16:04 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
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
#[ 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
) ;
2022-03-20 21:33:37 -04:00
let config_snapshot = config . snapshot ( ) ;
assert! ( config_snapshot . specifier_enabled ( & specifier ) ) ;
}
#[ 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 ) ) ;
let config_snapshot = config . snapshot ( ) ;
assert! ( config_snapshot . specifier_enabled ( & specifier_a ) ) ;
assert! ( ! config_snapshot . 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 ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable = Some ( true ) ;
config . settings . unscoped . enable_paths =
2023-09-13 12:30:27 -04:00
Some ( vec! [ " mod1.ts " . to_string ( ) , " mod2.ts " . to_string ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . disable_paths = vec! [ " mod2.ts " . to_string ( ) ] ;
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 ,
2021-06-01 07:53:08 -04:00
unstable : false ,
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 ,
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 ,
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 ( ) {
2023-09-09 10:04:21 -04:00
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 ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable = None ;
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 ( ) ,
& deno_config ::ParseOptions ::default ( ) ,
)
. 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 ( ) {
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 ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable_paths = Some ( vec! [ " mo " . to_string ( ) ] ) ;
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 ( ) {
2023-09-09 14:37:01 -04:00
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 ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable = Some ( true ) ;
2023-09-09 14:37:01 -04:00
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable_paths =
2023-09-13 12:30:27 -04:00
Some ( vec! [ " mod1.ts " . to_string ( ) , " mod2.ts " . to_string ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . disable_paths = vec! [ " mod2.ts " . to_string ( ) ] ;
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 ( ) )
) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable_paths = None ;
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-03-27 17:14:27 -04:00
& deno_config ::ParseOptions ::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-03-27 17:14:27 -04:00
& deno_config ::ParseOptions ::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-03-27 17:14:27 -04:00
& deno_config ::ParseOptions ::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-03-26 11:52:20 -04:00
#[ tokio::test ]
async fn config_snapshot_specifier_enabled_for_test ( ) {
2023-09-09 14:37:01 -04:00
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 ( ) ] ) ;
2023-10-24 16:27:27 -04:00
config . settings . unscoped . enable = Some ( true ) ;
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-03-27 17:14:27 -04:00
& deno_config ::ParseOptions ::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
let config_snapshot = config . snapshot ( ) ;
assert! ( config_snapshot
. specifier_enabled_for_test ( & root_uri . join ( " mod1.ts " ) . unwrap ( ) ) ) ;
assert! ( ! config_snapshot
. specifier_enabled_for_test ( & root_uri . join ( " mod2.ts " ) . unwrap ( ) ) ) ;
assert! ( ! config_snapshot
. specifier_enabled_for_test ( & root_uri . join ( " mod3.ts " ) . unwrap ( ) ) ) ;
}
2020-12-07 05:46:39 -05:00
}