Skip to content

Commit f862cb6

Browse files
committed
fix: disable object literal completions for global completions (fix regression)
1 parent 0fbf3ca commit f862cb6

File tree

4 files changed

+81
-82
lines changed

4 files changed

+81
-82
lines changed

typescript/src/completions/objectLiteralCompletions.ts

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,85 @@
1-
import { GetConfig } from '../types'
21
import { getFullTypeChecker, isTs5 } from '../utils'
32
import { sharedCompletionContext } from './sharedContext'
43

5-
export default (
6-
entries: ts.CompletionEntry[],
7-
node: ts.Node,
8-
languageService: ts.LanguageService,
9-
preferences: ts.UserPreferences,
10-
c: GetConfig,
11-
): ts.CompletionEntry[] | void => {
12-
const { position } = sharedCompletionContext
4+
export default (prior: ts.CompletionInfo): ts.CompletionEntry[] | void => {
5+
const { entries, isMemberCompletion } = prior
6+
let { position, c, node, languageService, preferences } = sharedCompletionContext
7+
if (!node || entries.length === 0 || !isMemberCompletion) return
138

14-
if (entries.length > 0 && node) {
15-
const enableMoreVariants = c('objectLiteralCompletions.moreVariants')
16-
const keepOriginal = c('objectLiteralCompletions.keepOriginal')
17-
if (!preferences.includeCompletionsWithObjectLiteralMethodSnippets && !enableMoreVariants) return
18-
// plans to make it hihgly configurable! e.g. if user wants to make some subtype leading (e.g. from [] | {})
19-
if (ts.isIdentifier(node)) node = node.parent
20-
if (ts.isShorthandPropertyAssignment(node)) node = node.parent
21-
const nextChar = node.getSourceFile().getFullText()[position]
22-
if (!ts.isObjectLiteralExpression(node) || nextChar === ':') return
9+
const enableMoreVariants = c('objectLiteralCompletions.moreVariants')
10+
const keepOriginal = c('objectLiteralCompletions.keepOriginal')
11+
if (!preferences.includeCompletionsWithObjectLiteralMethodSnippets && !enableMoreVariants) return
12+
// plans to make it hihgly configurable! e.g. if user wants to make some subtype leading (e.g. from [] | {})
13+
if (ts.isIdentifier(node)) node = node.parent
14+
if (ts.isShorthandPropertyAssignment(node)) node = node.parent
15+
const nextChar = node.getSourceFile().getFullText()[position]
16+
if (!ts.isObjectLiteralExpression(node) || nextChar === ':') return
2317

24-
const typeChecker = languageService.getProgram()!.getTypeChecker()!
25-
const objType = typeChecker.getContextualType(node)
26-
let oldProperties: ts.Symbol[] | undefined
18+
const typeChecker = languageService.getProgram()!.getTypeChecker()!
19+
const objType = typeChecker.getContextualType(node)
20+
let oldProperties: ts.Symbol[] | undefined
21+
if (!isTs5()) {
22+
if (!objType) return
23+
oldProperties = getAllPropertiesOfType(objType, typeChecker)
24+
}
25+
// eslint-disable-next-line unicorn/no-useless-spread
26+
for (const entry of [...entries]) {
27+
let type: ts.Type | undefined
2728
if (!isTs5()) {
28-
if (!objType) return
29-
oldProperties = getAllPropertiesOfType(objType, typeChecker)
29+
const property = oldProperties!.find(property => property.name === entry.name)
30+
if (!property) continue
31+
type = typeChecker.getTypeOfSymbolAtLocation(property, node)
32+
} else if (entry.symbol) {
33+
type = typeChecker.getTypeOfSymbol(entry.symbol)
3034
}
31-
// eslint-disable-next-line unicorn/no-useless-spread
32-
for (const entry of [...entries]) {
33-
let type: ts.Type | undefined
34-
if (!isTs5()) {
35-
const property = oldProperties!.find(property => property.name === entry.name)
36-
if (!property) continue
37-
type = typeChecker.getTypeOfSymbolAtLocation(property, node)
38-
} else if (entry.symbol) {
39-
type = typeChecker.getTypeOfSymbol(entry.symbol)
40-
}
41-
if (!type) continue
42-
if (isFunctionType(type, typeChecker)) {
43-
if (['above', 'remove'].includes(keepOriginal) && preferences.includeCompletionsWithObjectLiteralMethodSnippets) {
44-
const methodEntryIndex = entries.findIndex(e => e.name === entry.name && isObjectLiteralMethodSnippet(e))
45-
const methodEntry = entries[methodEntryIndex]
46-
if (methodEntry) {
47-
entries.splice(methodEntryIndex, 1)
48-
entries.splice(entries.indexOf(entry) + (keepOriginal === 'before' ? 1 : 0), keepOriginal === 'remove' ? 1 : 0, {
49-
...methodEntry,
50-
// let correctSorting.enable sort it
51-
sortText: entry.sortText,
52-
})
53-
}
35+
if (!type) continue
36+
if (isFunctionType(type, typeChecker)) {
37+
if (['above', 'remove'].includes(keepOriginal) && preferences.includeCompletionsWithObjectLiteralMethodSnippets) {
38+
const methodEntryIndex = entries.findIndex(e => e.name === entry.name && isObjectLiteralMethodSnippet(e))
39+
const methodEntry = entries[methodEntryIndex]
40+
if (methodEntry) {
41+
entries.splice(methodEntryIndex, 1)
42+
entries.splice(entries.indexOf(entry) + (keepOriginal === 'before' ? 1 : 0), keepOriginal === 'remove' ? 1 : 0, {
43+
...methodEntry,
44+
// let correctSorting.enable sort it
45+
sortText: entry.sortText,
46+
})
5447
}
55-
continue
56-
}
57-
if (!enableMoreVariants) continue
58-
const getQuotedSnippet = (): [string, string] => {
59-
const quote = tsFull.getQuoteFromPreference(tsFull.getQuotePreference(node.getSourceFile() as any, preferences))
60-
return [`: ${quote}$1${quote},$0`, `: ${quote}${quote},`]
6148
}
62-
const insertObjectArrayInnerText = c('objectLiteralCompletions.insertNewLine') ? '\n\t$1\n' : '$1'
63-
const booleanCompletion = getBooleanCompletion(type, typeChecker)
64-
const completingStyleMap = [
65-
[getQuotedSnippet, isStringCompletion],
66-
[[`: ${booleanCompletion?.[0] ?? ''},`, `: ${booleanCompletion?.[0] ?? ''}`], () => booleanCompletion?.length === 1],
67-
[[': ${1|true,false|},$0', `: true/false,`], () => booleanCompletion?.length === 2],
68-
[[`: [${insertObjectArrayInnerText}],$0`, `: [],`], isArrayCompletion],
69-
[[`: {${insertObjectArrayInnerText}},$0`, `: {},`], isObjectCompletion],
70-
] as const
71-
const fallbackSnippet = c('objectLiteralCompletions.fallbackVariant') ? ([': $0,', ': ,'] as const) : undefined
72-
const insertSnippetVariant = completingStyleMap.find(([, detector]) => detector(type!, typeChecker))?.[0] ?? fallbackSnippet
73-
if (!insertSnippetVariant) continue
74-
const [insertSnippetText, insertSnippetPreview] = typeof insertSnippetVariant === 'function' ? insertSnippetVariant() : insertSnippetVariant
75-
const insertText = entry.name + insertSnippetText
76-
const index = entries.indexOf(entry)
77-
entries.splice(index + (keepOriginal === 'before' ? 1 : 0), keepOriginal === 'remove' ? 1 : 0, {
78-
...entry,
79-
// todo setting incompatible!!!
80-
sortText: entry.sortText,
81-
labelDetails: {
82-
detail: insertSnippetPreview,
83-
},
84-
insertText,
85-
isSnippet: true,
86-
})
49+
continue
8750
}
88-
return entries
51+
if (!enableMoreVariants) continue
52+
const getQuotedSnippet = (): [string, string] => {
53+
const quote = tsFull.getQuoteFromPreference(tsFull.getQuotePreference(node!.getSourceFile() as any, preferences))
54+
return [`: ${quote}$1${quote},$0`, `: ${quote}${quote},`]
55+
}
56+
const insertObjectArrayInnerText = c('objectLiteralCompletions.insertNewLine') ? '\n\t$1\n' : '$1'
57+
const booleanCompletion = getBooleanCompletion(type, typeChecker)
58+
const completingStyleMap = [
59+
[getQuotedSnippet, isStringCompletion],
60+
[[`: ${booleanCompletion?.[0] ?? ''},`, `: ${booleanCompletion?.[0] ?? ''}`], () => booleanCompletion?.length === 1],
61+
[[': ${1|true,false|},$0', `: true/false,`], () => booleanCompletion?.length === 2],
62+
[[`: [${insertObjectArrayInnerText}],$0`, `: [],`], isArrayCompletion],
63+
[[`: {${insertObjectArrayInnerText}},$0`, `: {},`], isObjectCompletion],
64+
] as const
65+
const fallbackSnippet = c('objectLiteralCompletions.fallbackVariant') ? ([': $0,', ': ,'] as const) : undefined
66+
const insertSnippetVariant = completingStyleMap.find(([, detector]) => detector(type!, typeChecker))?.[0] ?? fallbackSnippet
67+
if (!insertSnippetVariant) continue
68+
const [insertSnippetText, insertSnippetPreview] = typeof insertSnippetVariant === 'function' ? insertSnippetVariant() : insertSnippetVariant
69+
const insertText = entry.name + insertSnippetText
70+
const index = entries.indexOf(entry)
71+
entries.splice(index + (keepOriginal === 'before' ? 1 : 0), keepOriginal === 'remove' ? 1 : 0, {
72+
...entry,
73+
// todo setting incompatible!!!
74+
sortText: entry.sortText,
75+
labelDetails: {
76+
detail: insertSnippetPreview,
77+
},
78+
insertText,
79+
isSnippet: true,
80+
})
8981
}
82+
return entries
9083
}
9184

9285
const isObjectLiteralMethodSnippet = (entry: ts.CompletionEntry) => {

typescript/src/completionsAtPosition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export const getCompletionsAtPosition = (
229229
}
230230

231231
if (node) prior.entries = defaultHelpers(prior.entries, node, languageService) ?? prior.entries
232-
if (exactNode) prior.entries = objectLiteralCompletions(prior.entries, exactNode, languageService, options ?? {}, c) ?? prior.entries
232+
prior.entries = objectLiteralCompletions(prior) ?? prior.entries
233233
// 90%
234234
prior.entries = adjustAutoImports(prior.entries)
235235

typescript/test/completions.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,10 +562,15 @@ test('Object Literal Completions', () => {
562562
/*3*/
563563
}
564564
})
565+
566+
const a = {
567+
/*4*/
568+
}
565569
`)
566570
const { entriesSorted: pos1 } = getCompletionsAtPosition(numPositions[1]!)!
567571
const { entriesSorted: pos2 } = getCompletionsAtPosition(numPositions[2]!)!
568572
const { entriesSorted: pos3 } = getCompletionsAtPosition(numPositions[3]!)!
573+
const { entriesSorted: pos4 } = getCompletionsAtPosition(numPositions[4]!)!
569574
// todo resolve sorting problem + add tests with other keepOriginal (it was tested manually)
570575
for (const entry of [...pos1, ...pos2, ...pos3]) {
571576
entry.insertText = entry.insertText?.replaceAll('\n', '\\n')
@@ -657,6 +662,7 @@ test('Object Literal Completions', () => {
657662
"foo: \${1|true,false|},$0",
658663
]
659664
`)
665+
expect(pos4.filter(x => x.insertText?.includes(': '))).toEqual([])
660666
})
661667

662668
test('Extract to type / interface name inference', () => {

typescript/test/other.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import { createRequire } from 'module'
12
import decorateFormatFeatures from '../src/decorateFormatFeatures'
2-
import { defaultConfigFunc, entrypoint, sharedLanguageService } from './shared'
33
import { getNavTreeItems } from '../src/getPatchedNavTree'
4-
import { createRequire } from 'module'
4+
import { defaultConfigFunc, entrypoint, sharedLanguageService } from './shared'
55

66
const { languageService, languageServiceHost, updateProject, getCurrentFile } = sharedLanguageService
77

@@ -61,7 +61,7 @@ test('Patched navtree (outline)', () => {
6161
arraysTuplesNumberedItems: false,
6262
})
6363
const simplify = (items: ts.NavigationTree[]) => {
64-
const newItems: { text: any; childItems? }[] = []
64+
const newItems: Array<{ text: any; childItems? }> = []
6565
for (const { text, childItems } of items) {
6666
if (text === 'classes') continue
6767
newItems.push({ text, ...(childItems ? { childItems: simplify(childItems) } : {}) })

0 commit comments

Comments
 (0)