mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
feat(lsp): support deno.enablePaths setting (#13978)
Ref: denoland/vscode_deno#633
This commit is contained in:
parent
e43707792d
commit
fc8aa5ad8b
8 changed files with 534 additions and 72 deletions
|
@ -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,
|
||||
}),
|
||||
|
|
|
@ -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
|
||||
|
@ -389,9 +463,43 @@ mod tests {
|
|||
assert!(config.specifier_enabled(&specifier));
|
||||
}
|
||||
|
||||
#[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,
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -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(¶ms.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(¶ms));
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,11 +998,7 @@ fn lsp_hover_disabled() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||
assert_eq!(method, "workspace/configuration");
|
||||
client
|
||||
.write_response(id, json!([{ "enable": false }]))
|
||||
.unwrap();
|
||||
handle_configuration_request(&mut client, json!([{ "enable": false }]));
|
||||
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
|
@ -1006,6 +1019,205 @@ fn lsp_hover_disabled() {
|
|||
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_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": 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,
|
||||
"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("./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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_hover_unstable_disabled() {
|
||||
let mut client = init("initialize_params.json");
|
||||
|
|
77
cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json
vendored
Normal file
77
cli/tests/testdata/lsp/initialize_params_workspace_enable_paths.json
vendored
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue