summaryrefslogtreecommitdiff
path: root/cli/tests/lsp_tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/tests/lsp_tests.rs')
-rw-r--r--cli/tests/lsp_tests.rs6445
1 files changed, 0 insertions, 6445 deletions
diff --git a/cli/tests/lsp_tests.rs b/cli/tests/lsp_tests.rs
deleted file mode 100644
index cb8e2d24c..000000000
--- a/cli/tests/lsp_tests.rs
+++ /dev/null
@@ -1,6445 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-mod lsp {
- use deno_ast::ModuleSpecifier;
- use deno_core::serde::de::DeserializeOwned;
- 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 pretty_assertions::assert_eq;
- use std::collections::HashSet;
- use std::fs;
- use std::process::Stdio;
- use test_util::deno_cmd_with_deno_dir;
- use test_util::deno_exe_path;
- use test_util::env_vars_for_npm_tests;
- use test_util::http_server;
- use test_util::lsp::LspClient;
- use test_util::testdata_path;
- use test_util::TempDir;
- use tower_lsp::lsp_types as lsp;
-
- fn load_fixture(path: &str) -> Value {
- load_fixture_as(path)
- }
-
- fn load_fixture_as<T>(path: &str) -> T
- where
- T: DeserializeOwned,
- {
- let fixture_str = load_fixture_str(path);
- serde_json::from_str::<T>(&fixture_str).unwrap()
- }
-
- fn load_fixture_str(path: &str) -> String {
- let fixtures_path = testdata_path().join("lsp");
- let path = fixtures_path.join(path);
- fs::read_to_string(path).unwrap()
- }
-
- fn init(init_path: &str) -> LspClient {
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", load_fixture(init_path))
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- client
- }
-
- fn did_open<V>(
- client: &mut LspClient,
- params: V,
- ) -> Vec<lsp::PublishDiagnosticsParams>
- where
- V: Serialize,
- {
- client
- .write_notification("textDocument/didOpen", params)
- .unwrap();
-
- handle_configuration_request(
- client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
- read_diagnostics(client).0
- }
-
- fn handle_configuration_request(client: &mut LspClient, result: Value) {
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client.write_response(id, result).unwrap();
- }
-
- fn read_diagnostics(client: &mut LspClient) -> CollectedDiagnostics {
- // diagnostics come in batches of three unless they're cancelled
- let mut diagnostics = vec![];
- for _ in 0..3 {
- let (method, response) = client
- .read_notification::<lsp::PublishDiagnosticsParams>()
- .unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- diagnostics.push(response.unwrap());
- }
- CollectedDiagnostics(diagnostics)
- }
-
- fn shutdown(client: &mut LspClient) {
- client
- .write_request::<_, _, Value>("shutdown", json!(null))
- .unwrap();
- client.write_notification("exit", json!(null)).unwrap();
- }
-
- pub fn ensure_directory_specifier(
- mut specifier: ModuleSpecifier,
- ) -> ModuleSpecifier {
- let path = specifier.path();
- if !path.ends_with('/') {
- let new_path = format!("{}/", path);
- specifier.set_path(&new_path);
- }
- specifier
- }
-
- struct TestSession {
- client: LspClient,
- open_file_count: usize,
- }
-
- impl TestSession {
- pub fn from_file(init_path: &str) -> Self {
- Self::from_client(init(init_path))
- }
-
- pub fn from_client(client: LspClient) -> Self {
- Self {
- client,
- open_file_count: 0,
- }
- }
-
- pub fn did_open<V>(&mut self, params: V) -> CollectedDiagnostics
- where
- V: Serialize,
- {
- self
- .client
- .write_notification("textDocument/didOpen", params)
- .unwrap();
-
- let (id, method, _) = self.client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- self
- .client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- )
- .unwrap();
-
- self.open_file_count += 1;
- self.read_diagnostics()
- }
-
- pub fn read_diagnostics(&mut self) -> CollectedDiagnostics {
- let mut all_diagnostics = Vec::new();
- for _ in 0..self.open_file_count {
- all_diagnostics.extend(read_diagnostics(&mut self.client).0);
- }
- CollectedDiagnostics(all_diagnostics)
- }
-
- pub fn shutdown_and_exit(&mut self) {
- shutdown(&mut self.client);
- }
- }
-
- #[derive(Debug, Clone)]
- struct CollectedDiagnostics(Vec<lsp::PublishDiagnosticsParams>);
-
- impl CollectedDiagnostics {
- /// Gets the diagnostics that the editor will see after all the publishes.
- pub fn viewed(&self) -> Vec<lsp::Diagnostic> {
- self
- .viewed_messages()
- .into_iter()
- .flat_map(|m| m.diagnostics)
- .collect()
- }
-
- /// Gets the messages that the editor will see after all the publishes.
- pub fn viewed_messages(&self) -> Vec<lsp::PublishDiagnosticsParams> {
- // go over the publishes in reverse order in order to get
- // the final messages that will be shown in the editor
- let mut messages = Vec::new();
- let mut had_specifier = HashSet::new();
- for message in self.0.iter().rev() {
- if had_specifier.insert(message.uri.clone()) {
- messages.insert(0, message.clone());
- }
- }
- messages
- }
-
- pub fn with_source(&self, source: &str) -> lsp::PublishDiagnosticsParams {
- self
- .viewed_messages()
- .iter()
- .find(|p| {
- p.diagnostics
- .iter()
- .any(|d| d.source == Some(source.to_string()))
- })
- .map(ToOwned::to_owned)
- .unwrap()
- }
-
- pub fn with_file_and_source(
- &self,
- specifier: &str,
- source: &str,
- ) -> lsp::PublishDiagnosticsParams {
- let specifier = ModuleSpecifier::parse(specifier).unwrap();
- self
- .viewed_messages()
- .iter()
- .find(|p| {
- p.uri == specifier
- && p
- .diagnostics
- .iter()
- .any(|d| d.source == Some(source.to_string()))
- })
- .map(ToOwned::to_owned)
- .unwrap()
- }
- }
-
- #[test]
- fn lsp_startup_shutdown() {
- let mut client = init("initialize_params.json");
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_init_tsconfig() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let tsconfig =
- serde_json::to_vec_pretty(&load_fixture("lib.tsconfig.json")).unwrap();
- fs::write(temp_dir.path().join("lib.tsconfig.json"), tsconfig).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./lib.tsconfig.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "location.pathname;\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_tsconfig_types() {
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let temp_dir = TempDir::new();
- let tsconfig =
- serde_json::to_vec_pretty(&load_fixture("types.tsconfig.json")).unwrap();
- fs::write(temp_dir.path().join("types.tsconfig.json"), tsconfig).unwrap();
- let a_dts = load_fixture_str("a.d.ts");
- fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./types.tsconfig.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(a);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_tsconfig_bad_config_path() {
- let mut client = init("initialize_params_bad_config_option.json");
- let (method, maybe_params) = client.read_notification().unwrap();
- assert_eq!(method, "window/showMessage");
- assert_eq!(maybe_params, Some(lsp::ShowMessageParams {
- typ: lsp::MessageType::WARNING,
- message: "The path to the configuration file (\"bad_tsconfig.json\") is not resolvable.".to_string()
- }));
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.args);\n"
- }
- }),
- );
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
- }
-
- #[test]
- fn lsp_triple_slash_types() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let a_dts = load_fixture_str("a.d.ts");
- fs::write(temp_dir.path().join("a.d.ts"), a_dts).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": Url::from_file_path(temp_dir.path().join("test.ts")).unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "/// <reference types=\"./a.d.ts\" />\n\nconsole.log(a);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("importMap".to_string(), json!("import-map.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 2,
- "character": 12
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value":"(alias) const b: \"b\"\nimport b"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 12
- },
- "end": {
- "line": 2,
- "character": 13
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_data_url() {
- let mut client = init("initialize_params_import_map.json");
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import example from \"example\";\n"
- }
- }),
- );
-
- let mut diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- // This indicates that the import map from initialize_params_import_map.json
- // is applied correctly.
- assert!(diagnostics.any(|diagnostic| diagnostic.code
- == Some(lsp::NumberOrString::String("no-cache".to_string()))
- && diagnostic
- .message
- .contains("https://deno.land/x/example/mod.ts")));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_config_file() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
-
- let deno_import_map_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.import_map.jsonc"))
- .unwrap();
- fs::write(
- temp_dir.path().join("deno.import_map.jsonc"),
- deno_import_map_jsonc,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.import_map.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map.json")).unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
- }
- }),
- );
-
- let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
- assert_eq!(diagnostics.count(), 0);
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 2,
- "character": 12
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value":"(alias) const b: \"b\"\nimport b"
- },
- ""
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 12
- },
- "end": {
- "line": 2,
- "character": 13
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_deno_task() {
- let temp_dir = TempDir::new();
- let workspace_root = temp_dir.path().canonicalize().unwrap();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- fs::write(
- workspace_root.join("deno.jsonc"),
- r#"{
- "tasks": {
- "build": "deno test",
- "some:test": "deno bundle mod.ts"
- }
- }"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(workspace_root).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>("deno/task", json!(null))
- .unwrap();
-
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "name": "build",
- "detail": "deno test"
- },
- {
- "name": "some:test",
- "detail": "deno bundle mod.ts"
- }
- ]))
- );
- }
-
- #[test]
- fn lsp_import_assertions() {
- let mut client = init("initialize_params_import_map.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/test.json",
- "languageId": "json",
- "version": 1,
- "text": "{\"a\":1}"
- }
- }),
- )
- .unwrap();
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
-
- let diagnostics = CollectedDiagnostics(did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import a from \"./test.json\";\n\nconsole.log(a);\n"
- }
- }),
- ));
-
- assert_eq!(
- json!(
- diagnostics
- .with_file_and_source("file:///a/a.ts", "deno")
- .diagnostics
- ),
- json!([
- {
- "range": {
- "start": {
- "line": 0,
- "character": 14
- },
- "end": {
- "line": 0,
- "character": 27
- }
- },
- "severity": 1,
- "code": "no-assert-type",
- "source": "deno",
- "message": "The module is a JSON module and not being imported with an import assertion. Consider adding `assert { type: \"json\" }` to the import statement."
- }
- ])
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_import_assertion.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_import_assertion.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_import_map_import_completions() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let import_map =
- serde_json::to_vec_pretty(&load_fixture("import-map-completions.json"))
- .unwrap();
- fs::write(temp_dir.path().join("import-map.json"), import_map).unwrap();
- fs::create_dir(temp_dir.path().join("lib")).unwrap();
- fs::write(
- temp_dir.path().join("lib").join("b.ts"),
- r#"export const b = "b";"#,
- )
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("importMap".to_string(), json!("import-map.json"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
- let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": uri,
- "languageId": "typescript",
- "version": 1,
- "text": "import * as a from \"/~/b.ts\";\nimport * as b from \"\""
- }
- }),
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 1,
- "character": 20
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "\""
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "isIncomplete": false,
- "items": [
- {
- "label": ".",
- "kind": 19,
- "detail": "(local)",
- "sortText": "1",
- "insertText": ".",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "..",
- "kind": 19,
- "detail": "(local)",
- "sortText": "1",
- "insertText": "..",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "std",
- "kind": 19,
- "detail": "(import map)",
- "sortText": "std",
- "insertText": "std",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "fs",
- "kind": 17,
- "detail": "(import map)",
- "sortText": "fs",
- "insertText": "fs",
- "commitCharacters": ["\"", "'"],
- },
- {
- "label": "/~",
- "kind": 19,
- "detail": "(import map)",
- "sortText": "/~",
- "insertText": "/~",
- "commitCharacters": ["\"", "'"],
- }
- ]
- }))
- );
-
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": uri,
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 1,
- "character": 20
- },
- "end": {
- "line": 1,
- "character": 20
- }
- },
- "text": "/~/"
- }
- ]
- }),
- )
- .unwrap();
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": uri
- },
- "position": {
- "line": 1,
- "character": 23
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "/"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "b.ts",
- "kind": 9,
- "detail": "(import map)",
- "sortText": "1",
- "filterText": "/~/b.ts",
- "textEdit": {
- "range": {
- "start": {
- "line": 1,
- "character": 20
- },
- "end": {
- "line": 1,
- "character": 23
- }
- },
- "newText": "/~/b.ts"
- },
- "commitCharacters": ["\"", "'"],
- }
- ]
- }))
- );
-
- 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.\n\nGive the following command line invocation of Deno:\n\n```sh\ndeno run --allow-read https://deno.land/std/examples/cat.ts /etc/passwd\n```\n\nThen `Deno.args` will contain:\n\n```\n[ \"/etc/passwd\" ]\n```\n\nIf you are looking for a structured way to parse arguments, there is the\n[`std/flags`](https://deno.land/std/flags) module as part of the Deno\nstandard library.",
- "\n\n*@category* - Runtime Environment",
- ],
- "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();
-
- handle_configuration_request(&mut client, json!([{ "enable": false }]));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_inlay_hints() {
- let mut client = init("initialize_params_hints.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"function a(b: string) {
- return b;
- }
-
- a("foo");
-
- enum C {
- A,
- }
-
- parseInt("123", 8);
-
- const d = Date.now();
-
- class E {
- f = Date.now();
- }
-
- ["a"].map((v) => v + v);
- "#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/inlayHint",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 19,
- "character": 0,
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- json!(maybe_res),
- json!([
- {
- "position": {
- "line": 0,
- "character": 21
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 4,
- "character": 10
- },
- "label": "b:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 7,
- "character": 11
- },
- "label": "= 0",
- "paddingLeft": true
- },
- {
- "position": {
- "line": 10,
- "character": 17
- },
- "label": "string:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 10,
- "character": 24
- },
- "label": "radix:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 12,
- "character": 15
- },
- "label": ": number",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 15,
- "character": 11
- },
- "label": ": number",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 18,
- "character": 18
- },
- "label": "callbackfn:",
- "kind": 2,
- "paddingRight": true
- },
- {
- "position": {
- "line": 18,
- "character": 20
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- },
- {
- "position": {
- "line": 18,
- "character": 21
- },
- "label": ": string",
- "kind": 1,
- "paddingLeft": true
- }
- ])
- );
- }
-
- #[test]
- fn lsp_inlay_hints_not_enabled() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"function a(b: string) {
- return b;
- }
-
- a("foo");
-
- enum C {
- A,
- }
-
- parseInt("123", 8);
-
- const d = Date.now();
-
- class E {
- f = Date.now();
- }
-
- ["a"].map((v) => v + v);
- "#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/inlayHint",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 19,
- "character": 0,
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(json!(maybe_res), json!(null));
- }
-
- #[test]
- fn lsp_workspace_enable_paths() {
- let mut params: lsp::InitializeParams = serde_json::from_value(
- load_fixture("initialize_params_workspace_enable_paths.json"),
- )
- .unwrap();
- // we aren't actually writing anything to the tempdir in this test, but we
- // just need a legitimate file path on the host system so that logic that
- // tries to convert to and from the fs paths works on all env
- let temp_dir = TempDir::new();
-
- let root_specifier =
- ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
-
- params.root_uri = Some(root_specifier.clone());
- params.workspace_folders = Some(vec![lsp::WorkspaceFolder {
- uri: root_specifier.clone(),
- name: "project".to_string(),
- }]);
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": false,
- "enablePaths": [
- "./worker"
- ],
- }]),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./other/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Date.now());\n"
- }
- }),
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./other/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(method) DateConstructor.now(): number",
- },
- "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17,
- },
- "end": {
- "line": 0,
- "character": 20,
- }
- }
- }))
- );
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": root_specifier.join("./worker/subdir/file.ts").unwrap(),
- },
- "position": {
- "line": 0,
- "character": 19
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "(method) DateConstructor.now(): number",
- },
- "Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC)."
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 17,
- },
- "end": {
- "line": 0,
- "character": 20,
- }
- }
- }))
- );
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_unstable_disabled() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console.log(Deno.dlopen);\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": 23
- }
- }
- }))
- );
- 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.ppid);\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.ppid: number"
- },
- "The process ID of parent process of this instance of the Deno CLI.\n\n```ts\nconsole.log(Deno.ppid);\n```",
- "\n\n*@category* - Runtime Environment",
- ],
- "range":{
- "start":{
- "line":0,
- "character":17
- },
- "end":{
- "line":0,
- "character":21
- }
- }
- }))
- );
- 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,
- // the LSP uses utf16 encoded characters indexes, so
- // after the deno emoiji is character index 15
- "character": 15
- }
- },
- "text": ""
- }
- ]
- }),
- )
- .unwrap();
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().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": 15
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const b: \"πŸ˜ƒ\"",
- },
- "",
- ],
- "range": {
- "start": {
- "line": 2,
- "character": 15,
- },
- "end": {
- "line": 2,
- "character": 16,
- },
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_closed_document() {
- let temp_dir_guard = TempDir::new();
- let temp_dir = temp_dir_guard.path();
- let a_path = temp_dir.join("a.ts");
- fs::write(a_path, r#"export const a = "a";"#).unwrap();
- let b_path = temp_dir.join("b.ts");
- fs::write(&b_path, r#"export * from "./a.ts";"#).unwrap();
- let b_specifier = Url::from_file_path(b_path).unwrap();
- let c_path = temp_dir.join("c.ts");
- fs::write(&c_path, "import { a } from \"./b.ts\";\nconsole.log(a);\n")
- .unwrap();
- let c_specifier = Url::from_file_path(c_path).unwrap();
-
- 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();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .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 (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(id, json!([{ "enable": true }]))
- .unwrap();
-
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().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_hover_dependency() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }),
- );
- did_open(
- &mut client,
- load_fixture("did_open_params_import_hover.json"),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 62
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 3,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/type_reference.js\n\n**Types**: http&#8203;://127.0.0.1:4545/subdir/type_reference.d.ts\n"
- },
- "range": {
- "start": {
- "line": 3,
- "character": 19
- },
- "end":{
- "line": 3,
- "character": 67
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 4,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/subdir/mod1.ts\n"
- },
- "range": {
- "start": {
- "line": 4,
- "character": 19
- },
- "end":{
- "line": 4,
- "character": 57
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 5,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: _(a data url)_\n"
- },
- "range": {
- "start": {
- "line": 5,
- "character": 19
- },
- "end":{
- "line": 5,
- "character": 132
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 6,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: file&#8203;:///a/file_01.ts\n"
- },
- "range": {
- "start": {
- "line": 6,
- "character": 19
- },
- "end":{
- "line": 6,
- "character": 33
- }
- }
- }))
- );
- }
-
- // This tests for a regression covered by denoland/deno#12753 where the lsp was
- // unable to resolve dependencies when there was an invalid syntax in the module
- #[test]
- fn lsp_hover_deps_preserved_when_invalid_parse() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file1.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export type Foo = { bar(): string };\n"
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { Foo } from './file1.ts'; declare const f: Foo; f\n"
- }
- }),
- );
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts"
- },
- "position": {
- "line": 0,
- "character": 56
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const f: Foo",
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 56,
- },
- "end": {
- "line": 0,
- "character": 57,
- }
- }
- }))
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 57
- },
- "end": {
- "line": 0,
- "character": 58
- }
- },
- "text": "."
- }
- ]
- }),
- )
- .unwrap();
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file2.ts"
- },
- "position": {
- "line": 0,
- "character": 56
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "const f: Foo",
- },
- ""
- ],
- "range": {
- "start": {
- "line": 0,
- "character": 56,
- },
- "end": {
- "line": 0,
- "character": 57,
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_typescript_types() {
- 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/xTypeScriptTypes.js\";\n\nconsole.log(a.foo);\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/xTypeScriptTypes.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 24
- }
- }),
- )
- .unwrap();
- assert!(maybe_res.is_some());
- assert!(maybe_err.is_none());
- assert_eq!(
- json!(maybe_res.unwrap()),
- json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end": {
- "line": 0,
- "character": 62
- }
- }
- })
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_hover_jsdoc_symbol_link() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/b.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export function hello() {}\n"
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { hello } from \"./b.ts\";\n\nhello();\n\nconst b = \"b\";\n\n/** JSDoc {@link hello} and {@linkcode b} */\nfunction a() {}\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 7,
- "character": 10
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": [
- {
- "language": "typescript",
- "value": "function a(): void"
- },
- "JSDoc [hello](file:///a/file.ts#L1,10) and [`b`](file:///a/file.ts#L5,7)"
- ],
- "range": {
- "start": {
- "line": 7,
- "character": 9
- },
- "end": {
- "line": 7,
- "character": 10
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_goto_type_definition() {
- 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 a: string;\n}\n\nexport class B implements A {\n a = \"a\";\n log() {\n console.log(this.a);\n }\n}\n\nconst b = new B();\nb;\n",
- }
- }),
- );
- let (maybe_res, maybe_error) = client
- .write_request::<_, _, Value>(
- "textDocument/typeDefinition",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 12,
- "character": 1
- }
- }),
- )
- .unwrap();
- assert!(maybe_error.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "targetUri": "file:///a/file.ts",
- "targetRange": {
- "start": {
- "line": 4,
- "character": 0
- },
- "end": {
- "line": 9,
- "character": 1
- }
- },
- "targetSelectionRange": {
- "start": {
- "line": 4,
- "character": 13
- },
- "end": {
- "line": 4,
- "character": 14
- }
- }
- }
- ]))
- );
- 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_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/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();
- 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,
- // this should not rename in comments and strings
- "text": "let variable = 'a'; // variable\nconsole.log(variable);\n\"variable\";\n"
- }
- }),
- );
- 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\ninterface C {\n c: string;\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"))
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "codeLens/resolve",
- json!({
- "range": {
- "start": {
- "line": 10,
- "character": 10
- },
- "end": {
- "line": 10,
- "character": 11
- }
- },
- "data": {
- "specifier": "file:///a/file.ts",
- "source": "implementations"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "range": {
- "start": {
- "line": 10,
- "character": 10
- },
- "end": {
- "line": 10,
- "character": 11
- }
- },
- "command": {
- "title": "0 implementations",
- "command": ""
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_test() {
- let mut client = init("initialize_params_code_lens_test.json");
- did_open(
- &mut client,
- load_fixture("did_open_params_test_code_lens.json"),
- );
- 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_test.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_lens_test_disabled() {
- let mut client = init("initialize_params_code_lens_test_disabled.json");
- client
- .write_notification(
- "textDocument/didOpen",
- load_fixture("did_open_params_test_code_lens.json"),
- )
- .unwrap();
-
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(
- id,
- json!([{
- "enable": true,
- "codeLens": {
- "test": false
- }
- }]),
- )
- .unwrap();
-
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- let (method, _) = client.read_notification::<Value>().unwrap();
- assert_eq!(method, "textDocument/publishDiagnostics");
- 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(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<lsp::CodeLens>>(
- "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_nav_tree_updates() {
- 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\ninterface C {\n c: string;\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"))
- );
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 10,
- "character": 0
- },
- "end": {
- "line": 13,
- "character": 0
- }
- },
- "text": ""
- }
- ]
- }),
- )
- .unwrap();
- 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_changed.json"))
- );
- 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": {
- "kind": "markdown",
- "value": "Adds two numbers."
- },
- "parameters": [
- {
- "label": "a: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a first number."
- }
- },
- {
- "label": "b: number",
- "documentation": {
- "kind": "markdown",
- "value": "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": {
- "kind": "markdown",
- "value": "Adds two numbers."
- },
- "parameters": [
- {
- "label": "a: number",
- "documentation": {
- "kind": "markdown",
- "value": "This is a first number."
- }
- },
- {
- "label": "b: number",
- "documentation": {
- "kind": "markdown",
- "value": "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 session = TestSession::from_file("initialize_params.json");
- let diagnostics = session.did_open(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"
- }
- }));
- assert_eq!(
- diagnostics.with_source("deno"),
- load_fixture_as("diagnostics_deno_deps.json")
- );
-
- let (maybe_res, maybe_err) = session
- .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"))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_deno_cache_npm() {
- let mut session = TestSession::from_file("initialize_params.json");
- let diagnostics = session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import chalk from \"npm:chalk\";\n\nconsole.log(chalk.green);\n"
- }
- }));
- assert_eq!(
- diagnostics.with_source("deno"),
- load_fixture_as("code_actions/cache_npm/diagnostics.json")
- );
-
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_actions/cache_npm/cache_action.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_actions/cache_npm/cache_response.json"))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_imports() {
- let mut session = TestSession::from_file("initialize_params.json");
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file00.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"export interface MallardDuckConfigOptions extends DuckConfigOptions {
- kind: "mallard";
-}
-
-export class MallardDuckConfig extends DuckConfig {
- constructor(options: MallardDuckConfigOptions) {
- super(options);
- }
-}
-"#
- }
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"import { DuckConfigOptions } from "./file02.ts";
-
-export class DuckConfig {
- readonly kind;
- constructor(options: DuckConfigOptions) {
- this.kind = options.kind;
- }
-}
-"#
- }
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file02.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"export interface DuckConfigOptions {
- kind: string;
- quacks: boolean;
-}
-"#
- }
- }));
-
- let (maybe_res, maybe_err) = session
- .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) = session
- .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"))
- );
-
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_code_actions_refactor() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "var x: { a?: number; b?: string } = {};\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_params_refactor.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_refactor.json"))
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "codeAction/resolve",
- load_fixture("code_action_resolve_params_refactor.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_resolve_response_refactor.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_refactor_no_disabled_support() {
- let mut client = init("initialize_params_ca_no_disabled.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "interface A {\n a: string;\n}\n\ninterface B {\n b: string;\n}\n\nclass AB implements A, B {\n a = \"a\";\n b = \"b\";\n}\n\nnew AB().a;\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 14,
- "character": 0
- }
- },
- "context": {
- "diagnostics": [],
- "only": [
- "refactor"
- ]
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_response_no_disabled.json"))
- );
- 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::<Value>().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());
- read_diagnostics(&mut client);
- 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());
-
- read_diagnostics(&mut client);
-
- 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_private_fields() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": r#"class Foo { #myProperty = "value"; constructor() { this.# } }"#
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 57
- },
- "context": {
- "triggerKind": 1
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert_eq!(list.items.len(), 1);
- let item = &list.items[0];
- assert_eq!(item.label, "#myProperty");
- assert!(!list.is_incomplete);
- } else {
- panic!("unexpected response");
- }
- 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": "11",
- "filterText": "b",
- "insertText": "b",
- "commitCharacters": [".", ",", ";", "("],
- "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_auto_import() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/b.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const foo = \"foo\";\n",
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export {};\n\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 0,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- if !list.items.iter().any(|item| item.label == "foo") {
- panic!("completions items missing 'foo' symbol");
- }
- } else {
- panic!("unexpected completion response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- json!({
- "label": "foo",
- "kind": 6,
- "sortText": "οΏΏ16",
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/file.ts",
- "position": 12,
- "name": "foo",
- "source": "./b",
- "data": {
- "exportName": "foo",
- "moduleSpecifier": "./b",
- "fileName": "file:///a/b.ts"
- },
- "useCodeSnippet": false
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "label": "foo",
- "kind": 6,
- "detail": "const foo: \"foo\"",
- "documentation": {
- "kind": "markdown",
- "value": ""
- },
- "sortText": "οΏΏ16",
- "additionalTextEdits": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 0
- },
- "end": {
- "line": 0,
- "character": 0
- }
- },
- "newText": "import { foo } from \"./b.ts\";\n\n"
- }
- ]
- }))
- );
- }
-
- #[test]
- fn lsp_completions_snippet() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx"
- },
- "position": {
- "line": 5,
- "character": 13,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(
- json!(list),
- json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2,
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }
- ]
- })
- );
- } else {
- panic!("unexpected completion response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- json!({
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2,
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "label": "type",
- "kind": 5,
- "detail": "(property) type: string",
- "documentation": {
- "kind": "markdown",
- "value": ""
- },
- "sortText": "11",
- "filterText": "type=\"$1\"",
- "insertText": "type=\"$1\"",
- "insertTextFormat": 2
- }))
- );
- }
-
- #[test]
- fn lsp_completions_no_snippet() {
- let mut client = init("initialize_params_no_snippet.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text": "function A({ type }: { type: string }) {\n return type;\n}\n\nfunction B() {\n return <A t\n}",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/a.tsx"
- },
- "position": {
- "line": 5,
- "character": 13,
- },
- "context": {
- "triggerKind": 1,
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
- assert!(!list.is_incomplete);
- assert_eq!(
- json!(list),
- json!({
- "isIncomplete": false,
- "items": [
- {
- "label": "type",
- "kind": 5,
- "sortText": "11",
- "commitCharacters": [
- ".",
- ",",
- ";",
- "("
- ],
- "data": {
- "tsc": {
- "specifier": "file:///a/a.tsx",
- "position": 87,
- "name": "type",
- "useCodeSnippet": false
- }
- }
- }
- ]
- })
- );
- } else {
- panic!("unexpected completion response");
- }
- }
-
- #[test]
- fn lsp_completions_npm() {
- 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 cjsDefault from 'npm:@denotest/cjs-default-export';import chalk from 'npm:chalk';\n\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [
- {
- "uri": "npm:@denotest/cjs-default-export",
- },
- {
- "uri": "npm:chalk",
- }
- ]
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
-
- // check importing a cjs default import
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "text": "cjsDefault."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 11
- },
- "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);
- assert!(list.items.iter().any(|i| i.label == "default"));
- assert!(list.items.iter().any(|i| i.label == "MyClass"));
- } else {
- panic!("unexpected response");
- }
- let (maybe_res, maybe_err) = client
- .write_request(
- "completionItem/resolve",
- load_fixture("completions/npm/resolve_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("completions/npm/resolve_response.json"))
- );
-
- // now check chalk, which is esm
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "version": 3
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 11
- }
- },
- "text": "chalk."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 2,
- "character": 6
- },
- "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.iter().any(|i| i.label == "green"));
- assert!(list.items.iter().any(|i| i.label == "red"));
- } else {
- panic!("unexpected response");
- }
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_npm_specifier_unopened_file() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
-
- // create other.ts, which re-exports an npm specifier
- client.deno_dir().write(
- "other.ts",
- "export { default as chalk } from 'npm:chalk@5';",
- );
-
- // cache the other.ts file to the DENO_DIR
- let deno = deno_cmd_with_deno_dir(client.deno_dir())
- .current_dir(client.deno_dir().path())
- .arg("cache")
- .arg("--quiet")
- .arg("other.ts")
- .envs(env_vars_for_npm_tests())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- .unwrap();
- let output = deno.wait_with_output().unwrap();
- assert!(output.status.success());
- assert_eq!(output.status.code(), Some(0));
-
- let stdout = String::from_utf8(output.stdout).unwrap();
- assert!(stdout.is_empty());
- let stderr = String::from_utf8(output.stderr).unwrap();
- assert!(stderr.is_empty());
-
- // open main.ts, which imports other.ts (unopened)
- let main_url =
- ModuleSpecifier::from_file_path(client.deno_dir().path().join("main.ts"))
- .unwrap();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": main_url,
- "languageId": "typescript",
- "version": 1,
- "text": "import { chalk } from './other.ts';\n\n",
- }
- }),
- );
-
- client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": main_url,
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "text": "chalk."
- }
- ]
- }),
- )
- .unwrap();
- read_diagnostics(&mut client);
-
- // now ensure completions work
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": main_url
- },
- "position": {
- "line": 2,
- "character": 6
- },
- "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(), 63);
- assert!(list.items.iter().any(|i| i.label == "ansi256"));
- } else {
- panic!("unexpected response");
- }
- }
-
- #[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);
- }
-
- #[test]
- fn lsp_auto_discover_registry() {
- 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://localhost:4545/x/a@\""
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/completion",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "position": {
- "line": 0,
- "character": 46
- },
- "context": {
- "triggerKind": 2,
- "triggerCharacter": "@"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (method, maybe_res) = client.read_notification().unwrap();
- assert_eq!(method, "deno/registryState");
- assert_eq!(
- maybe_res,
- Some(json!({
- "origin": "http://localhost:4545",
- "suggestions": true,
- }))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_cache_location() {
- let _g = http_server();
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params_registry.json"))
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("cache".to_string(), json!(".cache"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- let mut session = TestSession::from_client(client);
-
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }));
- let diagnostics =
- session.did_open(load_fixture("did_open_params_import_hover.json"));
- assert_eq!(diagnostics.viewed().len(), 7);
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.js\n\n**Types**: http&#8203;://127.0.0.1:4545/xTypeScriptTypes.d.ts\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 62
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 7,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
- },
- "range": {
- "start": {
- "line": 7,
- "character": 19
- },
- "end": {
- "line": 7,
- "character": 53
- }
- }
- }))
- );
- let cache_path = temp_dir.path().join(".cache");
- assert!(cache_path.is_dir());
- assert!(cache_path.join("gen").is_dir());
- session.shutdown_and_exit();
- }
-
- /// Sets the TLS root certificate on startup, which allows the LSP to connect to
- /// the custom signed test server and be able to retrieve the registry config
- /// and cache files.
- #[test]
- fn lsp_tls_cert() {
- let _g = http_server();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params_tls_cert.json"))
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(testdata_path()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- client.write_notification("initialized", json!({})).unwrap();
- let mut session = TestSession::from_client(client);
-
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- }
- }));
- let diagnostics =
- session.did_open(load_fixture("did_open_params_tls_cert.json"));
- let diagnostics = diagnostics.viewed();
- assert_eq!(diagnostics.len(), 7);
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.ts",
- },
- "uris": [],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = session
- .client
- .write_request(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 0,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: https&#8203;://localhost:5545/xTypeScriptTypes.js\n"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end":{
- "line": 0,
- "character": 63
- }
- }
- }))
- );
- let (maybe_res, maybe_err) = session
- .client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- },
- "position": {
- "line": 7,
- "character": 28
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
- },
- "range": {
- "start": {
- "line": 7,
- "character": 19
- },
- "end": {
- "line": 7,
- "character": 53
- }
- }
- }))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_diagnostics_warn_redirect() {
- 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/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/x_deno_warning.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let diagnostics = read_diagnostics(&mut client);
- assert_eq!(
- diagnostics.with_source("deno"),
- 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: 60
- }
- },
- 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()
- },
- lsp::Diagnostic {
- range: lsp::Range {
- start: lsp::Position {
- line: 0,
- character: 19
- },
- end: lsp::Position {
- line: 0,
- character: 60
- }
- },
- severity: Some(lsp::DiagnosticSeverity::INFORMATION),
- code: Some(lsp::NumberOrString::String("redirect".to_string())),
- source: Some("deno".to_string()),
- message: "The import of \"http://127.0.0.1:4545/x_deno_warning.js\" was redirected to \"http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js\".".to_string(),
- data: Some(json!({"specifier": "http://127.0.0.1:4545/x_deno_warning.js", "redirect": "http://127.0.0.1:4545/lsp/x_deno_warning_redirect.js"})),
- ..Default::default()
- }
- ],
- version: Some(1),
- }
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_redirect_quick_fix() {
- 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/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/x_deno_warning.js",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let diagnostics = read_diagnostics(&mut client)
- .with_source("deno")
- .diagnostics;
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- json!(json!({
- "textDocument": {
- "uri": "file:///a/file.ts"
- },
- "range": {
- "start": {
- "line": 0,
- "character": 19
- },
- "end": {
- "line": 0,
- "character": 60
- }
- },
- "context": {
- "diagnostics": diagnostics,
- "only": [
- "quickfix"
- ]
- }
- })),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_redirect_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_diagnostics_deprecated() {
- let mut client = init("initialize_params.json");
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "/** @deprecated */\nexport const a = \"a\";\n\na;\n",
- },
- }),
- );
- assert_eq!(
- json!(diagnostics),
- json!([
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [],
- "version": 1
- },
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [],
- "version": 1
- },
- {
- "uri": "file:///a/file.ts",
- "diagnostics": [
- {
- "range": {
- "start": {
- "line": 3,
- "character": 0
- },
- "end": {
- "line": 3,
- "character": 1
- }
- },
- "severity": 4,
- "code": 6385,
- "source": "deno-ts",
- "message": "'a' is deprecated.",
- "relatedInformation": [],
- "tags": [
- 2
- ]
- }
- ],
- "version": 1
- }
- ])
- );
- 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::<Value>().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 diagnostics = read_diagnostics(&mut client);
- assert_eq!(diagnostics.viewed().len(), 5);
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_diagnostics_refresh_dependents() {
- let mut session = TestSession::from_file("initialize_params.json");
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_00.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export const a = \"a\";\n",
- },
- }));
- session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export * from \"./file_00.ts\";\n",
- },
- }));
- let diagnostics = session.did_open(json!({
- "textDocument": {
- "uri": "file:///a/file_02.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "import { a, b } from \"./file_01.ts\";\n\nconsole.log(a, b);\n"
- }
- }));
- assert_eq!(
- json!(diagnostics.with_file_and_source("file:///a/file_02.ts", "deno-ts")),
- json!({
- "uri": "file:///a/file_02.ts",
- "diagnostics": [
- {
- "range": {
- "start": {
- "line": 0,
- "character": 12
- },
- "end": {
- "line": 0,
- "character": 13
- }
- },
- "severity": 1,
- "code": 2305,
- "source": "deno-ts",
- "message": "Module '\"./file_01.ts\"' has no exported member 'b'."
- }
- ],
- "version": 1
- })
- );
-
- // fix the code causing the diagnostic
- session
- .client
- .write_notification(
- "textDocument/didChange",
- json!({
- "textDocument": {
- "uri": "file:///a/file_00.ts",
- "version": 2
- },
- "contentChanges": [
- {
- "range": {
- "start": {
- "line": 1,
- "character": 0
- },
- "end": {
- "line": 1,
- "character": 0
- }
- },
- "text": "export const b = \"b\";\n"
- }
- ]
- }),
- )
- .unwrap();
- let diagnostics = session.read_diagnostics();
- assert_eq!(diagnostics.viewed().len(), 0); // no diagnostics now
-
- session.shutdown_and_exit();
- assert_eq!(session.client.queue_len(), 0);
- }
-
- #[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<PerformanceAverage>,
- }
-
- #[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!(null),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- if let Some(res) = maybe_res {
- assert_eq!(res.averages.len(), 13);
- } else {
- panic!("unexpected result");
- }
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_no_changes() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console;\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!(null)));
- client.assert_no_notification("window/showMessage");
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_error() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "console test test\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!(null)));
- 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_format_exclude_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_fmt_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc"))
- .unwrap();
- fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let file_uri =
- ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts"))
- .unwrap()
- .to_string();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": file_uri,
- "languageId": "typescript",
- "version": 1,
- "text": "function myFunc(){}"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": file_uri
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_exclude_default_config() {
- let temp_dir = TempDir::new();
- let workspace_root = temp_dir.path().canonicalize().unwrap();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.exclude.jsonc"))
- .unwrap();
- fs::write(workspace_root.join("deno.jsonc"), deno_jsonc).unwrap();
-
- params.root_uri =
- Some(Url::from_file_path(workspace_root.clone()).unwrap());
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let file_uri =
- ModuleSpecifier::from_file_path(workspace_root.join("ignored.ts"))
- .unwrap()
- .to_string();
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": file_uri,
- "languageId": "typescript",
- "version": 1,
- "text": "function myFunc(){}"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/formatting",
- json!({
- "textDocument": {
- "uri": file_uri
- },
- "options": {
- "tabSize": 2,
- "insertSpaces": true
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_format_json() {
- let mut client = init("initialize_params.json");
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- // Also test out using a non-json file extension here.
- // What should matter is the language identifier.
- "uri": "file:///a/file.lock",
- "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.lock"
- },
- "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_json_no_diagnostics() {
- 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(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.json"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.json"
- },
- "position": {
- "line": 0,
- "character": 3
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- 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);
- }
-
- #[test]
- fn lsp_format_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_fmt_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.fmt.jsonc")).unwrap();
- fs::write(temp_dir.path().join("deno.fmt.jsonc"), deno_fmt_jsonc).unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.fmt.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export async function someVeryLongFunctionName() {\nconst response = fetch(\"http://localhost:4545/some/non/existent/path.json\");\nconsole.log(response.text());\nconsole.log(\"finished!\")\n}"
- }
- }),
- )
- .unwrap();
-
- // The options below should be ignored in favor of configuration from config file.
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "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!([{
- "range": {
- "start": {
- "line": 1,
- "character": 0
- },
- "end": {
- "line": 1,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 1,
- "character": 23
- },
- "end": {
- "line": 1,
- "character": 24
- }
- },
- "newText": "\n\t\t'"
- },
- {
- "range": {
- "start": {
- "line": 1,
- "character": 73
- },
- "end": {
- "line": 1,
- "character": 74
- }
- },
- "newText": "',\n\t"
- },
- {
- "range": {
- "start": {
- "line": 2,
- "character": 0
- },
- "end": {
- "line": 2,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 0
- },
- "end": {
- "line": 3,
- "character": 0
- }
- },
- "newText": "\t"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 12
- },
- "end": {
- "line": 3,
- "character": 13
- }
- },
- "newText": "'"
- },
- {
- "range": {
- "start": {
- "line": 3,
- "character": 22
- },
- "end": {
- "line": 3,
- "character": 24
- }
- },
- "newText": "');"
- },
- {
- "range": {
- "start": {
- "line": 4,
- "character": 1
- },
- "end": {
- "line": 4,
- "character": 1
- }
- },
- "newText": "\n"
- }]
- ))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_markdown_no_diagnostics() {
- 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(
- "textDocument/semanticTokens/full",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md"
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.md"
- },
- "position": {
- "line": 0,
- "character": 3
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(maybe_res, Some(json!(null)));
-
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_configuration_did_change() {
- let _g = http_server();
- let mut client = init("initialize_params_did_config_change.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@\""
- }
- }),
- );
- client
- .write_notification(
- "workspace/didChangeConfiguration",
- json!({
- "settings": {}
- }),
- )
- .unwrap();
- let (id, method, _) = client.read_request::<Value>().unwrap();
- assert_eq!(method, "workspace/configuration");
- client
- .write_response(
- id,
- json!([{
- "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
- }]),
- )
- .unwrap();
- 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_workspace_symbol() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export class A {\n fieldA: string;\n fieldB: string;\n}\n",
- }
- }),
- );
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file_01.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "export class B {\n fieldC: string;\n fieldD: string;\n}\n",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "workspace/symbol",
- json!({
- "query": "field"
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!([
- {
- "name": "fieldA",
- "kind": 8,
- "location": {
- "uri": "file:///a/file.ts",
- "range": {
- "start": {
- "line": 1,
- "character": 2
- },
- "end": {
- "line": 1,
- "character": 17
- }
- }
- },
- "containerName": "A"
- },
- {
- "name": "fieldB",
- "kind": 8,
- "location": {
- "uri": "file:///a/file.ts",
- "range": {
- "start": {
- "line": 2,
- "character": 2
- },
- "end": {
- "line": 2,
- "character": 17
- }
- }
- },
- "containerName": "A"
- },
- {
- "name": "fieldC",
- "kind": 8,
- "location": {
- "uri": "file:///a/file_01.ts",
- "range": {
- "start": {
- "line": 1,
- "character": 2
- },
- "end": {
- "line": 1,
- "character": 17
- }
- }
- },
- "containerName": "B"
- },
- {
- "name": "fieldD",
- "kind": 8,
- "location": {
- "uri": "file:///a/file_01.ts",
- "range": {
- "start": {
- "line": 2,
- "character": 2
- },
- "end": {
- "line": 2,
- "character": 17
- }
- }
- },
- "containerName": "B"
- }
- ]))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_code_actions_ignore_lint() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text": "let message = 'Hello, Deno!';\nconsole.log(message);\n"
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_ignore_lint_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_ignore_lint_response.json"))
- );
- shutdown(&mut client);
- }
-
- /// This test exercises updating an existing deno-lint-ignore-file comment.
- #[test]
- fn lsp_code_actions_update_ignore_lint() {
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.ts",
- "languageId": "typescript",
- "version": 1,
- "text":
-"#!/usr/bin/env -S deno run
-// deno-lint-ignore-file camelcase
-let snake_case = 'Hello, Deno!';
-console.log(snake_case);
-",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request(
- "textDocument/codeAction",
- load_fixture("code_action_update_ignore_lint_params.json"),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(load_fixture("code_action_update_ignore_lint_response.json"))
- );
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_lint_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_lint_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.lint.jsonc")).unwrap();
- fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc)
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.lint.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
- let mut session = TestSession::from_client(client);
-
- let diagnostics = session.did_open(load_fixture("did_open_lint.json"));
- let diagnostics = diagnostics.viewed();
- assert_eq!(diagnostics.len(), 1);
- assert_eq!(
- diagnostics[0].code,
- Some(lsp::NumberOrString::String("ban-untagged-todo".to_string()))
- );
- session.shutdown_and_exit();
- }
-
- #[test]
- fn lsp_lint_exclude_with_config() {
- let temp_dir = TempDir::new();
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let deno_lint_jsonc =
- serde_json::to_vec_pretty(&load_fixture("deno.lint.exclude.jsonc"))
- .unwrap();
- fs::write(temp_dir.path().join("deno.lint.jsonc"), deno_lint_jsonc)
- .unwrap();
-
- params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
- if let Some(Value::Object(mut map)) = params.initialization_options {
- map.insert("config".to_string(), json!("./deno.lint.jsonc"));
- params.initialization_options = Some(Value::Object(map));
- }
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- let diagnostics = did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": ModuleSpecifier::from_file_path(temp_dir.path().join("ignored.ts")).unwrap().to_string(),
- "languageId": "typescript",
- "version": 1,
- "text": "// TODO: fixme\nexport async function non_camel_case() {\nconsole.log(\"finished!\")\n}"
- }
- }),
- );
- let diagnostics = diagnostics
- .into_iter()
- .flat_map(|x| x.diagnostics)
- .collect::<Vec<_>>();
- assert_eq!(diagnostics, Vec::new());
- shutdown(&mut client);
- }
-
- #[test]
- fn lsp_jsx_import_source_pragma() {
- let _g = http_server();
- let mut client = init("initialize_params.json");
- did_open(
- &mut client,
- json!({
- "textDocument": {
- "uri": "file:///a/file.tsx",
- "languageId": "typescriptreact",
- "version": 1,
- "text":
-"/** @jsxImportSource http://localhost:4545/jsx */
-
-function A() {
- return \"hello\";
-}
-
-export function B() {
- return <A></A>;
-}
-",
- }
- }),
- );
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "deno/cache",
- json!({
- "referrer": {
- "uri": "file:///a/file.tsx",
- },
- "uris": [
- {
- "uri": "http://127.0.0.1:4545/jsx/jsx-runtime",
- }
- ],
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, Value>(
- "textDocument/hover",
- json!({
- "textDocument": {
- "uri": "file:///a/file.tsx"
- },
- "position": {
- "line": 0,
- "character": 25
- }
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert_eq!(
- maybe_res,
- Some(json!({
- "contents": {
- "kind": "markdown",
- "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/jsx/jsx-runtime\n",
- },
- "range": {
- "start": {
- "line": 0,
- "character": 21
- },
- "end": {
- "line": 0,
- "character": 46
- }
- }
- }))
- );
- shutdown(&mut client);
- }
-
- #[derive(Debug, Clone, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- struct TestData {
- id: String,
- label: String,
- steps: Option<Vec<TestData>>,
- range: Option<lsp::Range>,
- }
-
- #[derive(Debug, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- enum TestModuleNotificationKind {
- Insert,
- Replace,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct TestModuleNotificationParams {
- text_document: lsp::TextDocumentIdentifier,
- kind: TestModuleNotificationKind,
- label: String,
- tests: Vec<TestData>,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct EnqueuedTestModule {
- text_document: lsp::TextDocumentIdentifier,
- ids: Vec<String>,
- }
-
- #[derive(Debug, Deserialize)]
- #[serde(rename_all = "camelCase")]
- struct TestRunResponseParams {
- enqueued: Vec<EnqueuedTestModule>,
- }
-
- #[test]
- fn lsp_testing_api() {
- let mut params: lsp::InitializeParams =
- serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
- let temp_dir = TempDir::new();
-
- let root_specifier =
- ensure_directory_specifier(Url::from_file_path(temp_dir.path()).unwrap());
-
- let module_path = temp_dir.path().join("./test.ts");
- let specifier = ModuleSpecifier::from_file_path(&module_path).unwrap();
- let contents = r#"
-Deno.test({
- name: "test a",
- fn() {
- console.log("test a");
- }
-});
-"#;
- fs::write(&module_path, contents).unwrap();
- fs::write(temp_dir.path().join("./deno.jsonc"), r#"{}"#).unwrap();
-
- params.root_uri = Some(root_specifier);
-
- let deno_exe = deno_exe_path();
- let mut client = LspClient::new(&deno_exe, false).unwrap();
- client
- .write_request::<_, _, Value>("initialize", params)
- .unwrap();
-
- client.write_notification("initialized", json!({})).unwrap();
-
- client
- .write_notification(
- "textDocument/didOpen",
- json!({
- "textDocument": {
- "uri": specifier,
- "languageId": "typescript",
- "version": 1,
- "text": contents,
- }
- }),
- )
- .unwrap();
-
- handle_configuration_request(
- &mut client,
- json!([{
- "enable": true,
- "codeLens": {
- "test": true
- }
- }]),
- );
-
- for _ in 0..4 {
- let result = client.read_notification::<Value>();
- assert!(result.is_ok());
- let (method, notification) = result.unwrap();
- if method.as_str() == "deno/testModule" {
- let params: TestModuleNotificationParams =
- serde_json::from_value(notification.unwrap()).unwrap();
- assert_eq!(params.text_document.uri, specifier);
- assert_eq!(params.kind, TestModuleNotificationKind::Replace);
- assert_eq!(params.label, "test.ts");
- assert_eq!(params.tests.len(), 1);
- let test = &params.tests[0];
- assert_eq!(test.label, "test a");
- assert!(test.steps.is_none());
- assert_eq!(
- test.range,
- Some(lsp::Range {
- start: lsp::Position {
- line: 1,
- character: 5,
- },
- end: lsp::Position {
- line: 1,
- character: 9,
- }
- })
- );
- }
- }
-
- let (maybe_res, maybe_err) = client
- .write_request::<_, _, TestRunResponseParams>(
- "deno/testRun",
- json!({
- "id": 1,
- "kind": "run",
- }),
- )
- .unwrap();
- assert!(maybe_err.is_none());
- assert!(maybe_res.is_some());
- let res = maybe_res.unwrap();
- assert_eq!(res.enqueued.len(), 1);
- assert_eq!(res.enqueued[0].text_document.uri, specifier);
- assert_eq!(res.enqueued[0].ids.len(), 1);
- let id = res.enqueued[0].ids[0].clone();
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "started",
- "test": {
- "textDocument": {
- "uri": specifier,
- },
- "id": id,
- },
- }
- }))
- );
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- let notification_value = notification
- .as_ref()
- .unwrap()
- .as_object()
- .unwrap()
- .get("message")
- .unwrap()
- .as_object()
- .unwrap()
- .get("value")
- .unwrap()
- .as_str()
- .unwrap();
- // deno test's output capturing flushes with a zero-width space in order to
- // synchronize the output pipes. Occassionally this zero width space
- // might end up in the output so strip it from the output comparison here.
- assert_eq!(notification_value.replace('\u{200B}', ""), "test a\r\n");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "output",
- "value": notification_value,
- "test": {
- "textDocument": {
- "uri": specifier,
- },
- "id": id,
- },
- }
- }))
- );
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- let notification = notification.unwrap();
- let obj = notification.as_object().unwrap();
- assert_eq!(obj.get("id"), Some(&json!(1)));
- let message = obj.get("message").unwrap().as_object().unwrap();
- match message.get("type").and_then(|v| v.as_str()) {
- Some("passed") => {
- assert_eq!(
- message.get("test"),
- Some(&json!({
- "textDocument": {
- "uri": specifier
- },
- "id": id,
- }))
- );
- assert!(message.contains_key("duration"));
-
- let res = client.read_notification::<Value>();
- assert!(res.is_ok());
- let (method, notification) = res.unwrap();
- assert_eq!(method, "deno/testRunProgress");
- assert_eq!(
- notification,
- Some(json!({
- "id": 1,
- "message": {
- "type": "end",
- }
- }))
- );
- }
- // sometimes on windows, the messages come out of order, but it actually is
- // working, so if we do get the end before the passed, we will simply let
- // the test pass
- Some("end") => (),
- _ => panic!("unexpected message {}", json!(notification)),
- }
-
- shutdown(&mut client);
- }
-}