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/commands | |
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/commands')
-rw-r--r-- | editors/code/src/commands/cargo_watch.ts | 67 |
1 files changed, 21 insertions, 46 deletions
diff --git a/editors/code/src/commands/cargo_watch.ts b/editors/code/src/commands/cargo_watch.ts index 1ec5f8d5f..4c3c10c8b 100644 --- a/editors/code/src/commands/cargo_watch.ts +++ b/editors/code/src/commands/cargo_watch.ts | |||
@@ -5,16 +5,15 @@ import * as vscode from 'vscode'; | |||
5 | 5 | ||
6 | import { Server } from '../server'; | 6 | import { Server } from '../server'; |
7 | import { terminate } from '../utils/processes'; | 7 | import { terminate } from '../utils/processes'; |
8 | import { LineBuffer } from './line_buffer'; | ||
9 | import { StatusDisplay } from './watch_status'; | ||
10 | |||
8 | import { | 11 | import { |
9 | mapRustDiagnosticToVsCode, | 12 | mapRustDiagnosticToVsCode, |
10 | RustDiagnostic | 13 | RustDiagnostic |
11 | } from '../utils/rust_diagnostics'; | 14 | } from '../utils/diagnostics/rust'; |
12 | import { | 15 | import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection'; |
13 | areCodeActionsEqual, | 16 | import { areDiagnosticsEqual } from '../utils/diagnostics/vscode'; |
14 | areDiagnosticsEqual | ||
15 | } from '../utils/vscode_diagnostics'; | ||
16 | import { LineBuffer } from './line_buffer'; | ||
17 | import { StatusDisplay } from './watch_status'; | ||
18 | 17 | ||
19 | export function registerCargoWatchProvider( | 18 | export function registerCargoWatchProvider( |
20 | subscriptions: vscode.Disposable[] | 19 | subscriptions: vscode.Disposable[] |
@@ -42,16 +41,13 @@ export function registerCargoWatchProvider( | |||
42 | return provider; | 41 | return provider; |
43 | } | 42 | } |
44 | 43 | ||
45 | export class CargoWatchProvider | 44 | export class CargoWatchProvider implements vscode.Disposable { |
46 | implements vscode.Disposable, vscode.CodeActionProvider { | ||
47 | private readonly diagnosticCollection: vscode.DiagnosticCollection; | 45 | private readonly diagnosticCollection: vscode.DiagnosticCollection; |
48 | private readonly statusDisplay: StatusDisplay; | 46 | private readonly statusDisplay: StatusDisplay; |
49 | private readonly outputChannel: vscode.OutputChannel; | 47 | private readonly outputChannel: vscode.OutputChannel; |
50 | 48 | ||
51 | private codeActions: { | 49 | private suggestedFixCollection: SuggestedFixCollection; |
52 | [fileUri: string]: vscode.CodeAction[]; | 50 | private codeActionDispose: vscode.Disposable; |
53 | }; | ||
54 | private readonly codeActionDispose: vscode.Disposable; | ||
55 | 51 | ||
56 | private cargoProcess?: child_process.ChildProcess; | 52 | private cargoProcess?: child_process.ChildProcess; |
57 | 53 | ||
@@ -66,13 +62,14 @@ export class CargoWatchProvider | |||
66 | 'Cargo Watch Trace' | 62 | 'Cargo Watch Trace' |
67 | ); | 63 | ); |
68 | 64 | ||
69 | // Register code actions for rustc's suggested fixes | 65 | // Track `rustc`'s suggested fixes so we can convert them to code actions |
70 | this.codeActions = {}; | 66 | this.suggestedFixCollection = new SuggestedFixCollection(); |
71 | this.codeActionDispose = vscode.languages.registerCodeActionsProvider( | 67 | this.codeActionDispose = vscode.languages.registerCodeActionsProvider( |
72 | [{ scheme: 'file', language: 'rust' }], | 68 | [{ scheme: 'file', language: 'rust' }], |
73 | this, | 69 | this.suggestedFixCollection, |
74 | { | 70 | { |
75 | providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] | 71 | providedCodeActionKinds: |
72 | SuggestedFixCollection.PROVIDED_CODE_ACTION_KINDS | ||
76 | } | 73 | } |
77 | ); | 74 | ); |
78 | } | 75 | } |
@@ -156,13 +153,6 @@ export class CargoWatchProvider | |||
156 | this.codeActionDispose.dispose(); | 153 | this.codeActionDispose.dispose(); |
157 | } | 154 | } |
158 | 155 | ||
159 | public provideCodeActions( | ||
160 | document: vscode.TextDocument | ||
161 | ): vscode.ProviderResult<Array<vscode.Command | vscode.CodeAction>> { | ||
162 | const documentActions = this.codeActions[document.uri.toString()]; | ||
163 | return documentActions || []; | ||
164 | } | ||
165 | |||
166 | private logInfo(line: string) { | 156 | private logInfo(line: string) { |
167 | if (Server.config.cargoWatchOptions.trace === 'verbose') { | 157 | if (Server.config.cargoWatchOptions.trace === 'verbose') { |
168 | this.outputChannel.append(line); | 158 | this.outputChannel.append(line); |
@@ -181,7 +171,7 @@ export class CargoWatchProvider | |||
181 | private parseLine(line: string) { | 171 | private parseLine(line: string) { |
182 | if (line.startsWith('[Running')) { | 172 | if (line.startsWith('[Running')) { |
183 | this.diagnosticCollection.clear(); | 173 | this.diagnosticCollection.clear(); |
184 | this.codeActions = {}; | 174 | this.suggestedFixCollection.clear(); |
185 | this.statusDisplay.show(); | 175 | this.statusDisplay.show(); |
186 | } | 176 | } |
187 | 177 | ||
@@ -225,7 +215,7 @@ export class CargoWatchProvider | |||
225 | return; | 215 | return; |
226 | } | 216 | } |
227 | 217 | ||
228 | const { location, diagnostic, codeActions } = mapResult; | 218 | const { location, diagnostic, suggestedFixes } = mapResult; |
229 | const fileUri = location.uri; | 219 | const fileUri = location.uri; |
230 | 220 | ||
231 | const diagnostics: vscode.Diagnostic[] = [ | 221 | const diagnostics: vscode.Diagnostic[] = [ |
@@ -236,7 +226,6 @@ export class CargoWatchProvider | |||
236 | const isDuplicate = diagnostics.some(d => | 226 | const isDuplicate = diagnostics.some(d => |
237 | areDiagnosticsEqual(d, diagnostic) | 227 | areDiagnosticsEqual(d, diagnostic) |
238 | ); | 228 | ); |
239 | |||
240 | if (isDuplicate) { | 229 | if (isDuplicate) { |
241 | return; | 230 | return; |
242 | } | 231 | } |
@@ -244,29 +233,15 @@ export class CargoWatchProvider | |||
244 | diagnostics.push(diagnostic); | 233 | diagnostics.push(diagnostic); |
245 | this.diagnosticCollection!.set(fileUri, diagnostics); | 234 | this.diagnosticCollection!.set(fileUri, diagnostics); |
246 | 235 | ||
247 | if (codeActions.length) { | 236 | if (suggestedFixes.length) { |
248 | const fileUriString = fileUri.toString(); | 237 | for (const suggestedFix of suggestedFixes) { |
249 | const existingActions = this.codeActions[fileUriString] || []; | 238 | this.suggestedFixCollection.addSuggestedFixForDiagnostic( |
250 | 239 | suggestedFix, | |
251 | for (const newAction of codeActions) { | 240 | diagnostic |
252 | const existingAction = existingActions.find(existing => | ||
253 | areCodeActionsEqual(existing, newAction) | ||
254 | ); | 241 | ); |
255 | |||
256 | if (existingAction) { | ||
257 | if (!existingAction.diagnostics) { | ||
258 | existingAction.diagnostics = []; | ||
259 | } | ||
260 | // This action also applies to this diagnostic | ||
261 | existingAction.diagnostics.push(diagnostic); | ||
262 | } else { | ||
263 | newAction.diagnostics = [diagnostic]; | ||
264 | existingActions.push(newAction); | ||
265 | } | ||
266 | } | 242 | } |
267 | 243 | ||
268 | // Have VsCode query us for the code actions | 244 | // Have VsCode query us for the code actions |
269 | this.codeActions[fileUriString] = existingActions; | ||
270 | vscode.commands.executeCommand( | 245 | vscode.commands.executeCommand( |
271 | 'vscode.executeCodeActionProvider', | 246 | 'vscode.executeCodeActionProvider', |
272 | fileUri, | 247 | fileUri, |