8000 Enable method signature completion for object literals by gabritto · Pull Request #48168 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

Enable method signature completion for object literals #48168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
41913b7
skeleton of new feature
gabritto Feb 23, 2022
d5038ce
working prototype
gabritto Feb 25, 2022
fca74c1
refactor print and format code into its own function
gabritto Feb 25, 2022
249c3ba
minor changes; don't support overloads
gabritto Mar 1, 2022
ef15f81
have two completion entries
gabritto Mar 2, 2022
1f26a52
get rid of accessor support
gabritto Mar 4, 2022
1c28090
add snippet support
gabritto Mar 4, 2022
e628590
add formatting
gabritto Mar 4, 2022
515fc73
add trailing comma
gabritto Mar 4, 2022
0229a7d 8000
add sourcedisplay
gabritto Mar 5, 2022
9892dc8
support auto-imports via completion details
gabritto Mar 5, 2022
8550bba
add user preference option and fix ordering of entries
gabritto Mar 7, 2022
78c7d9e
cleanup
gabritto Mar 7, 2022
1f67d97
don't return code actions for no import fixes
gabritto Mar 8, 2022
6c96290
Merge branch 'main' into gabritto/issue46590
gabritto Mar 8, 2022
814561f
make sortText lower priority for snippets
gabritto Mar 9, 2022
088904e
get rid of flag
gabritto Mar 9, 2022
4e63276
use optional member sort text
gabritto Mar 9, 2022
474d9a9
update baselines
gabritto Mar 9, 2022
b5d05c0
don't collect method symbols if insert text is not supported
gabritto Mar 9, 2022
6914576
remove comment
gabritto Mar 16, 2022
0e0ae05
return undefined if type is not function type
gabritto Mar 17, 2022
36eac73
only slice if needed
gabritto Mar 17, 2022
b9cb703
use union reduction; more test cases
gabritto Mar 17, 2022
7c9cbdb
WIP: modify sort text system
gabritto Mar 21, 2022
07c4fcf
Improve new sort text system
gabritto Mar 22, 2022
5b0d86d
add signature and union type check
gabritto Mar 22, 2022
b032aa5
Merge branch 'main' into gabritto/issue46590
gabritto Mar 23, 2022
08a55c3
re-add flag
gabritto Mar 23, 2022
6ebe361
fix tests
gabritto Mar 23, 2022
411bc18
rename sort text helper
gabritto Mar 23, 2022
1ebdf3d
fix test and code for union case
gabritto Mar 24, 2022
5477408
add new flag to protocol type
gabritto Mar 24, 2022
af7de76
fix spaces
gabritto Mar 24, 2022
1b884c4
CR: minor fixes
gabritto Mar 29, 2022
3820f32
CR: more fixes
gabritto Mar 29, 2022
e7a51e6
CR: restructure main flow
gabritto Mar 29, 2022
203aab0
minor fix
gabritto Mar 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
have two completion entries
  • Loading branch information
gabritto committed Mar 2, 2022
commit ef15f8193eb8f09ae5aec7a1e19d6f46f002bfbc
82 changes: 55 additions & 27 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ namespace ts.Completions {
ThisProperty = "ThisProperty/",
/** Auto-import that comes attached to a class member snippet */
ClassMemberSnippet = "ClassMemberSnippet/",
/** Auto-import that comes attached to an object literal method snippet */
ObjectLiteralMethodSnippet = "ObjectLiteralMethodSnippet/",
/** A type-only import that needs to be promoted in order to be used at the completion location */
TypeOnlyAlias = "TypeOnlyAlias/",
}
Expand All @@ -72,6 +74,7 @@ namespace ts.Completions {
Nullable = 1 << 4,
ResolvedExport = 1 << 5,
TypeOnlyAlias = 1 << 6,
ObjectLiteralMember = 1 << 7,

SymbolMemberNoExport = SymbolMember,
SymbolMemberExport = SymbolMember | Export,
Expand Down Expand Up @@ -147,6 +150,10 @@ namespace ts.Completions {
return !!(origin && origin.kind & SymbolOriginInfoKind.TypeOnlyAlias);
}

function originIsObjectLiteralMember(origin: SymbolOriginInfo | undefined): boolean {
return !!(origin && origin.kind & SymbolOriginInfoKind.ObjectLiteralMember);
}

interface UniqueNameSet {
add(name: string): void;
has(name: string): boolean;
Expand Down Expand Up @@ -783,14 +790,17 @@ namespace ts.Completions {
}
}

const objLit = isObjectLiteralFunctionPropertyCompletion(symbol, location, contextToken, program.getTypeChecker());
const objLit = isObjectLiteralFunctionPropertyCompletion(symbol, origin, location, contextToken, program.getTypeChecker());
if (objLit) {
let importAdder;
({ insertText, isSnippet, importAdder } = getEntryForObjectLiteralFunctionCompletion(symbol, name, objLit, sourceFile, program, host, options, preferences, formatContext));
const entry = getEntryForObjectLiteralFunctionCompletion(symbol, name, objLit, sourceFile, program, host, options, preferences, formatContext);
if (!entry) {
return undefined;
}
({ insertText, isSnippet, importAdder } = entry);
source = CompletionSource.ObjectLiteralMethodSnippet; // >> Needed for disambiguiation from normal completions (with just the method name).
if (importAdder?.hasFixes()) {
hasAction = true;
source = CompletionSource.ClassMemberSnippet; // >> TODO: revisit & fix;
// >> TODO: change to bundling action with the completion entry instead of getting it separately, if possible
hasAction = true; // >> TODO: implement this computation in `getCompletionEntryDetails`.
}
}

Expand Down Expand Up @@ -1062,7 +1072,10 @@ namespace ts.Completions {
return undefined;
}

function isObjectLiteralFunctionPropertyCompletion(symbol: Symbol, location: Node, contextToken: Node | undefined, checker: TypeChecker): ObjectLiteralExpression | undefined {
function isObjectLiteralFunctionPropertyCompletion(symbol: Symbol, origin: SymbolOriginInfo | undefined, location: Node, contextToken: Node | undefined, checker: TypeChecker): ObjectLiteralExpression | undefined {
if (!originIsObjectLiteralMember(origin)) {
return undefined;
}
// >> TODO: check completion kind is memberlike
// TODO: support JS files.
if (isInJSFile(location)) {
Expand Down Expand Up @@ -1107,35 +1120,36 @@ namespace ts.Completions {
options: CompilerOptions,
preferences: UserPreferences,
_formatContext: formatting.FormatContext | undefined,
): { insertText: string, isSnippet?: true, importAdder: codefix.ImportAdder } {
): { insertText: string, isSnippet?: true, importAdder: codefix.ImportAdder } | undefined {
let isSnippet: true | undefined;
let insertText: string = name;

const style = ObjectLiteralFunctionPropertyStyle.ArrowFunction; // >> TODO: Get this from somewhere
const importAdder = codefix.createImportAdder(sourceFile, program, preferences, host);
const body = factory.createBlock([]);
const functionProps = createObjectLiteralFunctionProperty(symbol, enclosingDeclaration, sourceFile, program, host, preferences, importAdder, /*body*/ body, style);
if (!functionProps) {
return undefined;
}

if (functionProps) {
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
target: options.target,
omitTrailingSemicolon: false, // >> ??
newLine: getNewLineKind(getNewLineCharacter(options, maybeBind(host, host.getNewLine))),
});
const printer = createSnippetPrinter({
removeComments: true,
module: options.module,
target: options.target,
omitTrailingSemicolon: false, // >> ??
newLine: getNewLineKind(getNewLineCharacter(options, maybeBind(host, host.getNewLine))),
});

// insertText = printer.printSnippet(EmitHint.Unspecified, functionProp, sourceFile);
const format = ListFormat.CommaDelimited;
// if (formatContext) {
// // >> TODO: this is breaking get/set pairs because they're on the same line when printing
// insertText = printer.printAndFormatSnippetList(format, factory.createNodeArray(functionProps), sourceFile, formatContext);
// }
// else {
// insertText = printer.printSnippetList(format, factory.createNodeArray(functionProps), sourceFile);
// }
insertText = printer.printSnippetList(format, factory.createNodeArray(functionProps), sourceFile);
}
// insertText = printer.printSnippet(EmitHint.Unspecified, functionProp, sourceFile);
const format = ListFormat.CommaDelimited;
// if (formatContext) {
// // >> TODO: this is breaking get/set pairs because they're on the same line when printing
// insertText = printer.printAndFormatSnippetList(format, factory.createNodeArray(functionProps), sourceFile, formatContext);
// }
// else {
// insertText = printer.printSnippetList(format, factory.createNodeArray(functionProps), sourceFile);
// }
insertText = printer.printSnippetList(format, factory.createNodeArray(functionProps), sourceFile);

return { isSnippet, insertText, importAdder };
};
Expand Down Expand Up @@ -2851,6 +2865,16 @@ namespace ts.Completions {
symbols.push(symbol);
}

/* Mutates `symbols` and `symbolToOriginInfoMap`. */
function collectObjectLiteralSymbols(members: Symbol[]) {
// >> TODO: only do this if user preferences for this completion scenario is enabled
for (const member of members) {
const origin = { kind: SymbolOriginInfoKind.ObjectLiteralMember };
symbolToOriginInfoMap[symbols.length] = origin;
symbols.push(member);
}
}

/**
* Finds the first node that "embraces" the position, so that one may
* accurately aggregate locals from the closest containing scope.
Expand Down Expand Up @@ -3081,7 +3105,11 @@ namespace ts.Completions {

if (typeMembers && typeMembers.length > 0) {
// Add filtered items to the completion list
symbols = concatenate(symbols, filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers)));
const filteredMembers = filterObjectMembersList(typeMembers, Debug.checkDefined(existingMembers));
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
collectObjectLiteralSymbols(filteredMembers);
}
symbols = concatenate(symbols, filteredMembers);
}
setSortTextToOptionalMember();

Expand Down
46 changes: 20 additions & 26 deletions tests/cases/fourslash/completionsObjectLiteralFunctionProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ verify.completions({
{
name: "bar",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
source: "ObjectLiteralMethodSnippet/",
insertText: "bar: (x: number): void => { }",
},
{
name: "bar",
sortText: completion.SortText.LocationPriority,
insertText: undefined,
},
],
});
verify.completions({
Expand All @@ -62,22 +63,24 @@ verify.completions({
{
name: "bar",
sortText: completion.SortText.LocationPriority,
6D47 replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
source: "ObjectLiteralMethodSnippet/",
insertText: "bar: (x: number): void => { }",
},
{
name: "bar",
sortText: completion.SortText.LocationPriority,
insertText: undefined,
},
{
name: "foo",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
source: "ObjectLiteralMethodSnippet/",
insertText: "foo: (x: string): string => { }",
},
{
name: "foo",
sortText: completion.SortText.LocationPriority,
insertText: undefined,
}
],
});
Expand All @@ -91,13 +94,8 @@ verify.completions({
{
name: "buzz",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
insertText: "buzz",
// no declaration insert text, because this property has overloads
insertText: undefined,
},
],
});
Expand All @@ -111,11 +109,7 @@ verify.completions({
{
name: "prop",
sortText: completion.SortText.LocationPriority,
replacementSpan: {
fileName: "",
pos: 0,
end: 0,
},
source: "ObjectLiteralMethodSnippet/",
insertText: "get prop(): number { },set prop(n: number) { }",
},
],
Expand Down
0