diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-06-29 10:50:56 +0100 |
---|---|---|
committer | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-06-29 10:50:56 +0100 |
commit | 8865db6768fd913b971df56fc0bcf7439fcb1e71 (patch) | |
tree | 3fff3954f7f5ca0c6045f9f7d96c1cd75bd896ee /editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts | |
parent | 64f71dd3fff7b902656a2c2465a1b5071b2b1903 (diff) | |
parent | 50c6ab709e38b63fb1e3ee5db40426ef131cbd71 (diff) |
Merge #1454
1454: Fix `cargo watch` code action filtering r=etaoins a=etaoins
There are two issues with the implementation of `provideCodeActions` introduced in #1439:
1. We're returning the code action based on the file its diagnostic is in; not the file the suggested fix is in. I'm not sure how often fixes are suggested cross-file but it's something we should handle.
2. We're not filtering code actions based on the passed range. The means if there is any suggestion in a file we'll show an action for every line of the file. I naively thought that VS Code would filter for us but that was wrong.
Unfortunately the VS Code `CodeAction` object is very complex - it can handle edits across multiple files, run commands, etc. This makes it complex to check them for equality or see if any of their edits intersects with a specified range.
To make it easier to work with suggestions this introduces a `SuggestedFix` model object and a `SuggestFixCollection` code action provider. This is a layer between the raw Rust JSON and VS Code's `CodeAction`s. I was reluctant to introduce another layer of abstraction here but my attempt to work directly with VS Code's model objects was worse.
Co-authored-by: Ryan Cumming <[email protected]>
Diffstat (limited to 'editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts')
-rw-r--r-- | editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts new file mode 100644 index 000000000..f0328893e --- /dev/null +++ b/editors/code/src/test/utils/diagnotics/SuggestedFixCollection.test.ts | |||
@@ -0,0 +1,125 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as vscode from 'vscode'; | ||
3 | |||
4 | import SuggestedFix from '../../../utils/diagnostics/SuggestedFix'; | ||
5 | import SuggestedFixCollection from '../../../utils/diagnostics/SuggestedFixCollection'; | ||
6 | |||
7 | const uri1 = vscode.Uri.file('/file/1'); | ||
8 | const uri2 = vscode.Uri.file('/file/2'); | ||
9 | |||
10 | const mockDocument1 = ({ | ||
11 | uri: uri1 | ||
12 | } as unknown) as vscode.TextDocument; | ||
13 | |||
14 | const mockDocument2 = ({ | ||
15 | uri: uri2 | ||
16 | } as unknown) as vscode.TextDocument; | ||
17 | |||
18 | const range1 = new vscode.Range( | ||
19 | new vscode.Position(1, 2), | ||
20 | new vscode.Position(3, 4) | ||
21 | ); | ||
22 | const range2 = new vscode.Range( | ||
23 | new vscode.Position(5, 6), | ||
24 | new vscode.Position(7, 8) | ||
25 | ); | ||
26 | |||
27 | const diagnostic1 = new vscode.Diagnostic(range1, 'First diagnostic'); | ||
28 | const diagnostic2 = new vscode.Diagnostic(range2, 'Second diagnostic'); | ||
29 | |||
30 | // This is a mutable object so return a fresh instance every time | ||
31 | function suggestion1(): SuggestedFix { | ||
32 | return new SuggestedFix( | ||
33 | 'Replace me!', | ||
34 | new vscode.Location(uri1, range1), | ||
35 | 'With this!' | ||
36 | ); | ||
37 | } | ||
38 | |||
39 | describe('SuggestedFixCollection', () => { | ||
40 | it('should add a suggestion then return it as a code action', () => { | ||
41 | const suggestedFixes = new SuggestedFixCollection(); | ||
42 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
43 | |||
44 | // Specify the document and range that exactly matches | ||
45 | const codeActions = suggestedFixes.provideCodeActions( | ||
46 | mockDocument1, | ||
47 | range1 | ||
48 | ); | ||
49 | |||
50 | assert.strictEqual(codeActions.length, 1); | ||
51 | const [codeAction] = codeActions; | ||
52 | assert.strictEqual(codeAction.title, suggestion1().title); | ||
53 | |||
54 | const { diagnostics } = codeAction; | ||
55 | if (!diagnostics) { | ||
56 | return assert.fail('Diagnostics unexpectedly missing'); | ||
57 | } | ||
58 | |||
59 | assert.strictEqual(diagnostics.length, 1); | ||
60 | assert.strictEqual(diagnostics[0], diagnostic1); | ||
61 | }); | ||
62 | |||
63 | it('should not return code actions for different ranges', () => { | ||
64 | const suggestedFixes = new SuggestedFixCollection(); | ||
65 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
66 | |||
67 | const codeActions = suggestedFixes.provideCodeActions( | ||
68 | mockDocument1, | ||
69 | range2 | ||
70 | ); | ||
71 | |||
72 | assert(!codeActions || codeActions.length === 0); | ||
73 | }); | ||
74 | |||
75 | it('should not return code actions for different documents', () => { | ||
76 | const suggestedFixes = new SuggestedFixCollection(); | ||
77 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
78 | |||
79 | const codeActions = suggestedFixes.provideCodeActions( | ||
80 | mockDocument2, | ||
81 | range1 | ||
82 | ); | ||
83 | |||
84 | assert(!codeActions || codeActions.length === 0); | ||
85 | }); | ||
86 | |||
87 | it('should not return code actions that have been cleared', () => { | ||
88 | const suggestedFixes = new SuggestedFixCollection(); | ||
89 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
90 | suggestedFixes.clear(); | ||
91 | |||
92 | const codeActions = suggestedFixes.provideCodeActions( | ||
93 | mockDocument1, | ||
94 | range1 | ||
95 | ); | ||
96 | |||
97 | assert(!codeActions || codeActions.length === 0); | ||
98 | }); | ||
99 | |||
100 | it('should merge identical suggestions together', () => { | ||
101 | const suggestedFixes = new SuggestedFixCollection(); | ||
102 | |||
103 | // Add the same suggestion for two diagnostics | ||
104 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic1); | ||
105 | suggestedFixes.addSuggestedFixForDiagnostic(suggestion1(), diagnostic2); | ||
106 | |||
107 | const codeActions = suggestedFixes.provideCodeActions( | ||
108 | mockDocument1, | ||
109 | range1 | ||
110 | ); | ||
111 | |||
112 | assert.strictEqual(codeActions.length, 1); | ||
113 | const [codeAction] = codeActions; | ||
114 | const { diagnostics } = codeAction; | ||
115 | |||
116 | if (!diagnostics) { | ||
117 | return assert.fail('Diagnostics unexpectedly missing'); | ||
118 | } | ||
119 | |||
120 | // We should be associated with both diagnostics | ||
121 | assert.strictEqual(diagnostics.length, 2); | ||
122 | assert.strictEqual(diagnostics[0], diagnostic1); | ||
123 | assert.strictEqual(diagnostics[1], diagnostic2); | ||
124 | }); | ||
125 | }); | ||