-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathgetCodeActions.ts
182 lines (170 loc) · 7.05 KB
/
getCodeActions.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import { compact } from '@zardoy/utils'
import { Except } from 'type-fest'
import { findChildContainingExactPosition, findChildContainingPosition } from '../utils'
import { ApplyExtendedCodeActionResult, IpcExtendedCodeAction } from '../ipcTypes'
import { GetConfig } from '../types'
import objectSwapKeysAndValues from './custom/objectSwapKeysAndValues'
import changeStringReplaceToRegex from './custom/changeStringReplaceToRegex'
import splitDeclarationAndInitialization from './custom/splitDeclarationAndInitialization'
import declareMissingProperties from './extended/declareMissingProperties'
import { renameParameterToNameFromType, renameAllParametersToNameFromType } from './custom/renameParameterToNameFromType'
import addDestructure_1 from './custom/addDestructure/addDestructure'
import fromDestructure_1 from './custom/fromDestructure/fromDestructure'
const codeActions: CodeAction[] = [
addDestructure_1,
fromDestructure_1,
objectSwapKeysAndValues,
changeStringReplaceToRegex,
splitDeclarationAndInitialization,
renameParameterToNameFromType,
renameAllParametersToNameFromType,
]
const extendedCodeActions: ExtendedCodeAction[] = [declareMissingProperties]
type SimplifiedRefactorInfo =
| {
start: number
length: number
newText: string
}
| ts.TextChange
export type ApplyCodeAction = (
sourceFile: ts.SourceFile,
position: number,
range: ts.TextRange | undefined,
node: ts.Node | undefined,
/** undefined when no edits is requested */
formatOptions: ts.FormatCodeSettings | undefined,
languageService: ts.LanguageService,
languageServiceHost: ts.LanguageServiceHost,
) => ts.RefactorEditInfo | SimplifiedRefactorInfo[] | true | undefined
export type CodeAction = {
name: string
id: string
/** Base kind https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.d.ts#L2236 */
kind: string
tryToApply: ApplyCodeAction
}
export type ApplyExtendedCodeAction = (options: {
sourceFile: ts.SourceFile
position: number
range: ts.TextRange | undefined
node: ts.Node | undefined
/** undefined when no edits is requested */
formatOptions: ts.FormatCodeSettings | undefined
languageService: ts.LanguageService
c: GetConfig
// languageServiceHost: ts.LanguageServiceHost
}) => ApplyExtendedCodeActionResult | boolean | undefined
// extended code actions support snippets and diagnostic codes (so they can be quickfixes)
export type ExtendedCodeAction = {
title: string
// id: string
kind: string
tryToApply: ApplyExtendedCodeAction
codes?: number[]
}
type Satisfies<T, U extends T> = any
// ensure props are in sync
type CheckCodeAction = Satisfies<Except<ExtendedCodeAction, 'tryToApply'>, IpcExtendedCodeAction>
export const getExtendedCodeActions = <T extends string | undefined>(
sourceFile: ts.SourceFile,
positionOrRange: ts.TextRange | number,
languageService: ts.LanguageService,
// languageServiceHost: ts.LanguageServiceHost,
formatOptions: ts.FormatCodeSettings | undefined,
applyCodeActionTitle: T,
config: GetConfig,
filterErrorCodes?: number[],
): T extends undefined ? ExtendedCodeAction[] : ApplyExtendedCodeActionResult => {
const range = typeof positionOrRange !== 'number' && positionOrRange.pos !== positionOrRange.end ? positionOrRange : undefined
const position = typeof positionOrRange === 'number' ? positionOrRange : positionOrRange.pos
const node = findChildContainingExactPosition(sourceFile, position)
const tryToApplyOptions = {
formatOptions,
languageService,
// languageServiceHost,
node,
position,
range,
sourceFile,
c: config,
}
if (applyCodeActionTitle) {
const codeAction = extendedCodeActions.find(codeAction => codeAction.title === applyCodeActionTitle)
return codeAction!.tryToApply(tryToApplyOptions) as T extends undefined ? never : ApplyExtendedCodeActionResult
}
return compact(
extendedCodeActions.map(codeAction => {
if (
!filterErrorCodes ||
!codeAction.codes ||
(codeAction.codes.some(c => filterErrorCodes.includes(c)) && codeAction.tryToApply(tryToApplyOptions))
) {
return codeAction
}
return
}),
) as T extends undefined ? ExtendedCodeAction[] : never
}
export const REFACTORS_CATEGORY = 'essential-refactors'
// main function to get regular TS refactoring code actions
export default (
sourceFile: ts.SourceFile,
positionOrRange: ts.TextRange | number,
languageService: ts.LanguageService,
languageServiceHost: ts.LanguageServiceHost,
formatOptions?: ts.FormatCodeSettings,
requestingEditsId?: string,
): { info?: ts.ApplicableRefactorInfo; edit: ts.RefactorEditInfo } => {
const range = typeof positionOrRange !== 'number' && positionOrRange.pos !== positionOrRange.end ? positionOrRange : undefined
const pos = typeof positionOrRange === 'number' ? positionOrRange : positionOrRange.pos
const node = findChildContainingExactPosition(sourceFile, pos)
const appliableCodeActions = compact(
codeActions.map(action => {
const edits = action.tryToApply(sourceFile, pos, range, node, formatOptions, languageService, languageServiceHost)
if (edits === true) return action
if (!edits) return
return {
...action,
edits: Array.isArray(edits)
? {
edits: [
{
fileName: sourceFile.fileName,
textChanges: edits.map(change => {
if ('start' in change) {
const { newText, start, length } = change
return {
newText,
span: {
length,
start,
},
}
}
return change
}),
},
],
}
: edits,
}
}),
)
const requestingEdit: any = requestingEditsId ? appliableCodeActions.find(({ id }) => id === requestingEditsId) : null
return {
info:
(appliableCodeActions.length > 0 && {
actions: appliableCodeActions.map(({ id, name, kind }) => ({
description: name,
kind,
name: id,
})),
// not visible in ui anyway
description: 'Essential Refactors',
name: REFACTORS_CATEGORY,
}) ||
undefined,
edit: requestingEdit?.edits,
}
}