mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 16:42:21 -05:00
perf(lsp): cleanup workspace settings scopes (#20937)
This commit is contained in:
parent
8f065a60e7
commit
a7bd0cf7a8
9 changed files with 559 additions and 769 deletions
|
@ -6,14 +6,13 @@ use async_trait::async_trait;
|
||||||
use deno_core::anyhow::anyhow;
|
use deno_core::anyhow::anyhow;
|
||||||
use deno_core::anyhow::bail;
|
use deno_core::anyhow::bail;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::unsync::spawn;
|
use deno_core::unsync::spawn;
|
||||||
use tower_lsp::lsp_types as lsp;
|
use tower_lsp::lsp_types as lsp;
|
||||||
use tower_lsp::lsp_types::ConfigurationItem;
|
use tower_lsp::lsp_types::ConfigurationItem;
|
||||||
|
|
||||||
use crate::lsp::repl::get_repl_workspace_settings;
|
use crate::lsp::repl::get_repl_workspace_settings;
|
||||||
|
|
||||||
use super::config::SpecifierSettings;
|
|
||||||
use super::config::WorkspaceSettings;
|
use super::config::WorkspaceSettings;
|
||||||
use super::config::SETTINGS_SECTION;
|
use super::config::SETTINGS_SECTION;
|
||||||
use super::lsp_custom;
|
use super::lsp_custom;
|
||||||
|
@ -125,46 +124,11 @@ impl OutsideLockClient {
|
||||||
self.0.register_capability(registrations).await
|
self.0.register_capability(registrations).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn specifier_configurations(
|
|
||||||
&self,
|
|
||||||
specifiers: Vec<LspClientUrl>,
|
|
||||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
|
||||||
self
|
|
||||||
.0
|
|
||||||
.specifier_configurations(
|
|
||||||
specifiers.into_iter().map(|s| s.into_url()).collect(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn specifier_configuration(
|
|
||||||
&self,
|
|
||||||
specifier: &LspClientUrl,
|
|
||||||
) -> Result<SpecifierSettings, AnyError> {
|
|
||||||
let values = self
|
|
||||||
.0
|
|
||||||
.specifier_configurations(vec![specifier.as_url().clone()])
|
|
||||||
.await?;
|
|
||||||
if let Some(value) = values.into_iter().next() {
|
|
||||||
value.map_err(|err| {
|
|
||||||
anyhow!(
|
|
||||||
"Error converting specifier settings ({}): {}",
|
|
||||||
specifier,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
bail!(
|
|
||||||
"Expected the client to return a configuration item for specifier: {}",
|
|
||||||
specifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn workspace_configuration(
|
pub async fn workspace_configuration(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<WorkspaceSettings, AnyError> {
|
scopes: Vec<Option<lsp::Url>>,
|
||||||
self.0.workspace_configuration().await
|
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||||
|
self.0.workspace_configuration(scopes).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn publish_diagnostics(
|
pub async fn publish_diagnostics(
|
||||||
|
@ -201,13 +165,10 @@ trait ClientTrait: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||||
);
|
);
|
||||||
async fn specifier_configurations(
|
|
||||||
&self,
|
|
||||||
uris: Vec<lsp::Url>,
|
|
||||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError>;
|
|
||||||
async fn workspace_configuration(
|
async fn workspace_configuration(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<WorkspaceSettings, AnyError>;
|
scopes: Vec<Option<lsp::Url>>,
|
||||||
|
) -> Result<Vec<WorkspaceSettings>, AnyError>;
|
||||||
async fn show_message(&self, message_type: lsp::MessageType, text: String);
|
async fn show_message(&self, message_type: lsp::MessageType, text: String);
|
||||||
async fn register_capability(
|
async fn register_capability(
|
||||||
&self,
|
&self,
|
||||||
|
@ -288,67 +249,50 @@ impl ClientTrait for TowerClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn specifier_configurations(
|
async fn workspace_configuration(
|
||||||
&self,
|
&self,
|
||||||
uris: Vec<lsp::Url>,
|
scopes: Vec<Option<lsp::Url>>,
|
||||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||||
let config_response = self
|
let config_response = self
|
||||||
.0
|
.0
|
||||||
.configuration(
|
.configuration(
|
||||||
uris
|
scopes
|
||||||
.into_iter()
|
.iter()
|
||||||
.map(|uri| ConfigurationItem {
|
.flat_map(|scope_uri| {
|
||||||
scope_uri: Some(uri),
|
vec![
|
||||||
section: Some(SETTINGS_SECTION.to_string()),
|
ConfigurationItem {
|
||||||
|
scope_uri: scope_uri.clone(),
|
||||||
|
section: Some(SETTINGS_SECTION.to_string()),
|
||||||
|
},
|
||||||
|
ConfigurationItem {
|
||||||
|
scope_uri: scope_uri.clone(),
|
||||||
|
section: Some("javascript".to_string()),
|
||||||
|
},
|
||||||
|
ConfigurationItem {
|
||||||
|
scope_uri: scope_uri.clone(),
|
||||||
|
section: Some("typescript".to_string()),
|
||||||
|
},
|
||||||
|
]
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
config_response
|
|
||||||
.into_iter()
|
|
||||||
.map(|value| {
|
|
||||||
serde_json::from_value::<SpecifierSettings>(value).map_err(|err| {
|
|
||||||
anyhow!("Error converting specifier settings: {}", err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn workspace_configuration(
|
|
||||||
&self,
|
|
||||||
) -> Result<WorkspaceSettings, AnyError> {
|
|
||||||
let config_response = self
|
|
||||||
.0
|
|
||||||
.configuration(vec![
|
|
||||||
ConfigurationItem {
|
|
||||||
scope_uri: None,
|
|
||||||
section: Some(SETTINGS_SECTION.to_string()),
|
|
||||||
},
|
|
||||||
ConfigurationItem {
|
|
||||||
scope_uri: None,
|
|
||||||
section: Some("javascript".to_string()),
|
|
||||||
},
|
|
||||||
ConfigurationItem {
|
|
||||||
scope_uri: None,
|
|
||||||
section: Some("typescript".to_string()),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.await;
|
.await;
|
||||||
match config_response {
|
match config_response {
|
||||||
Ok(configs) => {
|
Ok(configs) => {
|
||||||
let mut configs = configs.into_iter();
|
let mut configs = configs.into_iter();
|
||||||
let deno = serde_json::to_value(configs.next()).unwrap();
|
let mut result = Vec::with_capacity(scopes.len());
|
||||||
let javascript = serde_json::to_value(configs.next()).unwrap();
|
for _ in 0..scopes.len() {
|
||||||
let typescript = serde_json::to_value(configs.next()).unwrap();
|
let deno = json!(configs.next());
|
||||||
Ok(WorkspaceSettings::from_raw_settings(
|
let javascript = json!(configs.next());
|
||||||
deno, javascript, typescript,
|
let typescript = json!(configs.next());
|
||||||
))
|
result.push(WorkspaceSettings::from_raw_settings(
|
||||||
|
deno, javascript, typescript,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bail!("Error getting workspace configuration: {}", err)
|
bail!("Error getting workspace configurations: {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -406,27 +350,11 @@ impl ClientTrait for ReplClient {
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn specifier_configurations(
|
|
||||||
&self,
|
|
||||||
uris: Vec<lsp::Url>,
|
|
||||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
|
||||||
// all specifiers are enabled for the REPL
|
|
||||||
let settings = uris
|
|
||||||
.into_iter()
|
|
||||||
.map(|_| {
|
|
||||||
Ok(SpecifierSettings {
|
|
||||||
enable: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn workspace_configuration(
|
async fn workspace_configuration(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<WorkspaceSettings, AnyError> {
|
scopes: Vec<Option<lsp::Url>>,
|
||||||
Ok(get_repl_workspace_settings())
|
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||||
|
Ok(vec![get_repl_workspace_settings(); scopes.len()])
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn show_message(
|
async fn show_message(
|
||||||
|
|
|
@ -408,7 +408,7 @@ fn collect_test(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
||||||
if config.specifier_enabled_for_test(specifier)
|
if config.specifier_enabled_for_test(specifier)
|
||||||
&& config.specifier_code_lens_test(specifier)
|
&& config.enabled_code_lens_test_for_specifier(specifier)
|
||||||
{
|
{
|
||||||
if let Some(parsed_source) = parsed_source {
|
if let Some(parsed_source) = parsed_source {
|
||||||
let mut collector =
|
let mut collector =
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use super::client::Client;
|
use super::client::Client;
|
||||||
use super::config::ConfigSnapshot;
|
use super::config::ConfigSnapshot;
|
||||||
|
use super::config::WorkspaceSettings;
|
||||||
use super::documents::file_like_to_file_specifier;
|
use super::documents::file_like_to_file_specifier;
|
||||||
use super::documents::Documents;
|
use super::documents::Documents;
|
||||||
use super::documents::DocumentsFilter;
|
use super::documents::DocumentsFilter;
|
||||||
|
@ -52,12 +53,12 @@ pub struct CompletionItemData {
|
||||||
/// a notification to the client.
|
/// a notification to the client.
|
||||||
async fn check_auto_config_registry(
|
async fn check_auto_config_registry(
|
||||||
url_str: &str,
|
url_str: &str,
|
||||||
config: &ConfigSnapshot,
|
workspace_settings: &WorkspaceSettings,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
module_registries: &ModuleRegistry,
|
module_registries: &ModuleRegistry,
|
||||||
) {
|
) {
|
||||||
// check to see if auto discovery is enabled
|
// check to see if auto discovery is enabled
|
||||||
if config.settings.workspace.suggest.imports.auto_discover {
|
if workspace_settings.suggest.imports.auto_discover {
|
||||||
if let Ok(specifier) = resolve_url(url_str) {
|
if let Ok(specifier) = resolve_url(url_str) {
|
||||||
let scheme = specifier.scheme();
|
let scheme = specifier.scheme();
|
||||||
let path = &specifier[Position::BeforePath..];
|
let path = &specifier[Position::BeforePath..];
|
||||||
|
@ -67,11 +68,14 @@ async fn check_auto_config_registry(
|
||||||
{
|
{
|
||||||
// check to see if this origin is already explicitly set
|
// check to see if this origin is already explicitly set
|
||||||
let in_config =
|
let in_config =
|
||||||
config.settings.workspace.suggest.imports.hosts.iter().any(
|
workspace_settings
|
||||||
|(h, _)| {
|
.suggest
|
||||||
|
.imports
|
||||||
|
.hosts
|
||||||
|
.iter()
|
||||||
|
.any(|(h, _)| {
|
||||||
resolve_url(h).map(|u| u.origin()) == Ok(specifier.origin())
|
resolve_url(h).map(|u| u.origin()) == Ok(specifier.origin())
|
||||||
},
|
});
|
||||||
);
|
|
||||||
// if it isn't in the configuration, we will check to see if it supports
|
// if it isn't in the configuration, we will check to see if it supports
|
||||||
// suggestions and send a notification to the client.
|
// suggestions and send a notification to the client.
|
||||||
if !in_config {
|
if !in_config {
|
||||||
|
@ -176,7 +180,13 @@ pub async fn get_import_completions(
|
||||||
}))
|
}))
|
||||||
} else if !text.is_empty() {
|
} else if !text.is_empty() {
|
||||||
// completion of modules from a module registry or cache
|
// completion of modules from a module registry or cache
|
||||||
check_auto_config_registry(&text, config, client, module_registries).await;
|
check_auto_config_registry(
|
||||||
|
&text,
|
||||||
|
config.workspace_settings_for_specifier(specifier),
|
||||||
|
client,
|
||||||
|
module_registries,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let offset = if position.character > range.start.character {
|
let offset = if position.character > range.start.character {
|
||||||
(position.character - range.start.character) as usize
|
(position.character - range.start.character) as usize
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -73,21 +73,6 @@ impl Default for CodeLensSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct CodeLensSpecifierSettings {
|
|
||||||
/// 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CodeLensSpecifierSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { test: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DenoCompletionSettings {
|
pub struct DenoCompletionSettings {
|
||||||
|
@ -277,25 +262,6 @@ impl Default for ImportCompletionSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deno language server specific settings that can be applied uniquely to a
|
|
||||||
/// specifier.
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct SpecifierSettings {
|
|
||||||
/// A flag that indicates if Deno is enabled for this specifier or not.
|
|
||||||
pub enable: Option<bool>,
|
|
||||||
/// A list of paths, using the workspace folder as a base that should be Deno
|
|
||||||
/// disabled.
|
|
||||||
#[serde(default)]
|
|
||||||
pub disable_paths: Vec<String>,
|
|
||||||
/// A list of paths, using the workspace folder as a base that should be Deno
|
|
||||||
/// enabled.
|
|
||||||
pub enable_paths: Option<Vec<String>>,
|
|
||||||
/// Code lens specific settings for the resource.
|
|
||||||
#[serde(default)]
|
|
||||||
pub code_lens: CodeLensSpecifierSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TestingSettings {
|
pub struct TestingSettings {
|
||||||
|
@ -712,56 +678,6 @@ impl WorkspaceSettings {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
Self::from_raw_settings(deno, javascript, typescript)
|
Self::from_raw_settings(deno, javascript, typescript)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if any code lenses are enabled at all. This allows short
|
|
||||||
/// circuiting when there are no code lenses enabled.
|
|
||||||
pub fn enabled_code_lens(&self) -> bool {
|
|
||||||
self.code_lens.implementations || self.code_lens.references
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(nayeemrmn): Factor in out-of-band media type here.
|
|
||||||
pub fn language_settings_for_specifier(
|
|
||||||
&self,
|
|
||||||
specifier: &ModuleSpecifier,
|
|
||||||
) -> Option<&LanguageWorkspaceSettings> {
|
|
||||||
if specifier.scheme() == "deno-notebook-cell" {
|
|
||||||
return Some(&self.typescript);
|
|
||||||
}
|
|
||||||
match MediaType::from_specifier(specifier) {
|
|
||||||
MediaType::JavaScript
|
|
||||||
| MediaType::Jsx
|
|
||||||
| MediaType::Mjs
|
|
||||||
| MediaType::Cjs => Some(&self.javascript),
|
|
||||||
MediaType::TypeScript
|
|
||||||
| MediaType::Mts
|
|
||||||
| MediaType::Cts
|
|
||||||
| MediaType::Dts
|
|
||||||
| MediaType::Dmts
|
|
||||||
| MediaType::Dcts
|
|
||||||
| MediaType::Tsx => Some(&self.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(&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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
@ -773,6 +689,13 @@ pub struct ConfigSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigSnapshot {
|
impl ConfigSnapshot {
|
||||||
|
pub fn workspace_settings_for_specifier(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> &WorkspaceSettings {
|
||||||
|
self.settings.get_for_specifier(specifier).0
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine if the provided specifier is enabled or not.
|
/// Determine if the provided specifier is enabled or not.
|
||||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
specifier_enabled(
|
specifier_enabled(
|
||||||
|
@ -803,8 +726,59 @@ impl ConfigSnapshot {
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub specifiers: BTreeMap<ModuleSpecifier, SpecifierSettings>,
|
pub unscoped: WorkspaceSettings,
|
||||||
pub workspace: WorkspaceSettings,
|
pub by_workspace_folder: Option<BTreeMap<ModuleSpecifier, WorkspaceSettings>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
if let Some(by_workspace_folder) = &self.by_workspace_folder {
|
||||||
|
for (folder_uri, settings) in by_workspace_folder.iter().rev() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_unscoped(&mut self, mut settings: WorkspaceSettings) {
|
||||||
|
// See https://github.com/denoland/vscode_deno/issues/908.
|
||||||
|
if settings.enable_paths == Some(vec![]) {
|
||||||
|
settings.enable_paths = None;
|
||||||
|
}
|
||||||
|
self.unscoped = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_for_workspace_folders(
|
||||||
|
&mut self,
|
||||||
|
mut by_workspace_folder: Option<
|
||||||
|
BTreeMap<ModuleSpecifier, WorkspaceSettings>,
|
||||||
|
>,
|
||||||
|
) {
|
||||||
|
if let Some(by_workspace_folder) = &mut by_workspace_folder {
|
||||||
|
for settings in by_workspace_folder.values_mut() {
|
||||||
|
// See https://github.com/denoland/vscode_deno/issues/908.
|
||||||
|
if settings.enable_paths == Some(vec![]) {
|
||||||
|
settings.enable_paths = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.by_workspace_folder = by_workspace_folder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -860,6 +834,93 @@ impl Config {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_workspace_settings(
|
||||||
|
&mut self,
|
||||||
|
unscoped: WorkspaceSettings,
|
||||||
|
by_workspace_folder: Option<BTreeMap<ModuleSpecifier, WorkspaceSettings>>,
|
||||||
|
) {
|
||||||
|
self.settings.set_unscoped(unscoped);
|
||||||
|
self.settings.set_for_workspace_folders(by_workspace_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if specifier.scheme() == "deno-notebook-cell" {
|
||||||
|
return Some(&workspace_settings.typescript);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine if any code lenses are enabled at all. This allows short
|
||||||
|
/// circuiting when there are no code lenses enabled.
|
||||||
|
pub fn enabled_code_lens_for_specifier(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> bool {
|
||||||
|
let settings = self.workspace_settings_for_specifier(specifier);
|
||||||
|
settings.code_lens.implementations
|
||||||
|
|| settings.code_lens.references
|
||||||
|
|| settings.code_lens.test
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enabled_code_lens_test_for_specifier(
|
||||||
|
&self,
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
) -> bool {
|
||||||
|
let settings = self.workspace_settings_for_specifier(specifier);
|
||||||
|
settings.code_lens.test
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_uri(&self) -> Option<&Url> {
|
pub fn root_uri(&self) -> Option<&Url> {
|
||||||
self.workspace_folders.get(0).map(|p| &p.0)
|
self.workspace_folders.get(0).map(|p| &p.0)
|
||||||
}
|
}
|
||||||
|
@ -949,20 +1010,6 @@ impl Config {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn workspace_settings(&self) -> &WorkspaceSettings {
|
|
||||||
&self.settings.workspace
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the workspace settings directly, which occurs during initialization
|
|
||||||
/// and when the client does not support workspace configuration requests
|
|
||||||
pub fn set_workspace_settings(&mut self, settings: WorkspaceSettings) {
|
|
||||||
self.settings.workspace = settings;
|
|
||||||
// See https://github.com/denoland/vscode_deno/issues/908.
|
|
||||||
if self.settings.workspace.enable_paths == Some(vec![]) {
|
|
||||||
self.settings.workspace.enable_paths = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn snapshot(&self) -> Arc<ConfigSnapshot> {
|
pub fn snapshot(&self) -> Arc<ConfigSnapshot> {
|
||||||
Arc::new(ConfigSnapshot {
|
Arc::new(ConfigSnapshot {
|
||||||
client_capabilities: self.client_capabilities.clone(),
|
client_capabilities: self.client_capabilities.clone(),
|
||||||
|
@ -972,10 +1019,6 @@ impl Config {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_specifier_settings(&self, specifier: &ModuleSpecifier) -> bool {
|
|
||||||
self.settings.specifiers.contains_key(specifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
specifier_enabled(
|
specifier_enabled(
|
||||||
specifier,
|
specifier,
|
||||||
|
@ -1009,11 +1052,8 @@ impl Config {
|
||||||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let specifier_settings = self.settings.specifiers.get(workspace_uri);
|
let settings = self.workspace_settings_for_specifier(workspace_uri);
|
||||||
let enable_paths = specifier_settings
|
if let Some(enable_paths) = &settings.enable_paths {
|
||||||
.and_then(|s| s.enable_paths.as_ref())
|
|
||||||
.or(self.settings.workspace.enable_paths.as_ref());
|
|
||||||
if let Some(enable_paths) = enable_paths {
|
|
||||||
for path in enable_paths {
|
for path in enable_paths {
|
||||||
paths.push(workspace_path.join(path));
|
paths.push(workspace_path.join(path));
|
||||||
}
|
}
|
||||||
|
@ -1035,25 +1075,14 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let root_enable = self
|
|
||||||
.settings
|
|
||||||
.workspace
|
|
||||||
.enable
|
|
||||||
.unwrap_or(self.has_config_file());
|
|
||||||
for (workspace_uri, _) in &self.workspace_folders {
|
for (workspace_uri, _) in &self.workspace_folders {
|
||||||
let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
|
let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
|
||||||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let specifier_settings = self.settings.specifiers.get(workspace_uri);
|
let settings = self.workspace_settings_for_specifier(workspace_uri);
|
||||||
let enable = specifier_settings
|
if settings.enable.unwrap_or_else(|| self.has_config_file()) {
|
||||||
.and_then(|s| s.enable)
|
for path in &settings.disable_paths {
|
||||||
.unwrap_or(root_enable);
|
|
||||||
if enable {
|
|
||||||
let disable_paths = specifier_settings
|
|
||||||
.map(|s| &s.disable_paths)
|
|
||||||
.unwrap_or(&self.settings.workspace.disable_paths);
|
|
||||||
for path in disable_paths {
|
|
||||||
paths.push(workspace_path.join(path));
|
paths.push(workspace_path.join(path));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1065,16 +1094,6 @@ impl Config {
|
||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn specifier_code_lens_test(&self, specifier: &ModuleSpecifier) -> bool {
|
|
||||||
let value = self
|
|
||||||
.settings
|
|
||||||
.specifiers
|
|
||||||
.get(specifier)
|
|
||||||
.map(|settings| settings.code_lens.test)
|
|
||||||
.unwrap_or_else(|| self.settings.workspace.code_lens.test);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_capabilities(
|
pub fn update_capabilities(
|
||||||
&mut self,
|
&mut self,
|
||||||
capabilities: &lsp::ClientCapabilities,
|
capabilities: &lsp::ClientCapabilities,
|
||||||
|
@ -1127,37 +1146,13 @@ impl Config {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_specifiers(&self) -> Vec<ModuleSpecifier> {
|
|
||||||
self.settings.specifiers.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_specifier_settings(
|
|
||||||
&mut self,
|
|
||||||
specifier: ModuleSpecifier,
|
|
||||||
mut settings: SpecifierSettings,
|
|
||||||
) -> bool {
|
|
||||||
// See https://github.com/denoland/vscode_deno/issues/908.
|
|
||||||
if settings.enable_paths == Some(vec![]) {
|
|
||||||
settings.enable_paths = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(existing) = self.settings.specifiers.get(&specifier) {
|
|
||||||
if *existing == settings {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.settings.specifiers.insert(specifier, settings);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn specifier_enabled(
|
fn specifier_enabled(
|
||||||
specifier: &Url,
|
specifier: &Url,
|
||||||
config_file: Option<&ConfigFile>,
|
config_file: Option<&ConfigFile>,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
workspace_folders: &Vec<(Url, lsp::WorkspaceFolder)>,
|
workspace_folders: &[(Url, lsp::WorkspaceFolder)],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some(cf) = config_file {
|
if let Some(cf) = config_file {
|
||||||
if let Some(files) = cf.to_files_config().ok().flatten() {
|
if let Some(files) = cf.to_files_config().ok().flatten() {
|
||||||
|
@ -1166,55 +1161,42 @@ fn specifier_enabled(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_enable = settings.workspace.enable.unwrap_or(config_file.is_some());
|
|
||||||
if let Some(settings) = settings.specifiers.get(specifier) {
|
|
||||||
// TODO(nayeemrmn): We don't know from where to resolve path lists in this
|
|
||||||
// case. If they're detected, instead defer to workspace scopes.
|
|
||||||
if settings.enable_paths.is_none() && settings.disable_paths.is_empty() {
|
|
||||||
return settings.enable.unwrap_or(root_enable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let Ok(path) = specifier_to_file_path(specifier) else {
|
let Ok(path) = specifier_to_file_path(specifier) else {
|
||||||
// Non-file URLs are not disabled by these settings.
|
// Non-file URLs are not disabled by these settings.
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
for (workspace_uri, _) in workspace_folders {
|
let (settings, mut folder_uri) = settings.get_for_specifier(specifier);
|
||||||
let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
|
folder_uri = folder_uri.or_else(|| workspace_folders.get(0).map(|f| &f.0));
|
||||||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
let mut disable_paths = vec![];
|
||||||
continue;
|
let mut enable_paths = None;
|
||||||
};
|
if let Some(folder_uri) = folder_uri {
|
||||||
if path.starts_with(&workspace_path) {
|
if let Ok(folder_path) = specifier_to_file_path(folder_uri) {
|
||||||
let specifier_settings = settings.specifiers.get(workspace_uri);
|
disable_paths = settings
|
||||||
let disable_paths = specifier_settings
|
.disable_paths
|
||||||
.map(|s| &s.disable_paths)
|
|
||||||
.unwrap_or(&settings.workspace.disable_paths);
|
|
||||||
let resolved_disable_paths = disable_paths
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| workspace_path.join(p))
|
.map(|p| folder_path.join(p))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let enable_paths = specifier_settings
|
enable_paths = settings.enable_paths.as_ref().map(|enable_paths| {
|
||||||
.and_then(|s| s.enable_paths.as_ref())
|
enable_paths
|
||||||
.or(settings.workspace.enable_paths.as_ref());
|
.iter()
|
||||||
if let Some(enable_paths) = enable_paths {
|
.map(|p| folder_path.join(p))
|
||||||
for enable_path in enable_paths {
|
.collect::<Vec<_>>()
|
||||||
let enable_path = workspace_path.join(enable_path);
|
});
|
||||||
if path.starts_with(&enable_path)
|
|
||||||
&& !resolved_disable_paths.iter().any(|p| path.starts_with(p))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return specifier_settings
|
|
||||||
.and_then(|s| s.enable)
|
|
||||||
.unwrap_or(root_enable)
|
|
||||||
&& !resolved_disable_paths.iter().any(|p| path.starts_with(p));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
root_enable
|
if let Some(enable_paths) = &enable_paths {
|
||||||
|
for enable_path in enable_paths {
|
||||||
|
if path.starts_with(enable_path)
|
||||||
|
&& !disable_paths.iter().any(|p| path.starts_with(p))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
settings.enable.unwrap_or_else(|| config_file.is_some())
|
||||||
|
&& !disable_paths.iter().any(|p| path.starts_with(p))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> {
|
fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> {
|
||||||
|
@ -1285,6 +1267,7 @@ mod tests {
|
||||||
"enable": true
|
"enable": true
|
||||||
}))
|
}))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(config.specifier_enabled(&specifier));
|
assert!(config.specifier_enabled(&specifier));
|
||||||
}
|
}
|
||||||
|
@ -1300,6 +1283,7 @@ mod tests {
|
||||||
"enable": true
|
"enable": true
|
||||||
}))
|
}))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let config_snapshot = config.snapshot();
|
let config_snapshot = config.snapshot();
|
||||||
assert!(config_snapshot.specifier_enabled(&specifier));
|
assert!(config_snapshot.specifier_enabled(&specifier));
|
||||||
|
@ -1315,7 +1299,7 @@ mod tests {
|
||||||
assert!(!config.specifier_enabled(&specifier_b));
|
assert!(!config.specifier_enabled(&specifier_b));
|
||||||
let workspace_settings =
|
let workspace_settings =
|
||||||
serde_json::from_str(r#"{ "enablePaths": ["worker"] }"#).unwrap();
|
serde_json::from_str(r#"{ "enablePaths": ["worker"] }"#).unwrap();
|
||||||
config.set_workspace_settings(workspace_settings);
|
config.set_workspace_settings(workspace_settings, None);
|
||||||
assert!(config.specifier_enabled(&specifier_a));
|
assert!(config.specifier_enabled(&specifier_a));
|
||||||
assert!(!config.specifier_enabled(&specifier_b));
|
assert!(!config.specifier_enabled(&specifier_b));
|
||||||
let config_snapshot = config.snapshot();
|
let config_snapshot = config.snapshot();
|
||||||
|
@ -1327,10 +1311,10 @@ mod tests {
|
||||||
fn test_config_specifier_disabled_path() {
|
fn test_config_specifier_disabled_path() {
|
||||||
let root_uri = resolve_url("file:///root/").unwrap();
|
let root_uri = resolve_url("file:///root/").unwrap();
|
||||||
let mut config = Config::new_with_root(root_uri.clone());
|
let mut config = Config::new_with_root(root_uri.clone());
|
||||||
config.settings.workspace.enable = Some(true);
|
config.settings.unscoped.enable = Some(true);
|
||||||
config.settings.workspace.enable_paths =
|
config.settings.unscoped.enable_paths =
|
||||||
Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]);
|
Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]);
|
||||||
config.settings.workspace.disable_paths = vec!["mod2.ts".to_string()];
|
config.settings.unscoped.disable_paths = vec!["mod2.ts".to_string()];
|
||||||
|
|
||||||
assert!(config.specifier_enabled(&root_uri.join("mod1.ts").unwrap()));
|
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("mod2.ts").unwrap()));
|
||||||
|
@ -1340,7 +1324,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_workspace_settings_defaults() {
|
fn test_set_workspace_settings_defaults() {
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_workspace_settings(serde_json::from_value(json!({})).unwrap());
|
config
|
||||||
|
.set_workspace_settings(serde_json::from_value(json!({})).unwrap(), None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.workspace_settings().clone(),
|
config.workspace_settings().clone(),
|
||||||
WorkspaceSettings {
|
WorkspaceSettings {
|
||||||
|
@ -1472,6 +1457,7 @@ mod tests {
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_workspace_settings(
|
config.set_workspace_settings(
|
||||||
serde_json::from_value(json!({ "cache": "" })).unwrap(),
|
serde_json::from_value(json!({ "cache": "" })).unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.workspace_settings().clone(),
|
config.workspace_settings().clone(),
|
||||||
|
@ -1484,6 +1470,7 @@ mod tests {
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_workspace_settings(
|
config.set_workspace_settings(
|
||||||
serde_json::from_value(json!({ "import_map": "" })).unwrap(),
|
serde_json::from_value(json!({ "import_map": "" })).unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.workspace_settings().clone(),
|
config.workspace_settings().clone(),
|
||||||
|
@ -1496,6 +1483,7 @@ mod tests {
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_workspace_settings(
|
config.set_workspace_settings(
|
||||||
serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
|
serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.workspace_settings().clone(),
|
config.workspace_settings().clone(),
|
||||||
|
@ -1508,6 +1496,7 @@ mod tests {
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_workspace_settings(
|
config.set_workspace_settings(
|
||||||
serde_json::from_value(json!({ "config": "" })).unwrap(),
|
serde_json::from_value(json!({ "config": "" })).unwrap(),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.workspace_settings().clone(),
|
config.workspace_settings().clone(),
|
||||||
|
@ -1541,30 +1530,39 @@ mod tests {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
config.set_specifier_settings(
|
config.set_workspace_settings(
|
||||||
Url::parse("file:///root1/").unwrap(),
|
Default::default(),
|
||||||
SpecifierSettings {
|
Some(
|
||||||
enable_paths: Some(vec![
|
vec![
|
||||||
"sub_dir".to_string(),
|
(
|
||||||
"sub_dir/other".to_string(),
|
Url::parse("file:///root1/").unwrap(),
|
||||||
"test.ts".to_string(),
|
WorkspaceSettings {
|
||||||
]),
|
enable_paths: Some(vec![
|
||||||
..Default::default()
|
"sub_dir".to_string(),
|
||||||
},
|
"sub_dir/other".to_string(),
|
||||||
);
|
"test.ts".to_string(),
|
||||||
config.set_specifier_settings(
|
]),
|
||||||
Url::parse("file:///root2/").unwrap(),
|
..Default::default()
|
||||||
SpecifierSettings {
|
},
|
||||||
enable_paths: Some(vec!["other.ts".to_string()]),
|
),
|
||||||
..Default::default()
|
(
|
||||||
},
|
Url::parse("file:///root2/").unwrap(),
|
||||||
);
|
WorkspaceSettings {
|
||||||
config.set_specifier_settings(
|
enable_paths: Some(vec!["other.ts".to_string()]),
|
||||||
Url::parse("file:///root3/").unwrap(),
|
..Default::default()
|
||||||
SpecifierSettings {
|
},
|
||||||
enable: Some(true),
|
),
|
||||||
..Default::default()
|
(
|
||||||
},
|
Url::parse("file:///root3/").unwrap(),
|
||||||
|
WorkspaceSettings {
|
||||||
|
enable: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1583,7 +1581,7 @@ mod tests {
|
||||||
fn config_enable_via_config_file_detection() {
|
fn config_enable_via_config_file_detection() {
|
||||||
let root_uri = resolve_url("file:///root/").unwrap();
|
let root_uri = resolve_url("file:///root/").unwrap();
|
||||||
let mut config = Config::new_with_root(root_uri.clone());
|
let mut config = Config::new_with_root(root_uri.clone());
|
||||||
config.settings.workspace.enable = None;
|
config.settings.unscoped.enable = None;
|
||||||
assert!(!config.specifier_enabled(&root_uri));
|
assert!(!config.specifier_enabled(&root_uri));
|
||||||
|
|
||||||
config.set_config_file(
|
config.set_config_file(
|
||||||
|
@ -1597,7 +1595,7 @@ mod tests {
|
||||||
fn config_specifier_enabled_matches_by_path_component() {
|
fn config_specifier_enabled_matches_by_path_component() {
|
||||||
let root_uri = resolve_url("file:///root/").unwrap();
|
let root_uri = resolve_url("file:///root/").unwrap();
|
||||||
let mut config = Config::new_with_root(root_uri.clone());
|
let mut config = Config::new_with_root(root_uri.clone());
|
||||||
config.settings.workspace.enable_paths = Some(vec!["mo".to_string()]);
|
config.settings.unscoped.enable_paths = Some(vec!["mo".to_string()]);
|
||||||
assert!(!config.specifier_enabled(&root_uri.join("mod.ts").unwrap()));
|
assert!(!config.specifier_enabled(&root_uri.join("mod.ts").unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1605,11 +1603,11 @@ mod tests {
|
||||||
fn config_specifier_enabled_for_test() {
|
fn config_specifier_enabled_for_test() {
|
||||||
let root_uri = resolve_url("file:///root/").unwrap();
|
let root_uri = resolve_url("file:///root/").unwrap();
|
||||||
let mut config = Config::new_with_root(root_uri.clone());
|
let mut config = Config::new_with_root(root_uri.clone());
|
||||||
config.settings.workspace.enable = Some(true);
|
config.settings.unscoped.enable = Some(true);
|
||||||
|
|
||||||
config.settings.workspace.enable_paths =
|
config.settings.unscoped.enable_paths =
|
||||||
Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]);
|
Some(vec!["mod1.ts".to_string(), "mod2.ts".to_string()]);
|
||||||
config.settings.workspace.disable_paths = vec!["mod2.ts".to_string()];
|
config.settings.unscoped.disable_paths = vec!["mod2.ts".to_string()];
|
||||||
assert!(
|
assert!(
|
||||||
config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap())
|
config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap())
|
||||||
);
|
);
|
||||||
|
@ -1619,7 +1617,7 @@ mod tests {
|
||||||
assert!(
|
assert!(
|
||||||
!config.specifier_enabled_for_test(&root_uri.join("mod3.ts").unwrap())
|
!config.specifier_enabled_for_test(&root_uri.join("mod3.ts").unwrap())
|
||||||
);
|
);
|
||||||
config.settings.workspace.enable_paths = None;
|
config.settings.unscoped.enable_paths = None;
|
||||||
|
|
||||||
config.set_config_file(
|
config.set_config_file(
|
||||||
ConfigFile::new(
|
ConfigFile::new(
|
||||||
|
@ -1688,7 +1686,7 @@ mod tests {
|
||||||
fn config_snapshot_specifier_enabled_for_test() {
|
fn config_snapshot_specifier_enabled_for_test() {
|
||||||
let root_uri = resolve_url("file:///root/").unwrap();
|
let root_uri = resolve_url("file:///root/").unwrap();
|
||||||
let mut config = Config::new_with_root(root_uri.clone());
|
let mut config = Config::new_with_root(root_uri.clone());
|
||||||
config.settings.workspace.enable = Some(true);
|
config.settings.unscoped.enable = Some(true);
|
||||||
config.set_config_file(
|
config.set_config_file(
|
||||||
ConfigFile::new(
|
ConfigFile::new(
|
||||||
&json!({
|
&json!({
|
||||||
|
|
|
@ -788,37 +788,37 @@ fn generate_lint_diagnostics(
|
||||||
let documents = snapshot
|
let documents = snapshot
|
||||||
.documents
|
.documents
|
||||||
.documents(DocumentsFilter::OpenDiagnosable);
|
.documents(DocumentsFilter::OpenDiagnosable);
|
||||||
let workspace_settings = config.settings.workspace.clone();
|
|
||||||
let lint_rules = get_configured_rules(lint_options.rules.clone());
|
let lint_rules = get_configured_rules(lint_options.rules.clone());
|
||||||
let mut diagnostics_vec = Vec::new();
|
let mut diagnostics_vec = Vec::new();
|
||||||
if workspace_settings.lint {
|
for document in documents {
|
||||||
for document in documents {
|
let settings =
|
||||||
// exit early if cancelled
|
config.workspace_settings_for_specifier(document.specifier());
|
||||||
if token.is_cancelled() {
|
if !settings.lint {
|
||||||
break;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// ignore any npm package files
|
|
||||||
if let Some(npm) = &snapshot.npm {
|
|
||||||
if npm.node_resolver.in_npm_package(document.specifier()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = document.maybe_lsp_version();
|
|
||||||
diagnostics_vec.push(DiagnosticRecord {
|
|
||||||
specifier: document.specifier().clone(),
|
|
||||||
versioned: VersionedDiagnostics {
|
|
||||||
version,
|
|
||||||
diagnostics: generate_document_lint_diagnostics(
|
|
||||||
config,
|
|
||||||
lint_options,
|
|
||||||
lint_rules.clone(),
|
|
||||||
&document,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// exit early if cancelled
|
||||||
|
if token.is_cancelled() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ignore any npm package files
|
||||||
|
if let Some(npm) = &snapshot.npm {
|
||||||
|
if npm.node_resolver.in_npm_package(document.specifier()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let version = document.maybe_lsp_version();
|
||||||
|
diagnostics_vec.push(DiagnosticRecord {
|
||||||
|
specifier: document.specifier().clone(),
|
||||||
|
versioned: VersionedDiagnostics {
|
||||||
|
version,
|
||||||
|
diagnostics: generate_document_lint_diagnostics(
|
||||||
|
config,
|
||||||
|
lint_options,
|
||||||
|
lint_rules.clone(),
|
||||||
|
&document,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
diagnostics_vec
|
diagnostics_vec
|
||||||
}
|
}
|
||||||
|
@ -1442,7 +1442,6 @@ mod tests {
|
||||||
use crate::cache::RealDenoCacheEnv;
|
use crate::cache::RealDenoCacheEnv;
|
||||||
use crate::lsp::config::ConfigSnapshot;
|
use crate::lsp::config::ConfigSnapshot;
|
||||||
use crate::lsp::config::Settings;
|
use crate::lsp::config::Settings;
|
||||||
use crate::lsp::config::SpecifierSettings;
|
|
||||||
use crate::lsp::config::WorkspaceSettings;
|
use crate::lsp::config::WorkspaceSettings;
|
||||||
use crate::lsp::documents::Documents;
|
use crate::lsp::documents::Documents;
|
||||||
use crate::lsp::documents::LanguageId;
|
use crate::lsp::documents::LanguageId;
|
||||||
|
@ -1497,7 +1496,7 @@ mod tests {
|
||||||
let root_uri = resolve_url("file:///").unwrap();
|
let root_uri = resolve_url("file:///").unwrap();
|
||||||
ConfigSnapshot {
|
ConfigSnapshot {
|
||||||
settings: Settings {
|
settings: Settings {
|
||||||
workspace: WorkspaceSettings {
|
unscoped: WorkspaceSettings {
|
||||||
enable: Some(true),
|
enable: Some(true),
|
||||||
lint: true,
|
lint: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -1529,7 +1528,6 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_enabled_then_disabled_specifier() {
|
async fn test_enabled_then_disabled_specifier() {
|
||||||
let temp_dir = TempDir::new();
|
let temp_dir = TempDir::new();
|
||||||
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
|
|
||||||
let (snapshot, cache_location) = setup(
|
let (snapshot, cache_location) = setup(
|
||||||
&temp_dir,
|
&temp_dir,
|
||||||
&[(
|
&[(
|
||||||
|
@ -1578,15 +1576,10 @@ let c: number = "a";
|
||||||
// now test disabled specifier
|
// now test disabled specifier
|
||||||
{
|
{
|
||||||
let mut disabled_config = mock_config();
|
let mut disabled_config = mock_config();
|
||||||
disabled_config.settings.specifiers.insert(
|
disabled_config.settings.unscoped = WorkspaceSettings {
|
||||||
specifier.clone(),
|
enable: Some(false),
|
||||||
SpecifierSettings {
|
..Default::default()
|
||||||
enable: Some(false),
|
};
|
||||||
disable_paths: vec![],
|
|
||||||
enable_paths: None,
|
|
||||||
code_lens: Default::default(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let diagnostics = generate_lint_diagnostics(
|
let diagnostics = generate_lint_diagnostics(
|
||||||
&snapshot,
|
&snapshot,
|
||||||
|
|
|
@ -23,6 +23,7 @@ use import_map::ImportMap;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use log::error;
|
use log::error;
|
||||||
use serde_json::from_value;
|
use serde_json::from_value;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -80,7 +81,6 @@ use super::tsc::AssetsSnapshot;
|
||||||
use super::tsc::GetCompletionDetailsArgs;
|
use super::tsc::GetCompletionDetailsArgs;
|
||||||
use super::tsc::TsServer;
|
use super::tsc::TsServer;
|
||||||
use super::urls;
|
use super::urls;
|
||||||
use super::urls::LspClientUrl;
|
|
||||||
use crate::args::get_root_cert_store;
|
use crate::args::get_root_cert_store;
|
||||||
use crate::args::package_json;
|
use crate::args::package_json;
|
||||||
use crate::args::resolve_import_map_from_specifier;
|
use crate::args::resolve_import_map_from_specifier;
|
||||||
|
@ -403,68 +403,42 @@ impl LanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh_specifiers_from_client(&self) -> bool {
|
pub async fn refresh_configuration(&self) {
|
||||||
let (client, specifiers) = {
|
let (client, folders, capable) = {
|
||||||
let ls = self.0.read().await;
|
let ls = self.0.read().await;
|
||||||
let specifiers = if ls.config.client_capabilities.workspace_configuration
|
(
|
||||||
{
|
ls.client.clone(),
|
||||||
let root_capacity = std::cmp::max(ls.config.workspace_folders.len(), 1);
|
ls.config.workspace_folders.clone(),
|
||||||
let config_specifiers = ls.config.get_specifiers();
|
ls.config.client_capabilities.workspace_configuration,
|
||||||
let mut specifiers =
|
)
|
||||||
HashMap::with_capacity(root_capacity + config_specifiers.len());
|
|
||||||
for (specifier, folder) in &ls.config.workspace_folders {
|
|
||||||
specifiers
|
|
||||||
.insert(specifier.clone(), LspClientUrl::new(folder.uri.clone()));
|
|
||||||
}
|
|
||||||
specifiers.extend(
|
|
||||||
ls.config
|
|
||||||
.get_specifiers()
|
|
||||||
.iter()
|
|
||||||
.map(|s| (s.clone(), ls.url_map.normalize_specifier(s).unwrap())),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(specifiers.into_iter().collect::<Vec<_>>())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
(ls.client.clone(), specifiers)
|
|
||||||
};
|
};
|
||||||
|
if capable {
|
||||||
let mut touched = false;
|
let mut scopes = Vec::with_capacity(folders.len() + 1);
|
||||||
if let Some(specifiers) = specifiers {
|
scopes.push(None);
|
||||||
let configs_result = client
|
for (_, folder) in &folders {
|
||||||
|
scopes.push(Some(folder.uri.clone()));
|
||||||
|
}
|
||||||
|
let configs = client
|
||||||
.when_outside_lsp_lock()
|
.when_outside_lsp_lock()
|
||||||
.specifier_configurations(
|
.workspace_configuration(scopes)
|
||||||
specifiers
|
|
||||||
.iter()
|
|
||||||
.map(|(_, client_uri)| client_uri.clone())
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
if let Ok(configs) = configs {
|
||||||
let mut ls = self.0.write().await;
|
if configs.len() != folders.len() + 1 {
|
||||||
if let Ok(configs) = configs_result {
|
lsp_warn!("Incorrect number of configurations received.");
|
||||||
for (value, internal_uri) in
|
return;
|
||||||
configs.into_iter().zip(specifiers.into_iter().map(|s| s.0))
|
|
||||||
{
|
|
||||||
match value {
|
|
||||||
Ok(specifier_settings) => {
|
|
||||||
if ls
|
|
||||||
.config
|
|
||||||
.set_specifier_settings(internal_uri, specifier_settings)
|
|
||||||
{
|
|
||||||
touched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let mut configs = configs.into_iter();
|
||||||
|
let unscoped = configs.next().unwrap();
|
||||||
|
let mut by_workspace_folder = BTreeMap::new();
|
||||||
|
for (folder_uri, _) in &folders {
|
||||||
|
by_workspace_folder
|
||||||
|
.insert(folder_uri.clone(), configs.next().unwrap());
|
||||||
|
}
|
||||||
|
let mut ls = self.0.write().await;
|
||||||
|
ls.config
|
||||||
|
.set_workspace_settings(unscoped, Some(by_workspace_folder));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
touched
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1182,6 +1156,7 @@ impl Inner {
|
||||||
if let Some(options) = params.initialization_options {
|
if let Some(options) = params.initialization_options {
|
||||||
self.config.set_workspace_settings(
|
self.config.set_workspace_settings(
|
||||||
WorkspaceSettings::from_initialization_options(options),
|
WorkspaceSettings::from_initialization_options(options),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Some(folders) = params.workspace_folders {
|
if let Some(folders) = params.workspace_folders {
|
||||||
|
@ -1391,27 +1366,22 @@ impl Inner {
|
||||||
|
|
||||||
async fn did_change_configuration(
|
async fn did_change_configuration(
|
||||||
&mut self,
|
&mut self,
|
||||||
client_workspace_config: Option<WorkspaceSettings>,
|
|
||||||
params: DidChangeConfigurationParams,
|
params: DidChangeConfigurationParams,
|
||||||
) {
|
) {
|
||||||
let maybe_config =
|
if !self.config.client_capabilities.workspace_configuration {
|
||||||
if self.config.client_capabilities.workspace_configuration {
|
let config = params.settings.as_object().map(|settings| {
|
||||||
client_workspace_config
|
let deno =
|
||||||
} else {
|
serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
|
||||||
params.settings.as_object().map(|settings| {
|
let javascript =
|
||||||
let deno =
|
serde_json::to_value(settings.get("javascript")).unwrap();
|
||||||
serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
|
let typescript =
|
||||||
let javascript =
|
serde_json::to_value(settings.get("typescript")).unwrap();
|
||||||
serde_json::to_value(settings.get("javascript")).unwrap();
|
WorkspaceSettings::from_raw_settings(deno, javascript, typescript)
|
||||||
let typescript =
|
});
|
||||||
serde_json::to_value(settings.get("typescript")).unwrap();
|
if let Some(settings) = config {
|
||||||
WorkspaceSettings::from_raw_settings(deno, javascript, typescript)
|
self.config.set_workspace_settings(settings, None);
|
||||||
})
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(settings) = maybe_config {
|
|
||||||
self.config.set_workspace_settings(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update_debug_flag();
|
self.update_debug_flag();
|
||||||
if let Err(err) = self.update_cache().await {
|
if let Err(err) = self.update_cache().await {
|
||||||
|
@ -2148,8 +2118,7 @@ impl Inner {
|
||||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||||
if !self.is_diagnosable(&specifier)
|
if !self.is_diagnosable(&specifier)
|
||||||
|| !self.config.specifier_enabled(&specifier)
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|| !(self.config.workspace_settings().enabled_code_lens()
|
|| !self.config.enabled_code_lens_for_specifier(&specifier)
|
||||||
|| self.config.specifier_code_lens_test(&specifier))
|
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
@ -2385,10 +2354,8 @@ impl Inner {
|
||||||
¶ms.text_document_position.text_document.uri,
|
¶ms.text_document_position.text_document.uri,
|
||||||
LspUrlKind::File,
|
LspUrlKind::File,
|
||||||
);
|
);
|
||||||
let language_settings = self
|
let language_settings =
|
||||||
.config
|
self.config.language_settings_for_specifier(&specifier);
|
||||||
.workspace_settings()
|
|
||||||
.language_settings_for_specifier(&specifier);
|
|
||||||
if !self.is_diagnosable(&specifier)
|
if !self.is_diagnosable(&specifier)
|
||||||
|| !self.config.specifier_enabled(&specifier)
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|| !language_settings.map(|s| s.suggest.enabled).unwrap_or(true)
|
|| !language_settings.map(|s| s.suggest.enabled).unwrap_or(true)
|
||||||
|
@ -2457,7 +2424,6 @@ impl Inner {
|
||||||
line_index,
|
line_index,
|
||||||
&self
|
&self
|
||||||
.config
|
.config
|
||||||
.workspace_settings()
|
|
||||||
.language_settings_for_specifier(&specifier)
|
.language_settings_for_specifier(&specifier)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
@ -2992,7 +2958,6 @@ impl Inner {
|
||||||
);
|
);
|
||||||
let options = self
|
let options = self
|
||||||
.config
|
.config
|
||||||
.workspace_settings()
|
|
||||||
.language_settings_for_specifier(&old_specifier)
|
.language_settings_for_specifier(&old_specifier)
|
||||||
.map(|s| s.update_imports_on_file_move.clone())
|
.map(|s| s.update_imports_on_file_move.clone())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
@ -3188,7 +3153,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.refresh_specifiers_from_client().await;
|
self.refresh_configuration().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut ls = self.0.write().await;
|
let mut ls = self.0.write().await;
|
||||||
|
@ -3212,58 +3177,17 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (client, client_uri, specifier, should_get_specifier_settings) = {
|
let mut inner = self.0.write().await;
|
||||||
let mut inner = self.0.write().await;
|
let specifier = inner
|
||||||
let client = inner.client.clone();
|
.url_map
|
||||||
let client_uri = LspClientUrl::new(params.text_document.uri.clone());
|
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||||
let specifier = inner
|
let document = inner.did_open(&specifier, params).await;
|
||||||
.url_map
|
if document.is_diagnosable() {
|
||||||
.normalize_url(client_uri.as_url(), LspUrlKind::File);
|
inner.refresh_npm_specifiers().await;
|
||||||
let document = inner.did_open(&specifier, params).await;
|
let specifiers = inner.documents.dependents(&specifier);
|
||||||
let should_get_specifier_settings =
|
inner.diagnostics_server.invalidate(&specifiers);
|
||||||
!inner.config.has_specifier_settings(&specifier)
|
inner.send_diagnostics_update();
|
||||||
&& inner.config.client_capabilities.workspace_configuration;
|
inner.send_testing_update();
|
||||||
if document.is_diagnosable() {
|
|
||||||
inner.refresh_npm_specifiers().await;
|
|
||||||
let specifiers = inner.documents.dependents(&specifier);
|
|
||||||
inner.diagnostics_server.invalidate(&specifiers);
|
|
||||||
// don't send diagnostics yet if we don't have the specifier settings
|
|
||||||
if !should_get_specifier_settings {
|
|
||||||
inner.send_diagnostics_update();
|
|
||||||
inner.send_testing_update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(client, client_uri, specifier, should_get_specifier_settings)
|
|
||||||
};
|
|
||||||
|
|
||||||
// retrieve the specifier settings outside the lock if
|
|
||||||
// they haven't been asked for yet
|
|
||||||
if should_get_specifier_settings {
|
|
||||||
let response = client
|
|
||||||
.when_outside_lsp_lock()
|
|
||||||
.specifier_configuration(&client_uri)
|
|
||||||
.await;
|
|
||||||
let mut ls = self.0.write().await;
|
|
||||||
match response {
|
|
||||||
Ok(specifier_settings) => {
|
|
||||||
ls.config
|
|
||||||
.set_specifier_settings(specifier.clone(), specifier_settings);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ls
|
|
||||||
.documents
|
|
||||||
.get(&specifier)
|
|
||||||
.map(|d| d.is_diagnosable())
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
ls.refresh_documents_config().await;
|
|
||||||
ls.send_diagnostics_update();
|
|
||||||
ls.send_testing_update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3277,7 +3201,10 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
let mut inner = self.0.write().await;
|
let mut inner = self.0.write().await;
|
||||||
let specifier = inner.url_map.normalize_url(uri, LspUrlKind::File);
|
let specifier = inner.url_map.normalize_url(uri, LspUrlKind::File);
|
||||||
inner.documents.save(&specifier);
|
inner.documents.save(&specifier);
|
||||||
if !inner.config.workspace_settings().cache_on_save
|
if !inner
|
||||||
|
.config
|
||||||
|
.workspace_settings_for_specifier(&specifier)
|
||||||
|
.cache_on_save
|
||||||
|| !inner.config.specifier_enabled(&specifier)
|
|| !inner.config.specifier_enabled(&specifier)
|
||||||
|| !inner.diagnostics_state.has_no_cache_diagnostics(&specifier)
|
|| !inner.diagnostics_state.has_no_cache_diagnostics(&specifier)
|
||||||
{
|
{
|
||||||
|
@ -3310,47 +3237,17 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
&self,
|
&self,
|
||||||
params: DidChangeConfigurationParams,
|
params: DidChangeConfigurationParams,
|
||||||
) {
|
) {
|
||||||
let (mark, has_workspace_capability, client) = {
|
let mark = {
|
||||||
let inner = self.0.read().await;
|
let inner = self.0.read().await;
|
||||||
(
|
inner
|
||||||
inner
|
.performance
|
||||||
.performance
|
.mark("did_change_configuration", Some(¶ms))
|
||||||
.mark("did_change_configuration", Some(¶ms)),
|
|
||||||
inner.config.client_capabilities.workspace_configuration,
|
|
||||||
inner.client.clone(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.refresh_specifiers_from_client().await;
|
self.refresh_configuration().await;
|
||||||
|
|
||||||
// Get the configuration from the client outside of the lock
|
|
||||||
// in order to prevent potential deadlocking scenarios where
|
|
||||||
// the server holds a lock and calls into the client, which
|
|
||||||
// calls into the server which deadlocks acquiring the lock.
|
|
||||||
// There is a gap here between when the configuration is
|
|
||||||
// received and acquiring the lock, but most likely there
|
|
||||||
// won't be any racing here.
|
|
||||||
let client_workspace_config = if has_workspace_capability {
|
|
||||||
let config_response = client
|
|
||||||
.when_outside_lsp_lock()
|
|
||||||
.workspace_configuration()
|
|
||||||
.await;
|
|
||||||
match config_response {
|
|
||||||
Ok(settings) => Some(settings),
|
|
||||||
Err(err) => {
|
|
||||||
error!("{}", err);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// now update the inner state
|
|
||||||
let mut inner = self.0.write().await;
|
let mut inner = self.0.write().await;
|
||||||
inner
|
inner.did_change_configuration(params).await;
|
||||||
.did_change_configuration(client_workspace_config, params)
|
|
||||||
.await;
|
|
||||||
inner.performance.measure(mark);
|
inner.performance.measure(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3374,7 +3271,8 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
||||||
(ls.performance.clone(), mark)
|
(ls.performance.clone(), mark)
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.refresh_specifiers_from_client().await {
|
self.refresh_configuration().await;
|
||||||
|
{
|
||||||
let mut ls = self.0.write().await;
|
let mut ls = self.0.write().await;
|
||||||
ls.refresh_documents_config().await;
|
ls.refresh_documents_config().await;
|
||||||
ls.diagnostics_server.invalidate_all();
|
ls.diagnostics_server.invalidate_all();
|
||||||
|
@ -3681,10 +3579,7 @@ impl Inner {
|
||||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||||
if !self.is_diagnosable(&specifier)
|
if !self.is_diagnosable(&specifier)
|
||||||
|| !self.config.specifier_enabled(&specifier)
|
|| !self.config.specifier_enabled(&specifier)
|
||||||
|| !self
|
|| !self.config.enabled_inlay_hints_for_specifier(&specifier)
|
||||||
.config
|
|
||||||
.workspace_settings()
|
|
||||||
.enabled_inlay_hints(&specifier)
|
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4215,9 +4215,8 @@ impl UserPreferences {
|
||||||
use_label_details_in_completion_entries: Some(true),
|
use_label_details_in_completion_entries: Some(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let Some(language_settings) = config
|
let Some(language_settings) =
|
||||||
.workspace_settings()
|
config.language_settings_for_specifier(specifier)
|
||||||
.language_settings_for_specifier(specifier)
|
|
||||||
else {
|
else {
|
||||||
return base_preferences;
|
return base_preferences;
|
||||||
};
|
};
|
||||||
|
@ -5312,7 +5311,7 @@ mod tests {
|
||||||
.variable_types
|
.variable_types
|
||||||
.suppress_when_type_matches_name = true;
|
.suppress_when_type_matches_name = true;
|
||||||
let mut config = config::Config::new();
|
let mut config = config::Config::new();
|
||||||
config.set_workspace_settings(settings);
|
config.set_workspace_settings(settings, None);
|
||||||
let user_preferences = UserPreferences::from_config_for_specifier(
|
let user_preferences = UserPreferences::from_config_for_specifier(
|
||||||
&config,
|
&config,
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
|
|
|
@ -1024,23 +1024,23 @@ fn lsp_import_attributes() {
|
||||||
client.initialize(|builder| {
|
client.initialize(|builder| {
|
||||||
builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}");
|
builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}");
|
||||||
});
|
});
|
||||||
|
client.change_configuration(json!({
|
||||||
client.did_open_with_config(
|
"deno": {
|
||||||
json!({
|
|
||||||
"textDocument": {
|
|
||||||
"uri": "file:///a/test.json",
|
|
||||||
"languageId": "json",
|
|
||||||
"version": 1,
|
|
||||||
"text": "{\"a\":1}"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
&json!({ "deno": {
|
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"test": true
|
"test": true,
|
||||||
}
|
},
|
||||||
} }),
|
},
|
||||||
);
|
}));
|
||||||
|
|
||||||
|
client.did_open(json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": "file:///a/test.json",
|
||||||
|
"languageId": "json",
|
||||||
|
"version": 1,
|
||||||
|
"text": "{\"a\":1}",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
let diagnostics = client.did_open(json!({
|
let diagnostics = client.did_open(json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
|
@ -1380,17 +1380,15 @@ fn lsp_hover_disabled() {
|
||||||
client.initialize(|builder| {
|
client.initialize(|builder| {
|
||||||
builder.set_deno_enable(false);
|
builder.set_deno_enable(false);
|
||||||
});
|
});
|
||||||
client.did_open_with_config(
|
client.change_configuration(json!({ "deno": { "enable": false } }));
|
||||||
json!({
|
client.did_open(json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "file:///a/file.ts",
|
"uri": "file:///a/file.ts",
|
||||||
"languageId": "typescript",
|
"languageId": "typescript",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"text": "console.log(Date.now());\n"
|
"text": "console.log(Date.now());\n",
|
||||||
}
|
},
|
||||||
}),
|
}));
|
||||||
&json!({ "deno": { "enable": false } }),
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = client.write_request(
|
let res = client.write_request(
|
||||||
"textDocument/hover",
|
"textDocument/hover",
|
||||||
|
@ -3794,24 +3792,22 @@ fn lsp_code_lens_test_disabled() {
|
||||||
"test": false
|
"test": false
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
client
|
client.change_configuration(json!({
|
||||||
.did_open_with_config(
|
"deno": {
|
||||||
json!({
|
"enable": true,
|
||||||
"textDocument": {
|
"codeLens": {
|
||||||
"uri": "file:///a/file.ts",
|
"test": false,
|
||||||
"languageId": "typescript",
|
},
|
||||||
"version": 1,
|
},
|
||||||
"text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n"
|
}));
|
||||||
}
|
client.did_open(json!({
|
||||||
}),
|
"textDocument": {
|
||||||
// disable test code lens
|
"uri": "file:///a/file.ts",
|
||||||
&json!({ "deno": {
|
"languageId": "typescript",
|
||||||
"enable": true,
|
"version": 1,
|
||||||
"codeLens": {
|
"text": "const { test } = Deno;\nconst { test: test2 } = Deno;\nconst test3 = Deno.test;\n\nDeno.test(\"test a\", () => {});\nDeno.test({\n name: \"test b\",\n fn() {},\n});\ntest({\n name: \"test c\",\n fn() {},\n});\ntest(\"test d\", () => {});\ntest2({\n name: \"test e\",\n fn() {},\n});\ntest2(\"test f\", () => {});\ntest3({\n name: \"test g\",\n fn() {},\n});\ntest3(\"test h\", () => {});\n"
|
||||||
"test": false
|
},
|
||||||
}
|
}));
|
||||||
} }),
|
|
||||||
);
|
|
||||||
let res = client.write_request(
|
let res = client.write_request(
|
||||||
"textDocument/codeLens",
|
"textDocument/codeLens",
|
||||||
json!({
|
json!({
|
||||||
|
@ -3820,7 +3816,7 @@ fn lsp_code_lens_test_disabled() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
assert_eq!(res, json!([]));
|
assert_eq!(res, json!(null));
|
||||||
client.shutdown();
|
client.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4898,22 +4894,12 @@ fn lsp_cache_on_save() {
|
||||||
);
|
);
|
||||||
let mut client = context.new_lsp_command().build();
|
let mut client = context.new_lsp_command().build();
|
||||||
client.initialize_default();
|
client.initialize_default();
|
||||||
client.write_notification(
|
client.change_configuration(json!({
|
||||||
"workspace/didChangeConfiguration",
|
|
||||||
json!({
|
|
||||||
"settings": {}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let settings = json!({
|
|
||||||
"deno": {
|
"deno": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"cacheOnSave": true,
|
"cacheOnSave": true,
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
|
|
||||||
let diagnostics = client.did_open(json!({
|
let diagnostics = client.did_open(json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
|
@ -5592,23 +5578,16 @@ fn lsp_quote_style_from_workspace_settings() {
|
||||||
);
|
);
|
||||||
let mut client = context.new_lsp_command().build();
|
let mut client = context.new_lsp_command().build();
|
||||||
client.initialize_default();
|
client.initialize_default();
|
||||||
client.write_notification(
|
client.change_configuration(json!({
|
||||||
"workspace/didChangeConfiguration",
|
"deno": {
|
||||||
json!({
|
"enable": true,
|
||||||
"settings": {}
|
},
|
||||||
}),
|
|
||||||
);
|
|
||||||
let settings = json!({
|
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"preferences": {
|
"preferences": {
|
||||||
"quoteStyle": "single",
|
"quoteStyle": "single",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
|
|
||||||
let code_action_params = json!({
|
let code_action_params = json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
|
@ -5792,7 +5771,7 @@ fn lsp_code_actions_deadlock() {
|
||||||
let large_file_text =
|
let large_file_text =
|
||||||
fs::read_to_string(testdata_path().join("lsp").join("large_file.txt"))
|
fs::read_to_string(testdata_path().join("lsp").join("large_file.txt"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
client.did_open_raw(json!({
|
client.did_open(json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "file:///a/file.ts",
|
"uri": "file:///a/file.ts",
|
||||||
"languageId": "javascript",
|
"languageId": "javascript",
|
||||||
|
@ -5800,7 +5779,6 @@ fn lsp_code_actions_deadlock() {
|
||||||
"text": large_file_text,
|
"text": large_file_text,
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
client.handle_configuration_request(&json!({ "deno": { "enable": true } }));
|
|
||||||
client.write_request(
|
client.write_request(
|
||||||
"textDocument/semanticTokens/full",
|
"textDocument/semanticTokens/full",
|
||||||
json!({
|
json!({
|
||||||
|
@ -5809,7 +5787,6 @@ fn lsp_code_actions_deadlock() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
client.read_diagnostics();
|
|
||||||
client.write_notification(
|
client.write_notification(
|
||||||
"textDocument/didChange",
|
"textDocument/didChange",
|
||||||
json!({
|
json!({
|
||||||
|
@ -8901,17 +8878,11 @@ fn lsp_configuration_did_change() {
|
||||||
"text": "import * as a from \"http://localhost:4545/x/a@\""
|
"text": "import * as a from \"http://localhost:4545/x/a@\""
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
client.write_notification(
|
client.change_configuration(json!({ "deno": {
|
||||||
"workspace/didChangeConfiguration",
|
|
||||||
json!({
|
|
||||||
"settings": {}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let settings = json!({ "deno": {
|
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"implementations": true,
|
"implementations": true,
|
||||||
"references": true
|
"references": true,
|
||||||
},
|
},
|
||||||
"importMap": null,
|
"importMap": null,
|
||||||
"lint": true,
|
"lint": true,
|
||||||
|
@ -8922,16 +8893,12 @@ fn lsp_configuration_did_change() {
|
||||||
"paths": true,
|
"paths": true,
|
||||||
"imports": {
|
"imports": {
|
||||||
"hosts": {
|
"hosts": {
|
||||||
"http://localhost:4545/": true
|
"http://localhost:4545/": true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"unstable": false
|
"unstable": false,
|
||||||
} });
|
} }));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
|
|
||||||
let list = client.get_completion_list(
|
let list = client.get_completion_list(
|
||||||
"file:///a/file.ts",
|
"file:///a/file.ts",
|
||||||
|
@ -8997,13 +8964,7 @@ fn lsp_completions_complete_function_calls() {
|
||||||
"text": "[]."
|
"text": "[]."
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
client.write_notification(
|
client.change_configuration(json!({
|
||||||
"workspace/didChangeConfiguration",
|
|
||||||
json!({
|
|
||||||
"settings": {}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
let settings = json!({
|
|
||||||
"deno": {
|
"deno": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
},
|
},
|
||||||
|
@ -9012,11 +8973,7 @@ fn lsp_completions_complete_function_calls() {
|
||||||
"completeFunctionCalls": true,
|
"completeFunctionCalls": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
|
|
||||||
let list = client.get_completion_list(
|
let list = client.get_completion_list(
|
||||||
"file:///a/file.ts",
|
"file:///a/file.ts",
|
||||||
|
@ -10099,22 +10056,12 @@ fn lsp_node_modules_dir() {
|
||||||
"{ \"nodeModulesDir\": true, \"lock\": false }\n",
|
"{ \"nodeModulesDir\": true, \"lock\": false }\n",
|
||||||
);
|
);
|
||||||
let refresh_config = |client: &mut LspClient| {
|
let refresh_config = |client: &mut LspClient| {
|
||||||
client.write_notification(
|
client.change_configuration(json!({ "deno": {
|
||||||
"workspace/didChangeConfiguration",
|
|
||||||
json!({
|
|
||||||
"settings": {
|
|
||||||
"enable": true,
|
|
||||||
"config": "./deno.json",
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let settings = json!({ "deno": {
|
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"config": "./deno.json",
|
"config": "./deno.json",
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"implementations": true,
|
"implementations": true,
|
||||||
"references": true
|
"references": true,
|
||||||
},
|
},
|
||||||
"importMap": null,
|
"importMap": null,
|
||||||
"lint": false,
|
"lint": false,
|
||||||
|
@ -10123,14 +10070,10 @@ fn lsp_node_modules_dir() {
|
||||||
"completeFunctionCalls": false,
|
"completeFunctionCalls": false,
|
||||||
"names": true,
|
"names": true,
|
||||||
"paths": true,
|
"paths": true,
|
||||||
"imports": {}
|
"imports": {},
|
||||||
},
|
},
|
||||||
"unstable": false
|
"unstable": false,
|
||||||
} });
|
} }));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
};
|
};
|
||||||
refresh_config(&mut client);
|
refresh_config(&mut client);
|
||||||
|
|
||||||
|
@ -10228,22 +10171,12 @@ fn lsp_vendor_dir() {
|
||||||
"{ \"vendor\": true, \"lock\": false }\n",
|
"{ \"vendor\": true, \"lock\": false }\n",
|
||||||
);
|
);
|
||||||
let refresh_config = |client: &mut LspClient| {
|
let refresh_config = |client: &mut LspClient| {
|
||||||
client.write_notification(
|
client.change_configuration(json!({ "deno": {
|
||||||
"workspace/didChangeConfiguration",
|
|
||||||
json!({
|
|
||||||
"settings": {
|
|
||||||
"enable": true,
|
|
||||||
"config": "./deno.json",
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let settings = json!({ "deno": {
|
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"config": "./deno.json",
|
"config": "./deno.json",
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"implementations": true,
|
"implementations": true,
|
||||||
"references": true
|
"references": true,
|
||||||
},
|
},
|
||||||
"importMap": null,
|
"importMap": null,
|
||||||
"lint": false,
|
"lint": false,
|
||||||
|
@ -10252,14 +10185,10 @@ fn lsp_vendor_dir() {
|
||||||
"completeFunctionCalls": false,
|
"completeFunctionCalls": false,
|
||||||
"names": true,
|
"names": true,
|
||||||
"paths": true,
|
"paths": true,
|
||||||
"imports": {}
|
"imports": {},
|
||||||
},
|
},
|
||||||
"unstable": false
|
"unstable": false,
|
||||||
} });
|
} }));
|
||||||
// one for the workspace
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
// one for the specifier
|
|
||||||
client.handle_configuration_request(&settings);
|
|
||||||
};
|
};
|
||||||
refresh_config(&mut client);
|
refresh_config(&mut client);
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,22 @@ pub struct InitializeParamsBuilder {
|
||||||
|
|
||||||
impl InitializeParamsBuilder {
|
impl InitializeParamsBuilder {
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self {
|
pub fn new(config: Value) -> Self {
|
||||||
|
let mut config_as_options = json!({});
|
||||||
|
if let Some(object) = config.as_object() {
|
||||||
|
if let Some(deno) = object.get("deno") {
|
||||||
|
if let Some(deno) = deno.as_object() {
|
||||||
|
config_as_options = json!(deno.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let config_as_options = config_as_options.as_object_mut().unwrap();
|
||||||
|
if let Some(typescript) = object.get("typescript") {
|
||||||
|
config_as_options.insert("typescript".to_string(), typescript.clone());
|
||||||
|
}
|
||||||
|
if let Some(javascript) = object.get("javascript") {
|
||||||
|
config_as_options.insert("javascript".to_string(), javascript.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
params: InitializeParams {
|
params: InitializeParams {
|
||||||
process_id: None,
|
process_id: None,
|
||||||
|
@ -219,38 +234,7 @@ impl InitializeParamsBuilder {
|
||||||
version: Some("1.0.0".to_string()),
|
version: Some("1.0.0".to_string()),
|
||||||
}),
|
}),
|
||||||
root_uri: None,
|
root_uri: None,
|
||||||
initialization_options: Some(json!({
|
initialization_options: Some(config_as_options),
|
||||||
"enableBuiltinCommands": true,
|
|
||||||
"enable": true,
|
|
||||||
"cache": null,
|
|
||||||
"certificateStores": null,
|
|
||||||
"codeLens": {
|
|
||||||
"implementations": true,
|
|
||||||
"references": true,
|
|
||||||
"test": true
|
|
||||||
},
|
|
||||||
"config": null,
|
|
||||||
"importMap": null,
|
|
||||||
"lint": true,
|
|
||||||
"suggest": {
|
|
||||||
"autoImports": true,
|
|
||||||
"completeFunctionCalls": false,
|
|
||||||
"names": true,
|
|
||||||
"paths": true,
|
|
||||||
"imports": {
|
|
||||||
"hosts": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"testing": {
|
|
||||||
"args": [
|
|
||||||
"--allow-all"
|
|
||||||
],
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"tlsCertificate": null,
|
|
||||||
"unsafelyIgnoreCertificateErrors": null,
|
|
||||||
"unstable": false
|
|
||||||
})),
|
|
||||||
capabilities: ClientCapabilities {
|
capabilities: ClientCapabilities {
|
||||||
text_document: Some(TextDocumentClientCapabilities {
|
text_document: Some(TextDocumentClientCapabilities {
|
||||||
code_action: Some(CodeActionClientCapabilities {
|
code_action: Some(CodeActionClientCapabilities {
|
||||||
|
@ -686,21 +670,70 @@ impl LspClient {
|
||||||
) {
|
) {
|
||||||
self.initialize_with_config(
|
self.initialize_with_config(
|
||||||
do_build,
|
do_build,
|
||||||
json!({"deno":{
|
json!({ "deno": {
|
||||||
"enable": true
|
"enableBuiltinCommands": true,
|
||||||
}}),
|
"enable": true,
|
||||||
|
"cache": null,
|
||||||
|
"certificateStores": null,
|
||||||
|
"codeLens": {
|
||||||
|
"implementations": true,
|
||||||
|
"references": true,
|
||||||
|
"test": true,
|
||||||
|
},
|
||||||
|
"config": null,
|
||||||
|
"importMap": null,
|
||||||
|
"lint": true,
|
||||||
|
"suggest": {
|
||||||
|
"autoImports": true,
|
||||||
|
"completeFunctionCalls": false,
|
||||||
|
"names": true,
|
||||||
|
"paths": true,
|
||||||
|
"imports": {
|
||||||
|
"hosts": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"testing": {
|
||||||
|
"args": [
|
||||||
|
"--allow-all"
|
||||||
|
],
|
||||||
|
"enable": true,
|
||||||
|
},
|
||||||
|
"tlsCertificate": null,
|
||||||
|
"unsafelyIgnoreCertificateErrors": null,
|
||||||
|
"unstable": false,
|
||||||
|
} }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_with_config(
|
pub fn initialize_with_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
do_build: impl Fn(&mut InitializeParamsBuilder),
|
do_build: impl Fn(&mut InitializeParamsBuilder),
|
||||||
config: Value,
|
mut config: Value,
|
||||||
) {
|
) {
|
||||||
let mut builder = InitializeParamsBuilder::new();
|
let mut builder = InitializeParamsBuilder::new(config.clone());
|
||||||
builder.set_root_uri(self.context.temp_dir().uri());
|
builder.set_root_uri(self.context.temp_dir().uri());
|
||||||
do_build(&mut builder);
|
do_build(&mut builder);
|
||||||
let params: InitializeParams = builder.build();
|
let params: InitializeParams = builder.build();
|
||||||
|
// `config` must be updated to account for the builder changes.
|
||||||
|
// TODO(nayeemrmn): Remove config-related methods from builder.
|
||||||
|
if let Some(options) = ¶ms.initialization_options {
|
||||||
|
if let Some(options) = options.as_object() {
|
||||||
|
if let Some(config) = config.as_object_mut() {
|
||||||
|
let mut deno = options.clone();
|
||||||
|
let typescript = options.get("typescript");
|
||||||
|
let javascript = options.get("javascript");
|
||||||
|
deno.remove("typescript");
|
||||||
|
deno.remove("javascript");
|
||||||
|
config.insert("deno".to_string(), json!(deno));
|
||||||
|
if let Some(typescript) = typescript {
|
||||||
|
config.insert("typescript".to_string(), typescript.clone());
|
||||||
|
}
|
||||||
|
if let Some(javascript) = javascript {
|
||||||
|
config.insert("javascript".to_string(), javascript.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.supports_workspace_configuration = match ¶ms.capabilities.workspace
|
self.supports_workspace_configuration = match ¶ms.capabilities.workspace
|
||||||
{
|
{
|
||||||
Some(workspace) => workspace.configuration == Some(true),
|
Some(workspace) => workspace.configuration == Some(true),
|
||||||
|
@ -710,23 +743,12 @@ impl LspClient {
|
||||||
self.write_notification("initialized", json!({}));
|
self.write_notification("initialized", json!({}));
|
||||||
self.config = config;
|
self.config = config;
|
||||||
if self.supports_workspace_configuration {
|
if self.supports_workspace_configuration {
|
||||||
self.handle_configuration_request(&self.config.clone());
|
self.handle_configuration_request();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn did_open(&mut self, params: Value) -> CollectedDiagnostics {
|
pub fn did_open(&mut self, params: Value) -> CollectedDiagnostics {
|
||||||
self.did_open_with_config(params, &self.config.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn did_open_with_config(
|
|
||||||
&mut self,
|
|
||||||
params: Value,
|
|
||||||
config: &Value,
|
|
||||||
) -> CollectedDiagnostics {
|
|
||||||
self.did_open_raw(params);
|
self.did_open_raw(params);
|
||||||
if self.supports_workspace_configuration {
|
|
||||||
self.handle_configuration_request(config);
|
|
||||||
}
|
|
||||||
self.read_diagnostics()
|
self.read_diagnostics()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,17 +756,33 @@ impl LspClient {
|
||||||
self.write_notification("textDocument/didOpen", params);
|
self.write_notification("textDocument/didOpen", params);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_configuration_request(&mut self, settings: &Value) {
|
pub fn change_configuration(&mut self, config: Value) {
|
||||||
|
self.config = config;
|
||||||
|
if self.supports_workspace_configuration {
|
||||||
|
self.write_notification(
|
||||||
|
"workspace/didChangeConfiguration",
|
||||||
|
json!({ "settings": {} }),
|
||||||
|
);
|
||||||
|
self.handle_configuration_request();
|
||||||
|
} else {
|
||||||
|
self.write_notification(
|
||||||
|
"workspace/didChangeConfiguration",
|
||||||
|
json!({ "settings": &self.config }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_configuration_request(&mut self) {
|
||||||
let (id, method, args) = self.read_request::<Value>();
|
let (id, method, args) = self.read_request::<Value>();
|
||||||
assert_eq!(method, "workspace/configuration");
|
assert_eq!(method, "workspace/configuration");
|
||||||
let params = args.as_ref().unwrap().as_object().unwrap();
|
let params = args.as_ref().unwrap().as_object().unwrap();
|
||||||
let items = params.get("items").unwrap().as_array().unwrap();
|
let items = params.get("items").unwrap().as_array().unwrap();
|
||||||
let settings_object = settings.as_object().unwrap();
|
let config_object = self.config.as_object().unwrap();
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
for item in items {
|
for item in items {
|
||||||
let item = item.as_object().unwrap();
|
let item = item.as_object().unwrap();
|
||||||
let section = item.get("section").unwrap().as_str().unwrap();
|
let section = item.get("section").unwrap().as_str().unwrap();
|
||||||
result.push(settings_object.get(section).cloned().unwrap_or_default());
|
result.push(config_object.get(section).cloned().unwrap_or_default());
|
||||||
}
|
}
|
||||||
self.write_response(id, result);
|
self.write_response(id, result);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue