From 96abb27e7328391e9319c99dd23595e42f7982a2 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 18 May 2021 06:45:13 +1000 Subject: [PATCH 01/53] refactor: share test harness for lsp between bench and integration (#10659) --- Cargo.lock | 2 + cli/bench/lsp.rs | 266 +- cli/lsp/language_server.rs | 2395 ----------------- cli/tests/integration_tests_lsp.rs | 1661 ++++++++++++ ...e_request.json => code_action_params.json} | 27 +- cli/tests/lsp/code_action_params_cache.json | 41 + cli/tests/lsp/code_action_request.json | 44 - cli/tests/lsp/code_action_request_cache.json | 46 - cli/tests/lsp/code_action_resolve_params.json | 27 + ...json => code_action_resolve_response.json} | 0 cli/tests/lsp/code_lens_request.json | 10 - cli/tests/lsp/code_lens_request_asset.json | 10 - cli/tests/lsp/code_lens_resolve_request.json | 21 - .../lsp/code_lens_resolve_request_asset.json | 21 - .../lsp/code_lens_resolve_request_impl.json | 21 - cli/tests/lsp/code_lens_resolve_response.json | 38 + .../lsp/code_lens_resolve_response_impl.json | 38 + cli/tests/lsp/code_lens_response.json | 34 + cli/tests/lsp/code_lens_response_impl.json | 50 + cli/tests/lsp/completion_request.json | 18 - .../lsp/completion_request_optional.json | 18 - .../completion_request_params_optional.json | 13 + .../lsp/completion_request_registry.json | 18 - .../lsp/completion_request_registry_02.json | 18 - .../completion_request_response_empty.json | 38 + cli/tests/lsp/completion_resolve_params.json | 14 + .../completion_resolve_params_optional.json | 15 + .../completion_resolve_params_registry.json | 20 + cli/tests/lsp/completion_resolve_request.json | 19 - .../completion_resolve_request_optional.json | 20 - .../completion_resolve_request_registry.json | 25 - .../lsp/completion_resolve_response.json | 11 + .../completion_resolve_response_registry.json | 20 + cli/tests/lsp/definition_request_asset.json | 14 - .../lsp/did_change_notification_large.json | 25 - .../lsp/did_change_notification_large_02.json | 25 - .../lsp/did_change_notification_large_03.json | 25 - .../lsp/did_change_notification_mbc.json | 25 - cli/tests/lsp/did_open_notification.json | 12 - .../lsp/did_open_notification_asset.json | 12 - .../lsp/did_open_notification_cache.json | 12 - .../lsp/did_open_notification_cl_impl.json | 12 - .../did_open_notification_cl_references.json | 12 - .../did_open_notification_code_action.json | 12 - ...open_notification_completion_optional.json | 12 - ...open_notification_completion_registry.json | 12 - ...n_notification_completion_registry_02.json | 12 - .../did_open_notification_completions.json | 12 - .../lsp/did_open_notification_large.json | 12 - cli/tests/lsp/did_open_notification_mbc.json | 12 - .../lsp/did_open_notification_mbc_fmt.json | 12 - .../lsp/did_open_notification_unstable.json | 12 - cli/tests/lsp/did_open_params_doc_symbol.json | 8 + cli/tests/lsp/did_open_params_large.json | 8 + .../lsp/did_open_params_semantic_tokens.json | 8 + ...document_symbol_did_open_notification.json | 12 - cli/tests/lsp/document_symbol_request.json | 10 - cli/tests/lsp/document_symbol_response.json | 371 +++ cli/tests/lsp/exit_notification.json | 5 - .../folding_range_did_open_notification.json | 12 - cli/tests/lsp/folding_range_request.json | 10 - cli/tests/lsp/formatting_mbc_response.json | 54 + cli/tests/lsp/formatting_request_mbc_fmt.json | 14 - cli/tests/lsp/hover_request.json | 14 - cli/tests/lsp/hover_request_asset.json | 14 - cli/tests/lsp/hover_request_large_01.json | 14 - cli/tests/lsp/hover_request_large_02.json | 14 - cli/tests/lsp/hover_request_large_03.json | 14 - cli/tests/lsp/hover_request_mbc.json | 14 - cli/tests/lsp/incoming_calls_params.json | 28 + cli/tests/lsp/incoming_calls_request.json | 33 - cli/tests/lsp/incoming_calls_response.json | 42 + cli/tests/lsp/initialize_params.json | 56 + cli/tests/lsp/initialize_params_disabled.json | 56 + cli/tests/lsp/initialize_params_registry.json | 58 + cli/tests/lsp/initialize_params_unstable.json | 56 + cli/tests/lsp/initialize_request.json | 61 - .../lsp/initialize_request_disabled.json | 29 - .../lsp/initialize_request_registry.json | 63 - .../lsp/initialize_request_unstable.json | 30 - cli/tests/lsp/initialized_notification.json | 5 - cli/tests/lsp/outgoing_calls_params.json | 28 + cli/tests/lsp/outgoing_calls_request.json | 33 - cli/tests/lsp/outgoing_calls_response.json | 42 + cli/tests/lsp/performance_request.json | 6 - ..._call_hierarchy_did_open_notification.json | 12 - .../lsp/prepare_call_hierarchy_request.json | 14 - .../lsp/prepare_call_hierarchy_response.json | 28 + cli/tests/lsp/references_request_asset.json | 17 - .../lsp/rename_did_open_notification.json | 12 - cli/tests/lsp/rename_request.json | 15 - cli/tests/lsp/rename_response.json | 38 + ...selection_range_did_open_notification.json | 12 - cli/tests/lsp/selection_range_request.json | 16 - cli/tests/lsp/selection_range_response.json | 86 + ...semantic_tokens_did_open_notification.json | 12 - .../lsp/semantic_tokens_full_request.json | 10 - .../lsp/semantic_tokens_range_request.json | 20 - cli/tests/lsp/shutdown_request.json | 6 - ...ignature_help_did_change_notification.json | 25 - .../signature_help_did_open_notification.json | 12 - cli/tests/lsp/signature_help_request_01.json | 19 - cli/tests/lsp/signature_help_request_02.json | 14 - .../lsp/virtual_text_document_request.json | 10 - test_util/Cargo.toml | 2 + test_util/src/lib.rs | 2 + test_util/src/lsp.rs | 270 ++ 107 files changed, 3296 insertions(+), 3879 deletions(-) create mode 100644 cli/tests/integration_tests_lsp.rs rename cli/tests/lsp/{code_action_resolve_request.json => code_action_params.json} (65%) create mode 100644 cli/tests/lsp/code_action_params_cache.json delete mode 100644 cli/tests/lsp/code_action_request.json delete mode 100644 cli/tests/lsp/code_action_request_cache.json create mode 100644 cli/tests/lsp/code_action_resolve_params.json rename cli/tests/lsp/{code_action_resolve_request_response.json => code_action_resolve_response.json} (100%) delete mode 100644 cli/tests/lsp/code_lens_request.json delete mode 100644 cli/tests/lsp/code_lens_request_asset.json delete mode 100644 cli/tests/lsp/code_lens_resolve_request.json delete mode 100644 cli/tests/lsp/code_lens_resolve_request_asset.json delete mode 100644 cli/tests/lsp/code_lens_resolve_request_impl.json create mode 100644 cli/tests/lsp/code_lens_resolve_response.json create mode 100644 cli/tests/lsp/code_lens_resolve_response_impl.json create mode 100644 cli/tests/lsp/code_lens_response.json create mode 100644 cli/tests/lsp/code_lens_response_impl.json delete mode 100644 cli/tests/lsp/completion_request.json delete mode 100644 cli/tests/lsp/completion_request_optional.json create mode 100644 cli/tests/lsp/completion_request_params_optional.json delete mode 100644 cli/tests/lsp/completion_request_registry.json delete mode 100644 cli/tests/lsp/completion_request_registry_02.json create mode 100644 cli/tests/lsp/completion_request_response_empty.json create mode 100644 cli/tests/lsp/completion_resolve_params.json create mode 100644 cli/tests/lsp/completion_resolve_params_optional.json create mode 100644 cli/tests/lsp/completion_resolve_params_registry.json delete mode 100644 cli/tests/lsp/completion_resolve_request.json delete mode 100644 cli/tests/lsp/completion_resolve_request_optional.json delete mode 100644 cli/tests/lsp/completion_resolve_request_registry.json create mode 100644 cli/tests/lsp/completion_resolve_response.json create mode 100644 cli/tests/lsp/completion_resolve_response_registry.json delete mode 100644 cli/tests/lsp/definition_request_asset.json delete mode 100644 cli/tests/lsp/did_change_notification_large.json delete mode 100644 cli/tests/lsp/did_change_notification_large_02.json delete mode 100644 cli/tests/lsp/did_change_notification_large_03.json delete mode 100644 cli/tests/lsp/did_change_notification_mbc.json delete mode 100644 cli/tests/lsp/did_open_notification.json delete mode 100644 cli/tests/lsp/did_open_notification_asset.json delete mode 100644 cli/tests/lsp/did_open_notification_cache.json delete mode 100644 cli/tests/lsp/did_open_notification_cl_impl.json delete mode 100644 cli/tests/lsp/did_open_notification_cl_references.json delete mode 100644 cli/tests/lsp/did_open_notification_code_action.json delete mode 100644 cli/tests/lsp/did_open_notification_completion_optional.json delete mode 100644 cli/tests/lsp/did_open_notification_completion_registry.json delete mode 100644 cli/tests/lsp/did_open_notification_completion_registry_02.json delete mode 100644 cli/tests/lsp/did_open_notification_completions.json delete mode 100644 cli/tests/lsp/did_open_notification_large.json delete mode 100644 cli/tests/lsp/did_open_notification_mbc.json delete mode 100644 cli/tests/lsp/did_open_notification_mbc_fmt.json delete mode 100644 cli/tests/lsp/did_open_notification_unstable.json create mode 100644 cli/tests/lsp/did_open_params_doc_symbol.json create mode 100644 cli/tests/lsp/did_open_params_large.json create mode 100644 cli/tests/lsp/did_open_params_semantic_tokens.json delete mode 100644 cli/tests/lsp/document_symbol_did_open_notification.json delete mode 100644 cli/tests/lsp/document_symbol_request.json create mode 100644 cli/tests/lsp/document_symbol_response.json delete mode 100644 cli/tests/lsp/exit_notification.json delete mode 100644 cli/tests/lsp/folding_range_did_open_notification.json delete mode 100644 cli/tests/lsp/folding_range_request.json create mode 100644 cli/tests/lsp/formatting_mbc_response.json delete mode 100644 cli/tests/lsp/formatting_request_mbc_fmt.json delete mode 100644 cli/tests/lsp/hover_request.json delete mode 100644 cli/tests/lsp/hover_request_asset.json delete mode 100644 cli/tests/lsp/hover_request_large_01.json delete mode 100644 cli/tests/lsp/hover_request_large_02.json delete mode 100644 cli/tests/lsp/hover_request_large_03.json delete mode 100644 cli/tests/lsp/hover_request_mbc.json create mode 100644 cli/tests/lsp/incoming_calls_params.json delete mode 100644 cli/tests/lsp/incoming_calls_request.json create mode 100644 cli/tests/lsp/incoming_calls_response.json create mode 100644 cli/tests/lsp/initialize_params.json create mode 100644 cli/tests/lsp/initialize_params_disabled.json create mode 100644 cli/tests/lsp/initialize_params_registry.json create mode 100644 cli/tests/lsp/initialize_params_unstable.json delete mode 100644 cli/tests/lsp/initialize_request.json delete mode 100644 cli/tests/lsp/initialize_request_disabled.json delete mode 100644 cli/tests/lsp/initialize_request_registry.json delete mode 100644 cli/tests/lsp/initialize_request_unstable.json delete mode 100644 cli/tests/lsp/initialized_notification.json create mode 100644 cli/tests/lsp/outgoing_calls_params.json delete mode 100644 cli/tests/lsp/outgoing_calls_request.json create mode 100644 cli/tests/lsp/outgoing_calls_response.json delete mode 100644 cli/tests/lsp/performance_request.json delete mode 100644 cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json delete mode 100644 cli/tests/lsp/prepare_call_hierarchy_request.json create mode 100644 cli/tests/lsp/prepare_call_hierarchy_response.json delete mode 100644 cli/tests/lsp/references_request_asset.json delete mode 100644 cli/tests/lsp/rename_did_open_notification.json delete mode 100644 cli/tests/lsp/rename_request.json create mode 100644 cli/tests/lsp/rename_response.json delete mode 100644 cli/tests/lsp/selection_range_did_open_notification.json delete mode 100644 cli/tests/lsp/selection_range_request.json create mode 100644 cli/tests/lsp/selection_range_response.json delete mode 100644 cli/tests/lsp/semantic_tokens_did_open_notification.json delete mode 100644 cli/tests/lsp/semantic_tokens_full_request.json delete mode 100644 cli/tests/lsp/semantic_tokens_range_request.json delete mode 100644 cli/tests/lsp/shutdown_request.json delete mode 100644 cli/tests/lsp/signature_help_did_change_notification.json delete mode 100644 cli/tests/lsp/signature_help_did_open_notification.json delete mode 100644 cli/tests/lsp/signature_help_request_01.json delete mode 100644 cli/tests/lsp/signature_help_request_02.json delete mode 100644 cli/tests/lsp/virtual_text_document_request.json create mode 100644 test_util/src/lsp.rs diff --git a/Cargo.lock b/Cargo.lock index af4168fcd6..9ce829a97b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3471,6 +3471,7 @@ dependencies = [ name = "test_util" version = "0.1.0" dependencies = [ + "anyhow", "async-stream", "bytes", "futures", @@ -3480,6 +3481,7 @@ dependencies = [ "pty", "regex", "serde", + "serde_json", "tempfile", "tokio", "tokio-rustls", diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs index ed474b9eaa..cabcef1dbf 100644 --- a/cli/bench/lsp.rs +++ b/cli/bench/lsp.rs @@ -1,37 +1,21 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use deno_core::error::generic_error; use deno_core::error::AnyError; -use deno_core::serde::de; use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; -use lazy_static::lazy_static; -use regex::Regex; use std::collections::HashMap; -use std::io::BufRead; -use std::io::Read; -use std::io::Write; use std::path::Path; -use std::process::ChildStdin; -use std::process::ChildStdout; -use std::process::Command; -use std::process::Stdio; use std::time::Duration; -use std::time::Instant; +use test_util::lsp::LspClient; +use test_util::lsp::LspResponseError; static FIXTURE_DB_TS: &str = include_str!("fixtures/db.ts"); static FIXTURE_DB_MESSAGES: &[u8] = include_bytes!("fixtures/db_messages.json"); static FIXTURE_INIT_JSON: &[u8] = include_bytes!("fixtures/initialize_params.json"); -lazy_static! { - static ref CONTENT_TYPE_REG: Regex = - Regex::new(r"(?i)^content-length:\s+(\d+)").unwrap(); -} - #[derive(Debug, Deserialize)] enum FixtureType { #[serde(rename = "action")] @@ -53,224 +37,6 @@ struct FixtureMessage { params: Value, } -#[derive(Debug, Deserialize, Serialize)] -struct LspResponseError { - code: i32, - message: String, - data: Option, -} - -#[derive(Debug)] -enum LspMessage { - Notification(String, Option), - Request(u64, String, Option), - Response(u64, Option, Option), -} - -impl<'a> From<&'a [u8]> for LspMessage { - fn from(s: &'a [u8]) -> Self { - let value: Value = serde_json::from_slice(s).unwrap(); - let obj = value.as_object().unwrap(); - if obj.contains_key("id") && obj.contains_key("method") { - let id = obj.get("id").unwrap().as_u64().unwrap(); - let method = obj.get("method").unwrap().as_str().unwrap().to_string(); - Self::Request(id, method, obj.get("params").cloned()) - } else if obj.contains_key("id") { - let id = obj.get("id").unwrap().as_u64().unwrap(); - let maybe_error: Option = obj - .get("error") - .map(|v| serde_json::from_value(v.clone()).unwrap()); - Self::Response(id, obj.get("result").cloned(), maybe_error) - } else { - assert!(obj.contains_key("method")); - let method = obj.get("method").unwrap().as_str().unwrap().to_string(); - Self::Notification(method, obj.get("params").cloned()) - } - } -} - -struct LspClient { - reader: std::io::BufReader, - child: std::process::Child, - request_id: u64, - start: Instant, - writer: std::io::BufWriter, -} - -fn read_message(reader: &mut R) -> Result, AnyError> -where - R: Read + BufRead, -{ - let mut content_length = 0_usize; - loop { - let mut buf = String::new(); - reader.read_line(&mut buf)?; - if let Some(captures) = CONTENT_TYPE_REG.captures(&buf) { - let content_length_match = captures - .get(1) - .ok_or_else(|| generic_error("missing capture"))?; - content_length = content_length_match.as_str().parse::()?; - } - if &buf == "\r\n" { - break; - } - } - - let mut msg_buf = vec![0_u8; content_length]; - reader.read_exact(&mut msg_buf)?; - Ok(msg_buf) -} - -impl Drop for LspClient { - fn drop(&mut self) { - match self.child.try_wait() { - Ok(None) => { - self.child.kill().unwrap(); - let _ = self.child.wait(); - } - Ok(Some(status)) => panic!("deno lsp exited unexpectedly {}", status), - Err(e) => panic!("pebble error: {}", e), - } - } -} - -impl LspClient { - fn new(deno_exe: &Path) -> Result { - let mut child = Command::new(deno_exe) - .arg("lsp") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn()?; - - let stdout = child.stdout.take().unwrap(); - let reader = std::io::BufReader::new(stdout); - - let stdin = child.stdin.take().unwrap(); - let writer = std::io::BufWriter::new(stdin); - - Ok(Self { - child, - reader, - request_id: 1, - start: Instant::now(), - writer, - }) - } - - fn duration(&self) -> Duration { - self.start.elapsed() - } - - fn read(&mut self) -> Result { - let msg_buf = read_message(&mut self.reader)?; - let msg = LspMessage::from(msg_buf.as_slice()); - Ok(msg) - } - - fn read_notification(&mut self) -> Result<(String, Option), AnyError> - where - R: de::DeserializeOwned, - { - loop { - if let LspMessage::Notification(method, maybe_params) = self.read()? { - if let Some(p) = maybe_params { - let params = serde_json::from_value(p)?; - return Ok((method, Some(params))); - } else { - return Ok((method, None)); - } - } - } - } - - #[allow(unused)] - fn read_request(&mut self) -> Result<(u64, String, Option), AnyError> - where - R: de::DeserializeOwned, - { - loop { - if let LspMessage::Request(id, method, maybe_params) = self.read()? { - if let Some(p) = maybe_params { - let params = serde_json::from_value(p)?; - return Ok((id, method, Some(params))); - } else { - return Ok((id, method, None)); - } - } - } - } - - fn write(&mut self, value: Value) -> Result<(), AnyError> { - let value_str = value.to_string(); - let msg = format!( - "Content-Length: {}\r\n\r\n{}", - value_str.as_bytes().len(), - value_str - ); - self.writer.write_all(msg.as_bytes())?; - self.writer.flush()?; - Ok(()) - } - - fn write_request( - &mut self, - method: S, - params: V, - ) -> Result<(Option, Option), AnyError> - where - S: AsRef, - V: Serialize, - { - let value = json!({ - "jsonrpc": "2.0", - "id": self.request_id, - "method": method.as_ref(), - "params": params, - }); - self.write(value)?; - - loop { - if let LspMessage::Response(id, result, error) = self.read()? { - assert_eq!(id, self.request_id); - self.request_id += 1; - return Ok((result, error)); - } - } - } - - #[allow(unused)] - fn write_response(&mut self, id: u64, result: V) -> Result<(), AnyError> - where - V: Serialize, - { - let value = json!({ - "jsonrpc": "2.0", - "id": id, - "result": result - }); - self.write(value) - } - - fn write_notification( - &mut self, - method: S, - params: V, - ) -> Result<(), AnyError> - where - S: AsRef, - V: Serialize, - { - let value = json!({ - "jsonrpc": "2.0", - "method": method.as_ref(), - "params": params, - }); - self.write(value)?; - Ok(()) - } -} - /// A benchmark that opens a 8000+ line TypeScript document, adds a function to /// the end of the document and does a level of hovering and gets quick fix /// code actions. @@ -320,19 +86,29 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result { for msg in messages { match msg.fixture_type { FixtureType::Action => { - client.write_request("textDocument/codeAction", msg.params)?; + client.write_request::<_, _, Value>( + "textDocument/codeAction", + msg.params, + )?; } FixtureType::Change => { client.write_notification("textDocument/didChange", msg.params)?; } FixtureType::Completion => { - client.write_request("textDocument/completion", msg.params)?; + client.write_request::<_, _, Value>( + "textDocument/completion", + msg.params, + )?; } FixtureType::Highlight => { - client.write_request("textDocument/documentHighlight", msg.params)?; + client.write_request::<_, _, Value>( + "textDocument/documentHighlight", + msg.params, + )?; } FixtureType::Hover => { - client.write_request("textDocument/hover", msg.params)?; + client + .write_request::<_, _, Value>("textDocument/hover", msg.params)?; } } } @@ -427,13 +203,3 @@ pub(crate) fn benchmarks( Ok(exec_times) } - -#[cfg(test)] -mod tests { - #[test] - fn test_read_message() { - let msg = b"content-length: 11\r\n\r\nhello world"; - let reader = std::io::Cursor::new(msg); - assert_eq!(read_message(reader).unwrap(), b"hello world"); - } -} diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 1be65d0fe4..9f5a7c84ad 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -2608,2398 +2608,3 @@ impl Inner { Ok(contents) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::lsp::performance::PerformanceAverage; - use lspower::jsonrpc; - use lspower::ExitedError; - use lspower::LspService; - use std::fs; - use std::task::Poll; - use std::time::Instant; - use tempfile::TempDir; - use tower_test::mock::Spawn; - - enum LspResponse - where - V: FnOnce(Value), - { - None, - Delay(u64), - RequestAny, - Request(u64, Value), - RequestAssert(V), - RequestFixture(u64, String), - } - - enum LspFixture { - None, - Path(&'static str), - Value(Value), - } - - type LspTestHarnessRequest = (LspFixture, LspResponse); - - struct LspTestHarness { - requests: Vec, - service: Spawn, - } - - impl LspTestHarness { - pub fn new(requests: Vec) -> Self { - let (service, _) = LspService::new(LanguageServer::new); - let service = Spawn::new(service); - Self { requests, service } - } - - async fn run(&mut self) { - for (value_or_str, expected) in self.requests.iter() { - assert_eq!(self.service.poll_ready(), Poll::Ready(Ok(()))); - let fixtures_path = test_util::root_path().join("cli/tests/lsp"); - assert!(fixtures_path.is_dir()); - let response: Result, ExitedError> = - match value_or_str { - LspFixture::None => Ok(None), - LspFixture::Path(req_path_str) => { - let req_path = fixtures_path.join(req_path_str); - let req_str = fs::read_to_string(req_path).unwrap(); - let req: jsonrpc::Incoming = - serde_json::from_str(&req_str).unwrap(); - self.service.call(req).await - } - LspFixture::Value(value) => { - let req: jsonrpc::Incoming = - serde_json::from_value(value.clone()).unwrap(); - self.service.call(req).await - } - }; - match response { - Ok(result) => match expected { - LspResponse::None => assert_eq!(result, None), - LspResponse::Delay(millis) => { - tokio::time::sleep(tokio::time::Duration::from_millis(*millis)) - .await - } - LspResponse::RequestAny => match result { - Some(jsonrpc::Outgoing::Response(_)) => (), - _ => panic!("unexpected result: {:?}", result), - }, - LspResponse::Request(id, value) => match result { - Some(jsonrpc::Outgoing::Response(resp)) => assert_eq!( - resp, - jsonrpc::Response::ok(jsonrpc::Id::Number(*id), value.clone()) - ), - _ => panic!("unexpected result: {:?}", result), - }, - LspResponse::RequestAssert(assert) => match result { - Some(jsonrpc::Outgoing::Response(resp)) => assert(json!(resp)), - _ => panic!("unexpected result: {:?}", result), - }, - LspResponse::RequestFixture(id, res_path_str) => { - let res_path = fixtures_path.join(res_path_str); - let res_str = fs::read_to_string(res_path).unwrap(); - match result { - Some(jsonrpc::Outgoing::Response(resp)) => assert_eq!( - resp, - jsonrpc::Response::ok( - jsonrpc::Id::Number(*id), - serde_json::from_str(&res_str).unwrap() - ) - ), - _ => panic!("unexpected result: {:?}", result), - } - } - }, - Err(err) => panic!("Error result: {}", err), - } - } - } - } - - #[tokio::test] - async fn test_startup_shutdown() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request.json"), - LspResponse::Request( - 2, - json!({ - "contents": [ - { - "language": "typescript", - "value": "const Deno.args: string[]" - }, - "Returns the script arguments to the program. If for example we run a\nprogram:\n\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n\nThen `Deno.args` will contain:\n\n[ \"/etc/passwd\" ]" - ], - "range": { - "start": { - "line": 0, - "character": 17 - }, - "end": { - "line": 0, - "character": 21 - } - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover_asset() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_asset.json"), - LspResponse::None, - ), - ( - LspFixture::Path("definition_request_asset.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("virtual_text_document_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("hover_request_asset.json"), - LspResponse::Request( - 5, - json!({ - "contents": [ - { - "language": "typescript", - "value": "interface Date", - }, - "Enables basic storage and retrieval of dates and times." - ], - "range": { - "start": { - "line": 109, - "character": 10, - }, - "end": { - "line": 109, - "character": 14, - } - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover_disabled() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request_disabled.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request.json"), - LspResponse::Request(2, json!(null)), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover_unstable_disabled() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_unstable.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request.json"), - LspResponse::Request( - 2, - json!({ - "contents": [ - { - "language": "typescript", - "value": "any" - } - ], - "range": { - "start": { - "line": 0, - "character": 17 - }, - "end": { - "line": 0, - "character": 27 - } - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover_unstable_enabled() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request_unstable.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_unstable.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request.json"), - LspResponse::Request( - 2, - json!({ - "contents": [ - { - "language": "typescript", - "value": "function Deno.openPlugin(filename: string): number" - }, - "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\nconst opId = Deno.core.ops()[\"some_op\"];\nconst response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4]));\nconsole.log(`Response from plugin ${response}`);\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" - ], - "range": { - "start": { - "line": 0, - "character": 17 - }, - "end": { - "line": 0, - "character": 27 - } - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_hover_change_mbc() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_mbc.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_change_notification_mbc.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request_mbc.json"), - LspResponse::Request( - 2, - json!({ - "contents": [ - { - "language": "typescript", - "value": "const b: \"🦕😃\"", - }, - "", - ], - "range": { - "start": { - "line": 2, - "character": 15, - }, - "end": { - "line": 2, - "character": 16, - }, - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[derive(Deserialize)] - struct HoverResponse { - pub result: Option, - } - - #[tokio::test] - async fn test_hover_closed_document() { - let temp_dir = TempDir::new() - .expect("could not create temp dir") - .into_path(); - let a_path = temp_dir.join("a.ts"); - fs::write(a_path, r#"export const a = "a";"#) - .expect("could not write file"); - let b_path = temp_dir.join("b.ts"); - fs::write(&b_path, r#"export * from "./a.ts";"#) - .expect("could not write file"); - let b_specifier = - Url::from_file_path(b_path).expect("could not convert path"); - let c_path = temp_dir.join("c.ts"); - fs::write(&c_path, "import { a } from \"./b.ts\";\nconsole.log(a);\n") - .expect("could not write file"); - let c_specifier = - Url::from_file_path(c_path).expect("could not convert path"); - - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Value(json!({ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": b_specifier, - "languageId": "typescript", - "version": 1, - "text": r#"export * from "./a.ts";"# - } - } - })), - LspResponse::None, - ), - ( - LspFixture::Value(json!({ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": c_specifier, - "languageId": "typescript", - "version": 1, - "text": "import { a } from \"./b.ts\";\nconsole.log(a);\n", - } - } - })), - LspResponse::None, - ), - ( - LspFixture::Value(json!({ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": c_specifier, - }, - "position": { - "line": 0, - "character": 10 - } - } - })), - LspResponse::RequestAssert(|value| { - let resp: HoverResponse = serde_json::from_value(value).unwrap(); - if let Some(hover) = resp.result { - assert_eq!( - hover, - Hover { - contents: HoverContents::Array(vec![ - MarkedString::LanguageString(LanguageString { - language: "typescript".to_string(), - value: "(alias) const a: \"a\"\nimport a".to_string() - }), - MarkedString::String("".to_string()), - ]), - range: Some(Range { - start: Position { - line: 0, - character: 9, - }, - end: Position { - line: 0, - character: 10, - } - }), - } - ); - } else { - panic!("no response"); - } - }), - ), - ( - LspFixture::Value(json!({ - "jsonrpc": "2.0", - "method": "textDocument/didClose", - "params": { - "textDocument": { - "uri": b_specifier, - } - } - })), - LspResponse::None, - ), - ( - LspFixture::Value(json!({ - "jsonrpc": "2.0", - "id": 4, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": c_specifier, - }, - "position": { - "line": 0, - "character": 10 - } - } - })), - LspResponse::RequestAssert(|value| { - let resp: HoverResponse = serde_json::from_value(value).unwrap(); - if let Some(hover) = resp.result { - assert_eq!( - hover, - Hover { - contents: HoverContents::Array(vec![ - MarkedString::LanguageString(LanguageString { - language: "typescript".to_string(), - value: "(alias) const a: \"a\"\nimport a".to_string() - }), - MarkedString::String("".to_string()), - ]), - range: Some(Range { - start: Position { - line: 0, - character: 9, - }, - end: Position { - line: 0, - character: 10, - } - }), - } - ); - } else { - panic!("no response"); - } - }), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_call_hierarchy() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("prepare_call_hierarchy_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("prepare_call_hierarchy_request.json"), - LspResponse::Request( - 2, - json!([ - { - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 7, - "character": 3 - } - }, - "selectionRange": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 5, - "character": 5 - } - } - } - ]), - ), - ), - ( - LspFixture::Path("incoming_calls_request.json"), - LspResponse::Request( - 4, - json!([ - { - "from": { - "name": "main", - "kind": 12, - "detail": "", - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 10, - "character": 0 - }, - "end": { - "line": 13, - "character": 1 - } - }, - "selectionRange": { - "start": { - "line": 10, - "character": 9 - }, - "end": { - "line": 10, - "character": 13 - } - } - }, - "fromRanges": [ - { - "start": { - "line": 12, - "character": 6 - }, - "end": { - "line": 12, - "character": 9 - } - } - ] - } - ]), - ), - ), - ( - LspFixture::Path("outgoing_calls_request.json"), - LspResponse::Request( - 5, - json!([ - { - "to": { - "name": "foo", - "kind": 12, - "detail": "", - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 0, - "character": 0 - }, - "end": { - "line": 2, - "character": 1 - } - }, - "selectionRange": { - "start": { - "line": 0, - "character": 9 - }, - "end": { - "line": 0, - "character": 12 - } - } - }, - "fromRanges": [ - { - "start": { - "line": 6, - "character": 11 - }, - "end": { - "line": 6, - "character": 14 - } - } - ] - } - ]), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_format_mbc() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_mbc_fmt.json"), - LspResponse::None, - ), - ( - LspFixture::Path("formatting_request_mbc_fmt.json"), - LspResponse::Request( - 2, - json!([ - { - "range": { - "start": { - "line": 0, - "character": 12 - }, - "end": { - "line": 0, - "character": 13, - } - }, - "newText": "\"" - }, - { - "range": { - "start": { - "line": 0, - "character": 21 - }, - "end": { - "line": 0, - "character": 22 - } - }, - "newText": "\";" - }, - { - "range": { - "start": { - "line": 1, - "character": 12, - }, - "end": { - "line": 1, - "character": 13, - } - }, - "newText": "\"" - }, - { - "range": { - "start": { - "line": 1, - "character": 23, - }, - "end": { - "line": 1, - "character": 25, - } - }, - "newText": "\");" - } - ]), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - #[ignore] // TODO(ry) Re-enable. Flaky on ubuntu-latest-xl. - async fn test_large_doc_change() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_large.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_change_notification_large.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_change_notification_large_02.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_change_notification_large_03.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request_large_01.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("hover_request_large_02.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("hover_request_large_03.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - let time = Instant::now(); - harness.run().await; - assert!( - time.elapsed().as_millis() <= 10000, - "the execution time exceeded 10000ms" - ); - } - - #[tokio::test] - async fn test_document_symbol() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("document_symbol_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("document_symbol_request.json"), - LspResponse::Request( - 2, - json!([ - { - "name": "bar", - "kind": 13, - "range": { - "start": { - "line": 17, - "character": 4 - }, - "end": { - "line": 17, - "character": 26 - } - }, - "selectionRange": { - "start": { - "line": 17, - "character": 4 - }, - "end": { - "line": 17, - "character": 7 - } - } - }, - { - "name": "Bar", - "kind": 5, - "range": { - "start": { - "line": 4, - "character": 0 - }, - "end": { - "line": 13, - "character": 1 - } - }, - "selectionRange": { - "start": { - "line": 4, - "character": 6 - }, - "end": { - "line": 4, - "character": 9 - } - }, - "children": [ - { - "name": "constructor", - "kind": 9, - "range": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 5, - "character": 35 - } - }, - "selectionRange": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 5, - "character": 35 - } - } - }, - { - "name": "baz", - "kind": 6, - "tags": [ - 1 - ], - "range": { - "start": { - "line": 8, - "character": 2 - }, - "end": { - "line": 8, - "character": 25 - } - }, - "selectionRange": { - "start": { - "line": 8, - "character": 2 - }, - "end": { - "line": 8, - "character": 5 - } - } - }, - { - "name": "foo", - "kind": 6, - "range": { - "start": { - "line": 6, - "character": 2 - }, - "end": { - "line": 6, - "character": 24 - } - }, - "selectionRange": { - "start": { - "line": 6, - "character": 2 - }, - "end": { - "line": 6, - "character": 5 - } - } - }, - { - "name": "getStaticBar", - "kind": 6, - "range": { - "start": { - "line": 12, - "character": 2 - }, - "end": { - "line": 12, - "character": 57 - } - }, - "selectionRange": { - "start": { - "line": 12, - "character": 17 - }, - "end": { - "line": 12, - "character": 29 - } - } - }, - { - "name": "staticBar", - "kind": 7, - "range": { - "start": { - "line": 11, - "character": 2 - }, - "end": { - "line": 11, - "character": 32 - } - }, - "selectionRange": { - "start": { - "line": 11, - "character": 9 - }, - "end": { - "line": 11, - "character": 18 - } - } - }, - { - "name": "value", - "kind": 7, - "range": { - "start": { - "line": 9, - "character": 2 - }, - "end": { - "line": 9, - "character": 35 - } - }, - "selectionRange": { - "start": { - "line": 9, - "character": 6 - }, - "end": { - "line": 9, - "character": 11 - } - } - }, - { - "name": "value", - "kind": 7, - "range": { - "start": { - "line": 10, - "character": 2 - }, - "end": { - "line": 10, - "character": 42 - } - }, - "selectionRange": { - "start": { - "line": 10, - "character": 6 - }, - "end": { - "line": 10, - "character": 11 - } - } - }, - { - "name": "x", - "kind": 7, - "range": { - "start": { - "line": 5, - "character": 14 - }, - "end": { - "line": 5, - "character": 30 - } - }, - "selectionRange": { - "start": { - "line": 5, - "character": 21 - }, - "end": { - "line": 5, - "character": 22 - } - } - } - ] - }, - { - "name": "IFoo", - "kind": 11, - "range": { - "start": { - "line": 0, - "character": 0 - }, - "end": { - "line": 2, - "character": 1 - } - }, - "selectionRange": { - "start": { - "line": 0, - "character": 10 - }, - "end": { - "line": 0, - "character": 14 - } - }, - "children": [ - { - "name": "foo", - "kind": 6, - "range": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 1, - "character": 17 - } - }, - "selectionRange": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 1, - "character": 5 - } - } - } - ] - }, - { - "name": "Values", - "kind": 10, - "range": { - "start": { - "line": 15, - "character": 0 - }, - "end": { - "line": 15, - "character": 30 - } - }, - "selectionRange": { - "start": { - "line": 15, - "character": 5 - }, - "end": { - "line": 15, - "character": 11 - } - }, - "children": [ - { - "name": "value1", - "kind": 13, - "range": { - "start": { - "line": 15, - "character": 14 - }, - "end": { - "line": 15, - "character": 20 - } - }, - "selectionRange": { - "start": { - "line": 15, - "character": 14 - }, - "end": { - "line": 15, - "character": 20 - } - } - }, - { - "name": "value2", - "kind": 13, - "range": { - "start": { - "line": 15, - "character": 22 - }, - "end": { - "line": 15, - "character": 28 - } - }, - "selectionRange": { - "start": { - "line": 15, - "character": 22 - }, - "end": { - "line": 15, - "character": 28 - } - } - } - ] - } - ]), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_folding_range() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("folding_range_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("folding_range_request.json"), - LspResponse::Request( - 2, - json!([ - { - "startLine": 0, - "endLine": 12, - "kind": "region" - }, - { - "startLine": 1, - "endLine": 3, - "kind": "comment" - }, - { - "startLine": 4, - "endLine": 10 - }, - { - "startLine": 5, - "endLine": 9 - }, - { - "startLine": 6, - "endLine": 7 - } - ]), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_rename() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("rename_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("rename_request.json"), - LspResponse::Request( - 2, - json!({ - "documentChanges": [{ - "textDocument": { - "uri": "file:///a/file.ts", - "version": 1, - }, - "edits": [{ - "range": { - "start": { - "line": 0, - "character": 4 - }, - "end": { - "line": 0, - "character": 12 - } - }, - "newText": "variable_modified" - }, { - "range": { - "start": { - "line": 1, - "character": 12 - }, - "end": { - "line": 1, - "character": 20 - } - }, - "newText": "variable_modified" - }] - }] - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_selection_range() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("selection_range_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("selection_range_request.json"), - LspResponse::Request( - 2, - json!([{ - "range": { - "start": { - "line": 2, - "character": 8 - }, - "end": { - "line": 2, - "character": 9 - } - }, - "parent": { - "range": { - "start": { - "line": 2, - "character": 8 - }, - "end": { - "line": 2, - "character": 15 - } - }, - "parent": { - "range": { - "start": { - "line": 2, - "character": 4 - }, - "end": { - "line": 4, - "character": 5 - } - }, - "parent": { - "range": { - "start": { - "line": 1, - "character": 13 - }, - "end": { - "line": 6, - "character": 2 - } - }, - "parent": { - "range": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 6, - "character": 3 - } - }, - "parent": { - "range": { - "start": { - "line": 0, - "character": 11 - }, - "end": { - "line": 7, - "character": 0 - } - }, - "parent": { - "range": { - "start": { - "line": 0, - "character": 0 - }, - "end": { - "line": 7, - "character": 1 - } - } - } - } - } - } - } - } - }]), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - #[rustfmt::skip] - async fn test_semantic_tokens() { - let mut harness = LspTestHarness::new(vec![ - (LspFixture::Path("initialize_request.json"), LspResponse::RequestAny), - (LspFixture::Path("initialized_notification.json"), LspResponse::None), - ( - LspFixture::Path("semantic_tokens_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("semantic_tokens_full_request.json"), - LspResponse::Request( - 2, - json!({ - "data": [0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15 ,3, 10 ,5, 0, 4, 1, 6, 1, 0, 12 ,7, 2, 16 ,1, 8, 1, 7, 41 ,0, 4, 1, 6, 0, 0, 2, 5, 11 ,16 ,1, 9, 1, 7, 40 ,3, 10 ,4, 2, 1, 1, 11 ,1, 9, 9, 1, 2, 3, 11 ,1, 3, 6, 3, 0, 1, 0, 15 ,4, 2, 0, 1, 30 ,1, 6, 9, 1, 2, 3, 11 ,1, 1, 9, 9, 9, 3, 0, 16 ,3, 0, 0, 1, 17 ,12 ,11 ,3, 0, 24 ,3, 0, 0, 0, 4, 9, 9, 2] - }), - ), - ), - ( - LspFixture::Path("semantic_tokens_range_request.json"), - LspResponse::Request( - 4, - json!({ - "data": [0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15 ,3, 10 ,5, 0, 4, 1, 6, 1, 0, 12 ,7, 2, 16 ,1, 8, 1, 7, 41 ,0, 4, 1, 6, 0, 0, 2, 5, 11 ,16 ,1, 9, 1, 7, 40] - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - (LspFixture::Path("exit_notification.json"), LspResponse::None), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_code_lens_request() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_cl_references.json"), - LspResponse::None, - ), - ( - LspFixture::Path("code_lens_request.json"), - LspResponse::Request( - 2, - json!([ - { - "range": { - "start": { - "line": 0, - "character": 6, - }, - "end": { - "line": 0, - "character": 7, - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references", - }, - }, - { - "range": { - "start": { - "line": 1, - "character": 2, - }, - "end": { - "line": 1, - "character": 3, - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references", - } - } - ]), - ), - ), - ( - LspFixture::Path("code_lens_resolve_request.json"), - LspResponse::Request( - 4, - json!({ - "range": { - "start": { - "line": 0, - "character": 6, - }, - "end": { - "line": 0, - "character": 7, - } - }, - "command": { - "title": "1 reference", - "command": "deno.showReferences", - "arguments": [ - "file:///a/file.ts", - { - "line": 0, - "character": 6, - }, - [ - { - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 12, - "character": 14, - }, - "end": { - "line": 12, - "character": 15, - } - } - } - ], - ] - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_signature_help() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("signature_help_did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("signature_help_request_01.json"), - LspResponse::Request( - 1, - json!({ - "signatures": [ - { - "label": "add(a: number, b: number): number", - "documentation": "Adds two numbers.", - "parameters": [ - { - "label": "a: number", - "documentation": "This is a first number." - }, - { - "label": "b: number", - "documentation": "This is a second number." - } - ] - } - ], - "activeSignature": 0, - "activeParameter": 0 - }), - ), - ), - ( - LspFixture::Path("signature_help_did_change_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("signature_help_request_02.json"), - LspResponse::Request( - 2, - json!({ - "signatures": [ - { - "label": "add(a: number, b: number): number", - "documentation": "Adds two numbers.", - "parameters": [ - { - "label": "a: number", - "documentation": "This is a first number." - }, - { - "label": "b: number", - "documentation": "This is a second number." - } - ] - } - ], - "activeSignature": 0, - "activeParameter": 1 - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_code_lens_impl_request() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_cl_impl.json"), - LspResponse::None, - ), - ( - LspFixture::Path("code_lens_request.json"), - LspResponse::Request( - 2, - json!([ - { - "range": { - "start": { - "line": 0, - "character": 10, - }, - "end": { - "line": 0, - "character": 11, - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations", - }, - }, - { - "range": { - "start": { - "line": 0, - "character": 10, - }, - "end": { - "line": 0, - "character": 11, - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references", - }, - }, - { - "range": { - "start": { - "line": 4, - "character": 6, - }, - "end": { - "line": 4, - "character": 7, - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references", - }, - }, - ]), - ), - ), - ( - LspFixture::Path("code_lens_resolve_request_impl.json"), - LspResponse::Request( - 4, - json!({ - "range": { - "start": { - "line": 0, - "character": 10, - }, - "end": { - "line": 0, - "character": 11, - } - }, - "command": { - "title": "1 implementation", - "command": "deno.showReferences", - "arguments": [ - "file:///a/file.ts", - { - "line": 0, - "character": 10, - }, - [ - { - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 4, - "character": 6, - }, - "end": { - "line": 4, - "character": 7, - } - } - } - ], - ] - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[derive(Deserialize)] - struct CodeLensResponse { - pub result: Option>, - } - - #[derive(Deserialize)] - struct CodeLensResolveResponse { - pub result: CodeLens, - } - - #[tokio::test] - async fn test_code_lens_non_doc_nav_tree() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_asset.json"), - LspResponse::None, - ), - ( - LspFixture::Path("references_request_asset.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("virtual_text_document_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("code_lens_request_asset.json"), - LspResponse::RequestAssert(|value| { - let resp: CodeLensResponse = serde_json::from_value(value).unwrap(); - let lenses = resp.result.unwrap(); - assert!(lenses.len() > 50); - }), - ), - ( - LspFixture::Path("code_lens_resolve_request_asset.json"), - LspResponse::RequestAssert(|value| { - let resp: CodeLensResolveResponse = - serde_json::from_value(value).unwrap(); - assert!(resp.result.command.is_some()); - }), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_code_actions() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_code_action.json"), - LspResponse::None, - ), - (LspFixture::None, LspResponse::Delay(20000)), - ( - LspFixture::Path("code_action_request.json"), - LspResponse::RequestFixture(2, "code_action_response.json".to_string()), - ), - ( - LspFixture::Path("code_action_resolve_request.json"), - LspResponse::RequestFixture( - 4, - "code_action_resolve_request_response.json".to_string(), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_code_actions_deno_cache() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_cache.json"), - LspResponse::None, - ), - ( - LspFixture::Path("code_action_request_cache.json"), - LspResponse::RequestFixture( - 2, - "code_action_response_cache.json".to_string(), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[derive(Deserialize)] - struct CompletionResult { - pub result: Option, - } - - #[tokio::test] - async fn test_completions() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_completions.json"), - LspResponse::None, - ), - ( - LspFixture::Path("completion_request.json"), - LspResponse::RequestAssert(|value| { - let response: CompletionResult = - serde_json::from_value(value).unwrap(); - let result = response.result.unwrap(); - match result { - CompletionResponse::List(list) => { - // there should be at least 90 completions for `Deno.` - assert!(list.items.len() > 90); - } - _ => panic!("unexpected result"), - } - }), - ), - ( - LspFixture::Path("completion_resolve_request.json"), - LspResponse::Request( - 4, - json!({ - "label": "build", - "kind": 6, - "detail": "const Deno.build: {\n target: string;\n arch: \"x86_64\" | \"aarch64\";\n os: \"darwin\" | \"linux\" | \"windows\";\n vendor: string;\n env?: string | undefined;\n}", - "documentation": { - "kind": "markdown", - "value": "Build related information." - }, - "sortText": "1", - "insertTextFormat": 1, - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_completions_optional() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_completion_optional.json"), - LspResponse::None, - ), - ( - LspFixture::Path("completion_request_optional.json"), - LspResponse::Request( - 2, - json!({ - "isIncomplete": false, - "items": [ - { - "label": "b?", - "kind": 5, - "sortText": "1", - "filterText": "b", - "insertText": "b", - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 79, - "name": "b", - "useCodeSnippet": false - } - } - } - ] - }), - ), - ), - ( - LspFixture::Path("completion_resolve_request_optional.json"), - LspResponse::Request( - 4, - json!({ - "label": "b?", - "kind": 5, - "detail": "(property) A.b?: string | undefined", - "documentation": { - "kind": "markdown", - "value": "" - }, - "sortText": "1", - "filterText": "b", - "insertText": "b" - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_completions_registry() { - let _g = test_util::http_server(); - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request_registry.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_completion_registry.json"), - LspResponse::None, - ), - ( - LspFixture::Path("completion_request_registry.json"), - LspResponse::RequestAssert(|value| { - let response: CompletionResult = - serde_json::from_value(value).unwrap(); - let result = response.result.unwrap(); - if let CompletionResponse::List(list) = result { - assert_eq!(list.items.len(), 3); - } else { - panic!("unexpected result"); - } - }), - ), - ( - LspFixture::Path("completion_resolve_request_registry.json"), - LspResponse::Request( - 4, - json!({ - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { - "line": 0, - "character": 20 - }, - "end": { - "line": 0, - "character": 46 - } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[tokio::test] - async fn test_completion_registry_empty_specifier() { - let _g = test_util::http_server(); - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request_registry.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification_completion_registry_02.json"), - LspResponse::None, - ), - ( - LspFixture::Path("completion_request_registry_02.json"), - LspResponse::Request( - 2, - json!({ - "isIncomplete": false, - "items": [ - { - "label": ".", - "kind": 19, - "detail": "(local)", - "sortText": "1", - "insertText": "." - }, - { - "label": "..", - "kind": 19, - "detail": "(local)", - "sortText": "1", - "insertText": ".." - }, - { - "label": "http://localhost:4545", - "kind": 19, - "detail": "(registry)", - "sortText": "2", - "textEdit": { - "range": { - "start": { - "line": 0, - "character": 20 - }, - "end": { - "line": 0, - "character": 20 - } - }, - "newText": "http://localhost:4545" - } - } - ] - }), - ), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } - - #[derive(Deserialize)] - struct PerformanceAverages { - averages: Vec, - } - #[derive(Deserialize)] - struct PerformanceResponse { - result: PerformanceAverages, - } - - #[tokio::test] - async fn test_deno_performance_request() { - let mut harness = LspTestHarness::new(vec![ - ( - LspFixture::Path("initialize_request.json"), - LspResponse::RequestAny, - ), - ( - LspFixture::Path("initialized_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("did_open_notification.json"), - LspResponse::None, - ), - ( - LspFixture::Path("hover_request.json"), - LspResponse::Request( - 2, - json!({ - "contents": [ - { - "language": "typescript", - "value": "const Deno.args: string[]" - }, - "Returns the script arguments to the program. If for example we run a\nprogram:\n\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n\nThen `Deno.args` will contain:\n\n[ \"/etc/passwd\" ]" - ], - "range": { - "start": { - "line": 0, - "character": 17 - }, - "end": { - "line": 0, - "character": 21 - } - } - }), - ), - ), - ( - LspFixture::Path("performance_request.json"), - LspResponse::RequestAssert(|value| { - let resp: PerformanceResponse = - serde_json::from_value(value).unwrap(); - // the len can be variable since some of the parts of the language - // server run in separate threads and may not add to performance by - // the time the results are checked. - assert!(resp.result.averages.len() >= 6); - }), - ), - ( - LspFixture::Path("shutdown_request.json"), - LspResponse::Request(3, json!(null)), - ), - ( - LspFixture::Path("exit_notification.json"), - LspResponse::None, - ), - ]); - harness.run().await; - } -} diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs new file mode 100644 index 0000000000..6b0f6b7920 --- /dev/null +++ b/cli/tests/integration_tests_lsp.rs @@ -0,0 +1,1661 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use deno_core::serde::Deserialize; +use deno_core::serde::Serialize; +use deno_core::serde_json; +use deno_core::serde_json::json; +use deno_core::serde_json::Value; +use deno_core::url::Url; +use lspower::lsp; +use std::fs; +use tempfile::TempDir; +use test_util::deno_exe_path; +use test_util::http_server; +use test_util::lsp::LspClient; +use test_util::root_path; + +fn load_fixture(path: &str) -> Value { + let fixtures_path = root_path().join("cli/tests/lsp"); + let path = fixtures_path.join(path); + let fixture_str = fs::read_to_string(path).unwrap(); + serde_json::from_str(&fixture_str).unwrap() +} + +fn init(init_path: &str) -> LspClient { + let deno_exe = deno_exe_path(); + let mut client = LspClient::new(&deno_exe).unwrap(); + client + .write_request::<_, _, Value>("initialize", load_fixture(init_path)) + .unwrap(); + client.write_notification("initialized", json!({})).unwrap(); + client +} + +fn did_open(client: &mut LspClient, params: V) +where + V: Serialize, +{ + client + .write_notification("textDocument/didOpen", params) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); +} + +fn shutdown(client: &mut LspClient) { + client + .write_request::<_, _, Value>("shutdown", json!(null)) + .unwrap(); + client.write_notification("exit", json!(null)).unwrap(); +} + +#[test] +fn lsp_startup_shutdown() { + let mut client = init("initialize_params.json"); + shutdown(&mut client); +} + +#[test] +fn lsp_hover() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Deno.args);\n" + } + }), + ); + 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!({ + "contents": [ + { + "language": "typescript", + "value": "const Deno.args: string[]" + }, + "Returns the script arguments to the program. If for example we run a\nprogram:\n\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n\nThen `Deno.args` will contain:\n\n[ \"/etc/passwd\" ]" + ], + "range": { + "start": { + "line": 0, + "character": 17 + }, + "end": { + "line": 0, + "character": 21 + } + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_hover_asset() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Date.now());\n" + } + }), + ); + let (_, maybe_error) = client + .write_request::<_, _, Value>( + "textDocument/definition", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 14 + } + }), + ) + .unwrap(); + assert!(maybe_error.is_none()); + let (_, maybe_error) = client + .write_request::<_, _, Value>( + "deno/virtualTextDocument", + json!({ + "textDocument": { + "uri": "deno:/asset//lib.deno.shared_globals.d.ts" + } + }), + ) + .unwrap(); + assert!(maybe_error.is_none()); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts" + }, + "position": { + "line": 109, + "character": 13 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": [ + { + "language": "typescript", + "value": "interface Date", + }, + "Enables basic storage and retrieval of dates and times." + ], + "range": { + "start": { + "line": 109, + "character": 10, + }, + "end": { + "line": 109, + "character": 14, + } + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_hover_disabled() { + let mut client = init("initialize_params_disabled.json"); + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Date.now());\n" + } + }), + ) + .unwrap(); + 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_hover_unstable_disabled() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Deno.openPlugin);\n" + } + }), + ); + 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!({ + "contents": [ + { + "language": "typescript", + "value": "any" + } + ], + "range": { + "start": { + "line": 0, + "character": 17 + }, + "end": { + "line": 0, + "character": 27 + } + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_hover_unstable_enabled() { + let mut client = init("initialize_params_unstable.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Deno.openPlugin);\n" + } + }), + ); + 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!({ + "contents":[ + { + "language":"typescript", + "value":"function Deno.openPlugin(filename: string): number" + }, + "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\nconst opId = Deno.core.ops()[\"some_op\"];\nconst response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4]));\nconsole.log(`Response from plugin ${response}`);\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" + ], + "range":{ + "start":{ + "line":0, + "character":17 + }, + "end":{ + "line":0, + "character":27 + } + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_hover_change_mbc() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "const a = `编写软件很难`;\nconst b = `👍🦕😃`;\nconsole.log(a, b);\n" + } + }), + ); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 1, + "character": 11 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "text": "" + } + ] + }), + ) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 2, + "character": 14 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": [ + { + "language": "typescript", + "value": "const b: \"😃\"", + }, + "", + ], + "range": { + "start": { + "line": 2, + "character": 13, + }, + "end": { + "line": 2, + "character": 14, + }, + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_hover_closed_document() { + let temp_dir = TempDir::new() + .expect("could not create temp dir") + .into_path(); + let a_path = temp_dir.join("a.ts"); + fs::write(a_path, r#"export const a = "a";"#).expect("could not write file"); + let b_path = temp_dir.join("b.ts"); + fs::write(&b_path, r#"export * from "./a.ts";"#) + .expect("could not write file"); + let b_specifier = + Url::from_file_path(b_path).expect("could not convert path"); + let c_path = temp_dir.join("c.ts"); + fs::write(&c_path, "import { a } from \"./b.ts\";\nconsole.log(a);\n") + .expect("could not write file"); + let c_specifier = + Url::from_file_path(c_path).expect("could not convert path"); + + let mut client = init("initialize_params.json"); + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": b_specifier, + "languageId": "typescript", + "version": 1, + "text": r#"export * from "./a.ts";"# + } + }), + ) + .unwrap(); + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": c_specifier, + "languageId": "typescript", + "version": 1, + "text": "import { a } from \"./b.ts\";\nconsole.log(a);\n", + } + }), + ) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/hover", + json!({ + "textDocument": { + "uri": c_specifier, + }, + "position": { + "line": 0, + "character": 10 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": [ + { + "language": "typescript", + "value": "(alias) const a: \"a\"\nimport a" + }, + "" + ], + "range": { + "start": { + "line": 0, + "character": 9 + }, + "end": { + "line": 0, + "character": 10 + } + } + })) + ); + client + .write_notification( + "textDocument/didClose", + json!({ + "textDocument": { + "uri": b_specifier, + } + }), + ) + .unwrap(); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/hover", + json!({ + "textDocument": { + "uri": c_specifier, + }, + "position": { + "line": 0, + "character": 10 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": [ + { + "language": "typescript", + "value": "(alias) const a: \"a\"\nimport a" + }, + "" + ], + "range": { + "start": { + "line": 0, + "character": 9 + }, + "end": { + "line": 0, + "character": 10 + } + } + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_call_hierarchy() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "function foo() {\n return false;\n}\n\nclass Bar {\n baz() {\n return foo();\n }\n}\n\nfunction main() {\n const bar = new Bar();\n bar.baz();\n}\n\nmain();" + } + }), + ); + let (maybe_res, maybe_error) = client + .write_request( + "textDocument/prepareCallHierarchy", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 5, + "character": 3 + } + }), + ) + .unwrap(); + assert!(maybe_error.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("prepare_call_hierarchy_response.json")) + ); + let (maybe_res, maybe_error) = client + .write_request( + "callHierarchy/incomingCalls", + load_fixture("incoming_calls_params.json"), + ) + .unwrap(); + assert!(maybe_error.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("incoming_calls_response.json")) + ); + let (maybe_res, maybe_error) = client + .write_request( + "callHierarchy/outgoingCalls", + load_fixture("outgoing_calls_params.json"), + ) + .unwrap(); + assert!(maybe_error.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("outgoing_calls_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_format_mbc() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "const bar = '👍🇺🇸😃'\nconsole.log('hello deno')\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "options": { + "tabSize": 2, + "insertSpaces": true + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!(load_fixture("formatting_mbc_response.json"))) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_large_doc_changes() { + let mut client = init("initialize_params.json"); + did_open(&mut client, load_fixture("did_open_params_large.json")); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 444, + "character": 11 + }, + "end": { + "line": 444, + "character": 14 + } + }, + "text": "+++" + } + ] + }), + ) + .unwrap(); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 445, + "character": 4 + }, + "end": { + "line": 445, + "character": 4 + } + }, + "text": "// " + } + ] + }), + ) + .unwrap(); + client + .write_notification( + "textDocument/didChagne", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 477, + "character": 4 + }, + "end": { + "line": 477, + "character": 9 + } + }, + "text": "error" + } + ] + }), + ) + .unwrap(); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 421, + "character": 30 + } + }), + ) + .unwrap(); + assert!(maybe_res.is_some()); + assert!(maybe_err.is_none()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 444, + "character": 6 + } + }), + ) + .unwrap(); + assert!(maybe_res.is_some()); + assert!(maybe_err.is_none()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 461, + "character": 34 + } + }), + ) + .unwrap(); + assert!(maybe_res.is_some()); + assert!(maybe_err.is_none()); + shutdown(&mut client); + + assert!(client.duration().as_millis() <= 15000); +} + +#[test] +fn lsp_document_symbol() { + let mut client = init("initialize_params.json"); + did_open(&mut client, load_fixture("did_open_params_doc_symbol.json")); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/documentSymbol", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("document_symbol_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_folding_range() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "// #region 1\n/*\n * Some comment\n */\nclass Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}\n// #endregion" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/foldingRange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!([ + { + "startLine": 0, + "endLine": 12, + "kind": "region" + }, + { + "startLine": 1, + "endLine": 3, + "kind": "comment" + }, + { + "startLine": 4, + "endLine": 10 + }, + { + "startLine": 5, + "endLine": 9 + }, + { + "startLine": 6, + "endLine": 7 + } + ])) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_rename() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "let variable = 'a';\nconsole.log(variable);" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/rename", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 4 + }, + "newName": "variable_modified" + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!(maybe_res, Some(load_fixture("rename_response.json"))); + shutdown(&mut client); +} + +#[test] +fn lsp_selection_range() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "class Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/selectionRange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "positions": [ + { + "line": 2, + "character": 8 + } + ] + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("selection_range_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_semantic_tokens() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + load_fixture("did_open_params_semantic_tokens.json"), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/semanticTokens/full", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "data": [ + 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1, + 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16, + 1, 9, 1, 7, 40, 3, 10, 4, 2, 1, 1, 11, 1, 9, 9, 1, 2, 3, 11, 1, 3, 6, 3, + 0, 1, 0, 15, 4, 2, 0, 1, 30, 1, 6, 9, 1, 2, 3, 11,1, 1, 9, 9, 9, 3, 0, + 16, 3, 0, 0, 1, 17, 12, 11, 3, 0, 24, 3, 0, 0, 0, 4, 9, 9, 2 + ] + })) + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/semanticTokens/range", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 6, + "character": 0 + } + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "data": [ + 0, 5, 6, 1, 1, 0, 9, 6, 8, 9, 0, 8, 6, 8, 9, 2, 15, 3, 10, 5, 0, 4, 1, + 6, 1, 0, 12, 7, 2, 16, 1, 8, 1, 7, 41, 0, 4, 1, 6, 0, 0, 2, 5, 11, 16, + 1, 9, 1, 7, 40 + ] + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_lens() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "class A {\n a = \"a\";\n\n b() {\n console.log(this.a);\n }\n\n c() {\n this.a = \"c\";\n }\n}\n\nconst a = new A();\na.b();\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeLens", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!(maybe_res, Some(load_fixture("code_lens_response.json"))); + let (maybe_res, maybe_err) = client + .write_request( + "codeLens/resolve", + json!({ + "range": { + "start": { + "line": 0, + "character": 6 + }, + "end": { + "line": 0, + "character": 7 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_lens_resolve_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_lens_impl() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeLens", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_lens_response_impl.json")) + ); + let (maybe_res, maybe_err) = client + .write_request( + "codeLens/resolve", + json!({ + "range": { + "start": { + "line": 0, + "character": 10 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "implementations" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_lens_resolve_response_impl.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_lens_non_doc_nav_tree() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Date.now());\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/references", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 3 + }, + "context": { + "includeDeclaration": true + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "deno/virtualTextDocument", + json!({ + "textDocument": { + "uri": "deno:/asset//lib.deno.shared_globals.d.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Vec>( + "textDocument/codeLens", + json!({ + "textDocument": { + "uri": "deno:/asset//lib.deno.shared_globals.d.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let res = maybe_res.unwrap(); + assert!(res.len() > 50); + let (maybe_res, maybe_err) = client + .write_request::<_, _, lsp::CodeLens>( + "codeLens/resolve", + json!({ + "range": { + "start": { + "line": 416, + "character": 12 + }, + "end": { + "line": 416, + "character": 19 + } + }, + "data": { + "specifier": "asset:///lib.deno.shared_globals.d.ts", + "source": "references" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + shutdown(&mut client); +} + +#[test] +fn lsp_signature_help() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "/**\n * Adds two numbers.\n * @param a This is a first number.\n * @param b This is a second number.\n */\nfunction add(a: number, b: number) {\n return a + b;\n}\n\nadd(" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/signatureHelp", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "character": 4, + "line": 9 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "(", + "isRetrigger": false + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "signatures": [ + { + "label": "add(a: number, b: number): number", + "documentation": "Adds two numbers.", + "parameters": [ + { + "label": "a: number", + "documentation": "This is a first number." + }, + { + "label": "b: number", + "documentation": "This is a second number." + } + ] + } + ], + "activeSignature": 0, + "activeParameter": 0 + })) + ); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 9, + "character": 4 + }, + "end": { + "line": 9, + "character": 4 + } + }, + "text": "123, " + } + ] + }), + ) + .unwrap(); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/signatureHelp", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "character": 8, + "line": 9 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "signatures": [ + { + "label": "add(a: number, b: number): number", + "documentation": "Adds two numbers.", + "parameters": [ + { + "label": "a: number", + "documentation": "This is a first number." + }, + { + "label": "b: number", + "documentation": "This is a second number." + } + ] + } + ], + "activeSignature": 0, + "activeParameter": 1 + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_actions() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "export function a(): void {\n await Promise.resolve(\"a\");\n}\n\nexport function b(): void {\n await Promise.resolve(\"b\");\n}\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeAction", + load_fixture("code_action_params.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!(maybe_res, Some(load_fixture("code_action_response.json"))); + let (maybe_res, maybe_err) = client + .write_request( + "codeAction/resolve", + load_fixture("code_action_resolve_params.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_action_resolve_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_code_actions_deno_cache() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "import * as a from \"https://deno.land/x/a/mod.ts\";\n\nconsole.log(a);\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeAction", + load_fixture("code_action_params_cache.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_action_response_cache.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_completions() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "Deno." + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/completion", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 5 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "." + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + if let Some(lsp::CompletionResponse::List(list)) = maybe_res { + assert!(!list.is_incomplete); + assert!(list.items.len() > 90); + } else { + panic!("unexpected response"); + } + let (maybe_res, maybe_err) = client + .write_request( + "completionItem/resolve", + load_fixture("completion_resolve_params.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("completion_resolve_response.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_completions_optional() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "interface A {\n b?: string;\n}\n\nconst o: A = {};\n\nfunction c(s: string) {}\n\nc(o.)" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/completion", + load_fixture("completion_request_params_optional.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "isIncomplete": false, + "items": [ + { + "label": "b?", + "kind": 5, + "sortText": "1", + "filterText": "b", + "insertText": "b", + "data": { + "tsc": { + "specifier": "file:///a/file.ts", + "position": 79, + "name": "b", + "useCodeSnippet": false + } + } + } + ] + })) + ); + let (maybe_res, maybe_err) = client + .write_request( + "completionItem/resolve", + load_fixture("completion_resolve_params_optional.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "label": "b?", + "kind": 5, + "detail": "(property) A.b?: string | undefined", + "documentation": { + "kind": "markdown", + "value": "" + }, + "sortText": "1", + "filterText": "b", + "insertText": "b" + })) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_completions_registry() { + let _g = http_server(); + let mut client = init("initialize_params_registry.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "import * as a from \"http://localhost:4545/x/a@\"" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/completion", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 46 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "@" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + if let Some(lsp::CompletionResponse::List(list)) = maybe_res { + assert!(!list.is_incomplete); + assert_eq!(list.items.len(), 3); + } else { + panic!("unexpected response"); + } + let (maybe_res, maybe_err) = client + .write_request( + "completionItem/resolve", + load_fixture("completion_resolve_params_registry.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("completion_resolve_response_registry.json")) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_completions_registry_empty() { + let _g = http_server(); + let mut client = init("initialize_params_registry.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "import * as a from \"\"" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/completion", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 20 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "\"" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("completion_request_response_empty.json")) + ); + shutdown(&mut client); +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PerformanceAverage { + pub name: String, + pub count: u32, + pub average_duration: u32, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct PerformanceAverages { + averages: Vec, +} + +#[test] +fn lsp_performance() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "console.log(Deno.args);\n" + } + }), + ); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 0, + "character": 19 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, PerformanceAverages>("deno/performance", json!({})) + .unwrap(); + assert!(maybe_err.is_none()); + if let Some(res) = maybe_res { + assert!(res.averages.len() >= 6); + } else { + panic!("unexpected result"); + } + shutdown(&mut client); +} diff --git a/cli/tests/lsp/code_action_resolve_request.json b/cli/tests/lsp/code_action_params.json similarity index 65% rename from cli/tests/lsp/code_action_resolve_request.json rename to cli/tests/lsp/code_action_params.json index 48a2eea3bc..d026d61f68 100644 --- a/cli/tests/lsp/code_action_resolve_request.json +++ b/cli/tests/lsp/code_action_params.json @@ -1,10 +1,18 @@ { - "jsonrpc": "2.0", - "id": 4, - "method": "codeAction/resolve", - "params": { - "title": "Add all missing 'async' modifiers", - "kind": "quickfix", + "textDocument": { + "uri": "file:///a/file.ts" + }, + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 7 + } + }, + "context": { "diagnostics": [ { "range": { @@ -24,9 +32,8 @@ "relatedInformation": [] } ], - "data": { - "specifier": "file:///a/file.ts", - "fixId": "fixAwaitInSyncFunction" - } + "only": [ + "quickfix" + ] } } diff --git a/cli/tests/lsp/code_action_params_cache.json b/cli/tests/lsp/code_action_params_cache.json new file mode 100644 index 0000000000..61ae555a3c --- /dev/null +++ b/cli/tests/lsp/code_action_params_cache.json @@ -0,0 +1,41 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "range": { + "start": { + "line": 0, + "character": 19 + }, + "end": { + "line": 0, + "character": 49 + } + }, + "context": { + "diagnostics": [ + { + "range": { + "start": { + "line": 0, + "character": 19 + }, + "end": { + "line": 0, + "character": 49 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Unable to load the remote module: \"https://deno.land/x/a/mod.ts\".", + "data": { + "specifier": "https://deno.land/x/a/mod.ts" + } + } + ], + "only": [ + "quickfix" + ] + } +} diff --git a/cli/tests/lsp/code_action_request.json b/cli/tests/lsp/code_action_request.json deleted file mode 100644 index af6cbee8bd..0000000000 --- a/cli/tests/lsp/code_action_request.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/codeAction", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 1, - "character": 7 - } - }, - "context": { - "diagnostics": [ - { - "range": { - "start": { - "line": 1, - "character": 2 - }, - "end": { - "line": 1, - "character": 7 - } - }, - "severity": 1, - "code": 1308, - "source": "deno-ts", - "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", - "relatedInformation": [] - } - ], - "only": [ - "quickfix" - ] - } - } -} diff --git a/cli/tests/lsp/code_action_request_cache.json b/cli/tests/lsp/code_action_request_cache.json deleted file mode 100644 index 8e296be327..0000000000 --- a/cli/tests/lsp/code_action_request_cache.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/codeAction", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { - "line": 0, - "character": 19 - }, - "end": { - "line": 0, - "character": 49 - } - }, - "context": { - "diagnostics": [ - { - "range": { - "start": { - "line": 0, - "character": 19 - }, - "end": { - "line": 0, - "character": 49 - } - }, - "severity": 1, - "code": "no-cache", - "source": "deno", - "message": "Unable to load the remote module: \"https://deno.land/x/a/mod.ts\".", - "data": { - "specifier": "https://deno.land/x/a/mod.ts" - } - } - ], - "only": [ - "quickfix" - ] - } - } -} diff --git a/cli/tests/lsp/code_action_resolve_params.json b/cli/tests/lsp/code_action_resolve_params.json new file mode 100644 index 0000000000..50c1f9a43b --- /dev/null +++ b/cli/tests/lsp/code_action_resolve_params.json @@ -0,0 +1,27 @@ +{ + "title": "Add all missing 'async' modifiers", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 7 + } + }, + "severity": 1, + "code": 1308, + "source": "deno-ts", + "message": "'await' expressions are only allowed within async functions and at the top levels of modules.", + "relatedInformation": [] + } + ], + "data": { + "specifier": "file:///a/file.ts", + "fixId": "fixAwaitInSyncFunction" + } +} diff --git a/cli/tests/lsp/code_action_resolve_request_response.json b/cli/tests/lsp/code_action_resolve_response.json similarity index 100% rename from cli/tests/lsp/code_action_resolve_request_response.json rename to cli/tests/lsp/code_action_resolve_response.json diff --git a/cli/tests/lsp/code_lens_request.json b/cli/tests/lsp/code_lens_request.json deleted file mode 100644 index a876153bf7..0000000000 --- a/cli/tests/lsp/code_lens_request.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/codeLens", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - } - } -} diff --git a/cli/tests/lsp/code_lens_request_asset.json b/cli/tests/lsp/code_lens_request_asset.json deleted file mode 100644 index 6aa246ce5c..0000000000 --- a/cli/tests/lsp/code_lens_request_asset.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "textDocument/codeLens", - "params": { - "textDocument": { - "uri": "deno:/asset//lib.deno.shared_globals.d.ts" - } - } -} diff --git a/cli/tests/lsp/code_lens_resolve_request.json b/cli/tests/lsp/code_lens_resolve_request.json deleted file mode 100644 index 150603cd4e..0000000000 --- a/cli/tests/lsp/code_lens_resolve_request.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "codeLens/resolve", - "params": { - "range": { - "start": { - "line": 0, - "character": 6 - }, - "end": { - "line": 0, - "character": 7 - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "references" - } - } -} diff --git a/cli/tests/lsp/code_lens_resolve_request_asset.json b/cli/tests/lsp/code_lens_resolve_request_asset.json deleted file mode 100644 index 027af96b61..0000000000 --- a/cli/tests/lsp/code_lens_resolve_request_asset.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 5, - "method": "codeLens/resolve", - "params": { - "range": { - "start": { - "line": 416, - "character": 12 - }, - "end": { - "line": 416, - "character": 19 - } - }, - "data": { - "specifier": "asset:///lib.deno.shared_globals.d.ts", - "source": "references" - } - } -} diff --git a/cli/tests/lsp/code_lens_resolve_request_impl.json b/cli/tests/lsp/code_lens_resolve_request_impl.json deleted file mode 100644 index e44d19675e..0000000000 --- a/cli/tests/lsp/code_lens_resolve_request_impl.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "codeLens/resolve", - "params": { - "range": { - "start": { - "line": 0, - "character": 10 - }, - "end": { - "line": 0, - "character": 11 - } - }, - "data": { - "specifier": "file:///a/file.ts", - "source": "implementations" - } - } -} diff --git a/cli/tests/lsp/code_lens_resolve_response.json b/cli/tests/lsp/code_lens_resolve_response.json new file mode 100644 index 0000000000..1400eb4e6f --- /dev/null +++ b/cli/tests/lsp/code_lens_resolve_response.json @@ -0,0 +1,38 @@ +{ + "range": { + "start": { + "line": 0, + "character": 6 + }, + "end": { + "line": 0, + "character": 7 + } + }, + "command": { + "title": "1 reference", + "command": "deno.showReferences", + "arguments": [ + "file:///a/file.ts", + { + "line": 0, + "character": 6 + }, + [ + { + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 12, + "character": 14 + }, + "end": { + "line": 12, + "character": 15 + } + } + } + ] + ] + } +} diff --git a/cli/tests/lsp/code_lens_resolve_response_impl.json b/cli/tests/lsp/code_lens_resolve_response_impl.json new file mode 100644 index 0000000000..cabf2f8334 --- /dev/null +++ b/cli/tests/lsp/code_lens_resolve_response_impl.json @@ -0,0 +1,38 @@ +{ + "range": { + "start": { + "line": 0, + "character": 10 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "command": { + "title": "1 implementation", + "command": "deno.showReferences", + "arguments": [ + "file:///a/file.ts", + { + "line": 0, + "character": 10 + }, + [ + { + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 4, + "character": 6 + }, + "end": { + "line": 4, + "character": 7 + } + } + } + ] + ] + } +} diff --git a/cli/tests/lsp/code_lens_response.json b/cli/tests/lsp/code_lens_response.json new file mode 100644 index 0000000000..e3a87e4be6 --- /dev/null +++ b/cli/tests/lsp/code_lens_response.json @@ -0,0 +1,34 @@ +[ + { + "range": { + "start": { + "line": 0, + "character": 6 + }, + "end": { + "line": 0, + "character": 7 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + }, + { + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 3 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + } +] diff --git a/cli/tests/lsp/code_lens_response_impl.json b/cli/tests/lsp/code_lens_response_impl.json new file mode 100644 index 0000000000..b0073a23f7 --- /dev/null +++ b/cli/tests/lsp/code_lens_response_impl.json @@ -0,0 +1,50 @@ +[ + { + "range": { + "start": { + "line": 0, + "character": 10 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "implementations" + } + }, + { + "range": { + "start": { + "line": 0, + "character": 10 + }, + "end": { + "line": 0, + "character": 11 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + }, + { + "range": { + "start": { + "line": 4, + "character": 6 + }, + "end": { + "line": 4, + "character": 7 + } + }, + "data": { + "specifier": "file:///a/file.ts", + "source": "references" + } + } +] diff --git a/cli/tests/lsp/completion_request.json b/cli/tests/lsp/completion_request.json deleted file mode 100644 index 81bf719a9e..0000000000 --- a/cli/tests/lsp/completion_request.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/completion", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 5 - }, - "context": { - "triggerKind": 2, - "triggerCharacter": "." - } - } -} diff --git a/cli/tests/lsp/completion_request_optional.json b/cli/tests/lsp/completion_request_optional.json deleted file mode 100644 index 5e86c33ff6..0000000000 --- a/cli/tests/lsp/completion_request_optional.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/completion", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 8, - "character": 4 - }, - "context": { - "triggerKind": 2, - "triggerCharacter": "." - } - } -} diff --git a/cli/tests/lsp/completion_request_params_optional.json b/cli/tests/lsp/completion_request_params_optional.json new file mode 100644 index 0000000000..1f3c079c73 --- /dev/null +++ b/cli/tests/lsp/completion_request_params_optional.json @@ -0,0 +1,13 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "position": { + "line": 8, + "character": 4 + }, + "context": { + "triggerKind": 2, + "triggerCharacter": "." + } +} diff --git a/cli/tests/lsp/completion_request_registry.json b/cli/tests/lsp/completion_request_registry.json deleted file mode 100644 index 2165fbdab6..0000000000 --- a/cli/tests/lsp/completion_request_registry.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/completion", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 46 - }, - "context": { - "triggerKind": 2, - "triggerCharacter": "@" - } - } -} diff --git a/cli/tests/lsp/completion_request_registry_02.json b/cli/tests/lsp/completion_request_registry_02.json deleted file mode 100644 index 21c3bc4753..0000000000 --- a/cli/tests/lsp/completion_request_registry_02.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/completion", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 20 - }, - "context": { - "triggerKind": 2, - "triggerCharacter": "\"" - } - } -} diff --git a/cli/tests/lsp/completion_request_response_empty.json b/cli/tests/lsp/completion_request_response_empty.json new file mode 100644 index 0000000000..272dfb4756 --- /dev/null +++ b/cli/tests/lsp/completion_request_response_empty.json @@ -0,0 +1,38 @@ +{ + "isIncomplete": false, + "items": [ + { + "label": ".", + "kind": 19, + "detail": "(local)", + "sortText": "1", + "insertText": "." + }, + { + "label": "..", + "kind": 19, + "detail": "(local)", + "sortText": "1", + "insertText": ".." + }, + { + "label": "http://localhost:4545", + "kind": 19, + "detail": "(registry)", + "sortText": "2", + "textEdit": { + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 20 + } + }, + "newText": "http://localhost:4545" + } + } + ] +} diff --git a/cli/tests/lsp/completion_resolve_params.json b/cli/tests/lsp/completion_resolve_params.json new file mode 100644 index 0000000000..26231036da --- /dev/null +++ b/cli/tests/lsp/completion_resolve_params.json @@ -0,0 +1,14 @@ +{ + "label": "build", + "kind": 6, + "sortText": "1", + "insertTextFormat": 1, + "data": { + "tsc": { + "specifier": "file:///a/file.ts", + "position": 5, + "name": "build", + "useCodeSnippet": false + } + } +} diff --git a/cli/tests/lsp/completion_resolve_params_optional.json b/cli/tests/lsp/completion_resolve_params_optional.json new file mode 100644 index 0000000000..cb99bf960c --- /dev/null +++ b/cli/tests/lsp/completion_resolve_params_optional.json @@ -0,0 +1,15 @@ +{ + "label": "b?", + "kind": 5, + "sortText": "1", + "filterText": "b", + "insertText": "b", + "data": { + "tsc": { + "specifier": "file:///a/file.ts", + "position": 79, + "name": "b", + "useCodeSnippet": false + } + } +} diff --git a/cli/tests/lsp/completion_resolve_params_registry.json b/cli/tests/lsp/completion_resolve_params_registry.json new file mode 100644 index 0000000000..99a4a048e2 --- /dev/null +++ b/cli/tests/lsp/completion_resolve_params_registry.json @@ -0,0 +1,20 @@ +{ + "label": "v2.0.0", + "kind": 19, + "detail": "(version)", + "sortText": "0000000003", + "filterText": "http://localhost:4545/x/a@v2.0.0", + "textEdit": { + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 46 + } + }, + "newText": "http://localhost:4545/x/a@v2.0.0" + } +} diff --git a/cli/tests/lsp/completion_resolve_request.json b/cli/tests/lsp/completion_resolve_request.json deleted file mode 100644 index 7b78fb440e..0000000000 --- a/cli/tests/lsp/completion_resolve_request.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "completionItem/resolve", - "params": { - "label": "build", - "kind": 6, - "sortText": "1", - "insertTextFormat": 1, - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 5, - "name": "build", - "useCodeSnippet": false - } - } - } -} diff --git a/cli/tests/lsp/completion_resolve_request_optional.json b/cli/tests/lsp/completion_resolve_request_optional.json deleted file mode 100644 index ffa60b9197..0000000000 --- a/cli/tests/lsp/completion_resolve_request_optional.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "completionItem/resolve", - "params": { - "label": "b?", - "kind": 5, - "sortText": "1", - "filterText": "b", - "insertText": "b", - "data": { - "tsc": { - "specifier": "file:///a/file.ts", - "position": 79, - "name": "b", - "useCodeSnippet": false - } - } - } -} diff --git a/cli/tests/lsp/completion_resolve_request_registry.json b/cli/tests/lsp/completion_resolve_request_registry.json deleted file mode 100644 index bae19d0609..0000000000 --- a/cli/tests/lsp/completion_resolve_request_registry.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "completionItem/resolve", - "params": { - "label": "v2.0.0", - "kind": 19, - "detail": "(version)", - "sortText": "0000000003", - "filterText": "http://localhost:4545/x/a@v2.0.0", - "textEdit": { - "range": { - "start": { - "line": 0, - "character": 20 - }, - "end": { - "line": 0, - "character": 46 - } - }, - "newText": "http://localhost:4545/x/a@v2.0.0" - } - } -} diff --git a/cli/tests/lsp/completion_resolve_response.json b/cli/tests/lsp/completion_resolve_response.json new file mode 100644 index 0000000000..0edbc14ef2 --- /dev/null +++ b/cli/tests/lsp/completion_resolve_response.json @@ -0,0 +1,11 @@ +{ + "label": "build", + "kind": 6, + "detail": "const Deno.build: {\n target: string;\n arch: \"x86_64\" | \"aarch64\";\n os: \"darwin\" | \"linux\" | \"windows\";\n vendor: string;\n env?: string | undefined;\n}", + "documentation": { + "kind": "markdown", + "value": "Build related information." + }, + "sortText": "1", + "insertTextFormat": 1 +} diff --git a/cli/tests/lsp/completion_resolve_response_registry.json b/cli/tests/lsp/completion_resolve_response_registry.json new file mode 100644 index 0000000000..99a4a048e2 --- /dev/null +++ b/cli/tests/lsp/completion_resolve_response_registry.json @@ -0,0 +1,20 @@ +{ + "label": "v2.0.0", + "kind": 19, + "detail": "(version)", + "sortText": "0000000003", + "filterText": "http://localhost:4545/x/a@v2.0.0", + "textEdit": { + "range": { + "start": { + "line": 0, + "character": 20 + }, + "end": { + "line": 0, + "character": 46 + } + }, + "newText": "http://localhost:4545/x/a@v2.0.0" + } +} diff --git a/cli/tests/lsp/definition_request_asset.json b/cli/tests/lsp/definition_request_asset.json deleted file mode 100644 index 5e117cf147..0000000000 --- a/cli/tests/lsp/definition_request_asset.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "textDocument/definition", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 14 - } - } -} diff --git a/cli/tests/lsp/did_change_notification_large.json b/cli/tests/lsp/did_change_notification_large.json deleted file mode 100644 index c4999a7c3e..0000000000 --- a/cli/tests/lsp/did_change_notification_large.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didChange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 444, - "character": 11 - }, - "end": { - "line": 444, - "character": 14 - } - }, - "text": "+++" - } - ] - } -} diff --git a/cli/tests/lsp/did_change_notification_large_02.json b/cli/tests/lsp/did_change_notification_large_02.json deleted file mode 100644 index e8744d620e..0000000000 --- a/cli/tests/lsp/did_change_notification_large_02.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didChange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 445, - "character": 4 - }, - "end": { - "line": 445, - "character": 4 - } - }, - "text": "// " - } - ] - } -} diff --git a/cli/tests/lsp/did_change_notification_large_03.json b/cli/tests/lsp/did_change_notification_large_03.json deleted file mode 100644 index f39234510f..0000000000 --- a/cli/tests/lsp/did_change_notification_large_03.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didChange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 477, - "character": 4 - }, - "end": { - "line": 477, - "character": 9 - } - }, - "text": "error" - } - ] - } -} diff --git a/cli/tests/lsp/did_change_notification_mbc.json b/cli/tests/lsp/did_change_notification_mbc.json deleted file mode 100644 index fed742d396..0000000000 --- a/cli/tests/lsp/did_change_notification_mbc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didChange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 1, - "character": 11 - }, - "end": { - "line": 1, - "character": 13 - } - }, - "text": "" - } - ] - } -} diff --git a/cli/tests/lsp/did_open_notification.json b/cli/tests/lsp/did_open_notification.json deleted file mode 100644 index 04f12a7b3a..0000000000 --- a/cli/tests/lsp/did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Deno.args);\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_asset.json b/cli/tests/lsp/did_open_notification_asset.json deleted file mode 100644 index 413353f298..0000000000 --- a/cli/tests/lsp/did_open_notification_asset.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Date.now());\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_cache.json b/cli/tests/lsp/did_open_notification_cache.json deleted file mode 100644 index 6f21ee5cd9..0000000000 --- a/cli/tests/lsp/did_open_notification_cache.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"https://deno.land/x/a/mod.ts\";\n\nconsole.log(a);\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_cl_impl.json b/cli/tests/lsp/did_open_notification_cl_impl.json deleted file mode 100644 index eabcd7ccc8..0000000000 --- a/cli/tests/lsp/did_open_notification_cl_impl.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "interface A {\n b(): void;\n}\n\nclass B implements A {\n b() {\n console.log(\"b\");\n }\n}\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_cl_references.json b/cli/tests/lsp/did_open_notification_cl_references.json deleted file mode 100644 index 546ba5674c..0000000000 --- a/cli/tests/lsp/did_open_notification_cl_references.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "class A {\n a = \"a\";\n\n b() {\n console.log(this.a);\n }\n\n c() {\n this.a = \"c\";\n }\n}\n\nconst a = new A();\na.b();\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_code_action.json b/cli/tests/lsp/did_open_notification_code_action.json deleted file mode 100644 index 57559cf3c4..0000000000 --- a/cli/tests/lsp/did_open_notification_code_action.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "export function a(): void {\n await Promise.resolve(\"a\");\n}\n\nexport function b(): void {\n await Promise.resolve(\"b\");\n}\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_completion_optional.json b/cli/tests/lsp/did_open_notification_completion_optional.json deleted file mode 100644 index 5e919c80ab..0000000000 --- a/cli/tests/lsp/did_open_notification_completion_optional.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "interface A {\n b?: string;\n}\n\nconst o: A = {};\n\nfunction c(s: string) {}\n\nc(o.)" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_completion_registry.json b/cli/tests/lsp/did_open_notification_completion_registry.json deleted file mode 100644 index cb8ad25927..0000000000 --- a/cli/tests/lsp/did_open_notification_completion_registry.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"http://localhost:4545/x/a@\"" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_completion_registry_02.json b/cli/tests/lsp/did_open_notification_completion_registry_02.json deleted file mode 100644 index a53882b560..0000000000 --- a/cli/tests/lsp/did_open_notification_completion_registry_02.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "import * as a from \"\"" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_completions.json b/cli/tests/lsp/did_open_notification_completions.json deleted file mode 100644 index edcdc93734..0000000000 --- a/cli/tests/lsp/did_open_notification_completions.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "Deno." - } - } -} diff --git a/cli/tests/lsp/did_open_notification_large.json b/cli/tests/lsp/did_open_notification_large.json deleted file mode 100644 index 4a467891c2..0000000000 --- a/cli/tests/lsp/did_open_notification_large.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "javascript", - "version": 1, - "text": "// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.\n\n// @ts-check\n/// \n// deno-lint-ignore-file no-undef\n\n// This module is the entry point for \"compiler\" isolate, ie. the one\n// that is created when Deno needs to type check TypeScript, and in some\n// instances convert TypeScript to JavaScript.\n\n// Removes the `__proto__` for security reasons. This intentionally makes\n// Deno non compliant with ECMA-262 Annex B.2.2.1\ndelete Object.prototype.__proto__;\n\n((window) => {\n /** @type {DenoCore} */\n const core = window.Deno.core;\n\n let logDebug = false;\n let logSource = \"JS\";\n\n function setLogDebug(debug, source) {\n logDebug = debug;\n if (source) {\n logSource = source;\n }\n }\n\n function debug(...args) {\n if (logDebug) {\n const stringifiedArgs = args.map((arg) =>\n typeof arg === \"string\" ? arg : JSON.stringify(arg)\n ).join(\" \");\n // adding a non-zero integer value to the end of the debug string causes\n // the message to be printed to stderr instead of stdout, which is better\n // aligned to the behaviour of debug messages\n core.print(`DEBUG ${logSource} - ${stringifiedArgs}\\n`, 1);\n }\n }\n\n function error(...args) {\n const stringifiedArgs = args.map((arg) =>\n typeof arg === \"string\" || arg instanceof Error\n ? String(arg)\n : JSON.stringify(arg)\n ).join(\" \");\n core.print(`ERROR ${logSource} = ${stringifiedArgs}\\n`, 1);\n }\n\n class AssertionError extends Error {\n constructor(msg) {\n super(msg);\n this.name = \"AssertionError\";\n }\n }\n\n function assert(cond, msg = \"Assertion failed.\") {\n if (!cond) {\n throw new AssertionError(msg);\n }\n }\n\n /** @type {Map} */\n const sourceFileCache = new Map();\n\n /** @param {ts.DiagnosticRelatedInformation} diagnostic */\n function fromRelatedInformation({\n start,\n length,\n file,\n messageText: msgText,\n ...ri\n }) {\n let messageText;\n let messageChain;\n if (typeof msgText === \"object\") {\n messageChain = msgText;\n } else {\n messageText = msgText;\n }\n if (start !== undefined && length !== undefined && file) {\n const startPos = file.getLineAndCharacterOfPosition(start);\n const sourceLine = file.getFullText().split(\"\\n\")[startPos.line];\n const fileName = file.fileName;\n return {\n start: startPos,\n end: file.getLineAndCharacterOfPosition(start + length),\n fileName,\n messageChain,\n messageText,\n sourceLine,\n ...ri,\n };\n } else {\n return {\n messageChain,\n messageText,\n ...ri,\n };\n }\n }\n\n /** @param {ts.Diagnostic[]} diagnostics */\n function fromTypeScriptDiagnostic(diagnostics) {\n return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => {\n /** @type {any} */\n const value = fromRelatedInformation(diag);\n value.relatedInformation = ri\n ? ri.map(fromRelatedInformation)\n : undefined;\n value.source = source;\n return value;\n });\n }\n\n // Using incremental compile APIs requires that all\n // paths must be either relative or absolute. Since\n // analysis in Rust operates on fully resolved URLs,\n // it makes sense to use the same scheme here.\n const ASSETS = \"asset:///\";\n const CACHE = \"cache:///\";\n\n /** Diagnostics that are intentionally ignored when compiling TypeScript in\n * Deno, as they provide misleading or incorrect information. */\n const IGNORED_DIAGNOSTICS = [\n // TS1208: All files must be modules when the '--isolatedModules' flag is\n // provided. We can ignore because we guarantee that all files are\n // modules.\n 1208,\n // TS1375: 'await' expressions are only allowed at the top level of a file\n // when that file is a module, but this file has no imports or exports.\n // Consider adding an empty 'export {}' to make this file a module.\n 1375,\n // TS1103: 'for-await-of' statement is only allowed within an async function\n // or async generator.\n 1103,\n // TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is\n // not a module.\n 2306,\n // TS2691: An import path cannot end with a '.ts' extension. Consider\n // importing 'bad-module' instead.\n 2691,\n // TS2792: Cannot find module. Did you mean to set the 'moduleResolution'\n // option to 'node', or to add aliases to the 'paths' option?\n 2792,\n // TS5009: Cannot find the common subdirectory path for the input files.\n 5009,\n // TS5055: Cannot write file\n // 'http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js'\n // because it would overwrite input file.\n 5055,\n // TypeScript is overly opinionated that only CommonJS modules kinds can\n // support JSON imports. Allegedly this was fixed in\n // Microsoft/TypeScript#26825 but that doesn't seem to be working here,\n // so we will ignore complaints about this compiler setting.\n 5070,\n // TS7016: Could not find a declaration file for module '...'. '...'\n // implicitly has an 'any' type. This is due to `allowJs` being off by\n // default but importing of a JavaScript module.\n 7016,\n ];\n\n const SNAPSHOT_COMPILE_OPTIONS = {\n esModuleInterop: true,\n jsx: ts.JsxEmit.React,\n module: ts.ModuleKind.ESNext,\n noEmit: true,\n strict: true,\n target: ts.ScriptTarget.ESNext,\n };\n\n class ScriptSnapshot {\n /** @type {string} */\n specifier;\n /** @type {string} */\n version;\n /**\n * @param {string} specifier\n * @param {string} version \n */\n constructor(specifier, version) {\n this.specifier = specifier;\n this.version = version;\n }\n /**\n * @param {number} start \n * @param {number} end \n * @returns {string}\n */\n getText(start, end) {\n const { specifier, version } = this;\n debug(\n `snapshot.getText(${start}, ${end}) specifier: ${specifier} version: ${version}`,\n );\n return core.jsonOpSync(\"op_get_text\", { specifier, version, start, end });\n }\n /**\n * @returns {number}\n */\n getLength() {\n const { specifier, version } = this;\n debug(`snapshot.getLength() specifier: ${specifier} version: ${version}`);\n return core.jsonOpSync(\"op_get_length\", { specifier, version });\n }\n /**\n * @param {ScriptSnapshot} oldSnapshot\n * @returns {ts.TextChangeRange | undefined}\n */\n getChangeRange(oldSnapshot) {\n const { specifier, version } = this;\n const { version: oldVersion } = oldSnapshot;\n const oldLength = oldSnapshot.getLength();\n debug(\n `snapshot.getLength() specifier: ${specifier} oldVersion: ${oldVersion} version: ${version}`,\n );\n return core.jsonOpSync(\n \"op_get_change_range\",\n { specifier, oldLength, oldVersion, version },\n );\n }\n dispose() {\n const { specifier, version } = this;\n debug(`snapshot.dispose() specifier: ${specifier} version: ${version}`);\n core.jsonOpSync(\"op_dispose\", { specifier, version });\n }\n }\n\n /** @type {ts.CompilerOptions} */\n let compilationSettings = {};\n\n /** @type {ts.LanguageService} */\n let languageService;\n\n /** An object literal of the incremental compiler host, which provides the\n * specific \"bindings\" to the Deno environment that tsc needs to work.\n *\n * @type {ts.CompilerHost & ts.LanguageServiceHost} */\n const host = {\n fileExists(fileName) {\n debug(`host.fileExists(\"${fileName}\")`);\n return false;\n },\n readFile(specifier) {\n debug(`host.readFile(\"${specifier}\")`);\n return core.jsonOpSync(\"op_load\", { specifier }).data;\n },\n getSourceFile(\n specifier,\n languageVersion,\n _onError,\n _shouldCreateNewSourceFile,\n ) {\n debug(\n `host.getSourceFile(\"${specifier}\", ${\n ts.ScriptTarget[languageVersion]\n })`,\n );\n let sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return sourceFile;\n }\n\n /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */\n const { data, hash, scriptKind } = core.jsonOpSync(\n \"op_load\",\n { specifier },\n );\n assert(\n data != null,\n `\"data\" is unexpectedly null for \"${specifier}\".`,\n );\n sourceFile = ts.createSourceFile(\n specifier,\n data,\n languageVersion,\n false,\n scriptKind,\n );\n sourceFile.moduleName = specifier;\n sourceFile.version = hash;\n sourceFileCache.set(specifier, sourceFile);\n return sourceFile;\n },\n getDefaultLibFileName() {\n return `${ASSETS}/lib.esnext.d.ts`;\n },\n getDefaultLibLocation() {\n return ASSETS;\n },\n writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {\n debug(`host.writeFile(\"${fileName}\")`);\n let maybeSpecifiers;\n if (sourceFiles) {\n maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);\n }\n return core.jsonOpSync(\n \"op_emit\",\n { maybeSpecifiers, fileName, data },\n );\n },\n getCurrentDirectory() {\n return CACHE;\n },\n getCanonicalFileName(fileName) {\n return fileName;\n },\n useCaseSensitiveFileNames() {\n return true;\n },\n getNewLine() {\n return \"\\n\";\n },\n resolveModuleNames(specifiers, base) {\n debug(`host.resolveModuleNames()`);\n debug(` base: ${base}`);\n debug(` specifiers: ${specifiers.join(\", \")}`);\n /** @type {Array<[string, ts.Extension] | undefined>} */\n const resolved = core.jsonOpSync(\"op_resolve\", {\n specifiers,\n base,\n });\n if (resolved) {\n const result = resolved.map((item) => {\n if (item) {\n const [resolvedFileName, extension] = item;\n return {\n resolvedFileName,\n extension,\n isExternalLibraryImport: false,\n };\n }\n return undefined;\n });\n result.length = specifiers.length;\n return result;\n } else {\n return new Array(specifiers.length);\n }\n },\n createHash(data) {\n return core.jsonOpSync(\"op_create_hash\", { data }).hash;\n },\n\n // LanguageServiceHost\n getCompilationSettings() {\n debug(\"host.getCompilationSettings()\");\n return compilationSettings;\n },\n getScriptFileNames() {\n debug(\"host.getScriptFileNames()\");\n return core.jsonOpSync(\"op_script_names\", undefined);\n },\n getScriptVersion(specifier) {\n debug(`host.getScriptVersion(\"${specifier}\")`);\n const sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return sourceFile.version ?? \"1\";\n }\n return core.jsonOpSync(\"op_script_version\", { specifier });\n },\n getScriptSnapshot(specifier) {\n debug(`host.getScriptSnapshot(\"${specifier}\")`);\n const sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return {\n getText(start, end) {\n return sourceFile.text.substring(start, end);\n },\n getLength() {\n return sourceFile.text.length;\n },\n getChangeRange() {\n return undefined;\n },\n };\n }\n /** @type {string | undefined} */\n const version = core.jsonOpSync(\"op_script_version\", { specifier });\n if (version != null) {\n return new ScriptSnapshot(specifier, version);\n }\n return undefined;\n },\n };\n\n /** @type {Array<[string, number]>} */\n const stats = [];\n let statsStart = 0;\n\n function performanceStart() {\n stats.length = 0;\n statsStart = Date.now();\n ts.performance.enable();\n }\n\n /**\n * @param {{ program: ts.Program | ts.EmitAndSemanticDiagnosticsBuilderProgram, fileCount?: number }} options \n */\n function performanceProgram({ program, fileCount }) {\n if (program) {\n if (\"getProgram\" in program) {\n program = program.getProgram();\n }\n stats.push([\"Files\", program.getSourceFiles().length]);\n stats.push([\"Nodes\", program.getNodeCount()]);\n stats.push([\"Identifiers\", program.getIdentifierCount()]);\n stats.push([\"Symbols\", program.getSymbolCount()]);\n stats.push([\"Types\", program.getTypeCount()]);\n stats.push([\"Instantiations\", program.getInstantiationCount()]);\n } else if (fileCount != null) {\n stats.push([\"Files\", fileCount]);\n }\n const programTime = ts.performance.getDuration(\"Program\");\n const bindTime = ts.performance.getDuration(\"Bind\");\n const checkTime = ts.performance.getDuration(\"Check\");\n const emitTime = ts.performance.getDuration(\"Emit\");\n stats.push([\"Parse time\", programTime]);\n stats.push([\"Bind time\", bindTime]);\n stats.push([\"Check time\", checkTime]);\n stats.push([\"Emit time\", emitTime]);\n stats.push(\n [\"Total TS time\", programTime + bindTime + checkTime + emitTime],\n );\n }\n\n function performanceEnd() {\n const duration = Date.now() - statsStart;\n stats.push([\"Compile time\", duration]);\n return stats;\n }\n\n /**\n * @typedef {object} Request\n * @property {Record} config\n * @property {boolean} debug\n * @property {string[]} rootNames\n */\n\n /** The API that is called by Rust when executing a request.\n * @param {Request} request\n */\n function exec({ config, debug: debugFlag, rootNames }) {\n setLogDebug(debugFlag, \"TS\");\n performanceStart();\n debug(\">>> exec start\", { rootNames });\n debug(config);\n\n const { options, errors: configFileParsingDiagnostics } = ts\n .convertCompilerOptionsFromJson(config, \"\");\n // The `allowNonTsExtensions` is a \"hidden\" compiler option used in VSCode\n // which is not allowed to be passed in JSON, we need it to allow special\n // URLs which Deno supports. So we need to either ignore the diagnostic, or\n // inject it ourselves.\n Object.assign(options, { allowNonTsExtensions: true });\n const program = ts.createIncrementalProgram({\n rootNames,\n options,\n host,\n configFileParsingDiagnostics,\n });\n\n const { diagnostics: emitDiagnostics } = program.emit();\n\n const diagnostics = [\n ...program.getConfigFileParsingDiagnostics(),\n ...program.getSyntacticDiagnostics(),\n ...program.getOptionsDiagnostics(),\n ...program.getGlobalDiagnostics(),\n ...program.getSemanticDiagnostics(),\n ...emitDiagnostics,\n ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));\n performanceProgram({ program });\n\n core.jsonOpSync(\"op_respond\", {\n diagnostics: fromTypeScriptDiagnostic(diagnostics),\n stats: performanceEnd(),\n });\n debug(\"<<< exec stop\");\n }\n\n /**\n * @param {number} id \n * @param {any} data \n */\n function respond(id, data = null) {\n core.jsonOpSync(\"op_respond\", { id, data });\n }\n\n /**\n * @param {LanguageServerRequest} request \n */\n function serverRequest({ id, ...request }) {\n debug(`serverRequest()`, { id, ...request });\n switch (request.method) {\n case \"configure\": {\n const { options, errors } = ts\n .convertCompilerOptionsFromJson(request.compilerOptions, \"\");\n Object.assign(options, { allowNonTsExtensions: true });\n if (errors.length) {\n debug(ts.formatDiagnostics(errors, host));\n }\n compilationSettings = options;\n return respond(id, true);\n }\n case \"getAsset\": {\n const sourceFile = host.getSourceFile(\n request.specifier,\n ts.ScriptTarget.ESNext,\n );\n return respond(id, sourceFile && sourceFile.text);\n }\n case \"getDiagnostics\": {\n try {\n /** @type {Record} */\n const diagnosticMap = {};\n for (const specifier of request.specifiers) {\n diagnosticMap[specifier] = fromTypeScriptDiagnostic([\n ...languageService.getSemanticDiagnostics(specifier),\n ...languageService.getSuggestionDiagnostics(specifier),\n ...languageService.getSyntacticDiagnostics(specifier),\n ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code)));\n }\n return respond(id, diagnosticMap);\n } catch (e) {\n if (\"stack\" in e) {\n error(e.stack);\n } else {\n error(e);\n }\n return respond(id, {});\n }\n }\n case \"getQuickInfo\": {\n return respond(\n id,\n languageService.getQuickInfoAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getCompletions\": {\n return respond(\n id,\n languageService.getCompletionsAtPosition(\n request.specifier,\n request.position,\n request.preferences,\n ),\n );\n }\n case \"getDocumentHighlights\": {\n return respond(\n id,\n languageService.getDocumentHighlights(\n request.specifier,\n request.position,\n request.filesToSearch,\n ),\n );\n }\n case \"getReferences\": {\n return respond(\n id,\n languageService.getReferencesAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getDefinition\": {\n return respond(\n id,\n languageService.getDefinitionAndBoundSpan(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getImplementation\": {\n return respond(\n id,\n languageService.getImplementationAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"findRenameLocations\": {\n return respond(\n id,\n languageService.findRenameLocations(\n request.specifier,\n request.position,\n request.findInStrings,\n request.findInComments,\n request.providePrefixAndSuffixTextForRename,\n ),\n );\n }\n default:\n throw new TypeError(\n // @ts-ignore exhausted case statement sets type to never\n `Invalid request method for request: \"${request.method}\" (${id})`,\n );\n }\n }\n\n /** @param {{ debug: boolean; }} init */\n function serverInit({ debug: debugFlag }) {\n if (hasStarted) {\n throw new Error(\"The language server has already been initialized.\");\n }\n hasStarted = true;\n languageService = ts.createLanguageService(host);\n core.ops();\n setLogDebug(debugFlag, \"TSLS\");\n debug(\"serverInit()\");\n }\n\n let hasStarted = false;\n\n /** Startup the runtime environment, setting various flags.\n * @param {{ debugFlag?: boolean; legacyFlag?: boolean; }} msg\n */\n function startup({ debugFlag = false }) {\n if (hasStarted) {\n throw new Error(\"The compiler runtime already started.\");\n }\n hasStarted = true;\n core.ops();\n setLogDebug(!!debugFlag, \"TS\");\n }\n\n // Setup the compiler runtime during the build process.\n core.ops();\n core.registerErrorClass(\"Error\", Error);\n\n // A build time only op that provides some setup information that is used to\n // ensure the snapshot is setup properly.\n /** @type {{ buildSpecifier: string; libs: string[] }} */\n const { buildSpecifier, libs } = core.jsonOpSync(\"op_build_info\", {});\n for (const lib of libs) {\n const specifier = `lib.${lib}.d.ts`;\n // we are using internal APIs here to \"inject\" our custom libraries into\n // tsc, so things like `\"lib\": [ \"deno.ns\" ]` are supported.\n if (!ts.libs.includes(lib)) {\n ts.libs.push(lib);\n ts.libMap.set(lib, `lib.${lib}.d.ts`);\n }\n // we are caching in memory common type libraries that will be re-used by\n // tsc on when the snapshot is restored\n assert(\n host.getSourceFile(`${ASSETS}${specifier}`, ts.ScriptTarget.ESNext),\n );\n }\n // this helps ensure as much as possible is in memory that is re-usable\n // before the snapshotting is done, which helps unsure fast \"startup\" for\n // subsequent uses of tsc in Deno.\n const TS_SNAPSHOT_PROGRAM = ts.createProgram({\n rootNames: [buildSpecifier],\n options: SNAPSHOT_COMPILE_OPTIONS,\n host,\n });\n ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM);\n\n // exposes the two functions that are called by `tsc::exec()` when type\n // checking TypeScript.\n globalThis.startup = startup;\n globalThis.exec = exec;\n\n // exposes the functions that are called when the compiler is used as a\n // language service.\n globalThis.serverInit = serverInit;\n globalThis.serverRequest = serverRequest;\n})(this);\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_mbc.json b/cli/tests/lsp/did_open_notification_mbc.json deleted file mode 100644 index d7dd9444e7..0000000000 --- a/cli/tests/lsp/did_open_notification_mbc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const a = `编写软件很难`;\nconst b = `👍🦕😃`;\nconsole.log(a, b);\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_mbc_fmt.json b/cli/tests/lsp/did_open_notification_mbc_fmt.json deleted file mode 100644 index 528dad25dd..0000000000 --- a/cli/tests/lsp/did_open_notification_mbc_fmt.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "const bar = '👍🇺🇸😃'\nconsole.log('hello deno')\n" - } - } -} diff --git a/cli/tests/lsp/did_open_notification_unstable.json b/cli/tests/lsp/did_open_notification_unstable.json deleted file mode 100644 index bb7a1f6792..0000000000 --- a/cli/tests/lsp/did_open_notification_unstable.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "console.log(Deno.openPlugin);\n" - } - } -} diff --git a/cli/tests/lsp/did_open_params_doc_symbol.json b/cli/tests/lsp/did_open_params_doc_symbol.json new file mode 100644 index 0000000000..c748771919 --- /dev/null +++ b/cli/tests/lsp/did_open_params_doc_symbol.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "interface IFoo {\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public x: number) { }\n foo() { return true; }\n /** @deprecated */\n baz() { return false; }\n get value(): number { return 0; }\n set value(newVavlue: number) { return; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n\nenum Values { value1, value2 }\n\nvar bar: IFoo = new Bar(3);" + } +} diff --git a/cli/tests/lsp/did_open_params_large.json b/cli/tests/lsp/did_open_params_large.json new file mode 100644 index 0000000000..b78d6e799e --- /dev/null +++ b/cli/tests/lsp/did_open_params_large.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "javascript", + "version": 1, + "text": "// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.\n\n// @ts-check\n/// \n// deno-lint-ignore-file no-undef\n\n// This module is the entry point for \"compiler\" isolate, ie. the one\n// that is created when Deno needs to type check TypeScript, and in some\n// instances convert TypeScript to JavaScript.\n\n// Removes the `__proto__` for security reasons. This intentionally makes\n// Deno non compliant with ECMA-262 Annex B.2.2.1\ndelete Object.prototype.__proto__;\n\n((window) => {\n /** @type {DenoCore} */\n const core = window.Deno.core;\n\n let logDebug = false;\n let logSource = \"JS\";\n\n function setLogDebug(debug, source) {\n logDebug = debug;\n if (source) {\n logSource = source;\n }\n }\n\n function debug(...args) {\n if (logDebug) {\n const stringifiedArgs = args.map((arg) =>\n typeof arg === \"string\" ? arg : JSON.stringify(arg)\n ).join(\" \");\n // adding a non-zero integer value to the end of the debug string causes\n // the message to be printed to stderr instead of stdout, which is better\n // aligned to the behaviour of debug messages\n core.print(`DEBUG ${logSource} - ${stringifiedArgs}\\n`, 1);\n }\n }\n\n function error(...args) {\n const stringifiedArgs = args.map((arg) =>\n typeof arg === \"string\" || arg instanceof Error\n ? String(arg)\n : JSON.stringify(arg)\n ).join(\" \");\n core.print(`ERROR ${logSource} = ${stringifiedArgs}\\n`, 1);\n }\n\n class AssertionError extends Error {\n constructor(msg) {\n super(msg);\n this.name = \"AssertionError\";\n }\n }\n\n function assert(cond, msg = \"Assertion failed.\") {\n if (!cond) {\n throw new AssertionError(msg);\n }\n }\n\n /** @type {Map} */\n const sourceFileCache = new Map();\n\n /** @param {ts.DiagnosticRelatedInformation} diagnostic */\n function fromRelatedInformation({\n start,\n length,\n file,\n messageText: msgText,\n ...ri\n }) {\n let messageText;\n let messageChain;\n if (typeof msgText === \"object\") {\n messageChain = msgText;\n } else {\n messageText = msgText;\n }\n if (start !== undefined && length !== undefined && file) {\n const startPos = file.getLineAndCharacterOfPosition(start);\n const sourceLine = file.getFullText().split(\"\\n\")[startPos.line];\n const fileName = file.fileName;\n return {\n start: startPos,\n end: file.getLineAndCharacterOfPosition(start + length),\n fileName,\n messageChain,\n messageText,\n sourceLine,\n ...ri,\n };\n } else {\n return {\n messageChain,\n messageText,\n ...ri,\n };\n }\n }\n\n /** @param {ts.Diagnostic[]} diagnostics */\n function fromTypeScriptDiagnostic(diagnostics) {\n return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => {\n /** @type {any} */\n const value = fromRelatedInformation(diag);\n value.relatedInformation = ri\n ? ri.map(fromRelatedInformation)\n : undefined;\n value.source = source;\n return value;\n });\n }\n\n // Using incremental compile APIs requires that all\n // paths must be either relative or absolute. Since\n // analysis in Rust operates on fully resolved URLs,\n // it makes sense to use the same scheme here.\n const ASSETS = \"asset:///\";\n const CACHE = \"cache:///\";\n\n /** Diagnostics that are intentionally ignored when compiling TypeScript in\n * Deno, as they provide misleading or incorrect information. */\n const IGNORED_DIAGNOSTICS = [\n // TS1208: All files must be modules when the '--isolatedModules' flag is\n // provided. We can ignore because we guarantee that all files are\n // modules.\n 1208,\n // TS1375: 'await' expressions are only allowed at the top level of a file\n // when that file is a module, but this file has no imports or exports.\n // Consider adding an empty 'export {}' to make this file a module.\n 1375,\n // TS1103: 'for-await-of' statement is only allowed within an async function\n // or async generator.\n 1103,\n // TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is\n // not a module.\n 2306,\n // TS2691: An import path cannot end with a '.ts' extension. Consider\n // importing 'bad-module' instead.\n 2691,\n // TS2792: Cannot find module. Did you mean to set the 'moduleResolution'\n // option to 'node', or to add aliases to the 'paths' option?\n 2792,\n // TS5009: Cannot find the common subdirectory path for the input files.\n 5009,\n // TS5055: Cannot write file\n // 'http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js'\n // because it would overwrite input file.\n 5055,\n // TypeScript is overly opinionated that only CommonJS modules kinds can\n // support JSON imports. Allegedly this was fixed in\n // Microsoft/TypeScript#26825 but that doesn't seem to be working here,\n // so we will ignore complaints about this compiler setting.\n 5070,\n // TS7016: Could not find a declaration file for module '...'. '...'\n // implicitly has an 'any' type. This is due to `allowJs` being off by\n // default but importing of a JavaScript module.\n 7016,\n ];\n\n const SNAPSHOT_COMPILE_OPTIONS = {\n esModuleInterop: true,\n jsx: ts.JsxEmit.React,\n module: ts.ModuleKind.ESNext,\n noEmit: true,\n strict: true,\n target: ts.ScriptTarget.ESNext,\n };\n\n class ScriptSnapshot {\n /** @type {string} */\n specifier;\n /** @type {string} */\n version;\n /**\n * @param {string} specifier\n * @param {string} version \n */\n constructor(specifier, version) {\n this.specifier = specifier;\n this.version = version;\n }\n /**\n * @param {number} start \n * @param {number} end \n * @returns {string}\n */\n getText(start, end) {\n const { specifier, version } = this;\n debug(\n `snapshot.getText(${start}, ${end}) specifier: ${specifier} version: ${version}`,\n );\n return core.jsonOpSync(\"op_get_text\", { specifier, version, start, end });\n }\n /**\n * @returns {number}\n */\n getLength() {\n const { specifier, version } = this;\n debug(`snapshot.getLength() specifier: ${specifier} version: ${version}`);\n return core.jsonOpSync(\"op_get_length\", { specifier, version });\n }\n /**\n * @param {ScriptSnapshot} oldSnapshot\n * @returns {ts.TextChangeRange | undefined}\n */\n getChangeRange(oldSnapshot) {\n const { specifier, version } = this;\n const { version: oldVersion } = oldSnapshot;\n const oldLength = oldSnapshot.getLength();\n debug(\n `snapshot.getLength() specifier: ${specifier} oldVersion: ${oldVersion} version: ${version}`,\n );\n return core.jsonOpSync(\n \"op_get_change_range\",\n { specifier, oldLength, oldVersion, version },\n );\n }\n dispose() {\n const { specifier, version } = this;\n debug(`snapshot.dispose() specifier: ${specifier} version: ${version}`);\n core.jsonOpSync(\"op_dispose\", { specifier, version });\n }\n }\n\n /** @type {ts.CompilerOptions} */\n let compilationSettings = {};\n\n /** @type {ts.LanguageService} */\n let languageService;\n\n /** An object literal of the incremental compiler host, which provides the\n * specific \"bindings\" to the Deno environment that tsc needs to work.\n *\n * @type {ts.CompilerHost & ts.LanguageServiceHost} */\n const host = {\n fileExists(fileName) {\n debug(`host.fileExists(\"${fileName}\")`);\n return false;\n },\n readFile(specifier) {\n debug(`host.readFile(\"${specifier}\")`);\n return core.jsonOpSync(\"op_load\", { specifier }).data;\n },\n getSourceFile(\n specifier,\n languageVersion,\n _onError,\n _shouldCreateNewSourceFile,\n ) {\n debug(\n `host.getSourceFile(\"${specifier}\", ${\n ts.ScriptTarget[languageVersion]\n })`,\n );\n let sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return sourceFile;\n }\n\n /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */\n const { data, hash, scriptKind } = core.jsonOpSync(\n \"op_load\",\n { specifier },\n );\n assert(\n data != null,\n `\"data\" is unexpectedly null for \"${specifier}\".`,\n );\n sourceFile = ts.createSourceFile(\n specifier,\n data,\n languageVersion,\n false,\n scriptKind,\n );\n sourceFile.moduleName = specifier;\n sourceFile.version = hash;\n sourceFileCache.set(specifier, sourceFile);\n return sourceFile;\n },\n getDefaultLibFileName() {\n return `${ASSETS}/lib.esnext.d.ts`;\n },\n getDefaultLibLocation() {\n return ASSETS;\n },\n writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {\n debug(`host.writeFile(\"${fileName}\")`);\n let maybeSpecifiers;\n if (sourceFiles) {\n maybeSpecifiers = sourceFiles.map((sf) => sf.moduleName);\n }\n return core.jsonOpSync(\n \"op_emit\",\n { maybeSpecifiers, fileName, data },\n );\n },\n getCurrentDirectory() {\n return CACHE;\n },\n getCanonicalFileName(fileName) {\n return fileName;\n },\n useCaseSensitiveFileNames() {\n return true;\n },\n getNewLine() {\n return \"\\n\";\n },\n resolveModuleNames(specifiers, base) {\n debug(`host.resolveModuleNames()`);\n debug(` base: ${base}`);\n debug(` specifiers: ${specifiers.join(\", \")}`);\n /** @type {Array<[string, ts.Extension] | undefined>} */\n const resolved = core.jsonOpSync(\"op_resolve\", {\n specifiers,\n base,\n });\n if (resolved) {\n const result = resolved.map((item) => {\n if (item) {\n const [resolvedFileName, extension] = item;\n return {\n resolvedFileName,\n extension,\n isExternalLibraryImport: false,\n };\n }\n return undefined;\n });\n result.length = specifiers.length;\n return result;\n } else {\n return new Array(specifiers.length);\n }\n },\n createHash(data) {\n return core.jsonOpSync(\"op_create_hash\", { data }).hash;\n },\n\n // LanguageServiceHost\n getCompilationSettings() {\n debug(\"host.getCompilationSettings()\");\n return compilationSettings;\n },\n getScriptFileNames() {\n debug(\"host.getScriptFileNames()\");\n return core.jsonOpSync(\"op_script_names\", undefined);\n },\n getScriptVersion(specifier) {\n debug(`host.getScriptVersion(\"${specifier}\")`);\n const sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return sourceFile.version ?? \"1\";\n }\n return core.jsonOpSync(\"op_script_version\", { specifier });\n },\n getScriptSnapshot(specifier) {\n debug(`host.getScriptSnapshot(\"${specifier}\")`);\n const sourceFile = sourceFileCache.get(specifier);\n if (sourceFile) {\n return {\n getText(start, end) {\n return sourceFile.text.substring(start, end);\n },\n getLength() {\n return sourceFile.text.length;\n },\n getChangeRange() {\n return undefined;\n },\n };\n }\n /** @type {string | undefined} */\n const version = core.jsonOpSync(\"op_script_version\", { specifier });\n if (version != null) {\n return new ScriptSnapshot(specifier, version);\n }\n return undefined;\n },\n };\n\n /** @type {Array<[string, number]>} */\n const stats = [];\n let statsStart = 0;\n\n function performanceStart() {\n stats.length = 0;\n statsStart = Date.now();\n ts.performance.enable();\n }\n\n /**\n * @param {{ program: ts.Program | ts.EmitAndSemanticDiagnosticsBuilderProgram, fileCount?: number }} options \n */\n function performanceProgram({ program, fileCount }) {\n if (program) {\n if (\"getProgram\" in program) {\n program = program.getProgram();\n }\n stats.push([\"Files\", program.getSourceFiles().length]);\n stats.push([\"Nodes\", program.getNodeCount()]);\n stats.push([\"Identifiers\", program.getIdentifierCount()]);\n stats.push([\"Symbols\", program.getSymbolCount()]);\n stats.push([\"Types\", program.getTypeCount()]);\n stats.push([\"Instantiations\", program.getInstantiationCount()]);\n } else if (fileCount != null) {\n stats.push([\"Files\", fileCount]);\n }\n const programTime = ts.performance.getDuration(\"Program\");\n const bindTime = ts.performance.getDuration(\"Bind\");\n const checkTime = ts.performance.getDuration(\"Check\");\n const emitTime = ts.performance.getDuration(\"Emit\");\n stats.push([\"Parse time\", programTime]);\n stats.push([\"Bind time\", bindTime]);\n stats.push([\"Check time\", checkTime]);\n stats.push([\"Emit time\", emitTime]);\n stats.push(\n [\"Total TS time\", programTime + bindTime + checkTime + emitTime],\n );\n }\n\n function performanceEnd() {\n const duration = Date.now() - statsStart;\n stats.push([\"Compile time\", duration]);\n return stats;\n }\n\n /**\n * @typedef {object} Request\n * @property {Record} config\n * @property {boolean} debug\n * @property {string[]} rootNames\n */\n\n /** The API that is called by Rust when executing a request.\n * @param {Request} request\n */\n function exec({ config, debug: debugFlag, rootNames }) {\n setLogDebug(debugFlag, \"TS\");\n performanceStart();\n debug(\">>> exec start\", { rootNames });\n debug(config);\n\n const { options, errors: configFileParsingDiagnostics } = ts\n .convertCompilerOptionsFromJson(config, \"\");\n // The `allowNonTsExtensions` is a \"hidden\" compiler option used in VSCode\n // which is not allowed to be passed in JSON, we need it to allow special\n // URLs which Deno supports. So we need to either ignore the diagnostic, or\n // inject it ourselves.\n Object.assign(options, { allowNonTsExtensions: true });\n const program = ts.createIncrementalProgram({\n rootNames,\n options,\n host,\n configFileParsingDiagnostics,\n });\n\n const { diagnostics: emitDiagnostics } = program.emit();\n\n const diagnostics = [\n ...program.getConfigFileParsingDiagnostics(),\n ...program.getSyntacticDiagnostics(),\n ...program.getOptionsDiagnostics(),\n ...program.getGlobalDiagnostics(),\n ...program.getSemanticDiagnostics(),\n ...emitDiagnostics,\n ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));\n performanceProgram({ program });\n\n core.jsonOpSync(\"op_respond\", {\n diagnostics: fromTypeScriptDiagnostic(diagnostics),\n stats: performanceEnd(),\n });\n debug(\"<<< exec stop\");\n }\n\n /**\n * @param {number} id \n * @param {any} data \n */\n function respond(id, data = null) {\n core.jsonOpSync(\"op_respond\", { id, data });\n }\n\n /**\n * @param {LanguageServerRequest} request \n */\n function serverRequest({ id, ...request }) {\n debug(`serverRequest()`, { id, ...request });\n switch (request.method) {\n case \"configure\": {\n const { options, errors } = ts\n .convertCompilerOptionsFromJson(request.compilerOptions, \"\");\n Object.assign(options, { allowNonTsExtensions: true });\n if (errors.length) {\n debug(ts.formatDiagnostics(errors, host));\n }\n compilationSettings = options;\n return respond(id, true);\n }\n case \"getAsset\": {\n const sourceFile = host.getSourceFile(\n request.specifier,\n ts.ScriptTarget.ESNext,\n );\n return respond(id, sourceFile && sourceFile.text);\n }\n case \"getDiagnostics\": {\n try {\n /** @type {Record} */\n const diagnosticMap = {};\n for (const specifier of request.specifiers) {\n diagnosticMap[specifier] = fromTypeScriptDiagnostic([\n ...languageService.getSemanticDiagnostics(specifier),\n ...languageService.getSuggestionDiagnostics(specifier),\n ...languageService.getSyntacticDiagnostics(specifier),\n ].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code)));\n }\n return respond(id, diagnosticMap);\n } catch (e) {\n if (\"stack\" in e) {\n error(e.stack);\n } else {\n error(e);\n }\n return respond(id, {});\n }\n }\n case \"getQuickInfo\": {\n return respond(\n id,\n languageService.getQuickInfoAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getCompletions\": {\n return respond(\n id,\n languageService.getCompletionsAtPosition(\n request.specifier,\n request.position,\n request.preferences,\n ),\n );\n }\n case \"getDocumentHighlights\": {\n return respond(\n id,\n languageService.getDocumentHighlights(\n request.specifier,\n request.position,\n request.filesToSearch,\n ),\n );\n }\n case \"getReferences\": {\n return respond(\n id,\n languageService.getReferencesAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getDefinition\": {\n return respond(\n id,\n languageService.getDefinitionAndBoundSpan(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"getImplementation\": {\n return respond(\n id,\n languageService.getImplementationAtPosition(\n request.specifier,\n request.position,\n ),\n );\n }\n case \"findRenameLocations\": {\n return respond(\n id,\n languageService.findRenameLocations(\n request.specifier,\n request.position,\n request.findInStrings,\n request.findInComments,\n request.providePrefixAndSuffixTextForRename,\n ),\n );\n }\n default:\n throw new TypeError(\n // @ts-ignore exhausted case statement sets type to never\n `Invalid request method for request: \"${request.method}\" (${id})`,\n );\n }\n }\n\n /** @param {{ debug: boolean; }} init */\n function serverInit({ debug: debugFlag }) {\n if (hasStarted) {\n throw new Error(\"The language server has already been initialized.\");\n }\n hasStarted = true;\n languageService = ts.createLanguageService(host);\n core.ops();\n setLogDebug(debugFlag, \"TSLS\");\n debug(\"serverInit()\");\n }\n\n let hasStarted = false;\n\n /** Startup the runtime environment, setting various flags.\n * @param {{ debugFlag?: boolean; legacyFlag?: boolean; }} msg\n */\n function startup({ debugFlag = false }) {\n if (hasStarted) {\n throw new Error(\"The compiler runtime already started.\");\n }\n hasStarted = true;\n core.ops();\n setLogDebug(!!debugFlag, \"TS\");\n }\n\n // Setup the compiler runtime during the build process.\n core.ops();\n core.registerErrorClass(\"Error\", Error);\n\n // A build time only op that provides some setup information that is used to\n // ensure the snapshot is setup properly.\n /** @type {{ buildSpecifier: string; libs: string[] }} */\n const { buildSpecifier, libs } = core.jsonOpSync(\"op_build_info\", {});\n for (const lib of libs) {\n const specifier = `lib.${lib}.d.ts`;\n // we are using internal APIs here to \"inject\" our custom libraries into\n // tsc, so things like `\"lib\": [ \"deno.ns\" ]` are supported.\n if (!ts.libs.includes(lib)) {\n ts.libs.push(lib);\n ts.libMap.set(lib, `lib.${lib}.d.ts`);\n }\n // we are caching in memory common type libraries that will be re-used by\n // tsc on when the snapshot is restored\n assert(\n host.getSourceFile(`${ASSETS}${specifier}`, ts.ScriptTarget.ESNext),\n );\n }\n // this helps ensure as much as possible is in memory that is re-usable\n // before the snapshotting is done, which helps unsure fast \"startup\" for\n // subsequent uses of tsc in Deno.\n const TS_SNAPSHOT_PROGRAM = ts.createProgram({\n rootNames: [buildSpecifier],\n options: SNAPSHOT_COMPILE_OPTIONS,\n host,\n });\n ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM);\n\n // exposes the two functions that are called by `tsc::exec()` when type\n // checking TypeScript.\n globalThis.startup = startup;\n globalThis.exec = exec;\n\n // exposes the functions that are called when the compiler is used as a\n // language service.\n globalThis.serverInit = serverInit;\n globalThis.serverRequest = serverRequest;\n})(this);\n" + } +} diff --git a/cli/tests/lsp/did_open_params_semantic_tokens.json b/cli/tests/lsp/did_open_params_semantic_tokens.json new file mode 100644 index 0000000000..5cf48ae051 --- /dev/null +++ b/cli/tests/lsp/did_open_params_semantic_tokens.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "enum Values { value1, value2 }\n\nasync function baz(s: string): Promise {\n const r = s.slice(0);\n return r;\n}\n\ninterface IFoo {\n readonly x: number;\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public readonly x: number) { }\n foo() { return true; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n" + } +} diff --git a/cli/tests/lsp/document_symbol_did_open_notification.json b/cli/tests/lsp/document_symbol_did_open_notification.json deleted file mode 100644 index 31c37c152b..0000000000 --- a/cli/tests/lsp/document_symbol_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "interface IFoo {\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public x: number) { }\n foo() { return true; }\n /** @deprecated */\n baz() { return false; }\n get value(): number { return 0; }\n set value(newVavlue: number) { return; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n\nenum Values { value1, value2 }\n\nvar bar: IFoo = new Bar(3);" - } - } -} diff --git a/cli/tests/lsp/document_symbol_request.json b/cli/tests/lsp/document_symbol_request.json deleted file mode 100644 index a31317fc35..0000000000 --- a/cli/tests/lsp/document_symbol_request.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/documentSymbol", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - } - } -} diff --git a/cli/tests/lsp/document_symbol_response.json b/cli/tests/lsp/document_symbol_response.json new file mode 100644 index 0000000000..89d56ef707 --- /dev/null +++ b/cli/tests/lsp/document_symbol_response.json @@ -0,0 +1,371 @@ +[ + { + "name": "bar", + "kind": 13, + "range": { + "start": { + "line": 17, + "character": 4 + }, + "end": { + "line": 17, + "character": 26 + } + }, + "selectionRange": { + "start": { + "line": 17, + "character": 4 + }, + "end": { + "line": 17, + "character": 7 + } + } + }, + { + "name": "Bar", + "kind": 5, + "range": { + "start": { + "line": 4, + "character": 0 + }, + "end": { + "line": 13, + "character": 1 + } + }, + "selectionRange": { + "start": { + "line": 4, + "character": 6 + }, + "end": { + "line": 4, + "character": 9 + } + }, + "children": [ + { + "name": "constructor", + "kind": 9, + "range": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 35 + } + }, + "selectionRange": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 35 + } + } + }, + { + "name": "baz", + "kind": 6, + "tags": [ + 1 + ], + "range": { + "start": { + "line": 8, + "character": 2 + }, + "end": { + "line": 8, + "character": 25 + } + }, + "selectionRange": { + "start": { + "line": 8, + "character": 2 + }, + "end": { + "line": 8, + "character": 5 + } + } + }, + { + "name": "foo", + "kind": 6, + "range": { + "start": { + "line": 6, + "character": 2 + }, + "end": { + "line": 6, + "character": 24 + } + }, + "selectionRange": { + "start": { + "line": 6, + "character": 2 + }, + "end": { + "line": 6, + "character": 5 + } + } + }, + { + "name": "getStaticBar", + "kind": 6, + "range": { + "start": { + "line": 12, + "character": 2 + }, + "end": { + "line": 12, + "character": 57 + } + }, + "selectionRange": { + "start": { + "line": 12, + "character": 17 + }, + "end": { + "line": 12, + "character": 29 + } + } + }, + { + "name": "staticBar", + "kind": 7, + "range": { + "start": { + "line": 11, + "character": 2 + }, + "end": { + "line": 11, + "character": 32 + } + }, + "selectionRange": { + "start": { + "line": 11, + "character": 9 + }, + "end": { + "line": 11, + "character": 18 + } + } + }, + { + "name": "value", + "kind": 7, + "range": { + "start": { + "line": 9, + "character": 2 + }, + "end": { + "line": 9, + "character": 35 + } + }, + "selectionRange": { + "start": { + "line": 9, + "character": 6 + }, + "end": { + "line": 9, + "character": 11 + } + } + }, + { + "name": "value", + "kind": 7, + "range": { + "start": { + "line": 10, + "character": 2 + }, + "end": { + "line": 10, + "character": 42 + } + }, + "selectionRange": { + "start": { + "line": 10, + "character": 6 + }, + "end": { + "line": 10, + "character": 11 + } + } + }, + { + "name": "x", + "kind": 7, + "range": { + "start": { + "line": 5, + "character": 14 + }, + "end": { + "line": 5, + "character": 30 + } + }, + "selectionRange": { + "start": { + "line": 5, + "character": 21 + }, + "end": { + "line": 5, + "character": 22 + } + } + } + ] + }, + { + "name": "IFoo", + "kind": 11, + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "selectionRange": { + "start": { + "line": 0, + "character": 10 + }, + "end": { + "line": 0, + "character": 14 + } + }, + "children": [ + { + "name": "foo", + "kind": 6, + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 17 + } + }, + "selectionRange": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 1, + "character": 5 + } + } + } + ] + }, + { + "name": "Values", + "kind": 10, + "range": { + "start": { + "line": 15, + "character": 0 + }, + "end": { + "line": 15, + "character": 30 + } + }, + "selectionRange": { + "start": { + "line": 15, + "character": 5 + }, + "end": { + "line": 15, + "character": 11 + } + }, + "children": [ + { + "name": "value1", + "kind": 13, + "range": { + "start": { + "line": 15, + "character": 14 + }, + "end": { + "line": 15, + "character": 20 + } + }, + "selectionRange": { + "start": { + "line": 15, + "character": 14 + }, + "end": { + "line": 15, + "character": 20 + } + } + }, + { + "name": "value2", + "kind": 13, + "range": { + "start": { + "line": 15, + "character": 22 + }, + "end": { + "line": 15, + "character": 28 + } + }, + "selectionRange": { + "start": { + "line": 15, + "character": 22 + }, + "end": { + "line": 15, + "character": 28 + } + } + } + ] + } +] diff --git a/cli/tests/lsp/exit_notification.json b/cli/tests/lsp/exit_notification.json deleted file mode 100644 index 799a0d1d53..0000000000 --- a/cli/tests/lsp/exit_notification.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "exit", - "params": null -} diff --git a/cli/tests/lsp/folding_range_did_open_notification.json b/cli/tests/lsp/folding_range_did_open_notification.json deleted file mode 100644 index 938d99751d..0000000000 --- a/cli/tests/lsp/folding_range_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "// #region 1\n/*\n * Some comment\n */\nclass Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}\n// #endregion" - } - } -} diff --git a/cli/tests/lsp/folding_range_request.json b/cli/tests/lsp/folding_range_request.json deleted file mode 100644 index e82b6ec0bb..0000000000 --- a/cli/tests/lsp/folding_range_request.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/foldingRange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - } - } -} diff --git a/cli/tests/lsp/formatting_mbc_response.json b/cli/tests/lsp/formatting_mbc_response.json new file mode 100644 index 0000000000..1c0b9f8e89 --- /dev/null +++ b/cli/tests/lsp/formatting_mbc_response.json @@ -0,0 +1,54 @@ +[ + { + "range": { + "start": { + "line": 0, + "character": 12 + }, + "end": { + "line": 0, + "character": 13 + } + }, + "newText": "\"" + }, + { + "range": { + "start": { + "line": 0, + "character": 21 + }, + "end": { + "line": 0, + "character": 22 + } + }, + "newText": "\";" + }, + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 13 + } + }, + "newText": "\"" + }, + { + "range": { + "start": { + "line": 1, + "character": 23 + }, + "end": { + "line": 1, + "character": 25 + } + }, + "newText": "\");" + } +] diff --git a/cli/tests/lsp/formatting_request_mbc_fmt.json b/cli/tests/lsp/formatting_request_mbc_fmt.json deleted file mode 100644 index f20cc18e69..0000000000 --- a/cli/tests/lsp/formatting_request_mbc_fmt.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/formatting", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "options": { - "tabSize": 2, - "insertSpaces": true - } - } -} diff --git a/cli/tests/lsp/hover_request.json b/cli/tests/lsp/hover_request.json deleted file mode 100644 index f12bd52df6..0000000000 --- a/cli/tests/lsp/hover_request.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 19 - } - } -} diff --git a/cli/tests/lsp/hover_request_asset.json b/cli/tests/lsp/hover_request_asset.json deleted file mode 100644 index 2ae96acd5f..0000000000 --- a/cli/tests/lsp/hover_request_asset.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 5, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "deno:/asset//lib.es2015.symbol.wellknown.d.ts" - }, - "position": { - "line": 109, - "character": 13 - } - } -} diff --git a/cli/tests/lsp/hover_request_large_01.json b/cli/tests/lsp/hover_request_large_01.json deleted file mode 100644 index 78ddee5a33..0000000000 --- a/cli/tests/lsp/hover_request_large_01.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 421, - "character": 30 - } - } -} diff --git a/cli/tests/lsp/hover_request_large_02.json b/cli/tests/lsp/hover_request_large_02.json deleted file mode 100644 index 4d7eae1509..0000000000 --- a/cli/tests/lsp/hover_request_large_02.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 444, - "character": 6 - } - } -} diff --git a/cli/tests/lsp/hover_request_large_03.json b/cli/tests/lsp/hover_request_large_03.json deleted file mode 100644 index 5309450dd8..0000000000 --- a/cli/tests/lsp/hover_request_large_03.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 461, - "character": 34 - } - } -} diff --git a/cli/tests/lsp/hover_request_mbc.json b/cli/tests/lsp/hover_request_mbc.json deleted file mode 100644 index 6e7c55e085..0000000000 --- a/cli/tests/lsp/hover_request_mbc.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/hover", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 2, - "character": 15 - } - } -} diff --git a/cli/tests/lsp/incoming_calls_params.json b/cli/tests/lsp/incoming_calls_params.json new file mode 100644 index 0000000000..6b38d26ee5 --- /dev/null +++ b/cli/tests/lsp/incoming_calls_params.json @@ -0,0 +1,28 @@ +{ + "item": { + "name": "baz", + "kind": 6, + "detail": "Bar", + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 7, + "character": 3 + } + }, + "selectionRange": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 5 + } + } + } +} diff --git a/cli/tests/lsp/incoming_calls_request.json b/cli/tests/lsp/incoming_calls_request.json deleted file mode 100644 index 47af92c1b1..0000000000 --- a/cli/tests/lsp/incoming_calls_request.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "callHierarchy/incomingCalls", - "params": { - "item": { - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 7, - "character": 3 - } - }, - "selectionRange": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 5, - "character": 5 - } - } - } - } -} diff --git a/cli/tests/lsp/incoming_calls_response.json b/cli/tests/lsp/incoming_calls_response.json new file mode 100644 index 0000000000..231919a8c8 --- /dev/null +++ b/cli/tests/lsp/incoming_calls_response.json @@ -0,0 +1,42 @@ +[ + { + "from": { + "name": "main", + "kind": 12, + "detail": "", + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 10, + "character": 0 + }, + "end": { + "line": 13, + "character": 1 + } + }, + "selectionRange": { + "start": { + "line": 10, + "character": 9 + }, + "end": { + "line": 10, + "character": 13 + } + } + }, + "fromRanges": [ + { + "start": { + "line": 12, + "character": 6 + }, + "end": { + "line": 12, + "character": 9 + } + } + ] + } +] diff --git a/cli/tests/lsp/initialize_params.json b/cli/tests/lsp/initialize_params.json new file mode 100644 index 0000000000..b00e5720dd --- /dev/null +++ b/cli/tests/lsp/initialize_params.json @@ -0,0 +1,56 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "codeLens": { + "implementations": true, + "references": true + }, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": {} + } + }, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + } + } +} diff --git a/cli/tests/lsp/initialize_params_disabled.json b/cli/tests/lsp/initialize_params_disabled.json new file mode 100644 index 0000000000..ea12dfac58 --- /dev/null +++ b/cli/tests/lsp/initialize_params_disabled.json @@ -0,0 +1,56 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": false, + "codeLens": { + "implementations": true, + "references": true + }, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": {} + } + }, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + } + } +} diff --git a/cli/tests/lsp/initialize_params_registry.json b/cli/tests/lsp/initialize_params_registry.json new file mode 100644 index 0000000000..38777b3d48 --- /dev/null +++ b/cli/tests/lsp/initialize_params_registry.json @@ -0,0 +1,58 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "codeLens": { + "implementations": true, + "references": true + }, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": { + "http://localhost:4545/": true + } + } + }, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + } + } +} diff --git a/cli/tests/lsp/initialize_params_unstable.json b/cli/tests/lsp/initialize_params_unstable.json new file mode 100644 index 0000000000..fd8ccdd83a --- /dev/null +++ b/cli/tests/lsp/initialize_params_unstable.json @@ -0,0 +1,56 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "codeLens": { + "implementations": true, + "references": true + }, + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": {} + } + }, + "unstable": true + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + } + } +} diff --git a/cli/tests/lsp/initialize_request.json b/cli/tests/lsp/initialize_request.json deleted file mode 100644 index a6610ffe95..0000000000 --- a/cli/tests/lsp/initialize_request.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "processId": 0, - "clientInfo": { - "name": "test-harness", - "version": "1.0.0" - }, - "rootUri": null, - "initializationOptions": { - "enable": true, - "codeLens": { - "implementations": true, - "references": true - }, - "importMap": null, - "lint": true, - "suggest": { - "autoImports": true, - "completeFunctionCalls": false, - "names": true, - "paths": true, - "imports": { - "hosts": {} - } - }, - "unstable": false - }, - "capabilities": { - "textDocument": { - "codeAction": { - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "quickfix" - ] - } - }, - "isPreferredSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - } - }, - "foldingRange": { - "lineFoldingOnly": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - } - } - } - } -} diff --git a/cli/tests/lsp/initialize_request_disabled.json b/cli/tests/lsp/initialize_request_disabled.json deleted file mode 100644 index f763375f88..0000000000 --- a/cli/tests/lsp/initialize_request_disabled.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "processId": 0, - "clientInfo": { - "name": "test-harness", - "version": "1.0.0" - }, - "rootUri": null, - "initializationOptions": { - "enable": false, - "lint": true, - "importMap": null, - "unstable": false - }, - "capabilities": { - "textDocument": { - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - } - } - } - } -} diff --git a/cli/tests/lsp/initialize_request_registry.json b/cli/tests/lsp/initialize_request_registry.json deleted file mode 100644 index 94480934f9..0000000000 --- a/cli/tests/lsp/initialize_request_registry.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "processId": 0, - "clientInfo": { - "name": "test-harness", - "version": "1.0.0" - }, - "rootUri": null, - "initializationOptions": { - "enable": true, - "codeLens": { - "implementations": true, - "references": true - }, - "importMap": null, - "lint": true, - "suggest": { - "autoImports": true, - "completeFunctionCalls": false, - "names": true, - "paths": true, - "imports": { - "hosts": { - "http://localhost:4545/": true - } - } - }, - "unstable": false - }, - "capabilities": { - "textDocument": { - "codeAction": { - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "quickfix" - ] - } - }, - "isPreferredSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - } - }, - "foldingRange": { - "lineFoldingOnly": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - } - } - } - } -} diff --git a/cli/tests/lsp/initialize_request_unstable.json b/cli/tests/lsp/initialize_request_unstable.json deleted file mode 100644 index 8c086c838b..0000000000 --- a/cli/tests/lsp/initialize_request_unstable.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": { - "processId": 0, - "clientInfo": { - "name": "test-harness", - "version": "1.0.0" - }, - "rootUri": null, - "initializationOptions": { - "enable": true, - "lint": true, - "unstable": true, - "config": null, - "importMap": null - }, - "capabilities": { - "textDocument": { - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - } - } - } - } -} diff --git a/cli/tests/lsp/initialized_notification.json b/cli/tests/lsp/initialized_notification.json deleted file mode 100644 index 972f8abc8a..0000000000 --- a/cli/tests/lsp/initialized_notification.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "initialized", - "params": {} -} diff --git a/cli/tests/lsp/outgoing_calls_params.json b/cli/tests/lsp/outgoing_calls_params.json new file mode 100644 index 0000000000..6b38d26ee5 --- /dev/null +++ b/cli/tests/lsp/outgoing_calls_params.json @@ -0,0 +1,28 @@ +{ + "item": { + "name": "baz", + "kind": 6, + "detail": "Bar", + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 7, + "character": 3 + } + }, + "selectionRange": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 5 + } + } + } +} diff --git a/cli/tests/lsp/outgoing_calls_request.json b/cli/tests/lsp/outgoing_calls_request.json deleted file mode 100644 index a8d224ae8e..0000000000 --- a/cli/tests/lsp/outgoing_calls_request.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 5, - "method": "callHierarchy/outgoingCalls", - "params": { - "item": { - "name": "baz", - "kind": 6, - "detail": "Bar", - "uri": "file:///a/file.ts", - "range": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 7, - "character": 3 - } - }, - "selectionRange": { - "start": { - "line": 5, - "character": 2 - }, - "end": { - "line": 5, - "character": 5 - } - } - } - } -} diff --git a/cli/tests/lsp/outgoing_calls_response.json b/cli/tests/lsp/outgoing_calls_response.json new file mode 100644 index 0000000000..c7cf85cf81 --- /dev/null +++ b/cli/tests/lsp/outgoing_calls_response.json @@ -0,0 +1,42 @@ +[ + { + "to": { + "name": "foo", + "kind": 12, + "detail": "", + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "selectionRange": { + "start": { + "line": 0, + "character": 9 + }, + "end": { + "line": 0, + "character": 12 + } + } + }, + "fromRanges": [ + { + "start": { + "line": 6, + "character": 11 + }, + "end": { + "line": 6, + "character": 14 + } + } + ] + } +] diff --git a/cli/tests/lsp/performance_request.json b/cli/tests/lsp/performance_request.json deleted file mode 100644 index cb389cb9d3..0000000000 --- a/cli/tests/lsp/performance_request.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 99, - "method": "deno/performance", - "params": {} -} diff --git a/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json b/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json deleted file mode 100644 index a75bd3a53c..0000000000 --- a/cli/tests/lsp/prepare_call_hierarchy_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "function foo() {\n return false;\n}\n\nclass Bar {\n baz() {\n return foo();\n }\n}\n\nfunction main() {\n const bar = new Bar();\n bar.baz();\n}\n\nmain();" - } - } -} diff --git a/cli/tests/lsp/prepare_call_hierarchy_request.json b/cli/tests/lsp/prepare_call_hierarchy_request.json deleted file mode 100644 index 1f469ee8b4..0000000000 --- a/cli/tests/lsp/prepare_call_hierarchy_request.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/prepareCallHierarchy", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 5, - "character": 3 - } - } -} diff --git a/cli/tests/lsp/prepare_call_hierarchy_response.json b/cli/tests/lsp/prepare_call_hierarchy_response.json new file mode 100644 index 0000000000..93a7d4f1be --- /dev/null +++ b/cli/tests/lsp/prepare_call_hierarchy_response.json @@ -0,0 +1,28 @@ +[ + { + "name": "baz", + "kind": 6, + "detail": "Bar", + "uri": "file:///a/file.ts", + "range": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 7, + "character": 3 + } + }, + "selectionRange": { + "start": { + "line": 5, + "character": 2 + }, + "end": { + "line": 5, + "character": 5 + } + } + } +] diff --git a/cli/tests/lsp/references_request_asset.json b/cli/tests/lsp/references_request_asset.json deleted file mode 100644 index 6c2430e505..0000000000 --- a/cli/tests/lsp/references_request_asset.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/references", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 3 - }, - "context": { - "includeDeclaration": true - } - } -} diff --git a/cli/tests/lsp/rename_did_open_notification.json b/cli/tests/lsp/rename_did_open_notification.json deleted file mode 100644 index c6323b7421..0000000000 --- a/cli/tests/lsp/rename_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "let variable = 'a';\nconsole.log(variable);" - } - } -} diff --git a/cli/tests/lsp/rename_request.json b/cli/tests/lsp/rename_request.json deleted file mode 100644 index a317d92572..0000000000 --- a/cli/tests/lsp/rename_request.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/rename", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "line": 0, - "character": 4 - }, - "newName": "variable_modified" - } -} diff --git a/cli/tests/lsp/rename_response.json b/cli/tests/lsp/rename_response.json new file mode 100644 index 0000000000..5e0e28e4c0 --- /dev/null +++ b/cli/tests/lsp/rename_response.json @@ -0,0 +1,38 @@ +{ + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 4 + }, + "end": { + "line": 0, + "character": 12 + } + }, + "newText": "variable_modified" + }, + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 20 + } + }, + "newText": "variable_modified" + } + ] + } + ] +} diff --git a/cli/tests/lsp/selection_range_did_open_notification.json b/cli/tests/lsp/selection_range_did_open_notification.json deleted file mode 100644 index a6b3d9d39c..0000000000 --- a/cli/tests/lsp/selection_range_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "class Foo {\n bar(a, b) {\n if (a === b) {\n return true;\n }\n return false;\n }\n}" - } - } -} diff --git a/cli/tests/lsp/selection_range_request.json b/cli/tests/lsp/selection_range_request.json deleted file mode 100644 index 5125fa6a02..0000000000 --- a/cli/tests/lsp/selection_range_request.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/selectionRange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "positions": [ - { - "line": 2, - "character": 8 - } - ] - } -} diff --git a/cli/tests/lsp/selection_range_response.json b/cli/tests/lsp/selection_range_response.json new file mode 100644 index 0000000000..b5eef5ddce --- /dev/null +++ b/cli/tests/lsp/selection_range_response.json @@ -0,0 +1,86 @@ +[ + { + "range": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 9 + } + }, + "parent": { + "range": { + "start": { + "line": 2, + "character": 8 + }, + "end": { + "line": 2, + "character": 15 + } + }, + "parent": { + "range": { + "start": { + "line": 2, + "character": 4 + }, + "end": { + "line": 4, + "character": 5 + } + }, + "parent": { + "range": { + "start": { + "line": 1, + "character": 13 + }, + "end": { + "line": 6, + "character": 2 + } + }, + "parent": { + "range": { + "start": { + "line": 1, + "character": 2 + }, + "end": { + "line": 6, + "character": 3 + } + }, + "parent": { + "range": { + "start": { + "line": 0, + "character": 11 + }, + "end": { + "line": 7, + "character": 0 + } + }, + "parent": { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 7, + "character": 1 + } + } + } + } + } + } + } + } + } +] diff --git a/cli/tests/lsp/semantic_tokens_did_open_notification.json b/cli/tests/lsp/semantic_tokens_did_open_notification.json deleted file mode 100644 index 3bf1f6c448..0000000000 --- a/cli/tests/lsp/semantic_tokens_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "enum Values { value1, value2 }\n\nasync function baz(s: string): Promise {\n const r = s.slice(0);\n return r;\n}\n\ninterface IFoo {\n readonly x: number;\n foo(): boolean;\n}\n\nclass Bar implements IFoo {\n constructor(public readonly x: number) { }\n foo() { return true; }\n static staticBar = new Bar(0);\n private static getStaticBar() { return Bar.staticBar; }\n}\n" - } - } -} diff --git a/cli/tests/lsp/semantic_tokens_full_request.json b/cli/tests/lsp/semantic_tokens_full_request.json deleted file mode 100644 index 748cc43823..0000000000 --- a/cli/tests/lsp/semantic_tokens_full_request.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 2, - "method": "textDocument/semanticTokens/full", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - } - } -} diff --git a/cli/tests/lsp/semantic_tokens_range_request.json b/cli/tests/lsp/semantic_tokens_range_request.json deleted file mode 100644 index 3b09944a1d..0000000000 --- a/cli/tests/lsp/semantic_tokens_range_request.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "textDocument/semanticTokens/range", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "range": { - "start": { - "line": 0, - "character": 0 - }, - "end": { - "line": 6, - "character": 0 - } - } - } -} diff --git a/cli/tests/lsp/shutdown_request.json b/cli/tests/lsp/shutdown_request.json deleted file mode 100644 index fd4d784607..0000000000 --- a/cli/tests/lsp/shutdown_request.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 3, - "method": "shutdown", - "params": null -} diff --git a/cli/tests/lsp/signature_help_did_change_notification.json b/cli/tests/lsp/signature_help_did_change_notification.json deleted file mode 100644 index f88eaa9ffd..0000000000 --- a/cli/tests/lsp/signature_help_did_change_notification.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didChange", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "version": 2 - }, - "contentChanges": [ - { - "range": { - "start": { - "line": 9, - "character": 4 - }, - "end": { - "line": 9, - "character": 4 - } - }, - "text": "123, " - } - ] - } -} diff --git a/cli/tests/lsp/signature_help_did_open_notification.json b/cli/tests/lsp/signature_help_did_open_notification.json deleted file mode 100644 index 1ba1f75865..0000000000 --- a/cli/tests/lsp/signature_help_did_open_notification.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "jsonrpc": "2.0", - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": "file:///a/file.ts", - "languageId": "typescript", - "version": 1, - "text": "/**\n * Adds two numbers.\n * @param a This is a first number.\n * @param b This is a second number.\n */\nfunction add(a: number, b: number) {\n return a + b;\n}\n\nadd(" - } - } -} diff --git a/cli/tests/lsp/signature_help_request_01.json b/cli/tests/lsp/signature_help_request_01.json deleted file mode 100644 index c3e185e08b..0000000000 --- a/cli/tests/lsp/signature_help_request_01.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 1, - "method": "textDocument/signatureHelp", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "character": 4, - "line": 9 - }, - "context": { - "triggerKind": 2, - "triggerCharacter": "(", - "isRetrigger": false - } - } -} diff --git a/cli/tests/lsp/signature_help_request_02.json b/cli/tests/lsp/signature_help_request_02.json deleted file mode 100644 index c2a6e0abbb..0000000000 --- a/cli/tests/lsp/signature_help_request_02.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": 2, - "jsonrpc": "2.0", - "method": "textDocument/signatureHelp", - "params": { - "textDocument": { - "uri": "file:///a/file.ts" - }, - "position": { - "character": 8, - "line": 9 - } - } -} diff --git a/cli/tests/lsp/virtual_text_document_request.json b/cli/tests/lsp/virtual_text_document_request.json deleted file mode 100644 index 6ffab4a356..0000000000 --- a/cli/tests/lsp/virtual_text_document_request.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": 4, - "method": "deno/virtualTextDocument", - "params": { - "textDocument": { - "uri": "deno:/asset//lib.deno.shared_globals.d.ts" - } - } -} diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index ddb616ede3..b1750c5995 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -12,6 +12,7 @@ name = "test_server" path = "src/test_server.rs" [dependencies] +anyhow = "1.0.40" async-stream = "0.3.0" bytes = "1.0.1" futures = "0.3.13" @@ -20,6 +21,7 @@ lazy_static = "1.4.0" os_pipe = "0.9.2" regex = "1.4.3" serde = { version = "1.0.125", features = ["derive"] } +serde_json = "1.0.64" tempfile = "3.2.0" tokio = { version = "1.6.0", features = ["full"] } tokio-rustls = "0.22.0" diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index d2669e42ed..4cfc5cc9c0 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -46,6 +46,8 @@ use tokio_tungstenite::accept_async; #[cfg(unix)] pub use pty; +pub mod lsp; + const PORT: u16 = 4545; const TEST_AUTH_TOKEN: &str = "abcdef123456789"; const REDIRECT_PORT: u16 = 4546; diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs new file mode 100644 index 0000000000..52099ebe3f --- /dev/null +++ b/test_util/src/lsp.rs @@ -0,0 +1,270 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use super::new_deno_dir; + +use lazy_static::lazy_static; +use regex::Regex; +use serde::de; +use serde::Deserialize; +use serde::Serialize; +use serde_json::json; +use serde_json::Value; +use std::io; +use std::io::Write; +use std::path::Path; +use std::process::Child; +use std::process::ChildStdin; +use std::process::ChildStdout; +use std::process::Command; +use std::process::Stdio; +use std::time::Duration; +use std::time::Instant; + +lazy_static! { + static ref CONTENT_TYPE_REG: Regex = + Regex::new(r"(?i)^content-length:\s+(\d+)").unwrap(); +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct LspResponseError { + code: i32, + message: String, + data: Option, +} + +#[derive(Debug)] +pub enum LspMessage { + Notification(String, Option), + Request(u64, String, Option), + Response(u64, Option, Option), +} + +impl<'a> From<&'a [u8]> for LspMessage { + fn from(s: &'a [u8]) -> Self { + let value: Value = serde_json::from_slice(s).unwrap(); + let obj = value.as_object().unwrap(); + if obj.contains_key("id") && obj.contains_key("method") { + let id = obj.get("id").unwrap().as_u64().unwrap(); + let method = obj.get("method").unwrap().as_str().unwrap().to_string(); + Self::Request(id, method, obj.get("params").cloned()) + } else if obj.contains_key("id") { + let id = obj.get("id").unwrap().as_u64().unwrap(); + let maybe_error: Option = obj + .get("error") + .map(|v| serde_json::from_value(v.clone()).unwrap()); + Self::Response(id, obj.get("result").cloned(), maybe_error) + } else { + assert!(obj.contains_key("method")); + let method = obj.get("method").unwrap().as_str().unwrap().to_string(); + Self::Notification(method, obj.get("params").cloned()) + } + } +} + +fn read_message(reader: &mut R) -> Result, anyhow::Error> +where + R: io::Read + io::BufRead, +{ + let mut content_length = 0_usize; + loop { + let mut buf = String::new(); + reader.read_line(&mut buf)?; + if let Some(captures) = CONTENT_TYPE_REG.captures(&buf) { + let content_length_match = captures + .get(1) + .ok_or_else(|| anyhow::anyhow!("missing capture"))?; + content_length = content_length_match.as_str().parse::()?; + } + if &buf == "\r\n" { + break; + } + } + + let mut msg_buf = vec![0_u8; content_length]; + reader.read_exact(&mut msg_buf)?; + Ok(msg_buf) +} + +pub struct LspClient { + reader: io::BufReader, + child: Child, + request_id: u64, + start: Instant, + writer: io::BufWriter, +} + +impl Drop for LspClient { + fn drop(&mut self) { + match self.child.try_wait() { + Ok(None) => { + self.child.kill().unwrap(); + let _ = self.child.wait(); + } + Ok(Some(status)) => panic!("deno lsp exited unexpectedly {}", status), + Err(e) => panic!("pebble error: {}", e), + } + } +} + +impl LspClient { + pub fn new(deno_exe: &Path) -> Result { + let deno_dir = new_deno_dir(); + let mut child = Command::new(deno_exe) + .env("DENO_DIR", deno_dir.path()) + .arg("lsp") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn()?; + + let stdout = child.stdout.take().unwrap(); + let reader = io::BufReader::new(stdout); + + let stdin = child.stdin.take().unwrap(); + let writer = io::BufWriter::new(stdin); + + Ok(Self { + child, + reader, + request_id: 1, + start: Instant::now(), + writer, + }) + } + + pub fn duration(&self) -> Duration { + self.start.elapsed() + } + + fn read(&mut self) -> Result { + let msg_buf = read_message(&mut self.reader)?; + let msg = LspMessage::from(msg_buf.as_slice()); + Ok(msg) + } + + pub fn read_notification( + &mut self, + ) -> Result<(String, Option), anyhow::Error> + where + R: de::DeserializeOwned, + { + loop { + if let LspMessage::Notification(method, maybe_params) = self.read()? { + if let Some(p) = maybe_params { + let params = serde_json::from_value(p)?; + return Ok((method, Some(params))); + } else { + return Ok((method, None)); + } + } + } + } + + pub fn read_request( + &mut self, + ) -> Result<(u64, String, Option), anyhow::Error> + where + R: de::DeserializeOwned, + { + loop { + if let LspMessage::Request(id, method, maybe_params) = self.read()? { + if let Some(p) = maybe_params { + let params = serde_json::from_value(p)?; + return Ok((id, method, Some(params))); + } else { + return Ok((id, method, None)); + } + } + } + } + + fn write(&mut self, value: Value) -> Result<(), anyhow::Error> { + let value_str = value.to_string(); + let msg = format!( + "Content-Length: {}\r\n\r\n{}", + value_str.as_bytes().len(), + value_str + ); + self.writer.write_all(msg.as_bytes())?; + self.writer.flush()?; + Ok(()) + } + + pub fn write_request( + &mut self, + method: S, + params: V, + ) -> Result<(Option, Option), anyhow::Error> + where + S: AsRef, + V: Serialize, + R: de::DeserializeOwned, + { + let value = json!({ + "jsonrpc": "2.0", + "id": self.request_id, + "method": method.as_ref(), + "params": params, + }); + self.write(value)?; + + loop { + if let LspMessage::Response(id, result, error) = self.read()? { + assert_eq!(id, self.request_id); + self.request_id += 1; + if let Some(r) = result { + let result = serde_json::from_value(r)?; + return Ok((Some(result), error)); + } else { + return Ok((None, error)); + } + } + } + } + + pub fn write_response( + &mut self, + id: u64, + result: V, + ) -> Result<(), anyhow::Error> + where + V: Serialize, + { + let value = json!({ + "jsonrpc": "2.0", + "id": id, + "result": result + }); + self.write(value) + } + + pub fn write_notification( + &mut self, + method: S, + params: V, + ) -> Result<(), anyhow::Error> + where + S: AsRef, + V: Serialize, + { + let value = json!({ + "jsonrpc": "2.0", + "method": method.as_ref(), + "params": params, + }); + self.write(value)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_read_message() { + let msg = b"content-length: 11\r\n\r\nhello world"; + let mut reader = std::io::Cursor::new(msg); + assert_eq!(read_message(&mut reader).unwrap(), b"hello world"); + } +} From c6e32456d98efcdbdbef178031414ff8f20e8404 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 17 May 2021 23:11:14 +0200 Subject: [PATCH 02/53] ci: make rustc deny warnings on all platforms (#10672) --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7078748519..43f50e9dc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,10 +92,7 @@ jobs: curl -fsSL https://deno.land/x/install/install.sh | sh -s v1.7.2 echo "$HOME/.deno/bin" >> $GITHUB_PATH - # TODO(ry) Enable on all OSes https://github.com/denoland/deno/issues/10378 - name: Error on Warning - if: | - !startsWith(matrix.os, 'windows') run: echo "RUSTFLAGS=-D warnings" >> $GITHUB_ENV - name: Install Deno (Windows) From 7ad4a366cbf6435a6baf10de0d181c5e5dd0cca7 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 17 May 2021 23:11:40 +0200 Subject: [PATCH 03/53] chore: add/update crates listed in workspace (#10673) --- Cargo.lock | 9 +++++++++ Cargo.toml | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ce829a97b..b82c58ffa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bench_util" +version = "0.0.0" +dependencies = [ + "bencher", + "deno_core", + "tokio", +] + [[package]] name = "bencher" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index 47e278f824..31e2ee0915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,25 +2,26 @@ [workspace] members = [ + "bench_util", "cli", "core", "runtime", "serde_v8", "test_plugin", "test_util", + "extensions/console", "extensions/crypto", "extensions/fetch", + "extensions/file", "extensions/timers", "extensions/url", "extensions/web", "extensions/webgpu", "extensions/webidl", "extensions/websocket", + "extensions/webstorage" ] -exclude = [ - "std/hash/_wasm" -] - +exclude = ["test_util/std/hash/_wasm"] [profile.release] codegen-units = 1 From ef6d69c31471d33d5f1b37201f2706c6bf9608e3 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 18 May 2021 08:51:35 +1000 Subject: [PATCH 04/53] refactor(lsp): memoize script versions per tsc request (#10601) --- cli/tsc/99_main_compiler.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index b4626374d8..935ab59511 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -72,6 +72,9 @@ delete Object.prototype.__proto__; /** @type {Map} */ const sourceFileCache = new Map(); + /** @type {Map} */ + const scriptVersionCache = new Map(); + /** @param {ts.DiagnosticRelatedInformation} diagnostic */ function fromRelatedInformation({ start, @@ -368,7 +371,15 @@ delete Object.prototype.__proto__; if (sourceFile) { return sourceFile.version ?? "1"; } - return core.opSync("op_script_version", { specifier }); + // tsc neurotically requests the script version multiple times even though + // it can't possibly have changed, so we will memoize it on a per request + // basis. + if (scriptVersionCache.has(specifier)) { + return scriptVersionCache.get(specifier); + } + const scriptVersion = core.opSync("op_script_version", { specifier }); + scriptVersionCache.set(specifier, scriptVersion); + return scriptVersion; }, getScriptSnapshot(specifier) { debug(`host.getScriptSnapshot("${specifier}")`); @@ -386,8 +397,7 @@ delete Object.prototype.__proto__; }, }; } - /** @type {string | undefined} */ - const version = core.opSync("op_script_version", { specifier }); + const version = host.getScriptVersion(specifier); if (version != null) { return new ScriptSnapshot(specifier, version); } @@ -526,6 +536,8 @@ delete Object.prototype.__proto__; */ function serverRequest({ id, ...request }) { debug(`serverRequest()`, { id, ...request }); + // evict all memoized source file versions + scriptVersionCache.clear(); switch (request.method) { case "configure": { const { options, errors } = ts From a622486a1a8b9e403963bec9b6212ad8b9e6926d Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Tue, 18 May 2021 09:39:33 +0530 Subject: [PATCH 05/53] fix(lsp): ignore type definition not found diagnostic (#10610) Fixes #10575 --- cli/tsc/99_main_compiler.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 935ab59511..be0ed012a1 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -149,6 +149,9 @@ delete Object.prototype.__proto__; // TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is // not a module. 2306, + // TS2688: Cannot find type definition file for '...'. + // We ignore because type defintion files can end with '.ts'. + 2688, // TS2691: An import path cannot end with a '.ts' extension. Consider // importing 'bad-module' instead. 2691, From 6966c4e9166a0dc7e545fcec21d096f077cca996 Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Tue, 18 May 2021 12:05:46 +0530 Subject: [PATCH 06/53] feat(lsp): support formatting json and markdown files (#10180) Resolves #9447 Resolves #9415 --- cli/lsp/diagnostics.rs | 22 ++++-- cli/lsp/language_server.rs | 30 +++---- cli/tests/integration_tests_lsp.rs | 123 +++++++++++++++++++++++++++++ cli/tools/fmt.rs | 60 ++++++-------- 4 files changed, 180 insertions(+), 55 deletions(-) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index a5b8df879a..7ddb3ff7b4 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -218,6 +218,17 @@ impl<'a> From<&'a diagnostics::Position> for lsp::Position { } } +/// Check if diagnostics can be generated for the provided media type. +pub fn is_diagnosable(media_type: MediaType) -> bool { + matches!( + media_type, + MediaType::TypeScript + | MediaType::JavaScript + | MediaType::Tsx + | MediaType::Jsx + ) +} + fn get_diagnostic_message(diagnostic: &diagnostics::Diagnostic) -> String { if let Some(message) = diagnostic.message_text.clone() { message @@ -312,8 +323,8 @@ async fn generate_lint_diagnostics( .lock() .await .get_version(specifier, &DiagnosticSource::DenoLint); - if version != current_version { - let media_type = MediaType::from(specifier); + let media_type = MediaType::from(specifier); + if version != current_version && is_diagnosable(media_type) { if let Ok(Some(source_code)) = documents.content(specifier) { if let Ok(references) = analysis::get_lint_references( specifier, @@ -354,10 +365,11 @@ async fn generate_ts_diagnostics( let version = snapshot.documents.version(s); let current_version = collection.get_version(s, &DiagnosticSource::TypeScript); - if version == current_version { - None - } else { + let media_type = MediaType::from(s); + if version != current_version && is_diagnosable(media_type) { Some(s.clone()) + } else { + None } }) .collect() diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 9f5a7c84ad..fcf28dbf70 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -10,7 +10,6 @@ use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; -use dprint_plugin_typescript as dprint; use log::error; use log::info; use log::warn; @@ -30,13 +29,6 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use tokio::fs; -use crate::config_file::ConfigFile; -use crate::config_file::TsConfig; -use crate::deno_dir; -use crate::import_map::ImportMap; -use crate::logger; -use crate::media_type::MediaType; - use super::analysis; use super::analysis::ts_changes_to_edit; use super::analysis::CodeActionCollection; @@ -62,6 +54,15 @@ use super::tsc::AssetDocument; use super::tsc::Assets; use super::tsc::TsServer; use super::urls; +use crate::config_file::ConfigFile; +use crate::config_file::TsConfig; +use crate::deno_dir; +use crate::import_map::ImportMap; +use crate::logger; +use crate::lsp::diagnostics::is_diagnosable; +use crate::media_type::MediaType; +use crate::tools::fmt::format_file; +use crate::tools::fmt::get_typescript_config; pub const REGISTRIES_PATH: &str = "registries"; const SOURCES_PATH: &str = "deps"; @@ -785,6 +786,11 @@ impl Inner { if !self.config.specifier_enabled(&specifier) { return Ok(None); } + let media_type = MediaType::from(&specifier); + if !is_diagnosable(media_type) { + return Ok(None); + } + let mark = self.performance.mark("document_symbol", Some(¶ms)); let line_index = @@ -845,12 +851,8 @@ impl Inner { // TODO(lucacasonato): handle error properly let text_edits = tokio::task::spawn_blocking(move || { - let config = dprint::configuration::ConfigurationBuilder::new() - .deno() - .build(); - // TODO(@kitsonk) this could be handled better in `cli/tools/fmt.rs` in the - // future. - match dprint::format_text(&file_path, &file_text, &config) { + let config = get_typescript_config(); + match format_file(&file_path, &file_text, config) { Ok(new_text) => { Some(text::get_edits(&file_text, &new_text, line_index)) } diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index 6b0f6b7920..fa88ad17d4 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1659,3 +1659,126 @@ fn lsp_performance() { } shutdown(&mut client); } + +#[test] +fn lsp_format_json() { + let mut client = init("initialize_params.json"); + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": "file:///a/file.json", + "languageId": "json", + "version": 1, + "text": "{\"key\":\"value\"}" + } + }), + ) + .unwrap(); + + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "file:///a/file.json" + }, + "options": { + "tabSize": 2, + "insertSpaces": true + } + }), + ) + .unwrap(); + + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!([ + { + "range": { + "start": { + "line": 0, + "character": 1 + }, + "end": { + "line": 0, + "character": 1 + } + }, + "newText": " " + }, + { + "range": { + "start": { "line": 0, "character": 7 }, + "end": { "line": 0, "character": 7 } + }, + "newText": " " + }, + { + "range": { + "start": { "line": 0, "character": 14 }, + "end": { "line": 0, "character": 15 } + }, + "newText": " }\n" + } + ])) + ); + shutdown(&mut client); +} + +#[test] +fn lsp_format_markdown() { + let mut client = init("initialize_params.json"); + client + .write_notification( + "textDocument/didOpen", + json!({ + "textDocument": { + "uri": "file:///a/file.md", + "languageId": "markdown", + "version": 1, + "text": "# Hello World" + } + }), + ) + .unwrap(); + + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/formatting", + json!({ + "textDocument": { + "uri": "file:///a/file.md" + }, + "options": { + "tabSize": 2, + "insertSpaces": true + } + }), + ) + .unwrap(); + + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!([ + { + "range": { + "start": { "line": 0, "character": 1 }, + "end": { "line": 0, "character": 3 } + }, + "newText": "" + }, + { + "range": { + "start": { "line": 0, "character": 15 }, + "end": { "line": 0, "character": 15 } + }, + "newText": "\n" + } + ])) + ); + shutdown(&mut client); +} diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 9a16afecab..869403f071 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -152,6 +152,22 @@ fn format_json(file_text: &str) -> Result { dprint_plugin_json::format_text(&file_text, &json_config) } +/// Formats a single TS, TSX, JS, JSX, JSONC, JSON, or MD file. +pub fn format_file( + file_path: &Path, + file_text: &str, + config: dprint_plugin_typescript::configuration::Configuration, +) -> Result { + let ext = get_extension(file_path).unwrap_or_else(String::new); + if ext == "md" { + format_markdown(&file_text, config) + } else if matches!(ext.as_str(), "json" | "jsonc") { + format_json(&file_text) + } else { + dprint_plugin_typescript::format_text(&file_path, &file_text, &config) + } +} + async fn check_source_files( config: dprint_plugin_typescript::configuration::Configuration, paths: Vec, @@ -168,15 +184,8 @@ async fn check_source_files( move |file_path| { checked_files_count.fetch_add(1, Ordering::Relaxed); let file_text = read_file_contents(&file_path)?.text; - let ext = get_extension(&file_path).unwrap_or_else(String::new); - let r = if ext == "md" { - format_markdown(&file_text, config.clone()) - } else if matches!(ext.as_str(), "json" | "jsonc") { - format_json(&file_text) - } else { - dprint_plugin_typescript::format_text(&file_path, &file_text, &config) - }; - match r { + + match format_file(&file_path, &file_text, config) { Ok(formatted_text) => { if formatted_text != file_text { not_formatted_files_count.fetch_add(1, Ordering::Relaxed); @@ -229,19 +238,8 @@ async fn format_source_files( move |file_path| { checked_files_count.fetch_add(1, Ordering::Relaxed); let file_contents = read_file_contents(&file_path)?; - let ext = get_extension(&file_path).unwrap_or_else(String::new); - let r = if ext == "md" { - format_markdown(&file_contents.text, config.clone()) - } else if matches!(ext.as_str(), "json" | "jsonc") { - format_json(&file_contents.text) - } else { - dprint_plugin_typescript::format_text( - &file_path, - &file_contents.text, - &config, - ) - }; - match r { + + match format_file(&file_path, &file_contents.text, config) { Ok(formatted_text) => { if formatted_text != file_contents.text { write_file_contents( @@ -293,19 +291,9 @@ pub fn format_stdin(check: bool, ext: String) -> Result<(), AnyError> { return Err(generic_error("Failed to read from stdin")); } let config = get_typescript_config(); - let r = if ext.as_str() == "md" { - format_markdown(&source, config) - } else if matches!(ext.as_str(), "json" | "jsonc") { - format_json(&source) - } else { - // dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work - dprint_plugin_typescript::format_text( - &PathBuf::from("_stdin.ts"), - &source, - &config, - ) - }; - match r { + let file_path = PathBuf::from(format!("_stdin.{}", ext)); + + match format_file(&file_path, &source, config) { Ok(formatted_text) => { if check { if formatted_text != source { @@ -330,7 +318,7 @@ fn files_str(len: usize) -> &'static str { } } -fn get_typescript_config( +pub fn get_typescript_config( ) -> dprint_plugin_typescript::configuration::Configuration { dprint_plugin_typescript::configuration::ConfigurationBuilder::new() .deno() From d987ca5b0767d75b59b7c6c16840b164a4c786c4 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 18 May 2021 20:19:52 +1000 Subject: [PATCH 07/53] feat(lsp): provide X-Deno-Warning as a diagnostic (#10680) Closes #9932 --- cli/lsp/diagnostics.rs | 16 +++++-- cli/lsp/sources.rs | 45 ++++++++++++++++++-- cli/tests/integration_tests_lsp.rs | 68 ++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 8 deletions(-) diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 7ddb3ff7b4..47b26d92ea 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -444,13 +444,21 @@ async fn generate_deps_diagnostics( range, severity: Some(lsp::DiagnosticSeverity::Error), code, - code_description: None, source: Some("deno".to_string()), message, - related_information: None, - tags: None, - data: None, + ..Default::default() }); + } else if sources.contains_key(&specifier) { + if let Some(message) = sources.get_maybe_warning(&specifier) { + diagnostics.push(lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("deno-warn".to_string())), + source: Some("deno".to_string()), + message, + ..Default::default() + }) + } } }, } diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs index 5894e8d974..c5586a4401 100644 --- a/cli/lsp/sources.rs +++ b/cli/lsp/sources.rs @@ -14,11 +14,11 @@ use crate::module_graph::GraphBuilder; use crate::program_state::ProgramState; use crate::specifier_handler::FetchHandler; use crate::text_encoding; -use deno_runtime::permissions::Permissions; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::ModuleSpecifier; +use deno_runtime::permissions::Permissions; use std::collections::HashMap; use std::fs; use std::path::Path; @@ -105,6 +105,7 @@ struct Metadata { length_utf16: usize, line_index: LineIndex, maybe_types: Option, + maybe_warning: Option, media_type: MediaType, source: String, version: String, @@ -116,6 +117,7 @@ impl Metadata { source: &str, version: &str, media_type: &MediaType, + maybe_warning: Option, maybe_import_map: &Option, ) -> Self { let (dependencies, maybe_types) = if let Ok(parsed_module) = @@ -138,6 +140,7 @@ impl Metadata { length_utf16: source.encode_utf16().count(), line_index, maybe_types, + maybe_warning, media_type: media_type.to_owned(), source: source.to_string(), version: version.to_string(), @@ -180,6 +183,13 @@ impl Sources { self.0.lock().unwrap().get_maybe_types(specifier) } + pub fn get_maybe_warning( + &self, + specifier: &ModuleSpecifier, + ) -> Option { + self.0.lock().unwrap().get_maybe_warning(specifier) + } + pub fn get_media_type( &self, specifier: &ModuleSpecifier, @@ -271,6 +281,14 @@ impl Inner { metadata.maybe_types } + fn get_maybe_warning( + &mut self, + specifier: &ModuleSpecifier, + ) -> Option { + let metadata = self.get_metadata(&specifier)?; + metadata.maybe_warning + } + fn get_media_type( &mut self, specifier: &ModuleSpecifier, @@ -292,11 +310,11 @@ impl Inner { let path = self.get_path(specifier)?; let bytes = fs::read(path).ok()?; let scheme = specifier.scheme(); - let (source, media_type, maybe_types) = if scheme == "file" { + let (source, media_type, maybe_types, maybe_warning) = if scheme == "file" { let maybe_charset = Some(text_encoding::detect_charset(&bytes).to_string()); let source = get_source_from_bytes(bytes, maybe_charset).ok()?; - (source, MediaType::from(specifier), None) + (source, MediaType::from(specifier), None, None) } else { let cache_filename = self.http_cache.get_cache_filename(specifier)?; let headers = get_remote_headers(&cache_filename)?; @@ -307,13 +325,15 @@ impl Inner { let maybe_types = headers.get("x-typescript-types").map(|s| { analysis::resolve_import(s, &specifier, &self.maybe_import_map) }); - (source, media_type, maybe_types) + let maybe_warning = headers.get("x-deno-warning").cloned(); + (source, media_type, maybe_types, maybe_warning) }; let mut metadata = Metadata::new( specifier, &source, &version, &media_type, + maybe_warning, &self.maybe_import_map, ); if maybe_types.is_some() { @@ -547,6 +567,23 @@ mod tests { assert_eq!(actual, Some((specifier_type, MediaType::Dts))) } + #[test] + fn test_warning_header() { + let (sources, location) = setup(); + let cache = HttpCache::new(&location); + let specifier = resolve_url("https://deno.land/x/lib.js").unwrap(); + let mut headers = HashMap::new(); + headers.insert( + "x-deno-warning".to_string(), + "this is a warning".to_string(), + ); + cache + .set(&specifier, headers, b"export const a = 1;") + .unwrap(); + let actual = sources.get_maybe_warning(&specifier); + assert_eq!(actual, Some("this is a warning".to_string())); + } + #[test] fn test_resolve_dependency_evil_redirect() { let (sources, location) = setup(); diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index fa88ad17d4..d08da622f5 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1604,6 +1604,74 @@ fn lsp_completions_registry_empty() { shutdown(&mut client); } +#[test] +fn lsp_diagnostics_warn() { + let _g = http_server(); + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "import * as a from \"http://127.0.0.1:4545/cli/tests/x_deno_warning.js\";\n\nconsole.log(a)\n", + }, + }), + ); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "deno/cache", + json!({ + "referrer": { + "uri": "file:///a/file.ts", + }, + "uris": [ + { + "uri": "http://127.0.0.1:4545/cli/tests/x_deno_warning.js", + } + ], + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, maybe_params) = client + .read_notification::() + .unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + assert_eq!( + maybe_params, + Some(lsp::PublishDiagnosticsParams { + uri: Url::parse("file:///a/file.ts").unwrap(), + diagnostics: vec![lsp::Diagnostic { + range: lsp::Range { + start: lsp::Position { + line: 0, + character: 19 + }, + end: lsp::Position { + line: 0, + character: 70 + } + }, + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("deno-warn".to_string())), + source: Some("deno".to_string()), + message: "foobar".to_string(), + ..Default::default() + }], + version: Some(1), + }) + ); + shutdown(&mut client); +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct PerformanceAverage { From 22426fee1ee37149175a564c6102a6a3ca99fb42 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Wed, 19 May 2021 00:24:01 +0900 Subject: [PATCH 08/53] chore: update deno_lint binary used in CI to v0.5.0 (#10652) --- cli/dts/lib.deno.shared_globals.d.ts | 1 - cli/tests/018_async_catch.ts | 2 +- cli/tests/038_checkjs.js | 1 - cli/tests/038_checkjs.js.out | 4 ++-- cli/tests/048_media_types_jsx.ts | 1 - cli/tests/067_test_no_run_type_error.out | 4 ++-- cli/tests/076_info_json_deps_order.out | 6 +++--- cli/tests/076_info_json_deps_order.ts | 1 + cli/tests/091_use_define_for_class_fields.ts | 2 +- cli/tests/deno_doc.ts | 1 - cli/tests/error_004_missing_module.ts | 1 - cli/tests/error_004_missing_module.ts.out | 2 +- cli/tests/error_005_missing_dynamic_import.ts | 3 +-- cli/tests/error_005_missing_dynamic_import.ts.out | 6 +++--- cli/tests/error_008_checkjs.js | 1 - cli/tests/error_011_bad_module_specifier.ts | 1 - .../error_012_bad_dynamic_import_specifier.ts | 3 +-- .../error_012_bad_dynamic_import_specifier.ts.out | 6 +++--- cli/tests/file_exists.ts | 2 +- cli/tests/heapstats.js | 1 + cli/tests/no_validate_asm.js | 2 +- cli/tests/resolve_dns.ts | 2 +- cli/tests/test/unhandled_rejection.ts | 2 +- cli/tests/test_type_error/foo_test.ts | 2 +- cli/tests/unit/opcall_test.ts | 2 +- cli/tests/workers/bench_large_message.ts | 2 +- cli/tests/workers/permissions_blob_local.ts.out | 2 +- cli/tests/workers/permissions_blob_remote.ts.out | 2 +- cli/tests/workers/permissions_data_local.ts.out | 2 +- cli/tests/workers/permissions_data_remote.ts.out | 2 +- .../workers/permissions_dynamic_remote.ts.out | 2 +- cli/tests/workers/permissions_remote_remote.ts.out | 2 +- cli/tests/workers/test.ts | 4 ++-- cli/tests/workers/worker_large_message.js | 2 +- cli/tests/workers/worker_with_top_level_await.ts | 2 +- cli/tsc/compiler.d.ts | 2 -- extensions/fetch/21_formdata.js | 2 +- extensions/fetch/23_request.js | 6 +++--- extensions/fetch/23_response.js | 4 ++-- extensions/file/01_file.js | 1 + extensions/timers/01_timers.js | 1 - extensions/web/02_event.js | 2 +- extensions/web/08_text_encoding.js | 2 +- extensions/webidl/00_webidl.js | 1 - runtime/js/11_workers.js | 1 - runtime/js/40_http.js | 1 + runtime/js/40_testing.js | 2 +- runtime/js/99_main.js | 2 -- third_party | 2 +- tools/wpt/runner.ts | 4 ++-- tools/wpt/testharnessreport.js | 14 ++++++++------ 51 files changed, 59 insertions(+), 69 deletions(-) diff --git a/cli/dts/lib.deno.shared_globals.d.ts b/cli/dts/lib.deno.shared_globals.d.ts index a2609be85c..d3784705eb 100644 --- a/cli/dts/lib.deno.shared_globals.d.ts +++ b/cli/dts/lib.deno.shared_globals.d.ts @@ -571,7 +571,6 @@ declare class CustomEvent extends Event { interface ErrorConstructor { /** See https://v8.dev/docs/stack-trace-api#stack-trace-collection-for-custom-exceptions. */ - // eslint-disable-next-line @typescript-eslint/ban-types captureStackTrace(error: Object, constructor?: Function): void; // TODO(nayeemrmn): Support `Error.prepareStackTrace()`. We currently use this // internally in a way that makes it unavailable for users. diff --git a/cli/tests/018_async_catch.ts b/cli/tests/018_async_catch.ts index a16f11696b..85423ceda9 100644 --- a/cli/tests/018_async_catch.ts +++ b/cli/tests/018_async_catch.ts @@ -6,7 +6,7 @@ async function call(): Promise { console.log("before await fn()"); await fn(); console.log("after await fn()"); - } catch (error) { + } catch (_error) { console.log("catch"); } console.log("after try-catch"); diff --git a/cli/tests/038_checkjs.js b/cli/tests/038_checkjs.js index 628d3e3761..f0856d94c4 100644 --- a/cli/tests/038_checkjs.js +++ b/cli/tests/038_checkjs.js @@ -2,5 +2,4 @@ consol.log("hello world!"); // the following error should be ignored and not output to the console -// eslint-disable-next-line const foo = new Foo(); diff --git a/cli/tests/038_checkjs.js.out b/cli/tests/038_checkjs.js.out index 4522902725..bd3fc35481 100644 --- a/cli/tests/038_checkjs.js.out +++ b/cli/tests/038_checkjs.js.out @@ -12,11 +12,11 @@ consol.log("hello world!"); TS2552 [ERROR]: Cannot find name 'Foo'. Did you mean 'foo'? const foo = new Foo(); ~~~ - at [WILDCARD]tests/038_checkjs.js:6:17 + at [WILDCARD]tests/038_checkjs.js:5:17 'foo' is declared here. const foo = new Foo(); ~~~ - at [WILDCARD]tests/038_checkjs.js:6:7 + at [WILDCARD]tests/038_checkjs.js:5:7 Found 2 errors. diff --git a/cli/tests/048_media_types_jsx.ts b/cli/tests/048_media_types_jsx.ts index c2ece995a6..0449630006 100644 --- a/cli/tests/048_media_types_jsx.ts +++ b/cli/tests/048_media_types_jsx.ts @@ -11,7 +11,6 @@ import { loaded as loadedJsx3 } from "http://localhost:4545/cli/tests/subdir/mt_ import { loaded as loadedJsx4 } from "http://localhost:4545/cli/tests/subdir/mt_application_x_javascript_jsx.j4.jsx"; declare global { - // deno-lint-ignore no-namespace namespace JSX { interface IntrinsicElements { // deno-lint-ignore no-explicit-any diff --git a/cli/tests/067_test_no_run_type_error.out b/cli/tests/067_test_no_run_type_error.out index f67ee66474..a09d043483 100644 --- a/cli/tests/067_test_no_run_type_error.out +++ b/cli/tests/067_test_no_run_type_error.out @@ -1,4 +1,4 @@ [WILDCARD]error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. -const a: string = 1; - ^ +const _a: string = 1; + ~~ at [WILDCARD]foo_test.ts[WILDCARD] diff --git a/cli/tests/076_info_json_deps_order.out b/cli/tests/076_info_json_deps_order.out index 615732e67c..2d87a10540 100644 --- a/cli/tests/076_info_json_deps_order.out +++ b/cli/tests/076_info_json_deps_order.out @@ -10,10 +10,10 @@ "code": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts" } ], - "size": 46, + "size": 81, "mediaType": "TypeScript", "local": "[WILDCARD]076_info_json_deps_order.ts", - "checksum": "88b144f362d31ac42263648aadef727dd36d039d3b8ac0248fdaff25d4de415a" + "checksum": "5dd40fe33e5924cca513489ce568e86c9b9fe318a87975403c8923629018680d" }, { "specifier": "file://[WILDCARD]/cli/tests/recursive_imports/A.ts", @@ -81,5 +81,5 @@ "checksum": "01b595d69514bfd001ba2cf421feabeaef559513f10697bf1a22781f8a8ed7f0" } ], - "size": 440 + "size": 475 } \ No newline at end of file diff --git a/cli/tests/076_info_json_deps_order.ts b/cli/tests/076_info_json_deps_order.ts index f9d35fd5ac..b1ae75e68e 100644 --- a/cli/tests/076_info_json_deps_order.ts +++ b/cli/tests/076_info_json_deps_order.ts @@ -1 +1,2 @@ +// deno-lint-ignore no-unused-vars import { A } from "./recursive_imports/A.ts"; diff --git a/cli/tests/091_use_define_for_class_fields.ts b/cli/tests/091_use_define_for_class_fields.ts index 9be984abf4..46be3ac0b8 100644 --- a/cli/tests/091_use_define_for_class_fields.ts +++ b/cli/tests/091_use_define_for_class_fields.ts @@ -1,4 +1,4 @@ -class A { +class _A { b = this.a; constructor(public a: unknown) {} } diff --git a/cli/tests/deno_doc.ts b/cli/tests/deno_doc.ts index 92ba2f174d..fb3c509573 100644 --- a/cli/tests/deno_doc.ts +++ b/cli/tests/deno_doc.ts @@ -1,4 +1,3 @@ /** Some JSDoc */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function foo() { } diff --git a/cli/tests/error_004_missing_module.ts b/cli/tests/error_004_missing_module.ts index ab5350408c..82b2811816 100644 --- a/cli/tests/error_004_missing_module.ts +++ b/cli/tests/error_004_missing_module.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import * as badModule from "./bad-module.ts"; console.log(badModule); diff --git a/cli/tests/error_004_missing_module.ts.out b/cli/tests/error_004_missing_module.ts.out index 1b3dd534f2..474fdf8ed1 100644 --- a/cli/tests/error_004_missing_module.ts.out +++ b/cli/tests/error_004_missing_module.ts.out @@ -1,2 +1,2 @@ [WILDCARD]error: Cannot resolve module "file:///[WILDCARD]cli/tests/bad-module.ts" from "file:///[WILDCARD]cli/tests/error_004_missing_module.ts". - at file:///[WILDCARD]cli/tests/error_004_missing_module.ts:2:0 + at file:///[WILDCARD]cli/tests/error_004_missing_module.ts:1:0 diff --git a/cli/tests/error_005_missing_dynamic_import.ts b/cli/tests/error_005_missing_dynamic_import.ts index 4c09feb5fa..4ff10f275f 100644 --- a/cli/tests/error_005_missing_dynamic_import.ts +++ b/cli/tests/error_005_missing_dynamic_import.ts @@ -1,4 +1,3 @@ (async (): Promise => { - // eslint-disable-next-line - const badModule = await import("./bad-module.ts"); + const _badModule = await import("./bad-module.ts"); })(); diff --git a/cli/tests/error_005_missing_dynamic_import.ts.out b/cli/tests/error_005_missing_dynamic_import.ts.out index e21c7fffee..da680e1957 100644 --- a/cli/tests/error_005_missing_dynamic_import.ts.out +++ b/cli/tests/error_005_missing_dynamic_import.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in promise) TypeError: Cannot resolve module "[WILDCARD]/cli/tests/bad-module.ts". - const badModule = await import("./bad-module.ts"); - ^ - at async file://[WILDCARD]/error_005_missing_dynamic_import.ts:3:21 + const _badModule = await import("./bad-module.ts"); + ^ + at async file://[WILDCARD]/error_005_missing_dynamic_import.ts:2:22 diff --git a/cli/tests/error_008_checkjs.js b/cli/tests/error_008_checkjs.js index 628d3e3761..f0856d94c4 100644 --- a/cli/tests/error_008_checkjs.js +++ b/cli/tests/error_008_checkjs.js @@ -2,5 +2,4 @@ consol.log("hello world!"); // the following error should be ignored and not output to the console -// eslint-disable-next-line const foo = new Foo(); diff --git a/cli/tests/error_011_bad_module_specifier.ts b/cli/tests/error_011_bad_module_specifier.ts index a9ccc45231..1c57e37a51 100644 --- a/cli/tests/error_011_bad_module_specifier.ts +++ b/cli/tests/error_011_bad_module_specifier.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line import * as badModule from "bad-module.ts"; console.log(badModule); diff --git a/cli/tests/error_012_bad_dynamic_import_specifier.ts b/cli/tests/error_012_bad_dynamic_import_specifier.ts index 0420a80bf7..1b538b7871 100644 --- a/cli/tests/error_012_bad_dynamic_import_specifier.ts +++ b/cli/tests/error_012_bad_dynamic_import_specifier.ts @@ -1,4 +1,3 @@ (async (): Promise => { - // eslint-disable-next-line - const badModule = await import("bad-module.ts"); + const _badModule = await import("bad-module.ts"); })(); diff --git a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out index 15658a6ebf..45bce82616 100644 --- a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out +++ b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out @@ -1,5 +1,5 @@ Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts error: Uncaught (in promise) TypeError: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts" - const badModule = await import("bad-module.ts"); - ^ - at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:3:21 + const _badModule = await import("bad-module.ts"); + ^ + at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22 diff --git a/cli/tests/file_exists.ts b/cli/tests/file_exists.ts index 5fc5414b38..20de4d4f2f 100644 --- a/cli/tests/file_exists.ts +++ b/cli/tests/file_exists.ts @@ -1,6 +1,6 @@ try { await Deno.open(Deno.args[0]); Deno.exit(0); -} catch (e) { +} catch (_e) { Deno.exit(1); } diff --git a/cli/tests/heapstats.js b/cli/tests/heapstats.js index fba9e66d9c..675fac2d61 100644 --- a/cli/tests/heapstats.js +++ b/cli/tests/heapstats.js @@ -14,6 +14,7 @@ function allocTest(alloc, allocAssert, deallocAssert) { const t1 = sample(); // Alloc + // deno-lint-ignore no-unused-vars let x = alloc(); const t2 = sample(); allocAssert(delta(t1, t2)); diff --git a/cli/tests/no_validate_asm.js b/cli/tests/no_validate_asm.js index 38ea0a446b..ef999e0802 100644 --- a/cli/tests/no_validate_asm.js +++ b/cli/tests/no_validate_asm.js @@ -1,7 +1,7 @@ // V8 logs any asmjs validation errors to stdout, but it shows line numbers that // are non-existent in the source. -const asmJsModule = function () { +const _asmJsModule = function () { "use asm"; function func( diff --git a/cli/tests/resolve_dns.ts b/cli/tests/resolve_dns.ts index 7865d9680a..d765e0536a 100644 --- a/cli/tests/resolve_dns.ts +++ b/cli/tests/resolve_dns.ts @@ -37,6 +37,6 @@ console.log(JSON.stringify(txt)); try { await Deno.resolveDns("not-found-example.com", "A", nameServer); -} catch (e) { +} catch (_e) { console.log("Error thrown for not-found-example.com"); } diff --git a/cli/tests/test/unhandled_rejection.ts b/cli/tests/test/unhandled_rejection.ts index 396e1c09d0..32f3111ead 100644 --- a/cli/tests/test/unhandled_rejection.ts +++ b/cli/tests/test/unhandled_rejection.ts @@ -1,3 +1,3 @@ -new Promise((resolve, reject) => { +new Promise((_resolve, reject) => { reject(new Error("rejection")); }); diff --git a/cli/tests/test_type_error/foo_test.ts b/cli/tests/test_type_error/foo_test.ts index 68d1afccf8..4b9404558b 100644 --- a/cli/tests/test_type_error/foo_test.ts +++ b/cli/tests/test_type_error/foo_test.ts @@ -1 +1 @@ -const a: string = 1; +const _a: string = 1; diff --git a/cli/tests/unit/opcall_test.ts b/cli/tests/unit/opcall_test.ts index 6bade65457..bd549570e8 100644 --- a/cli/tests/unit/opcall_test.ts +++ b/cli/tests/unit/opcall_test.ts @@ -17,7 +17,7 @@ unitTest(async function sendAsyncStackTrace() { declare global { namespace Deno { // deno-lint-ignore no-explicit-any - var core: any; // eslint-disable-line no-var + var core: any; } } diff --git a/cli/tests/workers/bench_large_message.ts b/cli/tests/workers/bench_large_message.ts index 53076e7113..7f6cb26ce0 100644 --- a/cli/tests/workers/bench_large_message.ts +++ b/cli/tests/workers/bench_large_message.ts @@ -7,7 +7,7 @@ function oneWorker(i: number) { new URL("worker_large_message.js", import.meta.url).href, { type: "module" }, ); - worker.onmessage = (e): void => { + worker.onmessage = (_e): void => { if (countDown > 0) { countDown--; return; diff --git a/cli/tests/workers/permissions_blob_local.ts.out b/cli/tests/workers/permissions_blob_local.ts.out index fab2388d8d..0f6ae1435f 100644 --- a/cli/tests/workers/permissions_blob_local.ts.out +++ b/cli/tests/workers/permissions_blob_local.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag at blob:null/[WILDCARD]:1:0 error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/permissions_blob_remote.ts.out b/cli/tests/workers/permissions_blob_remote.ts.out index c89f7b41cc..751218ba47 100644 --- a/cli/tests/workers/permissions_blob_remote.ts.out +++ b/cli/tests/workers/permissions_blob_remote.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag at blob:null/[WILDCARD]:1:0 error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/permissions_data_local.ts.out b/cli/tests/workers/permissions_data_local.ts.out index a1e1de8ab4..00a2516591 100644 --- a/cli/tests/workers/permissions_data_local.ts.out +++ b/cli/tests/workers/permissions_data_local.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag at data:application/javascript;base64,[WILDCARD]:1:0 error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/permissions_data_remote.ts.out b/cli/tests/workers/permissions_data_remote.ts.out index 2c40806724..f2b5fa48bc 100644 --- a/cli/tests/workers/permissions_data_remote.ts.out +++ b/cli/tests/workers/permissions_data_remote.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0 error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/permissions_dynamic_remote.ts.out b/cli/tests/workers/permissions_dynamic_remote.ts.out index 8600fa1916..63a8af51f1 100644 --- a/cli/tests/workers/permissions_dynamic_remote.ts.out +++ b/cli/tests/workers/permissions_dynamic_remote.ts.out @@ -3,4 +3,4 @@ await import("https://example.com/some/file.ts"); ^ at async http://localhost:4545/cli/tests/workers/dynamic_remote.ts:2:1 [WILDCARD]error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/permissions_remote_remote.ts.out b/cli/tests/workers/permissions_remote_remote.ts.out index 714150a3a5..964f8b8e91 100644 --- a/cli/tests/workers/permissions_remote_remote.ts.out +++ b/cli/tests/workers/permissions_remote_remote.ts.out @@ -1,4 +1,4 @@ error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag at http://localhost:4545/cli/tests/workers/static_remote.ts:2:0 error: Uncaught (in promise) Error: Unhandled error event reached main worker. - at Worker.#poll (deno:runtime/js/11_workers.js:246:23) + at Worker.#poll (deno:runtime/js/11_workers.js:245:23) diff --git a/cli/tests/workers/test.ts b/cli/tests/workers/test.ts index 954737b3a9..bf1e46a343 100644 --- a/cli/tests/workers/test.ts +++ b/cli/tests/workers/test.ts @@ -198,7 +198,7 @@ Deno.test({ { type: "module" }, ); - racyWorker.onmessage = (e): void => { + racyWorker.onmessage = (_e): void => { setTimeout(() => { promise.resolve(); }, 100); @@ -353,7 +353,7 @@ Deno.test({ ); const arr: number[] = []; w.addEventListener("message", () => arr.push(1)); - w.onmessage = (e): void => { + w.onmessage = (_e): void => { arr.push(2); }; w.addEventListener("message", () => arr.push(3)); diff --git a/cli/tests/workers/worker_large_message.js b/cli/tests/workers/worker_large_message.js index f7b7da8a02..a1ddae4f98 100644 --- a/cli/tests/workers/worker_large_message.js +++ b/cli/tests/workers/worker_large_message.js @@ -3,7 +3,7 @@ const dataSmall = ""; const dataLarge = "x".repeat(10 * 1024); -onmessage = function (e) { +onmessage = function (_e) { for (let i = 0; i <= 10; i++) { if (i % 2 == 0) { postMessage(dataLarge); diff --git a/cli/tests/workers/worker_with_top_level_await.ts b/cli/tests/workers/worker_with_top_level_await.ts index 6c55289008..bc77c7932e 100644 --- a/cli/tests/workers/worker_with_top_level_await.ts +++ b/cli/tests/workers/worker_with_top_level_await.ts @@ -6,7 +6,7 @@ function delay(ms: number): Promise { }); } -onmessage = (e: MessageEvent) => { +onmessage = (_e: MessageEvent) => { postMessage("triggered worker handler"); close(); }; diff --git a/cli/tsc/compiler.d.ts b/cli/tsc/compiler.d.ts index e5ce12cd32..949d98ee0b 100644 --- a/cli/tsc/compiler.d.ts +++ b/cli/tsc/compiler.d.ts @@ -5,7 +5,6 @@ import * as _ts from "../dts/typescript"; declare global { - // deno-lint-ignore no-namespace namespace ts { var libs: string[]; var libMap: Map; @@ -22,7 +21,6 @@ declare global { var performance: Performance; } - // deno-lint-ignore no-namespace namespace ts { export = _ts; } diff --git a/extensions/fetch/21_formdata.js b/extensions/fetch/21_formdata.js index c50cf4cf77..379d5eca12 100644 --- a/extensions/fetch/21_formdata.js +++ b/extensions/fetch/21_formdata.js @@ -11,7 +11,7 @@ /// "use strict"; -((window) => { +((_window) => { const webidl = globalThis.__bootstrap.webidl; const { Blob, File, _byteSequence } = globalThis.__bootstrap.file; diff --git a/extensions/fetch/23_request.js b/extensions/fetch/23_request.js index 603a37a5fe..480b83182b 100644 --- a/extensions/fetch/23_request.js +++ b/extensions/fetch/23_request.js @@ -16,7 +16,7 @@ const { HTTP_TOKEN_CODE_POINT_RE, byteUpperCase } = window.__bootstrap.infra; const { URL } = window.__bootstrap.url; const { guardFromHeaders } = window.__bootstrap.headers; - const { InnerBody, mixinBody, extractBody } = window.__bootstrap.fetchBody; + const { mixinBody, extractBody } = window.__bootstrap.fetchBody; const { getLocationHref } = window.__bootstrap.location; const mimesniff = window.__bootstrap.mimesniff; const { @@ -38,7 +38,7 @@ * @property {() => string} url * @property {() => string} currentUrl * @property {[string, string][]} headerList - * @property {null | InnerBody} body + * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body * @property {"follow" | "error" | "manual"} redirectMode * @property {number} redirectCount * @property {string[]} urlList @@ -61,7 +61,7 @@ * @param {string} method * @param {string} url * @param {[string, string][]} headerList - * @param {InnerBody} body + * @param {typeof __window.bootstrap.fetchBody.InnerBody} body * @returns */ function newInnerRequest(method, url, headerList = [], body = null) { diff --git a/extensions/fetch/23_response.js b/extensions/fetch/23_response.js index 6bd7a64875..6d9874c6a5 100644 --- a/extensions/fetch/23_response.js +++ b/extensions/fetch/23_response.js @@ -15,7 +15,7 @@ ((window) => { const webidl = window.__bootstrap.webidl; const { HTTP_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra; - const { InnerBody, extractBody, mixinBody } = window.__bootstrap.fetchBody; + const { extractBody, mixinBody } = window.__bootstrap.fetchBody; const { getLocationHref } = window.__bootstrap.location; const mimesniff = window.__bootstrap.mimesniff; const { URL } = window.__bootstrap.url; @@ -47,7 +47,7 @@ * @property {number} status * @property {string} statusMessage * @property {[string, string][]} headerList - * @property {null | InnerBody} body + * @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body * @property {string} [error] */ diff --git a/extensions/file/01_file.js b/extensions/file/01_file.js index 05c4105723..cbbf98e7d8 100644 --- a/extensions/file/01_file.js +++ b/extensions/file/01_file.js @@ -216,6 +216,7 @@ }); } + // deno-lint-ignore no-this-alias const O = this; /** @type {number} */ let relativeStart; diff --git a/extensions/timers/01_timers.js b/extensions/timers/01_timers.js index 756e388751..66faf85fd7 100644 --- a/extensions/timers/01_timers.js +++ b/extensions/timers/01_timers.js @@ -344,7 +344,6 @@ opStartGlobalTimer(timeout); await opWaitGlobalTimer(); pendingEvents--; - // eslint-disable-next-line @typescript-eslint/no-use-before-define prepareReadyTimers(); } diff --git a/extensions/web/02_event.js b/extensions/web/02_event.js index 35112132e0..b6b5609bcc 100644 --- a/extensions/web/02_event.js +++ b/extensions/web/02_event.js @@ -706,7 +706,7 @@ for (let i = 0; i < handlers.length; i++) { const listener = handlers[i]; - let capture, once, passive, signal; + let capture, once, passive; if (typeof listener.options === "boolean") { capture = listener.options; once = false; diff --git a/extensions/web/08_text_encoding.js b/extensions/web/08_text_encoding.js index c293633c31..2804ff6a02 100644 --- a/extensions/web/08_text_encoding.js +++ b/extensions/web/08_text_encoding.js @@ -4474,7 +4474,7 @@ return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen; } - function _byteLength(b64, validLen, placeHoldersLen) { + function _byteLength(_b64, validLen, placeHoldersLen) { return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen; } diff --git a/extensions/webidl/00_webidl.js b/extensions/webidl/00_webidl.js index 6bf98be064..6fffa93190 100644 --- a/extensions/webidl/00_webidl.js +++ b/extensions/webidl/00_webidl.js @@ -385,7 +385,6 @@ function isArrayBufferDetached(V) { try { - // eslint-disable-next-line no-new new Uint8Array(V); return false; } catch { diff --git a/runtime/js/11_workers.js b/runtime/js/11_workers.js index dca83c8181..641833778b 100644 --- a/runtime/js/11_workers.js +++ b/runtime/js/11_workers.js @@ -7,7 +7,6 @@ const { getLocationHref } = window.__bootstrap.location; const { log, pathFromURL } = window.__bootstrap.util; const { defineEventHandler } = window.__bootstrap.webUtil; - const build = window.__bootstrap.build.build; function createWorker( specifier, diff --git a/runtime/js/40_http.js b/runtime/js/40_http.js index afc5635ac9..eb3c58a632 100644 --- a/runtime/js/40_http.js +++ b/runtime/js/40_http.js @@ -77,6 +77,7 @@ } [Symbol.asyncIterator]() { + // deno-lint-ignore no-this-alias const httpConn = this; return { async next() { diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js index f835a0cf79..8abb8a6c87 100644 --- a/runtime/js/40_testing.js +++ b/runtime/js/40_testing.js @@ -4,7 +4,7 @@ ((window) => { const core = window.Deno.core; const { parsePermissions } = window.__bootstrap.worker; - const { setExitHandler, exit } = window.__bootstrap.os; + const { setExitHandler } = window.__bootstrap.os; const { Console, inspectArgs } = window.__bootstrap.console; const { metrics } = window.__bootstrap.metrics; const { assert } = window.__bootstrap.util; diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 082c83593e..796d5178f3 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -59,8 +59,6 @@ delete Object.prototype.__proto__; } } - const encoder = new TextEncoder(); - function workerClose() { if (isClosing) { return; diff --git a/third_party b/third_party index 3bd4e36523..6c449eaecb 160000 --- a/third_party +++ b/third_party @@ -1 +1 @@ -Subproject commit 3bd4e3652304eb97177e1fc217943529322f4680 +Subproject commit 6c449eaecb0783b06003b5eecd2893bd2617d66e diff --git a/tools/wpt/runner.ts b/tools/wpt/runner.ts index b106283d2a..eae53094df 100644 --- a/tools/wpt/runner.ts +++ b/tools/wpt/runner.ts @@ -21,7 +21,7 @@ export async function runWithTestUtil( if (req.status == 200) { break; } - } catch (err) { + } catch (_err) { // do nothing if this fails } const passedTime = performance.now() - start; @@ -61,7 +61,7 @@ export interface TestCaseResult { export async function runSingleTest( url: URL, - options: ManifestTestOptions, + _options: ManifestTestOptions, reporter: (result: TestCaseResult) => void, ): Promise { const bundle = await generateBundle(url); diff --git a/tools/wpt/testharnessreport.js b/tools/wpt/testharnessreport.js index 8cff0752c6..d3e7833767 100644 --- a/tools/wpt/testharnessreport.js +++ b/tools/wpt/testharnessreport.js @@ -1,13 +1,15 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + window.add_result_callback(({ message, name, stack, status }) => { - Deno.writeAllSync( - Deno.stderr, - new TextEncoder().encode( - `${JSON.stringify({ name, status, message, stack })}\n`, - ), + const data = new TextEncoder().encode( + `${JSON.stringify({ name, status, message, stack })}\n`, ); + let bytesWritten = 0; + while (bytesWritten < data.byteLength) { + bytesWritten += Deno.stderr.writeSync(data.subarray(bytesWritten)); + } }); -window.add_completion_callback((tests, harnessStatus) => { +window.add_completion_callback((_tests, _harnessStatus) => { Deno.exit(0); }); From 913d4f28b319e509483b08752961d925c8081ab7 Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Wed, 19 May 2021 02:18:11 +0530 Subject: [PATCH 09/53] fix(lsp): make failed to load config error descriptive (#10685) --- cli/config_file.rs | 6 ++++++ cli/lsp/language_server.rs | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index 41d9bbbeca..a7fe7f2dec 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -339,6 +339,12 @@ mod tests { assert!(config_file.json.compiler_options.is_some()); } + #[test] + fn include_config_path_on_error() { + let error = ConfigFile::read("404.json").err().unwrap(); + assert!(error.to_string().contains("404.json")); + } + #[test] fn test_json_merge() { let mut value_a = json!({ diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index fcf28dbf70..f9bb52cbeb 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -2,7 +2,6 @@ use deno_core::error::anyhow; use deno_core::error::AnyError; -use deno_core::error::Context; use deno_core::resolve_url; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; @@ -432,8 +431,7 @@ impl Inner { )) }?; - let config_file = ConfigFile::read(config_url.path()) - .context("Failed to load configuration file")?; + let config_file = ConfigFile::read(config_url.path())?; let (value, maybe_ignored_options) = config_file.as_compiler_options()?; tsconfig.merge(&value); self.maybe_config_uri = Some(config_url); From 0c40446bed093909c1f744233909b5044c1c60d8 Mon Sep 17 00:00:00 2001 From: Raika Toriyama Date: Wed, 19 May 2021 12:56:44 +0900 Subject: [PATCH 10/53] docs(typescript): fix typo in faqs (#10682) --- docs/typescript/faqs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/typescript/faqs.md b/docs/typescript/faqs.md index 3b5b7e0b1a..8ca9a7bff7 100644 --- a/docs/typescript/faqs.md +++ b/docs/typescript/faqs.md @@ -82,7 +82,7 @@ are using the TypeScript compiler to emit the code, it will follow the same This means that certain language features are not supportable. Those features are: -- Re-exporting of types is ambigious and requires to know if the source module +- Re-exporting of types is ambiguous and requires to know if the source module is exporting runtime code or just type information. Therefore, it is recommended that you use `import type` and `export type` for type only imports and exports. This will help ensure that when the code is emitted, that all the From c02af343426dd8f2e147775a8edc8af7541fbd80 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 19 May 2021 14:06:14 +1000 Subject: [PATCH 11/53] docs: update getting started for clarity (#10694) Co-authored-by: RobyCigar <69680330+RobyCigar@users.noreply.github.com> --- docs/getting_started/command_line_interface.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/getting_started/command_line_interface.md b/docs/getting_started/command_line_interface.md index 617278579c..191c1e7568 100644 --- a/docs/getting_started/command_line_interface.md +++ b/docs/getting_started/command_line_interface.md @@ -73,11 +73,9 @@ Some see it as unconventional that: However: -1. This is the most logical way of distinguishing between runtime flags and - script arguments. -2. This is the most ergonomic way of distinguishing between runtime flags and - script arguments. -3. This is, in fact, the same behaviour as that of any other popular runtime. +1. This is the most logical and ergonomic way of distinguishing between runtime + flags and script arguments. +2. This is, in fact, the same behaviour as that of any other popular runtime. - Try `node -c index.js` and `node index.js -c`. The first will only do a syntax check on `index.js` as per Node's `-c` flag. The second will _execute_ `index.js` with `-c` passed to `require("process").argv`. From 4fedc702c60c4d0358354312e19eadef4d97e922 Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Wed, 19 May 2021 09:48:01 +0530 Subject: [PATCH 12/53] fix(runtime): support source maps with Deno.emit() and bundle (#10510) Closes: #10413 --- cli/ast.rs | 5 +++ cli/config_file.rs | 1 + cli/module_graph.rs | 54 +++++++++++++++++++++++++------- cli/tests/compiler_api_test.ts | 57 +++++++++++++++++++++++++++++++--- 4 files changed, 102 insertions(+), 15 deletions(-) diff --git a/cli/ast.rs b/cli/ast.rs index 76a5f13629..636dc1881a 100644 --- a/cli/ast.rs +++ b/cli/ast.rs @@ -205,6 +205,9 @@ pub struct EmitOptions { /// Should the source map be inlined in the emitted code file, or provided /// as a separate file. Defaults to `true`. pub inline_source_map: bool, + // Should a corresponding .map file be created for the output. This should be + // false if inline_source_map is true. Defaults to `false`. + pub source_map: bool, /// When transforming JSX, what value should be used for the JSX factory. /// Defaults to `React.createElement`. pub jsx_factory: String, @@ -222,6 +225,7 @@ impl Default for EmitOptions { emit_metadata: false, imports_not_used_as_values: ImportsNotUsedAsValues::Remove, inline_source_map: true, + source_map: false, jsx_factory: "React.createElement".into(), jsx_fragment_factory: "React.Fragment".into(), transform_jsx: true, @@ -244,6 +248,7 @@ impl From for EmitOptions { emit_metadata: options.emit_decorator_metadata, imports_not_used_as_values, inline_source_map: options.inline_source_map, + source_map: options.source_map, jsx_factory: options.jsx_factory, jsx_fragment_factory: options.jsx_fragment_factory, transform_jsx: options.jsx == "react", diff --git a/cli/config_file.rs b/cli/config_file.rs index a7fe7f2dec..1875f9906d 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -24,6 +24,7 @@ pub struct EmitConfigOptions { pub emit_decorator_metadata: bool, pub imports_not_used_as_values: String, pub inline_source_map: bool, + pub source_map: bool, pub jsx: String, pub jsx_factory: String, pub jsx_fragment_factory: String, diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 1ca738977f..8613be1e7b 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -769,7 +769,8 @@ impl Graph { "checkJs": false, "emitDecoratorMetadata": false, "importsNotUsedAsValues": "remove", - "inlineSourceMap": true, + "inlineSourceMap": false, + "sourceMap": false, "jsx": "react", "jsxFactory": "React.createElement", "jsxFragmentFactory": "React.Fragment", @@ -777,7 +778,7 @@ impl Graph { let maybe_ignored_options = ts_config .merge_tsconfig_from_config_file(options.maybe_config_file.as_ref())?; - let s = self.emit_bundle( + let (src, _) = self.emit_bundle( &root_specifier, &ts_config.into(), &BundleType::Module, @@ -787,7 +788,7 @@ impl Graph { ("Total time".to_string(), start.elapsed().as_millis() as u32), ]); - Ok((s, stats, maybe_ignored_options)) + Ok((src, stats, maybe_ignored_options)) } /// Type check the module graph, corresponding to the options provided. @@ -945,6 +946,7 @@ impl Graph { "experimentalDecorators": true, "importsNotUsedAsValues": "remove", "inlineSourceMap": false, + "sourceMap": false, "isolatedModules": true, "jsx": "react", "jsxFactory": "React.createElement", @@ -958,6 +960,8 @@ impl Graph { let opts = match options.bundle_type { BundleType::Module | BundleType::Classic => json!({ "noEmit": true, + "removeComments": true, + "sourceMap": true, }), BundleType::None => json!({ "outDir": "deno://", @@ -1008,12 +1012,15 @@ impl Graph { "Only a single root module supported." ); let specifier = &graph.roots[0]; - let s = graph.emit_bundle( + let (src, maybe_src_map) = graph.emit_bundle( specifier, &config.into(), &options.bundle_type, )?; - emitted_files.insert("deno:///bundle.js".to_string(), s); + emitted_files.insert("deno:///bundle.js".to_string(), src); + if let Some(src_map) = maybe_src_map { + emitted_files.insert("deno:///bundle.js.map".to_string(), src_map); + } } BundleType::None => { for emitted_file in &response.emitted_files { @@ -1060,13 +1067,16 @@ impl Graph { "Only a single root module supported." ); let specifier = &self.roots[0]; - let s = self.emit_bundle( + let (src, maybe_src_map) = self.emit_bundle( specifier, &config.into(), &options.bundle_type, )?; emit_count += 1; - emitted_files.insert("deno:///bundle.js".to_string(), s); + emitted_files.insert("deno:///bundle.js".to_string(), src); + if let Some(src_map) = maybe_src_map { + emitted_files.insert("deno:///bundle.js.map".to_string(), src_map); + } } BundleType::None => { let emit_options: ast::EmitOptions = config.into(); @@ -1118,7 +1128,7 @@ impl Graph { specifier: &ModuleSpecifier, emit_options: &ast::EmitOptions, bundle_type: &BundleType, - ) -> Result { + ) -> Result<(String, Option), AnyError> { let cm = Rc::new(swc_common::SourceMap::new( swc_common::FilePathMapping::empty(), )); @@ -1150,13 +1160,17 @@ impl Graph { .bundle(entries) .context("Unable to output bundle during Graph::bundle().")?; let mut buf = Vec::new(); + let mut src_map_buf = Vec::new(); { let mut emitter = swc_ecmascript::codegen::Emitter { cfg: swc_ecmascript::codegen::Config { minify: false }, cm: cm.clone(), comments: None, wr: Box::new(swc_ecmascript::codegen::text_writer::JsWriter::new( - cm, "\n", &mut buf, None, + cm.clone(), + "\n", + &mut buf, + Some(&mut src_map_buf), )), }; @@ -1164,8 +1178,24 @@ impl Graph { .emit_module(&output[0].module) .context("Unable to emit bundle during Graph::bundle().")?; } + let mut src = String::from_utf8(buf) + .context("Emitted bundle is an invalid utf-8 string.")?; + let mut map: Option = None; + { + let mut buf = Vec::new(); + cm.build_source_map_from(&mut src_map_buf, None) + .to_writer(&mut buf)?; - String::from_utf8(buf).context("Emitted bundle is an invalid utf-8 string.") + if emit_options.inline_source_map { + src.push_str("//# sourceMappingURL=data:application/json;base64,"); + let encoded_map = base64::encode(buf); + src.push_str(&encoded_map); + } else if emit_options.source_map { + map = Some(String::from_utf8(buf)?); + } + } + + Ok((src, map)) } /// Update the handler with any modules that are marked as _dirty_ and update @@ -1606,6 +1636,7 @@ impl Graph { "emitDecoratorMetadata": false, "importsNotUsedAsValues": "remove", "inlineSourceMap": true, + "sourceMap": false, "jsx": "react", "jsxFactory": "React.createElement", "jsxFragmentFactory": "React.Fragment", @@ -2413,9 +2444,10 @@ pub mod tests { .expect("should have emitted"); assert!(result_info.diagnostics.is_empty()); assert!(result_info.maybe_ignored_options.is_none()); - assert_eq!(emitted_files.len(), 1); + assert_eq!(emitted_files.len(), 2); let actual = emitted_files.get("deno:///bundle.js"); assert!(actual.is_some()); + assert!(emitted_files.contains_key("deno:///bundle.js.map")); let actual = actual.unwrap(); assert!(actual.contains("const b = \"b\";")); assert!(actual.contains("console.log(mod);")); diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts index c6e7de6515..00116e7e1d 100644 --- a/cli/tests/compiler_api_test.ts +++ b/cli/tests/compiler_api_test.ts @@ -2,6 +2,7 @@ import { assert, assertEquals, + assertStringIncludes, assertThrowsAsync, } from "../../test_util/std/testing/asserts.ts"; @@ -188,7 +189,10 @@ Deno.test({ assertEquals(diagnostics.length, 0); assert(!ignoredOptions); assertEquals(stats.length, 12); - assertEquals(Object.keys(files), ["deno:///bundle.js"]); + assertEquals( + Object.keys(files).sort(), + ["deno:///bundle.js", "deno:///bundle.js.map"].sort(), + ); assert(files["deno:///bundle.js"].includes(`const bar1 = "bar"`)); }, }); @@ -205,7 +209,10 @@ Deno.test({ assertEquals(diagnostics.length, 0); assert(!ignoredOptions); assertEquals(stats.length, 12); - assertEquals(Object.keys(files), ["deno:///bundle.js"]); + assertEquals( + Object.keys(files).sort(), + ["deno:///bundle.js", "deno:///bundle.js.map"].sort(), + ); assert(files["deno:///bundle.js"].length); }, }); @@ -226,7 +233,10 @@ Deno.test({ assertEquals(diagnostics.length, 0); assert(!ignoredOptions); assertEquals(stats.length, 12); - assertEquals(Object.keys(files), ["deno:///bundle.js"]); + assertEquals( + Object.keys(files).sort(), + ["deno:///bundle.js.map", "deno:///bundle.js"].sort(), + ); assert(files["deno:///bundle.js"].includes(`const bar1 = "bar"`)); }, }); @@ -333,9 +343,10 @@ Deno.test({ }); assert(diagnostics); assertEquals(diagnostics.length, 0); - assertEquals(Object.keys(files).length, 1); + assertEquals(Object.keys(files).length, 2); assert(files["deno:///bundle.js"].startsWith("(function() {\n")); assert(files["deno:///bundle.js"].endsWith("})();\n")); + assert(files["deno:///bundle.js.map"]); }, }); @@ -357,3 +368,41 @@ Deno.test({ ); }, }); + +Deno.test({ + name: `Deno.emit() - support source maps with bundle option`, + async fn() { + { + const { diagnostics, files } = await Deno.emit("/a.ts", { + bundle: "classic", + sources: { + "/a.ts": `import { b } from "./b.ts"; + console.log(b);`, + "/b.ts": `export const b = "b";`, + }, + compilerOptions: { + inlineSourceMap: true, + sourceMap: false, + }, + }); + assert(diagnostics); + assertEquals(diagnostics.length, 0); + assertEquals(Object.keys(files).length, 1); + assertStringIncludes(files["deno:///bundle.js"], "sourceMappingURL"); + } + + const { diagnostics, files } = await Deno.emit("/a.ts", { + bundle: "classic", + sources: { + "/a.ts": `import { b } from "./b.ts"; + console.log(b);`, + "/b.ts": `export const b = "b";`, + }, + }); + assert(diagnostics); + assertEquals(diagnostics.length, 0); + assertEquals(Object.keys(files).length, 2); + assert(files["deno:///bundle.js"]); + assert(files["deno:///bundle.js.map"]); + }, +}); From 127d938c84935cdb6783f49fec334807e53328f3 Mon Sep 17 00:00:00 2001 From: Raika Toriyama Date: Wed, 19 May 2021 13:22:04 +0900 Subject: [PATCH 13/53] docs: fix misspelling (#10683) --- docs/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/testing.md b/docs/testing.md index 085e2d093d..d47ea356af 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -270,6 +270,6 @@ By default, `deno coverage` will exclude any files matching the regular expression `test\.(js|mjs|ts|jsx|tsx)` and only consider including files matching the regular expression `^file:`. -These filters can be overriden using the `--exclude` and `--include` flags. A +These filters can be overridden using the `--exclude` and `--include` flags. A source file's url must match both regular expressions for it to be a part of the report. From 44cd0b1ef6d302993288f872cdd0816a581d2d0b Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 15:33:01 +0800 Subject: [PATCH 14/53] docs(cli/dts): fix typo in `TestDefinition.only` description (#10697) --- cli/dts/lib.deno.ns.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index ddbb890b7e..e3a59ac78f 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -98,7 +98,7 @@ declare namespace Deno { fn: () => void | Promise; name: string; ignore?: boolean; - /** If at lease one test has `only` set to true, only run tests that have + /** If at least one test has `only` set to true, only run tests that have * `only` set to true and fail the test suite. */ only?: boolean; /** Check that the number of async completed ops after the test is the same From 7a751b813539f3048e56022248aeef42bb73b3ee Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Wed, 19 May 2021 17:10:23 +0530 Subject: [PATCH 15/53] fix(deno install): support `file:` scheme URLs (#10562) --- cli/tools/installer.rs | 77 ++++++++++++++++++++-------------------- cli/tools/test_runner.rs | 16 ++++++++- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 3693b2fb36..02dbd71ce4 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -3,7 +3,7 @@ use crate::flags::Flags; use crate::fs_util::canonicalize_path; use deno_core::error::generic_error; use deno_core::error::AnyError; -use deno_core::error::Context; +use deno_core::resolve_url_or_path; use deno_core::url::Url; use log::Level; use regex::Regex; @@ -29,11 +29,6 @@ lazy_static::lazy_static! { ).case_insensitive(true).build().unwrap(); } -pub fn is_remote_url(module_url: &str) -> bool { - let lower = module_url.to_lowercase(); - lower.starts_with("http://") || lower.starts_with("https://") -} - fn validate_name(exec_name: &str) -> Result<(), AnyError> { if EXEC_NAME_RE.is_match(exec_name) { Ok(()) @@ -144,26 +139,6 @@ pub fn infer_name_from_url(url: &Url) -> Option { Some(stem) } -/// Get a valid URL from the provided value. -/// When the provided value is a URL with 'http(s)' scheme, -/// it ensures it is valid by parsing it and if not, it will -/// construct a URL of 'file' scheme from the provided value. -fn get_valid_url(module_url: &str) -> Result { - if is_remote_url(module_url) { - Ok(Url::parse(module_url).expect("Should be valid url")) - } else { - let module_path = PathBuf::from(module_url); - let module_path = if module_path.is_absolute() { - module_path - } else { - let cwd = env::current_dir() - .context("Failed to get current working directory")?; - cwd.join(module_path) - }; - Ok(Url::from_file_path(module_path).expect("Path should be absolute")) - } -} - pub fn install( flags: Flags, module_url: &str, @@ -189,7 +164,7 @@ pub fn install( }; // Check if module_url is remote - let module_url = get_valid_url(module_url)?; + let module_url = resolve_url_or_path(module_url)?; let name = name.or_else(|| infer_name_from_url(&module_url)); @@ -279,7 +254,7 @@ pub fn install( } if let Some(import_map_path) = flags.import_map_path { - let import_map_url = get_valid_url(&import_map_path)?; + let import_map_url = resolve_url_or_path(&import_map_path)?; executable_args.push("--import-map".to_string()); executable_args.push(import_map_url.to_string()); } @@ -345,21 +320,12 @@ mod tests { use std::process::Command; use std::sync::Mutex; use tempfile::TempDir; + use test_util::tests_path; lazy_static::lazy_static! { pub static ref ENV_LOCK: Mutex<()> = Mutex::new(()); } - #[test] - fn test_is_remote_url() { - assert!(is_remote_url("https://deno.land/std/http/file_server.ts")); - assert!(is_remote_url("http://deno.land/std/http/file_server.ts")); - assert!(is_remote_url("HTTP://deno.land/std/http/file_server.ts")); - assert!(is_remote_url("HTTp://deno.land/std/http/file_server.ts")); - assert!(!is_remote_url("file:///dev/deno_std/http/file_server.ts")); - assert!(!is_remote_url("./dev/deno_std/http/file_server.ts")); - } - #[test] fn install_infer_name_from_url() { assert_eq!( @@ -923,4 +889,39 @@ mod tests { let content = fs::read_to_string(file_path).unwrap(); assert!(content.contains(&expected_string)); } + + // Regression test for https://github.com/denoland/deno/issues/10556. + #[test] + fn install_file_url() { + let temp_dir = TempDir::new().expect("tempdir fail"); + let bin_dir = temp_dir.path().join("bin"); + let module_path = fs::canonicalize(tests_path().join("cat.ts")).unwrap(); + let file_module_string = + Url::from_file_path(module_path).unwrap().to_string(); + assert!(file_module_string.starts_with("file:///")); + + let result = install( + Flags::default(), + &file_module_string, + vec![], + Some("echo_test".to_string()), + Some(temp_dir.path().to_path_buf()), + true, + ); + assert!(result.is_ok()); + + let mut file_path = bin_dir.join("echo_test"); + if cfg!(windows) { + file_path = file_path.with_extension("cmd"); + } + assert!(file_path.exists()); + + let mut expected_string = format!("run '{}'", &file_module_string); + if cfg!(windows) { + expected_string = format!("\"run\" \"{}\"", &file_module_string); + } + + let content = fs::read_to_string(file_path).unwrap(); + assert!(content.contains(&expected_string)); + } } diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index 6b2eab36bc..a2bd0edead 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -11,7 +11,6 @@ use crate::module_graph; use crate::program_state::ProgramState; use crate::tokio_util; use crate::tools::coverage::CoverageCollector; -use crate::tools::installer::is_remote_url; use deno_core::error::AnyError; use deno_core::futures::future; use deno_core::futures::stream; @@ -226,6 +225,11 @@ pub(crate) fn is_supported(p: &Path) -> bool { } } +pub fn is_remote_url(module_url: &str) -> bool { + let lower = module_url.to_lowercase(); + lower.starts_with("http://") || lower.starts_with("https://") +} + pub fn collect_test_module_specifiers

( include: Vec, root_path: &Path, @@ -642,4 +646,14 @@ mod tests { .collect(); assert_eq!(matched_urls, expected); } + + #[test] + fn test_is_remote_url() { + assert!(is_remote_url("https://deno.land/std/http/file_server.ts")); + assert!(is_remote_url("http://deno.land/std/http/file_server.ts")); + assert!(is_remote_url("HTTP://deno.land/std/http/file_server.ts")); + assert!(is_remote_url("HTTp://deno.land/std/http/file_server.ts")); + assert!(!is_remote_url("file:///dev/deno_std/http/file_server.ts")); + assert!(!is_remote_url("./dev/deno_std/http/file_server.ts")); + } } From 736b73647989bb51f2f9737b4a2bc27c3f336aed Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 20:15:01 +0800 Subject: [PATCH 16/53] docs: fix unix socket examples (#10705) --- cli/dts/lib.deno.unstable.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 0fe6df1443..5a3547a6b3 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -929,7 +929,7 @@ declare namespace Deno { * * ```ts * const listener = Deno.listenDatagram({ - * address: "/foo/bar.sock", + * path: "/foo/bar.sock", * transport: "unixpacket" * }); * ``` From 073e05f9fbf54f4de6e8a033d157e61969f1335c Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 20:15:35 +0800 Subject: [PATCH 17/53] docs(cli/dts): make worker example pass (#10703) --- cli/dts/lib.deno.unstable.d.ts | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 5a3547a6b3..b64117c23e 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -1351,28 +1351,6 @@ declare interface WorkerOptions { * }, * } * ); - * worker.postMessage({ cmd: "readFile", fileName: "./log.txt" }); - * - * // deno_worker.ts - * - * - * self.onmessage = async function (e) { - * const { cmd, fileName } = e.data; - * if (cmd !== "readFile") { - * throw new Error("Invalid command"); - * } - * const buf = await Deno.readFile(fileName); - * const fileContents = new TextDecoder().decode(buf); - * console.log(fileContents); - * } - * - * // $ cat log.txt - * // hello world - * // hello world 2 - * - * // $ deno run --allow-read mod.ts - * // hello world - * // hello world2 * ``` */ // TODO(Soremwar) From 6ef64b9fce9ed0f2c78025063d9a44f8e0acd124 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Wed, 19 May 2021 22:28:23 +1000 Subject: [PATCH 18/53] fix(#10695): deps diagnostics include data property (#10696) Fixes #10695 --- cli/lsp/diagnostics.rs | 2 ++ cli/tests/integration_tests_lsp.rs | 17 +++++++++++----- cli/tests/lsp/diagnostics_deno_deps.json | 25 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 cli/tests/lsp/diagnostics_deno_deps.json diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 47b26d92ea..33705dbff0 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -11,6 +11,7 @@ use crate::tokio_util::create_basic_runtime; use deno_core::error::anyhow; use deno_core::error::AnyError; use deno_core::resolve_url; +use deno_core::serde_json::json; use deno_core::ModuleSpecifier; use log::error; use lspower::lsp; @@ -446,6 +447,7 @@ async fn generate_deps_diagnostics( code, source: Some("deno".to_string()), message, + data: Some(json!({ "specifier": specifier })), ..Default::default() }); } else if sources.contains_key(&specifier) { diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index d08da622f5..ad41afadaf 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1362,17 +1362,24 @@ fn lsp_code_actions() { #[test] fn lsp_code_actions_deno_cache() { let mut client = init("initialize_params.json"); - did_open( - &mut client, - json!({ + client + .write_notification("textDocument/didOpen", json!({ "textDocument": { "uri": "file:///a/file.ts", "languageId": "typescript", "version": 1, "text": "import * as a from \"https://deno.land/x/a/mod.ts\";\n\nconsole.log(a);\n" } - }), - ); + })) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, params) = client.read_notification().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + assert_eq!(params, Some(load_fixture("diagnostics_deno_deps.json"))); + let (maybe_res, maybe_err) = client .write_request( "textDocument/codeAction", diff --git a/cli/tests/lsp/diagnostics_deno_deps.json b/cli/tests/lsp/diagnostics_deno_deps.json new file mode 100644 index 0000000000..ec6cc4f516 --- /dev/null +++ b/cli/tests/lsp/diagnostics_deno_deps.json @@ -0,0 +1,25 @@ +{ + "uri": "file:///a/file.ts", + "diagnostics": [ + { + "range": { + "start": { + "line": 0, + "character": 19 + }, + "end": { + "line": 0, + "character": 49 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://deno.land/x/a/mod.ts\".", + "data": { + "specifier": "https://deno.land/x/a/mod.ts" + } + } + ], + "version": 1 +} From a1125765ec8ef5d13ed49ad5cceadb0b41202be4 Mon Sep 17 00:00:00 2001 From: crowlKats <13135287+crowlKats@users.noreply.github.com> Date: Wed, 19 May 2021 14:28:50 +0200 Subject: [PATCH 19/53] fix(webstorage): use opstate for sqlite connection (#10692) Fixes #10691 --- extensions/webstorage/01_webstorage.js | 34 ++--- extensions/webstorage/Cargo.toml | 2 +- extensions/webstorage/lib.rs | 181 +++++++++---------------- 3 files changed, 80 insertions(+), 137 deletions(-) diff --git a/extensions/webstorage/01_webstorage.js b/extensions/webstorage/01_webstorage.js index a11d44068a..43a1dbbfb6 100644 --- a/extensions/webstorage/01_webstorage.js +++ b/extensions/webstorage/01_webstorage.js @@ -1,11 +1,13 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + ((window) => { const core = window.Deno.core; const webidl = window.__bootstrap.webidl; - const _rid = Symbol("[[rid]]"); + const _persistent = Symbol("[[persistent]]"); class Storage { - [_rid]; + [_persistent]; constructor() { webidl.illegalConstructor(); @@ -13,7 +15,7 @@ get length() { webidl.assertBranded(this, Storage); - return core.opSync("op_webstorage_length", this[_rid]); + return core.opSync("op_webstorage_length", this[_persistent]); } key(index) { @@ -25,10 +27,7 @@ context: "Argument 1", }); - return core.opSync("op_webstorage_key", { - rid: this[_rid], - index, - }); + return core.opSync("op_webstorage_key", index, this[_persistent]); } setItem(key, value) { @@ -45,10 +44,9 @@ }); core.opSync("op_webstorage_set", { - rid: this[_rid], keyName: key, keyValue: value, - }); + }, this[_persistent]); } getItem(key) { @@ -60,10 +58,7 @@ context: "Argument 1", }); - return core.opSync("op_webstorage_get", { - rid: this[_rid], - keyName: key, - }); + return core.opSync("op_webstorage_get", key, this[_persistent]); } removeItem(key) { @@ -75,25 +70,20 @@ context: "Argument 1", }); - core.opSync("op_webstorage_remove", { - rid: this[_rid], - keyName: key, - }); + core.opSync("op_webstorage_remove", key, this[_persistent]); } clear() { webidl.assertBranded(this, Storage); - core.opSync("op_webstorage_clear", this[_rid]); + core.opSync("op_webstorage_clear", this[_persistent]); } } function createStorage(persistent) { if (persistent) window.location; - const rid = core.opSync("op_webstorage_open", persistent); - const storage = webidl.createBranded(Storage); - storage[_rid] = rid; + storage[_persistent] = persistent; const proxy = new Proxy(storage, { deleteProperty(target, key) { @@ -135,7 +125,7 @@ return (typeof target.getItem(p)) === "string"; }, ownKeys() { - return core.opSync("op_webstorage_iterate_keys", rid); + return core.opSync("op_webstorage_iterate_keys", persistent); }, getOwnPropertyDescriptor(target, key) { if (arguments.length === 1) { diff --git a/extensions/webstorage/Cargo.toml b/extensions/webstorage/Cargo.toml index ab0a6299e9..506014b170 100644 --- a/extensions/webstorage/Cargo.toml +++ b/extensions/webstorage/Cargo.toml @@ -1,4 +1,4 @@ -# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. [package] name = "deno_webstorage" diff --git a/extensions/webstorage/lib.rs b/extensions/webstorage/lib.rs index 90ae0598ad..8b3e206fba 100644 --- a/extensions/webstorage/lib.rs +++ b/extensions/webstorage/lib.rs @@ -1,18 +1,14 @@ -// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use deno_core::error::bad_resource_id; use deno_core::error::AnyError; use deno_core::include_js_files; use deno_core::op_sync; use deno_core::Extension; use deno_core::OpState; -use deno_core::Resource; -use deno_core::ZeroCopyBuf; use rusqlite::params; use rusqlite::Connection; use rusqlite::OptionalExtension; use serde::Deserialize; -use std::borrow::Cow; use std::fmt; use std::path::PathBuf; @@ -26,7 +22,6 @@ pub fn init(location_data_dir: Option) -> Extension { "01_webstorage.js", )) .ops(vec![ - ("op_webstorage_open", op_sync(op_webstorage_open)), ("op_webstorage_length", op_sync(op_webstorage_length)), ("op_webstorage_key", op_sync(op_webstorage_key)), ("op_webstorage_set", op_sync(op_webstorage_set)), @@ -51,82 +46,73 @@ pub fn get_declaration() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webstorage.d.ts") } -struct WebStorageConnectionResource(Connection); +struct LocalStorage(Connection); +struct SessionStorage(Connection); -impl Resource for WebStorageConnectionResource { - fn name(&self) -> Cow { - "webStorage".into() - } -} - -pub fn op_webstorage_open( +fn get_webstorage( state: &mut OpState, persistent: bool, - _zero_copy: Option, -) -> Result { - let connection = if persistent { - let path = state.try_borrow::().ok_or_else(|| { - DomExceptionNotSupportedError::new( - "LocalStorage is not supported in this context.", - ) - })?; - std::fs::create_dir_all(&path.0)?; - Connection::open(path.0.join("local_storage"))? +) -> Result<&Connection, AnyError> { + let conn = if persistent { + if state.try_borrow::().is_none() { + let path = state.try_borrow::().ok_or_else(|| { + DomExceptionNotSupportedError::new( + "LocalStorage is not supported in this context.", + ) + })?; + std::fs::create_dir_all(&path.0)?; + let conn = Connection::open(path.0.join("local_storage"))?; + conn.execute( + "CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)", + params![], + )?; + + state.put(LocalStorage(conn)); + } + + &state.borrow::().0 } else { - Connection::open_in_memory()? + if state.try_borrow::().is_none() { + let conn = Connection::open_in_memory()?; + conn.execute( + "CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)", + params![], + )?; + + state.put(SessionStorage(conn)); + } + + &state.borrow::().0 }; - connection.execute( - "CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)", - params![], - )?; - - let rid = state - .resource_table - .add(WebStorageConnectionResource(connection)); - Ok(rid) + Ok(conn) } pub fn op_webstorage_length( state: &mut OpState, - rid: u32, - _zero_copy: Option, + persistent: bool, + _: (), ) -> Result { - let resource = state - .resource_table - .get::(rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - let mut stmt = resource.0.prepare("SELECT COUNT(*) FROM data")?; + let mut stmt = conn.prepare("SELECT COUNT(*) FROM data")?; let length: u32 = stmt.query_row(params![], |row| row.get(0))?; Ok(length) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct KeyArgs { - rid: u32, - index: u32, -} - pub fn op_webstorage_key( state: &mut OpState, - args: KeyArgs, - _zero_copy: Option, + index: u32, + persistent: bool, ) -> Result, AnyError> { - let resource = state - .resource_table - .get::(args.rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - let mut stmt = resource - .0 - .prepare("SELECT key FROM data LIMIT 1 OFFSET ?")?; + let mut stmt = conn.prepare("SELECT key FROM data LIMIT 1 OFFSET ?")?; let key: Option = stmt - .query_row(params![args.index], |row| row.get(0)) + .query_row(params![index], |row| row.get(0)) .optional()?; Ok(key) @@ -135,7 +121,6 @@ pub fn op_webstorage_key( #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct SetArgs { - rid: u32, key_name: String, key_value: String, } @@ -143,16 +128,12 @@ pub struct SetArgs { pub fn op_webstorage_set( state: &mut OpState, args: SetArgs, - _zero_copy: Option, + persistent: bool, ) -> Result<(), AnyError> { - let resource = state - .resource_table - .get::(args.rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - let mut stmt = resource - .0 - .prepare("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?; + let mut stmt = + conn.prepare("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?; let size: u32 = stmt.query_row(params![], |row| row.get(0))?; if size >= 5000000 { @@ -162,7 +143,7 @@ pub fn op_webstorage_set( ); } - resource.0.execute( + conn.execute( "INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)", params![args.key_name, args.key_value], )?; @@ -170,68 +151,43 @@ pub fn op_webstorage_set( Ok(()) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GetArgs { - rid: u32, - key_name: String, -} - pub fn op_webstorage_get( state: &mut OpState, - args: GetArgs, - _zero_copy: Option, + key_name: String, + persistent: bool, ) -> Result, AnyError> { - let resource = state - .resource_table - .get::(args.rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - let mut stmt = resource.0.prepare("SELECT value FROM data WHERE key = ?")?; + let mut stmt = conn.prepare("SELECT value FROM data WHERE key = ?")?; let val = stmt - .query_row(params![args.key_name], |row| row.get(0)) + .query_row(params![key_name], |row| row.get(0)) .optional()?; Ok(val) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RemoveArgs { - rid: u32, - key_name: String, -} - pub fn op_webstorage_remove( state: &mut OpState, - args: RemoveArgs, - _zero_copy: Option, + key_name: String, + persistent: bool, ) -> Result<(), AnyError> { - let resource = state - .resource_table - .get::(args.rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - resource - .0 - .execute("DELETE FROM data WHERE key = ?", params![args.key_name])?; + conn.execute("DELETE FROM data WHERE key = ?", params![key_name])?; Ok(()) } pub fn op_webstorage_clear( state: &mut OpState, - rid: u32, - _zero_copy: Option, + persistent: bool, + _: (), ) -> Result<(), AnyError> { - let resource = state - .resource_table - .get::(rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - resource.0.execute("DROP TABLE data", params![])?; - resource.0.execute( + conn.execute("DROP TABLE data", params![])?; + conn.execute( "CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)", params![], )?; @@ -241,15 +197,12 @@ pub fn op_webstorage_clear( pub fn op_webstorage_iterate_keys( state: &mut OpState, - rid: u32, - _zero_copy: Option, + persistent: bool, + _: (), ) -> Result, AnyError> { - let resource = state - .resource_table - .get::(rid) - .ok_or_else(bad_resource_id)?; + let conn = get_webstorage(state, persistent)?; - let mut stmt = resource.0.prepare("SELECT key FROM data")?; + let mut stmt = conn.prepare("SELECT key FROM data")?; let keys = stmt .query_map(params![], |row| row.get::<_, String>(0))? From 218ba031f06919b1871b4d360e5ae6c5cc777f35 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 19 May 2021 13:39:52 +0100 Subject: [PATCH 20/53] fix(runtime/http): expose nextRequest() errors in respondWith() (#10384) --- cli/tests/unit/http_test.ts | 45 +++++++++++++++++++++++++++++++++++++ runtime/js/40_http.js | 26 ++++++++++++++++++--- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index ab43323bd4..d4c35545f5 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -272,3 +272,48 @@ unitTest( await promise; }, ); + +unitTest( + { perms: { net: true } }, + async function httpServerNextRequestErrorExposedInResponse() { + const promise = (async () => { + const listener = Deno.listen({ port: 4501 }); + const conn = await listener.accept(); + const httpConn = Deno.serveHttp(conn); + const event = await httpConn.nextRequest(); + assert(event); + // Start polling for the next request before awaiting response. + const nextRequestPromise = httpConn.nextRequest(); + const { respondWith } = event; + await assertThrowsAsync( + async () => { + let interval = 0; + await respondWith( + new Response( + new ReadableStream({ + start(controller) { + interval = setInterval(() => { + const message = `data: ${Date.now()}\n\n`; + controller.enqueue(new TextEncoder().encode(message)); + }, 200); + }, + cancel() { + clearInterval(interval); + }, + }), + ), + ); + }, + Deno.errors.Http, + "connection closed", + ); + // The error from `op_http_request_next` reroutes to `respondWith()`. + assertEquals(await nextRequestPromise, null); + listener.close(); + })(); + + const resp = await fetch("http://127.0.0.1:4501/"); + await resp.body!.cancel(); + await promise; + }, +); diff --git a/runtime/js/40_http.js b/runtime/js/40_http.js index eb3c58a632..eb4d214ca4 100644 --- a/runtime/js/40_http.js +++ b/runtime/js/40_http.js @@ -14,6 +14,8 @@ return new HttpConn(rid); } + const connErrorSymbol = Symbol("connError"); + class HttpConn { #rid = 0; @@ -35,10 +37,16 @@ this.#rid, ); } catch (error) { + // A connection error seen here would cause disrupted responses to throw + // a generic `BadResource` error. Instead store this error and replace + // those with it. + this[connErrorSymbol] = error; if (error instanceof errors.BadResource) { return null; } else if (error instanceof errors.Interrupted) { return null; + } else if (error.message.includes("connection closed")) { + return null; } throw error; } @@ -66,7 +74,7 @@ ); const request = fromInnerRequest(innerRequest, "immutable"); - const respondWith = createRespondWith(responseSenderRid, this.#rid); + const respondWith = createRespondWith(this, responseSenderRid); return { request, respondWith }; } @@ -97,7 +105,7 @@ ); } - function createRespondWith(responseSenderRid) { + function createRespondWith(httpConn, responseSenderRid) { return async function respondWith(resp) { if (resp instanceof Promise) { resp = await resp; @@ -145,6 +153,11 @@ innerResp.headerList, ], respBody instanceof Uint8Array ? respBody : null); } catch (error) { + const connError = httpConn[connErrorSymbol]; + if (error instanceof errors.BadResource && connError != null) { + // deno-lint-ignore no-ex-assign + error = new connError.constructor(connError.message); + } if (respBody !== null && respBody instanceof ReadableStream) { await respBody.cancel(error); } @@ -173,6 +186,11 @@ value, ); } catch (error) { + const connError = httpConn[connErrorSymbol]; + if (error instanceof errors.BadResource && connError != null) { + // deno-lint-ignore no-ex-assign + error = new connError.constructor(connError.message); + } await reader.cancel(error); throw error; } @@ -180,7 +198,9 @@ } finally { // Once all chunks are sent, and the request body is closed, we can // close the response body. - await Deno.core.opAsync("op_http_response_close", responseBodyRid); + try { + await Deno.core.opAsync("op_http_response_close", responseBodyRid); + } catch { /* pass */ } } } }; From 736ff290b8780f76fb0f01ede0c9b704bb8bc3a1 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 21:07:10 +0800 Subject: [PATCH 21/53] docs(cli/dts): fix plugin example (#10647) --- cli/dts/lib.deno.unstable.d.ts | 21 ++++++++++++++++++--- cli/tests/integration_tests_lsp.rs | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index b64117c23e..480b0ee06c 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -133,10 +133,25 @@ declare namespace Deno { * Open and initialize a plugin. * * ```ts + * import { assert } from "https://deno.land/std/testing/asserts.ts"; * const rid = Deno.openPlugin("./path/to/some/plugin.so"); - * const opId = Deno.core.ops()["some_op"]; - * const response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4])); - * console.log(`Response from plugin ${response}`); + * + * // The Deno.core namespace is needed to interact with plugins, but this is + * // internal so we use ts-ignore to skip type checking these calls. + * // @ts-ignore + * const { + * op_test_sync, + * op_test_async, + * } = Deno.core.ops(); + * + * assert(op_test_sync); + * assert(op_test_async); + * + * // @ts-ignore + * const result = Deno.core.opSync("op_test_sync"); + * + * // @ts-ignore + * const result = await Deno.core.opAsync("op_test_sync"); * ``` * * Requires `allow-plugin` permission. diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index ad41afadaf..e4a9eb11ca 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -318,7 +318,7 @@ fn lsp_hover_unstable_enabled() { "language":"typescript", "value":"function Deno.openPlugin(filename: string): number" }, - "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\nconst opId = Deno.core.ops()[\"some_op\"];\nconst response = Deno.core.dispatch(opId, new Uint8Array([1,2,3,4]));\nconsole.log(`Response from plugin ${response}`);\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" + "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nimport { assert } from \"https://deno.land/std/testing/asserts.ts\";\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\n\n// The Deno.core namespace is needed to interact with plugins, but this is\n// internal so we use ts-ignore to skip type checking these calls.\n// @ts-ignore\nconst {\n op_test_sync,\n op_test_async,\n} = Deno.core.ops();\n\nassert(op_test_sync);\nassert(op_test_async);\n\n// @ts-ignore\nconst result = Deno.core.opSync(\"op_test_sync\");\n\n// @ts-ignore\nconst result = await Deno.core.opAsync(\"op_test_sync\");\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" ], "range":{ "start":{ From 2e9bd3e295b67cad01f63847a2ad1b0d2387e1e7 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 21:33:22 +0800 Subject: [PATCH 22/53] fix(cli): canonicalize coverage dir (#10364) --- cli/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/main.rs b/cli/main.rs index 5a5972e92c..3b147ffcc3 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -925,7 +925,10 @@ async fn test_command( concurrent_jobs: usize, ) -> Result<(), AnyError> { if let Some(ref coverage_dir) = flags.coverage_dir { - env::set_var("DENO_UNSTABLE_COVERAGE_DIR", coverage_dir); + env::set_var( + "DENO_UNSTABLE_COVERAGE_DIR", + PathBuf::from(coverage_dir).canonicalize()?, + ); } // TODO(caspervonb) move this chunk into tools::test_runner. From 8a512b99c90719492f7490e70f68b829f4c85530 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 19 May 2021 22:45:00 +0800 Subject: [PATCH 23/53] fix(cli/tools/test_runner): use file_fetcher.fetch to get sources (#10708) --- cli/tools/test_runner.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index a2bd0edead..de251947bd 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -346,7 +346,10 @@ pub async fn run_tests( let lines_regex = Regex::new(r"(?:\* ?)(?:\# ?)?(.*)")?; for specifier in &doc_modules { - let file = program_state.file_fetcher.get_source(&specifier).unwrap(); + let file = program_state + .file_fetcher + .fetch(&specifier, &mut permissions.clone()) + .await?; let parsed_module = ast::parse(&file.specifier.as_str(), &file.source, &file.media_type)?; From c5f700ca63a2013e268ee5e71449201fcebbac07 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Wed, 19 May 2021 23:48:38 +0900 Subject: [PATCH 24/53] ci: fix 'skip save cache' step in windows CI (#10704) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43f50e9dc3..7ac23cab0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -217,6 +217,7 @@ jobs: # Skips saving cache in PR branches - name: Skip save cache (PR) run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV + shell: bash if: github.ref != 'refs/heads/main' - name: Apply and update mtime cache From 3df9f41dfdcdbfa82da0dd758cab132121420e1a Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 20 May 2021 00:41:36 +0800 Subject: [PATCH 25/53] test(cli/dts): typecheck examples in declaration files (#10707) This commits adds two integration tests that typecheck examples in the docstrings in Deno declaration files. --- cli/dts/lib.deno.unstable.d.ts | 5 +---- cli/tests/integration_tests.rs | 33 ++++++++++++++++++++++++++++++ cli/tests/integration_tests_lsp.rs | 2 +- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 480b0ee06c..c8e2f42074 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -139,10 +139,7 @@ declare namespace Deno { * // The Deno.core namespace is needed to interact with plugins, but this is * // internal so we use ts-ignore to skip type checking these calls. * // @ts-ignore - * const { - * op_test_sync, - * op_test_async, - * } = Deno.core.ops(); + * const { op_test_sync, op_test_async } = Deno.core.ops(); * * assert(op_test_sync); * assert(op_test_async); diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 19a1a8952c..ce12056988 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -18,6 +18,39 @@ use tempfile::TempDir; use test_util as util; use tokio::task::LocalSet; +// TODO(caspervonb): investiate why this fails on Windows. +#[cfg(unix)] +#[test] +fn typecheck_declarations_ns() { + let status = util::deno_cmd() + .arg("test") + .arg("--allow-all") + .arg("--doc") + .arg(util::root_path().join("cli/dts/lib.deno.ns.d.ts")) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(status.success()); +} + +// TODO(caspervonb): investiate why this fails on Windows. +#[cfg(unix)] +#[test] +fn typecheck_declarations_unstable() { + let status = util::deno_cmd() + .arg("test") + .arg("--doc") + .arg("--allow-all") + .arg("--unstable") + .arg(util::root_path().join("cli/dts/lib.deno.unstable.d.ts")) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(status.success()); +} + #[test] fn js_unit_tests_lint() { let status = util::deno_cmd() diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index e4a9eb11ca..15abfe6781 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -318,7 +318,7 @@ fn lsp_hover_unstable_enabled() { "language":"typescript", "value":"function Deno.openPlugin(filename: string): number" }, - "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nimport { assert } from \"https://deno.land/std/testing/asserts.ts\";\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\n\n// The Deno.core namespace is needed to interact with plugins, but this is\n// internal so we use ts-ignore to skip type checking these calls.\n// @ts-ignore\nconst {\n op_test_sync,\n op_test_async,\n} = Deno.core.ops();\n\nassert(op_test_sync);\nassert(op_test_async);\n\n// @ts-ignore\nconst result = Deno.core.opSync(\"op_test_sync\");\n\n// @ts-ignore\nconst result = await Deno.core.opAsync(\"op_test_sync\");\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" + "**UNSTABLE**: new API, yet to be vetted.\n\nOpen and initialize a plugin.\n\n```ts\nimport { assert } from \"https://deno.land/std/testing/asserts.ts\";\nconst rid = Deno.openPlugin(\"./path/to/some/plugin.so\");\n\n// The Deno.core namespace is needed to interact with plugins, but this is\n// internal so we use ts-ignore to skip type checking these calls.\n// @ts-ignore\nconst { op_test_sync, op_test_async } = Deno.core.ops();\n\nassert(op_test_sync);\nassert(op_test_async);\n\n// @ts-ignore\nconst result = Deno.core.opSync(\"op_test_sync\");\n\n// @ts-ignore\nconst result = await Deno.core.opAsync(\"op_test_sync\");\n```\n\nRequires `allow-plugin` permission.\n\nThe plugin system is not stable and will change in the future, hence the\nlack of docs. For now take a look at the example\nhttps://github.com/denoland/deno/tree/main/test_plugin" ], "range":{ "start":{ From 72eb6e1d7cfec721e33ac08943c0f97dce93fc23 Mon Sep 17 00:00:00 2001 From: Aaron O'Mullan Date: Wed, 19 May 2021 20:45:48 +0200 Subject: [PATCH 26/53] fix(serde_v8): remove intentional deserialization error on non-utf8 strings (#10156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartek Iwańczuk --- serde_v8/src/de.rs | 18 +----------------- tools/wpt/expectation.json | 30 ++---------------------------- 2 files changed, 3 insertions(+), 45 deletions(-) diff --git a/serde_v8/src/de.rs b/serde_v8/src/de.rs index f351d78709..b0237d514a 100644 --- a/serde_v8/src/de.rs +++ b/serde_v8/src/de.rs @@ -154,12 +154,8 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> V: Visitor<'de>, { if self.input.is_string() { - // TODO(@AaronO): implement a `.to_rust_string -> Option` in rusty-v8 let v8_string = v8::Local::::try_from(self.input).unwrap(); - let string = match v8_to_rust_string(self.scope, v8_string) { - Some(string) => string, - None => return Err(Error::ExpectedUtf8), - }; + let string = v8_string.to_rust_string_lossy(self.scope); visitor.visit_string(string) } else { Err(Error::ExpectedString) @@ -619,15 +615,3 @@ impl<'de, 'a, 'b, 's> de::VariantAccess<'de> de::Deserializer::deserialize_struct(&mut d, "", fields, visitor) } } - -// Like v8::String::to_rust_string_lossy except returns None on non-utf8 -fn v8_to_rust_string( - scope: &mut v8::HandleScope, - s: v8::Local, -) -> Option { - let string = s.to_rust_string_lossy(scope); - match string.find(std::char::REPLACEMENT_CHARACTER) { - Some(_) => None, - None => Some(string), - } -} diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index afdc0cb1f4..4d6016104e 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -683,8 +683,6 @@ "Stringification of new URLSearchParams(\"hi=there&thank=you\")" ], "url-constructor.any.html": [ - "Parsing: against ", - "Parsing: against ", "Parsing: against ", "Parsing: against ", "Parsing: against ", @@ -740,10 +738,7 @@ "Parsing: <> against ", "Parsing: against " ], - "url-origin.any.html": [ - "Origin parsing: against ", - "Origin parsing: against " - ], + "url-origin.any.html": true, "url-searchparams.any.html": true, "url-setters-stripping.any.html": [ "Setting protocol with leading U+0000 (https:)", @@ -777,12 +772,7 @@ "urlsearchparams-getall.any.html": true, "urlsearchparams-has.any.html": true, "urlsearchparams-set.any.html": true, - "urlsearchparams-sort.any.html": [ - "Parse and sort: �=x&&�=a", - "URL parse and sort: �=x&&�=a", - "Parse and sort: é&e�&é", - "URL parse and sort: é&e�&é" - ], + "urlsearchparams-sort.any.html": true, "urlsearchparams-stringifier.any.html": true }, "fetch": { @@ -1088,33 +1078,17 @@ "storage_session_window_open.window.html": false, "storage_set_value_enumerate.window.html": true, "storage_setitem.window.html": [ - "localStorage[\"\ud800\"]", "localStorage[] = \"\ud800\"", - "localStorage[\"\udbff\"]", "localStorage[] = \"\udbff\"", - "localStorage[\"\udc00\"]", "localStorage[] = \"\udc00\"", - "localStorage[\"\udfff\"]", "localStorage[] = \"\udfff\"", - "localStorage[\"\\ufffd\"]", - "localStorage[] = \"\\ufffd\"", - "localStorage[\"\ud83ca\"]", "localStorage[] = \"\ud83ca\"", - "localStorage[\"a\udf4d\"]", "localStorage[] = \"a\udf4d\"", - "sessionStorage[\"\ud800\"]", "sessionStorage[] = \"\ud800\"", - "sessionStorage[\"\udbff\"]", "sessionStorage[] = \"\udbff\"", - "sessionStorage[\"\udc00\"]", "sessionStorage[] = \"\udc00\"", - "sessionStorage[\"\udfff\"]", "sessionStorage[] = \"\udfff\"", - "sessionStorage[\"\\ufffd\"]", - "sessionStorage[] = \"\\ufffd\"", - "sessionStorage[\"\ud83ca\"]", "sessionStorage[] = \"\ud83ca\"", - "sessionStorage[\"a\udf4d\"]", "sessionStorage[] = \"a\udf4d\"" ], "storage_string_conversion.window.html": true, From 2fe4aaa10d5a6fd2ff6a65372c7064cdef7768f3 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 20 May 2021 03:08:41 +0800 Subject: [PATCH 27/53] fix(cli): always allow documentation modules to be checked (#10581) --- cli/tools/test_runner.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index de251947bd..ddfad81996 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -331,14 +331,6 @@ pub async fn run_tests( filter: Option, concurrent_jobs: usize, ) -> Result { - if test_modules.is_empty() { - println!("No matching test modules found"); - if !allow_none { - std::process::exit(1); - } - return Ok(false); - } - if !doc_modules.is_empty() { let mut test_programs = Vec::new(); @@ -415,6 +407,13 @@ pub async fn run_tests( program_state.maybe_import_map.clone(), ) .await?; + } else if test_modules.is_empty() { + println!("No matching test modules found"); + if !allow_none { + std::process::exit(1); + } + + return Ok(false); } program_state From 67cf683aad7d6fb7b0dc741267585c991b8be847 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 20 May 2021 00:57:15 -0500 Subject: [PATCH 28/53] fix(cli/dts): fix missing error class (NotSupported) in types (#10713) --- cli/dts/lib.deno.ns.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index e3a59ac78f..c56371a23b 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -84,6 +84,7 @@ declare namespace Deno { BadResource: ErrorConstructor; Http: ErrorConstructor; Busy: ErrorConstructor; + NotSupported: ErrorConstructor; }; /** The current process id of the runtime. */ From 176075980bfdc8b07f70db3229bd2df9b6ace018 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Thu, 20 May 2021 19:56:48 +1000 Subject: [PATCH 29/53] fix(lsp): re-enable the per resource configuration without a deadlock (#10625) Fixes #10603 --- cli/bench/lsp.rs | 145 +++++++-- cli/lsp/config.rs | 289 +++++++++++++++--- cli/lsp/diagnostics.rs | 6 +- cli/lsp/language_server.rs | 206 ++++++------- cli/tests/integration_tests_lsp.rs | 26 ++ cli/tests/lsp/initialize_params.json | 4 + cli/tests/lsp/initialize_params_disabled.json | 4 + cli/tests/lsp/initialize_params_registry.json | 4 + cli/tests/lsp/initialize_params_unstable.json | 4 + 9 files changed, 499 insertions(+), 189 deletions(-) diff --git a/cli/bench/lsp.rs b/cli/bench/lsp.rs index cabcef1dbf..a227e5a996 100644 --- a/cli/bench/lsp.rs +++ b/cli/bench/lsp.rs @@ -5,6 +5,8 @@ use deno_core::serde::Deserialize; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; +use deno_core::url::Url; +use lspower::lsp; use std::collections::HashMap; use std::path::Path; use std::time::Duration; @@ -62,16 +64,15 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result { }), )?; - // TODO(@kitsonk) work around https://github.com/denoland/deno/issues/10603 - // let (id, method, _): (u64, String, Option) = client.read_request()?; - // assert_eq!(method, "workspace/configuration"); + let (id, method, _): (u64, String, Option) = client.read_request()?; + assert_eq!(method, "workspace/configuration"); - // client.write_response( - // id, - // json!({ - // "enable": true - // }), - // )?; + client.write_response( + id, + json!({ + "enable": true + }), + )?; let (method, _): (String, Option) = client.read_notification()?; assert_eq!(method, "textDocument/publishDiagnostics"); @@ -122,13 +123,108 @@ fn bench_big_file_edits(deno_exe: &Path) -> Result { Ok(client.duration()) } +fn bench_find_replace(deno_exe: &Path) -> Result { + 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::()?; + assert_eq!(method, "workspace/configuration"); + client.write_response(id, json!({ "enable": true }))?; + } + + for _ in 0..3 { + let (method, _): (String, Option) = 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) = client.read_notification()?; + assert_eq!(method, "textDocument/publishDiagnostics"); + } + + let (_, response_error): (Option, Option) = + 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. fn bench_startup_shutdown(deno_exe: &Path) -> Result { let mut client = LspClient::new(deno_exe)?; let params: Value = serde_json::from_slice(FIXTURE_INIT_JSON)?; - let (_, response_error): (Option, Option) = - client.write_request("initialize", params)?; + let (_, response_error) = + client.write_request::<_, _, Value>("initialize", params)?; assert!(response_error.is_none()); client.write_notification("initialized", json!({}))?; @@ -145,16 +241,15 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Result { }), )?; - // TODO(@kitsonk) work around https://github.com/denoland/deno/issues/10603 - // let (id, method, _): (u64, String, Option) = client.read_request()?; - // assert_eq!(method, "workspace/configuration"); + let (id, method, _) = client.read_request::()?; + assert_eq!(method, "workspace/configuration"); - // client.write_response( - // id, - // json!({ - // "enable": true - // }), - // )?; + client.write_response( + id, + json!({ + "enable": true + }), + )?; let (method, _): (String, Option) = client.read_notification()?; assert_eq!(method, "textDocument/publishDiagnostics"); @@ -199,6 +294,16 @@ pub(crate) fn benchmarks( println!(" ({} runs, mean: {}ms)", times.len(), 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::() / 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"); Ok(exec_times) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 441b2cc99d..3e1b9fe85b 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,14 +1,22 @@ // 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_json; use deno_core::serde_json::Value; use deno_core::url::Url; use deno_core::ModuleSpecifier; -use lspower::jsonrpc::Error as LSPError; -use lspower::jsonrpc::Result as LSPResult; +use log::error; use lspower::lsp; +use std::collections::BTreeMap; 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"; @@ -139,25 +147,201 @@ impl WorkspaceSettings { } } +#[derive(Debug, Clone, Default)] +pub struct ConfigSnapshot { + pub client_capabilities: ClientCapabilities, + pub root_uri: Option, + 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)] +pub struct Settings { + pub specifiers: + BTreeMap, + pub workspace: WorkspaceSettings, +} + +#[derive(Debug)] pub struct Config { pub client_capabilities: ClientCapabilities, pub root_uri: Option, - pub specifier_settings: HashMap, - pub workspace_settings: WorkspaceSettings, + settings: Arc>, + tx: mpsc::Sender, } impl Config { - #[allow(unused)] - pub fn contains(&self, specifier: &ModuleSpecifier) -> bool { - self.specifier_settings.contains_key(specifier) + pub fn new(client: lspower::Client) -> Self { + let (tx, mut rx) = mpsc::channel::(100); + 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, + ) = { + 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::(value) { + Ok(workspace_settings) => { + settings.workspace = workspace_settings; + } + Err(err) => { + error!( + "Error converting workspace settings: {}", + err + ); + } + } + } + _ => { + match serde_json::from_value::(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::( + 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 { + 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 { - if let Some(settings) = self.specifier_settings.get(specifier) { - settings.enable + let settings = self.settings.read().unwrap(); + if let Some(specifier_settings) = settings.specifiers.get(specifier) { + specifier_settings.1.enable } else { - self.workspace_settings.enable + settings.workspace.enable } } @@ -192,23 +376,24 @@ impl Config { } } - pub fn update_specifier( - &mut self, - specifier: ModuleSpecifier, - value: Value, - ) -> LSPResult<()> { - let settings: SpecifierSettings = serde_json::from_value(value) - .map_err(|err| LSPError::invalid_params(err.to_string()))?; - self.specifier_settings.insert(specifier, settings); - Ok(()) + pub async fn update_specifier_settings( + &self, + specifier: &ModuleSpecifier, + uri: &ModuleSpecifier, + ) -> Result<(), AnyError> { + self + .tx + .send(ConfigRequest::Specifier(specifier.clone(), uri.clone())) + .await + .map_err(|_| anyhow!("Error sending config update task.")) } - pub fn update_workspace(&mut self, value: Value) -> LSPResult<()> { - let settings: WorkspaceSettings = serde_json::from_value(value) - .map_err(|err| LSPError::invalid_params(err.to_string()))?; - self.workspace_settings = settings; - self.specifier_settings = HashMap::new(); - Ok(()) + pub async fn update_workspace_settings(&self) -> Result<(), AnyError> { + self + .tx + .send(ConfigRequest::Workspace) + .await + .map_err(|_| anyhow!("Error sending config update task.")) } } @@ -218,41 +403,45 @@ mod tests { use deno_core::resolve_url; use deno_core::serde_json::json; - #[test] - fn test_config_contains() { - let mut config = Config::default(); - let specifier = resolve_url("https://deno.land/x/a.ts").unwrap(); - assert!(!config.contains(&specifier)); - config - .update_specifier( - specifier.clone(), - json!({ - "enable": true - }), - ) - .expect("could not update specifier"); - assert!(config.contains(&specifier)); + #[derive(Debug, Default)] + struct MockLanguageServer; + + #[lspower::async_trait] + impl lspower::LanguageServer for MockLanguageServer { + async fn initialize( + &self, + _params: lspower::lsp::InitializeParams, + ) -> lspower::jsonrpc::Result { + Ok(lspower::lsp::InitializeResult { + capabilities: lspower::lsp::ServerCapabilities::default(), + server_info: None, + }) + } + + async fn shutdown(&self) -> lspower::jsonrpc::Result<()> { + Ok(()) + } + } + + fn setup() -> Config { + let mut maybe_client: Option = None; + let (_service, _) = lspower::LspService::new(|client| { + maybe_client = Some(client); + MockLanguageServer::default() + }); + Config::new(maybe_client.unwrap()) } #[test] fn test_config_specifier_enabled() { - let mut config = Config::default(); + let config = setup(); let specifier = resolve_url("file:///a.ts").unwrap(); assert!(!config.specifier_enabled(&specifier)); config - .update_workspace(json!({ + .set_workspace_settings(json!({ "enable": true })) .expect("could not update"); assert!(config.specifier_enabled(&specifier)); - config - .update_specifier( - specifier.clone(), - json!({ - "enable": false - }), - ) - .expect("could not update"); - assert!(!config.specifier_enabled(&specifier)); } } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index 33705dbff0..ea781abdef 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -170,7 +170,7 @@ impl DiagnosticsServer { dirty = false; 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( &client, collection.clone(), @@ -314,7 +314,7 @@ async fn generate_lint_diagnostics( collection: Arc>, ) -> Result { 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 { let mut diagnostics_vec = Vec::new(); if workspace_settings.lint { @@ -487,7 +487,7 @@ async fn publish_diagnostics( if let Some(changes) = collection.take_changes() { for specifier in changes { let mut diagnostics: Vec = - if snapshot.config.workspace_settings.lint { + if snapshot.config.settings.workspace.lint { collection .get(&specifier, DiagnosticSource::DenoLint) .cloned() diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index f9bb52cbeb..d86ccefd52 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -38,6 +38,7 @@ use super::analysis::ResolvedDependency; use super::capabilities; use super::completions; use super::config::Config; +use super::config::ConfigSnapshot; use super::config::SETTINGS_SECTION; use super::diagnostics; use super::diagnostics::DiagnosticSource; @@ -77,7 +78,7 @@ pub struct LanguageServer(Arc>); #[derive(Debug, Clone, Default)] pub struct StateSnapshot { pub assets: Assets, - pub config: Config, + pub config: ConfigSnapshot, pub documents: DocumentCache, pub module_registries: registries::ModuleRegistry, pub performance: Performance, @@ -141,11 +142,12 @@ impl Inner { let ts_server = Arc::new(TsServer::new()); let performance = Performance::default(); let diagnostics_server = diagnostics::DiagnosticsServer::new(); + let config = Config::new(client.clone()); Self { assets: Default::default(), client, - config: Default::default(), + config, diagnostics_server, documents: Default::default(), maybe_config_uri: Default::default(), @@ -282,7 +284,7 @@ impl Inner { let navigation_tree: tsc::NavigationTree = self .ts_server .request( - self.snapshot(), + self.snapshot()?, tsc::RequestMethod::GetNavigationTree(specifier.clone()), ) .await?; @@ -294,16 +296,19 @@ impl Inner { } } - pub(crate) fn snapshot(&self) -> StateSnapshot { - StateSnapshot { + pub(crate) fn snapshot(&self) -> LspResult { + Ok(StateSnapshot { assets: self.assets.clone(), - config: self.config.clone(), + config: self.config.snapshot().map_err(|err| { + error!("{}", err); + LspError::internal_error() + })?, documents: self.documents.clone(), module_registries: self.module_registries.clone(), performance: self.performance.clone(), sources: self.sources.clone(), url_map: self.url_map.clone(), - } + }) } pub async fn update_import_map(&mut self) -> Result<(), AnyError> { @@ -311,7 +316,7 @@ impl Inner { let (maybe_import_map, maybe_root_uri) = { let config = &self.config; ( - config.workspace_settings.import_map.clone(), + config.get_workspace_settings().import_map, config.root_uri.clone(), ) }; @@ -357,10 +362,11 @@ impl Inner { } pub fn update_debug_flag(&self) -> bool { + let internal_debug = self.config.get_workspace_settings().internal_debug; logger::LSP_DEBUG_FLAG .compare_exchange( - !self.config.workspace_settings.internal_debug, - self.config.workspace_settings.internal_debug, + !internal_debug, + internal_debug, Ordering::Acquire, Ordering::Relaxed, ) @@ -369,8 +375,13 @@ impl Inner { async fn update_registries(&mut self) -> Result<(), AnyError> { let mark = self.performance.mark("update_registries", None::<()>); - for (registry, enabled) in - self.config.workspace_settings.suggest.imports.hosts.iter() + for (registry, enabled) in self + .config + .get_workspace_settings() + .suggest + .imports + .hosts + .iter() { if *enabled { info!("Enabling auto complete registry for: {}", registry); @@ -401,16 +412,14 @@ impl Inner { })); let (maybe_config, maybe_root_uri) = { let config = &self.config; - if config.workspace_settings.unstable { + let workspace_settings = config.get_workspace_settings(); + if workspace_settings.unstable { let unstable_libs = json!({ "lib": ["deno.ns", "deno.window", "deno.unstable"] }); tsconfig.merge(&unstable_libs); } - ( - config.workspace_settings.config.clone(), - config.root_uri.clone(), - ) + (workspace_settings.config, config.root_uri.clone()) }; if let Some(config_str) = &maybe_config { info!("Updating TypeScript configuration from: \"{}\"", config_str); @@ -443,7 +452,7 @@ impl Inner { } let _ok: bool = self .ts_server - .request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig)) + .request(self.snapshot()?, tsc::RequestMethod::Configure(tsconfig)) .await?; self.performance.measure(mark); Ok(()) @@ -464,7 +473,7 @@ impl Inner { return Ok(maybe_asset.clone()); } else { 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()); Ok(maybe_asset) } @@ -507,7 +516,10 @@ impl Inner { let config = &mut self.config; config.root_uri = params.root_uri; 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); } @@ -520,7 +532,7 @@ impl Inner { if capabilities.code_action_provider.is_some() { let fixable_diagnostics: Vec = self .ts_server - .request(self.snapshot(), tsc::RequestMethod::GetSupportedCodeFixes) + .request(self.snapshot()?, tsc::RequestMethod::GetSupportedCodeFixes) .await .map_err(|err| { error!("Unable to get fixable diagnostics: {}", err); @@ -592,27 +604,13 @@ impl Inner { let mark = self.performance.mark("did_open", Some(¶ms)); let specifier = self.url_map.normalize_url(¶ms.text_document.uri); - // we only query the individual resource file if the client supports it - // TODO(@kitsonk) workaround https://github.com/denoland/deno/issues/10603 - // if self.config.client_capabilities.workspace_configuration - // && !self.config.contains(&specifier) - // { - // if let Ok(value) = self - // .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 let Err(err) = self + .config + .update_specifier_settings(&specifier, ¶ms.text_document.uri) + .await + { + error!("Error updating specifier settings: {}", err); + } if params.text_document.uri.scheme() == "deno" { // 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)); if self.config.client_capabilities.workspace_configuration { - let specifiers: Vec = - self.config.specifier_settings.keys().cloned().collect(); - 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); - } - } + if let Err(err) = self.config.update_workspace_settings().await { + error!("Error updating workspace settings: {}", err); } } else if let Some(config) = params .settings @@ -713,7 +687,7 @@ impl Inner { .flatten() .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); } } @@ -804,7 +778,7 @@ impl Inner { let req = tsc::RequestMethod::GetNavigationTree(specifier); let navigation_tree: tsc::NavigationTree = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -900,7 +874,7 @@ impl Inner { )); let maybe_quick_info: Option = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Unable to get quick info: {}", err); @@ -977,7 +951,7 @@ impl Inner { codes, )); let actions: Vec = - match self.ts_server.request(self.snapshot(), req).await { + match self.ts_server.request(self.snapshot()?, req).await { Ok(items) => items, Err(err) => { // sometimes tsc reports errors when retrieving code actions @@ -1040,7 +1014,7 @@ impl Inner { )); let combined_code_actions: tsc::CombinedCodeActions = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Unable to get combined fix from TypeScript: {}", err); @@ -1074,7 +1048,7 @@ impl Inner { ) -> LspResult>> { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); if !self.config.specifier_enabled(&specifier) - || !self.config.workspace_settings.enabled_code_lens() + || !self.config.get_workspace_settings().enabled_code_lens() { return Ok(None); } @@ -1093,9 +1067,10 @@ impl Inner { let cl = Rc::new(RefCell::new(Vec::new())); navigation_tree.walk(&|i, mp| { let mut code_lenses = cl.borrow_mut(); + let workspace_settings = self.config.get_workspace_settings(); // TSC Implementations Code Lens - if self.config.workspace_settings.code_lens.implementations { + if workspace_settings.code_lens.implementations { let source = CodeLensSource::Implementations; match i.kind { tsc::ScriptElementKind::InterfaceElement => { @@ -1119,7 +1094,7 @@ impl Inner { } // TSC References Code Lens - if self.config.workspace_settings.code_lens.references { + if workspace_settings.code_lens.references { let source = CodeLensSource::References; if let Some(parent) = &mp { if parent.kind == tsc::ScriptElementKind::EnumElement { @@ -1128,12 +1103,7 @@ impl Inner { } match i.kind { tsc::ScriptElementKind::FunctionElement => { - if self - .config - .workspace_settings - .code_lens - .references_all_functions - { + if workspace_settings.code_lens.references_all_functions { code_lenses.push(i.to_code_lens( &line_index, &specifier, @@ -1214,12 +1184,14 @@ impl Inner { line_index.offset_tsc(params.range.start)?, )); let maybe_implementations: Option> = - self.ts_server.request(self.snapshot(), req).await.map_err( - |err| { + self + .ts_server + .request(self.snapshot()?, req) + .await + .map_err(|err| { error!("Error processing TypeScript request: {}", err); LspError::internal_error() - }, - )?; + })?; if let Some(implementations) = maybe_implementations { let mut locations = Vec::new(); for implementation in implementations { @@ -1292,13 +1264,14 @@ impl Inner { code_lens_data.specifier.clone(), line_index.offset_tsc(params.range.start)?, )); - let maybe_references: Option> = - self.ts_server.request(self.snapshot(), req).await.map_err( - |err| { - error!("Error processing TypeScript request: {}", err); - LspError::internal_error() - }, - )?; + let maybe_references: Option> = self + .ts_server + .request(self.snapshot()?, req) + .await + .map_err(|err| { + error!("Error processing TypeScript request: {}", err); + LspError::internal_error() + })?; if let Some(references) = maybe_references { let mut locations = Vec::new(); for reference in references { @@ -1408,7 +1381,7 @@ impl Inner { )); let maybe_document_highlights: Option> = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Unable to get document highlights from TypeScript: {}", err); @@ -1455,7 +1428,7 @@ impl Inner { )); let maybe_references: Option> = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Unable to get references from TypeScript: {}", err); @@ -1510,7 +1483,7 @@ impl Inner { )); let maybe_definition: Option = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|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( &specifier, ¶ms.text_document_position.position, - &self.snapshot(), + &self.snapshot()?, ) .await { @@ -1580,7 +1553,7 @@ impl Inner { )); let maybe_completion_info: Option = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Unable to get completion info from TypeScript: {}", err); @@ -1590,7 +1563,7 @@ impl Inner { if let Some(completions) = maybe_completion_info { let results = completions.as_completion_response( &line_index, - &self.config.workspace_settings.suggest, + &self.config.get_workspace_settings().suggest, &specifier, position, ); @@ -1618,13 +1591,14 @@ impl Inner { })?; if let Some(data) = data.tsc { let req = tsc::RequestMethod::GetCompletionDetails(data.into()); - let maybe_completion_info: Option = - self.ts_server.request(self.snapshot(), req).await.map_err( - |err| { - error!("Unable to get completion info from TypeScript: {}", err); - LspError::internal_error() - }, - )?; + let maybe_completion_info: Option = self + .ts_server + .request(self.snapshot()?, req) + .await + .map_err(|err| { + error!("Unable to get completion info from TypeScript: {}", err); + LspError::internal_error() + })?; if let Some(completion_info) = maybe_completion_info { completion_info.as_completion_item(¶ms) } else { @@ -1670,7 +1644,7 @@ impl Inner { )); let maybe_implementations: Option> = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1716,7 +1690,7 @@ impl Inner { let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let outlining_spans: Vec = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1776,7 +1750,7 @@ impl Inner { )); let incoming_calls: Vec = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1830,7 +1804,7 @@ impl Inner { )); let outgoing_calls: Vec = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1890,7 +1864,7 @@ impl Inner { let maybe_one_or_many: Option> = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -1970,7 +1944,7 @@ impl Inner { let maybe_locations: Option> = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2057,7 +2031,7 @@ impl Inner { let selection_range: tsc::SelectionRange = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2099,7 +2073,7 @@ impl Inner { )); let semantic_classification: tsc::Classifications = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2147,7 +2121,7 @@ impl Inner { )); let semantic_classification: tsc::Classifications = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver {}", err); @@ -2204,7 +2178,7 @@ impl Inner { )); let maybe_signature_help_items: Option = self .ts_server - .request(self.snapshot(), req) + .request(self.snapshot()?, req) .await .map_err(|err| { error!("Failed to request to tsserver: {}", err); diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index 15abfe6781..a6d4f2d297 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -38,6 +38,13 @@ where client .write_notification("textDocument/didOpen", params) .unwrap(); + + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); assert_eq!(method, "textDocument/publishDiagnostics"); let (method, _) = client.read_notification::().unwrap(); @@ -209,6 +216,13 @@ fn lsp_hover_disabled() { }), ) .unwrap(); + + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": false })) + .unwrap(); + let (maybe_res, maybe_err) = client .write_request( "textDocument/hover", @@ -453,6 +467,12 @@ fn lsp_hover_closed_document() { }), ) .unwrap(); + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); + client .write_notification( "textDocument/didOpen", @@ -466,6 +486,12 @@ fn lsp_hover_closed_document() { }), ) .unwrap(); + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); + let (method, _) = client.read_notification::().unwrap(); assert_eq!(method, "textDocument/publishDiagnostics"); let (method, _) = client.read_notification::().unwrap(); diff --git a/cli/tests/lsp/initialize_params.json b/cli/tests/lsp/initialize_params.json index b00e5720dd..98ec53aa6f 100644 --- a/cli/tests/lsp/initialize_params.json +++ b/cli/tests/lsp/initialize_params.json @@ -51,6 +51,10 @@ "willSaveWaitUntil": true, "didSave": true } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true } } } diff --git a/cli/tests/lsp/initialize_params_disabled.json b/cli/tests/lsp/initialize_params_disabled.json index ea12dfac58..d5e59770ad 100644 --- a/cli/tests/lsp/initialize_params_disabled.json +++ b/cli/tests/lsp/initialize_params_disabled.json @@ -51,6 +51,10 @@ "willSaveWaitUntil": true, "didSave": true } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true } } } diff --git a/cli/tests/lsp/initialize_params_registry.json b/cli/tests/lsp/initialize_params_registry.json index 38777b3d48..0a19c50ec8 100644 --- a/cli/tests/lsp/initialize_params_registry.json +++ b/cli/tests/lsp/initialize_params_registry.json @@ -53,6 +53,10 @@ "willSaveWaitUntil": true, "didSave": true } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true } } } diff --git a/cli/tests/lsp/initialize_params_unstable.json b/cli/tests/lsp/initialize_params_unstable.json index fd8ccdd83a..153e3aef15 100644 --- a/cli/tests/lsp/initialize_params_unstable.json +++ b/cli/tests/lsp/initialize_params_unstable.json @@ -51,6 +51,10 @@ "willSaveWaitUntil": true, "didSave": true } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true } } } From 47ec33eca7b71b7f53c826dc97f37da4d4700499 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 20 May 2021 21:02:39 +0800 Subject: [PATCH 30/53] fix(cli/tools/test_runner): --doc should not require permissions (#10719) --- cli/tests/integration_tests.rs | 2 -- cli/tools/test_runner.rs | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index ce12056988..aff073d0d2 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -24,7 +24,6 @@ use tokio::task::LocalSet; fn typecheck_declarations_ns() { let status = util::deno_cmd() .arg("test") - .arg("--allow-all") .arg("--doc") .arg(util::root_path().join("cli/dts/lib.deno.ns.d.ts")) .spawn() @@ -41,7 +40,6 @@ fn typecheck_declarations_unstable() { let status = util::deno_cmd() .arg("test") .arg("--doc") - .arg("--allow-all") .arg("--unstable") .arg(util::root_path().join("cli/dts/lib.deno.unstable.d.ts")) .spawn() diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index ddfad81996..c650b2d64d 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -338,9 +338,10 @@ pub async fn run_tests( let lines_regex = Regex::new(r"(?:\* ?)(?:\# ?)?(.*)")?; for specifier in &doc_modules { + let mut fetch_permissions = Permissions::allow_all(); let file = program_state .file_fetcher - .fetch(&specifier, &mut permissions.clone()) + .fetch(&specifier, &mut fetch_permissions) .await?; let parsed_module = From 25b784f00d40e20714709bf61e09046d4b9c8fd8 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Fri, 21 May 2021 07:35:37 +1000 Subject: [PATCH 31/53] chore(lsp): provide test for lsp deadlock issue (#10679) Resolves: #10587 --- .github/workflows/ci.yml | 2 +- cli/tests/integration_tests_lsp.rs | 148 +++++++++++++++- .../lsp/code_action_params_deadlock.json | 38 +++++ test_util/src/lsp.rs | 159 +++++++++++++----- 4 files changed, 301 insertions(+), 46 deletions(-) create mode 100644 cli/tests/lsp/code_action_params_deadlock.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ac23cab0f..406d1bf2e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -210,7 +210,7 @@ jobs: path: | ./target key: | - dummy # Cache never be created for this key. + s0mth1ng_rand0m # Cache never be created for this key. restore-keys: | a-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ hashFiles('Cargo.lock') }}- diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index a6d4f2d297..76041c502d 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -737,7 +737,7 @@ fn lsp_large_doc_changes() { .unwrap(); client .write_notification( - "textDocument/didChagne", + "textDocument/didChange", json!({ "textDocument": { "uri": "file:///a/file.ts", @@ -1420,6 +1420,152 @@ fn lsp_code_actions_deno_cache() { shutdown(&mut client); } +#[test] +fn lsp_code_actions_deadlock() { + let mut client = init("initialize_params.json"); + client + .write_notification( + "textDocument/didOpen", + load_fixture("did_open_params_large.json"), + ) + .unwrap(); + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/semanticTokens/full", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + for _ in 0..3 { + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + } + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 444, + "character": 11 + }, + "end": { + "line": 444, + "character": 14 + } + }, + "text": "+++" + } + ] + }), + ) + .unwrap(); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 445, + "character": 4 + }, + "end": { + "line": 445, + "character": 4 + } + }, + "text": "// " + } + ] + }), + ) + .unwrap(); + client + .write_notification( + "textDocument/didChange", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + "version": 2 + }, + "contentChanges": [ + { + "range": { + "start": { + "line": 477, + "character": 4 + }, + "end": { + "line": 477, + "character": 9 + } + }, + "text": "error" + } + ] + }), + ) + .unwrap(); + // diagnostics only trigger after changes have elapsed in a separate thread, + // so we need to delay the next messages a little bit to attempt to create a + // potential for a deadlock with the codeAction + std::thread::sleep(std::time::Duration::from_millis(50)); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + }, + "position": { + "line": 609, + "character": 33, + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/codeAction", + load_fixture("code_action_params_deadlock.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + + for _ in 0..3 { + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + } + + assert!(client.queue_is_empty()); + shutdown(&mut client); +} + #[test] fn lsp_completions() { let mut client = init("initialize_params.json"); diff --git a/cli/tests/lsp/code_action_params_deadlock.json b/cli/tests/lsp/code_action_params_deadlock.json new file mode 100644 index 0000000000..be0e317e1d --- /dev/null +++ b/cli/tests/lsp/code_action_params_deadlock.json @@ -0,0 +1,38 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts" + }, + "range": { + "start": { + "line": 441, + "character": 33 + }, + "end": { + "line": 441, + "character": 42 + } + }, + "context": { + "diagnostics": [ + { + "range": { + "start": { + "line": 441, + "character": 33 + }, + "end": { + "line": 441, + "character": 42 + } + }, + "severity": 1, + "code": 7031, + "source": "deno-ts", + "message": "Binding element 'debugFlag' implicitly has an 'any' type." + } + ], + "only": [ + "quickfix" + ] + } +} diff --git a/test_util/src/lsp.rs b/test_util/src/lsp.rs index 52099ebe3f..7b9fc59650 100644 --- a/test_util/src/lsp.rs +++ b/test_util/src/lsp.rs @@ -2,6 +2,7 @@ use super::new_deno_dir; +use anyhow::Result; use lazy_static::lazy_static; use regex::Regex; use serde::de; @@ -9,6 +10,7 @@ use serde::Deserialize; use serde::Serialize; use serde_json::json; use serde_json::Value; +use std::collections::VecDeque; use std::io; use std::io::Write; use std::path::Path; @@ -61,7 +63,7 @@ impl<'a> From<&'a [u8]> for LspMessage { } } -fn read_message(reader: &mut R) -> Result, anyhow::Error> +fn read_message(reader: &mut R) -> Result> where R: io::Read + io::BufRead, { @@ -86,8 +88,12 @@ where } pub struct LspClient { - reader: io::BufReader, child: Child, + reader: io::BufReader, + /// Used to hold pending messages that have come out of the expected sequence + /// by the harness user which will be sent first when trying to consume a + /// message before attempting to read a new message. + msg_queue: VecDeque, request_id: u64, start: Instant, writer: io::BufWriter, @@ -106,8 +112,51 @@ impl Drop for LspClient { } } +fn notification_result( + method: String, + maybe_params: Option, +) -> Result<(String, Option)> +where + R: de::DeserializeOwned, +{ + let maybe_params = match maybe_params { + Some(params) => Some(serde_json::from_value(params)?), + None => None, + }; + Ok((method, maybe_params)) +} + +fn request_result( + id: u64, + method: String, + maybe_params: Option, +) -> Result<(u64, String, Option)> +where + R: de::DeserializeOwned, +{ + let maybe_params = match maybe_params { + Some(params) => Some(serde_json::from_value(params)?), + None => None, + }; + Ok((id, method, maybe_params)) +} + +fn response_result( + maybe_result: Option, + maybe_error: Option, +) -> Result<(Option, Option)> +where + R: de::DeserializeOwned, +{ + let maybe_result = match maybe_result { + Some(result) => Some(serde_json::from_value(result)?), + None => None, + }; + Ok((maybe_result, maybe_error)) +} + impl LspClient { - pub fn new(deno_exe: &Path) -> Result { + pub fn new(deno_exe: &Path) -> Result { let deno_dir = new_deno_dir(); let mut child = Command::new(deno_exe) .env("DENO_DIR", deno_dir.path()) @@ -125,6 +174,7 @@ impl LspClient { Ok(Self { child, + msg_queue: VecDeque::new(), reader, request_id: 1, start: Instant::now(), @@ -136,49 +186,79 @@ impl LspClient { self.start.elapsed() } - fn read(&mut self) -> Result { + pub fn queue_is_empty(&self) -> bool { + self.msg_queue.is_empty() + } + + pub fn queue_len(&self) -> usize { + self.msg_queue.len() + } + + fn read(&mut self) -> Result { let msg_buf = read_message(&mut self.reader)?; let msg = LspMessage::from(msg_buf.as_slice()); Ok(msg) } - pub fn read_notification( - &mut self, - ) -> Result<(String, Option), anyhow::Error> + pub fn read_notification(&mut self) -> Result<(String, Option)> where R: de::DeserializeOwned, { - loop { - if let LspMessage::Notification(method, maybe_params) = self.read()? { - if let Some(p) = maybe_params { - let params = serde_json::from_value(p)?; - return Ok((method, Some(params))); - } else { - return Ok((method, None)); + if !self.msg_queue.is_empty() { + let mut msg_queue = VecDeque::new(); + loop { + match self.msg_queue.pop_front() { + Some(LspMessage::Notification(method, maybe_params)) => { + return notification_result(method, maybe_params) + } + Some(msg) => msg_queue.push_back(msg), + _ => break, } } + self.msg_queue = msg_queue; + } + + loop { + match self.read() { + Ok(LspMessage::Notification(method, maybe_params)) => { + return notification_result(method, maybe_params) + } + Ok(msg) => self.msg_queue.push_back(msg), + Err(err) => return Err(err), + } } } - pub fn read_request( - &mut self, - ) -> Result<(u64, String, Option), anyhow::Error> + pub fn read_request(&mut self) -> Result<(u64, String, Option)> where R: de::DeserializeOwned, { - loop { - if let LspMessage::Request(id, method, maybe_params) = self.read()? { - if let Some(p) = maybe_params { - let params = serde_json::from_value(p)?; - return Ok((id, method, Some(params))); - } else { - return Ok((id, method, None)); + if !self.msg_queue.is_empty() { + let mut msg_queue = VecDeque::new(); + loop { + match self.msg_queue.pop_front() { + Some(LspMessage::Request(id, method, maybe_params)) => { + return request_result(id, method, maybe_params) + } + Some(msg) => msg_queue.push_back(msg), + _ => break, } } + self.msg_queue = msg_queue; + } + + loop { + match self.read() { + Ok(LspMessage::Request(id, method, maybe_params)) => { + return request_result(id, method, maybe_params) + } + Ok(msg) => self.msg_queue.push_back(msg), + Err(err) => return Err(err), + } } } - fn write(&mut self, value: Value) -> Result<(), anyhow::Error> { + fn write(&mut self, value: Value) -> Result<()> { let value_str = value.to_string(); let msg = format!( "Content-Length: {}\r\n\r\n{}", @@ -194,7 +274,7 @@ impl LspClient { &mut self, method: S, params: V, - ) -> Result<(Option, Option), anyhow::Error> + ) -> Result<(Option, Option)> where S: AsRef, V: Serialize, @@ -209,24 +289,19 @@ impl LspClient { self.write(value)?; loop { - if let LspMessage::Response(id, result, error) = self.read()? { - assert_eq!(id, self.request_id); - self.request_id += 1; - if let Some(r) = result { - let result = serde_json::from_value(r)?; - return Ok((Some(result), error)); - } else { - return Ok((None, error)); + match self.read() { + Ok(LspMessage::Response(id, maybe_result, maybe_error)) => { + assert_eq!(id, self.request_id); + self.request_id += 1; + return response_result(maybe_result, maybe_error); } + Ok(msg) => self.msg_queue.push_back(msg), + Err(err) => return Err(err), } } } - pub fn write_response( - &mut self, - id: u64, - result: V, - ) -> Result<(), anyhow::Error> + pub fn write_response(&mut self, id: u64, result: V) -> Result<()> where V: Serialize, { @@ -238,11 +313,7 @@ impl LspClient { self.write(value) } - pub fn write_notification( - &mut self, - method: S, - params: V, - ) -> Result<(), anyhow::Error> + pub fn write_notification(&mut self, method: S, params: V) -> Result<()> where S: AsRef, V: Serialize, From 7b1fd3d146292df01f61e9eb0202779be8d7da33 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 21 May 2021 10:11:53 +0900 Subject: [PATCH 32/53] fix(runtime/http): fix empty blob response (#10689) --- cli/tests/unit/http_test.ts | 22 ++++++++++++++++++++++ runtime/js/40_http.js | 11 +++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cli/tests/unit/http_test.ts b/cli/tests/unit/http_test.ts index d4c35545f5..9560394f04 100644 --- a/cli/tests/unit/http_test.ts +++ b/cli/tests/unit/http_test.ts @@ -317,3 +317,25 @@ unitTest( await promise; }, ); + +unitTest( + { perms: { net: true } }, + async function httpServerEmptyBlobResponse() { + const promise = (async () => { + const listener = Deno.listen({ port: 4501 }); + const conn = await listener.accept(); + const httpConn = Deno.serveHttp(conn); + const event = await httpConn.nextRequest(); + assert(event); + const { respondWith } = event; + await respondWith(new Response(new Blob([]))); + httpConn.close(); + listener.close(); + })(); + + const resp = await fetch("http://127.0.0.1:4501/"); + const respBody = await resp.text(); + assertEquals("", respBody); + await promise; + }, +); diff --git a/runtime/js/40_http.js b/runtime/js/40_http.js index eb4d214ca4..d4b658314a 100644 --- a/runtime/js/40_http.js +++ b/runtime/js/40_http.js @@ -132,10 +132,13 @@ } else { const reader = innerResp.body.stream.getReader(); const r1 = await reader.read(); - if (r1.done) throw new TypeError("Unreachable"); - respBody = r1.value; - const r2 = await reader.read(); - if (!r2.done) throw new TypeError("Unreachable"); + if (r1.done) { + respBody = new Uint8Array(0); + } else { + respBody = r1.value; + const r2 = await reader.read(); + if (!r2.done) throw new TypeError("Unreachable"); + } } } else { innerResp.body.streamOrStatic.consumed = true; From 195808a53879d73a4d79d0e2252783de64acea48 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 21 May 2021 22:57:00 +0900 Subject: [PATCH 33/53] fix(test): ensure coverage dir exists (#10717) --- cli/main.rs | 1 + cli/tests/integration_tests.rs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/main.rs b/cli/main.rs index 3b147ffcc3..aa144d1ded 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -925,6 +925,7 @@ async fn test_command( concurrent_jobs: usize, ) -> Result<(), AnyError> { if let Some(ref coverage_dir) = flags.coverage_dir { + std::fs::create_dir_all(&coverage_dir)?; env::set_var( "DENO_UNSTABLE_COVERAGE_DIR", PathBuf::from(coverage_dir).canonicalize()?, diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index aff073d0d2..d3e5784765 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -4425,12 +4425,13 @@ console.log("finish"); #[test] fn branch() { let tempdir = TempDir::new().expect("tempdir fail"); + let tempdir = tempdir.path().join("cov"); let status = util::deno_cmd() .current_dir(util::root_path()) .arg("test") .arg("--quiet") .arg("--unstable") - .arg(format!("--coverage={}", tempdir.path().to_str().unwrap())) + .arg(format!("--coverage={}", tempdir.to_str().unwrap())) .arg("cli/tests/coverage/branch_test.ts") .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::inherit()) @@ -4444,7 +4445,7 @@ console.log("finish"); .arg("coverage") .arg("--quiet") .arg("--unstable") - .arg(format!("{}/", tempdir.path().to_str().unwrap())) + .arg(format!("{}/", tempdir.to_str().unwrap())) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::inherit()) .output() @@ -4473,7 +4474,7 @@ console.log("finish"); .arg("--quiet") .arg("--unstable") .arg("--lcov") - .arg(format!("{}/", tempdir.path().to_str().unwrap())) + .arg(format!("{}/", tempdir.to_str().unwrap())) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::inherit()) .output() From 859a88ba441796fc9199257dd8d51709f8214faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BF=B7=E6=B8=A1?= Date: Fri, 21 May 2021 22:00:16 +0800 Subject: [PATCH 34/53] fix(docs): rename read to readSync (#10732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 迷渡 --- cli/dts/lib.deno.ns.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index c56371a23b..58782e1b0c 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -322,9 +322,9 @@ declare namespace Deno { export interface ReaderSync { /** Reads up to `p.byteLength` bytes into `p`. It resolves to the number * of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error - * encountered. Even if `read()` returns `n` < `p.byteLength`, it may use + * encountered. Even if `readSync()` returns `n` < `p.byteLength`, it may use * all of `p` as scratch space during the call. If some data is available - * but not `p.byteLength` bytes, `read()` conventionally returns what is + * but not `p.byteLength` bytes, `readSync()` conventionally returns what is * available instead of waiting for more. * * When `readSync()` encounters end-of-file condition, it returns EOF From f3d5e74d2d36ed75fc2b8945def3efe230f754f5 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 22 May 2021 18:08:24 +0200 Subject: [PATCH 35/53] chore: update wpt --- test_util/wpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_util/wpt b/test_util/wpt index 25303eda8c..922dd70162 160000 --- a/test_util/wpt +++ b/test_util/wpt @@ -1 +1 @@ -Subproject commit 25303eda8c01d5be0483e9288f67257119b9b49d +Subproject commit 922dd701623a70e61c713ca97218884f41fcd66f From 43417b4660b3811bab3b12ca79bb0821c17030c3 Mon Sep 17 00:00:00 2001 From: CGQAQ Date: Mon, 24 May 2021 15:55:44 +0800 Subject: [PATCH 36/53] fix(cli/upgrade): modify download size paddings (#10639) --- cli/tools/upgrade.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs index 7fadd59b06..75e84b9960 100644 --- a/cli/tools/upgrade.rs +++ b/cli/tools/upgrade.rs @@ -184,7 +184,7 @@ async fn download_package( print!("\u{001b}[1G\u{001b}[2K"); } print!( - "{:0>4.1} MiB / {:.1} MiB ({:0>5.1}%)", + "{:>4.1} MiB / {:.1} MiB ({:^5.1}%)", current_size / MEBIBYTE, total_size / MEBIBYTE, (current_size / total_size) * 100.0, From 7b6bba5d3a47522aa9491a833c3cd4cbd8fc3d04 Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Mon, 24 May 2021 23:26:04 +0800 Subject: [PATCH 37/53] fix(cli/test): don't use reserved symbol `:` in specifier (#10751) --- cli/tests/integration_tests.rs | 4 ---- cli/tests/test/doc.out | 4 ++-- cli/tools/test_runner.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index d3e5784765..dcde0d057b 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -18,8 +18,6 @@ use tempfile::TempDir; use test_util as util; use tokio::task::LocalSet; -// TODO(caspervonb): investiate why this fails on Windows. -#[cfg(unix)] #[test] fn typecheck_declarations_ns() { let status = util::deno_cmd() @@ -33,8 +31,6 @@ fn typecheck_declarations_ns() { assert!(status.success()); } -// TODO(caspervonb): investiate why this fails on Windows. -#[cfg(unix)] #[test] fn typecheck_declarations_unstable() { let status = util::deno_cmd() diff --git a/cli/tests/test/doc.out b/cli/tests/test/doc.out index 0f3d02aa33..18f81d3a30 100644 --- a/cli/tests/test/doc.out +++ b/cli/tests/test/doc.out @@ -1,5 +1,5 @@ -Check [WILDCARD]/doc.ts:2-7 +Check [WILDCARD]/doc.ts$2-7 error: TS2367 [ERROR]: This condition will always return 'false' since the types 'string' and 'number' have no overlap. console.assert(example() == 42); ~~~~~~~~~~~~~~~ - at [WILDCARD]/doc.ts:2-7.ts:3:16 + at [WILDCARD]/doc.ts$2-7.ts:3:16 diff --git a/cli/tools/test_runner.rs b/cli/tools/test_runner.rs index c650b2d64d..f112eef16c 100644 --- a/cli/tools/test_runner.rs +++ b/cli/tools/test_runner.rs @@ -379,7 +379,7 @@ pub async fn run_tests( let location = parsed_module.get_location(&span); let specifier = deno_core::resolve_url_or_path(&format!( - "{}:{}-{}", + "{}${}-{}", location.filename, location.line, location.line + element.as_str().split('\n').count(), From 6bbefdff395072503677e3b11dd8cba7d59efa18 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 25 May 2021 12:34:01 +1000 Subject: [PATCH 38/53] feat(lsp): diagnostics for deno types and triple-slash refs (#10699) Fixes #9823 --- cli/lsp/analysis.rs | 79 +++++++++-- cli/lsp/diagnostics.rs | 123 +++++++++++------- cli/module_graph.rs | 54 +++++--- cli/tests/integration_tests_lsp.rs | 45 +++++++ cli/tests/lsp/diagnostics_deno_types.json | 101 ++++++++++++++ cli/tests/lsp/did_open_params_deno_types.json | 8 ++ 6 files changed, 335 insertions(+), 75 deletions(-) create mode 100644 cli/tests/lsp/diagnostics_deno_types.json create mode 100644 cli/tests/lsp/did_open_params_deno_types.json diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 19ee60d4c0..831ad9b292 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -140,6 +140,7 @@ pub struct Dependency { pub maybe_code: Option, pub maybe_code_specifier_range: Option, pub maybe_type: Option, + pub maybe_type_specifier_range: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -259,13 +260,24 @@ pub fn analyze_dependencies( // Parse leading comments for supported triple slash references. for comment in parsed_module.get_leading_comments().iter() { - if let Some(ts_reference) = parse_ts_reference(&comment.text) { + if let Some((ts_reference, span)) = parse_ts_reference(&comment) { + let loc = parsed_module.source_map.lookup_char_pos(span.lo); match ts_reference { TypeScriptReference::Path(import) => { let dep = dependencies.entry(import.clone()).or_default(); let resolved_import = resolve_import(&import, specifier, maybe_import_map); dep.maybe_code = Some(resolved_import); + dep.maybe_code_specifier_range = Some(Range { + start: Position { + line: (loc.line - 1) as u32, + character: loc.col_display as u32, + }, + end: Position { + line: (loc.line - 1) as u32, + character: (loc.col_display + import.chars().count() + 2) as u32, + }, + }); } TypeScriptReference::Types(import) => { let resolved_import = @@ -273,11 +285,20 @@ pub fn analyze_dependencies( if media_type == &MediaType::JavaScript || media_type == &MediaType::Jsx { - maybe_type = Some(resolved_import) - } else { - let dep = dependencies.entry(import).or_default(); - dep.maybe_type = Some(resolved_import); + maybe_type = Some(resolved_import.clone()); } + let dep = dependencies.entry(import.clone()).or_default(); + dep.maybe_type = Some(resolved_import); + dep.maybe_type_specifier_range = Some(Range { + start: Position { + line: (loc.line - 1) as u32, + character: loc.col_display as u32, + }, + end: Position { + line: (loc.line - 1) as u32, + character: (loc.col_display + import.chars().count() + 2) as u32, + }, + }); } } } @@ -294,7 +315,13 @@ pub fn analyze_dependencies( let maybe_resolved_type_dependency = // Check for `@deno-types` pragmas that affect the import if let Some(comment) = desc.leading_comments.last() { - parse_deno_types(&comment.text).as_ref().map(|deno_types| resolve_import(deno_types, specifier, maybe_import_map)) + parse_deno_types(&comment).as_ref().map(|(deno_types, span)| { + ( + resolve_import(deno_types, specifier, maybe_import_map), + deno_types.clone(), + parsed_module.source_map.lookup_char_pos(span.lo) + ) + }) } else { None }; @@ -304,6 +331,17 @@ pub fn analyze_dependencies( match desc.kind { swc_ecmascript::dep_graph::DependencyKind::ExportType | swc_ecmascript::dep_graph::DependencyKind::ImportType => { + dep.maybe_type_specifier_range = Some(Range { + start: Position { + line: (desc.specifier_line - 1) as u32, + character: desc.specifier_col as u32, + }, + end: Position { + line: (desc.specifier_line - 1) as u32, + character: (desc.specifier_col + desc.specifier.chars().count() + 2) + as u32, + }, + }); dep.maybe_type = Some(resolved_import) } _ => { @@ -321,8 +359,22 @@ pub fn analyze_dependencies( dep.maybe_code = Some(resolved_import); } } - if maybe_resolved_type_dependency.is_some() && dep.maybe_type.is_none() { - dep.maybe_type = maybe_resolved_type_dependency; + if dep.maybe_type.is_none() { + if let Some((resolved_dependency, specifier, loc)) = + maybe_resolved_type_dependency + { + dep.maybe_type_specifier_range = Some(Range { + start: Position { + line: (loc.line - 1) as u32, + character: (loc.col_display + 1) as u32, + }, + end: Position { + line: (loc.line - 1) as u32, + character: (loc.col_display + 1 + specifier.chars().count()) as u32, + }, + }); + dep.maybe_type = Some(resolved_dependency); + } } } @@ -723,6 +775,16 @@ mod tests { character: 58, } }), + maybe_type_specifier_range: Some(Range { + start: Position { + line: 7, + character: 20, + }, + end: Position { + line: 7, + character: 62, + } + }) }) ); assert_eq!( @@ -743,6 +805,7 @@ mod tests { character: 50, } }), + maybe_type_specifier_range: None, }) ); } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index ea781abdef..9b9035ac5f 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -1,13 +1,16 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use super::analysis; +use super::documents::DocumentCache; use super::language_server; +use super::sources::Sources; use super::tsc; use crate::diagnostics; use crate::media_type::MediaType; use crate::tokio_util::create_basic_runtime; +use analysis::ResolvedDependency; use deno_core::error::anyhow; use deno_core::error::AnyError; use deno_core::resolve_url; @@ -392,6 +395,64 @@ async fn generate_ts_diagnostics( Ok(diagnostics_vec) } +fn diagnose_dependency( + diagnostics: &mut Vec, + documents: &DocumentCache, + sources: &Sources, + maybe_dependency: &Option, + maybe_range: &Option, +) { + if let (Some(dep), Some(range)) = (maybe_dependency, *maybe_range) { + match dep { + analysis::ResolvedDependency::Err(err) => { + diagnostics.push(lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Error), + code: Some(err.as_code()), + code_description: None, + source: Some("deno".to_string()), + message: err.to_string(), + related_information: None, + tags: None, + data: None, + }) + } + analysis::ResolvedDependency::Resolved(specifier) => { + if !(documents.contains_key(&specifier) + || sources.contains_key(&specifier)) + { + let (code, message) = match specifier.scheme() { + "file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)), + "data" => (Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string()), + "blob" => (Some(lsp::NumberOrString::String("no-cache-blob".to_string())), "Uncached blob URL.".to_string()), + _ => (Some(lsp::NumberOrString::String("no-cache".to_string())), format!("Uncached or missing remote URL: \"{}\".", specifier)), + }; + diagnostics.push(lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Error), + code, + source: Some("deno".to_string()), + message, + data: Some(json!({ "specifier": specifier })), + ..Default::default() + }); + } else if sources.contains_key(&specifier) { + if let Some(message) = sources.get_maybe_warning(&specifier) { + diagnostics.push(lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("deno-warn".to_string())), + source: Some("deno".to_string()), + message, + ..Default::default() + }) + } + } + } + } + } +} + /// Generate diagnostics for dependencies of a module, attempting to resolve /// dependencies on the local file system or in the DENO_DIR cache. async fn generate_deps_diagnostics( @@ -417,54 +478,20 @@ async fn generate_deps_diagnostics( let mut diagnostics = Vec::new(); if let Some(dependencies) = documents.dependencies(specifier) { for (_, dependency) in dependencies { - // TODO(@kitsonk) add diagnostics for maybe_type dependencies - if let (Some(code), Some(range)) = - (dependency.maybe_code, dependency.maybe_code_specifier_range) - { - match code { - analysis::ResolvedDependency::Err(err) => diagnostics.push(lsp::Diagnostic { - range, - severity: Some(lsp::DiagnosticSeverity::Error), - code: Some(err.as_code()), - code_description: None, - source: Some("deno".to_string()), - message: err.to_string(), - related_information: None, - tags: None, - data: None, - }), - analysis::ResolvedDependency::Resolved(specifier) => { - if !(documents.contains_key(&specifier) || sources.contains_key(&specifier)) { - let (code, message) = match specifier.scheme() { - "file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)), - "data" => (Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string()), - "blob" => (Some(lsp::NumberOrString::String("no-cache-blob".to_string())), "Uncached blob URL.".to_string()), - _ => (Some(lsp::NumberOrString::String("no-cache".to_string())), format!("Uncached or missing remote URL: \"{}\".", specifier)), - }; - diagnostics.push(lsp::Diagnostic { - range, - severity: Some(lsp::DiagnosticSeverity::Error), - code, - source: Some("deno".to_string()), - message, - data: Some(json!({ "specifier": specifier })), - ..Default::default() - }); - } else if sources.contains_key(&specifier) { - if let Some(message) = sources.get_maybe_warning(&specifier) { - diagnostics.push(lsp::Diagnostic { - range, - severity: Some(lsp::DiagnosticSeverity::Warning), - code: Some(lsp::NumberOrString::String("deno-warn".to_string())), - source: Some("deno".to_string()), - message, - ..Default::default() - }) - } - } - }, - } - } + diagnose_dependency( + &mut diagnostics, + &documents, + &sources, + &dependency.maybe_code, + &dependency.maybe_code_specifier_range, + ); + diagnose_dependency( + &mut diagnostics, + &documents, + &sources, + &dependency.maybe_type, + &dependency.maybe_type_specifier_range, + ); } } diagnostics_vec.push((specifier.clone(), version, diagnostics)); diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 8613be1e7b..368df0a742 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -52,6 +52,9 @@ use std::result; use std::sync::Arc; use std::sync::Mutex; use std::time::Instant; +use swc_common::comments::Comment; +use swc_common::BytePos; +use swc_common::Span; lazy_static::lazy_static! { /// Matched the `@deno-types` pragma. @@ -188,35 +191,48 @@ pub enum TypeScriptReference { Types(String), } +fn match_to_span(comment: &Comment, m: ®ex::Match) -> Span { + Span { + lo: comment.span.lo + BytePos((m.start() + 1) as u32), + hi: comment.span.lo + BytePos((m.end() + 1) as u32), + ctxt: comment.span.ctxt, + } +} + /// Determine if a comment contains a triple slash reference and optionally /// return its kind and value. -pub fn parse_ts_reference(comment: &str) -> Option { - if !TRIPLE_SLASH_REFERENCE_RE.is_match(comment) { +pub fn parse_ts_reference( + comment: &Comment, +) -> Option<(TypeScriptReference, Span)> { + if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) { None - } else if let Some(captures) = PATH_REFERENCE_RE.captures(comment) { - Some(TypeScriptReference::Path( - captures.get(1).unwrap().as_str().to_string(), + } else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) { + let m = captures.get(1).unwrap(); + Some(( + TypeScriptReference::Path(m.as_str().to_string()), + match_to_span(comment, &m), )) } else { - TYPES_REFERENCE_RE.captures(comment).map(|captures| { - TypeScriptReference::Types(captures.get(1).unwrap().as_str().to_string()) + TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| { + let m = captures.get(1).unwrap(); + ( + TypeScriptReference::Types(m.as_str().to_string()), + match_to_span(comment, &m), + ) }) } } /// Determine if a comment contains a `@deno-types` pragma and optionally return /// its value. -pub fn parse_deno_types(comment: &str) -> Option { - if let Some(captures) = DENO_TYPES_RE.captures(comment) { - if let Some(m) = captures.get(1) { - Some(m.as_str().to_string()) - } else if let Some(m) = captures.get(2) { - Some(m.as_str().to_string()) - } else { - panic!("unreachable"); - } +pub fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> { + let captures = DENO_TYPES_RE.captures(&comment.text)?; + if let Some(m) = captures.get(1) { + Some((m.as_str().to_string(), match_to_span(comment, &m))) + } else if let Some(m) = captures.get(2) { + Some((m.as_str().to_string(), match_to_span(comment, &m))) } else { - None + unreachable!(); } } @@ -327,7 +343,7 @@ impl Module { // parse out any triple slash references for comment in parsed_module.get_leading_comments().iter() { - if let Some(ts_reference) = parse_ts_reference(&comment.text) { + if let Some((ts_reference, _)) = parse_ts_reference(&comment) { let location = parsed_module.get_location(&comment.span); match ts_reference { TypeScriptReference::Path(import) => { @@ -392,7 +408,7 @@ impl Module { // Parse out any `@deno-types` pragmas and modify dependency let maybe_type = if !desc.leading_comments.is_empty() { let comment = desc.leading_comments.last().unwrap(); - if let Some(deno_types) = parse_deno_types(&comment.text).as_ref() { + if let Some((deno_types, _)) = parse_deno_types(&comment).as_ref() { Some(self.resolve_import(deno_types, Some(location.clone()))?) } else { None diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index 76041c502d..8e04cbb95d 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1398,6 +1398,11 @@ fn lsp_code_actions_deno_cache() { } })) .unwrap(); + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); let (method, _) = client.read_notification::().unwrap(); assert_eq!(method, "textDocument/publishDiagnostics"); let (method, _) = client.read_notification::().unwrap(); @@ -1851,6 +1856,46 @@ fn lsp_diagnostics_warn() { shutdown(&mut client); } +#[test] +fn lsp_diagnostics_deno_types() { + let mut client = init("initialize_params.json"); + client + .write_notification( + "textDocument/didOpen", + load_fixture("did_open_params_deno_types.json"), + ) + .unwrap(); + let (id, method, _) = client.read_request::().unwrap(); + assert_eq!(method, "workspace/configuration"); + client + .write_response(id, json!({ "enable": true })) + .unwrap(); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/documentSymbol", + json!({ + "textDocument": { + "uri": "file:///a/file.ts" + } + }), + ) + .unwrap(); + assert!(maybe_res.is_some()); + assert!(maybe_err.is_none()); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, _) = client.read_notification::().unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + let (method, maybe_params) = client + .read_notification::() + .unwrap(); + assert_eq!(method, "textDocument/publishDiagnostics"); + assert!(maybe_params.is_some()); + let params = maybe_params.unwrap(); + assert_eq!(params.diagnostics.len(), 5); + shutdown(&mut client); +} + #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct PerformanceAverage { diff --git a/cli/tests/lsp/diagnostics_deno_types.json b/cli/tests/lsp/diagnostics_deno_types.json new file mode 100644 index 0000000000..f33945a599 --- /dev/null +++ b/cli/tests/lsp/diagnostics_deno_types.json @@ -0,0 +1,101 @@ +{ + "uri": "file:///a/file.ts", + "diagnostics": [ + { + "range": { + "start": { + "line": 0, + "character": 21 + }, + "end": { + "line": 0, + "character": 51 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://example.com/a/b.d.ts\".", + "data": { + "specifier": "https://example.com/a/b.d.ts" + } + }, + { + "range": { + "start": { + "line": 7, + "character": 19 + }, + "end": { + "line": 7, + "character": 47 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://example.com/a/e.js\".", + "data": { + "specifier": "https://example.com/a/e.js" + } + }, + { + "range": { + "start": { + "line": 6, + "character": 16 + }, + "end": { + "line": 6, + "character": 44 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://example.com/a/e.d.ts\".", + "data": { + "specifier": "https://example.com/a/e.d.ts" + } + }, + { + "range": { + "start": { + "line": 4, + "character": 19 + }, + "end": { + "line": 4, + "character": 47 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://example.com/a/d.js\".", + "data": { + "specifier": "https://example.com/a/d.js" + } + }, + { + "range": { + "start": { + "line": 3, + "character": 15 + }, + "end": { + "line": 3, + "character": 43 + } + }, + "severity": 1, + "code": "no-cache", + "source": "deno", + "message": "Uncached or missing remote URL: \"https://example.com/a/d.d.ts\".", + "data": { + "specifier": "https://example.com/a/d.d.ts" + } + } + ], + "version": 1 +} diff --git a/cli/tests/lsp/did_open_params_deno_types.json b/cli/tests/lsp/did_open_params_deno_types.json new file mode 100644 index 0000000000..6f085d0454 --- /dev/null +++ b/cli/tests/lsp/did_open_params_deno_types.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "/// \n/// Date: Tue, 25 May 2021 21:01:15 +0800 Subject: [PATCH 39/53] docs(cli/dts): tag test permission example as typescript (#10753) --- cli/dts/lib.deno.unstable.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index c8e2f42074..0e54535541 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -1269,7 +1269,7 @@ declare namespace Deno { * }); * ``` * - * ``` + * ```ts * import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; * * Deno.test({ From 18a02c0f7923a9d23c6dc5ac1c3a0dfe03d03f10 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Tue, 25 May 2021 08:01:49 -0500 Subject: [PATCH 40/53] cleanup(test_plugin): use else if statement (#10718) --- test_plugin/tests/test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_plugin/tests/test.js b/test_plugin/tests/test.js index 5e0e6d5a48..2a2fa66b3a 100644 --- a/test_plugin/tests/test.js +++ b/test_plugin/tests/test.js @@ -9,8 +9,7 @@ let filenamePrefix = "lib"; if (Deno.build.os === "windows") { filenameSuffix = ".dll"; filenamePrefix = ""; -} -if (Deno.build.os === "darwin") { +} else if (Deno.build.os === "darwin") { filenameSuffix = ".dylib"; } From 323fa5272db8f70e543507e7aaa24e2a19e090ac Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 25 May 2021 22:35:17 +0900 Subject: [PATCH 41/53] fix(extension/file): update File constructor following the spec (#10760) --- cli/tests/unit/file_test.ts | 4 ---- extensions/file/01_file.js | 2 +- tools/wpt/expectation.json | 37 ++++++++++++---------------------- tools/wpt/testharnessreport.js | 9 +++++++++ 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/cli/tests/unit/file_test.ts b/cli/tests/unit/file_test.ts index f1db0d4965..f4bb0bb4de 100644 --- a/cli/tests/unit/file_test.ts +++ b/cli/tests/unit/file_test.ts @@ -88,10 +88,6 @@ unitTest(function fileUsingFileName(): void { testSecondArgument("dummy", "dummy"); }); -unitTest(function fileUsingSpecialCharacterInFileName(): void { - testSecondArgument("dummy/foo", "dummy:foo"); -}); - unitTest(function fileUsingNullFileName(): void { testSecondArgument(null, "null"); }); diff --git a/extensions/file/01_file.js b/extensions/file/01_file.js index cbbf98e7d8..c34561b5e8 100644 --- a/extensions/file/01_file.js +++ b/extensions/file/01_file.js @@ -371,7 +371,7 @@ super(fileBits, options); /** @type {string} */ - this[_Name] = fileName.replaceAll("/", ":"); + this[_Name] = fileName; if (options.lastModified === undefined) { /** @type {number} */ this[_LastModfied] = new Date().getTime(); diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index 4d6016104e..7843eefc6f 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -374,7 +374,6 @@ "getReader({mode: \"byob\"}) throws on non-bytes streams", "ReadableStream with byte source can be constructed with no errors", "getReader({mode}) must perform ToString()", - "ReadableStream with byte source: autoAllocateChunkSize cannot be 0", "ReadableStreamBYOBReader can be constructed directly", "ReadableStreamBYOBReader constructor requires a ReadableStream argument", "ReadableStreamBYOBReader constructor requires an unlocked ReadableStream", @@ -483,7 +482,6 @@ "writable-streams": { "aborting.any.html": false, "bad-strategies.any.html": [ - "reject any non-function value for strategy.size", "Writable stream: invalid size beats invalid highWaterMark" ], "bad-underlying-sinks.any.html": true, @@ -491,8 +489,6 @@ "close.any.html": false, "constructor.any.html": [ "underlyingSink argument should be converted after queuingStrategy argument", - "WritableStreamDefaultController constructor should throw", - "WritableStreamDefaultController constructor should throw when passed an initialised WritableStream", "WritableStreamDefaultWriter should throw unless passed a WritableStream" ], "count-queuing-strategy.any.html": true, @@ -522,9 +518,7 @@ "Performance interface: operation mark(DOMString, optional PerformanceMarkOptions)", "Performance interface: operation clearMarks(optional DOMString)", "Performance interface: operation measure(DOMString, optional (DOMString or PerformanceMeasureOptions), optional DOMString)", - "Performance interface: operation clearMeasures(optional DOMString)", - "Performance interface: calling mark(DOMString, optional PerformanceMarkOptions) on performance with too few arguments must throw TypeError", - "Performance interface: calling measure(DOMString, optional (DOMString or PerformanceMeasureOptions), optional DOMString) on performance with too few arguments must throw TypeError" + "Performance interface: operation clearMeasures(optional DOMString)" ], "mark-entry-constructor.any.html": true, "mark-errors.any.html": true, @@ -583,7 +577,10 @@ "toString.any.html": true, "type.tentative.any.html": false, "constructor-shared.tentative.any.html": true, - "constructor-types.tentative.any.html": false + "constructor-types.tentative.any.html": [ + "Zero minimum", + "Non-zero minimum" + ] }, "module": { "constructor.any.html": true, @@ -603,10 +600,12 @@ "toString.any.html": true, "constructor-reftypes.tentative.any.html": [ "initialize externref table with default value", - "initialize anyfunc table with default value", - "initialize anyfunc table with a bad default value" + "initialize anyfunc table with default value" + ], + "constructor-types.tentative.any.html": [ + "Zero minimum", + "Non-zero minimum" ], - "constructor-types.tentative.any.html": false, "grow-reftypes.tentative.any.html": false, "set-reftypes.tentative.any.html": false, "type.tentative.any.html": false @@ -670,6 +669,7 @@ "URL interface: attribute searchParams", "URL interface: attribute hash", "URL interface: operation toJSON()", + "URL interface: legacy window alias", "Stringification of new URL(\"http://foo\")", "URLSearchParams interface: operation append(USVString, USVString)", "URLSearchParams interface: operation delete(USVString)", @@ -683,12 +683,6 @@ "Stringification of new URLSearchParams(\"hi=there&thank=you\")" ], "url-constructor.any.html": [ - "Parsing: against ", - "Parsing: against ", - "Parsing: against ", - "Parsing: against ", - "Parsing: against ", - "Parsing: against ", "Parsing: against ", "Parsing: against ", "Parsing: against ", @@ -779,11 +773,7 @@ "api": { "request": { "request-init-002.any.html": true, - "request-init-stream.any.html": [ - "Constructing a Request with a Request on which body.getReader() is called", - "Constructing a Request with a Request on which body.getReader().read() is called", - "Constructing a Request with a Request on which read() and releaseLock() are called" - ], + "request-init-stream.any.html": true, "request-consume-empty.any.html": [ "Consume empty FormData request body as text" ], @@ -979,7 +969,6 @@ "Response interface: operation formData()", "Response interface: operation json()", "Response interface: operation text()", - "Response interface: calling redirect(USVString, optional unsigned short) on new Response() with too few arguments must throw TypeError", "Window interface: operation fetch(RequestInfo, optional RequestInit)" ] }, @@ -1110,4 +1099,4 @@ "set.any.html": true } } -} \ No newline at end of file +} diff --git a/tools/wpt/testharnessreport.js b/tools/wpt/testharnessreport.js index d3e7833767..b1852a4886 100644 --- a/tools/wpt/testharnessreport.js +++ b/tools/wpt/testharnessreport.js @@ -13,3 +13,12 @@ window.add_result_callback(({ message, name, stack, status }) => { window.add_completion_callback((_tests, _harnessStatus) => { Deno.exit(0); }); + +globalThis.document = { + // document.body shim for FileAPI/file/File-constructor.any.html test + body: { + toString() { + return "[object HTMLBodyElement]"; + }, + }, +}; From 8a19f28a005083bec61816f171482f538464043d Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Wed, 26 May 2021 07:15:09 +0900 Subject: [PATCH 42/53] feat(lsp): show hints from `deno_lint` in addition to messages (#10739) --- cli/lsp/analysis.rs | 107 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 831ad9b292..9a1a2d5077 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -60,6 +60,7 @@ lazy_static::lazy_static! { /// Category of self-generated diagnostic messages (those not coming from) /// TypeScript. +#[derive(Debug, PartialEq, Eq)] pub enum Category { /// A lint diagnostic, where the first element is the message. Lint { @@ -70,6 +71,7 @@ pub enum Category { } /// A structure to hold a reference to a diagnostic message. +#[derive(Debug, PartialEq, Eq)] pub struct Reference { category: Category, range: Range, @@ -78,13 +80,24 @@ pub struct Reference { impl Reference { pub fn to_diagnostic(&self) -> lsp::Diagnostic { match &self.category { - Category::Lint { message, code, .. } => lsp::Diagnostic { + Category::Lint { + message, + code, + hint, + } => lsp::Diagnostic { range: self.range, severity: Some(lsp::DiagnosticSeverity::Warning), code: Some(lsp::NumberOrString::String(code.to_string())), code_description: None, source: Some("deno-lint".to_string()), - message: message.to_string(), + message: { + let mut msg = message.to_string(); + if let Some(hint) = hint { + msg.push('\n'); + msg.push_str(hint); + } + msg + }, related_information: None, tags: None, // we should tag unused code data: None, @@ -702,6 +715,64 @@ mod tests { use super::*; use deno_core::resolve_url; + #[test] + fn test_reference_to_diagnostic() { + let range = Range { + start: Position { + line: 1, + character: 1, + }, + end: Position { + line: 2, + character: 2, + }, + }; + + let test_cases = [ + ( + Reference { + category: Category::Lint { + message: "message1".to_string(), + code: "code1".to_string(), + hint: None, + }, + range, + }, + lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("code1".to_string())), + source: Some("deno-lint".to_string()), + message: "message1".to_string(), + ..Default::default() + }, + ), + ( + Reference { + category: Category::Lint { + message: "message2".to_string(), + code: "code2".to_string(), + hint: Some("hint2".to_string()), + }, + range, + }, + lsp::Diagnostic { + range, + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("code2".to_string())), + source: Some("deno-lint".to_string()), + message: "message2\nhint2".to_string(), + ..Default::default() + }, + ), + ]; + + for (input, expected) in test_cases.iter() { + let actual = input.to_diagnostic(); + assert_eq!(&actual, expected); + } + } + #[test] fn test_as_lsp_range() { let fixture = deno_lint::diagnostic::Range { @@ -732,6 +803,38 @@ mod tests { ); } + #[test] + fn test_get_lint_references() { + let specifier = resolve_url("file:///a.ts").expect("bad specifier"); + let source = "const foo = 42;"; + let actual = + get_lint_references(&specifier, &MediaType::TypeScript, source).unwrap(); + + assert_eq!( + actual, + vec![Reference { + category: Category::Lint { + message: "`foo` is never used".to_string(), + code: "no-unused-vars".to_string(), + hint: Some( + "If this is intentional, prefix it with an underscore like `_foo`" + .to_string() + ), + }, + range: Range { + start: Position { + line: 0, + character: 6, + }, + end: Position { + line: 0, + character: 9, + } + } + }] + ); + } + #[test] fn test_analyze_dependencies() { let specifier = resolve_url("file:///a.ts").expect("bad specifier"); From 379d40955a46e3f1e5113aee3b4781e66d02c8e0 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 26 May 2021 23:44:42 +0200 Subject: [PATCH 43/53] fix(fetch): make prototype properties writable (#10769) --- cli/tests/unit/fetch_test.ts | 10 ++++++++++ extensions/fetch/22_body.js | 19 ++++++++++++++++++ extensions/fetch/23_request.js | 22 +++++++++++++++++++++ extensions/fetch/23_response.js | 34 +++++++++++++++++++++++++++++++++ tools/wpt/expectation.json | 23 ---------------------- 5 files changed, 85 insertions(+), 23 deletions(-) diff --git a/cli/tests/unit/fetch_test.ts b/cli/tests/unit/fetch_test.ts index e73953a5cf..c2224083dc 100644 --- a/cli/tests/unit/fetch_test.ts +++ b/cli/tests/unit/fetch_test.ts @@ -1139,3 +1139,13 @@ unitTest( assertEquals(actual, expected); }, ); + +unitTest({}, function fetchWritableRespProps(): void { + const original = new Response("https://deno.land", { + status: 404, + headers: { "x-deno": "foo" }, + }); + const new_ = new Response("https://deno.land", original); + assertEquals(original.status, new_.status); + assertEquals(new_.headers.get("x-deno"), "foo"); +}); diff --git a/extensions/fetch/22_body.js b/extensions/fetch/22_body.js index 938e3023ea..8c93e0fcf6 100644 --- a/extensions/fetch/22_body.js +++ b/extensions/fetch/22_body.js @@ -147,6 +147,8 @@ return this[bodySymbol].stream; } }, + configurable: true, + enumerable: true, }, bodyUsed: { /** @@ -159,6 +161,8 @@ } return false; }, + configurable: true, + enumerable: true, }, arrayBuffer: { /** @returns {Promise} */ @@ -167,6 +171,9 @@ const body = await consumeBody(this); return packageData(body, "ArrayBuffer"); }, + writable: true, + configurable: true, + enumerable: true, }, blob: { /** @returns {Promise} */ @@ -175,6 +182,9 @@ const body = await consumeBody(this); return packageData(body, "Blob", this[mimeTypeSymbol]); }, + writable: true, + configurable: true, + enumerable: true, }, formData: { /** @returns {Promise} */ @@ -183,6 +193,9 @@ const body = await consumeBody(this); return packageData(body, "FormData", this[mimeTypeSymbol]); }, + writable: true, + configurable: true, + enumerable: true, }, json: { /** @returns {Promise} */ @@ -191,6 +204,9 @@ const body = await consumeBody(this); return packageData(body, "JSON"); }, + writable: true, + configurable: true, + enumerable: true, }, text: { /** @returns {Promise} */ @@ -199,6 +215,9 @@ const body = await consumeBody(this); return packageData(body, "text"); }, + writable: true, + configurable: true, + enumerable: true, }, }; return Object.defineProperties(prototype.prototype, mixin); diff --git a/extensions/fetch/23_request.js b/extensions/fetch/23_request.js index 480b83182b..d8369b4049 100644 --- a/extensions/fetch/23_request.js +++ b/extensions/fetch/23_request.js @@ -403,6 +403,28 @@ mixinBody(Request, _body, _mimeType); + Object.defineProperty(Request.prototype, "method", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Request.prototype, "url", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Request.prototype, "headers", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Request.prototype, "redirect", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Request.prototype, "clone", { + enumerable: true, + writable: true, + configurable: true, + }); + webidl.converters["Request"] = webidl.createInterfaceConverter( "Request", Request, diff --git a/extensions/fetch/23_response.js b/extensions/fetch/23_response.js index 6d9874c6a5..7bbf3c66e6 100644 --- a/extensions/fetch/23_response.js +++ b/extensions/fetch/23_response.js @@ -365,6 +365,40 @@ mixinBody(Response, _body, _mimeType); + Object.defineProperty(Response.prototype, "type", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "url", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "redirected", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "status", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "ok", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "statusText", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "headers", { + enumerable: true, + configurable: true, + }); + Object.defineProperty(Response.prototype, "clone", { + enumerable: true, + writable: true, + configurable: true, + }); + webidl.converters["Response"] = webidl.createInterfaceConverter( "Response", Response, diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index 7843eefc6f..bfeca41c78 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -929,46 +929,23 @@ "Headers interface: operation has(ByteString)", "Headers interface: operation set(ByteString, ByteString)", "Headers interface: iterable", - "Request interface: attribute method", - "Request interface: attribute url", - "Request interface: attribute headers", "Request interface: attribute destination", "Request interface: attribute referrer", "Request interface: attribute referrerPolicy", "Request interface: attribute mode", "Request interface: attribute credentials", "Request interface: attribute cache", - "Request interface: attribute redirect", "Request interface: attribute integrity", "Request interface: attribute keepalive", "Request interface: attribute isReloadNavigation", "Request interface: attribute isHistoryNavigation", "Request interface: attribute signal", - "Request interface: operation clone()", "Request interface: attribute body", "Request interface: attribute bodyUsed", - "Request interface: operation arrayBuffer()", - "Request interface: operation blob()", - "Request interface: operation formData()", - "Request interface: operation json()", - "Request interface: operation text()", "Response interface: operation error()", "Response interface: operation redirect(USVString, optional unsigned short)", - "Response interface: attribute type", - "Response interface: attribute url", - "Response interface: attribute redirected", - "Response interface: attribute status", - "Response interface: attribute ok", - "Response interface: attribute statusText", - "Response interface: attribute headers", - "Response interface: operation clone()", "Response interface: attribute body", "Response interface: attribute bodyUsed", - "Response interface: operation arrayBuffer()", - "Response interface: operation blob()", - "Response interface: operation formData()", - "Response interface: operation json()", - "Response interface: operation text()", "Window interface: operation fetch(RequestInfo, optional RequestInit)" ] }, From cf6b764a35f17dcdd5903c92f9c140ac8f43aa79 Mon Sep 17 00:00:00 2001 From: Romain Prignon Date: Thu, 27 May 2021 08:33:33 +0200 Subject: [PATCH 44/53] fix(#10733): empty tsconfig.json file does not cause error (#10734) Fixes #10733 --- cli/config_file.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/cli/config_file.rs b/cli/config_file.rs index 1875f9906d..a8bd40e699 100644 --- a/cli/config_file.rs +++ b/cli/config_file.rs @@ -1,6 +1,7 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use crate::fs_util::canonicalize_path; +use deno_core::error::anyhow; use deno_core::error::AnyError; use deno_core::error::Context; use deno_core::serde::Deserialize; @@ -293,7 +294,23 @@ impl ConfigFile { } pub fn new(text: &str, path: &Path) -> Result { - let jsonc = jsonc_parser::parse_to_serde_value(text)?.unwrap(); + let jsonc = match jsonc_parser::parse_to_serde_value(text) { + Ok(None) => json!({}), + Ok(Some(value)) if value.is_object() => value, + Ok(Some(_)) => { + return Err(anyhow!( + "config file JSON {:?} should be an object", + path.to_str().unwrap() + )) + } + Err(e) => { + return Err(anyhow!( + "Unable to parse config file JSON {:?} because of {}", + path.to_str().unwrap(), + e.to_string() + )) + } + }; let json: ConfigFileJson = serde_json::from_value(jsonc)?; Ok(Self { @@ -393,6 +410,42 @@ mod tests { ); } + #[test] + fn test_parse_config_with_empty_file() { + let config_text = ""; + let config_path = PathBuf::from("/deno/tsconfig.json"); + let config_file = ConfigFile::new(config_text, &config_path).unwrap(); + let (options_value, _) = + config_file.as_compiler_options().expect("error parsing"); + assert!(options_value.is_object()); + } + + #[test] + fn test_parse_config_with_commented_file() { + let config_text = r#"//{"foo":"bar"}"#; + let config_path = PathBuf::from("/deno/tsconfig.json"); + let config_file = ConfigFile::new(config_text, &config_path).unwrap(); + let (options_value, _) = + config_file.as_compiler_options().expect("error parsing"); + assert!(options_value.is_object()); + } + + #[test] + fn test_parse_config_with_invalid_file() { + let config_text = "{foo:bar}"; + let config_path = PathBuf::from("/deno/tsconfig.json"); + // Emit error: Unable to parse config file JSON "" because of Unexpected token on line 1 column 6. + assert!(ConfigFile::new(config_text, &config_path).is_err()); + } + + #[test] + fn test_parse_config_with_not_object_file() { + let config_text = "[]"; + let config_path = PathBuf::from("/deno/tsconfig.json"); + // Emit error: config file JSON "" should be an object + assert!(ConfigFile::new(config_text, &config_path).is_err()); + } + #[test] fn test_tsconfig_merge_user_options() { let mut tsconfig = TsConfig::new(json!({ From a43d11fa3278cab3467d03f70079527736fe9c52 Mon Sep 17 00:00:00 2001 From: Jonathan Svenheden Date: Thu, 27 May 2021 09:48:05 +0200 Subject: [PATCH 45/53] docs(permissions): fix grammatical error in permissions docs (#10755) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartek Iwańczuk --- docs/getting_started/permissions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting_started/permissions.md b/docs/getting_started/permissions.md index d02f40886d..c59bb3aaa9 100644 --- a/docs/getting_started/permissions.md +++ b/docs/getting_started/permissions.md @@ -28,14 +28,14 @@ The following permissions are available: - **--allow-plugin** Allow loading plugins. Please note that --allow-plugin is an unstable feature. - **--allow-read=\** Allow file system read access. You can specify - an optional, comma-separated list of directories or files to provide a + an optional, comma-separated list of directories or files to provide an allow-list of allowed file system access. - **--allow-run** Allow running subprocesses. Be aware that subprocesses are not run in a sandbox and therefore do not have the same security restrictions as the deno process. Therefore, use with caution. - **--allow-write=\** Allow file system write access. You can - specify an optional, comma-separated list of directories or files to provide a - allow-list of allowed file system access. + specify an optional, comma-separated list of directories or files to provide + an allow-list of allowed file system access. ### Permissions allow-list From c00f9ad515561921894580d6d7132f65e70eae0a Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 27 May 2021 19:25:30 +0900 Subject: [PATCH 46/53] fix(ext/fetch): fix error message of Request constructor (#10772) --- extensions/fetch/23_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/fetch/23_request.js b/extensions/fetch/23_request.js index d8369b4049..de1bdd211b 100644 --- a/extensions/fetch/23_request.js +++ b/extensions/fetch/23_request.js @@ -273,7 +273,7 @@ ((init.body !== undefined && init.body !== null) || inputBody !== null) ) { - throw new TypeError("HEAD and GET requests may not have a body."); + throw new TypeError("Request with GET/HEAD method cannot have body."); } // 34. From 18db5be38611d5b7e8c8635bc55127f3f59bfdbd Mon Sep 17 00:00:00 2001 From: Takeshi Kurosawa Date: Fri, 28 May 2021 15:31:18 +0900 Subject: [PATCH 47/53] docs(runtime): fix fetch API usage of HTTP server (#10777) --- docs/runtime/http_server_apis.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/runtime/http_server_apis.md b/docs/runtime/http_server_apis.md index 9880b542a6..2b4d6f27b8 100644 --- a/docs/runtime/http_server_apis.md +++ b/docs/runtime/http_server_apis.md @@ -204,9 +204,11 @@ object. Responding with a basic "hello world" would look like this: async function handle(conn: Deno.Conn) { const httpConn = Deno.serveHttp(conn); for await (const requestEvent of httpConn) { - await requestEvent.respondWith(new Response("hello world"), { - status: 200, - }); + await requestEvent.respondWith( + new Response("hello world", { + status: 200, + }), + ); } } ``` From 283e7ca92bb009a950211ad0ab701288ff62c009 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sat, 29 May 2021 00:19:26 +0200 Subject: [PATCH 48/53] chore: release web extension@0.38.0 (#10773) --- Cargo.lock | 2 +- extensions/web/Cargo.toml | 2 +- runtime/Cargo.toml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b82c58ffa6..98fbf57f5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -756,7 +756,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.37.0" +version = "0.38.0" dependencies = [ "deno_core", "futures", diff --git a/extensions/web/Cargo.toml b/extensions/web/Cargo.toml index 2e2d96675b..e8a6f480e9 100644 --- a/extensions/web/Cargo.toml +++ b/extensions/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.37.0" +version = "0.38.0" edition = "2018" description = "Collection of Web APIs" authors = ["the Deno authors"] diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 5758219d80..b14c926f97 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -25,7 +25,7 @@ deno_fetch = { version = "0.29.0", path = "../extensions/fetch" } deno_file = { version = "0.6.0", path = "../extensions/file" } deno_timers = { version = "0.5.0", path = "../extensions/timers" } deno_url = { version = "0.7.0", path = "../extensions/url" } -deno_web = { version = "0.37.0", path = "../extensions/web" } +deno_web = { version = "0.38.0", path = "../extensions/web" } deno_webgpu = { version = "0.8.0", path = "../extensions/webgpu" } deno_webidl = { version = "0.7.0", path = "../extensions/webidl" } deno_websocket = { version = "0.12.0", path = "../extensions/websocket" } @@ -43,7 +43,7 @@ deno_fetch = { version = "0.29.0", path = "../extensions/fetch" } deno_file = { version = "0.6.0", path = "../extensions/file" } deno_timers = { version = "0.5.0", path = "../extensions/timers" } deno_url = { version = "0.7.0", path = "../extensions/url" } -deno_web = { version = "0.37.0", path = "../extensions/web" } +deno_web = { version = "0.38.0", path = "../extensions/web" } deno_webgpu = { version = "0.8.0", path = "../extensions/webgpu" } deno_webidl = { version = "0.7.0", path = "../extensions/webidl" } deno_websocket = { version = "0.12.0", path = "../extensions/websocket" } From 475bc35646bc25d796b2638768cd5a79e3a0033e Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sat, 29 May 2021 20:18:24 +0900 Subject: [PATCH 49/53] chore: upgrade Tokio to 1.6.1 (#10782) --- Cargo.lock | 4 ++-- bench_util/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- core/Cargo.toml | 2 +- extensions/fetch/Cargo.toml | 2 +- extensions/timers/Cargo.toml | 2 +- extensions/webgpu/Cargo.toml | 2 +- extensions/websocket/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- test_util/Cargo.toml | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98fbf57f5e..64185bb65a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3575,9 +3575,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd3076b5c8cc18138b8f8814895c11eb4de37114a5d127bafdc5e55798ceef37" +checksum = "0a38d31d7831c6ed7aad00aa4c12d9375fd225a6dd77da1d25b707346319a975" dependencies = [ "autocfg", "bytes", diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index f3e1140c8f..302712912c 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -15,4 +15,4 @@ publish = false [dependencies] bencher = "0.1" deno_core = { version = "0.88.0", path = "../core" } -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 44976eff11..c140725085 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -73,7 +73,7 @@ swc_ecmascript = { version = "0.33.0", features = ["codegen", "dep_graph", "pars tempfile = "3.2.0" termcolor = "1.1.2" text-size = "1.1.0" -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } tokio-rustls = "0.22.0" uuid = { version = "0.8.2", features = ["v4", "serde"] } walkdir = "2.3.2" diff --git a/core/Cargo.toml b/core/Cargo.toml index 0f2968e616..2b092fe991 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -33,7 +33,7 @@ path = "examples/http_bench_json_ops.rs" # These dependencies are only used for the 'http_bench_*_ops' examples. [dev-dependencies] -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } bencher = "0.1" [[bench]] diff --git a/extensions/fetch/Cargo.toml b/extensions/fetch/Cargo.toml index 83cd4ecdb7..2014178160 100644 --- a/extensions/fetch/Cargo.toml +++ b/extensions/fetch/Cargo.toml @@ -21,6 +21,6 @@ deno_file = { version = "0.6.0", path = "../file" } http = "0.2.4" reqwest = { version = "0.11.3", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } serde = { version = "1.0.125", features = ["derive"] } -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } tokio-stream = "0.1.5" tokio-util = "0.6.7" diff --git a/extensions/timers/Cargo.toml b/extensions/timers/Cargo.toml index d9bb23614e..32cf02e86d 100644 --- a/extensions/timers/Cargo.toml +++ b/extensions/timers/Cargo.toml @@ -15,4 +15,4 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.88.0", path = "../../core" } -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } diff --git a/extensions/webgpu/Cargo.toml b/extensions/webgpu/Cargo.toml index 5ada950b71..4a1c25db27 100644 --- a/extensions/webgpu/Cargo.toml +++ b/extensions/webgpu/Cargo.toml @@ -15,7 +15,7 @@ path = "lib.rs" [dependencies] deno_core = { version = "0.88.0", path = "../../core" } -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } serde = { version = "1.0.125", features = ["derive"] } wgpu-core = { version = "0.8.1", features = ["trace"] } wgpu-types = "0.8.0" diff --git a/extensions/websocket/Cargo.toml b/extensions/websocket/Cargo.toml index a5a962e54b..129aeb7ca0 100644 --- a/extensions/websocket/Cargo.toml +++ b/extensions/websocket/Cargo.toml @@ -17,7 +17,7 @@ path = "lib.rs" deno_core = { version = "0.88.0", path = "../../core" } http = "0.2.3" serde = { version = "1.0.125", features = ["derive"] } -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } tokio-rustls = "0.22.0" tokio-tungstenite = { version = "0.14.0", features = ["rustls-tls"] } webpki = "0.21.4" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b14c926f97..c2cf00b0b1 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -68,7 +68,7 @@ rustls = "0.19.0" serde = { version = "1.0.125", features = ["derive"] } sys-info = "0.9.0" termcolor = "1.1.2" -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } tokio-util = { version = "0.6", features = ["io"] } uuid = { version = "0.8.2", features = ["v4"] } webpki = "0.21.4" diff --git a/test_util/Cargo.toml b/test_util/Cargo.toml index b1750c5995..68181a16e6 100644 --- a/test_util/Cargo.toml +++ b/test_util/Cargo.toml @@ -23,7 +23,7 @@ regex = "1.4.3" serde = { version = "1.0.125", features = ["derive"] } serde_json = "1.0.64" tempfile = "3.2.0" -tokio = { version = "1.6.0", features = ["full"] } +tokio = { version = "1.6.1", features = ["full"] } tokio-rustls = "0.22.0" tokio-tungstenite = "0.14.0" From 925ba8fbbf947c2c95a616b43e3f89e20cd69e93 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Sat, 29 May 2021 21:21:11 +1000 Subject: [PATCH 50/53] fix(#10765): lsp import fixes include extensions (#10778) Fixes #10765 --- cli/lsp/analysis.rs | 145 +++++++++++ cli/lsp/language_server.rs | 28 +- cli/lsp/tsc.rs | 10 +- cli/tests/integration_tests_lsp.rs | 51 ++++ cli/tests/lsp/code_action_params_imports.json | 54 ++++ .../code_action_resolve_params_imports.json | 26 ++ .../code_action_resolve_response_imports.json | 51 ++++ .../lsp/code_action_response_imports.json | 242 ++++++++++++++++++ 8 files changed, 594 insertions(+), 13 deletions(-) create mode 100644 cli/tests/lsp/code_action_params_imports.json create mode 100644 cli/tests/lsp/code_action_resolve_params_imports.json create mode 100644 cli/tests/lsp/code_action_resolve_response_imports.json create mode 100644 cli/tests/lsp/code_action_response_imports.json diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 9a1a2d5077..4ef4a6e22a 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -11,6 +11,7 @@ use crate::module_graph::parse_ts_reference; use crate::module_graph::TypeScriptReference; use crate::tools::lint::create_linter; +use deno_core::error::anyhow; use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::serde::Deserialize; @@ -23,6 +24,7 @@ use deno_lint::rules; use lspower::lsp; use lspower::lsp::Position; use lspower::lsp::Range; +use regex::Regex; use std::cmp::Ordering; use std::collections::HashMap; use std::fmt; @@ -56,8 +58,12 @@ lazy_static::lazy_static! { .iter() .cloned() .collect(); + + static ref IMPORT_SPECIFIER_RE: Regex = Regex::new(r#"\sfrom\s+["']([^"']*)["']"#).unwrap(); } +const SUPPORTED_EXTENSIONS: &[&str] = &[".ts", ".tsx", ".js", ".jsx", ".mjs"]; + /// Category of self-generated diagnostic messages (those not coming from) /// TypeScript. #[derive(Debug, PartialEq, Eq)] @@ -417,6 +423,143 @@ fn code_as_string(code: &Option) -> String { } } +/// Iterate over the supported extensions, concatenating the extension on the +/// specifier, returning the first specifier that is resolve-able, otherwise +/// None if none match. +fn check_specifier( + specifier: &str, + referrer: &ModuleSpecifier, + snapshot: &language_server::StateSnapshot, + maybe_import_map: &Option, +) -> Option { + for ext in SUPPORTED_EXTENSIONS { + let specifier_with_ext = format!("{}{}", specifier, ext); + if let ResolvedDependency::Resolved(resolved_specifier) = + resolve_import(&specifier_with_ext, referrer, maybe_import_map) + { + if snapshot.documents.contains_key(&resolved_specifier) + || snapshot.sources.contains_key(&resolved_specifier) + { + return Some(specifier_with_ext); + } + } + } + + None +} + +/// For a set of tsc changes, can them for any that contain something that looks +/// like an import and rewrite the import specifier to include the extension +pub(crate) fn fix_ts_import_changes( + referrer: &ModuleSpecifier, + changes: &[tsc::FileTextChanges], + language_server: &language_server::Inner, +) -> Result, AnyError> { + let mut r = Vec::new(); + let snapshot = language_server.snapshot()?; + for change in changes { + let mut text_changes = Vec::new(); + for text_change in &change.text_changes { + if let Some(captures) = + IMPORT_SPECIFIER_RE.captures(&text_change.new_text) + { + let specifier = captures + .get(1) + .ok_or_else(|| anyhow!("Missing capture."))? + .as_str(); + if let Some(new_specifier) = check_specifier( + specifier, + referrer, + &snapshot, + &language_server.maybe_import_map, + ) { + let new_text = + text_change.new_text.replace(specifier, &new_specifier); + text_changes.push(tsc::TextChange { + span: text_change.span.clone(), + new_text, + }); + } else { + text_changes.push(text_change.clone()); + } + } else { + text_changes.push(text_change.clone()); + } + } + r.push(tsc::FileTextChanges { + file_name: change.file_name.clone(), + text_changes, + is_new_file: change.is_new_file, + }); + } + Ok(r) +} + +/// Fix tsc import code actions so that the module specifier is correct for +/// resolution by Deno (includes the extension). +fn fix_ts_import_action( + referrer: &ModuleSpecifier, + action: &tsc::CodeFixAction, + language_server: &language_server::Inner, +) -> Result { + if action.fix_name == "import" { + let change = action + .changes + .get(0) + .ok_or_else(|| anyhow!("Unexpected action changes."))?; + let text_change = change + .text_changes + .get(0) + .ok_or_else(|| anyhow!("Missing text change."))?; + if let Some(captures) = IMPORT_SPECIFIER_RE.captures(&text_change.new_text) + { + let specifier = captures + .get(1) + .ok_or_else(|| anyhow!("Missing capture."))? + .as_str(); + let snapshot = language_server.snapshot()?; + if let Some(new_specifier) = check_specifier( + specifier, + referrer, + &snapshot, + &language_server.maybe_import_map, + ) { + let description = action.description.replace(specifier, &new_specifier); + let changes = action + .changes + .iter() + .map(|c| { + let text_changes = c + .text_changes + .iter() + .map(|tc| tsc::TextChange { + span: tc.span.clone(), + new_text: tc.new_text.replace(specifier, &new_specifier), + }) + .collect(); + tsc::FileTextChanges { + file_name: c.file_name.clone(), + text_changes, + is_new_file: c.is_new_file, + } + }) + .collect(); + + return Ok(tsc::CodeFixAction { + description, + changes, + commands: None, + fix_name: action.fix_name.clone(), + fix_id: None, + fix_all_description: None, + }); + } + } + } + + Ok(action.clone()) +} + /// Determines if two TypeScript diagnostic codes are effectively equivalent. fn is_equivalent_code( a: &Option, @@ -547,6 +690,7 @@ impl CodeActionCollection { /// Add a TypeScript code fix action to the code actions collection. pub(crate) async fn add_ts_fix_action( &mut self, + specifier: &ModuleSpecifier, action: &tsc::CodeFixAction, diagnostic: &lsp::Diagnostic, language_server: &mut language_server::Inner, @@ -564,6 +708,7 @@ impl CodeActionCollection { "The action returned from TypeScript is unsupported.", )); } + let action = fix_ts_import_action(specifier, action, language_server)?; let edit = ts_changes_to_edit(&action.changes, language_server).await?; let code_action = lsp::CodeAction { title: action.description.clone(), diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index d86ccefd52..ebcb6b9e36 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -29,6 +29,7 @@ use std::sync::Arc; use tokio::fs; use super::analysis; +use super::analysis::fix_ts_import_changes; use super::analysis::ts_changes_to_edit; use super::analysis::CodeActionCollection; use super::analysis::CodeActionData; @@ -964,7 +965,7 @@ impl Inner { }; for action in actions { code_actions - .add_ts_fix_action(&action, diagnostic, self) + .add_ts_fix_action(&specifier, &action, diagnostic, self) .await .map_err(|err| { error!("Unable to convert fix: {}", err); @@ -1009,7 +1010,7 @@ impl Inner { LspError::invalid_params("The CodeAction's data is invalid.") })?; let req = tsc::RequestMethod::GetCombinedCodeFix(( - code_action_data.specifier, + code_action_data.specifier.clone(), json!(code_action_data.fix_id.clone()), )); let combined_code_actions: tsc::CombinedCodeActions = self @@ -1024,14 +1025,25 @@ impl Inner { error!("Deno does not support code actions with commands."); Err(LspError::invalid_request()) } else { + let changes = if code_action_data.fix_id == "fixMissingImport" { + fix_ts_import_changes( + &code_action_data.specifier, + &combined_code_actions.changes, + self, + ) + .map_err(|err| { + error!("Unable to remap changes: {}", err); + LspError::internal_error() + })? + } else { + combined_code_actions.changes.clone() + }; let mut code_action = params.clone(); code_action.edit = - ts_changes_to_edit(&combined_code_actions.changes, self) - .await - .map_err(|err| { - error!("Unable to convert changes to edits: {}", err); - LspError::internal_error() - })?; + ts_changes_to_edit(&changes, self).await.map_err(|err| { + error!("Unable to convert changes to edits: {}", err); + LspError::internal_error() + })?; Ok(code_action) } } else { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index c345b705d0..df39a3ba31 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -925,8 +925,8 @@ impl DocumentHighlights { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct TextChange { - span: TextSpan, - new_text: String, + pub span: TextSpan, + pub new_text: String, } impl TextChange { @@ -944,10 +944,10 @@ impl TextChange { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct FileTextChanges { - file_name: String, - text_changes: Vec, + pub file_name: String, + pub text_changes: Vec, #[serde(skip_serializing_if = "Option::is_none")] - is_new_file: Option, + pub is_new_file: Option, } impl FileTextChanges { diff --git a/cli/tests/integration_tests_lsp.rs b/cli/tests/integration_tests_lsp.rs index 8e04cbb95d..744b8d3872 100644 --- a/cli/tests/integration_tests_lsp.rs +++ b/cli/tests/integration_tests_lsp.rs @@ -1425,6 +1425,57 @@ fn lsp_code_actions_deno_cache() { shutdown(&mut client); } +#[test] +fn lsp_code_actions_imports() { + let mut client = init("initialize_params.json"); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file00.ts", + "languageId": "typescript", + "version": 1, + "text": "export const abc = \"abc\";\nexport const def = \"def\";\n" + } + }), + ); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file01.ts", + "languageId": "typescript", + "version": 1, + "text": "\nconsole.log(abc);\nconsole.log(def)\n" + } + }), + ); + + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/codeAction", + load_fixture("code_action_params_imports.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_action_response_imports.json")) + ); + let (maybe_res, maybe_err) = client + .write_request( + "codeAction/resolve", + load_fixture("code_action_resolve_params_imports.json"), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(load_fixture("code_action_resolve_response_imports.json")) + ); + shutdown(&mut client); +} + #[test] fn lsp_code_actions_deadlock() { let mut client = init("initialize_params.json"); diff --git a/cli/tests/lsp/code_action_params_imports.json b/cli/tests/lsp/code_action_params_imports.json new file mode 100644 index 0000000000..7a5824923a --- /dev/null +++ b/cli/tests/lsp/code_action_params_imports.json @@ -0,0 +1,54 @@ +{ + "textDocument": { + "uri": "file:///a/file01.ts" + }, + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "context": { + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + }, + { + "range": { + "start": { + "line": 2, + "character": 12 + }, + "end": { + "line": 2, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'def'." + } + ], + "only": [ + "quickfix" + ] + } +} diff --git a/cli/tests/lsp/code_action_resolve_params_imports.json b/cli/tests/lsp/code_action_resolve_params_imports.json new file mode 100644 index 0000000000..60178bbfea --- /dev/null +++ b/cli/tests/lsp/code_action_resolve_params_imports.json @@ -0,0 +1,26 @@ +{ + "title": "Add all missing imports", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "data": { + "specifier": "file:///a/file01.ts", + "fixId": "fixMissingImport" + } +} diff --git a/cli/tests/lsp/code_action_resolve_response_imports.json b/cli/tests/lsp/code_action_resolve_response_imports.json new file mode 100644 index 0000000000..6621c501f0 --- /dev/null +++ b/cli/tests/lsp/code_action_resolve_response_imports.json @@ -0,0 +1,51 @@ +{ + "title": "Add all missing imports", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "edit": { + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file01.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import { abc,def } from \"./file00.ts\";\n" + } + ] + } + ] + }, + "data": { + "specifier": "file:///a/file01.ts", + "fixId": "fixMissingImport" + } +} diff --git a/cli/tests/lsp/code_action_response_imports.json b/cli/tests/lsp/code_action_response_imports.json new file mode 100644 index 0000000000..e4d926bdd1 --- /dev/null +++ b/cli/tests/lsp/code_action_response_imports.json @@ -0,0 +1,242 @@ +[ + { + "title": "Import 'abc' from module \"./file00.ts\"", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "edit": { + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file01.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import { abc } from \"./file00.ts\";\n" + } + ] + } + ] + } + }, + { + "title": "Add all missing imports", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "data": { + "specifier": "file:///a/file01.ts", + "fixId": "fixMissingImport" + } + }, + { + "title": "Add missing function declaration 'abc'", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "edit": { + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file01.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 3, + "character": 0 + }, + "end": { + "line": 3, + "character": 0 + } + }, + "newText": "\nfunction abc(abc: any) {\nthrow new Error(\"Function not implemented.\");\n}\n" + } + ] + } + ] + } + }, + { + "title": "Import 'def' from module \"./file00.ts\"", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 2, + "character": 12 + }, + "end": { + "line": 2, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'def'." + } + ], + "edit": { + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file01.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import { def } from \"./file00.ts\";\n" + } + ] + } + ] + } + }, + { + "title": "Add missing function declaration 'def'", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 2, + "character": 12 + }, + "end": { + "line": 2, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'def'." + } + ], + "edit": { + "documentChanges": [ + { + "textDocument": { + "uri": "file:///a/file01.ts", + "version": 1 + }, + "edits": [ + { + "range": { + "start": { + "line": 3, + "character": 0 + }, + "end": { + "line": 3, + "character": 0 + } + }, + "newText": "\nfunction def(def: any) {\nthrow new Error(\"Function not implemented.\");\n}\n" + } + ] + } + ] + } + }, + { + "title": "Add all missing function declarations", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 12 + }, + "end": { + "line": 1, + "character": 15 + } + }, + "severity": 1, + "code": 2304, + "source": "deno-ts", + "message": "Cannot find name 'abc'." + } + ], + "data": { + "specifier": "file:///a/file01.ts", + "fixId": "fixMissingFunctionDeclaration" + } + } +] From 8a7e1c616d12feb93ed3fda4a70228924c747b5c Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 31 May 2021 01:20:34 +0100 Subject: [PATCH 51/53] fix(cli): Don't statically error on dynamic unmapped bare specifiers (#10618) Fixes #10168 Fixes #10615 Fixes #10616 --- cli/import_map.rs | 63 ++++++++++--------- cli/lsp/analysis.rs | 8 +-- cli/module_graph.rs | 18 +++--- cli/module_loader.rs | 7 +-- .../092_import_map_unmapped_bare_specifier.ts | 1 + ..._import_map_unmapped_bare_specifier.ts.out | 4 ++ .../error_011_bad_module_specifier.ts.out | 2 +- ...or_012_bad_dynamic_import_specifier.ts.out | 2 +- ...rror_014_catch_dynamic_import_error.js.out | 4 +- cli/tests/error_type_definitions.ts.out | 2 +- cli/tests/integration_tests.rs | 6 ++ core/module_specifier.rs | 4 +- 12 files changed, 66 insertions(+), 55 deletions(-) create mode 100644 cli/tests/092_import_map_unmapped_bare_specifier.ts create mode 100644 cli/tests/092_import_map_unmapped_bare_specifier.ts.out diff --git a/cli/import_map.rs b/cli/import_map.rs index d18633545f..f2126bed92 100644 --- a/cli/import_map.rs +++ b/cli/import_map.rs @@ -14,11 +14,25 @@ use std::error::Error; use std::fmt; #[derive(Debug)] -pub struct ImportMapError(String); +pub enum ImportMapError { + UnmappedBareSpecifier(String, Option), + Other(String), +} impl fmt::Display for ImportMapError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad(&self.0) + match self { + ImportMapError::UnmappedBareSpecifier(specifier, maybe_referrer) => write!( + f, + "Relative import path \"{}\" not prefixed with / or ./ or ../ and not in import map{}", + specifier, + match maybe_referrer { + Some(referrer) => format!(" from \"{}\"", referrer), + None => format!(""), + } + ), + ImportMapError::Other(message) => f.pad(message), + } } } @@ -51,7 +65,7 @@ impl ImportMap { let v: Value = match serde_json::from_str(json_string) { Ok(v) => v, Err(_) => { - return Err(ImportMapError( + return Err(ImportMapError::Other( "Unable to parse import map JSON".to_string(), )); } @@ -60,7 +74,7 @@ impl ImportMap { match v { Value::Object(_) => {} _ => { - return Err(ImportMapError( + return Err(ImportMapError::Other( "Import map JSON must be an object".to_string(), )); } @@ -70,7 +84,7 @@ impl ImportMap { let normalized_imports = match &v.get("imports") { Some(imports_map) => { if !imports_map.is_object() { - return Err(ImportMapError( + return Err(ImportMapError::Other( "Import map's 'imports' must be an object".to_string(), )); } @@ -84,7 +98,7 @@ impl ImportMap { let normalized_scopes = match &v.get("scopes") { Some(scope_map) => { if !scope_map.is_object() { - return Err(ImportMapError( + return Err(ImportMapError::Other( "Import map's 'scopes' must be an object".to_string(), )); } @@ -252,7 +266,7 @@ impl ImportMap { // Order is preserved because of "preserve_order" feature of "serde_json". for (scope_prefix, potential_specifier_map) in scope_map.iter() { if !potential_specifier_map.is_object() { - return Err(ImportMapError(format!( + return Err(ImportMapError::Other(format!( "The value for the {:?} scope prefix must be an object", scope_prefix ))); @@ -341,7 +355,7 @@ impl ImportMap { if let Some(address) = maybe_address { return Ok(Some(address.clone())); } else { - return Err(ImportMapError(format!( + return Err(ImportMapError::Other(format!( "Blocked by null entry for \"{:?}\"", normalized_specifier ))); @@ -367,7 +381,7 @@ impl ImportMap { } if maybe_address.is_none() { - return Err(ImportMapError(format!( + return Err(ImportMapError::Other(format!( "Blocked by null entry for \"{:?}\"", specifier_key ))); @@ -383,7 +397,7 @@ impl ImportMap { let url = match resolution_result.join(after_prefix) { Ok(url) => url, Err(_) => { - return Err(ImportMapError(format!( + return Err(ImportMapError::Other(format!( "Failed to resolve the specifier \"{:?}\" as its after-prefix portion \"{:?}\" could not be URL-parsed relative to the URL prefix \"{:?}\" mapped to by the prefix \"{:?}\"", @@ -396,7 +410,7 @@ impl ImportMap { }; if !url.as_str().starts_with(resolution_result.as_str()) { - return Err(ImportMapError(format!( + return Err(ImportMapError::Other(format!( "The specifier \"{:?}\" backtracks above its prefix \"{:?}\"", normalized_specifier, specifier_key ))); @@ -417,7 +431,7 @@ impl ImportMap { &self, specifier: &str, referrer: &str, - ) -> Result, ImportMapError> { + ) -> Result { let as_url: Option = ImportMap::try_url_like_specifier(specifier, referrer); let normalized_specifier = if let Some(url) = as_url.as_ref() { @@ -434,7 +448,7 @@ impl ImportMap { )?; // match found in scopes map - if scopes_match.is_some() { + if let Some(scopes_match) = scopes_match { return Ok(scopes_match); } @@ -445,19 +459,19 @@ impl ImportMap { )?; // match found in import map - if imports_match.is_some() { + if let Some(imports_match) = imports_match { return Ok(imports_match); } // The specifier was able to be turned into a URL, but wasn't remapped into anything. - if as_url.is_some() { + if let Some(as_url) = as_url { return Ok(as_url); } - Err(ImportMapError(format!( - "Unmapped bare specifier {:?}", - specifier - ))) + Err(ImportMapError::UnmappedBareSpecifier( + specifier.to_string(), + Some(referrer.to_string()), + )) } } @@ -465,7 +479,6 @@ impl ImportMap { mod tests { use super::*; - use deno_core::resolve_import; use std::path::Path; use std::path::PathBuf; use walkdir::WalkDir; @@ -652,15 +665,7 @@ mod tests { let maybe_resolved = import_map .resolve(&given_specifier, &base_url) .ok() - .map(|maybe_resolved| { - if let Some(specifier) = maybe_resolved { - specifier.to_string() - } else { - resolve_import(&given_specifier, &base_url) - .unwrap() - .to_string() - } - }); + .map(|url| url.to_string()); assert_eq!(expected_specifier, &maybe_resolved, "{}", test.name); } TestKind::Parse { diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 4ef4a6e22a..bd3ce799ae 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -214,13 +214,7 @@ pub fn resolve_import( maybe_import_map: &Option, ) -> ResolvedDependency { let maybe_mapped = if let Some(import_map) = maybe_import_map { - if let Ok(maybe_specifier) = - import_map.resolve(specifier, referrer.as_str()) - { - maybe_specifier - } else { - None - } + import_map.resolve(specifier, referrer.as_str()).ok() } else { None }; diff --git a/cli/module_graph.rs b/cli/module_graph.rs index 368df0a742..5bfa52e892 100644 --- a/cli/module_graph.rs +++ b/cli/module_graph.rs @@ -12,6 +12,7 @@ use crate::config_file::IgnoredCompilerOptions; use crate::config_file::TsConfig; use crate::diagnostics::Diagnostics; use crate::import_map::ImportMap; +use crate::import_map::ImportMapError; use crate::info; use crate::lockfile::Lockfile; use crate::media_type::MediaType; @@ -397,10 +398,13 @@ impl Module { Ok(specifier) => Some(specifier), Err(any_error) => { match any_error.downcast_ref::() { - Some(ModuleResolutionError::ImportPrefixMissing(_, _)) => None, - _ => { - return Err(any_error); - } + Some(ModuleResolutionError::ImportPrefixMissing(..)) => None, + _ => match any_error.downcast_ref::() { + Some(ImportMapError::UnmappedBareSpecifier(..)) => None, + _ => { + return Err(any_error); + } + }, } } }; @@ -447,10 +451,8 @@ impl Module { ) -> Result { let maybe_resolve = if let Some(import_map) = self.maybe_import_map.clone() { - import_map - .lock() - .unwrap() - .resolve(specifier, self.specifier.as_str())? + let import_map = import_map.lock().unwrap(); + Some(import_map.resolve(specifier, self.specifier.as_str())?) } else { None }; diff --git a/cli/module_loader.rs b/cli/module_loader.rs index acf7625065..349e72393e 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -83,10 +83,9 @@ impl ModuleLoader for CliModuleLoader { if !is_main { if let Some(import_map) = &self.import_map { - let result = import_map.resolve(specifier, referrer)?; - if let Some(r) = result { - return Ok(r); - } + return import_map + .resolve(specifier, referrer) + .map_err(AnyError::from); } } diff --git a/cli/tests/092_import_map_unmapped_bare_specifier.ts b/cli/tests/092_import_map_unmapped_bare_specifier.ts new file mode 100644 index 0000000000..87684430dd --- /dev/null +++ b/cli/tests/092_import_map_unmapped_bare_specifier.ts @@ -0,0 +1 @@ +await import("unmapped"); diff --git a/cli/tests/092_import_map_unmapped_bare_specifier.ts.out b/cli/tests/092_import_map_unmapped_bare_specifier.ts.out new file mode 100644 index 0000000000..1a55e352b8 --- /dev/null +++ b/cli/tests/092_import_map_unmapped_bare_specifier.ts.out @@ -0,0 +1,4 @@ +[WILDCARD]error: Uncaught (in promise) TypeError: Relative import path "unmapped" not prefixed with / or ./ or ../ and not in import map from "[WILDCARD]" +await import("unmapped"); +^ + at [WILDCARD] diff --git a/cli/tests/error_011_bad_module_specifier.ts.out b/cli/tests/error_011_bad_module_specifier.ts.out index e6f9b2321b..713072191e 100644 --- a/cli/tests/error_011_bad_module_specifier.ts.out +++ b/cli/tests/error_011_bad_module_specifier.ts.out @@ -1 +1 @@ -[WILDCARD]error: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_011_bad_module_specifier.ts" +[WILDCARD]error: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_011_bad_module_specifier.ts" diff --git a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out index 45bce82616..0d0b168a48 100644 --- a/cli/tests/error_012_bad_dynamic_import_specifier.ts.out +++ b/cli/tests/error_012_bad_dynamic_import_specifier.ts.out @@ -1,5 +1,5 @@ Check [WILDCARD]error_012_bad_dynamic_import_specifier.ts -error: Uncaught (in promise) TypeError: relative import path "bad-module.ts" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts" +error: Uncaught (in promise) TypeError: Relative import path "bad-module.ts" not prefixed with / or ./ or ../ from "[WILDCARD]/error_012_bad_dynamic_import_specifier.ts" const _badModule = await import("bad-module.ts"); ^ at async file:///[WILDCARD]/error_012_bad_dynamic_import_specifier.ts:2:22 diff --git a/cli/tests/error_014_catch_dynamic_import_error.js.out b/cli/tests/error_014_catch_dynamic_import_error.js.out index 4f133c834f..60de400db9 100644 --- a/cli/tests/error_014_catch_dynamic_import_error.js.out +++ b/cli/tests/error_014_catch_dynamic_import_error.js.out @@ -1,8 +1,8 @@ Caught direct dynamic import error. -TypeError: relative import path "does not exist" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/error_014_catch_dynamic_import_error.js" +TypeError: Relative import path "does not exist" not prefixed with / or ./ or ../ from "[WILDCARD]/error_014_catch_dynamic_import_error.js" at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:3:5 Caught indirect direct dynamic import error. -TypeError: relative import path "does not exist either" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/indirect_import_error.js" +TypeError: Relative import path "does not exist either" not prefixed with / or ./ or ../ from "[WILDCARD]/indirect_import_error.js" at async file:///[WILDCARD]/error_014_catch_dynamic_import_error.js:10:5 Caught error thrown by dynamically imported module. Error: An error diff --git a/cli/tests/error_type_definitions.ts.out b/cli/tests/error_type_definitions.ts.out index 32c3c9b525..304ec1bdfe 100644 --- a/cli/tests/error_type_definitions.ts.out +++ b/cli/tests/error_type_definitions.ts.out @@ -1 +1 @@ -[WILDCARD]error: relative import path "baz" not prefixed with / or ./ or ../ Imported from "[WILDCARD]/type_definitions/bar.d.ts" +[WILDCARD]error: Relative import path "baz" not prefixed with / or ./ or ../ from "[WILDCARD]/type_definitions/bar.d.ts" diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index dcde0d057b..de009a064d 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -3123,6 +3123,12 @@ console.log("finish"); exit_code: 1, }); + itest!(_092_import_map_unmapped_bare_specifier { + args: "run --import-map import_maps/import_map.json 092_import_map_unmapped_bare_specifier.ts", + output: "092_import_map_unmapped_bare_specifier.ts.out", + exit_code: 1, + }); + itest!(dynamic_import_permissions_remote_remote { args: "run --quiet --reload --allow-net=localhost:4545 dynamic_import/permissions_remote_remote.ts", output: "dynamic_import/permissions_remote_remote.ts.out", diff --git a/core/module_specifier.rs b/core/module_specifier.rs index dc6b4d6bf6..4de8750736 100644 --- a/core/module_specifier.rs +++ b/core/module_specifier.rs @@ -39,10 +39,10 @@ impl fmt::Display for ModuleResolutionError { InvalidPath(ref path) => write!(f, "invalid module path: {:?}", path), ImportPrefixMissing(ref specifier, ref maybe_referrer) => write!( f, - "relative import path \"{}\" not prefixed with / or ./ or ../{}", + "Relative import path \"{}\" not prefixed with / or ./ or ../{}", specifier, match maybe_referrer { - Some(referrer) => format!(" Imported from \"{}\"", referrer), + Some(referrer) => format!(" from \"{}\"", referrer), None => format!(""), } ), From 2c1f37b4a97922cfcec9393e2146c42d3d3b6e2e Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Mon, 31 May 2021 11:03:17 +0200 Subject: [PATCH 52/53] fix: remove unimplemented Request attributes (#10784) Not having them is better than throwing "unimplemented" errors on access. --- extensions/fetch/23_request.js | 111 --------------------------------- tools/wpt/expectation.json | 13 +++- 2 files changed, 12 insertions(+), 112 deletions(-) diff --git a/extensions/fetch/23_request.js b/extensions/fetch/23_request.js index de1bdd211b..ac38ce5522 100644 --- a/extensions/fetch/23_request.js +++ b/extensions/fetch/23_request.js @@ -316,66 +316,11 @@ return this[_headers]; } - get destination() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get referrer() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get referrerPolicy() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get mode() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get credentials() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get cache() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - get redirect() { webidl.assertBranded(this, Request); return this[_request].redirectMode; } - get integrity() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get keepalive() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get isReloadNavigation() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get isHistoryNavigation() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - - get signal() { - webidl.assertBranded(this, Request); - throw new TypeError("This property is not implemented."); - } - clone() { webidl.assertBranded(this, Request); if (this[_body] && this[_body].unusable()) { @@ -438,46 +383,6 @@ } return webidl.converters["USVString"](V, opts); }; - - webidl.converters["ReferrerPolicy"] = webidl.createEnumConverter( - "ReferrerPolicy", - [ - "", - "no-referrer", - "no-referrer-when-downgrade", - "same-origin", - "origin", - "strict-origin", - "origin-when-cross-origin", - "strict-origin-when-cross-origin", - "unsafe-url", - ], - ); - webidl.converters["RequestMode"] = webidl.createEnumConverter("RequestMode", [ - "navigate", - "same-origin", - "no-cors", - "cors", - ]); - webidl.converters["RequestCredentials"] = webidl.createEnumConverter( - "RequestCredentials", - [ - "omit", - "same-origin", - "include", - ], - ); - webidl.converters["RequestCache"] = webidl.createEnumConverter( - "RequestCache", - [ - "default", - "no-store", - "reload", - "no-cache", - "force-cache", - "only-if-cached", - ], - ); webidl.converters["RequestRedirect"] = webidl.createEnumConverter( "RequestRedirect", [ @@ -497,23 +402,7 @@ webidl.converters["BodyInit"], ), }, - { key: "referrer", converter: webidl.converters["USVString"] }, - { key: "referrerPolicy", converter: webidl.converters["ReferrerPolicy"] }, - { key: "mode", converter: webidl.converters["RequestMode"] }, - { - key: "credentials", - converter: webidl.converters["RequestCredentials"], - }, - { key: "cache", converter: webidl.converters["RequestCache"] }, { key: "redirect", converter: webidl.converters["RequestRedirect"] }, - { key: "integrity", converter: webidl.converters["DOMString"] }, - { key: "keepalive", converter: webidl.converters["boolean"] }, - { - key: "signal", - converter: webidl.createNullableConverter( - webidl.converters["AbortSignal"], - ), - }, { key: "client", converter: webidl.converters.any }, ], ); diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json index bfeca41c78..b0bca01ce7 100644 --- a/tools/wpt/expectation.json +++ b/tools/wpt/expectation.json @@ -942,6 +942,17 @@ "Request interface: attribute signal", "Request interface: attribute body", "Request interface: attribute bodyUsed", + "Request interface: new Request('about:blank') must inherit property \"destination\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"referrer\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"referrerPolicy\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"mode\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"credentials\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"cache\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"integrity\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"keepalive\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"isReloadNavigation\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"isHistoryNavigation\" with the proper type", + "Request interface: new Request('about:blank') must inherit property \"signal\" with the proper type", "Response interface: operation error()", "Response interface: operation redirect(USVString, optional unsigned short)", "Response interface: attribute body", @@ -1076,4 +1087,4 @@ "set.any.html": true } } -} +} \ No newline at end of file From 1567c1013cc8ff12cf039137792da66a1d0015b5 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Mon, 31 May 2021 17:39:24 +0000 Subject: [PATCH 53/53] v1.10.3 --- Cargo.lock | 30 +++++++++---------- Releases.md | 44 ++++++++++++++++++++++++++++ bench_util/Cargo.toml | 2 +- cli/Cargo.toml | 10 +++---- core/Cargo.toml | 4 +-- extensions/console/Cargo.toml | 4 +-- extensions/crypto/Cargo.toml | 5 ++-- extensions/fetch/Cargo.toml | 6 ++-- extensions/file/Cargo.toml | 4 +-- extensions/timers/Cargo.toml | 4 +-- extensions/url/Cargo.toml | 4 +-- extensions/web/Cargo.toml | 4 +-- extensions/webgpu/Cargo.toml | 4 +-- extensions/webidl/Cargo.toml | 4 +-- extensions/websocket/Cargo.toml | 4 +-- extensions/webstorage/Cargo.toml | 4 +-- runtime/Cargo.toml | 50 ++++++++++++++++---------------- serde_v8/Cargo.toml | 2 +- 18 files changed, 116 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64185bb65a..50e02ae76c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -530,7 +530,7 @@ dependencies = [ [[package]] name = "deno" -version = "1.10.2" +version = "1.10.3" dependencies = [ "atty", "base64 0.13.0", @@ -593,14 +593,14 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.7.0" +version = "0.7.1" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.88.0" +version = "0.88.1" dependencies = [ "anyhow", "bencher", @@ -620,7 +620,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.21.0" +version = "0.21.1" dependencies = [ "deno_core", "rand 0.8.3", @@ -644,7 +644,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.29.0" +version = "0.29.1" dependencies = [ "bytes", "data-url", @@ -660,7 +660,7 @@ dependencies = [ [[package]] name = "deno_file" -version = "0.6.0" +version = "0.6.1" dependencies = [ "deno_core", "uuid", @@ -688,7 +688,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.15.0" +version = "0.15.1" dependencies = [ "atty", "bytes", @@ -737,7 +737,7 @@ dependencies = [ [[package]] name = "deno_timers" -version = "0.5.0" +version = "0.5.1" dependencies = [ "deno_core", "tokio", @@ -745,7 +745,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.7.0" +version = "0.7.1" dependencies = [ "bencher", "deno_core", @@ -756,7 +756,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.38.0" +version = "0.38.1" dependencies = [ "deno_core", "futures", @@ -764,7 +764,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.8.0" +version = "0.8.1" dependencies = [ "deno_core", "serde", @@ -775,14 +775,14 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.7.0" +version = "0.7.1" dependencies = [ "deno_core", ] [[package]] name = "deno_websocket" -version = "0.12.0" +version = "0.12.1" dependencies = [ "deno_core", "http", @@ -796,7 +796,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.2.0" +version = "0.2.1" dependencies = [ "deno_core", "rusqlite", @@ -2874,7 +2874,7 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.4.0" +version = "0.4.1" dependencies = [ "bencher", "rusty_v8", diff --git a/Releases.md b/Releases.md index a533242269..35e9db430e 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,50 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 1.10.3 / 2021.05.31 + +- feat(lsp): diagnostics for deno types and triple-slash refs (#10699) +- feat(lsp): provide X-Deno-Warning as a diagnostic (#10680) +- feat(lsp): show hints from `deno_lint` in addition to messages (#10739) +- feat(lsp): support formatting json and markdown files (#10180) +- fix(cli): always allow documentation modules to be checked (#10581) +- fix(cli): canonicalize coverage dir (#10364) +- fix(cli): don't statically error on dynamic unmapped bare specifiers (#10618) +- fix(cli): empty tsconfig.json file does not cause error (#10734) +- fix(cli): support source maps with Deno.emit() and bundle (#10510) +- fix(cli/dts): fix missing error class (NotSupported) in types (#10713) +- fix(cli/install): support `file:` scheme URLs (#10562) +- fix(cli/test): don't use reserved symbol `:` in specifier (#10751) +- fix(cli/test): ensure coverage dir exists (#10717) +- fix(cli/upgrade): modify download size paddings (#10639) +- fix(runtime/http): expose nextRequest() errors in respondWith() (#10384) +- fix(runtime/http): fix empty blob response (#10689) +- fix(serde_v8): remove intentional deserialization error on non-utf8 strings + (#10156) +- fix(ext/fetch): fix error message of Request constructor (#10772) +- fix(ext/fetch): make prototype properties writable (#10769) +- fix(ext/fetch): remove unimplemented Request attributes (#10784) +- fix(ext/file): update File constructor following the spec (#10760) +- fix(ext/webstorage): use opstate for sqlite connection (#10692) +- fix(lsp): deps diagnostics include data property (#10696) +- fix(lsp): ignore type definition not found diagnostic (#10610) +- fix(lsp): local module import added by code action now includes the file + extension (#10778) +- fix(lsp): make failed to load config error descriptive (#10685) +- fix(lsp): memoize script versions per tsc request (#10601) +- fix(lsp): re-enable the per resource configuration without a deadlock (#10625) +- docs(cli): update getting started for clarity (#10694) +- docs(cli/dts) replace `read()` with `readSync()` (#10732) +- docs(cli/dts): fix plugin example (#10647) +- docs(cli/dts): fix typo in `TestDefinition.only` description (#10697) +- docs(cli/dts): fix unix socket examples (#10705) +- docs(cli/dts): make worker example pass (#10703) +- docs(cli/dts): tag test permission example as typescript (#10753) +- docs(permissions): fix grammatical error in permissions docs (#10755) +- docs(runtime): fix fetch API usage of HTTP server (#10777) +- docs(testing): fix misspelling (#10683) +- docs(typescript): fix typo in faqs (#10682) + ### 1.10.2 / 2021.05.17 - fix: static import permissions in dynamic imports diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 302712912c..60e7b1cc1f 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -14,5 +14,5 @@ publish = false [dependencies] bencher = "0.1" -deno_core = { version = "0.88.0", path = "../core" } +deno_core = { version = "0.88.1", path = "../core" } tokio = { version = "1.6.1", features = ["full"] } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c140725085..def5e209a8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno" -version = "1.10.2" +version = "1.10.3" license = "MIT" authors = ["the Deno authors"] edition = "2018" @@ -20,8 +20,8 @@ harness = false path = "./bench/main.rs" [build-dependencies] -deno_core = { path = "../core", version = "0.88.0" } -deno_runtime = { path = "../runtime", version = "0.15.0" } +deno_core = { path = "../core", version = "0.88.1" } +deno_runtime = { path = "../runtime", version = "0.15.1" } regex = "1.4.3" serde = { version = "1.0.125", features = ["derive"] } @@ -30,10 +30,10 @@ winapi = "0.3.9" winres = "0.1.11" [dependencies] -deno_core = { path = "../core", version = "0.88.0" } +deno_core = { path = "../core", version = "0.88.1" } deno_doc = "0.4.0" deno_lint = "0.5.0" -deno_runtime = { path = "../runtime", version = "0.15.0" } +deno_runtime = { path = "../runtime", version = "0.15.1" } atty = "0.2.14" base64 = "0.13.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index 2b092fe991..e22016e67e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,7 +1,7 @@ # Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. [package] name = "deno_core" -version = "0.88.0" +version = "0.88.1" edition = "2018" description = "A secure JavaScript/TypeScript runtime built with V8, Rust, and Tokio" authors = ["the Deno authors"] @@ -13,7 +13,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -serde_v8 = { version = "0.4.0", path = "../serde_v8" } +serde_v8 = { version = "0.4.1", path = "../serde_v8" } anyhow = "1.0.40" futures = "0.3.15" diff --git a/extensions/console/Cargo.toml b/extensions/console/Cargo.toml index 189d565006..06db0a2d6f 100644 --- a/extensions/console/Cargo.toml +++ b/extensions/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.7.0" +version = "0.7.1" edition = "2018" description = "Implementation of Console API for Deno" authors = ["the Deno authors"] @@ -14,4 +14,4 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } diff --git a/extensions/crypto/Cargo.toml b/extensions/crypto/Cargo.toml index 5295aa9f1d..fa0a6634e7 100644 --- a/extensions/crypto/Cargo.toml +++ b/extensions/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.21.0" +version = "0.21.1" edition = "2018" description = "Web Cryptography API implementation for Deno" authors = ["the Deno authors"] @@ -14,6 +14,5 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } rand = "0.8.3" - diff --git a/extensions/fetch/Cargo.toml b/extensions/fetch/Cargo.toml index 2014178160..0ba969bc1a 100644 --- a/extensions/fetch/Cargo.toml +++ b/extensions/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.29.0" +version = "0.29.1" edition = "2018" description = "Fetch API implementation for Deno" authors = ["the Deno authors"] @@ -16,8 +16,8 @@ path = "lib.rs" [dependencies] bytes = "1.0.1" data-url = "0.1.0" -deno_core = { version = "0.88.0", path = "../../core" } -deno_file = { version = "0.6.0", path = "../file" } +deno_core = { version = "0.88.1", path = "../../core" } +deno_file = { version = "0.6.1", path = "../file" } http = "0.2.4" reqwest = { version = "0.11.3", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] } serde = { version = "1.0.125", features = ["derive"] } diff --git a/extensions/file/Cargo.toml b/extensions/file/Cargo.toml index cab6f56662..f9f17e427a 100644 --- a/extensions/file/Cargo.toml +++ b/extensions/file/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_file" -version = "0.6.0" +version = "0.6.1" edition = "2018" description = "File API implementation for Deno" authors = ["the Deno authors"] @@ -14,5 +14,5 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } uuid = { version = "0.8.2", features = ["v4"] } diff --git a/extensions/timers/Cargo.toml b/extensions/timers/Cargo.toml index 32cf02e86d..afdbf2e4cd 100644 --- a/extensions/timers/Cargo.toml +++ b/extensions/timers/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_timers" -version = "0.5.0" +version = "0.5.1" edition = "2018" description = "Timers API implementation for Deno" authors = ["the Deno authors"] @@ -14,5 +14,5 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } tokio = { version = "1.6.1", features = ["full"] } diff --git a/extensions/url/Cargo.toml b/extensions/url/Cargo.toml index 70a880e0d0..57f82bba8f 100644 --- a/extensions/url/Cargo.toml +++ b/extensions/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.7.0" +version = "0.7.1" edition = "2018" description = "URL API implementation for Deno" authors = ["the Deno authors"] @@ -14,7 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } idna = "0.2.3" percent-encoding = "2.1.0" serde = { version = "1.0.125", features = ["derive"] } diff --git a/extensions/web/Cargo.toml b/extensions/web/Cargo.toml index e8a6f480e9..1d0cd54113 100644 --- a/extensions/web/Cargo.toml +++ b/extensions/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.38.0" +version = "0.38.1" edition = "2018" description = "Collection of Web APIs" authors = ["the Deno authors"] @@ -14,7 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } [dev-dependencies] futures = "0.3.15" diff --git a/extensions/webgpu/Cargo.toml b/extensions/webgpu/Cargo.toml index 4a1c25db27..a1609fea83 100644 --- a/extensions/webgpu/Cargo.toml +++ b/extensions/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.8.0" +version = "0.8.1" edition = "2018" description = "WebGPU implementation for Deno" authors = ["the Deno authors"] @@ -14,7 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } tokio = { version = "1.6.1", features = ["full"] } serde = { version = "1.0.125", features = ["derive"] } wgpu-core = { version = "0.8.1", features = ["trace"] } diff --git a/extensions/webidl/Cargo.toml b/extensions/webidl/Cargo.toml index ad6b2d57b5..f2842c5c28 100644 --- a/extensions/webidl/Cargo.toml +++ b/extensions/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.7.0" +version = "0.7.1" edition = "2018" description = "WebIDL implementation for Deno" authors = ["the Deno authors"] @@ -14,4 +14,4 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } diff --git a/extensions/websocket/Cargo.toml b/extensions/websocket/Cargo.toml index 129aeb7ca0..7e2a82b99e 100644 --- a/extensions/websocket/Cargo.toml +++ b/extensions/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.12.0" +version = "0.12.1" edition = "2018" description = "Implementation of WebSocket API for Deno" authors = ["the Deno authors"] @@ -14,7 +14,7 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } http = "0.2.3" serde = { version = "1.0.125", features = ["derive"] } tokio = { version = "1.6.1", features = ["full"] } diff --git a/extensions/webstorage/Cargo.toml b/extensions/webstorage/Cargo.toml index 506014b170..b72970bbd7 100644 --- a/extensions/webstorage/Cargo.toml +++ b/extensions/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.2.0" +version = "0.2.1" edition = "2018" description = "Implementation of WebStorage API for Deno" authors = ["the Deno authors"] @@ -14,6 +14,6 @@ repository = "https://github.com/denoland/deno" path = "lib.rs" [dependencies] -deno_core = { version = "0.88.0", path = "../../core" } +deno_core = { version = "0.88.1", path = "../../core" } rusqlite = { version = "0.25.3", features = ["unlock_notify", "bundled"] } serde = { version = "1.0.125", features = ["derive"] } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index c2cf00b0b1..786b7f5520 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.15.0" +version = "0.15.1" license = "MIT" authors = ["the Deno authors"] edition = "2018" @@ -18,36 +18,36 @@ name = "hello_runtime" path = "examples/hello_runtime.rs" [build-dependencies] -deno_console = { version = "0.7.0", path = "../extensions/console" } -deno_core = { version = "0.88.0", path = "../core" } -deno_crypto = { version = "0.21.0", path = "../extensions/crypto" } -deno_fetch = { version = "0.29.0", path = "../extensions/fetch" } -deno_file = { version = "0.6.0", path = "../extensions/file" } -deno_timers = { version = "0.5.0", path = "../extensions/timers" } -deno_url = { version = "0.7.0", path = "../extensions/url" } -deno_web = { version = "0.38.0", path = "../extensions/web" } -deno_webgpu = { version = "0.8.0", path = "../extensions/webgpu" } -deno_webidl = { version = "0.7.0", path = "../extensions/webidl" } -deno_websocket = { version = "0.12.0", path = "../extensions/websocket" } -deno_webstorage = { version = "0.2.0", path = "../extensions/webstorage" } +deno_console = { version = "0.7.1", path = "../extensions/console" } +deno_core = { version = "0.88.1", path = "../core" } +deno_crypto = { version = "0.21.1", path = "../extensions/crypto" } +deno_fetch = { version = "0.29.1", path = "../extensions/fetch" } +deno_file = { version = "0.6.1", path = "../extensions/file" } +deno_timers = { version = "0.5.1", path = "../extensions/timers" } +deno_url = { version = "0.7.1", path = "../extensions/url" } +deno_web = { version = "0.38.1", path = "../extensions/web" } +deno_webgpu = { version = "0.8.1", path = "../extensions/webgpu" } +deno_webidl = { version = "0.7.1", path = "../extensions/webidl" } +deno_websocket = { version = "0.12.1", path = "../extensions/websocket" } +deno_webstorage = { version = "0.2.1", path = "../extensions/webstorage" } [target.'cfg(windows)'.build-dependencies] winres = "0.1.11" winapi = "0.3.9" [dependencies] -deno_console = { version = "0.7.0", path = "../extensions/console" } -deno_core = { version = "0.88.0", path = "../core" } -deno_crypto = { version = "0.21.0", path = "../extensions/crypto" } -deno_fetch = { version = "0.29.0", path = "../extensions/fetch" } -deno_file = { version = "0.6.0", path = "../extensions/file" } -deno_timers = { version = "0.5.0", path = "../extensions/timers" } -deno_url = { version = "0.7.0", path = "../extensions/url" } -deno_web = { version = "0.38.0", path = "../extensions/web" } -deno_webgpu = { version = "0.8.0", path = "../extensions/webgpu" } -deno_webidl = { version = "0.7.0", path = "../extensions/webidl" } -deno_websocket = { version = "0.12.0", path = "../extensions/websocket" } -deno_webstorage = { version = "0.2.0", path = "../extensions/webstorage" } +deno_console = { version = "0.7.1", path = "../extensions/console" } +deno_core = { version = "0.88.1", path = "../core" } +deno_crypto = { version = "0.21.1", path = "../extensions/crypto" } +deno_fetch = { version = "0.29.1", path = "../extensions/fetch" } +deno_file = { version = "0.6.1", path = "../extensions/file" } +deno_timers = { version = "0.5.1", path = "../extensions/timers" } +deno_url = { version = "0.7.1", path = "../extensions/url" } +deno_web = { version = "0.38.1", path = "../extensions/web" } +deno_webgpu = { version = "0.8.1", path = "../extensions/webgpu" } +deno_webidl = { version = "0.7.1", path = "../extensions/webidl" } +deno_websocket = { version = "0.12.1", path = "../extensions/websocket" } +deno_webstorage = { version = "0.2.1", path = "../extensions/webstorage" } atty = "0.2.14" bytes = "1" diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml index 1e320956f8..78a064626a 100644 --- a/serde_v8/Cargo.toml +++ b/serde_v8/Cargo.toml @@ -1,7 +1,7 @@ # Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. [package] name = "serde_v8" -version = "0.4.0" +version = "0.4.1" authors = ["the Deno authors"] edition = "2018" description = "Rust to V8 serialization and deserialization"