mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 02:33:53 -06:00
Complete constructor keyword after property declaration (#43654)
* Complete `constructor` keyword after property declaration. * Fix logical errors. * Fix for more universal situations. * Only provide completions if property declaration is terminated. * Simplify many logical conditions. * Make the fix more reliable. * Narrowing the fix.
This commit is contained in:
parent
3e25424652
commit
4ecb563aa4
@ -2360,7 +2360,7 @@ namespace ts.Completions {
|
||||
return isFunctionLike(contextToken.parent) && !isMethodDeclaration(contextToken.parent);
|
||||
}
|
||||
|
||||
// If the previous token is keyword correspoding to class member completion keyword
|
||||
// If the previous token is keyword corresponding to class member completion keyword
|
||||
// there will be completion available here
|
||||
if (isClassMemberCompletionKeyword(keywordForNode(contextToken)) && isFromObjectTypeDeclaration(contextToken)) {
|
||||
return false;
|
||||
@ -2398,6 +2398,33 @@ namespace ts.Completions {
|
||||
return isPropertyDeclaration(contextToken.parent);
|
||||
}
|
||||
|
||||
// If we are inside a class declaration, and `constructor` is totally not present,
|
||||
// but we request a completion manually at a whitespace...
|
||||
const ancestorClassLike = findAncestor(contextToken.parent, isClassLike);
|
||||
if (ancestorClassLike && contextToken === previousToken && isPreviousPropertyDeclarationTerminated(contextToken, position)) {
|
||||
return false; // Don't block completions.
|
||||
}
|
||||
|
||||
const ancestorPropertyDeclaraion = getAncestor(contextToken.parent, SyntaxKind.PropertyDeclaration);
|
||||
// If we are inside a class declaration and typing `constructor` after property declaration...
|
||||
if (ancestorPropertyDeclaraion
|
||||
&& contextToken !== previousToken
|
||||
&& isClassLike(previousToken.parent.parent)
|
||||
// And the cursor is at the token...
|
||||
&& position <= previousToken.end) {
|
||||
// If we are sure that the previous property declaration is terminated according to newline or semicolon...
|
||||
if (isPreviousPropertyDeclarationTerminated(contextToken, previousToken.end)) {
|
||||
return false; // Don't block completions.
|
||||
}
|
||||
else if (contextToken.kind !== SyntaxKind.EqualsToken
|
||||
// Should not block: `class C { blah = c/**/ }`
|
||||
// But should block: `class C { blah = somewhat c/**/ }` and `class C { blah: SomeType c/**/ }`
|
||||
&& (isInitializedProperty(ancestorPropertyDeclaraion as PropertyDeclaration)
|
||||
|| hasType(ancestorPropertyDeclaraion))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return isDeclarationName(contextToken)
|
||||
&& !isShorthandPropertyAssignment(contextToken.parent)
|
||||
&& !isJsxAttribute(contextToken.parent)
|
||||
@ -2406,6 +2433,12 @@ namespace ts.Completions {
|
||||
&& !(isClassLike(contextToken.parent) && (contextToken !== previousToken || position > previousToken.end));
|
||||
}
|
||||
|
||||
function isPreviousPropertyDeclarationTerminated(contextToken: Node, position: number) {
|
||||
return contextToken.kind !== SyntaxKind.EqualsToken &&
|
||||
(contextToken.kind === SyntaxKind.SemicolonToken
|
||||
|| !positionsAreOnSameLine(contextToken.end, position, sourceFile));
|
||||
}
|
||||
|
||||
function isFunctionLikeButNotConstructor(kind: SyntaxKind) {
|
||||
return isFunctionLikeKind(kind) && kind !== SyntaxKind.Constructor;
|
||||
}
|
||||
@ -2859,6 +2892,13 @@ namespace ts.Completions {
|
||||
|
||||
if (!contextToken) return undefined;
|
||||
|
||||
// class C { blah; constructor/**/ } and so on
|
||||
if (location.kind === SyntaxKind.ConstructorKeyword
|
||||
// class C { blah \n constructor/**/ }
|
||||
|| (isIdentifier(contextToken) && isPropertyDeclaration(contextToken.parent) && isClassLike(location))) {
|
||||
return findAncestor(contextToken, isClassLike) as ObjectTypeDeclaration;
|
||||
}
|
||||
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.EqualsToken: // class c { public prop = | /* global completions */ }
|
||||
return undefined;
|
||||
|
||||
@ -0,0 +1,98 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
//// // situations that `constructor` is partly present
|
||||
//// class A {
|
||||
//// blah; con/*1*/
|
||||
//// }
|
||||
//// class B {
|
||||
//// blah
|
||||
//// con/*2*/
|
||||
//// }
|
||||
//// class C {
|
||||
//// blah: number
|
||||
//// con/*3*/
|
||||
//// }
|
||||
//// class D {
|
||||
//// blah = 123
|
||||
//// con/*4*/
|
||||
//// }
|
||||
//// class E {
|
||||
//// blah = [123]
|
||||
//// con/*5*/
|
||||
//// }
|
||||
//// class F {
|
||||
//// blah = {key: 123}
|
||||
//// con/*6*/
|
||||
//// }
|
||||
//// // situations that `constructor` is fully present
|
||||
//// class G {
|
||||
//// blah; constructor/*7*/
|
||||
//// }
|
||||
//// class H {
|
||||
//// blah
|
||||
//// constructor/*8*/
|
||||
//// }
|
||||
//// class I {
|
||||
//// blah: number
|
||||
//// constructor/*9*/
|
||||
//// }
|
||||
//// class J {
|
||||
//// blah = 123
|
||||
//// constructor/*10*/
|
||||
//// }
|
||||
//// class K {
|
||||
//// blah = [123]
|
||||
//// constructor/*11*/
|
||||
//// }
|
||||
//// class L {
|
||||
//// blah = {key: 123}
|
||||
//// constructor/*12*/
|
||||
//// }
|
||||
//// // situations that `constructor` isn't present, but we should offer it
|
||||
//// class M {
|
||||
//// blah; /*13*/
|
||||
//// }
|
||||
//// class N {
|
||||
//// blah
|
||||
//// /*14*/
|
||||
//// }
|
||||
//// // situations that `constructor` should not be suggested
|
||||
//// class O {
|
||||
//// blah /*15*/
|
||||
//// }
|
||||
//// class P {
|
||||
//// blah con/*16*/
|
||||
//// }
|
||||
//// class Q {
|
||||
//// blah: number con/*17*/
|
||||
//// }
|
||||
//// class R {
|
||||
//// blah = 123 con/*18*/
|
||||
//// }
|
||||
//// class S {
|
||||
//// blah = {key: 123} con/*19*/
|
||||
//// }
|
||||
//// type SomeType = number
|
||||
//// class T {
|
||||
//// blah: SomeType con/*20*/
|
||||
//// }
|
||||
//// const SomeValue = 123
|
||||
//// class U {
|
||||
//// blah = SomeValue con/*21*/
|
||||
//// }
|
||||
|
||||
function generateRange(l: number, r: number) {
|
||||
return Array.from(Array(r - l + 1), (_, i) => String(i + l)); // [l, r]
|
||||
}
|
||||
|
||||
verify.completions({
|
||||
marker: generateRange(1, 14),
|
||||
includes: { name: "constructor", sortText: completion.SortText.GlobalsOrKeywords },
|
||||
isNewIdentifierLocation: true,
|
||||
});
|
||||
|
||||
verify.completions({
|
||||
marker: generateRange(15, 21),
|
||||
exact: [],
|
||||
isNewIdentifierLocation: true,
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user