Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lsp): send "deno/didChangeDenoConfiguration" notifications #20827

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions cli/lsp/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ impl Client {
});
}

pub fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
) {
// do on a task in case the caller currently is in the lsp lock
let client = self.0.clone();
spawn(async move {
client
.send_did_change_deno_configuration_notification(params)
.await;
});
}

pub fn show_message(
&self,
message_type: lsp::MessageType,
Expand Down Expand Up @@ -184,6 +197,10 @@ trait ClientTrait: Send + Sync {
params: lsp_custom::DiagnosticBatchNotificationParams,
);
async fn send_test_notification(&self, params: TestingNotification);
async fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
);
async fn specifier_configurations(
&self,
uris: Vec<lsp::Url>,
Expand Down Expand Up @@ -259,6 +276,18 @@ impl ClientTrait for TowerClient {
}
}

async fn send_did_change_deno_configuration_notification(
bartlomieju marked this conversation as resolved.
Show resolved Hide resolved
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
) {
self
.0
.send_notification::<lsp_custom::DidChangeDenoConfigurationNotification>(
params,
)
.await
}

async fn specifier_configurations(
&self,
uris: Vec<lsp::Url>,
Expand Down Expand Up @@ -371,6 +400,12 @@ impl ClientTrait for ReplClient {

async fn send_test_notification(&self, _params: TestingNotification) {}

async fn send_did_change_deno_configuration_notification(
&self,
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
) {
}

async fn specifier_configurations(
&self,
uris: Vec<lsp::Url>,
Expand Down
120 changes: 102 additions & 18 deletions cli/lsp/language_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use import_map::ImportMap;
use indexmap::IndexSet;
use log::error;
use serde_json::from_value;
use std::collections::HashMap;
Expand Down Expand Up @@ -64,6 +65,7 @@ use super::documents::UpdateDocumentConfigOptions;
use super::logging::lsp_log;
use super::logging::lsp_warn;
use super::lsp_custom;
use super::lsp_custom::TaskDefinition;
use super::npm::CliNpmSearchApi;
use super::parent_process_checker;
use super::performance::Performance;
Expand Down Expand Up @@ -343,8 +345,8 @@ impl LanguageServer {
self.0.write().await.reload_import_registries().await
}

pub async fn task_request(&self) -> LspResult<Option<Value>> {
self.0.read().await.get_tasks()
pub async fn task_definitions(&self) -> LspResult<Vec<TaskDefinition>> {
self.0.read().await.task_definitions()
}

pub async fn test_run_request(
Expand Down Expand Up @@ -1458,7 +1460,7 @@ impl Inner {
}
}

fn has_config_changed(config: &Config, changes: &HashSet<Url>) -> bool {
fn has_config_changed(config: &Config, changes: &IndexSet<Url>) -> bool {
// Check the canonicalized specifier here because file watcher
// changes will be for the canonicalized path in vscode, but also check the
// non-canonicalized specifier in order to please the tests and handle
Expand Down Expand Up @@ -1509,31 +1511,91 @@ impl Inner {
.performance
.mark("did_change_watched_files", Some(&params));
let mut touched = false;
let changes: HashSet<Url> = params
let changes: IndexSet<Url> = params
.changes
.iter()
.map(|f| self.url_map.normalize_url(&f.uri, LspUrlKind::File))
.collect();

let mut config_changes = IndexSet::with_capacity(changes.len());

// if the current deno.json has changed, we need to reload it
if has_config_changed(&self.config, &changes) {
// Check the 'current' config specifier from both before and after it's
// updated. Check canonicalized and uncanonicalized variants for each.
// If any are included in `changes`, send our custom notification for
// `deno.json` changes: `deno/didChangeDenoConfigurationNotification`.
let mut files_to_check = IndexSet::with_capacity(4);
// Collect previous config specifiers.
if let Some(url) = self.config.maybe_config_file().map(|c| &c.specifier) {
files_to_check.insert(url.clone());
}
if let Some(url) = self.config.maybe_config_file_canonicalized_specifier()
{
files_to_check.insert(url.clone());
}
// Update config.
if let Err(err) = self.update_config_file().await {
self.client.show_message(MessageType::WARNING, err);
}
// Collect new config specifiers.
if let Some(url) = self.config.maybe_config_file().map(|c| &c.specifier) {
files_to_check.insert(url.clone());
}
if let Some(url) = self.config.maybe_config_file_canonicalized_specifier()
{
files_to_check.insert(url.clone());
}
config_changes.extend(
params
.changes
.iter()
.filter(|e| files_to_check.contains(&e.uri))
.map(|e| lsp_custom::DenoConfigurationChangeEvent {
file_event: e.clone(),
configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
}),
);
if let Err(err) = self.update_tsconfig().await {
self.client.show_message(MessageType::WARNING, err);
}
touched = true;
}

if let Some(package_json) = &self.maybe_package_json {
// always update the package json if the deno config changes
if touched || changes.contains(&package_json.specifier()) {
if let Err(err) = self.update_package_json() {
self.client.show_message(MessageType::WARNING, err);
}
touched = true;
let has_package_json_changed = changes
.iter()
.any(|e| e.as_str().ends_with("/package.json"));

if has_package_json_changed {
let mut files_to_check = IndexSet::with_capacity(2);
if let Some(package_json) = &self.maybe_package_json {
files_to_check.insert(package_json.specifier());
}
if let Err(err) = self.update_package_json() {
self.client.show_message(MessageType::WARNING, err);
}
if let Some(package_json) = &self.maybe_package_json {
files_to_check.insert(package_json.specifier());
}
config_changes.extend(
params
.changes
.iter()
.filter(|e| files_to_check.contains(&e.uri))
.map(|e| lsp_custom::DenoConfigurationChangeEvent {
file_event: e.clone(),
configuration_type: lsp_custom::DenoConfigurationType::PackageJson,
}),
);
touched = true;
}

if !config_changes.is_empty() {
self.client.send_did_change_deno_configuration_notification(
lsp_custom::DidChangeDenoConfigurationNotificationParams {
changes: config_changes.into_iter().collect(),
},
);
}

// if the current import map, or config file has changed, we need to
Expand Down Expand Up @@ -3580,13 +3642,35 @@ impl Inner {
json!({ "averages": averages })
}

fn get_tasks(&self) -> LspResult<Option<Value>> {
Ok(
self
.config
.maybe_config_file()
.and_then(|cf| cf.to_lsp_tasks()),
)
fn task_definitions(&self) -> LspResult<Vec<TaskDefinition>> {
let mut result = vec![];
if let Some(config_file) = self.config.maybe_config_file() {
if let Some(tasks) = json!(&config_file.json.tasks).as_object() {
for (name, value) in tasks {
let Some(command) = value.as_str() else {
continue;
};
result.push(TaskDefinition {
name: name.clone(),
command: command.to_string(),
source_uri: config_file.specifier.clone(),
});
}
};
}
if let Some(package_json) = &self.maybe_package_json {
if let Some(scripts) = &package_json.scripts {
for (name, command) in scripts {
result.push(TaskDefinition {
name: name.clone(),
command: command.clone(),
source_uri: package_json.specifier(),
});
}
}
}
result.sort_by_key(|d| d.name.clone());
Ok(result)
}

async fn inlay_hint(
Expand Down
43 changes: 42 additions & 1 deletion cli/lsp/lsp_custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tower_lsp::lsp_types as lsp;

pub const CACHE_REQUEST: &str = "deno/cache";
pub const PERFORMANCE_REQUEST: &str = "deno/performance";
pub const TASK_REQUEST: &str = "deno/task";
pub const TASK_REQUEST: &str = "deno/taskDefinitions";
pub const RELOAD_IMPORT_REGISTRIES_REQUEST: &str =
"deno/reloadImportRegistries";
pub const VIRTUAL_TEXT_DOCUMENT: &str = "deno/virtualTextDocument";
Expand All @@ -24,6 +24,16 @@ pub struct CacheParams {
pub uris: Vec<lsp::TextDocumentIdentifier>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskDefinition {
pub name: String,
// TODO(nayeemrmn): Rename this to `command` in vscode_deno.
#[serde(rename = "detail")]
pub command: String,
pub source_uri: lsp::Url,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct RegistryStateNotificationParams {
pub origin: String,
Expand All @@ -50,6 +60,37 @@ pub struct DiagnosticBatchNotificationParams {
pub messages_len: usize,
}

#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum DenoConfigurationType {
DenoJson,
PackageJson,
}

#[derive(Debug, Eq, Hash, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DenoConfigurationChangeEvent {
#[serde(flatten)]
pub file_event: lsp::FileEvent,
pub configuration_type: DenoConfigurationType,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidChangeDenoConfigurationNotificationParams {
pub changes: Vec<DenoConfigurationChangeEvent>,
}

pub enum DidChangeDenoConfigurationNotification {}

impl lsp::notification::Notification
for DidChangeDenoConfigurationNotification
{
type Params = DidChangeDenoConfigurationNotificationParams;

const METHOD: &'static str = "deno/didChangeDenoConfiguration";
}

/// This notification is only sent for testing purposes
/// in order to know what the latest diagnostics are.
pub enum DiagnosticBatchNotification {}
Expand Down
5 changes: 4 additions & 1 deletion cli/lsp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ pub async fn start() -> Result<(), AnyError> {
lsp_custom::RELOAD_IMPORT_REGISTRIES_REQUEST,
LanguageServer::reload_import_registries_request,
)
.custom_method(lsp_custom::TASK_REQUEST, LanguageServer::task_request)
.custom_method(lsp_custom::TASK_REQUEST, LanguageServer::task_definitions)
// TODO(nayeemrmn): Rename this to `deno/taskDefinitions` in vscode_deno and
// remove this alias.
.custom_method("deno/task", LanguageServer::task_definitions)
.custom_method(testing::TEST_RUN_REQUEST, LanguageServer::test_run_request)
.custom_method(
testing::TEST_RUN_CANCEL_REQUEST,
Expand Down
Loading