diff options
author | Ryan Cumming <[email protected]> | 2019-06-27 12:30:23 +0100 |
---|---|---|
committer | Ryan Cumming <[email protected]> | 2019-06-29 08:39:36 +0100 |
commit | abc0784e57610a0cceca63301489918015418df6 (patch) | |
tree | 05aec9fef88f31cee82e3507903a1dbcd6b4d30d /editors/code/src/utils/diagnostics/SuggestedFixCollection.ts | |
parent | 0e1912de528b5092c10eedaf94c43c67d5f86f1a (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/utils/diagnostics/SuggestedFixCollection.ts')
-rw-r--r-- | editors/code/src/utils/diagnostics/SuggestedFixCollection.ts | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts new file mode 100644 index 000000000..3b0bf7468 --- /dev/null +++ b/editors/code/src/utils/diagnostics/SuggestedFixCollection.ts | |||
@@ -0,0 +1,74 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import SuggestedFix from './SuggestedFix'; | ||
3 | |||
4 | /** | ||
5 | * Collection of suggested fixes across multiple documents | ||
6 | * | ||
7 | * This stores `SuggestedFix` model objects and returns them via the | ||
8 | * `vscode.CodeActionProvider` interface. | ||
9 | */ | ||
10 | export default class SuggestedFixCollection | ||
11 | implements vscode.CodeActionProvider { | ||
12 | public static PROVIDED_CODE_ACTION_KINDS = [vscode.CodeActionKind.QuickFix]; | ||
13 | |||
14 | private suggestedFixes: Map<string, SuggestedFix[]>; | ||
15 | |||
16 | constructor() { | ||
17 | this.suggestedFixes = new Map(); | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Clears all suggested fixes across all documents | ||
22 | */ | ||
23 | public clear(): void { | ||
24 | this.suggestedFixes = new Map(); | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * Adds a suggested fix for the given diagnostic | ||
29 | * | ||
30 | * Some suggested fixes will appear in multiple diagnostics. For example, | ||
31 | * forgetting a `mut` on a variable will suggest changing the delaration on | ||
32 | * every mutable usage site. If the suggested fix has already been added | ||
33 | * this method will instead associate the existing fix with the new | ||
34 | * diagnostic. | ||
35 | */ | ||
36 | public addSuggestedFixForDiagnostic( | ||
37 | suggestedFix: SuggestedFix, | ||
38 | diagnostic: vscode.Diagnostic | ||
39 | ): void { | ||
40 | const fileUriString = suggestedFix.location.uri.toString(); | ||
41 | const fileSuggestions = this.suggestedFixes.get(fileUriString) || []; | ||
42 | |||
43 | const existingSuggestion = fileSuggestions.find(s => | ||
44 | s.isEqual(suggestedFix) | ||
45 | ); | ||
46 | |||
47 | if (existingSuggestion) { | ||
48 | // The existing suggestion also applies to this new diagnostic | ||
49 | existingSuggestion.diagnostics.push(diagnostic); | ||
50 | } else { | ||
51 | // We haven't seen this suggestion before | ||
52 | suggestedFix.diagnostics.push(diagnostic); | ||
53 | fileSuggestions.push(suggestedFix); | ||
54 | } | ||
55 | |||
56 | this.suggestedFixes.set(fileUriString, fileSuggestions); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Filters suggested fixes by their document and range and converts them to | ||
61 | * code actions | ||
62 | */ | ||
63 | public provideCodeActions( | ||
64 | document: vscode.TextDocument, | ||
65 | range: vscode.Range | ||
66 | ): vscode.CodeAction[] { | ||
67 | const documentUriString = document.uri.toString(); | ||
68 | |||
69 | const suggestedFixes = this.suggestedFixes.get(documentUriString); | ||
70 | return (suggestedFixes || []) | ||
71 | .filter(({ location }) => location.range.intersection(range)) | ||
72 | .map(suggestedEdit => suggestedEdit.toCodeAction()); | ||
73 | } | ||
74 | } | ||