diff --git a/src/services/completions.ts b/src/services/completions.ts index ac1eb8f6a4622..6435e976761a6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -18,6 +18,7 @@ namespace ts.Completions { None, ClassElementKeywords, // Keywords at class keyword ConstructorParameterKeywords, // Keywords at constructor parameter + FunctionLikeBodyKeywords // Keywords at function like body } export function getCompletionsAtPosition( @@ -1032,6 +1033,10 @@ namespace ts.Completions { return true; } + if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { + keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords; + } + if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { // cursor inside class declaration getGetClassLikeCompletionSymbols(classLikeContainer); @@ -1648,6 +1653,22 @@ namespace ts.Completions { return undefined; } + function tryGetFunctionLikeBodyCompletionContainer(contextToken: Node): FunctionLikeDeclaration { + if (contextToken) { + let prev: Node; + const container = findAncestor(contextToken.parent, (node: Node) => { + if (isClassLike(node)) { + return "quit"; + } + if (isFunctionLikeDeclaration(node) && prev === node.body) { + return true; + } + prev = node; + }); + return container && container as FunctionLikeDeclaration; + } + } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { if (contextToken) { const parent = contextToken.parent; @@ -2086,6 +2107,8 @@ namespace ts.Completions { return getFilteredKeywordCompletions(isClassMemberCompletionKeywordText); case KeywordCompletionFilters.ConstructorParameterKeywords: return getFilteredKeywordCompletions(isConstructorParameterCompletionKeywordText); + case KeywordCompletionFilters.FunctionLikeBodyKeywords: + return getFilteredKeywordCompletions(isFunctionLikeBodyCompletionKeywordText); default: Debug.assertNever(keywordFilter); } @@ -2148,6 +2171,26 @@ namespace ts.Completions { return isConstructorParameterCompletionKeyword(stringToToken(text)); } + function isFunctionLikeBodyCompletionKeyword(kind: SyntaxKind) { + switch (kind) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.ConstructorKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + return false; + } + return true; + } + + function isFunctionLikeBodyCompletionKeywordText(text: string) { + return isFunctionLikeBodyCompletionKeyword(stringToToken(text)); + } + function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { switch (kind) { case ts.SyntaxKind.EqualsEqualsEqualsToken: diff --git a/tests/cases/fourslash/completionInFunctionLikeBody.ts b/tests/cases/fourslash/completionInFunctionLikeBody.ts new file mode 100644 index 0000000000000..a4c5e92dde970 --- /dev/null +++ b/tests/cases/fourslash/completionInFunctionLikeBody.ts @@ -0,0 +1,43 @@ +/// + +//// class Foo { +//// bar () { +//// /*1*/ +//// class Foo1 { +//// bar1 () { +//// /*2*/ +//// } +//// /*3*/ +//// } +//// } +//// /*4*/ +//// } + + +goTo.marker("1"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("2"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("3"); +verify.completionListContainsClassElementKeywords(); + +goTo.marker("4"); +verify.completionListContainsClassElementKeywords(); diff --git a/tests/cases/fourslash/completionInJSDocFunctionNew.ts b/tests/cases/fourslash/completionInJSDocFunctionNew.ts index 6a06ec76fe5b9..1266eb5fad3f5 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionNew.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionNew.ts @@ -6,5 +6,5 @@ ////var f = function () { return new/**/; } goTo.marker(); -verify.completionListCount(116); +verify.completionListCount(107); verify.completionListContains('new'); diff --git a/tests/cases/fourslash/completionInJSDocFunctionThis.ts b/tests/cases/fourslash/completionInJSDocFunctionThis.ts index 0fd771f272b46..57cf08c9ff363 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionThis.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionThis.ts @@ -5,5 +5,5 @@ ////var f = function (s) { return this/**/; } goTo.marker(); -verify.completionListCount(117); +verify.completionListCount(108); verify.completionListContains('this')