Skip to content
This repository was archived by the owner on Dec 25, 2023. It is now read-only.

Commit 7909676

Browse files
authored
Setting to prevent unused import removal (#454)
* Setting to prevent unused import removal Using feature added to the language server in v0.7.0 Resolves #273 * Changelog
1 parent 17ca8e1 commit 7909676

File tree

7 files changed

+148
-2
lines changed

7 files changed

+148
-2
lines changed

src/commands/organizeImports.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ class MockRange {
66
}
77
(global as any).Range = MockRange;
88

9+
jest.mock("../skipDestructiveOrganizeImports", () => ({
10+
skipDestructiveOrganizeImports: () => false,
11+
}));
12+
913
describe("organizeImports command", () => {
1014
beforeEach(() => {
1115
(global as any).nova = Object.assign(nova, {
@@ -89,7 +93,7 @@ describe("organizeImports command", () => {
8993
2,
9094
"workspace/executeCommand",
9195
{
92-
arguments: ["/path"],
96+
arguments: ["/path", { skipDestructiveCodeActions: false }],
9397
command: "_typescript.organizeImports",
9498
}
9599
);

src/commands/organizeImports.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type * as lspTypes from "vscode-languageserver-protocol";
22
import { wrapCommand } from "../novaUtils";
3+
import { skipDestructiveOrganizeImports } from "../skipDestructiveOrganizeImports";
34

45
// NOTE: this is explicitly built for the typescript-language-server; it directly invokes the specific command it uses.
56
// In order to decouple and become LSP generic, we'd need to first send a code action request for only
@@ -51,7 +52,10 @@ export function registerOrganizeImports(client: LanguageClient) {
5152

5253
const organizeImportsCommand: lspTypes.ExecuteCommandParams = {
5354
command: "_typescript.organizeImports",
54-
arguments: [editor.document.path],
55+
arguments: [
56+
editor.document.path,
57+
{ skipDestructiveCodeActions: skipDestructiveOrganizeImports() },
58+
],
5559
};
5660
await client.sendRequest(
5761
"workspace/executeCommand",

src/main.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ jest.mock("./tsUserPreferences", () => ({
99
jest.mock("./isEnabledForJavascript", () => ({
1010
isEnabledForJavascript: () => true,
1111
}));
12+
jest.mock("./skipDestructiveOrganizeImports", () => ({
13+
skipDestructiveOrganizeImports: () => false,
14+
}));
1215
jest.mock("nova-extension-utils");
1316

1417
jest.useFakeTimers();
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
(global as any).nova = Object.assign(nova, {
2+
commands: {
3+
invoke: jest.fn(),
4+
},
5+
config: {
6+
onDidChange: jest.fn(),
7+
["get"]: jest.fn(),
8+
},
9+
workspace: {
10+
config: { onDidChange: jest.fn(), ["get"]: jest.fn() },
11+
},
12+
});
13+
14+
describe("skipDestructiveOrganizeImports", () => {
15+
beforeEach(() => {
16+
(nova.workspace.config.get as jest.Mock).mockReset();
17+
(nova.config.get as jest.Mock).mockReset();
18+
});
19+
20+
const {
21+
skipDestructiveOrganizeImports,
22+
} = require("./skipDestructiveOrganizeImports");
23+
24+
describe("reloads extension when it changes", () => {
25+
it("globally and for the workspace", () => {
26+
expect(nova.config.onDidChange).toBeCalledTimes(1);
27+
expect(nova.config.onDidChange).toBeCalledWith(
28+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
29+
expect.any(Function)
30+
);
31+
expect(nova.workspace.config.onDidChange).toBeCalledTimes(1);
32+
expect(nova.workspace.config.onDidChange).toBeCalledWith(
33+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
34+
expect.any(Function)
35+
);
36+
// same function
37+
const onWorkspaceChange = (nova.workspace.config.onDidChange as jest.Mock)
38+
.mock.calls[0][1];
39+
const onGlobalChange = (nova.config.onDidChange as jest.Mock).mock
40+
.calls[0][1];
41+
expect(onWorkspaceChange).toBe(onGlobalChange);
42+
});
43+
44+
it("by calling the reload command", () => {
45+
const reload = (nova.config.onDidChange as jest.Mock).mock.calls[0][1];
46+
reload();
47+
expect(nova.commands.invoke).toBeCalledTimes(1);
48+
expect(nova.commands.invoke).toBeCalledWith(
49+
"apexskier.typescript.reload"
50+
);
51+
});
52+
});
53+
54+
describe("is true by default", () => {
55+
expect(skipDestructiveOrganizeImports()).toBe(true);
56+
});
57+
58+
describe("can be disabled globally", () => {
59+
(nova.workspace.config.get as jest.Mock).mockReturnValueOnce(null);
60+
(nova.config.get as jest.Mock).mockReturnValueOnce(false);
61+
expect(skipDestructiveOrganizeImports()).toBe(false);
62+
});
63+
64+
describe("can be enabled globally", () => {
65+
(nova.workspace.config.get as jest.Mock).mockReturnValueOnce(null);
66+
(nova.config.get as jest.Mock).mockReturnValueOnce(true);
67+
expect(skipDestructiveOrganizeImports()).toBe(true);
68+
});
69+
70+
describe("can be disabled in the workspace", () => {
71+
(nova.workspace.config.get as jest.Mock).mockReturnValueOnce("False");
72+
(nova.config.get as jest.Mock).mockReturnValueOnce(true);
73+
expect(skipDestructiveOrganizeImports()).toBe(false);
74+
});
75+
76+
describe("can be enabled in the workspace", () => {
77+
(nova.workspace.config.get as jest.Mock).mockReturnValueOnce("True");
78+
(nova.config.get as jest.Mock).mockReturnValueOnce(false);
79+
expect(skipDestructiveOrganizeImports()).toBe(true);
80+
});
81+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function reload() {
2+
nova.commands.invoke("apexskier.typescript.reload");
3+
}
4+
nova.config.onDidChange(
5+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
6+
reload
7+
);
8+
nova.workspace.config.onDidChange(
9+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
10+
reload
11+
);
12+
13+
function getWorkspaceSetting(): boolean | null {
14+
const str = nova.workspace.config.get(
15+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
16+
"string"
17+
);
18+
switch (str) {
19+
case "False":
20+
return false;
21+
case "True":
22+
return true;
23+
default:
24+
return null;
25+
}
26+
}
27+
28+
export function skipDestructiveOrganizeImports(): boolean {
29+
return (
30+
getWorkspaceSetting() ??
31+
nova.config.get(
32+
"apexskier.typescript.config.skipDestructiveOrganizeImports",
33+
"boolean"
34+
) ??
35+
true
36+
);
37+
}

typescript.novaextension/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## future
44

5+
### Added
6+
7+
- Configuration to prevent deleting unused imports when auto-organizing imports
8+
59
### Changed
610

711
- When search results are ready, display a notification instead of an alert.

typescript.novaextension/extension.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@
6060
"type": "boolean",
6161
"default": true
6262
},
63+
{
64+
"key": "apexskier.typescript.config.skipDestructiveOrganizeImports",
65+
"title": "Skip destructive organize imports changes",
66+
"type": "boolean",
67+
"default": false
68+
},
6369
{
6470
"title": "TypeScript server User Preferences",
6571
"description": "Advanced configuration passed to the underlying typescript server. These may not apply to older versions of TypeScript.",
@@ -352,6 +358,13 @@
352358
"values": ["Inherit from Global Settings", "Disable", "Enable"],
353359
"default": "Inherit from Global Settings"
354360
},
361+
{
362+
"key": "apexskier.typescript.config.skipDestructiveOrganizeImports",
363+
"title": "Skip destructive organize imports changes",
364+
"type": "enum",
365+
"values": ["Inherit from Global Settings", "False", "True"],
366+
"default": "Inherit from Global Settings"
367+
},
355368
{
356369
"title": "TypeScript server User Preferences",
357370
"description": "Advanced configuration passed to the underlying typescript server. These may not apply to older versions of TypeScript.",

0 commit comments

Comments
 (0)