mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
add more tests and fix some details
This commit is contained in:
@@ -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",
|
||||
}
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user