Reland feat(lsp): deno/didRefreshDenoConfigurationTree notifications (#26325)

This commit is contained in:
Nayeem Rahman 2024-10-16 22:43:26 +01:00 committed by GitHub
parent f8417224eb
commit 72dd74d83a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 324 additions and 13 deletions

View File

@ -150,7 +150,11 @@ fn bench_big_file_edits(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",
@ -206,6 +210,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": {
"enable": true,
"codeLens": {
@ -214,6 +220,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
"test": true,
},
} }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",
@ -257,7 +265,11 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
for i in 0..10 {
client.write_notification(
@ -341,7 +353,11 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",

View File

@ -13,7 +13,11 @@ use test_util::lsp::LspClientBuilder;
fn incremental_change_wait(bench: &mut Bencher) {
let mut client = LspClientBuilder::new().use_diagnostic_sync(false).build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",

View File

@ -147,11 +147,11 @@ pub fn server_capabilities(
moniker_provider: None,
experimental: Some(json!({
"denoConfigTasks": true,
"testingApi":true,
"testingApi": true,
"didRefreshDenoConfigurationTreeNotifications": true,
})),
inlay_hint_provider: Some(OneOf::Left(true)),
position_encoding: None,
// TODO(nayeemrmn): Support pull-based diagnostics.
diagnostic_provider: None,
inline_value_provider: None,
inline_completion_provider: None,

View File

@ -92,6 +92,19 @@ impl Client {
});
}
pub fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
// 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_refresh_deno_configuration_tree_notification(params)
.await;
});
}
pub fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -169,6 +182,10 @@ trait ClientTrait: Send + Sync {
params: lsp_custom::DiagnosticBatchNotificationParams,
);
async fn send_test_notification(&self, params: TestingNotification);
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
);
async fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -249,6 +266,18 @@ impl ClientTrait for TowerClient {
}
}
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
self
.0
.send_notification::<lsp_custom::DidRefreshDenoConfigurationTreeNotification>(
params,
)
.await
}
async fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -366,6 +395,12 @@ impl ClientTrait for ReplClient {
async fn send_test_notification(&self, _params: TestingNotification) {}
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
_params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
}
async fn send_did_change_deno_configuration_notification(
&self,
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,

View File

@ -50,6 +50,8 @@ use std::sync::Arc;
use tower_lsp::lsp_types as lsp;
use super::logging::lsp_log;
use super::lsp_custom;
use super::urls::url_to_uri;
use crate::args::discover_npmrc_from_workspace;
use crate::args::has_flag_env_var;
use crate::args::CliLockfile;
@ -1716,14 +1718,14 @@ impl ConfigTree {
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
}
/// Returns (scope_uri, type).
/// Returns (scope_url, type).
pub fn watched_file_type(
&self,
specifier: &ModuleSpecifier,
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
for (scope_uri, data) in self.scopes.iter() {
for (scope_url, data) in self.scopes.iter() {
if let Some(typ) = data.watched_files.get(specifier) {
return Some((scope_uri, *typ));
return Some((scope_url, *typ));
}
}
None
@ -1747,6 +1749,46 @@ impl ConfigTree {
.any(|data| data.watched_files.contains_key(specifier))
}
pub fn to_did_refresh_params(
&self,
) -> lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams {
let data = self
.scopes
.values()
.filter_map(|data| {
let workspace_root_scope_uri =
Some(data.member_dir.workspace.root_dir())
.filter(|s| *s != data.member_dir.dir_url())
.and_then(|s| url_to_uri(s).ok());
Some(lsp_custom::DenoConfigurationData {
scope_uri: url_to_uri(&data.scope).ok()?,
deno_json: data.maybe_deno_json().and_then(|c| {
if workspace_root_scope_uri.is_some()
&& Some(&c.specifier)
== data
.member_dir
.workspace
.root_deno_json()
.map(|c| &c.specifier)
{
return None;
}
Some(lsp::TextDocumentIdentifier {
uri: url_to_uri(&c.specifier).ok()?,
})
}),
package_json: data.maybe_pkg_json().and_then(|p| {
Some(lsp::TextDocumentIdentifier {
uri: url_to_uri(&p.specifier()).ok()?,
})
}),
workspace_root_scope_uri,
})
})
.collect();
lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams { data }
}
pub async fn refresh(
&mut self,
settings: &Settings,

View File

@ -963,6 +963,11 @@ impl Inner {
.tree
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
.await;
self
.client
.send_did_refresh_deno_configuration_tree_notification(
self.config.tree.to_did_refresh_params(),
);
for config_file in self.config.tree.config_files() {
(|| {
let compiler_options = config_file.to_compiler_options().ok()?.options;

View File

@ -46,6 +46,30 @@ pub struct DiagnosticBatchNotificationParams {
pub messages_len: usize,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DenoConfigurationData {
pub scope_uri: lsp::Uri,
pub workspace_root_scope_uri: Option<lsp::Uri>,
pub deno_json: Option<lsp::TextDocumentIdentifier>,
pub package_json: Option<lsp::TextDocumentIdentifier>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidRefreshDenoConfigurationTreeNotificationParams {
pub data: Vec<DenoConfigurationData>,
}
pub enum DidRefreshDenoConfigurationTreeNotification {}
impl lsp::notification::Notification
for DidRefreshDenoConfigurationTreeNotification
{
type Params = DidRefreshDenoConfigurationTreeNotificationParams;
const METHOD: &'static str = "deno/didRefreshDenoConfigurationTree";
}
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum DenoConfigurationChangeType {
@ -88,13 +112,15 @@ pub struct DidChangeDenoConfigurationNotificationParams {
pub changes: Vec<DenoConfigurationChangeEvent>,
}
// TODO(nayeemrmn): This is being replaced by
// `DidRefreshDenoConfigurationTreeNotification` for Deno > v2.0.0. Remove it
// soon.
pub enum DidChangeDenoConfigurationNotification {}
impl lsp::notification::Notification
for DidChangeDenoConfigurationNotification
{
type Params = DidChangeDenoConfigurationNotificationParams;
const METHOD: &'static str = "deno/didChangeDenoConfiguration";
}
@ -102,7 +128,6 @@ pub enum DidUpgradeCheckNotification {}
impl lsp::notification::Notification for DidUpgradeCheckNotification {
type Params = DidUpgradeCheckNotificationParams;
const METHOD: &'static str = "deno/didUpgradeCheck";
}
@ -125,6 +150,5 @@ pub enum DiagnosticBatchNotification {}
impl lsp::notification::Notification for DiagnosticBatchNotification {
type Params = DiagnosticBatchNotificationParams;
const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
}

View File

@ -1049,6 +1049,191 @@ fn lsp_workspace_enable_paths_no_workspace_configuration() {
client.shutdown();
}
#[test]
fn lsp_did_refresh_deno_configuration_tree_notification() {
let context = TestContextBuilder::new().use_temp_cwd().build();
let temp_dir = context.temp_dir();
temp_dir.create_dir_all("workspace/member1");
temp_dir.create_dir_all("workspace/member2");
temp_dir.create_dir_all("non_workspace1");
temp_dir.create_dir_all("non_workspace2");
temp_dir.write(
"workspace/deno.json",
json!({
"workspace": [
"member1",
"member2",
],
})
.to_string(),
);
temp_dir.write("workspace/member1/deno.json", json!({}).to_string());
temp_dir.write("workspace/member1/package.json", json!({}).to_string());
temp_dir.write("workspace/member2/package.json", json!({}).to_string());
temp_dir.write("non_workspace1/deno.json", json!({}).to_string());
let mut client = context.new_lsp_command().build();
client.initialize_default();
let res = client
.read_notification_with_method::<Value>(
"deno/didRefreshDenoConfigurationTree",
)
.unwrap();
assert_eq!(
res,
json!({
"data": [
{
"scopeUri": temp_dir.url().join("non_workspace1/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("non_workspace1/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("workspace/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/member1/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": {
"uri": temp_dir.url().join("workspace/member1/deno.json").unwrap(),
},
"packageJson": {
"uri": temp_dir.url().join("workspace/member1/package.json").unwrap(),
},
},
{
"scopeUri": temp_dir.url().join("workspace/member2/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": null,
"packageJson": {
"uri": temp_dir.url().join("workspace/member2/package.json").unwrap(),
},
},
],
}),
);
temp_dir.write("non_workspace2/deno.json", json!({}).to_string());
client.did_change_watched_files(json!({
"changes": [{
"uri": temp_dir.url().join("non_workspace2/deno.json").unwrap(),
"type": 1,
}],
}));
let res = client
.read_notification_with_method::<Value>(
"deno/didRefreshDenoConfigurationTree",
)
.unwrap();
assert_eq!(
res,
json!({
"data": [
{
"scopeUri": temp_dir.url().join("non_workspace1/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("non_workspace1/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("non_workspace2/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("non_workspace2/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("workspace/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/member1/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": {
"uri": temp_dir.url().join("workspace/member1/deno.json").unwrap(),
},
"packageJson": {
"uri": temp_dir.url().join("workspace/member1/package.json").unwrap(),
},
},
{
"scopeUri": temp_dir.url().join("workspace/member2/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": null,
"packageJson": {
"uri": temp_dir.url().join("workspace/member2/package.json").unwrap(),
},
},
],
}),
);
client.change_configuration(json!({
"deno": {
"disablePaths": ["non_workspace1"],
},
}));
let res = client
.read_notification_with_method::<Value>(
"deno/didRefreshDenoConfigurationTree",
)
.unwrap();
assert_eq!(
res,
json!({
"data": [
{
"scopeUri": temp_dir.url().join("non_workspace2/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("non_workspace2/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/").unwrap(),
"workspaceRootScopeUri": null,
"denoJson": {
"uri": temp_dir.url().join("workspace/deno.json").unwrap(),
},
"packageJson": null,
},
{
"scopeUri": temp_dir.url().join("workspace/member1/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": {
"uri": temp_dir.url().join("workspace/member1/deno.json").unwrap(),
},
"packageJson": {
"uri": temp_dir.url().join("workspace/member1/package.json").unwrap(),
},
},
{
"scopeUri": temp_dir.url().join("workspace/member2/").unwrap(),
"workspaceRootScopeUri": temp_dir.url().join("workspace/").unwrap(),
"denoJson": null,
"packageJson": {
"uri": temp_dir.url().join("workspace/member2/package.json").unwrap(),
},
},
],
}),
);
client.shutdown();
}
#[test]
fn lsp_did_change_deno_configuration_notification() {
let context = TestContextBuilder::new().use_temp_cwd().build();
@ -9403,14 +9588,15 @@ fn lsp_auto_discover_registry() {
"triggerCharacter": "@"
}),
);
let (method, res) = client.read_notification();
assert_eq!(method, "deno/registryState");
let res = client
.read_notification_with_method::<Value>("deno/registryState")
.unwrap();
assert_eq!(
res,
Some(json!({
json!({
"origin": "http://localhost:4545",
"suggestions": true,
}))
}),
);
client.shutdown();
}
@ -10117,7 +10303,6 @@ fn lsp_diagnostics_refresh_dependents() {
assert_eq!(json!(diagnostics.all()), json!([])); // no diagnostics now
client.shutdown();
assert_eq!(client.queue_len(), 0);
}
// Regression test for https://github.com/denoland/deno/issues/10897.