/
code_fixes.ts
106 lines (99 loc) 路 3.76 KB
/
code_fixes.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core';
import * as tss from 'typescript/lib/tsserverlibrary';
import {TemplateInfo} from '../utils';
import {CodeActionMeta, FixIdForCodeFixesAll, isFixAllAvailable} from './utils';
export class CodeFixes {
private errorCodeToFixes: Map<number, CodeActionMeta[]> = new Map();
private fixIdToRegistration = new Map<FixIdForCodeFixesAll, CodeActionMeta>();
constructor(
private readonly tsLS: tss.LanguageService, readonly codeActionMetas: CodeActionMeta[]) {
for (const meta of codeActionMetas) {
for (const err of meta.errorCodes) {
let errMeta = this.errorCodeToFixes.get(err);
if (errMeta === undefined) {
this.errorCodeToFixes.set(err, errMeta = []);
}
errMeta.push(meta);
}
for (const fixId of meta.fixIds) {
if (this.fixIdToRegistration.has(fixId)) {
// https://github.com/microsoft/TypeScript/blob/28dc248e5c500c7be9a8c3a7341d303e026b023f/src/services/codeFixProvider.ts#L28
// In ts services, only one meta can be registered for a fixId.
continue;
}
this.fixIdToRegistration.set(fixId, meta);
}
}
}
/**
* When the user moves the cursor or hovers on a diagnostics, this function will be invoked by LS,
* and collect all the responses from the `codeActionMetas` which could handle the `errorCodes`.
*/
getCodeFixesAtPosition(
fileName: string, templateInfo: TemplateInfo, compiler: NgCompiler, start: number,
end: number, errorCodes: readonly number[], diagnostics: tss.Diagnostic[],
formatOptions: tss.FormatCodeSettings,
preferences: tss.UserPreferences): readonly tss.CodeFixAction[] {
const codeActions: tss.CodeFixAction[] = [];
for (const code of errorCodes) {
const metas = this.errorCodeToFixes.get(code);
if (metas === undefined) {
continue;
}
for (const meta of metas) {
const codeActionsForMeta = meta.getCodeActions({
fileName,
templateInfo,
compiler,
start,
end,
errorCode: code,
formatOptions,
preferences,
tsLs: this.tsLS,
});
const fixAllAvailable = isFixAllAvailable(meta, diagnostics);
const removeFixIdForCodeActions =
codeActionsForMeta.map(({fixId, fixAllDescription, ...codeActionForMeta}) => {
return fixAllAvailable ? {...codeActionForMeta, fixId, fixAllDescription} :
codeActionForMeta;
});
codeActions.push(...removeFixIdForCodeActions);
}
}
return codeActions;
}
/**
* When the user wants to fix the all same type of diagnostics in the `scope`, this function will
* be called and fix all diagnostics which will be filtered by the `errorCodes` from the
* `CodeActionMeta` that the `fixId` belongs to.
*/
getAllCodeActions(
compiler: NgCompiler, diagnostics: tss.Diagnostic[], scope: tss.CombinedCodeFixScope,
fixId: string, formatOptions: tss.FormatCodeSettings,
preferences: tss.UserPreferences): tss.CombinedCodeActions {
const meta = this.fixIdToRegistration.get(fixId as FixIdForCodeFixesAll);
if (meta === undefined) {
return {
changes: [],
};
}
return meta.getAllCodeActions({
compiler,
fixId,
formatOptions,
preferences,
tsLs: this.tsLS,
scope,
// only pass the diagnostics the `meta` cares about.
diagnostics: diagnostics.filter(diag => meta.errorCodes.includes(diag.code)),
});
}
}