summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/capabilities.rs2
-rw-r--r--cli/lsp/config.rs175
-rw-r--r--cli/lsp/diagnostics.rs6
-rw-r--r--cli/lsp/language_server.rs90
-rw-r--r--cli/lsp/repl.rs1
-rw-r--r--cli/lsp/tsc.rs5
-rw-r--r--cli/tests/integration/lsp_tests.rs248
-rw-r--r--cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json77
8 files changed, 533 insertions, 71 deletions
diff --git a/cli/lsp/capabilities.rs b/cli/lsp/capabilities.rs
index 87d2c31da..49adc961d 100644
--- a/cli/lsp/capabilities.rs
+++ b/cli/lsp/capabilities.rs
@@ -154,7 +154,7 @@ pub fn server_capabilities(
workspace: Some(WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
- change_notifications: None,
+ change_notifications: Some(OneOf::Left(true)),
}),
file_operations: None,
}),
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index d35451e84..7b4294943 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -1,12 +1,14 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+use super::client::Client;
+use super::logging::lsp_log;
+use crate::fs_util;
use deno_core::error::AnyError;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
use deno_core::serde_json;
use deno_core::serde_json::Value;
use deno_core::ModuleSpecifier;
-use lsp::WorkspaceFolder;
use lspower::lsp;
use std::collections::BTreeMap;
use std::collections::HashMap;
@@ -128,6 +130,10 @@ impl Default for ImportCompletionSettings {
pub struct SpecifierSettings {
/// A flag that indicates if Deno is enabled for this specifier or not.
pub enable: bool,
+ /// A list of paths, using the workspace folder as a base that should be Deno
+ /// enabled.
+ #[serde(default)]
+ pub enable_paths: Vec<String>,
/// Code lens specific settings for the resource.
#[serde(default)]
pub code_lens: CodeLensSpecifierSettings,
@@ -141,6 +147,10 @@ pub struct WorkspaceSettings {
#[serde(default)]
pub enable: bool,
+ /// A list of paths, using the root_uri as a base that should be Deno enabled.
+ #[serde(default)]
+ pub enable_paths: Vec<String>,
+
/// An option that points to a path string of the path to utilise as the
/// cache/DENO_DIR for the language server.
pub cache: Option<String>,
@@ -198,14 +208,27 @@ impl WorkspaceSettings {
#[derive(Debug, Clone, Default)]
pub struct ConfigSnapshot {
pub client_capabilities: ClientCapabilities,
+ pub enabled_paths: HashMap<String, Vec<String>>,
pub settings: Settings,
- pub workspace_folders: Option<Vec<lsp::WorkspaceFolder>>,
}
impl ConfigSnapshot {
+ /// Determine if the provided specifier is enabled or not.
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
- if let Some(settings) = self.settings.specifiers.get(specifier) {
- settings.1.enable
+ if !self.enabled_paths.is_empty() {
+ let specifier_str = specifier.to_string();
+ for (workspace, enabled_paths) in self.enabled_paths.iter() {
+ if specifier_str.starts_with(workspace) {
+ return enabled_paths
+ .iter()
+ .any(|path| specifier_str.starts_with(path));
+ }
+ }
+ }
+ if let Some((_, SpecifierSettings { enable, .. })) =
+ self.settings.specifiers.get(specifier)
+ {
+ *enable
} else {
self.settings.workspace.enable
}
@@ -228,14 +251,19 @@ pub struct Settings {
#[derive(Debug)]
pub struct Config {
pub client_capabilities: ClientCapabilities,
+ enabled_paths: HashMap<String, Vec<String>>,
+ pub root_uri: Option<ModuleSpecifier>,
settings: Settings,
- pub workspace_folders: Option<Vec<WorkspaceFolder>>,
+ pub workspace_folders: Option<Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>>,
}
impl Config {
pub fn new() -> Self {
Self {
client_capabilities: ClientCapabilities::default(),
+ enabled_paths: Default::default(),
+ /// Root provided by the initialization parameters.
+ root_uri: None,
settings: Default::default(),
workspace_folders: None,
}
@@ -259,8 +287,8 @@ impl Config {
pub fn snapshot(&self) -> Arc<ConfigSnapshot> {
Arc::new(ConfigSnapshot {
client_capabilities: self.client_capabilities.clone(),
+ enabled_paths: self.enabled_paths.clone(),
settings: self.settings.clone(),
- workspace_folders: self.workspace_folders.clone(),
})
}
@@ -269,6 +297,16 @@ impl Config {
}
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
+ if !self.enabled_paths.is_empty() {
+ let specifier_str = specifier.to_string();
+ for (workspace, enabled_paths) in self.enabled_paths.iter() {
+ if specifier_str.starts_with(workspace) {
+ return enabled_paths
+ .iter()
+ .any(|path| specifier_str.starts_with(path));
+ }
+ }
+ }
self
.settings
.specifiers
@@ -321,6 +359,66 @@ impl Config {
}
}
+ /// Given the configured workspaces or root URI and the their settings,
+ /// update and resolve any paths that should be enabled
+ pub async fn update_enabled_paths(&mut self, client: Client) -> bool {
+ if let Some(workspace_folders) = self.workspace_folders.clone() {
+ let mut touched = false;
+ for (workspace, folder) in workspace_folders {
+ if let Ok(settings) = client.specifier_configuration(&folder.uri).await
+ {
+ if self.update_enabled_paths_entry(&workspace, settings.enable_paths)
+ {
+ touched = true;
+ }
+ }
+ }
+ touched
+ } else if let Some(root_uri) = self.root_uri.clone() {
+ self.update_enabled_paths_entry(
+ &root_uri,
+ self.settings.workspace.enable_paths.clone(),
+ )
+ } else {
+ false
+ }
+ }
+
+ /// Update a specific entry in the enabled paths for a given workspace.
+ fn update_enabled_paths_entry(
+ &mut self,
+ workspace: &ModuleSpecifier,
+ enabled_paths: Vec<String>,
+ ) -> bool {
+ let workspace = fs_util::ensure_directory_specifier(workspace.clone());
+ let key = workspace.to_string();
+ let mut touched = false;
+ if !enabled_paths.is_empty() {
+ if let Ok(workspace_path) = fs_util::specifier_to_file_path(&workspace) {
+ let mut paths = Vec::new();
+ for path in &enabled_paths {
+ let fs_path = workspace_path.join(path);
+ match ModuleSpecifier::from_file_path(fs_path) {
+ Ok(path_uri) => {
+ paths.push(path_uri.to_string());
+ }
+ Err(_) => {
+ lsp_log!("Unable to resolve a file path for `deno.enablePath` from \"{}\" for workspace \"{}\".", path, workspace);
+ }
+ }
+ }
+ if !paths.is_empty() {
+ touched = true;
+ self.enabled_paths.insert(key, paths);
+ }
+ }
+ } else {
+ touched = true;
+ self.enabled_paths.remove(&key);
+ }
+ touched
+ }
+
pub fn get_specifiers_with_client_uris(&self) -> Vec<SpecifierWithClientUri> {
self
.settings
@@ -330,7 +428,7 @@ impl Config {
specifier: s.clone(),
client_uri: u.clone(),
})
- .collect::<Vec<_>>()
+ .collect()
}
pub fn set_specifier_settings(
@@ -352,33 +450,9 @@ mod tests {
use deno_core::resolve_url;
use deno_core::serde_json::json;
- #[derive(Debug, Default)]
- struct MockLanguageServer;
-
- #[lspower::async_trait]
- impl lspower::LanguageServer for MockLanguageServer {
- async fn initialize(
- &self,
- _params: lspower::lsp::InitializeParams,
- ) -> lspower::jsonrpc::Result<lsp::InitializeResult> {
- Ok(lspower::lsp::InitializeResult {
- capabilities: lspower::lsp::ServerCapabilities::default(),
- server_info: None,
- })
- }
-
- async fn shutdown(&self) -> lspower::jsonrpc::Result<()> {
- Ok(())
- }
- }
-
- fn setup() -> Config {
- Config::new()
- }
-
#[test]
fn test_config_specifier_enabled() {
- let mut config = setup();
+ let mut config = Config::new();
let specifier = resolve_url("file:///a.ts").unwrap();
assert!(!config.specifier_enabled(&specifier));
config
@@ -390,8 +464,42 @@ mod tests {
}
#[test]
+ fn test_config_snapshot_specifier_enabled() {
+ let mut config = Config::new();
+ let specifier = resolve_url("file:///a.ts").unwrap();
+ assert!(!config.specifier_enabled(&specifier));
+ config
+ .set_workspace_settings(json!({
+ "enable": true
+ }))
+ .expect("could not update");
+ let config_snapshot = config.snapshot();
+ assert!(config_snapshot.specifier_enabled(&specifier));
+ }
+
+ #[test]
+ fn test_config_specifier_enabled_path() {
+ let mut config = Config::new();
+ let specifier_a = resolve_url("file:///project/worker/a.ts").unwrap();
+ let specifier_b = resolve_url("file:///project/other/b.ts").unwrap();
+ assert!(!config.specifier_enabled(&specifier_a));
+ assert!(!config.specifier_enabled(&specifier_b));
+ let mut enabled_paths = HashMap::new();
+ enabled_paths.insert(
+ "file:///project/".to_string(),
+ vec!["file:///project/worker/".to_string()],
+ );
+ config.enabled_paths = enabled_paths;
+ assert!(config.specifier_enabled(&specifier_a));
+ assert!(!config.specifier_enabled(&specifier_b));
+ let config_snapshot = config.snapshot();
+ assert!(config_snapshot.specifier_enabled(&specifier_a));
+ assert!(!config_snapshot.specifier_enabled(&specifier_b));
+ }
+
+ #[test]
fn test_set_workspace_settings_defaults() {
- let mut config = setup();
+ let mut config = Config::new();
config
.set_workspace_settings(json!({}))
.expect("could not update");
@@ -399,6 +507,7 @@ mod tests {
config.get_workspace_settings(),
WorkspaceSettings {
enable: false,
+ enable_paths: Vec::new(),
cache: None,
certificate_stores: None,
config: None,
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index 8801af722..8a515ef3c 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -845,7 +845,8 @@ async fn generate_deps_diagnostics(
break;
}
let mut diagnostics = Vec::new();
- if config.specifier_enabled(document.specifier()) {
+ let specifier = document.specifier();
+ if config.specifier_enabled(specifier) {
for (_, dependency) in document.dependencies() {
diagnose_dependency(
&mut diagnostics,
@@ -866,7 +867,7 @@ async fn generate_deps_diagnostics(
}
}
diagnostics_vec.push((
- document.specifier().clone(),
+ specifier.clone(),
document.maybe_lsp_version(),
diagnostics,
));
@@ -985,6 +986,7 @@ let c: number = "a";
specifier.clone(),
SpecifierSettings {
enable: false,
+ enable_paths: Vec::new(),
code_lens: Default::default(),
},
),
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index f9dfce4d5..5db7011bb 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -117,8 +117,6 @@ pub(crate) struct Inner {
maybe_import_map_uri: Option<Url>,
/// A collection of measurements which instrument that performance of the LSP.
performance: Arc<Performance>,
- /// Root provided by the initialization parameters.
- root_uri: Option<Url>,
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
ts_fixable_diagnostics: Vec<String>,
/// An abstraction that handles interactions with TypeScript.
@@ -173,7 +171,6 @@ impl Inner {
maybe_import_map_uri: None,
module_registries,
module_registries_location,
- root_uri: None,
performance,
ts_fixable_diagnostics: Default::default(),
ts_server,
@@ -306,10 +303,10 @@ impl Inner {
let maybe_config = workspace_settings.config;
if let Some(config_str) = &maybe_config {
if !config_str.is_empty() {
- lsp_log!("Setting TypeScript configuration from: \"{}\"", config_str);
+ lsp_log!("Setting Deno configuration from: \"{}\"", config_str);
let config_url = if let Ok(url) = Url::from_file_path(config_str) {
Ok(url)
- } else if let Some(root_uri) = &self.root_uri {
+ } else if let Some(root_uri) = &self.config.root_uri {
root_uri.join(config_str).map_err(|_| {
anyhow!("Bad file path for configuration file: \"{}\"", config_str)
})
@@ -331,7 +328,7 @@ impl Inner {
// It is possible that root_uri is not set, for example when having a single
// file open and not a workspace. In those situations we can't
// automatically discover the configuration
- if let Some(root_uri) = &self.root_uri {
+ if let Some(root_uri) = &self.config.root_uri {
let root_path = fs_util::specifier_to_file_path(root_uri)?;
let mut checked = std::collections::HashSet::new();
let maybe_config =
@@ -392,7 +389,7 @@ impl Inner {
assets: self.assets.snapshot(),
cache_metadata: self.cache_metadata.clone(),
documents: self.documents.clone(),
- root_uri: self.root_uri.clone(),
+ root_uri: self.config.root_uri.clone(),
})
}
@@ -405,7 +402,7 @@ impl Inner {
lsp_log!("Setting cache path from: \"{}\"", cache_str);
let cache_url = if let Ok(url) = Url::from_file_path(cache_str) {
Ok(url)
- } else if let Some(root_uri) = &self.root_uri {
+ } else if let Some(root_uri) = &self.config.root_uri {
let root_path = fs_util::specifier_to_file_path(root_uri)?;
let cache_path = root_path.join(cache_str);
Url::from_file_path(cache_path).map_err(|_| {
@@ -434,6 +431,7 @@ impl Inner {
let module_registries_location = dir.root.join(REGISTRIES_PATH);
let workspace_settings = self.config.get_workspace_settings();
let maybe_root_path = self
+ .config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
@@ -477,7 +475,7 @@ impl Inner {
Some(Url::parse(&import_map_str).map_err(|_| {
anyhow!("Bad data url for import map: {}", import_map_str)
})?)
- } else if let Some(root_uri) = &self.root_uri {
+ } else if let Some(root_uri) = &self.config.root_uri {
let root_path = fs_util::specifier_to_file_path(root_uri)?;
let import_map_path = root_path.join(&import_map_str);
Some(Url::from_file_path(import_map_path).map_err(|_| {
@@ -554,6 +552,7 @@ impl Inner {
let mark = self.performance.mark("update_registries", None::<()>);
let workspace_settings = self.config.get_workspace_settings();
let maybe_root_path = self
+ .config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
@@ -687,7 +686,7 @@ impl Inner {
{
// sometimes this root uri may not have a trailing slash, so force it to
- self.root_uri = params
+ self.config.root_uri = params
.root_uri
.map(|s| self.url_map.normalize_url(&s))
.map(fs_util::ensure_directory_specifier);
@@ -698,6 +697,12 @@ impl Inner {
LspError::internal_error()
})?;
}
+ self.config.workspace_folders = params.workspace_folders.map(|folders| {
+ folders
+ .into_iter()
+ .map(|folder| (self.url_map.normalize_url(&folder.uri), folder))
+ .collect()
+ });
self.config.update_capabilities(&params.capabilities);
}
@@ -774,6 +779,7 @@ impl Inner {
warn!("Client errored on capabilities.\n{}", err);
}
}
+ self.config.update_enabled_paths(self.client.clone()).await;
lsp_log!("Server ready.");
}
@@ -952,6 +958,34 @@ impl Inner {
self.performance.measure(mark);
}
+ async fn did_change_workspace_folders(
+ &mut self,
+ params: DidChangeWorkspaceFoldersParams,
+ ) {
+ let mark = self
+ .performance
+ .mark("did_change_workspace_folders", Some(&params));
+ let mut workspace_folders = params
+ .event
+ .added
+ .into_iter()
+ .map(|folder| (self.url_map.normalize_url(&folder.uri), folder))
+ .collect::<Vec<(ModuleSpecifier, WorkspaceFolder)>>();
+ if let Some(current_folders) = &self.config.workspace_folders {
+ for (specifier, folder) in current_folders {
+ if !params.event.removed.is_empty()
+ && params.event.removed.iter().any(|f| f.uri == folder.uri)
+ {
+ continue;
+ }
+ workspace_folders.push((specifier.clone(), folder.clone()));
+ }
+ }
+
+ self.config.workspace_folders = Some(workspace_folders);
+ self.performance.measure(mark);
+ }
+
async fn document_symbol(
&mut self,
params: DocumentSymbolParams,
@@ -1896,6 +1930,7 @@ impl Inner {
})?;
let maybe_root_path_owned = self
+ .config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
@@ -1944,6 +1979,7 @@ impl Inner {
})?;
let maybe_root_path_owned = self
+ .config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
@@ -1999,6 +2035,7 @@ impl Inner {
let response = if let Some(one_or_many) = maybe_one_or_many {
let maybe_root_path_owned = self
+ .config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
@@ -2390,7 +2427,7 @@ impl lspower::LanguageServer for LanguageServer {
if document.is_diagnosable() {
let specifiers = inner.documents.dependents(&specifier);
inner.diagnostics_server.invalidate(&specifiers);
- // don't send diagnotics yet if we don't have the specifier settings
+ // don't send diagnostics yet if we don't have the specifier settings
if has_specifier_settings {
inner.send_diagnostics_update();
}
@@ -2467,7 +2504,8 @@ impl lspower::LanguageServer for LanguageServer {
)
};
- // start retreiving all the specifiers' settings outside the lock on its own time
+ // start retrieving all the specifiers' settings outside the lock on its own
+ // time
if let Some(specifiers) = specifiers {
let language_server = self.clone();
let client = client.clone();
@@ -2495,6 +2533,15 @@ impl lspower::LanguageServer for LanguageServer {
}
}
}
+ let mut ls = language_server.0.lock().await;
+ if ls.config.update_enabled_paths(client).await {
+ ls.diagnostics_server.invalidate_all();
+ // this will be called in the inner did_change_configuration, but the
+ // problem then becomes, if there was a change, the snapshot used
+ // will be an out of date one, so we will call it again here if the
+ // workspace folders have been touched
+ ls.send_diagnostics_update();
+ }
});
}
@@ -2533,6 +2580,25 @@ impl lspower::LanguageServer for LanguageServer {
self.0.lock().await.did_change_watched_files(params).await
}
+ async fn did_change_workspace_folders(
+ &self,
+ params: DidChangeWorkspaceFoldersParams,
+ ) {
+ let client = {
+ let mut inner = self.0.lock().await;
+ inner.did_change_workspace_folders(params).await;
+ inner.client.clone()
+ };
+ let language_server = self.clone();
+ tokio::spawn(async move {
+ let mut ls = language_server.0.lock().await;
+ if ls.config.update_enabled_paths(client).await {
+ ls.diagnostics_server.invalidate_all();
+ ls.send_diagnostics_update();
+ }
+ });
+ }
+
async fn document_symbol(
&self,
params: DocumentSymbolParams,
diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs
index a208b2266..1026c7bda 100644
--- a/cli/lsp/repl.rs
+++ b/cli/lsp/repl.rs
@@ -273,6 +273,7 @@ fn get_cwd_uri() -> Result<ModuleSpecifier, AnyError> {
pub fn get_repl_workspace_settings() -> WorkspaceSettings {
WorkspaceSettings {
enable: true,
+ enable_paths: Vec::new(),
config: None,
certificate_stores: None,
cache: None,
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 4f3e88e7d..eaeef7a51 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -882,11 +882,6 @@ impl DocumentSpan {
language_server: &language_server::Inner,
) -> Option<ModuleSpecifier> {
let specifier = normalize_specifier(&self.file_name).ok()?;
- log::info!(
- "to_target file_name: {} specifier: {}",
- self.file_name,
- specifier
- );
let asset_or_doc =
language_server.get_maybe_cached_asset_or_document(&specifier)?;
let line_index = asset_or_doc.line_index();
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 5547dc214..90fbd608d 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -57,24 +57,22 @@ where
.write_notification("textDocument/didOpen", params)
.unwrap();
- handle_configuration_request(client);
+ handle_configuration_request(
+ client,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ );
read_diagnostics(client).0
}
-fn handle_configuration_request(client: &mut LspClient) {
+fn handle_configuration_request(client: &mut LspClient, result: Value) {
let (id, method, _) = client.read_request::<Value>().unwrap();
assert_eq!(method, "workspace/configuration");
- client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- )
- .unwrap();
+ client.write_response(id, result).unwrap();
}
fn read_diagnostics(client: &mut LspClient) -> CollectedDiagnostics {
@@ -97,6 +95,17 @@ fn shutdown(client: &mut LspClient) {
client.write_notification("exit", json!(null)).unwrap();
}
+pub fn ensure_directory_specifier(
+ mut specifier: ModuleSpecifier,
+) -> ModuleSpecifier {
+ let path = specifier.path();
+ if !path.ends_with('/') {
+ let new_path = format!("{}/", path);
+ specifier.set_path(&new_path);
+ }
+ specifier
+}
+
struct TestSession {
client: LspClient,
open_file_count: usize,
@@ -586,7 +595,15 @@ fn lsp_import_assertions() {
}),
)
.unwrap();
- handle_configuration_request(&mut client);
+ handle_configuration_request(
+ &mut client,
+ json!([{
+ "enable": true,
+ "codeLens": {
+ "test": true
+ }
+ }]),
+ );
let diagnostics = CollectedDiagnostics(did_open(
&mut client,
@@ -981,18 +998,136 @@ fn lsp_hover_disabled() {
)
.unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
+ handle_configuration_request(&mut client, json!([{ "enable": false }]));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+ shutdown(&mut client);
+}
+
+#[test]
+fn lsp_workspace_enable_paths() {
+ let mut params: lsp::InitializeParams = serde_json::from_value(load_fixture(
+ "initialize_params_workspace_enable_paths.json",
+ ))
+ .unwrap();
+ // we aren't actually writing anything to the tempdir in this test, but we
+ // just need a legitimate file path on the host system so that logic that
+ // tries to convert to and from the fs paths works on all env
+ let temp_dir = TempDir::new().unwrap();
+
+ let root_specifier =
+ ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
+
+ params.root_uri = Some(root_specifier.clone());
+ params.workspace_folders = Some(vec![lsp::WorkspaceFolder {
+ uri: root_specifier.clone(),
+ name: "project".to_string(),
+ }]);
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe).unwrap();
client
- .write_response(id, json!([{ "enable": false }]))
+ .write_request::<_, _, Value>("initialize", params)
.unwrap();
+ client.write_notification("initialized", json!({})).unwrap();
+
+ handle_configuration_request(
+ &mut client,
+ json!([{
+ "enable": false,
+ "enablePaths": [
+ "./worker"
+ ],
+ }]),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./other/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
+ "languageId": "typescript",
+ "version": 1,
+ "text": "console.log(Date.now());\n"
+ }
+ }),
+ );
+
let (maybe_res, maybe_err) = client
.write_request(
"textDocument/hover",
json!({
"textDocument": {
- "uri": "file:///a/file.ts"
+ "uri": root_specifier.join("./file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./other/file.ts").unwrap(),
},
"position": {
"line": 0,
@@ -1003,6 +1138,83 @@ fn lsp_hover_disabled() {
.unwrap();
assert!(maybe_err.is_none());
assert_eq!(maybe_res, Some(json!(null)));
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(method) DateConstructor.now(): number",
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17,
+ },
+ "end": {
+ "line": 0,
+ "character": 20,
+ }
+ }
+ }))
+ );
+
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
+ },
+ "position": {
+ "line": 0,
+ "character": 19
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value": "(method) DateConstructor.now(): number",
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 17,
+ },
+ "end": {
+ "line": 0,
+ "character": 20,
+ }
+ }
+ }))
+ );
+
shutdown(&mut client);
}
diff --git a/cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json b/cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json
new file mode 100644
index 000000000..87581ebd7
--- /dev/null
+++ b/cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json
@@ -0,0 +1,77 @@
+{
+ "processId": 0,
+ "clientInfo": {
+ "name": "test-harness",
+ "version": "1.0.0"
+ },
+ "rootUri": "file:///project/",
+ "initializationOptions": {
+ "enable": false,
+ "enablePaths": [
+ "./worker"
+ ],
+ "cache": null,
+ "certificateStores": null,
+ "codeLens": {
+ "implementations": true,
+ "references": true,
+ "test": true
+ },
+ "config": "",
+ "importMap": null,
+ "lint": true,
+ "suggest": {
+ "autoImports": true,
+ "completeFunctionCalls": false,
+ "names": true,
+ "paths": true,
+ "imports": {
+ "hosts": {}
+ }
+ },
+ "tlsCertificate": null,
+ "unsafelyIgnoreCertificateErrors": null,
+ "unstable": false
+ },
+ "workspaceFolders": [
+ {
+ "uri": "file:///project/",
+ "name": "project"
+ }
+ ],
+ "capabilities": {
+ "textDocument": {
+ "codeAction": {
+ "codeActionLiteralSupport": {
+ "codeActionKind": {
+ "valueSet": [
+ "quickfix",
+ "refactor"
+ ]
+ }
+ },
+ "isPreferredSupport": true,
+ "dataSupport": true,
+ "disabledSupport": true,
+ "resolveSupport": {
+ "properties": [
+ "edit"
+ ]
+ }
+ },
+ "foldingRange": {
+ "lineFoldingOnly": true
+ },
+ "synchronization": {
+ "dynamicRegistration": true,
+ "willSave": true,
+ "willSaveWaitUntil": true,
+ "didSave": true
+ }
+ },
+ "workspace": {
+ "configuration": true,
+ "workspaceFolders": true
+ }
+ }
+}