From f4a3e0961d24fbf1b4b17c98c5f51402f65d7410 Mon Sep 17 00:00:00 2001 From: takeokunn Date: Sat, 14 Feb 2026 18:42:57 +0900 Subject: [PATCH] feat(code-action): add quickfix for redundant parentheses - Mark redundant parentheses diagnostics as preferred quickfix - Add tests for basic, multiline, and nested redundant paren cases - Ensure no action is offered when parentheses are required --- nixd/lib/Controller/CodeAction.cpp | 1 + .../test/code-action/redundant-paren/basic.md | 86 ++++++++++++++++++ .../code-action/redundant-paren/multiline.md | 88 +++++++++++++++++++ .../redundant-paren/needed-paren.md | 69 +++++++++++++++ .../code-action/redundant-paren/nested.md | 73 +++++++++++++++ 5 files changed, 317 insertions(+) create mode 100644 nixd/tools/nixd/test/code-action/redundant-paren/basic.md create mode 100644 nixd/tools/nixd/test/code-action/redundant-paren/multiline.md create mode 100644 nixd/tools/nixd/test/code-action/redundant-paren/needed-paren.md create mode 100644 nixd/tools/nixd/test/code-action/redundant-paren/nested.md diff --git a/nixd/lib/Controller/CodeAction.cpp b/nixd/lib/Controller/CodeAction.cpp index 6e737116f..eea65fc91 100644 --- a/nixd/lib/Controller/CodeAction.cpp +++ b/nixd/lib/Controller/CodeAction.cpp @@ -51,6 +51,7 @@ void Controller::onCodeAction(const lspserver::CodeActionParams &Params, bool IsPreferred = false; switch (D.kind()) { case nixf::Diagnostic::DK_UnusedDefLet: + case nixf::Diagnostic::DK_RedundantParen: IsPreferred = true; break; default: diff --git a/nixd/tools/nixd/test/code-action/redundant-paren/basic.md b/nixd/tools/nixd/test/code-action/redundant-paren/basic.md new file mode 100644 index 000000000..fd9cd9794 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/redundant-paren/basic.md @@ -0,0 +1,86 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test basic `remove redundant parens` action for unnecessary parentheses. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///redundant-paren.nix +(1) +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///redundant-paren.nix" + }, + "range":{ + "start":{ + "line": 0, + "character": 0 + }, + "end":{ + "line":0, + "character": 1 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +The action should remove both `(` and `)` since the inner expression `1` is simple. + +``` + CHECK: "id": 2, + CHECK: "newText": "", + CHECK: "range": + CHECK: "end": + CHECK: "character": 1, + CHECK: "line": 0 + CHECK: "start": + CHECK: "character": 0, + CHECK: "line": 0 + CHECK: "newText": "", + CHECK: "range": + CHECK: "end": + CHECK: "character": 3, + CHECK: "line": 0 + CHECK: "start": + CHECK: "character": 2, + CHECK: "line": 0 + CHECK: "isPreferred": true, + CHECK: "kind": "quickfix", + CHECK: "title": "remove ( and )" +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/redundant-paren/multiline.md b/nixd/tools/nixd/test/code-action/redundant-paren/multiline.md new file mode 100644 index 000000000..70fa08e09 --- /dev/null +++ b/nixd/tools/nixd/test/code-action/redundant-paren/multiline.md @@ -0,0 +1,88 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test `remove redundant parens` action with multiline expression. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///multiline-paren.nix +( + 1 +) +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///multiline-paren.nix" + }, + "range":{ + "start":{ + "line": 0, + "character": 0 + }, + "end":{ + "line":0, + "character": 1 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +The action should remove `(` on line 0 and `)` on line 2. + +``` + CHECK: "id": 2, + CHECK: "newText": "", + CHECK: "range": + CHECK: "end": + CHECK: "character": 1, + CHECK: "line": 0 + CHECK: "start": + CHECK: "character": 0, + CHECK: "line": 0 + CHECK: "newText": "", + CHECK: "range": + CHECK: "end": + CHECK: "character": 1, + CHECK: "line": 2 + CHECK: "start": + CHECK: "character": 0, + CHECK: "line": 2 + CHECK: "isPreferred": true, + CHECK: "kind": "quickfix", + CHECK: "title": "remove ( and )" +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/redundant-paren/needed-paren.md b/nixd/tools/nixd/test/code-action/redundant-paren/needed-paren.md new file mode 100644 index 000000000..2a340e79d --- /dev/null +++ b/nixd/tools/nixd/test/code-action/redundant-paren/needed-paren.md @@ -0,0 +1,69 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test that `remove redundant parens` is NOT offered when parentheses are needed. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///needed-paren.nix +(1 + 2) +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///needed-paren.nix" + }, + "range":{ + "start":{ + "line": 0, + "character": 0 + }, + "end":{ + "line":0, + "character": 1 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +No code action should be offered because `1 + 2` is a binary operation, making parentheses meaningful. + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": [] +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +``` diff --git a/nixd/tools/nixd/test/code-action/redundant-paren/nested.md b/nixd/tools/nixd/test/code-action/redundant-paren/nested.md new file mode 100644 index 000000000..3ebd6b86d --- /dev/null +++ b/nixd/tools/nixd/test/code-action/redundant-paren/nested.md @@ -0,0 +1,73 @@ +# RUN: nixd --lit-test < %s | FileCheck %s + +Test `remove redundant parens` action for nested parentheses `((1))`. + +<-- initialize(0) + +```json +{ + "jsonrpc":"2.0", + "id":0, + "method":"initialize", + "params":{ + "processId":123, + "rootPath":"", + "capabilities":{ + }, + "trace":"off" + } +} +``` + + +<-- textDocument/didOpen + +```nix file:///nested-paren.nix +((1)) +``` + +<-- textDocument/codeAction(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/codeAction", + "params":{ + "textDocument":{ + "uri":"file:///nested-paren.nix" + }, + "range":{ + "start":{ + "line": 0, + "character": 0 + }, + "end":{ + "line":0, + "character": 5 + } + }, + "context":{ + "diagnostics":[], + "triggerKind":2 + } + } +} +``` + +Both inner and outer parentheses are redundant, so two quickfix actions should be offered. + +``` + CHECK: "id": 2, + CHECK: "isPreferred": true, + CHECK: "kind": "quickfix", + CHECK: "title": "remove ( and )" + CHECK: "isPreferred": true, + CHECK: "kind": "quickfix", + CHECK: "title": "remove ( and )" +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +```