mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -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::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::unsync::spawn;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
use tower_lsp::lsp_types::ConfigurationItem;
|
||||
|
||||
use crate::lsp::repl::get_repl_workspace_settings;
|
||||
|
||||
use super::config::SpecifierSettings;
|
||||
use super::config::WorkspaceSettings;
|
||||
use super::config::SETTINGS_SECTION;
|
||||
use super::lsp_custom;
|
||||
|
@ -125,46 +124,11 @@ impl OutsideLockClient {
|
|||
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(
|
||||
&self,
|
||||
) -> Result<WorkspaceSettings, AnyError> {
|
||||
self.0.workspace_configuration().await
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
self.0.workspace_configuration(scopes).await
|
||||
}
|
||||
|
||||
pub async fn publish_diagnostics(
|
||||
|
@ -201,13 +165,10 @@ trait ClientTrait: Send + Sync {
|
|||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
);
|
||||
async fn specifier_configurations(
|
||||
&self,
|
||||
uris: Vec<lsp::Url>,
|
||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError>;
|
||||
async fn workspace_configuration(
|
||||
&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 register_capability(
|
||||
&self,
|
||||
|
@ -288,67 +249,50 @@ impl ClientTrait for TowerClient {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn specifier_configurations(
|
||||
async fn workspace_configuration(
|
||||
&self,
|
||||
uris: Vec<lsp::Url>,
|
||||
) -> Result<Vec<Result<SpecifierSettings, AnyError>>, AnyError> {
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
let config_response = self
|
||||
.0
|
||||
.configuration(
|
||||
uris
|
||||
.into_iter()
|
||||
.map(|uri| ConfigurationItem {
|
||||
scope_uri: Some(uri),
|
||||
section: Some(SETTINGS_SECTION.to_string()),
|
||||
})
|
||||
.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![
|
||||
scopes
|
||||
.iter()
|
||||
.flat_map(|scope_uri| {
|
||||
vec![
|
||||
ConfigurationItem {
|
||||
scope_uri: None,
|
||||
scope_uri: scope_uri.clone(),
|
||||
section: Some(SETTINGS_SECTION.to_string()),
|
||||
},
|
||||
ConfigurationItem {
|
||||
scope_uri: None,
|
||||
scope_uri: scope_uri.clone(),
|
||||
section: Some("javascript".to_string()),
|
||||
},
|
||||
ConfigurationItem {
|
||||
scope_uri: None,
|
||||
scope_uri: scope_uri.clone(),
|
||||
section: Some("typescript".to_string()),
|
||||
},
|
||||
])
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.await;
|
||||
match config_response {
|
||||
Ok(configs) => {
|
||||
let mut configs = configs.into_iter();
|
||||
let deno = serde_json::to_value(configs.next()).unwrap();
|
||||
let javascript = serde_json::to_value(configs.next()).unwrap();
|
||||
let typescript = serde_json::to_value(configs.next()).unwrap();
|
||||
Ok(WorkspaceSettings::from_raw_settings(
|
||||
let mut result = Vec::with_capacity(scopes.len());
|
||||
for _ in 0..scopes.len() {
|
||||
let deno = json!(configs.next());
|
||||
let javascript = json!(configs.next());
|
||||
let typescript = json!(configs.next());
|
||||
result.push(WorkspaceSettings::from_raw_settings(
|
||||
deno, javascript, typescript,
|
||||
))
|
||||
));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
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(
|
||||
&self,
|
||||
) -> Result<WorkspaceSettings, AnyError> {
|
||||
Ok(get_repl_workspace_settings())
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
Ok(vec![get_repl_workspace_settings(); scopes.len()])
|
||||
}
|
||||
|
||||
async fn show_message(
|
||||
|
|
|
@ -408,7 +408,7 @@ fn collect_test(
|
|||
config: &Config,
|
||||
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
||||
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 {
|
||||
let mut collector =
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use super::client::Client;
|
||||
use super::config::ConfigSnapshot;
|
||||
use super::config::WorkspaceSettings;
|
||||
use super::documents::file_like_to_file_specifier;
|
||||
use super::documents::Documents;
|
||||
use super::documents::DocumentsFilter;
|
||||
|
@ -52,12 +53,12 @@ pub struct CompletionItemData {
|
|||
/// a notification to the client.
|
||||
async fn check_auto_config_registry(
|
||||
url_str: &str,
|
||||
config: &ConfigSnapshot,
|
||||
workspace_settings: &WorkspaceSettings,
|
||||
client: &Client,
|
||||
module_registries: &ModuleRegistry,
|
||||
) {
|
||||
// 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) {
|
||||
let scheme = specifier.scheme();
|
||||
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
|
||||
let in_config =
|
||||
config.settings.workspace.suggest.imports.hosts.iter().any(
|
||||
|(h, _)| {
|
||||
workspace_settings
|
||||
.suggest
|
||||
.imports
|
||||
.hosts
|
||||
.iter()
|
||||
.any(|(h, _)| {
|
||||
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
|
||||
// suggestions and send a notification to the client.
|
||||
if !in_config {
|
||||
|
@ -176,7 +180,13 @@ pub async fn get_import_completions(
|
|||
}))
|
||||
} else if !text.is_empty() {
|
||||
// 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 {
|
||||
(position.character - range.start.character) as usize
|
||||
} 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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TestingSettings {
|
||||
|
@ -712,56 +678,6 @@ impl WorkspaceSettings {
|
|||
.unwrap_or_default();
|
||||
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)]
|
||||
|
@ -773,6 +689,13 @@ pub struct 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.
|
||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
specifier_enabled(
|
||||
|
@ -803,8 +726,59 @@ impl ConfigSnapshot {
|
|||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Settings {
|
||||
pub specifiers: BTreeMap<ModuleSpecifier, SpecifierSettings>,
|
||||
pub workspace: WorkspaceSettings,
|
||||
pub unscoped: 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)]
|
||||
|
@ -860,6 +834,93 @@ impl 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> {
|
||||
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> {
|
||||
Arc::new(ConfigSnapshot {
|
||||
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 {
|
||||
specifier_enabled(
|
||||
specifier,
|
||||
|
@ -1009,11 +1052,8 @@ impl Config {
|
|||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
||||
continue;
|
||||
};
|
||||
let specifier_settings = self.settings.specifiers.get(workspace_uri);
|
||||
let enable_paths = specifier_settings
|
||||
.and_then(|s| s.enable_paths.as_ref())
|
||||
.or(self.settings.workspace.enable_paths.as_ref());
|
||||
if let Some(enable_paths) = enable_paths {
|
||||
let settings = self.workspace_settings_for_specifier(workspace_uri);
|
||||
if let Some(enable_paths) = &settings.enable_paths {
|
||||
for path in enable_paths {
|
||||
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 {
|
||||
let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
|
||||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
||||
continue;
|
||||
};
|
||||
let specifier_settings = self.settings.specifiers.get(workspace_uri);
|
||||
let enable = specifier_settings
|
||||
.and_then(|s| s.enable)
|
||||
.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 {
|
||||
let settings = self.workspace_settings_for_specifier(workspace_uri);
|
||||
if settings.enable.unwrap_or_else(|| self.has_config_file()) {
|
||||
for path in &settings.disable_paths {
|
||||
paths.push(workspace_path.join(path));
|
||||
}
|
||||
} else {
|
||||
|
@ -1065,16 +1094,6 @@ impl Config {
|
|||
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(
|
||||
&mut self,
|
||||
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(
|
||||
specifier: &Url,
|
||||
config_file: Option<&ConfigFile>,
|
||||
settings: &Settings,
|
||||
workspace_folders: &Vec<(Url, lsp::WorkspaceFolder)>,
|
||||
workspace_folders: &[(Url, lsp::WorkspaceFolder)],
|
||||
) -> bool {
|
||||
if let Some(cf) = config_file {
|
||||
if let Some(files) = cf.to_files_config().ok().flatten() {
|
||||
|
@ -1166,56 +1161,43 @@ 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 {
|
||||
// Non-file URLs are not disabled by these settings.
|
||||
return true;
|
||||
};
|
||||
for (workspace_uri, _) in workspace_folders {
|
||||
let Ok(workspace_path) = specifier_to_file_path(workspace_uri) else {
|
||||
lsp_log!("Unable to convert uri \"{}\" to path.", workspace_uri);
|
||||
continue;
|
||||
};
|
||||
if path.starts_with(&workspace_path) {
|
||||
let specifier_settings = settings.specifiers.get(workspace_uri);
|
||||
let disable_paths = specifier_settings
|
||||
.map(|s| &s.disable_paths)
|
||||
.unwrap_or(&settings.workspace.disable_paths);
|
||||
let resolved_disable_paths = disable_paths
|
||||
let (settings, mut folder_uri) = settings.get_for_specifier(specifier);
|
||||
folder_uri = folder_uri.or_else(|| workspace_folders.get(0).map(|f| &f.0));
|
||||
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| workspace_path.join(p))
|
||||
.map(|p| folder_path.join(p))
|
||||
.collect::<Vec<_>>();
|
||||
let enable_paths = specifier_settings
|
||||
.and_then(|s| s.enable_paths.as_ref())
|
||||
.or(settings.workspace.enable_paths.as_ref());
|
||||
if let Some(enable_paths) = enable_paths {
|
||||
enable_paths = settings.enable_paths.as_ref().map(|enable_paths| {
|
||||
enable_paths
|
||||
.iter()
|
||||
.map(|p| folder_path.join(p))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(enable_paths) = &enable_paths {
|
||||
for enable_path in enable_paths {
|
||||
let enable_path = workspace_path.join(enable_path);
|
||||
if path.starts_with(&enable_path)
|
||||
&& !resolved_disable_paths.iter().any(|p| path.starts_with(p))
|
||||
if path.starts_with(enable_path)
|
||||
&& !disable_paths.iter().any(|p| path.starts_with(p))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
false
|
||||
} else {
|
||||
return specifier_settings
|
||||
.and_then(|s| s.enable)
|
||||
.unwrap_or(root_enable)
|
||||
&& !resolved_disable_paths.iter().any(|p| path.starts_with(p));
|
||||
settings.enable.unwrap_or_else(|| config_file.is_some())
|
||||
&& !disable_paths.iter().any(|p| path.starts_with(p))
|
||||
}
|
||||
}
|
||||
}
|
||||
root_enable
|
||||
}
|
||||
|
||||
fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> {
|
||||
let lockfile_path = match config_file.resolve_lockfile_path() {
|
||||
|
@ -1285,6 +1267,7 @@ mod tests {
|
|||
"enable": true
|
||||
}))
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
assert!(config.specifier_enabled(&specifier));
|
||||
}
|
||||
|
@ -1300,6 +1283,7 @@ mod tests {
|
|||
"enable": true
|
||||
}))
|
||||
.unwrap(),
|
||||
None,
|
||||
);
|
||||
let config_snapshot = config.snapshot();
|
||||
assert!(config_snapshot.specifier_enabled(&specifier));
|
||||
|
@ -1315,7 +1299,7 @@ mod tests {
|
|||
assert!(!config.specifier_enabled(&specifier_b));
|
||||
let workspace_settings =
|
||||
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_b));
|
||||
let config_snapshot = config.snapshot();
|
||||
|
@ -1327,10 +1311,10 @@ mod tests {
|
|||
fn test_config_specifier_disabled_path() {
|
||||
let root_uri = resolve_url("file:///root/").unwrap();
|
||||
let mut config = Config::new_with_root(root_uri.clone());
|
||||
config.settings.workspace.enable = Some(true);
|
||||
config.settings.workspace.enable_paths =
|
||||
config.settings.unscoped.enable = Some(true);
|
||||
config.settings.unscoped.enable_paths =
|
||||
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("mod2.ts").unwrap()));
|
||||
|
@ -1340,7 +1324,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_set_workspace_settings_defaults() {
|
||||
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!(
|
||||
config.workspace_settings().clone(),
|
||||
WorkspaceSettings {
|
||||
|
@ -1472,6 +1457,7 @@ mod tests {
|
|||
let mut config = Config::new();
|
||||
config.set_workspace_settings(
|
||||
serde_json::from_value(json!({ "cache": "" })).unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
config.workspace_settings().clone(),
|
||||
|
@ -1484,6 +1470,7 @@ mod tests {
|
|||
let mut config = Config::new();
|
||||
config.set_workspace_settings(
|
||||
serde_json::from_value(json!({ "import_map": "" })).unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
config.workspace_settings().clone(),
|
||||
|
@ -1496,6 +1483,7 @@ mod tests {
|
|||
let mut config = Config::new();
|
||||
config.set_workspace_settings(
|
||||
serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
config.workspace_settings().clone(),
|
||||
|
@ -1508,6 +1496,7 @@ mod tests {
|
|||
let mut config = Config::new();
|
||||
config.set_workspace_settings(
|
||||
serde_json::from_value(json!({ "config": "" })).unwrap(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
config.workspace_settings().clone(),
|
||||
|
@ -1541,9 +1530,13 @@ mod tests {
|
|||
},
|
||||
),
|
||||
];
|
||||
config.set_specifier_settings(
|
||||
config.set_workspace_settings(
|
||||
Default::default(),
|
||||
Some(
|
||||
vec![
|
||||
(
|
||||
Url::parse("file:///root1/").unwrap(),
|
||||
SpecifierSettings {
|
||||
WorkspaceSettings {
|
||||
enable_paths: Some(vec![
|
||||
"sub_dir".to_string(),
|
||||
"sub_dir/other".to_string(),
|
||||
|
@ -1551,20 +1544,25 @@ mod tests {
|
|||
]),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
config.set_specifier_settings(
|
||||
),
|
||||
(
|
||||
Url::parse("file:///root2/").unwrap(),
|
||||
SpecifierSettings {
|
||||
WorkspaceSettings {
|
||||
enable_paths: Some(vec!["other.ts".to_string()]),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
config.set_specifier_settings(
|
||||
),
|
||||
(
|
||||
Url::parse("file:///root3/").unwrap(),
|
||||
SpecifierSettings {
|
||||
WorkspaceSettings {
|
||||
enable: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1583,7 +1581,7 @@ mod tests {
|
|||
fn config_enable_via_config_file_detection() {
|
||||
let root_uri = resolve_url("file:///root/").unwrap();
|
||||
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));
|
||||
|
||||
config.set_config_file(
|
||||
|
@ -1597,7 +1595,7 @@ mod tests {
|
|||
fn config_specifier_enabled_matches_by_path_component() {
|
||||
let root_uri = resolve_url("file:///root/").unwrap();
|
||||
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()));
|
||||
}
|
||||
|
||||
|
@ -1605,11 +1603,11 @@ mod tests {
|
|||
fn config_specifier_enabled_for_test() {
|
||||
let root_uri = resolve_url("file:///root/").unwrap();
|
||||
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()]);
|
||||
config.settings.workspace.disable_paths = vec!["mod2.ts".to_string()];
|
||||
config.settings.unscoped.disable_paths = vec!["mod2.ts".to_string()];
|
||||
assert!(
|
||||
config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap())
|
||||
);
|
||||
|
@ -1619,7 +1617,7 @@ mod tests {
|
|||
assert!(
|
||||
!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(
|
||||
ConfigFile::new(
|
||||
|
@ -1688,7 +1686,7 @@ mod tests {
|
|||
fn config_snapshot_specifier_enabled_for_test() {
|
||||
let root_uri = resolve_url("file:///root/").unwrap();
|
||||
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(
|
||||
ConfigFile::new(
|
||||
&json!({
|
||||
|
|
|
@ -788,23 +788,24 @@ fn generate_lint_diagnostics(
|
|||
let documents = snapshot
|
||||
.documents
|
||||
.documents(DocumentsFilter::OpenDiagnosable);
|
||||
let workspace_settings = config.settings.workspace.clone();
|
||||
let lint_rules = get_configured_rules(lint_options.rules.clone());
|
||||
let mut diagnostics_vec = Vec::new();
|
||||
if workspace_settings.lint {
|
||||
for document in documents {
|
||||
let settings =
|
||||
config.workspace_settings_for_specifier(document.specifier());
|
||||
if !settings.lint {
|
||||
continue;
|
||||
}
|
||||
// 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(),
|
||||
|
@ -819,7 +820,6 @@ fn generate_lint_diagnostics(
|
|||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
diagnostics_vec
|
||||
}
|
||||
|
||||
|
@ -1442,7 +1442,6 @@ mod tests {
|
|||
use crate::cache::RealDenoCacheEnv;
|
||||
use crate::lsp::config::ConfigSnapshot;
|
||||
use crate::lsp::config::Settings;
|
||||
use crate::lsp::config::SpecifierSettings;
|
||||
use crate::lsp::config::WorkspaceSettings;
|
||||
use crate::lsp::documents::Documents;
|
||||
use crate::lsp::documents::LanguageId;
|
||||
|
@ -1497,7 +1496,7 @@ mod tests {
|
|||
let root_uri = resolve_url("file:///").unwrap();
|
||||
ConfigSnapshot {
|
||||
settings: Settings {
|
||||
workspace: WorkspaceSettings {
|
||||
unscoped: WorkspaceSettings {
|
||||
enable: Some(true),
|
||||
lint: true,
|
||||
..Default::default()
|
||||
|
@ -1529,7 +1528,6 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn test_enabled_then_disabled_specifier() {
|
||||
let temp_dir = TempDir::new();
|
||||
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
|
||||
let (snapshot, cache_location) = setup(
|
||||
&temp_dir,
|
||||
&[(
|
||||
|
@ -1578,15 +1576,10 @@ let c: number = "a";
|
|||
// now test disabled specifier
|
||||
{
|
||||
let mut disabled_config = mock_config();
|
||||
disabled_config.settings.specifiers.insert(
|
||||
specifier.clone(),
|
||||
SpecifierSettings {
|
||||
disabled_config.settings.unscoped = WorkspaceSettings {
|
||||
enable: Some(false),
|
||||
disable_paths: vec![],
|
||||
enable_paths: None,
|
||||
code_lens: Default::default(),
|
||||
},
|
||||
);
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let diagnostics = generate_lint_diagnostics(
|
||||
&snapshot,
|
||||
|
|
|
@ -23,6 +23,7 @@ use import_map::ImportMap;
|
|||
use indexmap::IndexSet;
|
||||
use log::error;
|
||||
use serde_json::from_value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
|
@ -80,7 +81,6 @@ use super::tsc::AssetsSnapshot;
|
|||
use super::tsc::GetCompletionDetailsArgs;
|
||||
use super::tsc::TsServer;
|
||||
use super::urls;
|
||||
use super::urls::LspClientUrl;
|
||||
use crate::args::get_root_cert_store;
|
||||
use crate::args::package_json;
|
||||
use crate::args::resolve_import_map_from_specifier;
|
||||
|
@ -403,68 +403,42 @@ impl LanguageServer {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn refresh_specifiers_from_client(&self) -> bool {
|
||||
let (client, specifiers) = {
|
||||
pub async fn refresh_configuration(&self) {
|
||||
let (client, folders, capable) = {
|
||||
let ls = self.0.read().await;
|
||||
let specifiers = if ls.config.client_capabilities.workspace_configuration
|
||||
{
|
||||
let root_capacity = std::cmp::max(ls.config.workspace_folders.len(), 1);
|
||||
let config_specifiers = ls.config.get_specifiers();
|
||||
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)
|
||||
};
|
||||
|
||||
let mut touched = false;
|
||||
if let Some(specifiers) = specifiers {
|
||||
let configs_result = client
|
||||
.when_outside_lsp_lock()
|
||||
.specifier_configurations(
|
||||
specifiers
|
||||
.iter()
|
||||
.map(|(_, client_uri)| client_uri.clone())
|
||||
.collect(),
|
||||
(
|
||||
ls.client.clone(),
|
||||
ls.config.workspace_folders.clone(),
|
||||
ls.config.client_capabilities.workspace_configuration,
|
||||
)
|
||||
};
|
||||
if capable {
|
||||
let mut scopes = Vec::with_capacity(folders.len() + 1);
|
||||
scopes.push(None);
|
||||
for (_, folder) in &folders {
|
||||
scopes.push(Some(folder.uri.clone()));
|
||||
}
|
||||
let configs = client
|
||||
.when_outside_lsp_lock()
|
||||
.workspace_configuration(scopes)
|
||||
.await;
|
||||
|
||||
if let Ok(configs) = configs {
|
||||
if configs.len() != folders.len() + 1 {
|
||||
lsp_warn!("Incorrect number of configurations received.");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
if let Ok(configs) = configs_result {
|
||||
for (value, internal_uri) in
|
||||
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;
|
||||
ls.config
|
||||
.set_workspace_settings(unscoped, Some(by_workspace_folder));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("{}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
touched
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1182,6 +1156,7 @@ impl Inner {
|
|||
if let Some(options) = params.initialization_options {
|
||||
self.config.set_workspace_settings(
|
||||
WorkspaceSettings::from_initialization_options(options),
|
||||
None,
|
||||
);
|
||||
}
|
||||
if let Some(folders) = params.workspace_folders {
|
||||
|
@ -1391,14 +1366,10 @@ impl Inner {
|
|||
|
||||
async fn did_change_configuration(
|
||||
&mut self,
|
||||
client_workspace_config: Option<WorkspaceSettings>,
|
||||
params: DidChangeConfigurationParams,
|
||||
) {
|
||||
let maybe_config =
|
||||
if self.config.client_capabilities.workspace_configuration {
|
||||
client_workspace_config
|
||||
} else {
|
||||
params.settings.as_object().map(|settings| {
|
||||
if !self.config.client_capabilities.workspace_configuration {
|
||||
let config = params.settings.as_object().map(|settings| {
|
||||
let deno =
|
||||
serde_json::to_value(settings.get(SETTINGS_SECTION)).unwrap();
|
||||
let javascript =
|
||||
|
@ -1406,12 +1377,11 @@ impl Inner {
|
|||
let typescript =
|
||||
serde_json::to_value(settings.get("typescript")).unwrap();
|
||||
WorkspaceSettings::from_raw_settings(deno, javascript, typescript)
|
||||
})
|
||||
};
|
||||
|
||||
if let Some(settings) = maybe_config {
|
||||
self.config.set_workspace_settings(settings);
|
||||
});
|
||||
if let Some(settings) = config {
|
||||
self.config.set_workspace_settings(settings, None);
|
||||
}
|
||||
};
|
||||
|
||||
self.update_debug_flag();
|
||||
if let Err(err) = self.update_cache().await {
|
||||
|
@ -2148,8 +2118,7 @@ impl Inner {
|
|||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
|| !(self.config.workspace_settings().enabled_code_lens()
|
||||
|| self.config.specifier_code_lens_test(&specifier))
|
||||
|| !self.config.enabled_code_lens_for_specifier(&specifier)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -2385,10 +2354,8 @@ impl Inner {
|
|||
¶ms.text_document_position.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
let language_settings = self
|
||||
.config
|
||||
.workspace_settings()
|
||||
.language_settings_for_specifier(&specifier);
|
||||
let language_settings =
|
||||
self.config.language_settings_for_specifier(&specifier);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
|| !language_settings.map(|s| s.suggest.enabled).unwrap_or(true)
|
||||
|
@ -2457,7 +2424,6 @@ impl Inner {
|
|||
line_index,
|
||||
&self
|
||||
.config
|
||||
.workspace_settings()
|
||||
.language_settings_for_specifier(&specifier)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
|
@ -2992,7 +2958,6 @@ impl Inner {
|
|||
);
|
||||
let options = self
|
||||
.config
|
||||
.workspace_settings()
|
||||
.language_settings_for_specifier(&old_specifier)
|
||||
.map(|s| s.update_imports_on_file_move.clone())
|
||||
.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;
|
||||
|
@ -3212,60 +3177,19 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
|||
return;
|
||||
}
|
||||
|
||||
let (client, client_uri, specifier, should_get_specifier_settings) = {
|
||||
let mut inner = self.0.write().await;
|
||||
let client = inner.client.clone();
|
||||
let client_uri = LspClientUrl::new(params.text_document.uri.clone());
|
||||
let specifier = inner
|
||||
.url_map
|
||||
.normalize_url(client_uri.as_url(), LspUrlKind::File);
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
let document = inner.did_open(&specifier, params).await;
|
||||
let should_get_specifier_settings =
|
||||
!inner.config.has_specifier_settings(&specifier)
|
||||
&& inner.config.client_capabilities.workspace_configuration;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
||||
self.0.write().await.did_change(params).await
|
||||
|
@ -3277,7 +3201,10 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
|||
let mut inner = self.0.write().await;
|
||||
let specifier = inner.url_map.normalize_url(uri, LspUrlKind::File);
|
||||
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.diagnostics_state.has_no_cache_diagnostics(&specifier)
|
||||
{
|
||||
|
@ -3310,47 +3237,17 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
|||
&self,
|
||||
params: DidChangeConfigurationParams,
|
||||
) {
|
||||
let (mark, has_workspace_capability, client) = {
|
||||
let mark = {
|
||||
let inner = self.0.read().await;
|
||||
(
|
||||
inner
|
||||
.performance
|
||||
.mark("did_change_configuration", Some(¶ms)),
|
||||
inner.config.client_capabilities.workspace_configuration,
|
||||
inner.client.clone(),
|
||||
)
|
||||
.mark("did_change_configuration", Some(¶ms))
|
||||
};
|
||||
|
||||
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;
|
||||
inner
|
||||
.did_change_configuration(client_workspace_config, params)
|
||||
.await;
|
||||
inner.did_change_configuration(params).await;
|
||||
inner.performance.measure(mark);
|
||||
}
|
||||
|
||||
|
@ -3374,7 +3271,8 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
|||
(ls.performance.clone(), mark)
|
||||
};
|
||||
|
||||
if self.refresh_specifiers_from_client().await {
|
||||
self.refresh_configuration().await;
|
||||
{
|
||||
let mut ls = self.0.write().await;
|
||||
ls.refresh_documents_config().await;
|
||||
ls.diagnostics_server.invalidate_all();
|
||||
|
@ -3681,10 +3579,7 @@ impl Inner {
|
|||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
|| !self
|
||||
.config
|
||||
.workspace_settings()
|
||||
.enabled_inlay_hints(&specifier)
|
||||
|| !self.config.enabled_inlay_hints_for_specifier(&specifier)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
|
|
@ -4215,9 +4215,8 @@ impl UserPreferences {
|
|||
use_label_details_in_completion_entries: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let Some(language_settings) = config
|
||||
.workspace_settings()
|
||||
.language_settings_for_specifier(specifier)
|
||||
let Some(language_settings) =
|
||||
config.language_settings_for_specifier(specifier)
|
||||
else {
|
||||
return base_preferences;
|
||||
};
|
||||
|
@ -5312,7 +5311,7 @@ mod tests {
|
|||
.variable_types
|
||||
.suppress_when_type_matches_name = true;
|
||||
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(
|
||||
&config,
|
||||
&Default::default(),
|
||||
|
|
|
@ -1024,23 +1024,23 @@ fn lsp_import_attributes() {
|
|||
client.initialize(|builder| {
|
||||
builder.set_import_map("data:application/json;utf8,{\"imports\": { \"example\": \"https://deno.land/x/example/mod.ts\" }}");
|
||||
});
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
"test": true,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
client.did_open_with_config(
|
||||
json!({
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/test.json",
|
||||
"languageId": "json",
|
||||
"version": 1,
|
||||
"text": "{\"a\":1}"
|
||||
}
|
||||
}),
|
||||
&json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
"test": true
|
||||
}
|
||||
} }),
|
||||
);
|
||||
"text": "{\"a\":1}",
|
||||
},
|
||||
}));
|
||||
|
||||
let diagnostics = client.did_open(json!({
|
||||
"textDocument": {
|
||||
|
@ -1380,17 +1380,15 @@ fn lsp_hover_disabled() {
|
|||
client.initialize(|builder| {
|
||||
builder.set_deno_enable(false);
|
||||
});
|
||||
client.did_open_with_config(
|
||||
json!({
|
||||
client.change_configuration(json!({ "deno": { "enable": false } }));
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "console.log(Date.now());\n"
|
||||
}
|
||||
}),
|
||||
&json!({ "deno": { "enable": false } }),
|
||||
);
|
||||
"text": "console.log(Date.now());\n",
|
||||
},
|
||||
}));
|
||||
|
||||
let res = client.write_request(
|
||||
"textDocument/hover",
|
||||
|
@ -3794,24 +3792,22 @@ fn lsp_code_lens_test_disabled() {
|
|||
"test": false
|
||||
})));
|
||||
});
|
||||
client
|
||||
.did_open_with_config(
|
||||
json!({
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
"test": false,
|
||||
},
|
||||
},
|
||||
}));
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"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"
|
||||
}
|
||||
}),
|
||||
// disable test code lens
|
||||
&json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
"test": false
|
||||
}
|
||||
} }),
|
||||
);
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/codeLens",
|
||||
json!({
|
||||
|
@ -3820,7 +3816,7 @@ fn lsp_code_lens_test_disabled() {
|
|||
}
|
||||
}),
|
||||
);
|
||||
assert_eq!(res, json!([]));
|
||||
assert_eq!(res, json!(null));
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
|
@ -4898,22 +4894,12 @@ fn lsp_cache_on_save() {
|
|||
);
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {}
|
||||
}),
|
||||
);
|
||||
let settings = json!({
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": 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!({
|
||||
"textDocument": {
|
||||
|
@ -5592,23 +5578,16 @@ fn lsp_quote_style_from_workspace_settings() {
|
|||
);
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize_default();
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {}
|
||||
}),
|
||||
);
|
||||
let settings = json!({
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": true,
|
||||
},
|
||||
"typescript": {
|
||||
"preferences": {
|
||||
"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!({
|
||||
"textDocument": {
|
||||
|
@ -5792,7 +5771,7 @@ fn lsp_code_actions_deadlock() {
|
|||
let large_file_text =
|
||||
fs::read_to_string(testdata_path().join("lsp").join("large_file.txt"))
|
||||
.unwrap();
|
||||
client.did_open_raw(json!({
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "javascript",
|
||||
|
@ -5800,7 +5779,6 @@ fn lsp_code_actions_deadlock() {
|
|||
"text": large_file_text,
|
||||
}
|
||||
}));
|
||||
client.handle_configuration_request(&json!({ "deno": { "enable": true } }));
|
||||
client.write_request(
|
||||
"textDocument/semanticTokens/full",
|
||||
json!({
|
||||
|
@ -5809,7 +5787,6 @@ fn lsp_code_actions_deadlock() {
|
|||
}
|
||||
}),
|
||||
);
|
||||
client.read_diagnostics();
|
||||
client.write_notification(
|
||||
"textDocument/didChange",
|
||||
json!({
|
||||
|
@ -8901,17 +8878,11 @@ fn lsp_configuration_did_change() {
|
|||
"text": "import * as a from \"http://localhost:4545/x/a@\""
|
||||
}
|
||||
}));
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {}
|
||||
}),
|
||||
);
|
||||
let settings = json!({ "deno": {
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
"implementations": true,
|
||||
"references": true
|
||||
"references": true,
|
||||
},
|
||||
"importMap": null,
|
||||
"lint": true,
|
||||
|
@ -8922,16 +8893,12 @@ fn lsp_configuration_did_change() {
|
|||
"paths": true,
|
||||
"imports": {
|
||||
"hosts": {
|
||||
"http://localhost:4545/": true
|
||||
}
|
||||
}
|
||||
"http://localhost:4545/": true,
|
||||
},
|
||||
"unstable": false
|
||||
} });
|
||||
// one for the workspace
|
||||
client.handle_configuration_request(&settings);
|
||||
// one for the specifier
|
||||
client.handle_configuration_request(&settings);
|
||||
},
|
||||
},
|
||||
"unstable": false,
|
||||
} }));
|
||||
|
||||
let list = client.get_completion_list(
|
||||
"file:///a/file.ts",
|
||||
|
@ -8997,13 +8964,7 @@ fn lsp_completions_complete_function_calls() {
|
|||
"text": "[]."
|
||||
}
|
||||
}));
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {}
|
||||
}),
|
||||
);
|
||||
let settings = json!({
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": true,
|
||||
},
|
||||
|
@ -9012,11 +8973,7 @@ fn lsp_completions_complete_function_calls() {
|
|||
"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(
|
||||
"file:///a/file.ts",
|
||||
|
@ -10099,22 +10056,12 @@ fn lsp_node_modules_dir() {
|
|||
"{ \"nodeModulesDir\": true, \"lock\": false }\n",
|
||||
);
|
||||
let refresh_config = |client: &mut LspClient| {
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {
|
||||
"enable": true,
|
||||
"config": "./deno.json",
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let settings = json!({ "deno": {
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"config": "./deno.json",
|
||||
"codeLens": {
|
||||
"implementations": true,
|
||||
"references": true
|
||||
"references": true,
|
||||
},
|
||||
"importMap": null,
|
||||
"lint": false,
|
||||
|
@ -10123,14 +10070,10 @@ fn lsp_node_modules_dir() {
|
|||
"completeFunctionCalls": false,
|
||||
"names": true,
|
||||
"paths": true,
|
||||
"imports": {}
|
||||
"imports": {},
|
||||
},
|
||||
"unstable": false
|
||||
} });
|
||||
// one for the workspace
|
||||
client.handle_configuration_request(&settings);
|
||||
// one for the specifier
|
||||
client.handle_configuration_request(&settings);
|
||||
"unstable": false,
|
||||
} }));
|
||||
};
|
||||
refresh_config(&mut client);
|
||||
|
||||
|
@ -10228,22 +10171,12 @@ fn lsp_vendor_dir() {
|
|||
"{ \"vendor\": true, \"lock\": false }\n",
|
||||
);
|
||||
let refresh_config = |client: &mut LspClient| {
|
||||
client.write_notification(
|
||||
"workspace/didChangeConfiguration",
|
||||
json!({
|
||||
"settings": {
|
||||
"enable": true,
|
||||
"config": "./deno.json",
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let settings = json!({ "deno": {
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"config": "./deno.json",
|
||||
"codeLens": {
|
||||
"implementations": true,
|
||||
"references": true
|
||||
"references": true,
|
||||
},
|
||||
"importMap": null,
|
||||
"lint": false,
|
||||
|
@ -10252,14 +10185,10 @@ fn lsp_vendor_dir() {
|
|||
"completeFunctionCalls": false,
|
||||
"names": true,
|
||||
"paths": true,
|
||||
"imports": {}
|
||||
"imports": {},
|
||||
},
|
||||
"unstable": false
|
||||
} });
|
||||
// one for the workspace
|
||||
client.handle_configuration_request(&settings);
|
||||
// one for the specifier
|
||||
client.handle_configuration_request(&settings);
|
||||
"unstable": false,
|
||||
} }));
|
||||
};
|
||||
refresh_config(&mut client);
|
||||
|
||||
|
|
|
@ -210,7 +210,22 @@ pub struct InitializeParamsBuilder {
|
|||
|
||||
impl InitializeParamsBuilder {
|
||||
#[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 {
|
||||
params: InitializeParams {
|
||||
process_id: None,
|
||||
|
@ -219,38 +234,7 @@ impl InitializeParamsBuilder {
|
|||
version: Some("1.0.0".to_string()),
|
||||
}),
|
||||
root_uri: None,
|
||||
initialization_options: Some(json!({
|
||||
"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
|
||||
})),
|
||||
initialization_options: Some(config_as_options),
|
||||
capabilities: ClientCapabilities {
|
||||
text_document: Some(TextDocumentClientCapabilities {
|
||||
code_action: Some(CodeActionClientCapabilities {
|
||||
|
@ -687,7 +671,36 @@ impl LspClient {
|
|||
self.initialize_with_config(
|
||||
do_build,
|
||||
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,
|
||||
} }),
|
||||
)
|
||||
}
|
||||
|
@ -695,12 +708,32 @@ impl LspClient {
|
|||
pub fn initialize_with_config(
|
||||
&mut self,
|
||||
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());
|
||||
do_build(&mut builder);
|
||||
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
|
||||
{
|
||||
Some(workspace) => workspace.configuration == Some(true),
|
||||
|
@ -710,23 +743,12 @@ impl LspClient {
|
|||
self.write_notification("initialized", json!({}));
|
||||
self.config = config;
|
||||
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 {
|
||||
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);
|
||||
if self.supports_workspace_configuration {
|
||||
self.handle_configuration_request(config);
|
||||
}
|
||||
self.read_diagnostics()
|
||||
}
|
||||
|
||||
|
@ -734,17 +756,33 @@ impl LspClient {
|
|||
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>();
|
||||
assert_eq!(method, "workspace/configuration");
|
||||
let params = args.as_ref().unwrap().as_object().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![];
|
||||
for item in items {
|
||||
let item = item.as_object().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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue