mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(lsp): re-enable the per resource configuration without a deadlock (#10625)
Fixes #10603
This commit is contained in:
parent
bdee065d42
commit
014d8d51c0
9 changed files with 499 additions and 189 deletions
145
cli/bench/lsp.rs
145
cli/bench/lsp.rs
|
@ -5,6 +5,8 @@ use deno_core::serde::Deserialize;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
|
use deno_core::url::Url;
|
||||||
|
use lspower::lsp;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -62,16 +64,15 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result<Duration, AnyError> {
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO(@kitsonk) work around https://github.com/denoland/deno/issues/10603
|
let (id, method, _): (u64, String, Option<Value>) = client.read_request()?;
|
||||||
// let (id, method, _): (u64, String, Option<Value>) = client.read_request()?;
|
assert_eq!(method, "workspace/configuration");
|
||||||
// assert_eq!(method, "workspace/configuration");
|
|
||||||
|
|
||||||
// client.write_response(
|
client.write_response(
|
||||||
// id,
|
id,
|
||||||
// json!({
|
json!({
|
||||||
// "enable": true
|
"enable": true
|
||||||
// }),
|
}),
|
||||||
// )?;
|
)?;
|
||||||
|
|
||||||
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
||||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
@ -122,13 +123,108 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result<Duration, AnyError> {
|
||||||
Ok(client.duration())
|
Ok(client.duration())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bench_find_replace(deno_exe: &Path) -> Result<Duration, AnyError> {
|
||||||
|
let mut client = LspClient::new(deno_exe)?;
|
||||||
|
|
||||||
|
let params: Value = serde_json::from_slice(FIXTURE_INIT_JSON)?;
|
||||||
|
let (_, maybe_err) =
|
||||||
|
client.write_request::<_, _, Value>("initialize", params)?;
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
client.write_notification("initialized", json!({}))?;
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
client.write_notification(
|
||||||
|
"textDocument/didOpen",
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": format!("file:///a/file_{}.ts", i),
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "console.log(\"000\");\n"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..10 {
|
||||||
|
let (id, method, _) = client.read_request::<Value>()?;
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client.write_response(id, json!({ "enable": true }))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
let file_name = format!("file:///a/file_{}.ts", i);
|
||||||
|
client.write_notification(
|
||||||
|
"textDocument/didChange",
|
||||||
|
lsp::DidChangeTextDocumentParams {
|
||||||
|
text_document: lsp::VersionedTextDocumentIdentifier {
|
||||||
|
uri: Url::parse(&file_name).unwrap(),
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
content_changes: vec![lsp::TextDocumentContentChangeEvent {
|
||||||
|
range: Some(lsp::Range {
|
||||||
|
start: lsp::Position {
|
||||||
|
line: 0,
|
||||||
|
character: 13,
|
||||||
|
},
|
||||||
|
end: lsp::Position {
|
||||||
|
line: 0,
|
||||||
|
character: 16,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
range_length: None,
|
||||||
|
text: "111".to_string(),
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
let file_name = format!("file:///a/file_{}.ts", i);
|
||||||
|
let (maybe_res, maybe_err) = client.write_request::<_, _, Value>(
|
||||||
|
"textDocument/formatting",
|
||||||
|
lsp::DocumentFormattingParams {
|
||||||
|
text_document: lsp::TextDocumentIdentifier {
|
||||||
|
uri: Url::parse(&file_name).unwrap(),
|
||||||
|
},
|
||||||
|
options: lsp::FormattingOptions {
|
||||||
|
tab_size: 2,
|
||||||
|
insert_spaces: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
assert!(maybe_err.is_none());
|
||||||
|
assert!(maybe_res.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..3 {
|
||||||
|
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
||||||
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, response_error): (Option<Value>, Option<LspResponseError>) =
|
||||||
|
client.write_request("shutdown", json!(null))?;
|
||||||
|
assert!(response_error.is_none());
|
||||||
|
|
||||||
|
client.write_notification("exit", json!(null))?;
|
||||||
|
|
||||||
|
Ok(client.duration())
|
||||||
|
}
|
||||||
|
|
||||||
/// A test that starts up the LSP, opens a single line document, and exits.
|
/// A test that starts up the LSP, opens a single line document, and exits.
|
||||||
fn bench_startup_shutdown(deno_exe: &Path) -> Result<Duration, AnyError> {
|
fn bench_startup_shutdown(deno_exe: &Path) -> Result<Duration, AnyError> {
|
||||||
let mut client = LspClient::new(deno_exe)?;
|
let mut client = LspClient::new(deno_exe)?;
|
||||||
|
|
||||||
let params: Value = serde_json::from_slice(FIXTURE_INIT_JSON)?;
|
let params: Value = serde_json::from_slice(FIXTURE_INIT_JSON)?;
|
||||||
let (_, response_error): (Option<Value>, Option<LspResponseError>) =
|
let (_, response_error) =
|
||||||
client.write_request("initialize", params)?;
|
client.write_request::<_, _, Value>("initialize", params)?;
|
||||||
assert!(response_error.is_none());
|
assert!(response_error.is_none());
|
||||||
|
|
||||||
client.write_notification("initialized", json!({}))?;
|
client.write_notification("initialized", json!({}))?;
|
||||||
|
@ -145,16 +241,15 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Result<Duration, AnyError> {
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO(@kitsonk) work around https://github.com/denoland/deno/issues/10603
|
let (id, method, _) = client.read_request::<Value>()?;
|
||||||
// let (id, method, _): (u64, String, Option<Value>) = client.read_request()?;
|
assert_eq!(method, "workspace/configuration");
|
||||||
// assert_eq!(method, "workspace/configuration");
|
|
||||||
|
|
||||||
// client.write_response(
|
client.write_response(
|
||||||
// id,
|
id,
|
||||||
// json!({
|
json!({
|
||||||
// "enable": true
|
"enable": true
|
||||||
// }),
|
}),
|
||||||
// )?;
|
)?;
|
||||||
|
|
||||||
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
let (method, _): (String, Option<Value>) = client.read_notification()?;
|
||||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
|
@ -199,6 +294,16 @@ pub(crate) fn benchmarks(
|
||||||
println!(" ({} runs, mean: {}ms)", times.len(), mean);
|
println!(" ({} runs, mean: {}ms)", times.len(), mean);
|
||||||
exec_times.insert("big_file_edits".to_string(), mean);
|
exec_times.insert("big_file_edits".to_string(), mean);
|
||||||
|
|
||||||
|
println!(" - Find/Replace");
|
||||||
|
let mut times = Vec::new();
|
||||||
|
for _ in 0..10 {
|
||||||
|
times.push(bench_find_replace(deno_exe)?);
|
||||||
|
}
|
||||||
|
let mean =
|
||||||
|
(times.iter().sum::<Duration>() / times.len() as u32).as_millis() as u64;
|
||||||
|
println!(" ({} runs, mean: {}ms)", times.len(), mean);
|
||||||
|
exec_times.insert("find_replace".to_string(), mean);
|
||||||
|
|
||||||
println!("<- End benchmarking lsp");
|
println!("<- End benchmarking lsp");
|
||||||
|
|
||||||
Ok(exec_times)
|
Ok(exec_times)
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use crate::tokio_util::create_basic_runtime;
|
||||||
|
|
||||||
|
use deno_core::error::anyhow;
|
||||||
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde::Deserialize;
|
use deno_core::serde::Deserialize;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
use deno_core::url::Url;
|
use deno_core::url::Url;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use lspower::jsonrpc::Error as LSPError;
|
use log::error;
|
||||||
use lspower::jsonrpc::Result as LSPResult;
|
|
||||||
use lspower::lsp;
|
use lspower::lsp;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
use std::thread;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub const SETTINGS_SECTION: &str = "deno";
|
pub const SETTINGS_SECTION: &str = "deno";
|
||||||
|
|
||||||
|
@ -139,25 +147,201 @@ impl WorkspaceSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ConfigSnapshot {
|
||||||
|
pub client_capabilities: ClientCapabilities,
|
||||||
|
pub root_uri: Option<Url>,
|
||||||
|
pub settings: Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConfigSnapshot {
|
||||||
|
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
|
if let Some(settings) = self.settings.specifiers.get(specifier) {
|
||||||
|
settings.1.enable
|
||||||
|
} else {
|
||||||
|
self.settings.workspace.enable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConfigRequest {
|
||||||
|
Specifier(ModuleSpecifier, ModuleSpecifier),
|
||||||
|
Workspace,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub specifiers:
|
||||||
|
BTreeMap<ModuleSpecifier, (ModuleSpecifier, SpecifierSettings)>,
|
||||||
|
pub workspace: WorkspaceSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub client_capabilities: ClientCapabilities,
|
pub client_capabilities: ClientCapabilities,
|
||||||
pub root_uri: Option<Url>,
|
pub root_uri: Option<Url>,
|
||||||
pub specifier_settings: HashMap<ModuleSpecifier, SpecifierSettings>,
|
settings: Arc<RwLock<Settings>>,
|
||||||
pub workspace_settings: WorkspaceSettings,
|
tx: mpsc::Sender<ConfigRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
#[allow(unused)]
|
pub fn new(client: lspower::Client) -> Self {
|
||||||
pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
|
let (tx, mut rx) = mpsc::channel::<ConfigRequest>(100);
|
||||||
self.specifier_settings.contains_key(specifier)
|
let settings = Arc::new(RwLock::new(Settings::default()));
|
||||||
|
let settings_ref = settings.clone();
|
||||||
|
|
||||||
|
let _join_handle = thread::spawn(move || {
|
||||||
|
let runtime = create_basic_runtime();
|
||||||
|
|
||||||
|
runtime.block_on(async {
|
||||||
|
loop {
|
||||||
|
match rx.recv().await {
|
||||||
|
None => break,
|
||||||
|
Some(ConfigRequest::Workspace) => {
|
||||||
|
let mut items = vec![lsp::ConfigurationItem {
|
||||||
|
scope_uri: None,
|
||||||
|
section: Some(SETTINGS_SECTION.to_string()),
|
||||||
|
}];
|
||||||
|
let (specifier_uri_map, mut specifier_items): (
|
||||||
|
Vec<(ModuleSpecifier, ModuleSpecifier)>,
|
||||||
|
Vec<lsp::ConfigurationItem>,
|
||||||
|
) = {
|
||||||
|
let settings = settings_ref.read().unwrap();
|
||||||
|
(
|
||||||
|
settings
|
||||||
|
.specifiers
|
||||||
|
.iter()
|
||||||
|
.map(|(s, (u, _))| (s.clone(), u.clone()))
|
||||||
|
.collect(),
|
||||||
|
settings
|
||||||
|
.specifiers
|
||||||
|
.iter()
|
||||||
|
.map(|(_, (uri, _))| lsp::ConfigurationItem {
|
||||||
|
scope_uri: Some(uri.clone()),
|
||||||
|
section: Some(SETTINGS_SECTION.to_string()),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
items.append(&mut specifier_items);
|
||||||
|
if let Ok(configs) = client.configuration(items).await {
|
||||||
|
let mut settings = settings_ref.write().unwrap();
|
||||||
|
for (i, value) in configs.into_iter().enumerate() {
|
||||||
|
match i {
|
||||||
|
0 => {
|
||||||
|
match serde_json::from_value::<WorkspaceSettings>(value) {
|
||||||
|
Ok(workspace_settings) => {
|
||||||
|
settings.workspace = workspace_settings;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Error converting workspace settings: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
match serde_json::from_value::<SpecifierSettings>(value) {
|
||||||
|
Ok(specifier_settings) => {
|
||||||
|
let (specifier, uri) =
|
||||||
|
specifier_uri_map[i - 1].clone();
|
||||||
|
settings
|
||||||
|
.specifiers
|
||||||
|
.insert(specifier, (uri, specifier_settings));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
"Error converting specifier settings: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(ConfigRequest::Specifier(specifier, uri)) => {
|
||||||
|
if settings_ref
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.specifiers
|
||||||
|
.contains_key(&specifier)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Ok(value) = client
|
||||||
|
.configuration(vec![lsp::ConfigurationItem {
|
||||||
|
scope_uri: Some(uri.clone()),
|
||||||
|
section: Some(SETTINGS_SECTION.to_string()),
|
||||||
|
}])
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
match serde_json::from_value::<SpecifierSettings>(
|
||||||
|
value[0].clone(),
|
||||||
|
) {
|
||||||
|
Ok(specifier_settings) => {
|
||||||
|
settings_ref
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.specifiers
|
||||||
|
.insert(specifier, (uri, specifier_settings));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error converting specifier settings: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"Error retrieving settings for specifier: {}",
|
||||||
|
specifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
client_capabilities: ClientCapabilities::default(),
|
||||||
|
root_uri: None,
|
||||||
|
settings,
|
||||||
|
tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_workspace_settings(&self) -> WorkspaceSettings {
|
||||||
|
self.settings.read().unwrap().workspace.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the workspace settings directly, which occurs during initialization
|
||||||
|
/// and when the client does not support workspace configuration requests
|
||||||
|
pub fn set_workspace_settings(&self, value: Value) -> Result<(), AnyError> {
|
||||||
|
let workspace_settings = serde_json::from_value(value)?;
|
||||||
|
self.settings.write().unwrap().workspace = workspace_settings;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn snapshot(&self) -> Result<ConfigSnapshot, AnyError> {
|
||||||
|
Ok(ConfigSnapshot {
|
||||||
|
client_capabilities: self.client_capabilities.clone(),
|
||||||
|
root_uri: self.root_uri.clone(),
|
||||||
|
settings: self
|
||||||
|
.settings
|
||||||
|
.try_read()
|
||||||
|
.map_err(|_| anyhow!("Error reading settings."))?
|
||||||
|
.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool {
|
||||||
if let Some(settings) = self.specifier_settings.get(specifier) {
|
let settings = self.settings.read().unwrap();
|
||||||
settings.enable
|
if let Some(specifier_settings) = settings.specifiers.get(specifier) {
|
||||||
|
specifier_settings.1.enable
|
||||||
} else {
|
} else {
|
||||||
self.workspace_settings.enable
|
settings.workspace.enable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,23 +376,24 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_specifier(
|
pub async fn update_specifier_settings(
|
||||||
&mut self,
|
&self,
|
||||||
specifier: ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
value: Value,
|
uri: &ModuleSpecifier,
|
||||||
) -> LSPResult<()> {
|
) -> Result<(), AnyError> {
|
||||||
let settings: SpecifierSettings = serde_json::from_value(value)
|
self
|
||||||
.map_err(|err| LSPError::invalid_params(err.to_string()))?;
|
.tx
|
||||||
self.specifier_settings.insert(specifier, settings);
|
.send(ConfigRequest::Specifier(specifier.clone(), uri.clone()))
|
||||||
Ok(())
|
.await
|
||||||
|
.map_err(|_| anyhow!("Error sending config update task."))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_workspace(&mut self, value: Value) -> LSPResult<()> {
|
pub async fn update_workspace_settings(&self) -> Result<(), AnyError> {
|
||||||
let settings: WorkspaceSettings = serde_json::from_value(value)
|
self
|
||||||
.map_err(|err| LSPError::invalid_params(err.to_string()))?;
|
.tx
|
||||||
self.workspace_settings = settings;
|
.send(ConfigRequest::Workspace)
|
||||||
self.specifier_settings = HashMap::new();
|
.await
|
||||||
Ok(())
|
.map_err(|_| anyhow!("Error sending config update task."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,41 +403,45 @@ mod tests {
|
||||||
use deno_core::resolve_url;
|
use deno_core::resolve_url;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
|
|
||||||
#[test]
|
#[derive(Debug, Default)]
|
||||||
fn test_config_contains() {
|
struct MockLanguageServer;
|
||||||
let mut config = Config::default();
|
|
||||||
let specifier = resolve_url("https://deno.land/x/a.ts").unwrap();
|
#[lspower::async_trait]
|
||||||
assert!(!config.contains(&specifier));
|
impl lspower::LanguageServer for MockLanguageServer {
|
||||||
config
|
async fn initialize(
|
||||||
.update_specifier(
|
&self,
|
||||||
specifier.clone(),
|
_params: lspower::lsp::InitializeParams,
|
||||||
json!({
|
) -> lspower::jsonrpc::Result<lsp::InitializeResult> {
|
||||||
"enable": true
|
Ok(lspower::lsp::InitializeResult {
|
||||||
}),
|
capabilities: lspower::lsp::ServerCapabilities::default(),
|
||||||
)
|
server_info: None,
|
||||||
.expect("could not update specifier");
|
})
|
||||||
assert!(config.contains(&specifier));
|
}
|
||||||
|
|
||||||
|
async fn shutdown(&self) -> lspower::jsonrpc::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> Config {
|
||||||
|
let mut maybe_client: Option<lspower::Client> = None;
|
||||||
|
let (_service, _) = lspower::LspService::new(|client| {
|
||||||
|
maybe_client = Some(client);
|
||||||
|
MockLanguageServer::default()
|
||||||
|
});
|
||||||
|
Config::new(maybe_client.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_config_specifier_enabled() {
|
fn test_config_specifier_enabled() {
|
||||||
let mut config = Config::default();
|
let config = setup();
|
||||||
let specifier = resolve_url("file:///a.ts").unwrap();
|
let specifier = resolve_url("file:///a.ts").unwrap();
|
||||||
assert!(!config.specifier_enabled(&specifier));
|
assert!(!config.specifier_enabled(&specifier));
|
||||||
config
|
config
|
||||||
.update_workspace(json!({
|
.set_workspace_settings(json!({
|
||||||
"enable": true
|
"enable": true
|
||||||
}))
|
}))
|
||||||
.expect("could not update");
|
.expect("could not update");
|
||||||
assert!(config.specifier_enabled(&specifier));
|
assert!(config.specifier_enabled(&specifier));
|
||||||
config
|
|
||||||
.update_specifier(
|
|
||||||
specifier.clone(),
|
|
||||||
json!({
|
|
||||||
"enable": false
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.expect("could not update");
|
|
||||||
assert!(!config.specifier_enabled(&specifier));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl DiagnosticsServer {
|
||||||
dirty = false;
|
dirty = false;
|
||||||
debounce_timer.as_mut().reset(Instant::now() + NEVER);
|
debounce_timer.as_mut().reset(Instant::now() + NEVER);
|
||||||
|
|
||||||
let snapshot = language_server.lock().await.snapshot();
|
let snapshot = language_server.lock().await.snapshot().unwrap();
|
||||||
update_diagnostics(
|
update_diagnostics(
|
||||||
&client,
|
&client,
|
||||||
collection.clone(),
|
collection.clone(),
|
||||||
|
@ -314,7 +314,7 @@ async fn generate_lint_diagnostics(
|
||||||
collection: Arc<Mutex<DiagnosticCollection>>,
|
collection: Arc<Mutex<DiagnosticCollection>>,
|
||||||
) -> Result<DiagnosticVec, AnyError> {
|
) -> Result<DiagnosticVec, AnyError> {
|
||||||
let documents = snapshot.documents.clone();
|
let documents = snapshot.documents.clone();
|
||||||
let workspace_settings = snapshot.config.workspace_settings.clone();
|
let workspace_settings = snapshot.config.settings.workspace.clone();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
let mut diagnostics_vec = Vec::new();
|
let mut diagnostics_vec = Vec::new();
|
||||||
if workspace_settings.lint {
|
if workspace_settings.lint {
|
||||||
|
@ -487,7 +487,7 @@ async fn publish_diagnostics(
|
||||||
if let Some(changes) = collection.take_changes() {
|
if let Some(changes) = collection.take_changes() {
|
||||||
for specifier in changes {
|
for specifier in changes {
|
||||||
let mut diagnostics: Vec<lsp::Diagnostic> =
|
let mut diagnostics: Vec<lsp::Diagnostic> =
|
||||||
if snapshot.config.workspace_settings.lint {
|
if snapshot.config.settings.workspace.lint {
|
||||||
collection
|
collection
|
||||||
.get(&specifier, DiagnosticSource::DenoLint)
|
.get(&specifier, DiagnosticSource::DenoLint)
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
|
@ -38,6 +38,7 @@ use super::analysis::ResolvedDependency;
|
||||||
use super::capabilities;
|
use super::capabilities;
|
||||||
use super::completions;
|
use super::completions;
|
||||||
use super::config::Config;
|
use super::config::Config;
|
||||||
|
use super::config::ConfigSnapshot;
|
||||||
use super::config::SETTINGS_SECTION;
|
use super::config::SETTINGS_SECTION;
|
||||||
use super::diagnostics;
|
use super::diagnostics;
|
||||||
use super::diagnostics::DiagnosticSource;
|
use super::diagnostics::DiagnosticSource;
|
||||||
|
@ -77,7 +78,7 @@ pub struct LanguageServer(Arc<tokio::sync::Mutex<Inner>>);
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct StateSnapshot {
|
pub struct StateSnapshot {
|
||||||
pub assets: Assets,
|
pub assets: Assets,
|
||||||
pub config: Config,
|
pub config: ConfigSnapshot,
|
||||||
pub documents: DocumentCache,
|
pub documents: DocumentCache,
|
||||||
pub module_registries: registries::ModuleRegistry,
|
pub module_registries: registries::ModuleRegistry,
|
||||||
pub performance: Performance,
|
pub performance: Performance,
|
||||||
|
@ -141,11 +142,12 @@ impl Inner {
|
||||||
let ts_server = Arc::new(TsServer::new());
|
let ts_server = Arc::new(TsServer::new());
|
||||||
let performance = Performance::default();
|
let performance = Performance::default();
|
||||||
let diagnostics_server = diagnostics::DiagnosticsServer::new();
|
let diagnostics_server = diagnostics::DiagnosticsServer::new();
|
||||||
|
let config = Config::new(client.clone());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
assets: Default::default(),
|
assets: Default::default(),
|
||||||
client,
|
client,
|
||||||
config: Default::default(),
|
config,
|
||||||
diagnostics_server,
|
diagnostics_server,
|
||||||
documents: Default::default(),
|
documents: Default::default(),
|
||||||
maybe_config_uri: Default::default(),
|
maybe_config_uri: Default::default(),
|
||||||
|
@ -282,7 +284,7 @@ impl Inner {
|
||||||
let navigation_tree: tsc::NavigationTree = self
|
let navigation_tree: tsc::NavigationTree = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(
|
.request(
|
||||||
self.snapshot(),
|
self.snapshot()?,
|
||||||
tsc::RequestMethod::GetNavigationTree(specifier.clone()),
|
tsc::RequestMethod::GetNavigationTree(specifier.clone()),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -294,16 +296,19 @@ impl Inner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn snapshot(&self) -> StateSnapshot {
|
pub(crate) fn snapshot(&self) -> LspResult<StateSnapshot> {
|
||||||
StateSnapshot {
|
Ok(StateSnapshot {
|
||||||
assets: self.assets.clone(),
|
assets: self.assets.clone(),
|
||||||
config: self.config.clone(),
|
config: self.config.snapshot().map_err(|err| {
|
||||||
|
error!("{}", err);
|
||||||
|
LspError::internal_error()
|
||||||
|
})?,
|
||||||
documents: self.documents.clone(),
|
documents: self.documents.clone(),
|
||||||
module_registries: self.module_registries.clone(),
|
module_registries: self.module_registries.clone(),
|
||||||
performance: self.performance.clone(),
|
performance: self.performance.clone(),
|
||||||
sources: self.sources.clone(),
|
sources: self.sources.clone(),
|
||||||
url_map: self.url_map.clone(),
|
url_map: self.url_map.clone(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
|
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
|
||||||
|
@ -311,7 +316,7 @@ impl Inner {
|
||||||
let (maybe_import_map, maybe_root_uri) = {
|
let (maybe_import_map, maybe_root_uri) = {
|
||||||
let config = &self.config;
|
let config = &self.config;
|
||||||
(
|
(
|
||||||
config.workspace_settings.import_map.clone(),
|
config.get_workspace_settings().import_map,
|
||||||
config.root_uri.clone(),
|
config.root_uri.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -357,10 +362,11 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_debug_flag(&self) -> bool {
|
pub fn update_debug_flag(&self) -> bool {
|
||||||
|
let internal_debug = self.config.get_workspace_settings().internal_debug;
|
||||||
logger::LSP_DEBUG_FLAG
|
logger::LSP_DEBUG_FLAG
|
||||||
.compare_exchange(
|
.compare_exchange(
|
||||||
!self.config.workspace_settings.internal_debug,
|
!internal_debug,
|
||||||
self.config.workspace_settings.internal_debug,
|
internal_debug,
|
||||||
Ordering::Acquire,
|
Ordering::Acquire,
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
)
|
)
|
||||||
|
@ -369,8 +375,13 @@ impl Inner {
|
||||||
|
|
||||||
async fn update_registries(&mut self) -> Result<(), AnyError> {
|
async fn update_registries(&mut self) -> Result<(), AnyError> {
|
||||||
let mark = self.performance.mark("update_registries", None::<()>);
|
let mark = self.performance.mark("update_registries", None::<()>);
|
||||||
for (registry, enabled) in
|
for (registry, enabled) in self
|
||||||
self.config.workspace_settings.suggest.imports.hosts.iter()
|
.config
|
||||||
|
.get_workspace_settings()
|
||||||
|
.suggest
|
||||||
|
.imports
|
||||||
|
.hosts
|
||||||
|
.iter()
|
||||||
{
|
{
|
||||||
if *enabled {
|
if *enabled {
|
||||||
info!("Enabling auto complete registry for: {}", registry);
|
info!("Enabling auto complete registry for: {}", registry);
|
||||||
|
@ -401,16 +412,14 @@ impl Inner {
|
||||||
}));
|
}));
|
||||||
let (maybe_config, maybe_root_uri) = {
|
let (maybe_config, maybe_root_uri) = {
|
||||||
let config = &self.config;
|
let config = &self.config;
|
||||||
if config.workspace_settings.unstable {
|
let workspace_settings = config.get_workspace_settings();
|
||||||
|
if workspace_settings.unstable {
|
||||||
let unstable_libs = json!({
|
let unstable_libs = json!({
|
||||||
"lib": ["deno.ns", "deno.window", "deno.unstable"]
|
"lib": ["deno.ns", "deno.window", "deno.unstable"]
|
||||||
});
|
});
|
||||||
tsconfig.merge(&unstable_libs);
|
tsconfig.merge(&unstable_libs);
|
||||||
}
|
}
|
||||||
(
|
(workspace_settings.config, config.root_uri.clone())
|
||||||
config.workspace_settings.config.clone(),
|
|
||||||
config.root_uri.clone(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
if let Some(config_str) = &maybe_config {
|
if let Some(config_str) = &maybe_config {
|
||||||
info!("Updating TypeScript configuration from: \"{}\"", config_str);
|
info!("Updating TypeScript configuration from: \"{}\"", config_str);
|
||||||
|
@ -443,7 +452,7 @@ impl Inner {
|
||||||
}
|
}
|
||||||
let _ok: bool = self
|
let _ok: bool = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig))
|
.request(self.snapshot()?, tsc::RequestMethod::Configure(tsconfig))
|
||||||
.await?;
|
.await?;
|
||||||
self.performance.measure(mark);
|
self.performance.measure(mark);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -464,7 +473,7 @@ impl Inner {
|
||||||
return Ok(maybe_asset.clone());
|
return Ok(maybe_asset.clone());
|
||||||
} else {
|
} else {
|
||||||
let maybe_asset =
|
let maybe_asset =
|
||||||
tsc::get_asset(&specifier, &self.ts_server, self.snapshot()).await?;
|
tsc::get_asset(&specifier, &self.ts_server, self.snapshot()?).await?;
|
||||||
self.assets.insert(specifier.clone(), maybe_asset.clone());
|
self.assets.insert(specifier.clone(), maybe_asset.clone());
|
||||||
Ok(maybe_asset)
|
Ok(maybe_asset)
|
||||||
}
|
}
|
||||||
|
@ -507,7 +516,10 @@ impl Inner {
|
||||||
let config = &mut self.config;
|
let config = &mut self.config;
|
||||||
config.root_uri = params.root_uri;
|
config.root_uri = params.root_uri;
|
||||||
if let Some(value) = params.initialization_options {
|
if let Some(value) = params.initialization_options {
|
||||||
config.update_workspace(value)?;
|
config.set_workspace_settings(value).map_err(|err| {
|
||||||
|
error!("Cannot set workspace settings: {}", err);
|
||||||
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
config.update_capabilities(¶ms.capabilities);
|
config.update_capabilities(¶ms.capabilities);
|
||||||
}
|
}
|
||||||
|
@ -520,7 +532,7 @@ impl Inner {
|
||||||
if capabilities.code_action_provider.is_some() {
|
if capabilities.code_action_provider.is_some() {
|
||||||
let fixable_diagnostics: Vec<String> = self
|
let fixable_diagnostics: Vec<String> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), tsc::RequestMethod::GetSupportedCodeFixes)
|
.request(self.snapshot()?, tsc::RequestMethod::GetSupportedCodeFixes)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get fixable diagnostics: {}", err);
|
error!("Unable to get fixable diagnostics: {}", err);
|
||||||
|
@ -592,27 +604,13 @@ impl Inner {
|
||||||
let mark = self.performance.mark("did_open", Some(¶ms));
|
let mark = self.performance.mark("did_open", Some(¶ms));
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
|
|
||||||
// we only query the individual resource file if the client supports it
|
if let Err(err) = self
|
||||||
// TODO(@kitsonk) workaround https://github.com/denoland/deno/issues/10603
|
.config
|
||||||
// if self.config.client_capabilities.workspace_configuration
|
.update_specifier_settings(&specifier, ¶ms.text_document.uri)
|
||||||
// && !self.config.contains(&specifier)
|
.await
|
||||||
// {
|
{
|
||||||
// if let Ok(value) = self
|
error!("Error updating specifier settings: {}", err);
|
||||||
// .client
|
}
|
||||||
// .configuration(vec![ConfigurationItem {
|
|
||||||
// scope_uri: Some(params.text_document.uri.clone()),
|
|
||||||
// section: Some(SETTINGS_SECTION.to_string()),
|
|
||||||
// }])
|
|
||||||
// .await
|
|
||||||
// {
|
|
||||||
// if let Err(err) = self
|
|
||||||
// .config
|
|
||||||
// .update_specifier(specifier.clone(), value[0].clone())
|
|
||||||
// {
|
|
||||||
// warn!("Error updating specifier configuration: {}", err);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if params.text_document.uri.scheme() == "deno" {
|
if params.text_document.uri.scheme() == "deno" {
|
||||||
// we can ignore virtual text documents opening, as they don't need to
|
// we can ignore virtual text documents opening, as they don't need to
|
||||||
|
@ -679,32 +677,8 @@ impl Inner {
|
||||||
.mark("did_change_configuration", Some(¶ms));
|
.mark("did_change_configuration", Some(¶ms));
|
||||||
|
|
||||||
if self.config.client_capabilities.workspace_configuration {
|
if self.config.client_capabilities.workspace_configuration {
|
||||||
let specifiers: Vec<ModuleSpecifier> =
|
if let Err(err) = self.config.update_workspace_settings().await {
|
||||||
self.config.specifier_settings.keys().cloned().collect();
|
error!("Error updating workspace settings: {}", err);
|
||||||
let mut snapshot = self.snapshot();
|
|
||||||
let mut config_items = specifiers
|
|
||||||
.iter()
|
|
||||||
.map(|s| ConfigurationItem {
|
|
||||||
scope_uri: Some(snapshot.url_map.normalize_specifier(s).unwrap()),
|
|
||||||
section: Some(SETTINGS_SECTION.to_string()),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let mut items = vec![ConfigurationItem {
|
|
||||||
scope_uri: None,
|
|
||||||
section: Some(SETTINGS_SECTION.to_string()),
|
|
||||||
}];
|
|
||||||
items.append(&mut config_items);
|
|
||||||
if let Ok(configs) = self.client.configuration(items).await {
|
|
||||||
for (i, value) in configs.into_iter().enumerate() {
|
|
||||||
if let Err(err) = match i {
|
|
||||||
0 => self.config.update_workspace(value),
|
|
||||||
_ => self
|
|
||||||
.config
|
|
||||||
.update_specifier(specifiers[i - 1].clone(), value),
|
|
||||||
} {
|
|
||||||
error!("failed to update settings: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if let Some(config) = params
|
} else if let Some(config) = params
|
||||||
.settings
|
.settings
|
||||||
|
@ -713,7 +687,7 @@ impl Inner {
|
||||||
.flatten()
|
.flatten()
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
if let Err(err) = self.config.update_workspace(config) {
|
if let Err(err) = self.config.set_workspace_settings(config) {
|
||||||
error!("failed to update settings: {}", err);
|
error!("failed to update settings: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -804,7 +778,7 @@ impl Inner {
|
||||||
let req = tsc::RequestMethod::GetNavigationTree(specifier);
|
let req = tsc::RequestMethod::GetNavigationTree(specifier);
|
||||||
let navigation_tree: tsc::NavigationTree = self
|
let navigation_tree: tsc::NavigationTree = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -900,7 +874,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_quick_info: Option<tsc::QuickInfo> = self
|
let maybe_quick_info: Option<tsc::QuickInfo> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get quick info: {}", err);
|
error!("Unable to get quick info: {}", err);
|
||||||
|
@ -977,7 +951,7 @@ impl Inner {
|
||||||
codes,
|
codes,
|
||||||
));
|
));
|
||||||
let actions: Vec<tsc::CodeFixAction> =
|
let actions: Vec<tsc::CodeFixAction> =
|
||||||
match self.ts_server.request(self.snapshot(), req).await {
|
match self.ts_server.request(self.snapshot()?, req).await {
|
||||||
Ok(items) => items,
|
Ok(items) => items,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// sometimes tsc reports errors when retrieving code actions
|
// sometimes tsc reports errors when retrieving code actions
|
||||||
|
@ -1040,7 +1014,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let combined_code_actions: tsc::CombinedCodeActions = self
|
let combined_code_actions: tsc::CombinedCodeActions = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get combined fix from TypeScript: {}", err);
|
error!("Unable to get combined fix from TypeScript: {}", err);
|
||||||
|
@ -1074,7 +1048,7 @@ impl Inner {
|
||||||
) -> LspResult<Option<Vec<CodeLens>>> {
|
) -> LspResult<Option<Vec<CodeLens>>> {
|
||||||
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
let specifier = self.url_map.normalize_url(¶ms.text_document.uri);
|
||||||
if !self.config.specifier_enabled(&specifier)
|
if !self.config.specifier_enabled(&specifier)
|
||||||
|| !self.config.workspace_settings.enabled_code_lens()
|
|| !self.config.get_workspace_settings().enabled_code_lens()
|
||||||
{
|
{
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
@ -1093,9 +1067,10 @@ impl Inner {
|
||||||
let cl = Rc::new(RefCell::new(Vec::new()));
|
let cl = Rc::new(RefCell::new(Vec::new()));
|
||||||
navigation_tree.walk(&|i, mp| {
|
navigation_tree.walk(&|i, mp| {
|
||||||
let mut code_lenses = cl.borrow_mut();
|
let mut code_lenses = cl.borrow_mut();
|
||||||
|
let workspace_settings = self.config.get_workspace_settings();
|
||||||
|
|
||||||
// TSC Implementations Code Lens
|
// TSC Implementations Code Lens
|
||||||
if self.config.workspace_settings.code_lens.implementations {
|
if workspace_settings.code_lens.implementations {
|
||||||
let source = CodeLensSource::Implementations;
|
let source = CodeLensSource::Implementations;
|
||||||
match i.kind {
|
match i.kind {
|
||||||
tsc::ScriptElementKind::InterfaceElement => {
|
tsc::ScriptElementKind::InterfaceElement => {
|
||||||
|
@ -1119,7 +1094,7 @@ impl Inner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TSC References Code Lens
|
// TSC References Code Lens
|
||||||
if self.config.workspace_settings.code_lens.references {
|
if workspace_settings.code_lens.references {
|
||||||
let source = CodeLensSource::References;
|
let source = CodeLensSource::References;
|
||||||
if let Some(parent) = &mp {
|
if let Some(parent) = &mp {
|
||||||
if parent.kind == tsc::ScriptElementKind::EnumElement {
|
if parent.kind == tsc::ScriptElementKind::EnumElement {
|
||||||
|
@ -1128,12 +1103,7 @@ impl Inner {
|
||||||
}
|
}
|
||||||
match i.kind {
|
match i.kind {
|
||||||
tsc::ScriptElementKind::FunctionElement => {
|
tsc::ScriptElementKind::FunctionElement => {
|
||||||
if self
|
if workspace_settings.code_lens.references_all_functions {
|
||||||
.config
|
|
||||||
.workspace_settings
|
|
||||||
.code_lens
|
|
||||||
.references_all_functions
|
|
||||||
{
|
|
||||||
code_lenses.push(i.to_code_lens(
|
code_lenses.push(i.to_code_lens(
|
||||||
&line_index,
|
&line_index,
|
||||||
&specifier,
|
&specifier,
|
||||||
|
@ -1214,12 +1184,14 @@ impl Inner {
|
||||||
line_index.offset_tsc(params.range.start)?,
|
line_index.offset_tsc(params.range.start)?,
|
||||||
));
|
));
|
||||||
let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> =
|
let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> =
|
||||||
self.ts_server.request(self.snapshot(), req).await.map_err(
|
self
|
||||||
|err| {
|
.ts_server
|
||||||
|
.request(self.snapshot()?, req)
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
error!("Error processing TypeScript request: {}", err);
|
error!("Error processing TypeScript request: {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
if let Some(implementations) = maybe_implementations {
|
if let Some(implementations) = maybe_implementations {
|
||||||
let mut locations = Vec::new();
|
let mut locations = Vec::new();
|
||||||
for implementation in implementations {
|
for implementation in implementations {
|
||||||
|
@ -1292,13 +1264,14 @@ impl Inner {
|
||||||
code_lens_data.specifier.clone(),
|
code_lens_data.specifier.clone(),
|
||||||
line_index.offset_tsc(params.range.start)?,
|
line_index.offset_tsc(params.range.start)?,
|
||||||
));
|
));
|
||||||
let maybe_references: Option<Vec<tsc::ReferenceEntry>> =
|
let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self
|
||||||
self.ts_server.request(self.snapshot(), req).await.map_err(
|
.ts_server
|
||||||
|err| {
|
.request(self.snapshot()?, req)
|
||||||
error!("Error processing TypeScript request: {}", err);
|
.await
|
||||||
LspError::internal_error()
|
.map_err(|err| {
|
||||||
},
|
error!("Error processing TypeScript request: {}", err);
|
||||||
)?;
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
if let Some(references) = maybe_references {
|
if let Some(references) = maybe_references {
|
||||||
let mut locations = Vec::new();
|
let mut locations = Vec::new();
|
||||||
for reference in references {
|
for reference in references {
|
||||||
|
@ -1408,7 +1381,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_document_highlights: Option<Vec<tsc::DocumentHighlights>> = self
|
let maybe_document_highlights: Option<Vec<tsc::DocumentHighlights>> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get document highlights from TypeScript: {}", err);
|
error!("Unable to get document highlights from TypeScript: {}", err);
|
||||||
|
@ -1455,7 +1428,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self
|
let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get references from TypeScript: {}", err);
|
error!("Unable to get references from TypeScript: {}", err);
|
||||||
|
@ -1510,7 +1483,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_definition: Option<tsc::DefinitionInfoAndBoundSpan> = self
|
let maybe_definition: Option<tsc::DefinitionInfoAndBoundSpan> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get definition from TypeScript: {}", err);
|
error!("Unable to get definition from TypeScript: {}", err);
|
||||||
|
@ -1545,7 +1518,7 @@ impl Inner {
|
||||||
let response = if let Some(response) = completions::get_import_completions(
|
let response = if let Some(response) = completions::get_import_completions(
|
||||||
&specifier,
|
&specifier,
|
||||||
¶ms.text_document_position.position,
|
¶ms.text_document_position.position,
|
||||||
&self.snapshot(),
|
&self.snapshot()?,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
@ -1580,7 +1553,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_completion_info: Option<tsc::CompletionInfo> = self
|
let maybe_completion_info: Option<tsc::CompletionInfo> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Unable to get completion info from TypeScript: {}", err);
|
error!("Unable to get completion info from TypeScript: {}", err);
|
||||||
|
@ -1590,7 +1563,7 @@ impl Inner {
|
||||||
if let Some(completions) = maybe_completion_info {
|
if let Some(completions) = maybe_completion_info {
|
||||||
let results = completions.as_completion_response(
|
let results = completions.as_completion_response(
|
||||||
&line_index,
|
&line_index,
|
||||||
&self.config.workspace_settings.suggest,
|
&self.config.get_workspace_settings().suggest,
|
||||||
&specifier,
|
&specifier,
|
||||||
position,
|
position,
|
||||||
);
|
);
|
||||||
|
@ -1618,13 +1591,14 @@ impl Inner {
|
||||||
})?;
|
})?;
|
||||||
if let Some(data) = data.tsc {
|
if let Some(data) = data.tsc {
|
||||||
let req = tsc::RequestMethod::GetCompletionDetails(data.into());
|
let req = tsc::RequestMethod::GetCompletionDetails(data.into());
|
||||||
let maybe_completion_info: Option<tsc::CompletionEntryDetails> =
|
let maybe_completion_info: Option<tsc::CompletionEntryDetails> = self
|
||||||
self.ts_server.request(self.snapshot(), req).await.map_err(
|
.ts_server
|
||||||
|err| {
|
.request(self.snapshot()?, req)
|
||||||
error!("Unable to get completion info from TypeScript: {}", err);
|
.await
|
||||||
LspError::internal_error()
|
.map_err(|err| {
|
||||||
},
|
error!("Unable to get completion info from TypeScript: {}", err);
|
||||||
)?;
|
LspError::internal_error()
|
||||||
|
})?;
|
||||||
if let Some(completion_info) = maybe_completion_info {
|
if let Some(completion_info) = maybe_completion_info {
|
||||||
completion_info.as_completion_item(¶ms)
|
completion_info.as_completion_item(¶ms)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1670,7 +1644,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = self
|
let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -1716,7 +1690,7 @@ impl Inner {
|
||||||
let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone());
|
let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone());
|
||||||
let outlining_spans: Vec<tsc::OutliningSpan> = self
|
let outlining_spans: Vec<tsc::OutliningSpan> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -1776,7 +1750,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let incoming_calls: Vec<tsc::CallHierarchyIncomingCall> = self
|
let incoming_calls: Vec<tsc::CallHierarchyIncomingCall> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -1830,7 +1804,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let outgoing_calls: Vec<tsc::CallHierarchyOutgoingCall> = self
|
let outgoing_calls: Vec<tsc::CallHierarchyOutgoingCall> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -1890,7 +1864,7 @@ impl Inner {
|
||||||
let maybe_one_or_many: Option<tsc::OneOrMany<tsc::CallHierarchyItem>> =
|
let maybe_one_or_many: Option<tsc::OneOrMany<tsc::CallHierarchyItem>> =
|
||||||
self
|
self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -1970,7 +1944,7 @@ impl Inner {
|
||||||
|
|
||||||
let maybe_locations: Option<Vec<tsc::RenameLocation>> = self
|
let maybe_locations: Option<Vec<tsc::RenameLocation>> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -2057,7 +2031,7 @@ impl Inner {
|
||||||
|
|
||||||
let selection_range: tsc::SelectionRange = self
|
let selection_range: tsc::SelectionRange = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -2099,7 +2073,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let semantic_classification: tsc::Classifications = self
|
let semantic_classification: tsc::Classifications = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -2147,7 +2121,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let semantic_classification: tsc::Classifications = self
|
let semantic_classification: tsc::Classifications = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver {}", err);
|
error!("Failed to request to tsserver {}", err);
|
||||||
|
@ -2204,7 +2178,7 @@ impl Inner {
|
||||||
));
|
));
|
||||||
let maybe_signature_help_items: Option<tsc::SignatureHelpItems> = self
|
let maybe_signature_help_items: Option<tsc::SignatureHelpItems> = self
|
||||||
.ts_server
|
.ts_server
|
||||||
.request(self.snapshot(), req)
|
.request(self.snapshot()?, req)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
error!("Failed to request to tsserver: {}", err);
|
error!("Failed to request to tsserver: {}", err);
|
||||||
|
|
|
@ -38,6 +38,13 @@ where
|
||||||
client
|
client
|
||||||
.write_notification("textDocument/didOpen", params)
|
.write_notification("textDocument/didOpen", params)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": true }))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
@ -209,6 +216,13 @@ fn lsp_hover_disabled() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": false }))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (maybe_res, maybe_err) = client
|
let (maybe_res, maybe_err) = client
|
||||||
.write_request(
|
.write_request(
|
||||||
"textDocument/hover",
|
"textDocument/hover",
|
||||||
|
@ -453,6 +467,12 @@ fn lsp_hover_closed_document() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": true }))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
client
|
client
|
||||||
.write_notification(
|
.write_notification(
|
||||||
"textDocument/didOpen",
|
"textDocument/didOpen",
|
||||||
|
@ -466,6 +486,12 @@ fn lsp_hover_closed_document() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let (id, method, _) = client.read_request::<Value>().unwrap();
|
||||||
|
assert_eq!(method, "workspace/configuration");
|
||||||
|
client
|
||||||
|
.write_response(id, json!({ "enable": true }))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
assert_eq!(method, "textDocument/publishDiagnostics");
|
assert_eq!(method, "textDocument/publishDiagnostics");
|
||||||
let (method, _) = client.read_notification::<Value>().unwrap();
|
let (method, _) = client.read_notification::<Value>().unwrap();
|
||||||
|
|
|
@ -51,6 +51,10 @@
|
||||||
"willSaveWaitUntil": true,
|
"willSaveWaitUntil": true,
|
||||||
"didSave": true
|
"didSave": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,10 @@
|
||||||
"willSaveWaitUntil": true,
|
"willSaveWaitUntil": true,
|
||||||
"didSave": true
|
"didSave": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@
|
||||||
"willSaveWaitUntil": true,
|
"willSaveWaitUntil": true,
|
||||||
"didSave": true
|
"didSave": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,10 @@
|
||||||
"willSaveWaitUntil": true,
|
"willSaveWaitUntil": true,
|
||||||
"didSave": true
|
"didSave": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"workspace": {
|
||||||
|
"configuration": true,
|
||||||
|
"workspaceFolders": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue