add more tests and fix some details

This commit is contained in:
Gabriela Araujo Britto
2021-09-21 18:48:36 -07:00
parent c608a6eee9
commit 2fa43e8531
2 changed files with 133 additions and 13 deletions
+27 -9
View File
@@ -678,7 +678,7 @@ namespace ts.Completions {
}
}
if (isMethodOverrideCompletion(symbol, location)) {
if (isClassLikeMemberCompletion(symbol, location)) {
({ insertText, isSnippet } = getEntryForMemberCompletion(host, program, options, preferences, name, symbol, location));
kindModifiers = SymbolDisplay.getSymbolModifiers(typeChecker, symbol); // >> TODO: remove `abstract` modifier from symbol?
}
@@ -720,11 +720,15 @@ namespace ts.Completions {
}
// >> TODO: Find better location for code
// >> TODO: update this to `isMemberCompletion`... ?
function isMethodOverrideCompletion(symbol: Symbol, location: Node): boolean {
return !!(symbol.flags & SymbolFlags.Method) && isPropertyDeclaration(location.parent);
// >> TODO: add more checks. e.g. the method suggestion could come from an interface the class implements,
// or some other possibilities?
function isClassLikeMemberCompletion(symbol: Symbol, location: Node): boolean {
const memberFlags =
// SymbolFlags.Method
// | SymbolFlags.Accessor
// | SymbolFlags.Property
SymbolFlags.ClassMember
& SymbolFlags.EnumMemberExcludes;
// >> TODO: Flags: Constructor? Signature?
return !!findAncestor(location, isClassLike) && !!(symbol.flags & memberFlags);
}
function getEntryForMemberCompletion(
@@ -753,11 +757,14 @@ namespace ts.Completions {
let body;
if (preferences.includeCompletionsWithSnippetText) {
isSnippet = true;
body = factory.createBlock([], /* multiline */ true); // TODO: add tabstop
const tabStopStatement = factory.createExpressionStatement(factory.createIdentifier("$1"));
body = factory.createBlock([tabStopStatement], /* multiline */ true);
}
else {
body = factory.createBlock([], /* multiline */ true);
}
const completionNodes: Node[] = [];
codefix.addNewNodeForMemberSymbol(
symbol,
classLikeDeclaration,
@@ -766,15 +773,23 @@ namespace ts.Completions {
preferences,
importAdder,
node => {
// `addNewNodeForMemberSymbol` calls this callback function for each new member node
// it adds for the given member symbol.
// We store these member nodes in the `completionNodes` array.
// Note that there might be:
// - No nodes if `addNewNodeForMemberSymbol` cannot figure out a node for the member;
// - One node;
// - More than one node if the member is overloaded (e.g. a method with overload signatures).
if (isClassDeclaration(classLikeDeclaration) && hasAbstractModifier(classLikeDeclaration)) {
// Add `abstract` modifier
node = factory.updateModifiers(
node,
concatenate([factory.createModifier(SyntaxKind.AbstractKeyword)], node.modifiers),
);
// >> TODO: we want to remove the body in more cases I think
// >> e.g. interfaces?
if (isMethodDeclaration(node)) {
// Remove method body
// >> TODO: maybe move this up, when creating the body above?
node = factory.updateMethodDeclaration(
node,
node.decorators,
@@ -789,10 +804,13 @@ namespace ts.Completions {
);
}
}
insertText = printer.printNode(EmitHint.Unspecified, node, sourceFile);
completionNodes.push(node);
},
body);
if (completionNodes.length) {
insertText = printer.printList(ListFormat.MultiLine, factory.createNodeArray(completionNodes), sourceFile);
}
return { insertText, isSnippet };
}
@@ -50,6 +50,37 @@
//// f/*d*/
////}
// @Filename: e.ts
// Case: Class implements interface
////interface EBase {
//// foo(a: string): string;
////}
////
////class ESub implements EBase {
//// f/*e*/
////}
// @Filename: f.ts
// Case: Abstract class implements interface
////interface FBase {
//// foo(a: string): string;
////}
////
////abstract class FSub implements FBase {
//// f/*f*/
////}
// @Filename: g.ts
// Case: Method has overloads
////interface GBase {
//// foo(a: string): string;
//// foo(a: undefined, b: number): string;
////}
////
////class GSub implements GBase {
//// f/*g*/
////}
// format.setFormatOptions({
// newLineCharacter: "\n",
// });
@@ -73,7 +104,7 @@ verify.completions({
},
isSnippet: true,
insertText:
"foo(param1: string, param2: boolean): Promise<void> {\r\n}",
"foo(param1: string, param2: boolean): Promise<void> {\r\n $1;\r\n}\r\n",
}
],
});
@@ -96,7 +127,7 @@ verify.completions({
},
isSnippet: true,
insertText:
"foo(a: string, b: string): string {\r\n}",
"foo(a: string, b: string): string {\r\n $1;\r\n}\r\n",
}
],
});
@@ -119,7 +150,7 @@ verify.completions({
},
isSnippet: true,
insertText:
"foo(a: string): string {\r\n}",
"foo(a: string): string {\r\n $1;\r\n}\r\n",
}
],
});
@@ -142,7 +173,78 @@ verify.completions({
},
isSnippet: true,
insertText:
"abstract foo(a: string): string;", // Currently fails because no trailing semicolon
"abstract foo(a: string): string;\r\n",
}
],
});
verify.completions({
marker: "e",
isNewIdentifierLocation: true,
preferences: {
includeCompletionsWithInsertText: true,
includeCompletionsWithSnippetText: true,
},
includes: [
{
name: "foo",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
isSnippet: true,
insertText:
"foo(a: string): string {\r\n $1;\r\n}\r\n",
}
],
});
verify.completions({
marker: "f",
isNewIdentifierLocation: true,
preferences: {
includeCompletionsWithInsertText: true,
includeCompletionsWithSnippetText: true,
},
includes: [
{
name: "foo",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
isSnippet: true,
insertText:
"abstract foo(a: string): string;\r\n",
}
],
});
verify.completions({
marker: "g",
isNewIdentifierLocation: true,
preferences: {
includeCompletionsWithInsertText: true,
includeCompletionsWithSnippetText: true,
},
includes: [
{
name: "foo",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
isSnippet: true,
insertText:
"foo(a: string): string;\r\n\
foo(a: undefined, b: number): string;\r\n\
foo(a: any, b?: any): string {\r\n $1;\r\n}\r\n",
}
],
});