Skip to content

Commit

Permalink
Show tuple labels and documentation in completions
Browse files Browse the repository at this point in the history
  • Loading branch information
weswigham committed May 7, 2020
1 parent bf7e5d6 commit e1c5989
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 54 deletions.
85 changes: 58 additions & 27 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4568,9 +4568,9 @@ namespace ts {
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
createOptionalTypeNode(tupleConstituentNodes[i]);
}
if ((type.target as TupleType).associatedNames) {
if ((type.target as TupleType).labeledElementDeclarations) {
for (let i = 0; i < tupleConstituentNodes.length; i++) {
tupleConstituentNodes[i] = createNamedTupleMember(createIdentifier(unescapeLeadingUnderscores((type.target as TupleType).associatedNames![i])), tupleConstituentNodes[i]);
tupleConstituentNodes[i] = createNamedTupleMember(createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel((type.target as TupleType).labeledElementDeclarations![i]))), tupleConstituentNodes[i]);
}
}
const tupleTypeNode = setEmitFlags(createTupleTypeNode(tupleConstituentNodes), EmitFlags.SingleLine);
Expand Down Expand Up @@ -12082,15 +12082,8 @@ namespace ts {
const lastElement = lastOrUndefined(node.elements);
const restElement = lastElement && unwrapNamedTupleMember(lastElement).kind === SyntaxKind.RestType ? lastElement : undefined;
const minLength = findLastIndex(node.elements, n => unwrapNamedTupleMember(n).kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
let missingName = false;
const names = map(node.elements, e => {
if (e.kind !== SyntaxKind.NamedTupleMember) {
missingName = true;
return escapeLeadingUnderscores("arg");
}
return (e as NamedTupleMember).name.escapedText;
});
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : names);
const missingName = some(node.elements, e => e.kind !== SyntaxKind.NamedTupleMember);
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : node.elements as readonly NamedTupleMember[]);
}

// Return true if the given type reference node is directly aliased or if it needs to be deferred
Expand Down Expand Up @@ -12187,7 +12180,7 @@ namespace ts {
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames: __String[] | undefined): TupleType {
function createTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration)[] | undefined): TupleType {
let typeParameters: TypeParameter[] | undefined;
const properties: Symbol[] = [];
const maxLength = hasRestElement ? arity - 1 : arity;
Expand All @@ -12198,6 +12191,7 @@ namespace ts {
if (i < maxLength) {
const property = createSymbol(SymbolFlags.Property | (i >= minLength ? SymbolFlags.Optional : 0),
"" + i as __String, readonly ? CheckFlags.Readonly : 0);
property.tupleLabelDeclaration = namedMemberDeclarations?.[i];
property.type = typeParameter;
properties.push(property);
}
Expand Down Expand Up @@ -12227,25 +12221,25 @@ namespace ts {
type.minLength = minLength;
type.hasRestElement = hasRestElement;
type.readonly = readonly;
type.associatedNames = associatedNames;
type.labeledElementDeclarations = namedMemberDeclarations;
return type;
}

function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, associatedNames?: __String[]): GenericType {
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames && associatedNames.length ? "," + associatedNames.join(",") : "");
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]): GenericType {
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (namedMemberDeclarations && namedMemberDeclarations.length ? "," + map(namedMemberDeclarations, getNodeId).join(",") : "");
let type = tupleTypes.get(key);
if (!type) {
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, associatedNames));
tupleTypes.set(key, type = createTupleTypeOfArity(arity, minLength, hasRestElement, readonly, namedMemberDeclarations));
}
return type;
}

function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, associatedNames?: __String[]) {
function createTupleType(elementTypes: readonly Type[], minLength = elementTypes.length, hasRestElement = false, readonly = false, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]) {
const arity = elementTypes.length;
if (arity === 1 && hasRestElement) {
return createArrayType(elementTypes[0], readonly);
}
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, associatedNames);
const tupleType = getTupleTypeOfArity(arity, minLength, arity > 0 && hasRestElement, readonly, namedMemberDeclarations);
return elementTypes.length ? createTypeReference(tupleType, elementTypes) : tupleType;
}

Expand All @@ -12260,7 +12254,7 @@ namespace ts {
Math.max(0, tuple.minLength - index),
tuple.hasRestElement,
tuple.readonly,
tuple.associatedNames && tuple.associatedNames.slice(index),
tuple.labeledElementDeclarations && tuple.labeledElementDeclarations.slice(index),
);
}

Expand Down Expand Up @@ -14260,7 +14254,7 @@ namespace ts {
minLength;
const newReadonly = getModifiedReadonlyState(tupleType.target.readonly, modifiers);
return contains(elementTypes, errorType) ? errorType :
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.associatedNames);
createTupleType(elementTypes, newMinLength, tupleType.target.hasRestElement, newReadonly, tupleType.target.labeledElementDeclarations);
}

function instantiateMappedTypeTemplate(type: MappedType, key: Type, isOptional: boolean, mapper: TypeMapper) {
Expand Down Expand Up @@ -18517,7 +18511,7 @@ namespace ts {
const elementTypes = map(getTypeArguments(source), t => inferReverseMappedType(t, target, constraint));
const minLength = getMappedTypeModifiers(target) & MappedTypeModifiers.IncludeOptional ?
getTypeReferenceArity(source) - (source.target.hasRestElement ? 1 : 0) : source.target.minLength;
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.associatedNames);
return createTupleType(elementTypes, minLength, source.target.hasRestElement, source.target.readonly, source.target.labeledElementDeclarations);
}
// For all other object types we infer a new object type where the reverse mapping has been
// applied to the type of each property.
Expand Down Expand Up @@ -25114,7 +25108,7 @@ namespace ts {
function getArrayifiedType(type: Type) {
return type.flags & TypeFlags.Union ? mapType(type, getArrayifiedType) :
type.flags & (TypeFlags.Any | TypeFlags.Instantiable) || isMutableArrayOrTuple(type) ? type :
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.associatedNames) :
isTupleType(type) ? createTupleType(getTypeArguments(type), type.target.minLength, type.target.hasRestElement, /*readonly*/ false, type.target.labeledElementDeclarations) :
createArrayType(getIndexedAccessType(type, numberType));
}

Expand Down Expand Up @@ -26974,6 +26968,11 @@ namespace ts {
return type;
}

function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember) {
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
return d.name.escapedText;
}

function getParameterNameAtPosition(signature: Signature, pos: number) {
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
if (pos < paramCount) {
Expand All @@ -26982,13 +26981,33 @@ namespace ts {
const restParameter = signature.parameters[paramCount] || unknownSymbol;
const restType = getTypeOfSymbol(restParameter);
if (isTupleType(restType)) {
const associatedNames = (<TupleType>(<TypeReference>restType).target).associatedNames;
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
const index = pos - paramCount;
return associatedNames && associatedNames[index] || restParameter.escapedName + "_" + index as __String;
return associatedNames && getTupleElementLabel(associatedNames[index]) || restParameter.escapedName + "_" + index as __String;
}
return restParameter.escapedName;
}

function isValidDeclarationForTupleLabel(d: Declaration): d is NamedTupleMember | (ParameterDeclaration & { name: Identifier }) {
return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && isIdentifier(d.name));
}

function getNameableDeclarationAtPosition(signature: Signature, pos: number) {
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
if (pos < paramCount) {
const decl = signature.parameters[pos].valueDeclaration;
return decl && isValidDeclarationForTupleLabel(decl) ? decl : undefined;
}
const restParameter = signature.parameters[paramCount] || unknownSymbol;
const restType = getTypeOfSymbol(restParameter);
if (isTupleType(restType)) {
const associatedNames = (<TupleType>(<TypeReference>restType).target).labeledElementDeclarations;
const index = pos - paramCount;
return associatedNames && associatedNames[index];
}
return restParameter.valueDeclaration && isValidDeclarationForTupleLabel(restParameter.valueDeclaration) ? restParameter.valueDeclaration : undefined;
}

function getTypeAtPosition(signature: Signature, pos: number): Type {
return tryGetTypeAtPosition(signature, pos) || anyType;
}
Expand Down Expand Up @@ -27019,14 +27038,26 @@ namespace ts {
return restType;
}
const types = [];
const names = [];
let names: (NamedTupleMember | ParameterDeclaration)[] | undefined = [];
for (let i = pos; i < nonRestCount; i++) {
types.push(getTypeAtPosition(source, i));
names.push(getParameterNameAtPosition(source, i));
const name = getNameableDeclarationAtPosition(source, i);
if (name && names) {
names.push(name);
}
else {
names = undefined;
}
}
if (restType) {
types.push(getIndexedAccessType(restType, numberType));
names.push(getParameterNameAtPosition(source, nonRestCount));
const name = getNameableDeclarationAtPosition(source, nonRestCount);
if (name && names) {
names.push(name);
}
else {
names = undefined;
}
}
const minArgumentCount = getMinArgumentCount(source);
const minLength = minArgumentCount < pos ? 0 : minArgumentCount - pos;
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1278,7 +1278,7 @@ namespace ts {
elements: NodeArray<TypeNode | NamedTupleMember>;
}

export interface NamedTupleMember extends TypeNode, JSDocContainer {
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
kind: SyntaxKind.NamedTupleMember;
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
name: Identifier;
Expand Down Expand Up @@ -4170,6 +4170,7 @@ namespace ts {
cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target
typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor
tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label
}

/* @internal */
Expand Down Expand Up @@ -4642,7 +4643,7 @@ namespace ts {
minLength: number;
hasRestElement: boolean;
readonly: boolean;
associatedNames?: __String[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
}

export interface TupleTypeReference extends TypeReference {
Expand Down
9 changes: 8 additions & 1 deletion src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,14 @@ namespace ts {
getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] {
if (!this.documentationComment) {
this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs
this.documentationComment = getDocumentationComment(this.declarations, checker);

if (!this.declarations && (this as Symbol as TransientSymbol).target && ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
const labelDecl = ((this as Symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
this.documentationComment = getDocumentationComment([labelDecl], checker);
}
else {
this.documentationComment = getDocumentationComment(this.declarations, checker);
}
}
return this.documentationComment;
}
Expand Down
8 changes: 8 additions & 0 deletions src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,14 @@ namespace ts.SymbolDisplay {
else {
addRange(displayParts, typeToDisplayParts(typeChecker, type, enclosingDeclaration));
}
if ((symbol as TransientSymbol).target && ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration) {
const labelDecl = ((symbol as TransientSymbol).target as TransientSymbol).tupleLabelDeclaration!;
Debug.assertNode(labelDecl.name, isIdentifier);
displayParts.push(spacePart());
displayParts.push(punctuationPart(SyntaxKind.OpenParenToken));
displayParts.push(textPart(idText(labelDecl.name)));
displayParts.push(punctuationPart(SyntaxKind.CloseParenToken));
}
}
else if (symbolFlags & SymbolFlags.Function ||
symbolFlags & SymbolFlags.Method ||
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ declare namespace ts {
kind: SyntaxKind.TupleType;
elements: NodeArray<TypeNode | NamedTupleMember>;
}
export interface NamedTupleMember extends TypeNode, JSDocContainer {
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
kind: SyntaxKind.NamedTupleMember;
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
name: Identifier;
Expand Down Expand Up @@ -2483,7 +2483,7 @@ declare namespace ts {
minLength: number;
hasRestElement: boolean;
readonly: boolean;
associatedNames?: __String[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
}
export interface TupleTypeReference extends TypeReference {
target: TupleType;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ declare namespace ts {
kind: SyntaxKind.TupleType;
elements: NodeArray<TypeNode | NamedTupleMember>;
}
export interface NamedTupleMember extends TypeNode, JSDocContainer {
export interface NamedTupleMember extends TypeNode, JSDocContainer, Declaration {
kind: SyntaxKind.NamedTupleMember;
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
name: Identifier;
Expand Down Expand Up @@ -2483,7 +2483,7 @@ declare namespace ts {
minLength: number;
hasRestElement: boolean;
readonly: boolean;
associatedNames?: __String[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
}
export interface TupleTypeReference extends TypeReference {
target: TupleType;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/genericRestParameters1.types
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ type T01 = Parameters<(x: number, y: string, z: boolean) => void>;
>z : boolean

type T02 = Parameters<(...args: [number, string, boolean]) => void>;
>T02 : [args_0: number, args_1: string, args_2: boolean]
>T02 : [number, string, boolean]
>args : [number, string, boolean]

type T03 = ConstructorParameters<new (x: number, y: string, z: boolean) => void>;
Expand All @@ -681,7 +681,7 @@ type T03 = ConstructorParameters<new (x: number, y: string, z: boolean) => void>
>z : boolean

type T04 = ConstructorParameters<new (...args: [number, string, boolean]) => void>;
>T04 : [args_0: number, args_1: string, args_2: boolean]
>T04 : [number, string, boolean]
>args : [number, string, boolean]

type T05<T> = Parameters<(...args: T[]) => void>;
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/genericRestParameters2.types
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ type T01 = Parameters<(x: number, y: string, ...z: boolean[]) => void>;
>z : boolean[]

type T02 = Parameters<(...args: [number, string, ...boolean[]]) => void>;
>T02 : [args_0: number, args_1: string, args_2: ...boolean[]]
>T02 : [number, string, ...boolean[]]
>args : [number, string, ...boolean[]]

type T03 = ConstructorParameters<new (x: number, y: string, ...z: boolean[]) => void>;
Expand All @@ -433,7 +433,7 @@ type T03 = ConstructorParameters<new (x: number, y: string, ...z: boolean[]) =>
>z : boolean[]

type T04 = ConstructorParameters<new (...args: [number, string, ...boolean[]]) => void>;
>T04 : [args_0: number, args_1: string, args_2: ...boolean[]]
>T04 : [number, string, ...boolean[]]
>args : [number, string, ...boolean[]]

type T05<T extends any[]> = Parameters<(x: string, ...args: T) => void>;
Expand All @@ -442,7 +442,7 @@ type T05<T extends any[]> = Parameters<(x: string, ...args: T) => void>;
>args : T

type T06 = T05<[number, ...boolean[]]>;
>T06 : [x: string, args_0: number, args_1: ...boolean[]]
>T06 : [string, number, ...boolean[]]

type P1<T extends Function> = T extends (head: infer A, ...tail: infer B) => any ? { head: A, tail: B } : any[];
>P1 : P1<T>
Expand Down
Loading

0 comments on commit e1c5989

Please sign in to comment.