diff options
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r-- | cli/lsp/language_server.rs | 2395 |
1 files changed, 0 insertions, 2395 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 1be65d0fe..9f5a7c84a 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<V> - 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<fn(Value)>); - - struct LspTestHarness { - requests: Vec<LspTestHarnessRequest>, - service: Spawn<LspService>, - } - - impl LspTestHarness { - pub fn new(requests: Vec<LspTestHarnessRequest>) -> 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<Option<jsonrpc::Outgoing>, 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<Hover>, - } - - #[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<Vec<CodeLens>>, - } - - #[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<CompletionResponse>, - } - - #[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<PerformanceAverage>, - } - #[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; - } -} |