prototype creation for method override completion snippet

This commit is contained in:
Gabriela Araujo Britto
2021-09-09 21:00:38 -07:00
parent 3c8e45b304
commit 701c5f3687
3 changed files with 99 additions and 3 deletions

View File

@@ -2289,7 +2289,7 @@ namespace ts.server.protocol {
/**
* Human-readable description of the `source`.
*/
sourceDisplay?: SymbolDisplayPart[];
sourceDisplay?: SymbolDisplayPart[];
/**
* If true, this completion should be highlighted as recommended. There will only be one of these.
* This will be set when we know the user should write an expression with a certain type and that type is an enum or constructable class.

View File

@@ -662,10 +662,18 @@ namespace ts.Completions {
sourceDisplay = [textPart(origin.moduleSpecifier)];
if (importCompletionNode) {
({ insertText, replacementSpan } = getInsertTextAndReplacementSpanForImportCompletion(name, importCompletionNode, origin, useSemicolons, options, preferences));
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined; // >> Using snippet here.
}
}
if (isMethodOverrideCompletion(symbol, location)) {
({ insertText } = getInsertTextForMethodOverrideCompletion(typeChecker, options, name, symbol, location));
// replacementSpan = undefined; // >> TODO
isSnippet = preferences.includeCompletionsWithSnippetText ? true : undefined;
hasAction = true;
}
// >> Should this actually be here and not at the very end of the function?
if (insertText !== undefined && !preferences.includeCompletionsWithInsertText) {
return undefined;
}
@@ -685,7 +693,7 @@ namespace ts.Completions {
// entries (like JavaScript identifier entries).
return {
name,
kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location), // TODO: GH#18217
kind: SymbolDisplay.getSymbolKind(typeChecker, symbol, location),
kindModifiers: SymbolDisplay.getSymbolModifiers(typeChecker, symbol),
sortText,
source: getSourceFromOrigin(origin),
@@ -701,6 +709,58 @@ namespace ts.Completions {
};
}
// >> TODO: Find better location for code
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 getInsertTextForMethodOverrideCompletion(_typeChecker: TypeChecker, options: CompilerOptions, name: string, symbol: Symbol, location: Node) {
// const methodType = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
// const signatures = methodType.getCallSignatures();
const methodDeclarations = symbol.declarations?.filter(isMethodDeclaration);
// >> TODO: what should we do if we have more than 1 signature? when could that happen?
if (methodDeclarations?.length === 1) {
const originalDeclaration = methodDeclarations[0];
const modifiers = originalDeclaration.modifiers?.filter(modifier => {
switch (modifier.kind) { // >> Simplify this if we only need to filter out "abstract" modifier.
case SyntaxKind.AbstractKeyword:
return false;
default:
return true;
}
});
if (options.noImplicitOverride) {
modifiers?.push(factory.createModifier(SyntaxKind.OverrideKeyword));
// Assuming it's ok if this modifier is duplicated.
}
const completionDeclaration = factory.createMethodDeclaration(
/*decorators*/ undefined, // I'm guessing we don't want to deal with decorators?
/*modifiers*/ modifiers,
/*asteriskToken*/ originalDeclaration.asteriskToken,
/*name*/ name,
/*questionToken*/ originalDeclaration.questionToken,
/*typeParameters*/ originalDeclaration.typeParameters,
/*parameters*/ originalDeclaration.parameters,
/*type*/ originalDeclaration.type,
/*body*/ factory.createBlock([], /*multiLine*/ true));
// const insertText = completionDeclaration.getText(location.getSourceFile());
// const insertText = completionDeclaration.getText(); // Doesn't work with synthetic nodes
const printer = createPrinter({
removeComments: true,
module: options.module,
target: options.target,
});
const insertText = printer.printNode(EmitHint.Unspecified, completionDeclaration, location.getSourceFile());
return {
insertText,
};
}
return { };
}
function originToCompletionEntryData(origin: SymbolOriginInfoExport): CompletionEntryData | undefined {
return {
exportName: origin.exportName,

View File

@@ -0,0 +1,36 @@
/// <reference path="fourslash.ts" />
////abstract class Base {
//// abstract foo(param1: string, param2: boolean): Promise<void>;
////}
////
////class Sub extends Base {
//// f/*a*/
////}
verify.completions({
marker: "a",
isNewIdentifierLocation: true,
preferences: {
includeCompletionsWithInsertText: true,
},
// exact: [
// ],
includes: [
{
name: "foo",
isRecommended: true,
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
insertText:
`foo(param1: string, param2: boolean): Promise<void> {
}`,
}
],
});