Skip to content

Commit 6201eeb

Browse files
committed
fix(method-snippets): skip trailing arguments with void type (user experience)
1 parent 5697844 commit 6201eeb

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

typescript/src/constructMethodSnippet.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
77
const node = findChildContainingExactPosition(sourceFile, position)
88
if (!node || isTypeNode(node)) return
99

10-
const typeChecker = languageService.getProgram()!.getTypeChecker()!
11-
const type = typeChecker.getTypeAtLocation(node)
12-
const signatures = typeChecker.getSignaturesOfType(type, ts.SignatureKind.Call)
10+
const checker = languageService.getProgram()!.getTypeChecker()!
11+
const type = checker.getTypeAtLocation(node)
12+
const signatures = checker.getSignaturesOfType(type, ts.SignatureKind.Call)
1313
if (signatures.length === 0) return
1414
const signature = signatures[0]
1515
if (signatures.length > 1 && c('methodSnippets.multipleSignatures') === 'empty') {
@@ -23,6 +23,7 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
2323
// Investigate merging signatures
2424
const { parameters } = signatures[0]!
2525
const printer = ts.createPrinter()
26+
let isVoidOrNotMap: boolean[] = []
2627
const paramsToInsert = compact(
2728
(skipMode === 'all' ? [] : parameters).map(param => {
2829
const valueDeclaration = param.valueDeclaration as ts.ParameterDeclaration | undefined
@@ -36,6 +37,18 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
3637
if (isOptional) return undefined
3738
break
3839
}
40+
const voidType = (checker as unknown as FullChecker).getVoidType()
41+
const parameterType = valueDeclaration && checker.getTypeOfSymbolAtLocation(param, valueDeclaration)
42+
isVoidOrNotMap.push(
43+
!!(
44+
parameterType &&
45+
(parameterType === voidType ||
46+
// new Promise<void> resolve type
47+
(parameterType.isUnion() &&
48+
parameterType.types[0] === voidType &&
49+
getPromiseLikeTypeArgument(parameterType.types[1], checker) === voidType))
50+
),
51+
)
3952
const insertName = insertMode === 'always-name' || !valueDeclaration
4053
const insertText = insertName
4154
? param.name
@@ -63,8 +76,15 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
6376
const allFiltered = paramsToInsert.length === 0 && parameters.length > paramsToInsert.length
6477
if (allFiltered) return ['']
6578

79+
const lastNonVoidIndex = isVoidOrNotMap.lastIndexOf(false)
80+
if (lastNonVoidIndex !== -1) {
81+
isVoidOrNotMap = [...repeatItems(false, lastNonVoidIndex + 1), .../* true */ isVoidOrNotMap.slice(lastNonVoidIndex + 1)]
82+
}
83+
6684
// methodSnippets.replaceArguments is processed with last stage in onCompletionAccepted
67-
return paramsToInsert
85+
86+
// do natural, final filtering
87+
return paramsToInsert.filter((_x, i) => !isVoidOrNotMap[i])
6888
// return `(${paramsToInsert.map((param, i) => `\${${i + 1}:${param.replaceAll}}`).join(', ')})`
6989

7090
function cloneBindingName(node: ts.BindingName): ts.BindingName {
@@ -87,4 +107,17 @@ export default (languageService: ts.LanguageService, sourceFile: ts.SourceFile,
87107
return ts.setEmitFlags(visited, ts.EmitFlags.SingleLine | ts.EmitFlags.NoAsciiEscaping)
88108
}
89109
}
110+
111+
function repeatItems<T>(item: T, count: number): T[] {
112+
return Array.from({ length: count }).map(() => item)
113+
}
114+
}
115+
116+
function getPromiseLikeTypeArgument(type: ts.Type | undefined, checker: ts.TypeChecker) {
117+
if (!type) return
118+
if (!(type.flags & ts.TypeFlags.Object) || !((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Reference)) return
119+
if (type.symbol.name !== 'PromiseLike') return
120+
const typeArgs = checker.getTypeArguments(type as ts.TypeReference)
121+
if (typeArgs.length !== 1) return
122+
return typeArgs[0]!
90123
}

typescript/test/completions.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,23 @@ describe('Method snippets', () => {
171171
compareMethodSnippetAgainstMarker(markers, 6, '(a, b, c)')
172172
})
173173

174+
test('Skip trailing void', () => {
175+
const [, _, markers] = fileContentsSpecialPositions(/* ts */ `
176+
new Promise<void>((resolve) => {
177+
resolve/*1*/
178+
})
179+
declare const foo: (a: void, b: boolean, c: void, d: void) => void
180+
type Bar<T> = (a: T) => void
181+
declare const bar: Bar<void>
182+
foo/*2*/
183+
bar/*3*/
184+
`)
185+
186+
compareMethodSnippetAgainstMarker(markers, 1, [])
187+
compareMethodSnippetAgainstMarker(markers, 2, ['a', 'b'])
188+
compareMethodSnippetAgainstMarker(markers, 3, [])
189+
})
190+
174191
test('Insert text = always-declaration', () => {
175192
overrideSettings({
176193
'methodSnippets.insertText': 'always-declaration',

0 commit comments

Comments
 (0)