aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/commands
diff options
context:
space:
mode:
authorRyan Cumming <[email protected]>2019-06-27 12:30:23 +0100
committerRyan Cumming <[email protected]>2019-06-29 08:39:36 +0100
commitabc0784e57610a0cceca63301489918015418df6 (patch)
tree05aec9fef88f31cee82e3507903a1dbcd6b4d30d /editors/code/src/commands
parent0e1912de528b5092c10eedaf94c43c67d5f86f1a (diff)
Fix `cargo watch` code action filtering
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.
Diffstat (limited to 'editors/code/src/commands')
-rw-r--r--editors/code/src/commands/cargo_watch.ts67
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
6import { Server } from '../server'; 6import { Server } from '../server';
7import { terminate } from '../utils/processes'; 7import { terminate } from '../utils/processes';
8import { LineBuffer } from './line_buffer';
9import { StatusDisplay } from './watch_status';
10
8import { 11import {
9 mapRustDiagnosticToVsCode, 12 mapRustDiagnosticToVsCode,
10 RustDiagnostic 13 RustDiagnostic
11} from '../utils/rust_diagnostics'; 14} from '../utils/diagnostics/rust';
12import { 15import SuggestedFixCollection from '../utils/diagnostics/SuggestedFixCollection';
13 areCodeActionsEqual, 16import { areDiagnosticsEqual } from '../utils/diagnostics/vscode';
14 areDiagnosticsEqual
15} from '../utils/vscode_diagnostics';
16import { LineBuffer } from './line_buffer';
17import { StatusDisplay } from './watch_status';
18 17
19export function registerCargoWatchProvider( 18export 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
45export class CargoWatchProvider 44export 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,