summaryrefslogtreecommitdiff
path: root/tests/integration/lsp_tests.rs
diff options
context:
space:
mode:
authorNathan Whitaker <17734409+nathanwhit@users.noreply.github.com>2024-05-06 16:54:52 -0700
committerGitHub <noreply@github.com>2024-05-06 23:54:52 +0000
commit672216d65a7e737256133615b0bf56092b57b87f (patch)
tree1ee7458192b2e61344fd7e3e2261be6790f5dcbc /tests/integration/lsp_tests.rs
parent8eb1f11112c3ced0ff4a35f3487a4da507db05c2 (diff)
fix(lsp): Pass diagnostic codes to TSC as numbers (#23720)
Fixes the `Debug Failure` errors described in https://github.com/denoland/deno/issues/23643#issuecomment-2094552765 . The issue here was that we were passing diagnostic codes as strings but TSC expects the codes to be numbers. This resulted in some quick fixes not working (as illustrated by the test added here which fails before this PR). The first commit is the actual fix. The rest are just test related.
Diffstat (limited to 'tests/integration/lsp_tests.rs')
-rw-r--r--tests/integration/lsp_tests.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs
index f5d09bc77..9d82e0afd 100644
--- a/tests/integration/lsp_tests.rs
+++ b/tests/integration/lsp_tests.rs
@@ -9,6 +9,7 @@ use deno_core::url::Url;
use pretty_assertions::assert_eq;
use std::fs;
use test_util::assert_starts_with;
+use test_util::assertions::assert_json_subset;
use test_util::deno_cmd_with_deno_dir;
use test_util::env_vars_for_npm_tests;
use test_util::lsp::LspClient;
@@ -16,6 +17,65 @@ use test_util::testdata_path;
use test_util::TestContextBuilder;
use tower_lsp::lsp_types as lsp;
+/// Helper to get the `lsp::Range` of the `n`th occurrence of
+/// `text` in `src`. `n` is zero-based, like most indexes.
+fn range_of_nth(
+ n: usize,
+ text: impl AsRef<str>,
+ src: impl AsRef<str>,
+) -> lsp::Range {
+ let text = text.as_ref();
+
+ let src = src.as_ref();
+
+ let start = src
+ .match_indices(text)
+ .nth(n)
+ .map(|(i, _)| i)
+ .unwrap_or_else(|| panic!("couldn't find text {text} in source {src}"));
+ let end = start + text.len();
+ let mut line = 0;
+ let mut col = 0;
+ let mut byte_idx = 0;
+
+ let pos = |line, col| lsp::Position {
+ line,
+ character: col,
+ };
+
+ let mut start_pos = None;
+ let mut end_pos = None;
+ for c in src.chars() {
+ if byte_idx == start {
+ start_pos = Some(pos(line, col));
+ }
+ if byte_idx == end {
+ end_pos = Some(pos(line, col));
+ break;
+ }
+ if c == '\n' {
+ line += 1;
+ col = 0;
+ } else {
+ col += c.len_utf16() as u32;
+ }
+ byte_idx += c.len_utf8();
+ }
+ if start_pos.is_some() && end_pos.is_none() {
+ // range extends to end of string
+ end_pos = Some(pos(line, col));
+ }
+
+ let (start, end) = (start_pos.unwrap(), end_pos.unwrap());
+ lsp::Range { start, end }
+}
+
+/// Helper to get the `lsp::Range` of the first occurrence of
+/// `text` in `src`. Equivalent to `range_of_nth(0, text, src)`.
+fn range_of(text: impl AsRef<str>, src: impl AsRef<str>) -> lsp::Range {
+ range_of_nth(0, text, src)
+}
+
#[test]
fn lsp_startup_shutdown() {
let context = TestContextBuilder::new().use_temp_cwd().build();
@@ -12548,3 +12608,72 @@ fn lsp_cjs_internal_types_default_export() {
// previously, diagnostic about `add` not being callable
assert_eq!(json!(diagnostics.all()), json!([]));
}
+
+#[test]
+fn lsp_ts_code_fix_any_param() {
+ let context = TestContextBuilder::new().use_temp_cwd().build();
+ let temp_dir = context.temp_dir();
+
+ let mut client = context.new_lsp_command().build();
+ client.initialize_default();
+
+ let src = "export function foo(param) { console.log(param); }";
+
+ let param_def = range_of("param", src);
+
+ let main_url = temp_dir.path().join("main.ts").uri_file();
+ let diagnostics = client.did_open(json!({
+ "textDocument": {
+ "uri": main_url,
+ "languageId": "typescript",
+ "version": 1,
+ "text": src,
+ }
+ }));
+ // make sure the "implicit any type" diagnostic is there for "param"
+ assert_json_subset(
+ json!(diagnostics.all()),
+ json!([{
+ "range": param_def,
+ "code": 7006,
+ "message": "Parameter 'param' implicitly has an 'any' type."
+ }]),
+ );
+
+ // response is array of fixes
+ let response = client.write_request(
+ "textDocument/codeAction",
+ json!({
+ "textDocument": {
+ "uri": main_url,
+ },
+ "range": lsp::Range {
+ start: param_def.end,
+ ..param_def
+ },
+ "context": {
+ "diagnostics": diagnostics.all(),
+ }
+ }),
+ );
+ let fixes = response.as_array().unwrap();
+
+ // we're looking for the quick fix that pertains to our diagnostic,
+ // specifically the "Infer parameter types from usage" fix
+ for fix in fixes {
+ let Some(diags) = fix.get("diagnostics") else {
+ continue;
+ };
+ let Some(fix_title) = fix.get("title").and_then(|s| s.as_str()) else {
+ continue;
+ };
+ if diags == &json!(diagnostics.all())
+ && fix_title == "Infer parameter types from usage"
+ {
+ // found it!
+ return;
+ }
+ }
+
+ panic!("failed to find 'Infer parameter types from usage' fix in fixes: {fixes:#?}");
+}