diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0f17e9fef860c..0be7c2c543ce9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8756,6 +8756,7 @@ namespace ts { readonly includeAutomaticOptionalChainCompletions?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly includeCompletionsWithClassMemberSnippets?: boolean; + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index b8513c3744297..a845158bdf930 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -960,7 +960,7 @@ namespace FourSlash { expected = typeof expected === "string" ? { name: expected } : expected; if (actual.insertText !== expected.insertText) { - this.raiseError(`Completion insert text did not match: ${showTextDiff(expected.insertText || "", actual.insertText || "")}`); + this.raiseError(`At entry ${actual.name}: Completion insert text did not match: ${showTextDiff(expected.insertText || "", actual.insertText || "")}`); } const convertedReplacementSpan = expected.replacementSpan && ts.createTextSpanFromRange(expected.replacementSpan); if (convertedReplacementSpan?.length) { @@ -968,28 +968,28 @@ namespace FourSlash { assert.deepEqual(actual.replacementSpan, convertedReplacementSpan); } catch { - this.raiseError(`Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`); + this.raiseError(`At entry ${actual.name}: Expected completion replacementSpan to be ${stringify(convertedReplacementSpan)}, got ${stringify(actual.replacementSpan)}`); } } if (expected.kind !== undefined || expected.kindModifiers !== undefined) { - assert.equal(actual.kind, expected.kind, `Expected 'kind' for ${actual.name} to match`); - assert.equal(actual.kindModifiers, expected.kindModifiers || "", `Expected 'kindModifiers' for ${actual.name} to match`); + assert.equal(actual.kind, expected.kind, `At entry ${actual.name}: Expected 'kind' for ${actual.name} to match`); + assert.equal(actual.kindModifiers, expected.kindModifiers || "", `At entry ${actual.name}: Expected 'kindModifiers' for ${actual.name} to match`); } if (expected.isFromUncheckedFile !== undefined) { - assert.equal(actual.isFromUncheckedFile, expected.isFromUncheckedFile, "Expected 'isFromUncheckedFile' properties to match"); + assert.equal(actual.isFromUncheckedFile, expected.isFromUncheckedFile, `At entry ${actual.name}: Expected 'isFromUncheckedFile' properties to match`); } if (expected.isPackageJsonImport !== undefined) { - assert.equal(actual.isPackageJsonImport, expected.isPackageJsonImport, "Expected 'isPackageJsonImport' properties to match"); + assert.equal(actual.isPackageJsonImport, expected.isPackageJsonImport, `At entry ${actual.name}: Expected 'isPackageJsonImport' properties to match`); } - assert.equal(actual.hasAction, expected.hasAction, `Expected 'hasAction' properties to match`); - assert.equal(actual.isRecommended, expected.isRecommended, `Expected 'isRecommended' properties to match'`); - assert.equal(actual.isSnippet, expected.isSnippet, `Expected 'isSnippet' properties to match`); - assert.equal(actual.source, expected.source, `Expected 'source' values to match`); - assert.equal(actual.sortText, expected.sortText || ts.Completions.SortText.LocationPriority, `Expected 'sortText' properties to match`); + assert.equal(actual.hasAction, expected.hasAction, `At entry ${actual.name}: Expected 'hasAction' properties to match`); + assert.equal(actual.isRecommended, expected.isRecommended, `At entry ${actual.name}: Expected 'isRecommended' properties to match'`); + assert.equal(actual.isSnippet, expected.isSnippet, `At entry ${actual.name}: Expected 'isSnippet' properties to match`); + assert.equal(actual.source, expected.source, `At entry ${actual.name}: Expected 'source' values to match`); + assert.equal(actual.sortText, expected.sortText || ts.Completions.SortText.LocationPriority, `At entry ${actual.name}: Expected 'sortText' properties to match`); if (expected.sourceDisplay && actual.sourceDisplay) { - assert.equal(ts.displayPartsToString(actual.sourceDisplay), expected.sourceDisplay, `Expected 'sourceDisplay' properties to match`); + assert.equal(ts.displayPartsToString(actual.sourceDisplay), expected.sourceDisplay, `At entry ${actual.name}: Expected 'sourceDisplay' properties to match`); } if (expected.text !== undefined) { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 98b61cf2fb55a..976fdf9220ba3 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -950,9 +950,36 @@ namespace FourSlashInterface { } export namespace Completion { - export import SortText = ts.Completions.SortText; + import SortTextType = ts.Completions.SortText; + export type SortText = SortTextType; export import CompletionSource = ts.Completions.CompletionSource; + export const SortText = { + // Presets + LocalDeclarationPriority: "10" as SortText, + LocationPriority: "11" as SortText, + OptionalMember: "12" as SortText, + MemberDeclaredBySpreadAssignment: "13" as SortText, + SuggestedClassMembers: "14" as SortText, + GlobalsOrKeywords: "15" as SortText, + AutoImportSuggestions: "16" as SortText, + ClassMemberSnippets: "17" as SortText, + JavascriptIdentifiers: "18" as SortText, + + // Transformations + Deprecated(sortText: SortText): SortText { + return "z" + sortText as SortText; + }, + + ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText { + return `${presetSortText}\0${symbolDisplayName}\0` as SortText; + }, + + SortBelow(sortText: SortText): SortText { + return sortText + "1" as SortText; + }, + }; + const functionEntry = (name: string): ExpectedCompletionEntryObject => ({ name, kind: "function", @@ -963,7 +990,7 @@ namespace FourSlashInterface { name, kind: "function", kindModifiers: "deprecated,declare", - sortText: SortText.DeprecatedGlobalsOrKeywords + sortText: "z15" as SortText, }); const varEntry = (name: string): ExpectedCompletionEntryObject => ({ name, @@ -992,7 +1019,7 @@ namespace FourSlashInterface { name, kind: "method", kindModifiers: "deprecated,declare", - sortText: SortText.DeprecatedLocationPriority + sortText: "z11" as SortText, }); const propertyEntry = (name: string): ExpectedCompletionEntryObject => ({ name, diff --git a/src/server/protocol.ts b/src/server/protocol.ts index ca8dbd1cac63b..35e941cabe253 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -3406,6 +3406,13 @@ namespace ts.server.protocol { * `class A { foo }`. */ readonly includeCompletionsWithClassMemberSnippets?: boolean; + /** + * If enabled, object literal methods will have a method declaration completion entry in addition + * to the regular completion entry containing just the method name. + * E.g., `const objectLiteral: T = { f| }` could be completed to `const objectLiteral: T = { foo(): void {} }`, + * in addition to `const objectLiteral: T = { foo }`. + */ + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ diff --git a/src/services/completions.ts b/src/services/completions.ts index 9fbbdfd339e01..1944e7ee816b4 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -6,44 +6,32 @@ namespace ts.Completions { export type Log = (message: string) => void; - // NOTE: Make sure that each entry has the exact same number of digits - // since many implementations will sort by string contents, - // where "10" is considered less than "2". - export enum SortText { - LocalDeclarationPriority = "10", - LocationPriority = "11", - OptionalMember = "12", - MemberDeclaredBySpreadAssignment = "13", - SuggestedClassMembers = "14", - GlobalsOrKeywords = "15", - AutoImportSuggestions = "16", - ClassMemberSnippets = "17", - JavascriptIdentifiers = "18", - DeprecatedLocalDeclarationPriority = "19", - DeprecatedLocationPriority = "20", - DeprecatedOptionalMember = "21", - DeprecatedMemberDeclaredBySpreadAssignment = "22", - DeprecatedSuggestedClassMembers = "23", - DeprecatedGlobalsOrKeywords = "24", - DeprecatedAutoImportSuggestions = "25" - } - - const enum SortTextId { - LocalDeclarationPriority = 10, - LocationPriority = 11, - OptionalMember = 12, - MemberDeclaredBySpreadAssignment = 13, - SuggestedClassMembers = 14, - GlobalsOrKeywords = 15, - AutoImportSuggestions = 16, - - // Don't use these directly. - _JavaScriptIdentifiers = 18, - _DeprecatedStart = 19, - _First = LocalDeclarationPriority, - - DeprecatedOffset = _DeprecatedStart - _First, - } + export type SortText = string & { __sortText: any }; + export const SortText = { + // Presets + LocalDeclarationPriority: "10" as SortText, + LocationPriority: "11" as SortText, + OptionalMember: "12" as SortText, + MemberDeclaredBySpreadAssignment: "13" as SortText, + SuggestedClassMembers: "14" as SortText, + GlobalsOrKeywords: "15" as SortText, + AutoImportSuggestions: "16" as SortText, + ClassMemberSnippets: "17" as SortText, + JavascriptIdentifiers: "18" as SortText, + + // Transformations + Deprecated(sortText: SortText): SortText { + return "z" + sortText as SortText; + }, + + ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText { + return `${presetSortText}\0${symbolDisplayName}\0` as SortText; + }, + + SortBelow(sortText: SortText): SortText { + return sortText + "1" as SortText; + }, + }; /** * Special values for `CompletionInfo['source']` used to disambiguate @@ -63,6 +51,8 @@ namespace ts.Completions { ClassMemberSnippet = "ClassMemberSnippet/", /** A type-only import that needs to be promoted in order to be used at the completion location */ TypeOnlyAlias = "TypeOnlyAlias/", + /** Auto-import that comes attached to an object literal method snippet */ + ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/", } const enum SymbolOriginInfoKind { @@ -73,6 +63,7 @@ namespace ts.Completions { Nullable = 1 << 4, ResolvedExport = 1 << 5, TypeOnlyAlias = 1 << 6, + ObjectLiteralMethod = 1 << 7, SymbolMemberNoExport = SymbolMember, SymbolMemberExport = SymbolMember | Export, @@ -104,6 +95,13 @@ namespace ts.Completions { declaration: TypeOnlyAliasDeclaration; } + interface SymbolOriginInfoObjectLiteralMethod extends SymbolOriginInfo { + importAdder: codefix.ImportAdder, + insertText: string, + sourceDisplay: SymbolDisplayPart[], + isSnippet?: true, + } + function originIsThisType(origin: SymbolOriginInfo): boolean { return !!(origin.kind & SymbolOriginInfoKind.ThisType); } @@ -140,6 +138,10 @@ namespace ts.Completions { return !!(origin && origin.kind & SymbolOriginInfoKind.TypeOnlyAlias); } + function originIsObjectLiteralMethod(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoObjectLiteralMethod { + return !!(origin && origin.kind & SymbolOriginInfoKind.ObjectLiteralMethod); + } + interface UniqueNameSet { add(name: string): void; has(name: string): boolean; @@ -147,12 +149,11 @@ namespace ts.Completions { /** * Map from symbol index in `symbols` -> SymbolOriginInfo. - * Only populated for symbols that come from other modules. */ type SymbolOriginInfoMap = Record; - /** Map from symbol id -> SortTextId. */ - type SymbolSortTextIdMap = (SortTextId | undefined)[]; + /** Map from symbol id -> SortText. */ + type SymbolSortTextMap = (SortText | undefined)[]; const enum KeywordCompletionFilters { None, // No keywords @@ -282,7 +283,7 @@ namespace ts.Completions { return getLabelCompletionAtPosition(previousToken.parent); } - const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, preferences, /*detailsEntryId*/ undefined, host, cancellationToken); + const completionData = getCompletionData(program, log, sourceFile, compilerOptions, position, preferences, /*detailsEntryId*/ undefined, host, formatContext, cancellationToken); if (!completionData) { return undefined; } @@ -473,7 +474,7 @@ namespace ts.Completions { isRightOfOpenTag, importCompletionNode, insideJsDocTagTypeExpression, - symbolToSortTextIdMap, + symbolToSortTextMap: symbolToSortTextMap, hasUnresolvedAutoImports, } = completionData; @@ -510,7 +511,7 @@ namespace ts.Completions { importCompletionNode, recommendedCompletion, symbolToOriginInfoMap, - symbolToSortTextIdMap, + symbolToSortTextMap, isJsxIdentifierExpected, isRightOfOpenTag, ); @@ -543,7 +544,7 @@ namespace ts.Completions { importCompletionNode, recommendedCompletion, symbolToOriginInfoMap, - symbolToSortTextIdMap, + symbolToSortTextMap, isJsxIdentifierExpected, isRightOfOpenTag, ); @@ -777,6 +778,16 @@ namespace ts.Completions { } } + if (origin && originIsObjectLiteralMethod(origin)) { + let importAdder; + ({ insertText, isSnippet, importAdder, sourceDisplay } = origin); + source = CompletionSource.ObjectLiteralMethodSnippet; + sortText = SortText.SortBelow(sortText); + if (importAdder.hasFixes()) { + hasAction = true; + } + } + if (isJsxIdentifierExpected && !isRightOfOpenTag && preferences.includeCompletionsWithSnippetText && preferences.jsxAttributeCompletionStyle && preferences.jsxAttributeCompletionStyle !== "none") { let useBraces = preferences.jsxAttributeCompletionStyle === "braces"; const type = typeChecker.getTypeOfSymbolAtLocation(symbol, location); @@ -977,36 +988,20 @@ namespace ts.Completions { isAbstract); if (completionNodes.length) { + const format = ListFormat.MultiLine | ListFormat.NoTrailingNewLine; replacementSpan = modifiersSpan; // If we have access to formatting settings, we print the nodes using the emitter, // and then format the printed text. if (formatContext) { - const syntheticFile = { - text: printer.printSnippetList( - ListFormat.MultiLine | ListFormat.NoTrailingNewLine, - factory.createNodeArray(completionNodes), - sourceFile), - getLineAndCharacterOfPosition(pos: number) { - return getLineAndCharacterOfPosition(this, pos); - }, - }; - - const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); - const changes = flatMap(completionNodes, node => { - const nodeWithPos = textChanges.assignPositionsToNode(node); - return formatting.formatNodeGivenIndentation( - nodeWithPos, - syntheticFile, - sourceFile.languageVariant, - /* indentation */ 0, - /* delta */ 0, - { ...formatContext, options: formatOptions }); - }); - insertText = textChanges.applyChanges(syntheticFile.text, changes); + insertText = printer.printAndFormatSnippetList( + format, + factory.createNodeArray(completionNodes), + sourceFile, + formatContext); } else { // Otherwise, just use emitter to print the new nodes. insertText = printer.printSnippetList( - ListFormat.MultiLine | ListFormat.NoTrailingNewLine, + format, factory.createNodeArray(completionNodes), sourceFile); } @@ -1062,6 +1057,134 @@ namespace ts.Completions { return undefined; } + function getEntryForObjectLiteralMethodCompletion( + symbol: Symbol, + name: string, + enclosingDeclaration: ObjectLiteralExpression, + program: Program, + host: LanguageServiceHost, + options: CompilerOptions, + preferences: UserPreferences, + formatContext: formatting.FormatContext | undefined, + ): { insertText: string, isSnippet?: true, importAdder: codefix.ImportAdder, sourceDisplay: SymbolDisplayPart[] } | undefined { + const isSnippet = preferences.includeCompletionsWithSnippetText || undefined; + let insertText: string = name; + + const sourceFile = enclosingDeclaration.getSourceFile(); + const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host); + + const method = createObjectLiteralMethod(symbol, enclosingDeclaration, sourceFile, program, host, preferences, importAdder); + if (!method) { + return undefined; + } + + const printer = createSnippetPrinter({ + removeComments: true, + module: options.module, + target: options.target, + omitTrailingSemicolon: false, + newLine: getNewLineKind(getNewLineCharacter(options, maybeBind(host, host.getNewLine))), + }); + if (formatContext) { + insertText = printer.printAndFormatSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile, formatContext); + } + else { + insertText = printer.printSnippetList(ListFormat.CommaDelimited | ListFormat.AllowTrailingComma, factory.createNodeArray([method], /*hasTrailingComma*/ true), sourceFile); + } + + const methodSignature = factory.createMethodSignature( + method.modifiers, + method.name, + method.questionToken, + method.typeParameters, + method.parameters, + method.type); + const sourceDisplay = nodeToDisplayParts(methodSignature, enclosingDeclaration); + + return { isSnippet, insertText, importAdder, sourceDisplay }; + + }; + + function createObjectLiteralMethod( + symbol: Symbol, + enclosingDeclaration: ObjectLiteralExpression, + sourceFile: SourceFile, + program: Program, + host: LanguageServiceHost, + preferences: UserPreferences, + importAdder: codefix.ImportAdder, + ): MethodDeclaration | undefined { + const declarations = symbol.getDeclarations(); + if (!(declarations && declarations.length)) { + return undefined; + } + const checker = program.getTypeChecker(); + const scriptTarget = getEmitScriptTarget(program.getCompilerOptions()); + const declaration = declarations[0]; + const name = getSynthesizedDeepClone(getNameOfDeclaration(declaration), /*includeTrivia*/ false) as PropertyName; + const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration)); + const quotePreference = getQuotePreference(sourceFile, preferences); + const builderFlags = quotePreference === QuotePreference.Single ? NodeBuilderFlags.UseSingleQuotesForStringLiteralType : undefined; + + switch (declaration.kind) { + case SyntaxKind.PropertySignature: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.MethodDeclaration: { + let effectiveType = type.flags & TypeFlags.Union && (type as UnionType).types.length < 10 + ? checker.getUnionType((type as UnionType).types, UnionReduction.Subtype) + : type; + if (effectiveType.flags & TypeFlags.Union) { + // Only offer the completion if there's a single function type component. + const functionTypes = filter((effectiveType as UnionType).types, type => checker.getSignaturesOfType(type, SignatureKind.Call).length > 0); + if (functionTypes.length === 1) { + effectiveType = functionTypes[0]; + } + else { + return undefined; + } + } + const signatures = checker.getSignaturesOfType(effectiveType, SignatureKind.Call); + if (signatures.length !== 1) { + // We don't support overloads in object literals. + return undefined; + } + let typeNode = checker.typeToTypeNode(effectiveType, enclosingDeclaration, builderFlags, codefix.getNoopSymbolTrackerWithResolver({ program, host })); + if (!typeNode || !isFunctionTypeNode(typeNode)) { + return undefined; + } + const importableReference = codefix.tryGetAutoImportableReferenceFromTypeNode(typeNode, scriptTarget); + if (importableReference) { + typeNode = importableReference.typeNode; + codefix.importSymbols(importAdder, importableReference.symbols); + } + + let body; + if (preferences.includeCompletionsWithSnippetText) { + const emptyStmt = factory.createEmptyStatement(); + body = factory.createBlock([emptyStmt], /* multiline */ true); + setSnippetElement(emptyStmt, { kind: SnippetKind.TabStop, order: 0 }); + } + else { + body = factory.createBlock([], /* multiline */ true); + } + + return factory.createMethodDeclaration( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*asteriskToken*/ undefined, + name, + /*questionToken*/ undefined, + (typeNode as FunctionTypeNode).typeParameters, + (typeNode as FunctionTypeNode).parameters, + (typeNode as FunctionTypeNode).type, + body); + } + default: + return undefined; + } + } + function createSnippetPrinter( printerOptions: PrinterOptions, ) { @@ -1081,9 +1204,9 @@ namespace ts.Completions { return { printSnippetList, + printAndFormatSnippetList, }; - /* Snippet-escaping version of `printer.printList`. */ function printSnippetList( format: ListFormat, @@ -1094,6 +1217,36 @@ namespace ts.Completions { printer.writeList(format, list, sourceFile, writer); return writer.getText(); } + + function printAndFormatSnippetList( + format: ListFormat, + list: NodeArray, + sourceFile: SourceFile, + formatContext: formatting.FormatContext, + ): string { + const syntheticFile = { + text: printSnippetList( + format, + list, + sourceFile), + getLineAndCharacterOfPosition(pos: number) { + return getLineAndCharacterOfPosition(this, pos); + }, + }; + + const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); + const changes = flatMap(list, node => { + const nodeWithPos = textChanges.assignPositionsToNode(node); + return formatting.formatNodeGivenIndentation( + nodeWithPos, + syntheticFile, + sourceFile.languageVariant, + /* indentation */ 0, + /* delta */ 0, + { ...formatContext, options: formatOptions }); + }); + return textChanges.applyChanges(syntheticFile.text, changes); + } } function originToCompletionEntryData(origin: SymbolOriginInfoExport | SymbolOriginInfoResolvedExport): CompletionEntryData | undefined { @@ -1221,7 +1374,7 @@ namespace ts.Completions { importCompletionNode?: Node, recommendedCompletion?: Symbol, symbolToOriginInfoMap?: SymbolOriginInfoMap, - symbolToSortTextIdMap?: SymbolSortTextIdMap, + symbolToSortTextMap?: SymbolSortTextMap, isJsxIdentifierExpected?: boolean, isRightOfOpenTag?: boolean, ): UniqueNameSet { @@ -1238,13 +1391,13 @@ namespace ts.Completions { const symbol = symbols[i]; const origin = symbolToOriginInfoMap?.[i]; const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind, !!jsxIdentifierExpected); - if (!info || uniques.get(info.name) || kind === CompletionKind.Global && symbolToSortTextIdMap && !shouldIncludeSymbol(symbol, symbolToSortTextIdMap)) { + if (!info || (uniques.get(info.name) && (!origin || !originIsObjectLiteralMethod(origin))) || kind === CompletionKind.Global && symbolToSortTextMap && !shouldIncludeSymbol(symbol, symbolToSortTextMap)) { continue; } const { name, needsConvertPropertyAccess } = info; - const sortTextId = symbolToSortTextIdMap?.[getSymbolId(symbol)] ?? SortTextId.LocationPriority; - const sortText = (isDeprecated(symbol, typeChecker) ? SortTextId.DeprecatedOffset + sortTextId : sortTextId).toString() as SortText; + const originalSortText = symbolToSortTextMap?.[getSymbolId(symbol)] ?? SortText.LocationPriority; + const sortText = (isDeprecated(symbol, typeChecker) ? SortText.Deprecated(originalSortText) : originalSortText); const entry = createCompletionEntry( symbol, sortText, @@ -1289,7 +1442,7 @@ namespace ts.Completions { add: name => uniques.set(name, true), }; - function shouldIncludeSymbol(symbol: Symbol, symbolToSortTextIdMap: SymbolSortTextIdMap): boolean { + function shouldIncludeSymbol(symbol: Symbol, symbolToSortTextMap: SymbolSortTextMap): boolean { let allFlags = symbol.flags; if (!isSourceFile(location)) { // export = /**/ here we want to get all meanings, so any symbol is ok @@ -1312,9 +1465,9 @@ namespace ts.Completions { // Auto Imports are not available for scripts so this conditional is always false if (!!sourceFile.externalModuleIndicator && !compilerOptions.allowUmdGlobalAccess - && symbolToSortTextIdMap[getSymbolId(symbol)] === SortTextId.GlobalsOrKeywords - && (symbolToSortTextIdMap[getSymbolId(symbolOrigin)] === SortTextId.AutoImportSuggestions - || symbolToSortTextIdMap[getSymbolId(symbolOrigin)] === SortTextId.LocationPriority)) { + && symbolToSortTextMap[getSymbolId(symbol)] === SortText.GlobalsOrKeywords + && (symbolToSortTextMap[getSymbolId(symbolOrigin)] === SortText.AutoImportSuggestions + || symbolToSortTextMap[getSymbolId(symbolOrigin)] === SortText.LocationPriority)) { return false; } @@ -1406,7 +1559,7 @@ namespace ts.Completions { } const compilerOptions = program.getCompilerOptions(); - const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host); + const completionData = getCompletionData(program, log, sourceFile, compilerOptions, position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host, /*formatContext*/ undefined); if (!completionData) { return { type: "none" }; } @@ -1426,7 +1579,10 @@ namespace ts.Completions { return firstDefined(symbols, (symbol, index): SymbolCompletion | undefined => { const origin = symbolToOriginInfoMap[index]; const info = getCompletionEntryDisplayNameForSymbol(symbol, getEmitScriptTarget(compilerOptions), origin, completionKind, completionData.isJsxIdentifierExpected); - return info && info.name === entryId.name && (entryId.source === CompletionSource.ClassMemberSnippet && symbol.flags & SymbolFlags.ClassMember || getSourceFromOrigin(origin) === entryId.source) + return info && info.name === entryId.name && ( + entryId.source === CompletionSource.ClassMemberSnippet && symbol.flags & SymbolFlags.ClassMember + || entryId.source === CompletionSource.ObjectLiteralMethodSnippet && symbol.flags & (SymbolFlags.Property | SymbolFlags.Method) + || getSourceFromOrigin(origin) === entryId.source) ? { type: "symbol" as const, symbol, location, origin, contextToken, previousToken, isJsxInitializer, isTypeOnlyLocation } : undefined; }) || { type: "none" }; @@ -1562,6 +1718,29 @@ namespace ts.Completions { } } + if (source === CompletionSource.ObjectLiteralMethodSnippet) { + const enclosingDeclaration = tryGetObjectLikeCompletionContainer(contextToken) as ObjectLiteralExpression; + const { importAdder } = getEntryForObjectLiteralMethodCompletion( + symbol, + name, + enclosingDeclaration, + program, + host, + compilerOptions, + preferences, + formatContext)!; + if (importAdder.hasFixes()) { + const changes = textChanges.ChangeTracker.with({ host, formatContext, preferences }, importAdder.writeFixes); + return { + sourceDisplay: undefined, + codeActions: [{ + changes, + description: diagnosticToString([Diagnostics.Includes_imports_of_types_referenced_by_0, name]), + }], + }; + } + } + if (originIsTypeOnlyAlias(origin)) { const codeAction = codefix.getPromoteTypeOnlyCompletionAction( sourceFile, @@ -1631,7 +1810,7 @@ namespace ts.Completions { readonly contextToken: Node | undefined; readonly isJsxInitializer: IsJsxInitializer; readonly insideJsDocTagTypeExpression: boolean; - readonly symbolToSortTextIdMap: SymbolSortTextIdMap; + readonly symbolToSortTextMap: SymbolSortTextMap; readonly isTypeOnlyLocation: boolean; /** In JSX tag name and attribute names, identifiers like "my-tag" or "aria-name" is valid identifier. */ readonly isJsxIdentifierExpected: boolean; @@ -1713,15 +1892,16 @@ namespace ts.Completions { program: Program, log: (message: string) => void, sourceFile: SourceFile, - isUncheckedFile: boolean, + compilerOptions: CompilerOptions, position: number, preferences: UserPreferences, detailsEntryId: CompletionEntryIdentifier | undefined, host: LanguageServiceHost, + formatContext: formatting.FormatContext | undefined, cancellationToken?: CancellationToken, ): CompletionData | Request | undefined { const typeChecker = program.getTypeChecker(); - + const inUncheckedFile = isUncheckedFile(sourceFile, compilerOptions); let start = timestamp(); let currentToken = getTokenAtPosition(sourceFile, position); // TODO: GH#15853 // We will check for jsdoc comments with insideComment and getJsDocTagAtPosition. (TODO: that seems rather inefficient to check the same thing so many times.) @@ -1981,7 +2161,7 @@ namespace ts.Completions { // This also gets mutated in nested-functions after the return let symbols: Symbol[] = []; const symbolToOriginInfoMap: SymbolOriginInfoMap = []; - const symbolToSortTextIdMap: SymbolSortTextIdMap = []; + const symbolToSortTextMap: SymbolSortTextMap = []; const seenPropertySymbols = new Map(); const isTypeOnlyLocation = isTypeOnlyCompletion(); const getModuleSpecifierResolutionHost = memoizeOne((isFromPackageJson: boolean) => { @@ -2042,7 +2222,7 @@ namespace ts.Completions { contextToken, isJsxInitializer, insideJsDocTagTypeExpression, - symbolToSortTextIdMap, + symbolToSortTextMap, isTypeOnlyLocation, isJsxIdentifierExpected, isRightOfOpenTag, @@ -2171,7 +2351,7 @@ namespace ts.Completions { } const propertyAccess = node.kind === SyntaxKind.ImportType ? node as ImportTypeNode : node.parent as PropertyAccessExpression | QualifiedName; - if (isUncheckedFile) { + if (inUncheckedFile) { // In javascript files, for union types, we don't just get the members that // the individual types have in common, we also include all the members that // each individual type has. This is because we're going to add all identifiers @@ -2258,7 +2438,7 @@ namespace ts.Completions { function addSymbolSortInfo(symbol: Symbol) { if (isStaticProperty(symbol)) { - symbolToSortTextIdMap[getSymbolId(symbol)] = SortTextId.LocalDeclarationPriority; + symbolToSortTextMap[getSymbolId(symbol)] = SortText.LocalDeclarationPriority; } } @@ -2378,7 +2558,7 @@ namespace ts.Completions { const symbol = symbols[i]; if (!typeChecker.isArgumentsSymbol(symbol) && !some(symbol.declarations, d => d.getSourceFile() === sourceFile)) { - symbolToSortTextIdMap[getSymbolId(symbol)] = SortTextId.GlobalsOrKeywords; + symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords; } if (typeOnlyAliasNeedsPromotion && !(symbol.flags & SymbolFlags.Value)) { const typeOnlyAliasDeclaration = symbol.declarations && find(symbol.declarations, isTypeOnlyImportOrExportDeclaration); @@ -2396,7 +2576,7 @@ namespace ts.Completions { for (const symbol of getPropertiesForCompletion(thisType, typeChecker)) { symbolToOriginInfoMap[symbols.length] = { kind: SymbolOriginInfoKind.ThisType }; symbols.push(symbol); - symbolToSortTextIdMap[getSymbolId(symbol)] = SortTextId.SuggestedClassMembers; + symbolToSortTextMap[getSymbolId(symbol)] = SortText.SuggestedClassMembers; } } } @@ -2479,7 +2659,7 @@ namespace ts.Completions { return false; } - /** Mutates `symbols`, `symbolToOriginInfoMap`, and `symbolToSortTextIdMap` */ + /** Mutates `symbols`, `symbolToOriginInfoMap`, and `symbolToSortTextMap` */ function collectAutoImports() { if (!shouldOfferImportCompletions()) return; Debug.assert(!detailsEntryId?.data, "Should not run 'collectAutoImports' when faster path is available via `data`"); @@ -2583,15 +2763,69 @@ namespace ts.Completions { function pushAutoImportSymbol(symbol: Symbol, origin: SymbolOriginInfoResolvedExport | SymbolOriginInfoExport) { const symbolId = getSymbolId(symbol); - if (symbolToSortTextIdMap[symbolId] === SortTextId.GlobalsOrKeywords) { + if (symbolToSortTextMap[symbolId] === SortText.GlobalsOrKeywords) { // If an auto-importable symbol is available as a global, don't add the auto import return; } symbolToOriginInfoMap[symbols.length] = origin; - symbolToSortTextIdMap[symbolId] = importCompletionNode ? SortTextId.LocationPriority : SortTextId.AutoImportSuggestions; + symbolToSortTextMap[symbolId] = importCompletionNode ? SortText.LocationPriority : SortText.AutoImportSuggestions; symbols.push(symbol); } + /* Mutates `symbols` and `symbolToOriginInfoMap`. */ + function collectObjectLiteralMethodSymbols(members: Symbol[], enclosingDeclaration: ObjectLiteralExpression): void { + // TODO: support JS files. + if (isInJSFile(location)) { + return; + } + members.forEach(member => { + if (!isObjectLiteralMethodSymbol(member)) { + return; + } + const displayName = getCompletionEntryDisplayNameForSymbol( + member, + getEmitScriptTarget(compilerOptions), + /*origin*/ undefined, + CompletionKind.ObjectPropertyDeclaration, + /*jsxIdentifierExpected*/ false); + if (!displayName) { + return; + } + const { name } = displayName; + const entryProps = getEntryForObjectLiteralMethodCompletion( + member, + name, + enclosingDeclaration, + program, + host, + compilerOptions, + preferences, + formatContext); + if (!entryProps) { + return; + } + const origin: SymbolOriginInfoObjectLiteralMethod = { kind: SymbolOriginInfoKind.ObjectLiteralMethod, ...entryProps }; + symbolToOriginInfoMap[symbols.length] = origin; + symbols.push(member); + }); + } + + function isObjectLiteralMethodSymbol(symbol: Symbol): boolean { + /* + For an object type + `type Foo = { + bar(x: number): void; + foo: (x: string) => string; + }`, + `bar` will have symbol flag `Method`, + `foo` will have symbol flag `Property`. + */ + if (!(symbol.flags & (SymbolFlags.Property | SymbolFlags.Method))) { + return false; + } + return true; + } + /** * Finds the first node that "embraces" the position, so that one may * accurately aggregate locals from the closest containing scope. @@ -2753,6 +2987,7 @@ namespace ts.Completions { * @returns true if 'symbols' was successfully populated; false otherwise. */ function tryGetObjectLikeCompletionSymbols(): GlobalsSearch | undefined { + const symbolsStartIndex = symbols.length; const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken); if (!objectLikeContainer) return GlobalsSearch.Continue; @@ -2822,9 +3057,16 @@ namespace ts.Completions { if (typeMembers && typeMembers.length > 0) { // Add filtered items to the completion list - symbols = concatenate(symbols, filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers))); + const filteredMembers = filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers)); + symbols = concatenate(symbols, filteredMembers); + setSortTextToOptionalMember(); + if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression + && preferences.includeCompletionsWithObjectLiteralMethodSnippets + && preferences.includeCompletionsWithInsertText) { + transformObjectLiteralMembersSortText(symbolsStartIndex); + collectObjectLiteralMethodSymbols(filteredMembers, objectLikeContainer); + } } - setSortTextToOptionalMember(); return GlobalsSearch.Success; } @@ -2906,7 +3148,7 @@ namespace ts.Completions { localsContainer.locals?.forEach((symbol, name) => { symbols.push(symbol); if (localsContainer.symbol?.exports?.has(name)) { - symbolToSortTextIdMap[getSymbolId(symbol)] = SortTextId.OptionalMember; + symbolToSortTextMap[getSymbolId(symbol)] = SortText.OptionalMember; } }); return GlobalsSearch.Success; @@ -2966,31 +3208,6 @@ namespace ts.Completions { return GlobalsSearch.Success; } - /** - * Returns the immediate owning object literal or binding pattern of a context token, - * on the condition that one exists and that the context implies completion should be given. - */ - function tryGetObjectLikeCompletionContainer(contextToken: Node): ObjectLiteralExpression | ObjectBindingPattern | undefined { - if (contextToken) { - const { parent } = contextToken; - switch (contextToken.kind) { - case SyntaxKind.OpenBraceToken: // const x = { | - case SyntaxKind.CommaToken: // const x = { a: 0, | - if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) { - return parent; - } - break; - case SyntaxKind.AsteriskToken: - return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined; - case SyntaxKind.Identifier: - return (contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent) - ? contextToken.parent.parent : undefined; - } - } - - return undefined; - } - function isConstructorParameterCompletion(node: Node): boolean { return !!node.parent && isParameter(node.parent) && isConstructorDeclaration(node.parent.parent) && (isParameterPropertyModifier(node.kind) || isDeclarationName(node)); @@ -3371,7 +3588,7 @@ namespace ts.Completions { symbols.forEach(m => { if (m.flags & SymbolFlags.Optional) { const symbolId = getSymbolId(m); - symbolToSortTextIdMap[symbolId] = symbolToSortTextIdMap[symbolId] ?? SortTextId.OptionalMember; + symbolToSortTextMap[symbolId] = symbolToSortTextMap[symbolId] ?? SortText.OptionalMember; } }); } @@ -3383,7 +3600,27 @@ namespace ts.Completions { } for (const contextualMemberSymbol of contextualMemberSymbols) { if (membersDeclaredBySpreadAssignment.has(contextualMemberSymbol.name)) { - symbolToSortTextIdMap[getSymbolId(contextualMemberSymbol)] = SortTextId.MemberDeclaredBySpreadAssignment; + symbolToSortTextMap[getSymbolId(contextualMemberSymbol)] = SortText.MemberDeclaredBySpreadAssignment; + } + } + } + + function transformObjectLiteralMembersSortText(start: number): void { + for (let i = start; i < symbols.length; i++) { + const symbol = symbols[i]; + const symbolId = getSymbolId(symbol); + const origin = symbolToOriginInfoMap?.[i]; + const target = getEmitScriptTarget(compilerOptions); + const displayName = getCompletionEntryDisplayNameForSymbol( + symbol, + target, + origin, + CompletionKind.ObjectPropertyDeclaration, + /*jsxIdentifierExpected*/ false); + if (displayName) { + const originalSortText = symbolToSortTextMap[symbolId] ?? SortText.LocationPriority; + const { name } = displayName; + symbolToSortTextMap[symbolId] = SortText.ObjectLiteralProperty(originalSortText, name); } } } @@ -3466,6 +3703,31 @@ namespace ts.Completions { } } + /** + * Returns the immediate owning object literal or binding pattern of a context token, + * on the condition that one exists and that the context implies completion should be given. + */ + function tryGetObjectLikeCompletionContainer(contextToken: Node | undefined): ObjectLiteralExpression | ObjectBindingPattern | undefined { + if (contextToken) { + const { parent } = contextToken; + switch (contextToken.kind) { + case SyntaxKind.OpenBraceToken: // const x = { | + case SyntaxKind.CommaToken: // const x = { a: 0, | + if (isObjectLiteralExpression(parent) || isObjectBindingPattern(parent)) { + return parent; + } + break; + case SyntaxKind.AsteriskToken: + return isMethodDeclaration(parent) ? tryCast(parent.parent, isObjectLiteralExpression) : undefined; + case SyntaxKind.Identifier: + return (contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent) + ? contextToken.parent.parent : undefined; + } + } + + return undefined; + } + function getRelevantTokens(position: number, sourceFile: SourceFile): { contextToken: Node, previousToken: Node } | { contextToken: undefined, previousToken: undefined } { const previousToken = findPrecedingToken(position, sourceFile); if (previousToken && position <= previousToken.end && (isMemberName(previousToken) || isKeyword(previousToken.kind))) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 88836f59c3428..04a79bb5ed9e6 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2395,6 +2395,14 @@ namespace ts { }); } + export function nodeToDisplayParts(node: Node, enclosingDeclaration: Node): SymbolDisplayPart[] { + const file = enclosingDeclaration.getSourceFile(); + return mapToDisplayParts(writer => { + const printer = createPrinter({ removeComments: true, omitTrailingSemicolon: true }); + printer.writeNode(EmitHint.Unspecified, node, file, writer); + }); + } + export function isImportOrExportSpecifierName(location: Node): location is Identifier { return !!location.parent && isImportOrExportSpecifier(location.parent) && location.parent.propertyName === location; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3fce914915dc4..32bbdc4c79a39 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4099,6 +4099,7 @@ declare namespace ts { readonly includeAutomaticOptionalChainCompletions?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly includeCompletionsWithClassMemberSnippets?: boolean; + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ @@ -9628,6 +9629,13 @@ declare namespace ts.server.protocol { * `class A { foo }`. */ readonly includeCompletionsWithClassMemberSnippets?: boolean; + /** + * If enabled, object literal methods will have a method declaration completion entry in addition + * to the regular completion entry containing just the method name. + * E.g., `const objectLiteral: T = { f| }` could be completed to `const objectLiteral: T = { foo(): void {} }`, + * in addition to `const objectLiteral: T = { foo }`. + */ + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 9e1c635ac7499..9f9f42ebdfa62 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4099,6 +4099,7 @@ declare namespace ts { readonly includeAutomaticOptionalChainCompletions?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly includeCompletionsWithClassMemberSnippets?: boolean; + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative"; /** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */ diff --git a/tests/baselines/reference/completionsCommentsClass.baseline b/tests/baselines/reference/completionsCommentsClass.baseline index 5f4294590d9f2..66766ee70bef1 100644 --- a/tests/baselines/reference/completionsCommentsClass.baseline +++ b/tests/baselines/reference/completionsCommentsClass.baseline @@ -3760,7 +3760,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -3850,7 +3850,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", diff --git a/tests/baselines/reference/completionsCommentsClassMembers.baseline b/tests/baselines/reference/completionsCommentsClassMembers.baseline index 0486ae6854a7a..af89101b92aed 100644 --- a/tests/baselines/reference/completionsCommentsClassMembers.baseline +++ b/tests/baselines/reference/completionsCommentsClassMembers.baseline @@ -4632,7 +4632,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -4722,7 +4722,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -11688,7 +11688,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -11778,7 +11778,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -16500,7 +16500,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -16590,7 +16590,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -23556,7 +23556,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -23646,7 +23646,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -27620,7 +27620,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -27710,7 +27710,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -32839,7 +32839,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -32929,7 +32929,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -36857,7 +36857,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -36947,7 +36947,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -42030,7 +42030,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -42120,7 +42120,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -47249,7 +47249,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -47339,7 +47339,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -52468,7 +52468,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -52558,7 +52558,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -57687,7 +57687,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -57777,7 +57777,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -61746,7 +61746,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -61836,7 +61836,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -65805,7 +65805,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -65895,7 +65895,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -69864,7 +69864,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -69954,7 +69954,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -73923,7 +73923,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -74013,7 +74013,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -77982,7 +77982,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -78072,7 +78072,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -82041,7 +82041,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -82131,7 +82131,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -86596,7 +86596,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -86686,7 +86686,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -91952,7 +91952,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -92042,7 +92042,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -96407,7 +96407,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -96497,7 +96497,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", diff --git a/tests/baselines/reference/completionsCommentsCommentParsing.baseline b/tests/baselines/reference/completionsCommentsCommentParsing.baseline index ea44b2c255f9a..1ec7d5e207235 100644 --- a/tests/baselines/reference/completionsCommentsCommentParsing.baseline +++ b/tests/baselines/reference/completionsCommentsCommentParsing.baseline @@ -5467,7 +5467,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -5557,7 +5557,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -11910,7 +11910,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -12000,7 +12000,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -17533,7 +17533,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -17623,7 +17623,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -23233,7 +23233,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -23323,7 +23323,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -28975,7 +28975,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -29065,7 +29065,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -35418,7 +35418,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -35508,7 +35508,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -41118,7 +41118,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -41208,7 +41208,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", diff --git a/tests/baselines/reference/completionsCommentsFunctionDeclaration.baseline b/tests/baselines/reference/completionsCommentsFunctionDeclaration.baseline index 9f0294c4e8286..ae41c9cc50c9e 100644 --- a/tests/baselines/reference/completionsCommentsFunctionDeclaration.baseline +++ b/tests/baselines/reference/completionsCommentsFunctionDeclaration.baseline @@ -4130,7 +4130,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -4220,7 +4220,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -7606,7 +7606,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -7696,7 +7696,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -11916,7 +11916,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -12006,7 +12006,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", diff --git a/tests/baselines/reference/completionsCommentsFunctionExpression.baseline b/tests/baselines/reference/completionsCommentsFunctionExpression.baseline index ddada8f7b64a3..9c1bc3392836e 100644 --- a/tests/baselines/reference/completionsCommentsFunctionExpression.baseline +++ b/tests/baselines/reference/completionsCommentsFunctionExpression.baseline @@ -3667,7 +3667,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -3757,7 +3757,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -8389,7 +8389,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -8479,7 +8479,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -12055,7 +12055,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -12145,7 +12145,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -16549,7 +16549,7 @@ "name": "escape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", @@ -16639,7 +16639,7 @@ "name": "unescape", "kind": "function", "kindModifiers": "deprecated,declare", - "sortText": "24", + "sortText": "z15", "displayParts": [ { "text": "function", diff --git a/tests/baselines/reference/completionsStringMethods.baseline b/tests/baselines/reference/completionsStringMethods.baseline index 4e12c7480abd9..327daf9f0d9a2 100644 --- a/tests/baselines/reference/completionsStringMethods.baseline +++ b/tests/baselines/reference/completionsStringMethods.baseline @@ -2164,7 +2164,7 @@ "name": "substr", "kind": "method", "kindModifiers": "deprecated,declare", - "sortText": "20", + "sortText": "z11", "displayParts": [ { "text": "(", diff --git a/tests/cases/fourslash/completionAfterGlobalThis.ts b/tests/cases/fourslash/completionAfterGlobalThis.ts index ae4056c0b60a0..f1c8aa59302f1 100644 --- a/tests/cases/fourslash/completionAfterGlobalThis.ts +++ b/tests/cases/fourslash/completionAfterGlobalThis.ts @@ -9,8 +9,8 @@ verify.completions({ ...completion.globalsVars, completion.undefinedVarEntry ].map(e => { - if (e.sortText === completion.SortText.DeprecatedGlobalsOrKeywords) { - return { ...e, sortText: completion.SortText.DeprecatedLocationPriority }; + if (e.sortText === completion.SortText.Deprecated(completion.SortText.GlobalsOrKeywords)) { + return { ...e, sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) }; } return { ...e, sortText: completion.SortText.LocationPriority }; }) diff --git a/tests/cases/fourslash/completionListAfterRegularExpressionLiteral01.ts b/tests/cases/fourslash/completionListAfterRegularExpressionLiteral01.ts index baf71f0533fb5..9451280e237b1 100644 --- a/tests/cases/fourslash/completionListAfterRegularExpressionLiteral01.ts +++ b/tests/cases/fourslash/completionListAfterRegularExpressionLiteral01.ts @@ -3,4 +3,16 @@ ////let v = 100; /////a/./**/ -verify.completions({ marker: "", unsorted: ["exec", "test", "source", "global", "ignoreCase", "multiline", "lastIndex", { name: "compile", sortText: completion.SortText.DeprecatedLocationPriority }] }); +verify.completions({ + marker: "", + unsorted: [ + "exec", + "test", + "source", + "global", + "ignoreCase", + "multiline", + "lastIndex", + { name: "compile", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } + ] +}); diff --git a/tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts b/tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts index 6c3f631dde5d2..872b216462510 100644 --- a/tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts +++ b/tests/cases/fourslash/completionListAfterRegularExpressionLiteral1.ts @@ -2,4 +2,16 @@ /////a/./**/ -verify.completions({ marker: "", unsorted: ["exec", "test", "source", "global", "ignoreCase", "multiline", "lastIndex", { name: "compile", sortText: completion.SortText.DeprecatedLocationPriority }] }); +verify.completions({ + marker: "", + unsorted: [ + "exec", + "test", + "source", + "global", + "ignoreCase", + "multiline", + "lastIndex", + { name: "compile", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) }, + ] +}); diff --git a/tests/cases/fourslash/completionListAfterStringLiteral1.ts b/tests/cases/fourslash/completionListAfterStringLiteral1.ts index 74cb1bb1c7eaa..51b5903be0193 100644 --- a/tests/cases/fourslash/completionListAfterStringLiteral1.ts +++ b/tests/cases/fourslash/completionListAfterStringLiteral1.ts @@ -6,6 +6,6 @@ verify.completions({ marker: "", unsorted: [ "toString", "charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "localeCompare", "match", "replace", "search", "slice", - "split", "substring", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", "length", { name: "substr", sortText: completion.SortText.DeprecatedLocationPriority }, "valueOf", + "split", "substring", "toLowerCase", "toLocaleLowerCase", "toUpperCase", "toLocaleUpperCase", "trim", "length", { name: "substr", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) }, "valueOf", ], }); diff --git a/tests/cases/fourslash/completionsObjectLiteralMethod1.ts b/tests/cases/fourslash/completionsObjectLiteralMethod1.ts new file mode 100644 index 0000000000000..e6fef55cfa4ff --- /dev/null +++ b/tests/cases/fourslash/completionsObjectLiteralMethod1.ts @@ -0,0 +1,151 @@ +/// + +// @newline: LF +// @Filename: a.ts +////interface IFoo { +//// bar(x: number): void; +////} +//// +////const obj: IFoo = { +//// /*a*/ +////} +////type Foo = { +//// bar(x: number): void; +//// foo: (x: string) => string; +////} +//// +////const f: Foo = { +//// /*b*/ +////} +//// +////interface Overload { +//// buzz(a: number): number; +//// buzz(a: string): string; +////} +////const o: Overload = { +//// /*c*/ +////} +////interface Prop { +//// "space bar"(): string; +////} +////const p: Prop = { +//// /*d*/ +////} + +verify.completions({ + marker: "a", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "bar", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"), + insertText: undefined, + }, + { + name: "bar", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "bar(x: number): void {\n},", + }, + ], +}); +verify.completions({ + marker: "b", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "bar", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"), + insertText: undefined, + }, + { + name: "bar", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "bar(x: number): void {\n},", + }, + { + name: "foo", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo"), + insertText: undefined, + }, + { + name: "foo", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "foo(x: string): string {\n},", + }, + ], +}); +verify.completions({ + marker: "c", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + exact: [ + { + name: "buzz", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "buzz"), + // no declaration insert text, because this property has overloads + insertText: undefined, + }, + ], +}); +verify.completions({ + marker: "d", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "\"space bar\"", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "\"space bar\""), + insertText: undefined, + }, + { + name: "\"space bar\"", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "\"space bar\"")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "\"space bar\"(): string {\n},", + }, + ], +}); +verify.completions({ + marker: "a", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: true, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "bar", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"), + insertText: undefined, + }, + { + name: "bar", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + isSnippet: true, + insertText: "bar(x: number): void {\n $0\n},", + }, + ], +}); diff --git a/tests/cases/fourslash/completionsObjectLiteralMethod2.ts b/tests/cases/fourslash/completionsObjectLiteralMethod2.ts new file mode 100644 index 0000000000000..08e2fa824a79c --- /dev/null +++ b/tests/cases/fourslash/completionsObjectLiteralMethod2.ts @@ -0,0 +1,60 @@ +/// + +// @newline: LF +// @Filename: a.ts +////export interface IFoo { +//// bar(x: number): void; +////} + +// @Filename: b.ts +////import { IFoo } from "./a"; +////export interface IBar { +//// foo(f: IFoo): void; +////} + +// @Filename: c.ts +////import { IBar } from "./b"; +////const obj: IBar = { +//// /*a*/ +////} + +verify.completions({ + marker: "a", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "foo", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo"), + insertText: undefined, + }, + { + name: "foo", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "foo(f: IFoo): void {\n},", + hasAction: true, + }, + ], +}); + +verify.applyCodeActionFromCompletion("a", { + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + name: "foo", + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + description: "Includes imports of types referenced by 'foo'", + newFileContent: +`import { IFoo } from "./a"; +import { IBar } from "./b"; +const obj: IBar = { + +}` +}); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsObjectLiteralMethod3.ts b/tests/cases/fourslash/completionsObjectLiteralMethod3.ts new file mode 100644 index 0000000000000..96aa3467540f1 --- /dev/null +++ b/tests/cases/fourslash/completionsObjectLiteralMethod3.ts @@ -0,0 +1,135 @@ +/// + +// @newline: LF +// @strictNullChecks: true +// @Filename: a.ts +////interface I1 { +//// M(x: number): void; +////} +////interface I2 { +//// M(x: number): void; +////} +////const u: I1 | I2 = { +//// /*a*/ +////} +////const i: I1 & I2 = { +//// /*b*/ +////} +////interface U1 { +//// M(x: number): string; +////} +////interface U2 { +//// M(x: string): number; +////} +////const o: U1 | U2 = { +//// /*c*/ +////} +////interface Op { +//// M?(x: number): void; +//// N: ((x: string) => void) | null | undefined; +//// O?: () => void; +////} +////const op: Op = { +//// /*d*/ +////} + +verify.completions({ + marker: "a", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "M", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"), + insertText: undefined, + }, + { + name: "M", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "M(x: number): void {\n},", + }, + ], +}); +verify.completions({ + marker: "b", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "M", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"), + insertText: undefined, + }, + // No signature completion because type of `M` is intersection type + ], +}); +verify.completions({ + marker: "c", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + exact: [ + { + name: "M", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"), + insertText: undefined, + }, + // No signature completion because type of `M` is intersection type + ], +}); +verify.completions({ + marker: "d", + preferences: { + includeCompletionsWithInsertText: true, + includeCompletionsWithSnippetText: false, + includeCompletionsWithObjectLiteralMethodSnippets: true, + }, + includes: [ + { + name: "M", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "M"), + insertText: undefined, + }, + { + name: "M", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "M")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "M(x: number): void {\n},", + }, + { + name: "N", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "N"), + insertText: undefined, + }, + { + name: "N", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "N")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "N(x: string): void {\n},", + }, + { + name: "O", + sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "O"), + insertText: undefined, + }, + { + name: "O", + sortText: completion.SortText.SortBelow( + completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "O")), + source: completion.CompletionSource.ObjectLiteralMethodSnippet, + insertText: "O(): void {\n},", + }, + ], +}); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag1.ts b/tests/cases/fourslash/completionsWithDeprecatedTag1.ts index 1ce322b6a1c4c..b72752f6671f4 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag1.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag1.ts @@ -25,26 +25,26 @@ verify.completions({ marker: "1", includes: [ - { name: "Foo", kind: "interface", kindModifiers: "deprecated", sortText: completion.SortText.DeprecatedLocationPriority } + { name: "Foo", kind: "interface", kindModifiers: "deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } ] }, { marker: "2", includes: [ - { name: "bar", kind: "method", kindModifiers: "deprecated", sortText: completion.SortText.DeprecatedLocationPriority } + { name: "bar", kind: "method", kindModifiers: "deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } ] }, { marker: "3", includes: [ - { name: "prop", kind: "property", kindModifiers: "deprecated", sortText: completion.SortText.DeprecatedLocationPriority } + { name: "prop", kind: "property", kindModifiers: "deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } ] }, { marker: "4", includes: [ - { name: "foobar", kind: "function", kindModifiers: "export,deprecated", sortText: completion.SortText.DeprecatedLocationPriority } + { name: "foobar", kind: "function", kindModifiers: "export,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } ] }, { marker: "5", includes: [ - { name: "foobar", kind: "alias", kindModifiers: "export,deprecated", sortText: completion.SortText.DeprecatedLocationPriority } + { name: "foobar", kind: "alias", kindModifiers: "export,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) } ] }); diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag10.ts b/tests/cases/fourslash/completionsWithDeprecatedTag10.ts index f12e33feef458..dd44edab2a1f1 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag10.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag10.ts @@ -16,7 +16,7 @@ verify.completions({ hasAction: true, kind: "const", kindModifiers: "export,deprecated", - sortText: completion.SortText.DeprecatedAutoImportSuggestions + sortText: completion.SortText.Deprecated(completion.SortText.AutoImportSuggestions), }], preferences: { includeCompletionsForModuleExports: true, diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag2.ts b/tests/cases/fourslash/completionsWithDeprecatedTag2.ts index 9561a9a56c502..5d9357cd089fe 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag2.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag2.ts @@ -13,6 +13,6 @@ verify.completions({ name: "foo", kind: "function", kindModifiers: "deprecated,declare", - sortText: completion.SortText.DeprecatedLocationPriority + sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority), }] }); diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag4.ts b/tests/cases/fourslash/completionsWithDeprecatedTag4.ts index 9df8bc60e91ba..ecd7571c7f4b1 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag4.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag4.ts @@ -18,6 +18,6 @@ verify.completions({ name: "abc", kind: "property", kindModifiers: "deprecated,declare,optional", - sortText: completion.SortText.DeprecatedOptionalMember + sortText: completion.SortText.Deprecated(completion.SortText.OptionalMember), }], }); diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag5.ts b/tests/cases/fourslash/completionsWithDeprecatedTag5.ts index e99be425e8a90..dcdf45626d9c2 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag5.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag5.ts @@ -17,7 +17,7 @@ verify.completions({ name: "m", kind: "method", kindModifiers: "static,deprecated", - sortText: completion.SortText.DeprecatedLocalDeclarationPriority + sortText: completion.SortText.Deprecated(completion.SortText.LocalDeclarationPriority), }, ]) }); diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag6.ts b/tests/cases/fourslash/completionsWithDeprecatedTag6.ts index b174e97660378..c3dfcc799d39a 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag6.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag6.ts @@ -12,6 +12,6 @@ verify.completions({ name: "foo", kind: "var", kindModifiers: "export,deprecated", - sortText: completion.SortText.DeprecatedLocationPriority + sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority), }] }); diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag7.ts b/tests/cases/fourslash/completionsWithDeprecatedTag7.ts index dc1bf9bdac521..72d9c53d83b18 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag7.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag7.ts @@ -20,7 +20,7 @@ verify.completions({ marker: "", exact: [{ name: "a", - sortText: completion.SortText.DeprecatedMemberDeclaredBySpreadAssignment, + sortText: completion.SortText.Deprecated(completion.SortText.MemberDeclaredBySpreadAssignment), kind: 'property', kindModifiers: "deprecated" }] diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag8.ts b/tests/cases/fourslash/completionsWithDeprecatedTag8.ts index d326d0daf0931..1c9338b26d94f 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag8.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag8.ts @@ -15,7 +15,7 @@ verify.completions({ kind: "property", kindModifiers: "deprecated", insertText: "this.p", - sortText: completion.SortText.DeprecatedSuggestedClassMembers, + sortText: completion.SortText.Deprecated(completion.SortText.SuggestedClassMembers), source: completion.CompletionSource.ThisProperty }], preferences: { diff --git a/tests/cases/fourslash/completionsWithDeprecatedTag9.ts b/tests/cases/fourslash/completionsWithDeprecatedTag9.ts index 819aeb1802587..0a1e2c0c5ad94 100644 --- a/tests/cases/fourslash/completionsWithDeprecatedTag9.ts +++ b/tests/cases/fourslash/completionsWithDeprecatedTag9.ts @@ -21,7 +21,7 @@ verify.completions({ name: "foo", kind: "var", kindModifiers: "deprecated,declare", - sortText: completion.SortText.DeprecatedGlobalsOrKeywords + sortText: completion.SortText.Deprecated(completion.SortText.GlobalsOrKeywords), }] }, { preferences: { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 1ed08b3a020a6..147bb243091fe 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -647,6 +647,7 @@ declare namespace FourSlashInterface { readonly includeCompletionsWithSnippetText?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly includeCompletionsWithClassMemberSnippets?: boolean; + readonly includeCompletionsWithObjectLiteralMethodSnippets?: boolean; readonly allowIncompleteCompletions?: boolean; /** @deprecated use `includeCompletionsWithInsertText` */ readonly includeInsertTextCompletions?: boolean; @@ -838,28 +839,28 @@ declare namespace completion { interface GlobalsPlusOptions { noLib?: boolean; } - export const enum SortText { - LocalDeclarationPriority = "10", - LocationPriority = "11", - OptionalMember = "12", - MemberDeclaredBySpreadAssignment = "13", - SuggestedClassMembers = "14", - GlobalsOrKeywords = "15", - AutoImportSuggestions = "16", - ClassMemberSnippets = "17", - JavascriptIdentifiers = "18", - DeprecatedLocalDeclarationPriority = "19", - DeprecatedLocationPriority = "20", - DeprecatedOptionalMember = "21", - DeprecatedMemberDeclaredBySpreadAssignment = "22", - DeprecatedSuggestedClassMembers = "23", - DeprecatedGlobalsOrKeywords = "24", - DeprecatedAutoImportSuggestions = "25" - } + export type SortText = string & { __sortText: any }; + export const SortText: { + LocalDeclarationPriority: SortText, + LocationPriority: SortText, + OptionalMember: SortText, + MemberDeclaredBySpreadAssignment: SortText, + SuggestedClassMembers: SortText, + GlobalsOrKeywords: SortText, + AutoImportSuggestions: SortText, + ClassMemberSnippets: SortText, + JavascriptIdentifiers: SortText, + + Deprecated(sortText: SortText): SortText, + ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText, + SortBelow(sortText: SortText): SortText, + }; + export const enum CompletionSource { ThisProperty = "ThisProperty/", ClassMemberSnippet = "ClassMemberSnippet/", TypeOnlyAlias = "TypeOnlyAlias/", + ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/", } export const globalThisEntry: Entry; export const undefinedVarEntry: Entry;