diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 56b273d5c2ea3..0d01c46edd7b5 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -7684,6 +7684,10 @@
"category": "Message",
"code": 95186
},
+ "Add missing comma for object member completion '{0}'.": {
+ "category": "Message",
+ "code": 95187
+ },
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",
diff --git a/src/services/completions.ts b/src/services/completions.ts
index d39b357776885..2b47de8b7db94 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -164,6 +164,7 @@ import {
isFunctionLikeDeclaration,
isFunctionLikeKind,
isFunctionTypeNode,
+ isGetAccessorDeclaration,
isIdentifier,
isIdentifierText,
isImportableFile,
@@ -217,9 +218,11 @@ import {
isPrivateIdentifier,
isPrivateIdentifierClassElementDeclaration,
isPropertyAccessExpression,
+ isPropertyAssignment,
isPropertyDeclaration,
isPropertyNameLiteral,
isRegularExpressionLiteral,
+ isSetAccessorDeclaration,
isShorthandPropertyAssignment,
isSingleOrDoubleQuote,
isSourceFile,
@@ -443,6 +446,8 @@ export enum CompletionSource {
ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/",
/** Case completions for switch statements */
SwitchCases = "SwitchCases/",
+ /** Completions for an Object literal expression */
+ ObjectLiteralMemberWithComma = "ObjectLiteralMemberWithComma/",
}
/** @internal */
@@ -1683,6 +1688,30 @@ function createCompletionEntry(
hasAction = true;
}
+ // Provide object member completions when missing commas, and insert missing commas.
+ // For example:
+ //
+ // interface I {
+ // a: string;
+ // b: number
+ // }
+ //
+ // const cc: I = { a: "red" | }
+ //
+ // Completion should add a comma after "red" and provide completions for b
+ if (completionKind === CompletionKind.ObjectPropertyDeclaration && contextToken && findPrecedingToken(contextToken.pos, sourceFile, contextToken)?.kind !== SyntaxKind.CommaToken) {
+ if (isMethodDeclaration(contextToken.parent.parent) ||
+ isGetAccessorDeclaration(contextToken.parent.parent) ||
+ isSetAccessorDeclaration(contextToken.parent.parent) ||
+ isSpreadAssignment(contextToken.parent) ||
+ findAncestor(contextToken.parent, isPropertyAssignment)?.getLastToken(sourceFile) === contextToken ||
+ isShorthandPropertyAssignment(contextToken.parent) && getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) {
+
+ source = CompletionSource.ObjectLiteralMemberWithComma;
+ hasAction = true;
+ }
+ }
+
if (preferences.includeCompletionsWithClassMemberSnippets &&
preferences.includeCompletionsWithInsertText &&
completionKind === CompletionKind.MemberLike &&
@@ -2664,7 +2693,8 @@ function getSymbolCompletionFromEntryId(
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)
+ || getSourceFromOrigin(origin) === entryId.source
+ || entryId.source === CompletionSource.ObjectLiteralMemberWithComma)
? { type: "symbol" as const, symbol, location, origin, contextToken, previousToken, isJsxInitializer, isTypeOnlyLocation }
: undefined;
}) || { type: "none" };
@@ -2860,6 +2890,23 @@ function getCompletionEntryCodeActionsAndSourceDisplay(
return { codeActions: [codeAction], sourceDisplay: undefined };
}
+ if (source === CompletionSource.ObjectLiteralMemberWithComma && contextToken) {
+ const changes = textChanges.ChangeTracker.with(
+ { host, formatContext, preferences },
+ tracker => tracker.insertText(sourceFile, contextToken.end, ",")
+ );
+
+ if (changes) {
+ return {
+ sourceDisplay: undefined,
+ codeActions: [{
+ changes,
+ description: diagnosticToString([Diagnostics.Add_missing_comma_for_object_member_completion_0, name]),
+ }],
+ };
+ }
+ }
+
if (!origin || !(originIsExport(origin) || originIsResolvedExport(origin))) {
return { codeActions: undefined, sourceDisplay: undefined };
}
@@ -4156,7 +4203,7 @@ function getCompletionData(
*/
function tryGetObjectLikeCompletionSymbols(): GlobalsSearch | undefined {
const symbolsStartIndex = symbols.length;
- const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken);
+ const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken, position, sourceFile);
if (!objectLikeContainer) return GlobalsSearch.Continue;
// We're looking up possible property names from contextual/inferred/declared type.
@@ -4884,7 +4931,7 @@ function getCompletionData(
* 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 {
+function tryGetObjectLikeCompletionContainer(contextToken: Node | undefined, position: number, sourceFile: SourceFile): ObjectLiteralExpression | ObjectBindingPattern | undefined {
if (contextToken) {
const { parent } = contextToken;
switch (contextToken.kind) {
@@ -4899,8 +4946,33 @@ function tryGetObjectLikeCompletionContainer(contextToken: Node | undefined): Ob
case SyntaxKind.AsyncKeyword:
return tryCast(parent.parent, isObjectLiteralExpression);
case SyntaxKind.Identifier:
- return (contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent)
- ? contextToken.parent.parent : undefined;
+ if ((contextToken as Identifier).text === "async" && isShorthandPropertyAssignment(contextToken.parent)) {
+ return contextToken.parent.parent;
+ }
+ else {
+ if (isObjectLiteralExpression(contextToken.parent.parent) &&
+ (isSpreadAssignment(contextToken.parent) || isShorthandPropertyAssignment(contextToken.parent) &&
+ (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line))) {
+ return contextToken.parent.parent;
+ }
+ const ancestorNode = findAncestor(parent, isPropertyAssignment);
+ if (ancestorNode?.getLastToken(sourceFile) === contextToken && isObjectLiteralExpression(ancestorNode.parent)) {
+ return ancestorNode.parent;
+ }
+ }
+ break;
+ default:
+ if (parent.parent?.parent && (isMethodDeclaration(parent.parent) || isGetAccessorDeclaration(parent.parent) || isSetAccessorDeclaration(parent.parent)) && isObjectLiteralExpression(parent.parent.parent)) {
+ return parent.parent.parent;
+ }
+ if (isSpreadAssignment(parent) && isObjectLiteralExpression(parent.parent)) {
+ return parent.parent;
+ }
+ const ancestorNode = findAncestor(parent, isPropertyAssignment);
+ if (contextToken.kind !== SyntaxKind.ColonToken && ancestorNode?.getLastToken(sourceFile) === contextToken &&
+ isObjectLiteralExpression(ancestorNode.parent)) {
+ return ancestorNode.parent;
+ }
}
}
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions1.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions1.ts
new file mode 100644
index 0000000000000..6345fcb6569ee
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions1.ts
@@ -0,0 +1,43 @@
+///
+//// interface ColorPalette {
+//// primary?: string;
+//// secondary?: string;
+//// }
+
+//// let colors: ColorPalette = {
+//// primary: "red"
+//// /**/
+//// };
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "secondary",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "secondary",
+ description: `Add missing comma for object member completion 'secondary'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface ColorPalette {
+ primary?: string;
+ secondary?: string;
+}
+let colors: ColorPalette = {
+ primary: "red",
+
+};`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions10.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions10.ts
new file mode 100644
index 0000000000000..5f98448820949
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions10.ts
@@ -0,0 +1,47 @@
+///
+//// interface TTTT {
+//// aaa: string,
+//// bbb?: number
+//// }
+//// const uuu: TTTT = {
+//// get aaa() {
+//// return ""
+//// }
+//// /**/
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "bbb",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+ });
+
+ verify.applyCodeActionFromCompletion("", {
+ name: "bbb",
+ description: `Add missing comma for object member completion 'bbb'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface TTTT {
+ aaa: string,
+ bbb?: number
+}
+const uuu: TTTT = {
+ get aaa() {
+ return ""
+ },
+
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+ });
+
\ No newline at end of file
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions2.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions2.ts
new file mode 100644
index 0000000000000..8a67507a409c0
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions2.ts
@@ -0,0 +1,48 @@
+///
+//// interface ColorPalette {
+//// primary?: string;
+//// secondary?: string;
+//// }
+
+//// interface I {
+//// color: ColorPalette;
+//// }
+
+//// const a: I = {
+//// color: {primary: "red" /**/}
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "secondary",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ }
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "secondary",
+ description: `Add missing comma for object member completion 'secondary'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+`interface ColorPalette {
+ primary?: string;
+ secondary?: string;
+}
+interface I {
+ color: ColorPalette;
+}
+const a: I = {
+ color: {primary: "red", }
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions3.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions3.ts
new file mode 100644
index 0000000000000..a8003c105888e
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions3.ts
@@ -0,0 +1,45 @@
+///
+////interface T {
+//// aaa?: string;
+//// foo(): void;
+//// }
+//// const obj: T = {
+//// foo() {
+//
+//// }
+//// /**/
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "aaa",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "aaa",
+ description: `Add missing comma for object member completion 'aaa'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface T {
+ aaa?: string;
+ foo(): void;
+ }
+ const obj: T = {
+ foo() {
+ },
+
+ }`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+ });
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions4.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions4.ts
new file mode 100644
index 0000000000000..3ef13a46e4b1d
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions4.ts
@@ -0,0 +1,42 @@
+///
+////interface T {
+//// aaa: number;
+//// bbb?: number;
+//// }
+//// const obj: T = {
+//// aaa: 1 * (2 + 3)
+//// /**/
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "bbb",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "bbb",
+ description: `Add missing comma for object member completion 'bbb'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface T {
+ aaa: number;
+ bbb?: number;
+ }
+ const obj: T = {
+ aaa: 1 * (2 + 3),
+
+ }`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions5.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions5.ts
new file mode 100644
index 0000000000000..9da760b90d493
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions5.ts
@@ -0,0 +1,39 @@
+///
+
+//// type E = {}
+//// type F = string
+//// interface I { e: E, f?: F }
+//// const i: I = { e: {}
+//// /**/
+//// };
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "f",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "f",
+ description: `Add missing comma for object member completion 'f'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `type E = {}
+type F = string
+interface I { e: E, f?: F }
+const i: I = { e: {},
+
+};`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions6.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions6.ts
new file mode 100644
index 0000000000000..0ab643271c48e
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions6.ts
@@ -0,0 +1,45 @@
+
+
+///
+
+//// type E = {}
+//// type F = string
+//// const i= { e: {} };
+//// interface I { e: E, f?: F }
+//// const k: I = {
+//// ["e"]: i
+//// /**/
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "f",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "f",
+ description: `Add missing comma for object member completion 'f'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `type E = {}
+type F = string
+const i= { e: {} };
+interface I { e: E, f?: F }
+const k: I = {
+ ["e"]: i,
+
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions7.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions7.ts
new file mode 100644
index 0000000000000..3cd6c3c011f5a
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions7.ts
@@ -0,0 +1,129 @@
+
+
+///
+
+// @Filename: a.ts
+//// interface I {
+//// aaa: number,
+//// bbb: number,
+//// }
+////
+//// interface U {
+//// a: number,
+//// b: {
+//// c: {
+//// d: {
+//// aaa: number,
+//// }
+//// }
+////
+//// },
+//// }
+//// const num: U = {} as any;
+////
+//// const l: I = {
+//// ...num.b.c.d
+//// /*a*/
+//// }
+
+// @Filename: b.ts
+//// interface pp {
+//// aaa: string;
+//// bbb: number;
+//// }
+////
+//// const abc: pp = {
+//// aaa: "",
+//// bbb: 1,
+//// }
+////
+//// const cab: pp = {
+//// ...abc
+//// /*b*/
+//// }
+
+verify.completions({
+ marker: "a",
+ includes: [{
+ name: "bbb",
+ sortText: completion.SortText.LocationPriority,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("a", {
+ name: "bbb",
+ description: `Add missing comma for object member completion 'bbb'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface I {
+ aaa: number,
+ bbb: number,
+}
+
+interface U {
+ a: number,
+ b: {
+ c: {
+ d: {
+ aaa: number,
+ }
+ }
+
+ },
+}
+const num: U = {} as any;
+
+const l: I = {
+ ...num.b.c.d,
+
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.completions({
+ marker: "b",
+ includes: [{
+ name: "aaa",
+ sortText: completion.SortText.MemberDeclaredBySpreadAssignment,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("b", {
+ name: "aaa",
+ description: `Add missing comma for object member completion 'aaa'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface pp {
+ aaa: string;
+ bbb: number;
+}
+
+const abc: pp = {
+ aaa: "",
+ bbb: 1,
+}
+
+const cab: pp = {
+ ...abc,
+
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
\ No newline at end of file
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions8.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions8.ts
new file mode 100644
index 0000000000000..5f2681dbb83c5
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions8.ts
@@ -0,0 +1,45 @@
+///
+
+//// interface A {
+//// b: string,
+//// c?: number,
+//// }
+//// const b = ""
+//// const a: A = {
+//// b
+//// /**/
+//// }
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "c",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "c",
+ description: `Add missing comma for object member completion 'c'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface A {
+ b: string,
+ c?: number,
+}
+const b = ""
+const a: A = {
+ b,
+
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
diff --git a/tests/cases/fourslash/completionsObjectLiteralExpressions9.ts b/tests/cases/fourslash/completionsObjectLiteralExpressions9.ts
new file mode 100644
index 0000000000000..fcf9537c61fd0
--- /dev/null
+++ b/tests/cases/fourslash/completionsObjectLiteralExpressions9.ts
@@ -0,0 +1,139 @@
+///
+// @Filename: a.ts
+////interface T {
+//// aaa: number;
+//// bbb?: number;
+//// ccc?: number;
+//// }
+//// const obj: T = {
+//// aaa: 1 * (2 + 3)
+//// c/*a*/
+//// }
+
+// @Filename: b.ts
+////interface T {
+//// aaa?: string;
+//// foo(): void;
+//// }
+//// const obj: T = {
+//// foo() {
+//
+//// }
+//// aa/*b*/
+//// }
+
+// @Filename: c.ts
+//// interface ColorPalette {
+//// primary?: string;
+//// secondary?: string;
+//// }
+//// interface I {
+//// color: ColorPalette;
+//// }
+//// const a: I = {
+//// color: {primary: "red" sec/**/}
+//// }
+
+verify.completions({
+ marker: "a",
+ includes: [{
+ name: "bbb",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("a", {
+ name: "bbb",
+ description: `Add missing comma for object member completion 'bbb'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface T {
+ aaa: number;
+ bbb?: number;
+ ccc?: number;
+ }
+ const obj: T = {
+ aaa: 1 * (2 + 3),
+ c
+ }`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.completions({
+ marker: "b",
+ includes: [{
+ name: "aaa",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.applyCodeActionFromCompletion("b", {
+ name: "aaa",
+ description: `Add missing comma for object member completion 'aaa'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface T {
+ aaa?: string;
+ foo(): void;
+ }
+ const obj: T = {
+ foo() {
+ },
+ aa
+ }`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
+verify.completions({
+ marker: "",
+ includes: [{
+ name: "secondary",
+ sortText: completion.SortText.OptionalMember,
+ hasAction: true,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ }],
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ }
+});
+
+verify.applyCodeActionFromCompletion("", {
+ name: "secondary",
+ description: `Add missing comma for object member completion 'secondary'.`,
+ source: completion.CompletionSource.ObjectLiteralMemberWithComma,
+ newFileContent:
+ `interface ColorPalette {
+ primary?: string;
+ secondary?: string;
+}
+interface I {
+ color: ColorPalette;
+}
+const a: I = {
+ color: {primary: "red", sec}
+}`,
+ preferences: {
+ allowIncompleteCompletions: true,
+ includeInsertTextCompletions: true,
+ },
+});
+
\ No newline at end of file
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 82321aef548a9..4121114c72be6 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -914,6 +914,7 @@ declare namespace completion {
TypeOnlyAlias = "TypeOnlyAlias/",
ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/",
SwitchCases = "SwitchCases/",
+ ObjectLiteralMemberWithComma = "ObjectLiteralMemberWithComma/",
}
export const globalThisEntry: Entry;
export const undefinedVarEntry: Entry;