Skip to content
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

Enable method signature completion for object literals #48168

Merged
merged 38 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
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
28 changes: 27 additions & 1 deletion src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -950,9 +950,35 @@ namespace FourSlashInterface {
}

export namespace Completion {
export import SortText = ts.Completions.SortText;
import SortTextType = ts.Completions.SortText;
export type SortText = SortTextType;
export import CompletionSource = ts.Completions.CompletionSource;

export const SortText = {
// Presets
LocalDeclarationPriority: "10" as SortText,
LocationPriority: "11" as SortText,
OptionalMember: "12" as SortText,
MemberDeclaredBySpreadAssignment: "13" as SortText,
SuggestedClassMembers: "14" as SortText,
GlobalsOrKeywords: "15" as SortText,
AutoImportSuggestions: "16" as SortText,
JavascriptIdentifiers: "17" as SortText,

// Transformations
Deprecated(sortText: SortText): SortText {
return "z" + sortText as SortText;
},

ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText {
return `${presetSortText}\0${symbolDisplayName}\0` as SortText;
},

AddIsSnippetSuffix(sortText: SortText): SortText {
return sortText + "1" as SortText;
},
};

const functionEntry = (name: string): ExpectedCompletionEntryObject => ({
name,
kind: "function",
Expand Down
67 changes: 44 additions & 23 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,31 @@ namespace ts.Completions {

export type Log = (message: string) => void;

// NOTE: Make sure that each entry has the exact same number of digits
// since many implementations will sort by string contents,
// where "10" is considered less than "2".
export enum SortText {
LocalDeclarationPriority = "10",
LocationPriority = "11",
OptionalMember = "12",
MemberDeclaredBySpreadAssignment = "13",
SuggestedClassMembers = "14",
GlobalsOrKeywords = "15",
AutoImportSuggestions = "16",
JavascriptIdentifiers = "17",
}
export type SortText = string & { __sortText: any };
export const SortText = {
// Presets
LocalDeclarationPriority: "10" as SortText,
LocationPriority: "11" as SortText,
OptionalMember: "12" as SortText,
MemberDeclaredBySpreadAssignment: "13" as SortText,
SuggestedClassMembers: "14" as SortText,
GlobalsOrKeywords: "15" as SortText,
AutoImportSuggestions: "16" as SortText,
JavascriptIdentifiers: "17" as SortText,

// Transformations
Deprecated(sortText: SortText): SortText {
return "z" + sortText as SortText;
},

ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText {
return `${presetSortText}\0${symbolDisplayName}\0` as SortText;
},

AddIsSnippetSuffix(sortText: SortText): SortText {
gabritto marked this conversation as resolved.
Show resolved Hide resolved
return sortText + "1" as SortText;
},
};

/**
* Special values for `CompletionInfo['source']` used to disambiguate
Expand Down Expand Up @@ -129,7 +141,6 @@ namespace ts.Completions {

/**
* Map from symbol index in `symbols` -> SymbolOriginInfo.
* Only populated for symbols that come from other modules.
*/
type SymbolOriginInfoMap = Record<number, SymbolOriginInfo>;

Expand Down Expand Up @@ -767,7 +778,7 @@ namespace ts.Completions {
}
({ insertText, isSnippet, importAdder, sourceDisplay } = entry);
source = CompletionSource.ObjectLiteralMethodSnippet;
sortText = sortText + "1" as SortText;
sortText = SortText.AddIsSnippetSuffix(sortText);
if (importAdder.hasFixes()) {
hasAction = true;
}
Expand Down Expand Up @@ -1129,13 +1140,23 @@ namespace ts.Completions {
case SyntaxKind.MethodSignature:
case SyntaxKind.MethodDeclaration: {
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
if (signatures.length > 1) {
if (signatures.length !== 1) {
// We don't support overloads in object literals.
return undefined;
}
const effectiveType = type.flags & TypeFlags.Union && (type as UnionType).types.length < 10
let effectiveType = type.flags & TypeFlags.Union && (type as UnionType).types.length < 10
? checker.getUnionType((type as UnionType).types, UnionReduction.Subtype)
: type;
if (effectiveType.flags & TypeFlags.Union) {
// Only offer the completion if there's a single function type component.
const functionTypes = filter((effectiveType as UnionType).types, type => checker.getSignaturesOfType(type, SignatureKind.Call).length > 0);
if (functionTypes.length === 1) {
effectiveType = functionTypes[0];
}
else {
return undefined;
}
}
let typeNode = checker.typeToTypeNode(effectiveType, enclosingDeclaration, builderFlags, codefix.getNoopSymbolTrackerWithResolver({ program, host }));
gabritto marked this conversation as resolved.
Show resolved Hide resolved
if (!typeNode || !isFunctionTypeNode(typeNode)) {
return undefined;
Expand Down Expand Up @@ -1373,7 +1394,7 @@ namespace ts.Completions {

const { name, needsConvertPropertyAccess } = info;
const originalSortText = symbolToSortTextIdMap?.[getSymbolId(symbol)] ?? SortText.LocationPriority;
const sortText = (isDeprecated(symbol, typeChecker) ? "z" + originalSortText : originalSortText) as SortText;
const sortText = (isDeprecated(symbol, typeChecker) ? SortText.Deprecated(originalSortText) : originalSortText);
const entry = createCompletionEntry(
symbol,
sortText,
Expand Down Expand Up @@ -2950,6 +2971,7 @@ namespace ts.Completions {
* @returns true if 'symbols' was successfully populated; false otherwise.
*/
function tryGetObjectLikeCompletionSymbols(): GlobalsSearch | undefined {
const symbolsStartIndex = symbols.length;
const objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken);
if (!objectLikeContainer) return GlobalsSearch.Continue;

Expand Down Expand Up @@ -3027,8 +3049,7 @@ namespace ts.Completions {
}
setSortTextToOptionalMember();
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression && preferences.includeCompletionsWithInsertText) {
// >> TODO: specify `symbols` range?
transformObjectLiteralMembersSortText();
transformObjectLiteralMembersSortText(symbolsStartIndex);
}

return GlobalsSearch.Success;
Expand Down Expand Up @@ -3568,9 +3589,9 @@ namespace ts.Completions {
}
}

function transformObjectLiteralMembersSortText(): void {
function transformObjectLiteralMembersSortText(start: number): void {
const pastSymbolIds: Set<number> = new Set();
for (let i = 0; i < symbols.length; i++) {
for (let i = start; i < symbols.length; i++) {
const symbol = symbols[i];
const symbolId = getSymbolId(symbol);
if (pastSymbolIds.has(symbolId)) {
Expand All @@ -3588,7 +3609,7 @@ namespace ts.Completions {
if (displayName) {
const originalSortText = symbolToSortTextIdMap[symbolId] ?? SortText.LocationPriority;
const { name } = displayName;
symbolToSortTextIdMap[symbolId] = `${originalSortText}\0${name}\0` as SortText;
symbolToSortTextIdMap[symbolId] = SortText.ObjectLiteralProperty(originalSortText, name);
}
}
}
Expand Down
27 changes: 16 additions & 11 deletions tests/cases/fourslash/completionsObjectLiteralMethod1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ verify.completions({
includes: [
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"),
insertText: undefined,
},
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0${1}` as completion.SortText,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "bar(x: number): void {\n},",
},
Expand All @@ -61,23 +62,25 @@ verify.completions({
includes: [
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"),
insertText: undefined,
},
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0${1}` as completion.SortText,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "bar(x: number): void {\n},",
},
{
name: "foo",
sortText: `${completion.SortText.LocationPriority}\0foo\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo"),
insertText: undefined,
},
{
name: "foo",
sortText: `${completion.SortText.LocationPriority}\0foo\0${1}` as completion.SortText,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "foo(x: string): string {\n},",
},
Expand All @@ -92,7 +95,7 @@ verify.completions({
exact: [
{
name: "buzz",
sortText: `${completion.SortText.LocationPriority}\0buzz\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "buzz"),
// no declaration insert text, because this property has overloads
insertText: undefined,
},
Expand All @@ -107,12 +110,13 @@ verify.completions({
includes: [
{
name: "\"space bar\"",
sortText: `${completion.SortText.LocationPriority}\0${"\"space bar\""}\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "\"space bar\""),
insertText: undefined,
},
{
name: "\"space bar\"",
sortText: `${completion.SortText.LocationPriority}\0${"\"space bar\""}\0${1}` as completion.SortText,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "\"space bar\"")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "\"space bar\"(): string {\n},",
},
Expand All @@ -127,12 +131,13 @@ verify.completions({
includes: [
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0` as completion.SortText,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar"),
insertText: undefined,
},
{
name: "bar",
sortText: `${completion.SortText.LocationPriority}\0bar\0${1}` as completion.SortText,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "bar")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
isSnippet: true,
insertText: "bar(x: number): void {\n $0\n},",
Expand Down
5 changes: 3 additions & 2 deletions tests/cases/fourslash/completionsObjectLiteralMethod2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ verify.completions({
includes: [
{
name: "foo",
sortText: completion.SortText.LocationPriority,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo"),
insertText: undefined,
},
{
name: "foo",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "foo")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "foo(f: IFoo): void {\n},",
hasAction: true,
Expand Down
24 changes: 14 additions & 10 deletions tests/cases/fourslash/completionsObjectLiteralMethod3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ verify.completions({
includes: [
{
name: "M",
sortText: completion.SortText.LocationPriority,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"),
insertText: undefined,
},
{
name: "M",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "M(x: number): void {\n},",
},
Expand All @@ -61,7 +62,7 @@ verify.completions({
includes: [
{
name: "M",
sortText: completion.SortText.LocationPriority,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"),
insertText: undefined,
},
// No signature completion because type of `M` is intersection type
Expand All @@ -76,7 +77,7 @@ verify.completions({
exact: [
{
name: "M",
sortText: completion.SortText.LocationPriority,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "M"),
insertText: undefined,
},
// No signature completion because type of `M` is intersection type
Expand All @@ -91,34 +92,37 @@ verify.completions({
includes: [
{
name: "M",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "M"),
insertText: undefined,
},
{
name: "M",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "M")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "M(x: number): void {\n},",
},
{
name: "N",
sortText: completion.SortText.LocationPriority,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "N"),
insertText: undefined,
},
{
name: "N",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.LocationPriority, "N")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "N(x: string): void {\n},",
},
{
name: "O",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "O"),
insertText: undefined,
},
{
name: "O",
sortText: completion.SortText.OptionalMember,
sortText: completion.SortText.AddIsSnippetSuffix(
completion.SortText.ObjectLiteralProperty(completion.SortText.OptionalMember, "O")),
source: completion.CompletionSource.ObjectLiteralMethodSnippet,
insertText: "O(): void {\n},",
},
Expand Down
33 changes: 16 additions & 17 deletions tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -838,23 +838,22 @@ declare namespace completion {
interface GlobalsPlusOptions {
noLib?: boolean;
}
export const enum SortText {
LocalDeclarationPriority = "10",
LocationPriority = "11",
OptionalMember = "12",
MemberDeclaredBySpreadAssignment = "13",
SuggestedClassMembers = "14",
GlobalsOrKeywords = "15",
AutoImportSuggestions = "16",
JavascriptIdentifiers = "17",
DeprecatedLocalDeclarationPriority = "18",
DeprecatedLocationPriority = "19",
DeprecatedOptionalMember = "20",
DeprecatedMemberDeclaredBySpreadAssignment = "21",
DeprecatedSuggestedClassMembers = "22",
DeprecatedGlobalsOrKeywords = "23",
DeprecatedAutoImportSuggestions = "24"
}
export type SortText = string & { __sortText: any };
export const SortText: {
LocalDeclarationPriority: SortText,
LocationPriority: SortText,
OptionalMember: SortText,
MemberDeclaredBySpreadAssignment: SortText,
SuggestedClassMembers: SortText,
GlobalsOrKeywords: SortText,
AutoImportSuggestions: SortText,
JavascriptIdentifiers: SortText,

Deprecated(sortText: SortText): SortText,
ObjectLiteralProperty(presetSortText: SortText, symbolDisplayName: string): SortText,
AddIsSnippetSuffix(sortText: SortText): SortText,
};

export const enum CompletionSource {
ThisProperty = "ThisProperty/",
ClassMemberSnippet = "ClassMemberSnippet/",
Expand Down