summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/lsp/analysis.rs22
-rw-r--r--cli/tests/integration/lsp_tests.rs198
2 files changed, 165 insertions, 55 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index ec279022d..808f09dec 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -848,8 +848,26 @@ impl CodeActionCollection {
/// Move out the code actions and return them as a `CodeActionResponse`.
pub fn get_response(self) -> lsp::CodeActionResponse {
- self
- .actions
+ let mut actions = self.actions;
+
+ // Prefer TSC fixes first, then Deno fixes, then Deno lint fixes.
+ actions.sort_by(|a, b| match (a, b) {
+ (CodeActionKind::Deno(a), CodeActionKind::Deno(b)) => {
+ a.title.cmp(&b.title)
+ }
+ (CodeActionKind::DenoLint(a), CodeActionKind::DenoLint(b)) => {
+ a.title.cmp(&b.title)
+ }
+ (CodeActionKind::Tsc(a, _), CodeActionKind::Tsc(b, _)) => {
+ a.title.cmp(&b.title)
+ }
+ (CodeActionKind::Tsc(_, _), _) => Ordering::Less,
+ (CodeActionKind::Deno(_), CodeActionKind::Tsc(_, _)) => Ordering::Greater,
+ (CodeActionKind::Deno(_), CodeActionKind::DenoLint(_)) => Ordering::Less,
+ (CodeActionKind::DenoLint(_), _) => Ordering::Greater,
+ });
+
+ actions
.into_iter()
.map(|i| match i {
CodeActionKind::Tsc(c, _) => lsp::CodeActionOrCommand::CodeAction(c),
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index f95e94182..9390f8f86 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -4045,6 +4045,24 @@ fn lsp_code_actions() {
assert_eq!(
res,
json!([{
+ "title": "Add all missing 'async' modifiers",
+ "kind": "quickfix",
+ "diagnostics": [{
+ "range": {
+ "start": { "line": 1, "character": 2 },
+ "end": { "line": 1, "character": 7 }
+ },
+ "severity": 1,
+ "code": 1308,
+ "source": "deno-ts",
+ "message": "'await' expressions are only allowed within async functions and at the top levels of modules.",
+ "relatedInformation": []
+ }],
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "fixId": "fixAwaitInSyncFunction"
+ }
+ }, {
"title": "Add async modifier to containing function",
"kind": "quickfix",
"diagnostics": [{
@@ -4079,24 +4097,6 @@ fn lsp_code_actions() {
}]
}]
}
- }, {
- "title": "Add all missing 'async' modifiers",
- "kind": "quickfix",
- "diagnostics": [{
- "range": {
- "start": { "line": 1, "character": 2 },
- "end": { "line": 1, "character": 7 }
- },
- "severity": 1,
- "code": 1308,
- "source": "deno-ts",
- "message": "'await' expressions are only allowed within async functions and at the top levels of modules.",
- "relatedInformation": []
- }],
- "data": {
- "specifier": "file:///a/file.ts",
- "fixId": "fixAwaitInSyncFunction"
- }
}])
);
let res = client
@@ -4189,6 +4189,98 @@ fn lsp_code_actions() {
}
#[test]
+fn test_lsp_code_actions_ordering() {
+ let context = TestContextBuilder::new().use_temp_cwd().build();
+ let mut client = context.new_lsp_command().build();
+ client.initialize_default();
+ let diagnostics = client.did_open(json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": r#"
+ import "https://deno.land/x/a/mod.ts";
+ let a = "a";
+ console.log(a);
+ export function b(): void {
+ await Promise.resolve("b");
+ }
+ "#
+ }
+ }));
+ let res = client.write_request(
+ "textDocument/codeAction",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts"
+ },
+ "range": {
+ "start": { "line": 1, "character": 11 },
+ "end": { "line": 6, "character": 12 }
+ },
+ "context": {
+ "diagnostics": diagnostics.all(),
+ "only": ["quickfix"]
+ }
+ }),
+ );
+
+ // Simplify the serialization to `{ title, source }` for this test.
+ let mut actions: Vec<Value> = serde_json::from_value(res).unwrap();
+ for action in &mut actions {
+ let action = action.as_object_mut().unwrap();
+ let title = action.get("title").unwrap().as_str().unwrap().to_string();
+ let diagnostics = action.get("diagnostics").unwrap().as_array().unwrap();
+ let diagnostic = diagnostics.get(0).unwrap().as_object().unwrap();
+ let source = diagnostic.get("source").unwrap();
+ let source = source.as_str().unwrap().to_string();
+ action.clear();
+ action.insert("title".to_string(), serde_json::to_value(title).unwrap());
+ action.insert("source".to_string(), serde_json::to_value(source).unwrap());
+ }
+ let res = serde_json::to_value(actions).unwrap();
+
+ // Ensure ordering is "deno-ts" -> "deno" -> "deno-lint".
+ assert_eq!(
+ res,
+ json!([
+ {
+ "title": "Add async modifier to containing function",
+ "source": "deno-ts",
+ },
+ {
+ "title": "Cache \"https://deno.land/x/a/mod.ts\" and its dependencies.",
+ "source": "deno",
+ },
+ {
+ "title": "Disable no-await-in-sync-fn for the entire file",
+ "source": "deno-lint",
+ },
+ {
+ "title": "Disable no-await-in-sync-fn for this line",
+ "source": "deno-lint",
+ },
+ {
+ "title": "Disable prefer-const for the entire file",
+ "source": "deno-lint",
+ },
+ {
+ "title": "Disable prefer-const for this line",
+ "source": "deno-lint",
+ },
+ {
+ "title": "Ignore lint errors for the entire file",
+ "source": "deno-lint",
+ },
+ {
+ "title": "Ignore lint errors for the entire file",
+ "source": "deno-lint",
+ },
+ ])
+ );
+}
+
+#[test]
fn lsp_code_actions_deno_cache() {
let context = TestContextBuilder::new().use_temp_cwd().build();
let mut client = context.new_lsp_command().build();
@@ -4451,7 +4543,7 @@ export class DuckConfig {
assert_eq!(
res,
json!([{
- "title": "Add import from \"./file02.ts\"",
+ "title": "Add all missing imports",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -4463,20 +4555,9 @@ export class DuckConfig {
"source": "deno-ts",
"message": "Cannot find name 'DuckConfigOptions'."
}],
- "edit": {
- "documentChanges": [{
- "textDocument": {
- "uri": "file:///a/file00.ts",
- "version": 1
- },
- "edits": [{
- "range": {
- "start": { "line": 0, "character": 0 },
- "end": { "line": 0, "character": 0 }
- },
- "newText": "import { DuckConfigOptions } from \"./file02.ts\";\n\n"
- }]
- }]
+ "data": {
+ "specifier": "file:///a/file00.ts",
+ "fixId": "fixMissingImport"
}
}, {
"title": "Add import from \"./file01.ts\"",
@@ -4507,7 +4588,7 @@ export class DuckConfig {
}]
}
}, {
- "title": "Add all missing imports",
+ "title": "Add import from \"./file02.ts\"",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -4519,9 +4600,20 @@ export class DuckConfig {
"source": "deno-ts",
"message": "Cannot find name 'DuckConfigOptions'."
}],
- "data": {
- "specifier": "file:///a/file00.ts",
- "fixId": "fixMissingImport"
+ "edit": {
+ "documentChanges": [{
+ "textDocument": {
+ "uri": "file:///a/file00.ts",
+ "version": 1
+ },
+ "edits": [{
+ "range": {
+ "start": { "line": 0, "character": 0 },
+ "end": { "line": 0, "character": 0 }
+ },
+ "newText": "import { DuckConfigOptions } from \"./file02.ts\";\n\n"
+ }]
+ }]
}
}])
);
@@ -8093,7 +8185,7 @@ fn lsp_code_actions_ignore_lint() {
assert_eq!(
res,
json!([{
- "title": "Disable prefer-const for this line",
+ "title": "Disable prefer-const for the entire file",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -8110,15 +8202,15 @@ fn lsp_code_actions_ignore_lint() {
"changes": {
"file:///a/file.ts": [{
"range": {
- "start": { "line": 1, "character": 0 },
- "end": { "line": 1, "character": 0 }
+ "start": { "line": 0, "character": 0 },
+ "end": { "line": 0, "character": 0 }
},
- "newText": "// deno-lint-ignore prefer-const\n"
+ "newText": "// deno-lint-ignore-file prefer-const\n"
}]
}
}
}, {
- "title": "Disable prefer-const for the entire file",
+ "title": "Disable prefer-const for this line",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -8135,10 +8227,10 @@ fn lsp_code_actions_ignore_lint() {
"changes": {
"file:///a/file.ts": [{
"range": {
- "start": { "line": 0, "character": 0 },
- "end": { "line": 0, "character": 0 }
+ "start": { "line": 1, "character": 0 },
+ "end": { "line": 1, "character": 0 }
},
- "newText": "// deno-lint-ignore-file prefer-const\n"
+ "newText": "// deno-lint-ignore prefer-const\n"
}]
}
}
@@ -8220,7 +8312,7 @@ console.log(snake_case);
assert_eq!(
res,
json!([{
- "title": "Disable prefer-const for this line",
+ "title": "Disable prefer-const for the entire file",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -8237,15 +8329,15 @@ console.log(snake_case);
"changes": {
"file:///a/file.ts": [{
"range": {
- "start": { "line": 3, "character": 0 },
- "end": { "line": 3, "character": 0 }
+ "start": { "line": 1, "character": 34 },
+ "end": { "line": 1, "character": 34 }
},
- "newText": "// deno-lint-ignore prefer-const\n"
+ "newText": " prefer-const"
}]
}
}
}, {
- "title": "Disable prefer-const for the entire file",
+ "title": "Disable prefer-const for this line",
"kind": "quickfix",
"diagnostics": [{
"range": {
@@ -8262,10 +8354,10 @@ console.log(snake_case);
"changes": {
"file:///a/file.ts": [{
"range": {
- "start": { "line": 1, "character": 34 },
- "end": { "line": 1, "character": 34 }
+ "start": { "line": 3, "character": 0 },
+ "end": { "line": 3, "character": 0 }
},
- "newText": " prefer-const"
+ "newText": "// deno-lint-ignore prefer-const\n"
}]
}
}