From bbf90d84769379b010d8f21af7a5921d230d3c28 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 12 Jan 2023 22:37:44 +0200 Subject: [PATCH 1/4] fix(52212): show the completion of class members when the class property initializer doesn't have a semicolon --- src/services/completions.ts | 8 +- .../completionsClassMembers.baseline | 230 ++++++++++++++++++ .../fourslash/completionsClassMembers.ts | 13 + 3 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/completionsClassMembers.baseline create mode 100644 tests/cases/fourslash/completionsClassMembers.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index a166845ba3e97..0d34bfdb28725 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -4825,7 +4825,7 @@ function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, return cls; } break; - case SyntaxKind.Identifier: { + case SyntaxKind.Identifier: { const originalKeywordKind = (location as Identifier).originalKeywordKind; if (originalKeywordKind && isKeyword(originalKeywordKind)) { return undefined; @@ -4850,6 +4850,12 @@ function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, return findAncestor(contextToken, isClassLike) as ObjectTypeDeclaration; } + // class C { prop = ""\n | } + if (contextToken && isPropertyDeclaration(contextToken.parent) && isClassLike(location) + && getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) { + return location; + } + switch (contextToken.kind) { case SyntaxKind.EqualsToken: // class c { public prop = | /* global completions */ } return undefined; diff --git a/tests/baselines/reference/completionsClassMembers.baseline b/tests/baselines/reference/completionsClassMembers.baseline new file mode 100644 index 0000000000000..e0cb8b16cccdb --- /dev/null +++ b/tests/baselines/reference/completionsClassMembers.baseline @@ -0,0 +1,230 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/completionsClassMembers.ts", + "position": 93, + "name": "" + }, + "completionList": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": true, + "isNewIdentifierLocation": true, + "entries": [ + { + "name": "method", + "kind": "method", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "I", + "kind": "interfaceName" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [] + }, + { + "name": "abstract", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "abstract", + "kind": "keyword" + } + ] + }, + { + "name": "accessor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "accessor", + "kind": "keyword" + } + ] + }, + { + "name": "async", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "async", + "kind": "keyword" + } + ] + }, + { + "name": "constructor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "constructor", + "kind": "keyword" + } + ] + }, + { + "name": "declare", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "declare", + "kind": "keyword" + } + ] + }, + { + "name": "get", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "get", + "kind": "keyword" + } + ] + }, + { + "name": "override", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "override", + "kind": "keyword" + } + ] + }, + { + "name": "private", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "private", + "kind": "keyword" + } + ] + }, + { + "name": "protected", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "protected", + "kind": "keyword" + } + ] + }, + { + "name": "public", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "public", + "kind": "keyword" + } + ] + }, + { + "name": "readonly", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "readonly", + "kind": "keyword" + } + ] + }, + { + "name": "set", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "set", + "kind": "keyword" + } + ] + }, + { + "name": "static", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "static", + "kind": "keyword" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/completionsClassMembers.ts b/tests/cases/fourslash/completionsClassMembers.ts new file mode 100644 index 0000000000000..094039a61853c --- /dev/null +++ b/tests/cases/fourslash/completionsClassMembers.ts @@ -0,0 +1,13 @@ +/// + +////interface I { +//// method(): void; +////} +//// +////export class C implements I { +//// property = "foo" +//// +//// /**/ +////} + +verify.baselineCompletions(); From b400eb95923e225be210332bf500e0c21bd23971 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Thu, 12 Jan 2023 23:10:53 +0200 Subject: [PATCH 2/4] add tests --- ...line => completionsClassMembers1.baseline} | 2 +- .../completionsClassMembers2.baseline | 235 ++++++++++++++++++ ...Members.ts => completionsClassMembers1.ts} | 0 .../fourslash/completionsClassMembers2.ts | 16 ++ 4 files changed, 252 insertions(+), 1 deletion(-) rename tests/baselines/reference/{completionsClassMembers.baseline => completionsClassMembers1.baseline} (99%) create mode 100644 tests/baselines/reference/completionsClassMembers2.baseline rename tests/cases/fourslash/{completionsClassMembers.ts => completionsClassMembers1.ts} (100%) create mode 100644 tests/cases/fourslash/completionsClassMembers2.ts diff --git a/tests/baselines/reference/completionsClassMembers.baseline b/tests/baselines/reference/completionsClassMembers1.baseline similarity index 99% rename from tests/baselines/reference/completionsClassMembers.baseline rename to tests/baselines/reference/completionsClassMembers1.baseline index e0cb8b16cccdb..e08c00fcc4481 100644 --- a/tests/baselines/reference/completionsClassMembers.baseline +++ b/tests/baselines/reference/completionsClassMembers1.baseline @@ -1,7 +1,7 @@ [ { "marker": { - "fileName": "/tests/cases/fourslash/completionsClassMembers.ts", + "fileName": "/tests/cases/fourslash/completionsClassMembers1.ts", "position": 93, "name": "" }, diff --git a/tests/baselines/reference/completionsClassMembers2.baseline b/tests/baselines/reference/completionsClassMembers2.baseline new file mode 100644 index 0000000000000..eb68eaf45a2ea --- /dev/null +++ b/tests/baselines/reference/completionsClassMembers2.baseline @@ -0,0 +1,235 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/completionsClassMembers2.ts", + "position": 93, + "name": "" + }, + "completionList": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": true, + "isNewIdentifierLocation": true, + "entries": [ + { + "name": "abstract", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "abstract", + "kind": "keyword" + } + ] + }, + { + "name": "accessor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "accessor", + "kind": "keyword" + } + ] + }, + { + "name": "async", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "async", + "kind": "keyword" + } + ] + }, + { + "name": "constructor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "constructor", + "kind": "keyword" + } + ] + }, + { + "name": "declare", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "declare", + "kind": "keyword" + } + ] + }, + { + "name": "get", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "get", + "kind": "keyword" + } + ] + }, + { + "name": "override", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "override", + "kind": "keyword" + } + ] + }, + { + "name": "private", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "private", + "kind": "keyword" + } + ] + }, + { + "name": "protected", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "protected", + "kind": "keyword" + } + ] + }, + { + "name": "public", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "public", + "kind": "keyword" + } + ] + }, + { + "name": "readonly", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "readonly", + "kind": "keyword" + } + ] + }, + { + "name": "set", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "set", + "kind": "keyword" + } + ] + }, + { + "name": "static", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "static", + "kind": "keyword" + } + ] + }, + { + "name": "method", + "kind": "method", + "kindModifiers": "", + "sortText": "17", + "insertText": "method(): void {\r\n}", + "replacementSpan": { + "start": 71, + "length": 16 + }, + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "I", + "kind": "interfaceName" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/completionsClassMembers.ts b/tests/cases/fourslash/completionsClassMembers1.ts similarity index 100% rename from tests/cases/fourslash/completionsClassMembers.ts rename to tests/cases/fourslash/completionsClassMembers1.ts diff --git a/tests/cases/fourslash/completionsClassMembers2.ts b/tests/cases/fourslash/completionsClassMembers2.ts new file mode 100644 index 0000000000000..039334e14c735 --- /dev/null +++ b/tests/cases/fourslash/completionsClassMembers2.ts @@ -0,0 +1,16 @@ +/// + +////interface I { +//// method(): void; +////} +//// +////export class C implements I { +//// property = "foo" +//// +//// /**/ +////} + +verify.baselineCompletions({ + includeCompletionsWithInsertText: true, + includeCompletionsWithClassMemberSnippets: true, +}); From 1660f14f8f55be2053c4f0f528dc71f8c482748a Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 13 Jan 2023 00:26:00 +0200 Subject: [PATCH 3/4] add tests. combine duplicate conditions --- src/services/completions.ts | 21 +- .../completionsClassMembers3.baseline | 230 ++++++++++++++++++ .../fourslash/completionsClassMembers3.ts | 13 + 3 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/completionsClassMembers3.baseline create mode 100644 tests/cases/fourslash/completionsClassMembers3.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 0d34bfdb28725..ebe7c36e9dd73 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -4850,12 +4850,6 @@ function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, return findAncestor(contextToken, isClassLike) as ObjectTypeDeclaration; } - // class C { prop = ""\n | } - if (contextToken && isPropertyDeclaration(contextToken.parent) && isClassLike(location) - && getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) { - return location; - } - switch (contextToken.kind) { case SyntaxKind.EqualsToken: // class c { public prop = | /* global completions */ } return undefined; @@ -4870,16 +4864,17 @@ function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, case SyntaxKind.CommaToken: // class c {getValue(): number, | } return tryCast(contextToken.parent, isObjectTypeDeclaration); default: - if (!isFromObjectTypeDeclaration(contextToken)) { - // class c extends React.Component { a: () => 1\n| } - if (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line && isObjectTypeDeclaration(location)) { + if (isObjectTypeDeclaration(location)) { + // class C extends React.Component { a: () => 1\n| } + // class C { prop = ""\n | } + if (isClassLike(location) && getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) { return location; } - return undefined; + const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword; + return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217 + ? contextToken.parent.parent as ObjectTypeDeclaration : undefined; } - const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword; - return (isValidKeyword(contextToken.kind) || contextToken.kind === SyntaxKind.AsteriskToken || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)!)) // TODO: GH#18217 - ? contextToken.parent.parent as ObjectTypeDeclaration : undefined; + return undefined; } } diff --git a/tests/baselines/reference/completionsClassMembers3.baseline b/tests/baselines/reference/completionsClassMembers3.baseline new file mode 100644 index 0000000000000..4d83c1f170378 --- /dev/null +++ b/tests/baselines/reference/completionsClassMembers3.baseline @@ -0,0 +1,230 @@ +[ + { + "marker": { + "fileName": "/tests/cases/fourslash/completionsClassMembers3.ts", + "position": 101, + "name": "" + }, + "completionList": { + "flags": 0, + "isGlobalCompletion": false, + "isMemberCompletion": true, + "isNewIdentifierLocation": true, + "entries": [ + { + "name": "method", + "kind": "method", + "kindModifiers": "", + "sortText": "11", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "I", + "kind": "interfaceName" + }, + { + "text": ".", + "kind": "punctuation" + }, + { + "text": "method", + "kind": "methodName" + }, + { + "text": "(", + "kind": "punctuation" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "void", + "kind": "keyword" + } + ], + "documentation": [] + }, + { + "name": "abstract", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "abstract", + "kind": "keyword" + } + ] + }, + { + "name": "accessor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "accessor", + "kind": "keyword" + } + ] + }, + { + "name": "async", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "async", + "kind": "keyword" + } + ] + }, + { + "name": "constructor", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "constructor", + "kind": "keyword" + } + ] + }, + { + "name": "declare", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "declare", + "kind": "keyword" + } + ] + }, + { + "name": "get", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "get", + "kind": "keyword" + } + ] + }, + { + "name": "override", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "override", + "kind": "keyword" + } + ] + }, + { + "name": "private", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "private", + "kind": "keyword" + } + ] + }, + { + "name": "protected", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "protected", + "kind": "keyword" + } + ] + }, + { + "name": "public", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "public", + "kind": "keyword" + } + ] + }, + { + "name": "readonly", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "readonly", + "kind": "keyword" + } + ] + }, + { + "name": "set", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "set", + "kind": "keyword" + } + ] + }, + { + "name": "static", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15", + "displayParts": [ + { + "text": "static", + "kind": "keyword" + } + ] + } + ] + } + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/completionsClassMembers3.ts b/tests/cases/fourslash/completionsClassMembers3.ts new file mode 100644 index 0000000000000..f648ef0ab433d --- /dev/null +++ b/tests/cases/fourslash/completionsClassMembers3.ts @@ -0,0 +1,13 @@ +/// + +////interface I { +//// method(): void; +////} +//// +////export class C implements I { +//// property = "foo" + "foo" +//// +//// /**/ +////} + +verify.baselineCompletions(); From 98e179d914bff237fb98a7eae1a2bf6ebd523c82 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 13 Jan 2023 20:48:45 +0200 Subject: [PATCH 4/4] remove useless check --- src/services/completions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 932d6b71c7907..02427425fdf1f 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -4871,7 +4871,7 @@ function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, if (isObjectTypeDeclaration(location)) { // class C extends React.Component { a: () => 1\n| } // class C { prop = ""\n | } - if (isClassLike(location) && getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) { + if (getLineAndCharacterOfPosition(sourceFile, contextToken.getEnd()).line !== getLineAndCharacterOfPosition(sourceFile, position).line) { return location; } const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;