Skip to content

Commit ee2a297

Browse files
authoredMay 27, 2024··
Code actions for removing dead code (#989)
* code actions for removing dead code in the current file + each dead item, driven by reanalyze * changelog and cleanup * comment and extra match * comment, and handle another case
1 parent 34c4a3b commit ee2a297

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- Complete `%todo`. https://github.com/rescript-lang/rescript-vscode/pull/981
3636
- Add code action for extracting a locally defined module into its own file. https://github.com/rescript-lang/rescript-vscode/pull/983
3737
- Add code action for expanding catch-all patterns. https://github.com/rescript-lang/rescript-vscode/pull/987
38+
- Add code actions for removing unused code (per item and for an entire file), driven by `reanalyze`. https://github.com/rescript-lang/rescript-vscode/pull/989
3839

3940
## 1.50.0
4041

‎client/src/commands/code_analysis.ts

+48-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ export type DiagnosticsResultFormat = Array<{
3434
};
3535
}>;
3636

37+
enum ClassifiedMessage {
38+
Removable,
39+
Default,
40+
}
41+
42+
let classifyMessage = (msg: string) => {
43+
if (
44+
msg.endsWith(" is never used") ||
45+
msg.endsWith(" has no side effects and can be removed")
46+
) {
47+
return ClassifiedMessage.Removable;
48+
}
49+
50+
return ClassifiedMessage.Default;
51+
};
52+
3753
let resultsToDiagnostics = (
3854
results: DiagnosticsResultFormat,
3955
diagnosticsResultCodeActions: DiagnosticsResultCodeActionsMap
@@ -91,8 +107,6 @@ let resultsToDiagnostics = (
91107

92108
let codeActionEdit = new WorkspaceEdit();
93109

94-
// In the future, it would be cool to have an additional code action
95-
// here for automatically removing whatever the thing that's dead is.
96110
codeActionEdit.replace(
97111
Uri.parse(item.file),
98112
// Make sure the full line is replaced
@@ -119,6 +133,38 @@ let resultsToDiagnostics = (
119133
}
120134
}
121135
}
136+
137+
// This heuristic below helps only target dead code that can be removed
138+
// safely by just removing its text.
139+
if (classifyMessage(item.message) === ClassifiedMessage.Removable) {
140+
{
141+
let codeAction = new CodeAction("Remove unused");
142+
codeAction.kind = CodeActionKind.RefactorRewrite;
143+
144+
let codeActionEdit = new WorkspaceEdit();
145+
146+
codeActionEdit.replace(
147+
Uri.parse(item.file),
148+
new Range(
149+
new Position(item.range[0], item.range[1]),
150+
new Position(item.range[2], item.range[3])
151+
),
152+
""
153+
);
154+
155+
codeAction.edit = codeActionEdit;
156+
157+
if (diagnosticsResultCodeActions.has(item.file)) {
158+
diagnosticsResultCodeActions
159+
.get(item.file)
160+
.push({ range: issueLocationRange, codeAction });
161+
} else {
162+
diagnosticsResultCodeActions.set(item.file, [
163+
{ range: issueLocationRange, codeAction },
164+
]);
165+
}
166+
}
167+
}
122168
}
123169
});
124170

‎client/src/extension.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ import {
99
Uri,
1010
Range,
1111
Position,
12+
CodeAction,
13+
WorkspaceEdit,
14+
CodeActionKind,
1215
} from "vscode";
1316

1417
import {
1518
LanguageClient,
1619
LanguageClientOptions,
1720
ServerOptions,
1821
State,
19-
Executable,
20-
TransportKind
22+
TransportKind,
2123
} from "vscode-languageclient/node";
2224

2325
import * as customCommands from "./commands";
@@ -91,7 +93,11 @@ export function activate(context: ExtensionContext) {
9193
// If the extension is launched in debug mode then the debug server options are used
9294
// Otherwise the run options are used
9395
let serverOptions: ServerOptions = {
94-
run: { module: serverModule, args: ["--node-ipc"], transport: TransportKind.ipc },
96+
run: {
97+
module: serverModule,
98+
args: ["--node-ipc"],
99+
transport: TransportKind.ipc,
100+
},
95101
debug: {
96102
module: serverModule,
97103
args: ["--node-ipc"],
@@ -189,12 +195,31 @@ export function activate(context: ExtensionContext) {
189195
let availableActions =
190196
diagnosticsResultCodeActions.get(document.uri.fsPath) ?? [];
191197

192-
return availableActions
198+
const allRemoveActionEdits = availableActions.filter(
199+
({ codeAction }) => codeAction.title === "Remove unused"
200+
);
201+
202+
const actions: CodeAction[] = availableActions
193203
.filter(
194204
({ range }) =>
195205
range.contains(rangeOrSelection) || range.isEqual(rangeOrSelection)
196206
)
197207
.map(({ codeAction }) => codeAction);
208+
209+
if (allRemoveActionEdits.length > 0) {
210+
const removeAllCodeAction = new CodeAction("Remove all unused in file");
211+
const edit = new WorkspaceEdit();
212+
allRemoveActionEdits.forEach((subEdit) => {
213+
subEdit.codeAction.edit.entries().forEach(([uri, [textEdit]]) => {
214+
edit.replace(uri, textEdit.range, textEdit.newText);
215+
});
216+
});
217+
removeAllCodeAction.kind = CodeActionKind.RefactorRewrite;
218+
removeAllCodeAction.edit = edit;
219+
actions.push(removeAllCodeAction);
220+
}
221+
222+
return actions;
198223
},
199224
});
200225

0 commit comments

Comments
 (0)
Please sign in to comment.