Add inlay hints support (#42089)

* Add signature arguments label support

* Support rest parameters and destruction

* make lint

* Fix tuple rest parameters

* Adjust name styles

* Rename to inline hints

* Partition inline hints

* Adjust range pred

* Add function expression like hints

* Support configure inline hints

* Display hints in single line

* Add test suits and tests

* Add range tests

* Support more hints

* Add more options

* Fix logical

* Add more cases

* Support call chains

* Rename options

* Match lastest protocol

* Update protocol changes

* Support context value and hover message

* Revert "Support context value and hover message"

This reverts commit 37a7089633.

* Revert "Update protocol changes"

This reverts commit e5ca31bc30.

* Add hover message

* Accept baseline

* Update src/services/inlineHints.ts

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>

* Update src/services/inlineHints.ts

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>

* Cache across the program

* Fix possible undefined

* Update protocol changes

* Fix missing property

* Make lint happy

* Avoid call chain hints

* I'm bad

* Add whitespace before type

* Add more tests

* Should care about jsdoc

* Support complex rest parameter

* Avoid module symbol  hints

* Care about leading comments

* Fix CR issues

* Avoid changes

* Simplify comments contains

* Fix CR issues

* Accept baseline

* Check parameter name before create regex

* Rename option

* Avoid makers

* Skip parens for argument

* Fix CR issues

* Fix enums

* Accept baseline

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Wenlu Wang
2021-06-24 23:06:34 -07:00
committed by GitHub
co-authored by Daniel Rosenwasser
parent 2767ab3e3e
commit 66b4ba4b35
71 changed files with 1829 additions and 2 deletions
+34
View File
@@ -428,6 +428,7 @@ namespace ts {
return node ? getTypeFromTypeNode(node) : errorType;
},
getParameterType: getTypeAtPosition,
getParameterIdentifierNameAtPosition,
getPromisedTypeOfPromise,
getAwaitedType: type => getAwaitedType(type),
getReturnTypeOfSignature,
@@ -30352,6 +30353,39 @@ namespace ts {
return restParameter.escapedName;
}
function getParameterIdentifierNameAtPosition(signature: Signature, pos: number): [parameterName: __String, isRestParameter: boolean] | undefined {
const paramCount = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
if (pos < paramCount) {
const param = signature.parameters[pos];
return isParameterDeclarationWithIdentifierName(param) ? [param.escapedName, false] : undefined;
}
const restParameter = signature.parameters[paramCount] || unknownSymbol;
if (!isParameterDeclarationWithIdentifierName(restParameter)) {
return undefined;
}
const restType = getTypeOfSymbol(restParameter);
if (isTupleType(restType)) {
const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations;
const index = pos - paramCount;
const associatedName = associatedNames?.[index];
const isRestTupleElement = !!associatedName?.dotDotDotToken;
return associatedName ? [
getTupleElementLabel(associatedName),
isRestTupleElement
] : undefined;
}
if (pos === paramCount) {
return [restParameter.escapedName, true];
}
return undefined;
}
function isParameterDeclarationWithIdentifierName(symbol: Symbol) {
return symbol.valueDeclaration && isParameter(symbol.valueDeclaration) && isIdentifier(symbol.valueDeclaration.name);
}
function isValidDeclarationForTupleLabel(d: Declaration): d is NamedTupleMember | (ParameterDeclaration & { name: Identifier }) {
return d.kind === SyntaxKind.NamedTupleMember || (isParameter(d) && d.name && isIdentifier(d.name));
}
+1
View File
@@ -4104,6 +4104,7 @@ namespace ts {
* Returns `any` if the index is not valid.
*/
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
/* @internal */ getParameterIdentifierNameAtPosition(signature: Signature, parameterIndex: number): [parameterName: __String, isRestParameter: boolean] | undefined;
getNullableType(type: Type, flags: TypeFlags): Type;
getNonNullableType(type: Type): Type;
/* @internal */ getNonOptionalType(type: Type): Type;
+5
View File
@@ -1232,6 +1232,11 @@ namespace ts {
return node && isFunctionLikeDeclarationKind(node.kind);
}
/* @internal */
export function isBooleanLiteral(node: Node): node is BooleanLiteral {
return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword;
}
function isFunctionLikeDeclarationKind(kind: SyntaxKind): boolean {
switch (kind) {
case SyntaxKind.FunctionDeclaration:
+14
View File
@@ -644,6 +644,20 @@ namespace ts.server {
applyCodeActionCommand = notImplemented;
provideInlayHints(file: string, span: TextSpan): InlayHint[] {
const { start, length } = span;
const args: protocol.InlayHintsRequestArgs = { file, start, length };
const request = this.processRequest<protocol.InlayHintsRequest>(CommandNames.ProvideInlayHints, args);
const response = this.processResponse<protocol.InlayHintsResponse>(request);
return response.body!.map(item => ({ // TODO: GH#18217
...item,
kind: item.kind as InlayHintKind | undefined,
position: this.lineOffsetToPosition(file, item.position),
}));
}
private createFileLocationOrRangeRequestArgs(positionOrRange: number | TextRange, fileName: string): protocol.FileLocationOrRangeRequestArgs {
return typeof positionOrRange === "number"
? this.createFileLocationRequestArgs(fileName, positionOrRange)
+17
View File
@@ -1,3 +1,4 @@
namespace FourSlash {
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
@@ -836,6 +837,22 @@ namespace FourSlash {
});
}
public verifyInlayHints(expected: readonly FourSlashInterface.VerifyInlayHintsOptions[], span: ts.TextSpan = { start: 0, length: this.activeFile.content.length }, preference?: ts.InlayHintsOptions) {
const hints = this.languageService.provideInlayHints(this.activeFile.fileName, span, preference);
assert.equal(hints.length, expected.length, "Number of hints");
const sortHints = (a: ts.InlayHint, b: ts.InlayHint) => {
return a.position - b.position;
};
ts.zipWith(hints.sort(sortHints), [...expected].sort(sortHints), (actual, expected) => {
assert.equal(actual.text, expected.text, "Text");
assert.equal(actual.position, expected.position, "Position");
assert.equal(actual.kind, expected.kind, "Kind");
assert.equal(actual.whitespaceBefore, expected.whitespaceBefore, "whitespaceBefore");
assert.equal(actual.whitespaceAfter, expected.whitespaceAfter, "whitespaceAfter");
});
}
public verifyCompletions(options: FourSlashInterface.VerifyCompletionsOptions) {
if (options.marker === undefined) {
this.verifyCompletionsWorker(options);
+12
View File
@@ -251,6 +251,10 @@ namespace FourSlashInterface {
}
}
public getInlayHints(expected: readonly VerifyInlayHintsOptions[], span: ts.TextSpan, preference?: ts.InlayHintsOptions) {
this.state.verifyInlayHints(expected, span, preference);
}
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
@@ -1667,6 +1671,14 @@ namespace FourSlashInterface {
readonly containerKind?: ts.ScriptElementKind;
}
export interface VerifyInlayHintsOptions {
text: string;
position: number;
kind?: ts.InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export type ArrayOrSingle<T> = T | readonly T[];
export interface VerifyCompletionListContainsOptions extends ts.UserPreferences {
+3
View File
@@ -599,6 +599,9 @@ namespace Harness.LanguageService {
provideCallHierarchyOutgoingCalls(fileName: string, position: number) {
return unwrapJSONCallResult(this.shim.provideCallHierarchyOutgoingCalls(fileName, position));
}
provideInlayHints(fileName: string, span: ts.TextSpan, preference: ts.InlayHintsOptions) {
return unwrapJSONCallResult(this.shim.provideInlayHints(fileName, span, preference));
}
getEmitOutput(fileName: string): ts.EmitOutput {
return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
}
+35
View File
@@ -154,6 +154,7 @@ namespace ts.server.protocol {
PrepareCallHierarchy = "prepareCallHierarchy",
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
ProvideInlayHints = "provideInlayHints"
// NOTE: If updating this, be sure to also update `allCommandNames` in `testRunner/unittests/tsserver/session.ts`.
}
@@ -2549,6 +2550,40 @@ namespace ts.server.protocol {
body?: SignatureHelpItems;
}
export const enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum",
}
export interface InlayHintsRequestArgs extends FileRequestArgs {
/**
* Start position of the span.
*/
start: number;
/**
* Length of the span.
*/
length: number;
}
export interface InlayHintsRequest extends Request {
command: CommandTypes.ProvideInlayHints;
arguments: InlayHintsRequestArgs;
}
export interface InlayHintItem {
text: string;
position: Location;
kind?: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export interface InlayHintsResponse extends Response {
body?: InlayHintItem[];
}
/**
* Synchronous request for semantic diagnostics of one file.
*/
+14
View File
@@ -1450,6 +1450,17 @@ namespace ts.server {
});
}
private provideInlayHints(args: protocol.InlayHintsRequestArgs) {
const { file, languageService } = this.getFileAndLanguageServiceForSyntacticOperation(args);
const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file)!;
const hints = languageService.provideInlayHints(file, args, this.getPreferences(file));
return hints.map(hint => ({
...hint,
position: scriptInfo.positionToLineOffset(hint.position),
}));
}
private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath);
}
@@ -2963,6 +2974,9 @@ namespace ts.server {
[CommandNames.UncommentSelectionFull]: (request: protocol.UncommentSelectionRequest) => {
return this.requiredResponse(this.uncommentSelection(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.ProvideInlayHints]: (request: protocol.InlayHintsRequest) => {
return this.requiredResponse(this.provideInlayHints(request.arguments));
}
}));
public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
+305
View File
@@ -0,0 +1,305 @@
/* @internal */
namespace ts.InlayHints {
const maxHintsLength = 30;
const leadingParameterNameCommentRegexFactory = (name: string) => {
return new RegExp(`^\\s?/\\*\\*?\\s?${name}\\s?\\*\\/\\s?$`);
};
function shouldShowParameterNameHints(preferences: InlayHintsOptions) {
return preferences.includeInlayParameterNameHints === "literals" || preferences.includeInlayParameterNameHints === "all";
}
function shouldShowLiteralParameterNameHintsOnly(preferences: InlayHintsOptions) {
return preferences.includeInlayParameterNameHints === "literals";
}
export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
const { file, program, span, cancellationToken, preferences } = context;
const sourceFileText = file.text;
const compilerOptions = program.getCompilerOptions();
const checker = program.getTypeChecker();
const result: InlayHint[] = [];
visitor(file);
return result;
function visitor(node: Node): true | undefined {
if (!node || node.getFullWidth() === 0) {
return;
}
switch (node.kind) {
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ClassExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.ArrowFunction:
cancellationToken.throwIfCancellationRequested();
}
if (!textSpanIntersectsWith(span, node.pos, node.getFullWidth())) {
return;
}
if (isTypeNode(node)) {
return;
}
if (preferences.includeInlayVariableTypeHints && isVariableDeclaration(node)) {
visitVariableLikeDeclaration(node);
}
else if (preferences.includeInlayPropertyDeclarationTypeHints && isPropertyDeclaration(node)) {
visitVariableLikeDeclaration(node);
}
else if (preferences.includeInlayEnumMemberValueHints && isEnumMember(node)) {
visitEnumMember(node);
}
else if (shouldShowParameterNameHints(preferences) && (isCallExpression(node) || isNewExpression(node))) {
visitCallOrNewExpression(node);
}
else {
if (preferences.includeInlayFunctionParameterTypeHints && isFunctionExpressionLike(node)) {
visitFunctionExpressionLikeForParameterType(node);
}
if (preferences.includeInlayFunctionLikeReturnTypeHints && isFunctionDeclarationLike(node)) {
visitFunctionDeclarationLikeForReturnType(node);
}
}
return forEachChild(node, visitor);
}
function isFunctionExpressionLike(node: Node): node is ArrowFunction | FunctionExpression {
return isArrowFunction(node) || isFunctionExpression(node);
}
function isFunctionDeclarationLike(node: Node): node is FunctionDeclaration | ArrowFunction | FunctionExpression | MethodDeclaration {
return isArrowFunction(node) || isFunctionExpression(node) || isFunctionDeclaration(node) || isMethodDeclaration(node);
}
function addParameterHints(text: string, position: number, isFirstVariadicArgument: boolean) {
result.push({
text: `${isFirstVariadicArgument ? "..." : ""}${truncation(text, maxHintsLength)}:`,
position,
kind: InlayHintKind.Parameter,
whitespaceAfter: true,
});
}
function addTypeHints(text: string, position: number) {
result.push({
text: `: ${truncation(text, maxHintsLength)}`,
position,
kind: InlayHintKind.Type,
whitespaceBefore: true,
});
}
function addEnumMemberValueHints(text: string, position: number) {
result.push({
text: `= ${truncation(text, maxHintsLength)}`,
position,
kind: InlayHintKind.Enum,
whitespaceBefore: true,
});
}
function visitEnumMember(member: EnumMember) {
if (member.initializer) {
return;
}
const enumValue = checker.getConstantValue(member);
if (enumValue !== undefined) {
addEnumMemberValueHints(enumValue.toString(), member.end);
}
}
function isModuleReferenceType(type: Type) {
return type.symbol && (type.symbol.flags & SymbolFlags.Module);
}
function visitVariableLikeDeclaration(decl: VariableDeclaration | PropertyDeclaration) {
const effectiveTypeAnnotation = getEffectiveTypeAnnotationNode(decl);
if (effectiveTypeAnnotation || !decl.initializer) {
return;
}
const declarationType = checker.getTypeAtLocation(decl);
if (isModuleReferenceType(declarationType)) {
return;
}
const typeDisplayString = printTypeInSingleLine(declarationType);
if (typeDisplayString) {
addTypeHints(typeDisplayString, decl.name.end);
}
}
function visitCallOrNewExpression(expr: CallExpression | NewExpression) {
const args = expr.arguments;
if (!args || !args.length) {
return;
}
const candidates: Signature[] = [];
const signature = checker.getResolvedSignatureForSignatureHelp(expr, candidates);
if (!signature || !candidates.length) {
return;
}
for (let i = 0; i < args.length; ++i) {
const originalArg = args[i];
const arg = skipParentheses(originalArg);
if (shouldShowLiteralParameterNameHintsOnly(preferences) && !isHintableExpression(arg)) {
continue;
}
const identifierNameInfo = checker.getParameterIdentifierNameAtPosition(signature, i);
if (identifierNameInfo) {
const [parameterName, isFirstVariadicArgument] = identifierNameInfo;
const isParameterNameNotSameAsArgument = preferences.includeInlayParameterNameHintsWhenArgumentMatchesName || !isIdentifier(arg) || arg.text !== parameterName;
if (!isParameterNameNotSameAsArgument && !isFirstVariadicArgument) {
continue;
}
const name = unescapeLeadingUnderscores(parameterName);
if (leadingCommentsContainsParameterName(arg, name)) {
continue;
}
addParameterHints(name, originalArg.getStart(), isFirstVariadicArgument);
}
}
}
function leadingCommentsContainsParameterName(node: Node, name: string) {
if (!isIdentifierText(name, compilerOptions.target, getLanguageVariant(file.scriptKind))) {
return false;
}
const ranges = getLeadingCommentRanges(sourceFileText, node.pos);
if (!ranges?.length) {
return false;
}
const regex = leadingParameterNameCommentRegexFactory(name);
return some(ranges, range => regex.test(sourceFileText.substring(range.pos, range.end)));
}
function isHintableExpression(node: Node) {
return isLiteralExpression(node) || isBooleanLiteral(node) || isFunctionExpressionLike(node) || isObjectLiteralExpression(node) || isArrayLiteralExpression(node);
}
function visitFunctionDeclarationLikeForReturnType(decl: ArrowFunction | FunctionExpression | MethodDeclaration | FunctionDeclaration) {
if (isArrowFunction(decl)) {
if (!findChildOfKind(decl, SyntaxKind.OpenParenToken, file)) {
return;
}
}
const effectiveTypeAnnotation = getEffectiveReturnTypeNode(decl);
if (effectiveTypeAnnotation || !decl.body) {
return;
}
const type = checker.getTypeAtLocation(decl);
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
const signature = firstOrUndefined(signatures);
if (!signature) {
return;
}
const returnType = checker.getReturnTypeOfSignature(signature);
if (isModuleReferenceType(returnType)) {
return;
}
const typeDisplayString = printTypeInSingleLine(returnType);
if (!typeDisplayString) {
return;
}
addTypeHints(typeDisplayString, getTypeAnnotationPosition(decl));
}
function getTypeAnnotationPosition(decl: ArrowFunction | FunctionExpression | MethodDeclaration | FunctionDeclaration) {
const closeParenToken = findChildOfKind(decl, SyntaxKind.CloseParenToken, file);
if (closeParenToken) {
return closeParenToken.end;
}
return decl.parameters.end;
}
function visitFunctionExpressionLikeForParameterType(expr: ArrowFunction | FunctionExpression) {
if (!expr.parameters.length || expr.parameters.every(param => !!getEffectiveTypeAnnotationNode(param))) {
return;
}
const contextualType = checker.getContextualType(expr);
if (!contextualType) {
return;
}
const signatures = checker.getSignaturesOfType(contextualType, SignatureKind.Call);
const signature = firstOrUndefined(signatures);
if (!signature) {
return;
}
for (let i = 0; i < expr.parameters.length && i < signature.parameters.length; ++i) {
const param = expr.parameters[i];
const effectiveTypeAnnotation = getEffectiveTypeAnnotationNode(param);
if (effectiveTypeAnnotation) {
continue;
}
const typeDisplayString = getParameterDeclarationTypeDisplayString(signature.parameters[i]);
if (!typeDisplayString) {
continue;
}
addTypeHints(typeDisplayString, param.end);
}
}
function getParameterDeclarationTypeDisplayString(symbol: Symbol) {
const valueDeclaration = symbol.valueDeclaration;
if (!valueDeclaration || !isParameter(valueDeclaration)) {
return undefined;
}
const signatureParamType = checker.getTypeOfSymbolAtLocation(symbol, valueDeclaration);
if (isModuleReferenceType(signatureParamType)) {
return undefined;
}
return printTypeInSingleLine(signatureParamType);
}
function truncation(text: string, maxLength: number) {
if (text.length > maxLength) {
return text.substr(0, maxLength - "...".length) + "...";
}
return text;
}
function printTypeInSingleLine(type: Type) {
const flags = NodeBuilderFlags.IgnoreErrors | TypeFormatFlags.AllowUniqueESSymbolType | TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
const options: PrinterOptions = { removeComments: true };
const printer = createPrinter(options);
return usingSingleLineStringWriter(writer => {
const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags, writer);
Debug.assertIsDefined(typeNode, "should always get typenode");
printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ file, writer);
});
}
}
}
+19
View File
@@ -1188,6 +1188,7 @@ namespace ts {
"prepareCallHierarchy",
"provideCallHierarchyIncomingCalls",
"provideCallHierarchyOutgoingCalls",
"provideInlayHints"
];
const invalidOperationsInSyntacticMode: readonly (keyof LanguageService)[] = [
@@ -2504,6 +2505,17 @@ namespace ts {
};
}
function getInlayHintsContext(file: SourceFile, span: TextSpan, preferences: UserPreferences): InlayHintsContext {
return {
file,
program: getProgram()!,
host,
span,
preferences,
cancellationToken,
};
}
function getSmartSelectionRange(fileName: string, position: number): SelectionRange {
return SmartSelectionRange.getSmartSelectionRange(position, syntaxTreeCache.getCurrentSourceFile(fileName));
}
@@ -2558,6 +2570,12 @@ namespace ts {
return declaration ? CallHierarchy.getOutgoingCalls(program, declaration) : [];
}
function provideInlayHints(fileName: string, span: TextSpan, preferences: InlayHintsOptions = emptyOptions): InlayHint[] {
synchronizeHostData();
const sourceFile = getValidSourceFile(fileName);
return InlayHints.provideInlayHints(getInlayHintsContext(sourceFile, span, preferences));
}
const ls: LanguageService = {
dispose,
cleanupSemanticCache,
@@ -2623,6 +2641,7 @@ namespace ts {
toggleMultilineComment,
commentSelection,
uncommentSelection,
provideInlayHints,
};
switch (languageServiceMode) {
+8 -1
View File
@@ -280,7 +280,7 @@ namespace ts {
prepareCallHierarchy(fileName: string, position: number): string;
provideCallHierarchyIncomingCalls(fileName: string, position: number): string;
provideCallHierarchyOutgoingCalls(fileName: string, position: number): string;
provideInlayHints(fileName: string, span: TextSpan, preference: InlayHintsOptions | undefined): string;
getEmitOutput(fileName: string): string;
getEmitOutputObject(fileName: string): EmitOutput;
@@ -1067,6 +1067,13 @@ namespace ts {
);
}
public provideInlayHints(fileName: string, span: TextSpan, preference: InlayHintsOptions | undefined): string {
return this.forwardJSONCall(
`provideInlayHints('${fileName}', '${JSON.stringify(span)}', ${JSON.stringify(preference)})`,
() => this.languageService.provideInlayHints(fileName, span, preference)
);
}
/// Emit
public getEmitOutput(fileName: string): string {
return this.forwardJSONCall(
+1
View File
@@ -33,6 +33,7 @@
"rename.ts",
"smartSelection.ts",
"signatureHelp.ts",
"inlayHints.ts",
"sourcemaps.ts",
"suggestionDiagnostics.ts",
"symbolDisplay.ts",
+35
View File
@@ -487,6 +487,8 @@ namespace ts {
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences | undefined): InlayHint[]
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@@ -570,6 +572,16 @@ namespace ts {
includeInsertTextCompletions?: boolean;
}
export interface InlayHintsOptions extends UserPreferences {
readonly includeInlayParameterNameHints?: "none" | "literals" | "all";
readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean;
readonly includeInlayFunctionParameterTypeHints?: boolean,
readonly includeInlayVariableTypeHints?: boolean;
readonly includeInlayPropertyDeclarationTypeHints?: boolean;
readonly includeInlayFunctionLikeReturnTypeHints?: boolean;
readonly includeInlayEnumMemberValueHints?: boolean;
}
export type SignatureHelpTriggerCharacter = "," | "(" | "<";
export type SignatureHelpRetriggerCharacter = SignatureHelpTriggerCharacter | ")";
@@ -693,6 +705,20 @@ namespace ts {
fromSpans: TextSpan[];
}
export const enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum",
}
export interface InlayHint {
text: string;
position: number;
kind?: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
export interface TodoCommentDescriptor {
text: string;
priority: number;
@@ -1556,4 +1582,13 @@ namespace ts {
triggerReason?: RefactorTriggerReason;
kind?: string;
}
export interface InlayHintsContext {
file: SourceFile;
program: Program;
cancellationToken: CancellationToken;
host: LanguageServiceHost;
span: TextSpan;
preferences: InlayHintsOptions;
}
}
@@ -278,6 +278,7 @@ namespace ts.server {
CommandNames.ToggleMultilineComment,
CommandNames.CommentSelection,
CommandNames.UncommentSelection,
CommandNames.ProvideInlayHints
];
it("should not throw when commands are executed with invalid arguments", () => {
+62 -1
View File
@@ -5676,6 +5676,7 @@ declare namespace ts {
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined;
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences | undefined): InlayHint[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@@ -5737,6 +5738,15 @@ declare namespace ts {
/** @deprecated Use includeCompletionsWithInsertText */
includeInsertTextCompletions?: boolean;
}
interface InlayHintsOptions extends UserPreferences {
readonly includeInlayParameterNameHints?: "none" | "literals" | "all";
readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean;
readonly includeInlayFunctionParameterTypeHints?: boolean;
readonly includeInlayVariableTypeHints?: boolean;
readonly includeInlayPropertyDeclarationTypeHints?: boolean;
readonly includeInlayFunctionLikeReturnTypeHints?: boolean;
readonly includeInlayEnumMemberValueHints?: boolean;
}
type SignatureHelpTriggerCharacter = "," | "(" | "<";
type SignatureHelpRetriggerCharacter = SignatureHelpTriggerCharacter | ")";
interface SignatureHelpItemsOptions {
@@ -5842,6 +5852,18 @@ declare namespace ts {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum"
}
interface InlayHint {
text: string;
position: number;
kind?: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
interface TodoCommentDescriptor {
text: string;
priority: number;
@@ -6507,6 +6529,14 @@ declare namespace ts {
jsxAttributeStringLiteralValue = 24,
bigintLiteral = 25
}
interface InlayHintsContext {
file: SourceFile;
program: Program;
cancellationToken: CancellationToken;
host: LanguageServiceHost;
span: TextSpan;
preferences: InlayHintsOptions;
}
}
declare namespace ts {
/** The classifier is used for syntactic highlighting in editors via the TSServer */
@@ -6798,7 +6828,8 @@ declare namespace ts.server.protocol {
UncommentSelection = "uncommentSelection",
PrepareCallHierarchy = "prepareCallHierarchy",
ProvideCallHierarchyIncomingCalls = "provideCallHierarchyIncomingCalls",
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls"
ProvideCallHierarchyOutgoingCalls = "provideCallHierarchyOutgoingCalls",
ProvideInlayHints = "provideInlayHints"
}
/**
* A TypeScript Server message
@@ -8670,6 +8701,35 @@ declare namespace ts.server.protocol {
interface SignatureHelpResponse extends Response {
body?: SignatureHelpItems;
}
enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum"
}
interface InlayHintsRequestArgs extends FileRequestArgs {
/**
* Start position of the span.
*/
start: number;
/**
* Length of the span.
*/
length: number;
}
interface InlayHintsRequest extends Request {
command: CommandTypes.ProvideInlayHints;
arguments: InlayHintsRequestArgs;
}
interface InlayHintItem {
text: string;
position: Location;
kind?: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
interface InlayHintsResponse extends Response {
body?: InlayHintItem[];
}
/**
* Synchronous request for semantic diagnostics of one file.
*/
@@ -10300,6 +10360,7 @@ declare namespace ts.server {
private getSuggestionDiagnosticsSync;
private getJsxClosingTag;
private getDocumentHighlights;
private provideInlayHints;
private setCompilerOptionsForInferredProjects;
private getProjectInfo;
private getProjectInfoWorker;
+30
View File
@@ -5676,6 +5676,7 @@ declare namespace ts {
prepareCallHierarchy(fileName: string, position: number): CallHierarchyItem | CallHierarchyItem[] | undefined;
provideCallHierarchyIncomingCalls(fileName: string, position: number): CallHierarchyIncomingCall[];
provideCallHierarchyOutgoingCalls(fileName: string, position: number): CallHierarchyOutgoingCall[];
provideInlayHints(fileName: string, span: TextSpan, preferences: UserPreferences | undefined): InlayHint[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];
getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[];
@@ -5737,6 +5738,15 @@ declare namespace ts {
/** @deprecated Use includeCompletionsWithInsertText */
includeInsertTextCompletions?: boolean;
}
interface InlayHintsOptions extends UserPreferences {
readonly includeInlayParameterNameHints?: "none" | "literals" | "all";
readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean;
readonly includeInlayFunctionParameterTypeHints?: boolean;
readonly includeInlayVariableTypeHints?: boolean;
readonly includeInlayPropertyDeclarationTypeHints?: boolean;
readonly includeInlayFunctionLikeReturnTypeHints?: boolean;
readonly includeInlayEnumMemberValueHints?: boolean;
}
type SignatureHelpTriggerCharacter = "," | "(" | "<";
type SignatureHelpRetriggerCharacter = SignatureHelpTriggerCharacter | ")";
interface SignatureHelpItemsOptions {
@@ -5842,6 +5852,18 @@ declare namespace ts {
to: CallHierarchyItem;
fromSpans: TextSpan[];
}
enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum"
}
interface InlayHint {
text: string;
position: number;
kind?: InlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
interface TodoCommentDescriptor {
text: string;
priority: number;
@@ -6507,6 +6529,14 @@ declare namespace ts {
jsxAttributeStringLiteralValue = 24,
bigintLiteral = 25
}
interface InlayHintsContext {
file: SourceFile;
program: Program;
cancellationToken: CancellationToken;
host: LanguageServiceHost;
span: TextSpan;
preferences: InlayHintsOptions;
}
}
declare namespace ts {
/** The classifier is used for syntactic highlighting in editors via the TSServer */
+27
View File
@@ -66,6 +66,12 @@ declare module ts {
Smart = 2,
}
const enum InlayHintKind {
Type = "Type",
Parameter = "Parameter",
Enum = "Enum",
}
enum SemicolonPreference {
Ignore = "ignore",
Insert = "insert",
@@ -396,6 +402,10 @@ declare namespace FourSlashInterface {
start: number;
length: number;
}, displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[], tags: { name: string, text?: string }[] | undefined): void;
getInlayHints(expected: readonly VerifyInlayHintsOptions[], textSpan?: {
start: number;
length: number;
}, preference?: InlayHintsOptions);
getSyntacticDiagnostics(expected: ReadonlyArray<Diagnostic>): void;
getSemanticDiagnostics(expected: ReadonlyArray<Diagnostic>): void;
getSuggestionDiagnostics(expected: ReadonlyArray<Diagnostic>): void;
@@ -633,6 +643,15 @@ declare namespace FourSlashInterface {
readonly importModuleSpecifierPreference?: "shortest" | "project-relative" | "relative" | "non-relative";
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
}
interface InlayHintsOptions extends UserPreferences {
readonly includeInlayParameterNameHints?: "none" | "literals" | "all";
readonly includeInlayParameterNameHintsWhenArgumentMatchesName?: boolean;
readonly includeInlayFunctionParameterTypeHints?: boolean;
readonly includeInlayVariableTypeHints?: boolean;
readonly includeInlayPropertyDeclarationTypeHints?: boolean;
readonly includeInlayFunctionLikeReturnTypeHints?: boolean;
readonly includeInlayEnumMemberValueHints?: boolean;
}
interface CompletionsOptions {
readonly marker?: ArrayOrSingle<string | Marker>;
readonly isNewIdentifierLocation?: boolean;
@@ -735,6 +754,14 @@ declare namespace FourSlashInterface {
readonly commands?: ReadonlyArray<{}>;
}
export interface VerifyInlayHintsOptions {
text: string;
position: number;
kind?: VerifyInlayHintKind;
whitespaceBefore?: boolean;
whitespaceAfter?: boolean;
}
interface VerifyNavigateToOptions {
readonly pattern: string;
readonly fileName?: string;
@@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, b: number) {}
//// foo(/*a*/1, /*b*/2);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,8 @@
/// <reference path="fourslash.ts" />
//// declare const unknownCall: any;
//// unknownCall();
verify.getInlayHints([], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,26 @@
/// <reference path="fourslash.ts" />
//// function foo(a: number) {
//// return (b: number) => {
//// return a + b
//// }
//// }
//// foo(/*a*/1)(/*b*/2);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,25 @@
/// <reference path="fourslash.ts" />
//// function foo(a: (b: number) => number) {
//// return a(/*a*/1) + 2
//// }
//// foo(/*b*/(c: number) => c + 1);
const markers = test.markers();
verify.getInlayHints([
{
text: 'b:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'a:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,18 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, b: number) {}
//// declare const a: 1;
//// foo(a, /*b*/2);
const markers = test.markers();
verify.getInlayHints([
{
text: 'b:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
], undefined, {
includeInlayParameterNameHints: "all",
includeInlayParameterNameHintsWhenArgumentMatchesName: false,
});
@@ -0,0 +1,8 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, b: number) {}
//// foo(1, 2);
verify.getInlayHints([], undefined, {
includeInlayParameterNameHints: "none"
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a/*a*/ = 123;
const markers = test.markers();
verify.getInlayHints([
{
text: ': 123',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a/*a*/ = 123;
const markers = test.markers();
verify.getInlayHints([
{
text: ': 123',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a/*a*/ = { a: 123 };
const markers = test.markers();
verify.getInlayHints([
{
text: ': { a: number; }',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// class Class {}
//// const a/*a*/ = new Class();
const markers = test.markers();
verify.getInlayHints([
{
text: ': Class',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a/*a*/ = () => 123;
const markers = test.markers();
verify.getInlayHints([
{
text: ': () => number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, { c }: any) {}
//// foo(/*a*/1, { c: 1});
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />
//// const a = 123;
verify.getInlayHints([], undefined, {
includeInlayVariableTypeHints: false
});
@@ -0,0 +1,7 @@
/// <reference path="fourslash.ts" />
//// const a;
verify.getInlayHints([], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a/*a*/ = "I'm very very very very very very very very very long";
const markers = test.markers();
verify.getInlayHints([
{
text: `: "I'm very very very very ve...`,
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// function foo (Im_very_very_very_very_very_very_very_long: number) {}
//// foo(/*a*/1);
const markers = test.markers();
verify.getInlayHints([
{
text: 'Im_very_very_very_very_very...:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
//// type F = (a: string, b: number) => void
//// const f: F = (a/*a*/, b/*b*/) => { }
const markers = test.markers();
verify.getInlayHints([
{
text: ': string',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': number',
position: markers[1].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// function foo (cb: (a: string) => void) {}
//// foo((a/*a*/) => { })
const markers = test.markers();
verify.getInlayHints([
{
text: ': string',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// function foo (cb: (a: Exclude<1 | 2 | 3, 1>) => void) {}
//// foo((a/*a*/) => { })
const markers = test.markers();
verify.getInlayHints([
{
text: ': 2 | 3',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,24 @@
/// <reference path="fourslash.ts" />
//// function foo (a: (b: (c: (d: Exclude<1 | 2 | 3, 1>) => void) => void) => void) {}
//// foo(a/*a*/ => {
//// a(d/*b*/ => {})
//// })
const markers = test.markers();
verify.getInlayHints([
{
text: ': (c: (d: 2 | 3) => void) => ...',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': 2 | 3',
position: markers[1].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// type F = (a: string, b: number) => void
//// const f: F = (a/*a*/, b: number) => { }
const markers = test.markers();
verify.getInlayHints([
{
text: ': string',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,37 @@
/// <reference path="fourslash.ts" />
//// function foo (a: (b: (c: (d: Exclude<1 | 2 | 3, 1>) => void) => void) => void) {}
//// foo(/*a*/a/*b*/ => {
//// a(/*c*/d/*d*/ => {})
//// })
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: ': (c: (d: 2 | 3) => void) => ...',
position: markers[1].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: 'c:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: ': 2 | 3',
position: markers[3].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayParameterNameHints: "literals",
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, ...b: number[]) {}
//// foo(/*a*/1, /*b*/1, 1, 1);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: '...b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// function f<T>(v: T, a: (v: T) => void) {}
//// f(1, a/*a*/ => { })
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
//// type F = (a: {
//// a: number
//// b: string
//// }) => void
//// const f: F = (a/*a*/) => { }
const markers = test.markers();
verify.getInlayHints([
{
text: ': { a: number; b: string; }',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayFunctionParameterTypeHints: true
});
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
//// function foo1 (a: number, b: number) {}
//// function foo2 (c: number, d: number) {}
//// function foo3 (e: number, f: number) {}
//// function foo4 (g: number, h: number) {}
//// function foo5 (i: number, j: number) {}
//// function foo6 (k: number, i: number) {}
//// function c1 () { foo1(/*a*/1, /*b*/2); }
//// function c2 () { foo2(/*c*/1, /*d*/2); }
//// function c3 () { foo3(/*e*/1, /*f*/2); }
//// function c4 () { foo4(/*g*/1, /*h*/2); }
//// function c5 () { foo5(/*i*/1, /*j*/2); }
//// function c6 () { foo6(/*k*/1, /*l*/2); }
const start = test.markerByName('c');
const end = test.markerByName('h');
const span = { start: start.position, length: end.position - start.position };
verify.getInlayHints(
['c', 'd', 'e', 'f', 'g', 'h'].map(mark => {
return {
text: `${mark}:`,
position: test.markerByName(mark).position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
}), span, {
includeInlayParameterNameHints: "literals"
})
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />
//// function foo1 (a: number, b: number) {}
//// function foo2 (c: number, d: number) {}
//// function foo3 (e: number, f: number) {}
//// function foo4 (g: number, h: number) {}
//// function foo5 (i: number, j: number) {}
//// function foo6 (k: number, l: number) {}
//// foo1(/*a*/1, /*b*/2);
//// foo2(/*c*/1, /*d*/2);
//// foo3(/*e*/1, /*f*/2);
//// foo4(/*g*/1, /*h*/2);
//// foo5(/*i*/1, /*j*/2);
//// foo6(/*k*/1, /*l*/2);
const start = test.markerByName('c');
const end = test.markerByName('h');
const span = { start: start.position, length: end.position - start.position };
verify.getInlayHints(
['c', 'd', 'e', 'f', 'g', 'h'].map(mark => {
return {
text: `${mark}:`,
position: test.markerByName(mark).position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
}), span, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,29 @@
/// <reference path="fourslash.ts" />
//// function foo (v: any) {}
//// foo(/*a*/1);
//// foo(/*b*/'');
//// foo(/*c*/true);
//// foo(/*d*/() => 1);
//// foo(/*e*/function () { return 1 });
//// foo(/*f*/{});
//// foo(/*g*/{ a: 1 });
//// foo(/*h*/[]);
//// foo(/*i*/[1]);
//// foo(foo);
//// foo(/*j*/(1));
//// foo(foo(/*k*/1));
const markers = test.markers();
verify.getInlayHints(
markers.map(m => ({
text: 'v:',
position: m.position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
})) , undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,29 @@
/// <reference path="fourslash.ts" />
//// function foo (v: any) {}
//// foo(/*a*/1);
//// foo(/*b*/'');
//// foo(/*c*/true);
//// foo(/*d*/() => 1);
//// foo(/*e*/function () { return 1 });
//// foo(/*f*/{});
//// foo(/*g*/{ a: 1 });
//// foo(/*h*/[]);
//// foo(/*i*/[1]);
//// foo(/*j*/foo);
//// foo(/*k*/(1));
//// foo(/*l*/foo(/*m*/1));
const markers = test.markers();
verify.getInlayHints(
markers.map(m => ({
text: 'v:',
position: m.position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
})) , undefined, {
includeInlayParameterNameHints: "all"
});
@@ -0,0 +1,24 @@
/// <reference path="fourslash.ts" />
//// function foo (a: number, b: number) {}
//// declare const a: 1;
//// foo(/*a*/a, /*b*/2);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
], undefined, {
includeInlayParameterNameHints: "all",
includeInlayParameterNameHintsWhenArgumentMatchesName: true,
});
@@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
//// class C {
//// a/*a*/ = 1
//// b: number = 2
//// c;
//// }
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayPropertyDeclarationTypeHints: true,
});
@@ -0,0 +1,17 @@
/// <reference path="fourslash.ts" />
//// function foo ()/*a*/ {
//// return 1
//// }
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,9 @@
/// <reference path="fourslash.ts" />
//// function foo (): number {
//// return 1
//// }
verify.getInlayHints([], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,32 @@
/// <reference path="fourslash.ts" />
//// declare function foo(w: number): void
//// declare function foo(a: number, b: number): void;
//// declare function foo(a: number | undefined, b: number | undefined): void;
//// foo(/*a*/1)
//// foo(/*b*/1, /*c*/2)
const markers = test.markers();
verify.getInlayHints([
{
text: 'w:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'a:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,19 @@
/// <reference path="fourslash.ts" />
//// class C {
//// foo()/*a*/ {
//// return 1
//// }
//// }
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a = ()/*a*/ => 1
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
//// const a = function ()/*a*/ { return 1}
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// const a = (b)/*a*/ => 1
//// const aa = b => 1
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayFunctionLikeReturnTypeHints: true,
});
@@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
//// enum E {
//// A/*a*/,
//// AA/*b*/,
//// B = 10,
//// BB/*c*/,
//// C = 'C',
//// }
const markers = test.markers();
verify.getInlayHints([
{
text: '= 0',
position: markers[0].position,
kind: ts.InlayHintKind.Enum,
whitespaceBefore: true
},
{
text: '= 1',
position: markers[1].position,
kind: ts.InlayHintKind.Enum,
whitespaceBefore: true
},
{
text: '= 11',
position: markers[2].position,
kind: ts.InlayHintKind.Enum,
whitespaceBefore: true
},
], undefined, {
includeInlayEnumMemberValueHints: true,
});
@@ -0,0 +1,15 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
//// module.exports.a = 1
// @Filename: /b.js
//// const a = require('./a');
goTo.file('/b.js')
verify.getInlayHints([], undefined, {
includeInlayVariableTypeHints: true,
});
@@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
//// module.exports.a = 1
// @Filename: /b.js
//// function foo () { return require('./a'); }
//// function bar ()/*a*/ { return require('./a').a; }
//// const c = foo()
//// const d/*b*/ = bar()
goTo.file('/b.js')
const markers = test.markers();
verify.getInlayHints([
{
text: ': number',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': number',
position: markers[1].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true,
includeInlayFunctionLikeReturnTypeHints: true
});
@@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
//// var x
//// x.foo(1, 2);
//// /**
//// * @type {{foo: (a: number, b: number) => void}}
//// */
//// var y
//// y.foo(/*a*/1, /*b*/2)
goTo.file('/a.js')
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// declare function foo<T extends number>(t: T): T
//// const x/*a*/ = foo(1)
const markers = test.markers();
verify.getInlayHints([
{
text: `: 1`,
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
], undefined, {
includeInlayVariableTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
// @allowJs: true
// @checkJs: true
// @Filename: /a.js
//// /**
//// * @type {string}
//// */
//// var x = ""
goTo.file('/a.js')
verify.getInlayHints([], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,29 @@
/// <reference path="fourslash.ts" />
//// type Args = [a: number, b: number]
//// declare function foo(c: number, ...args: Args);
//// foo(/*a*/1, /*b*/2, /*c*/3)
const markers = test.markers();
verify.getInlayHints([
{
text: 'c:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'a:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,49 @@
/// <reference path="fourslash.ts" />
//// type T = [a: string, b: boolean, ...c: number[]]
//// declare function foo(f: number, ...args: T):void
//// declare function foo1(f1: number, ...args: string[]): void
//// foo(/*f*/1, /*a*/'', /*b*/false, /*c*/1, 2)
//// foo1(/*f1*/1, /*args*/"", "")
const markers = test.markers();
verify.getInlayHints([
{
text: 'f:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'a:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: '...c:',
position: markers[3].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'f1:',
position: markers[4].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: '...args:',
position: markers[5].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,46 @@
/// <reference path="fourslash.ts" />
// @Filename: /a.ts
//// export interface Foo { a: string }
// @Filename: /b.ts
//// async function foo ()/*a*/ {
//// return {} as any as import('./a').Foo
//// }
//// function bar ()/*b*/ { return import('./a') }
//// async function main ()/*c*/ {
//// const a/*d*/ = await foo()
//// const b = await bar()
//// }
goTo.file('/b.ts')
const markers = test.markers();
verify.getInlayHints([
{
text: ': Promise<Foo>',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': Promise<typeof import("/a")>',
position: markers[1].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': Promise<void>',
position: markers[2].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: ': Foo',
position: markers[3].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
}
], undefined, {
includeInlayVariableTypeHints: true,
includeInlayFunctionLikeReturnTypeHints: true
});
@@ -0,0 +1,78 @@
/// <reference path="fourslash.ts" />
//// function foo (aParameter: number, bParameter: number, cParameter: number)/*f*/ { }
//// foo(
//// /** aParameter */
//// 1,
//// // bParameter
//// /*a*/2,
//// /* cParameter */
//// 3
//// )
//// foo(
//// /** multiple comments */
//// /** aParameter */
//// 1,
//// /** bParameter */
//// /** multiple comments */
//// 2,
//// // cParameter
//// /** multiple comments */
//// /*b*/3
//// )
//// foo(
//// /** wrong name */
//// /*c*/1,
//// /*d*/2,
//// /** multiple */
//// /** wrong */
//// /** name */
//// /*e*/3
//// )
const markers = test.markers();
verify.getInlayHints([
{
text: ': void',
position: markers[0].position,
kind: ts.InlayHintKind.Type,
whitespaceBefore: true
},
{
text: 'bParameter:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'cParameter:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'aParameter:',
position: markers[3].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'bParameter:',
position: markers[4].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'cParameter:',
position: markers[5].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals",
includeInlayFunctionLikeReturnTypeHints: true
});
@@ -0,0 +1,16 @@
/// <reference path="fourslash.ts" />
//// const fn = (x: any) => { }
//// fn(/* nobody knows exactly what this param is */ /*a*/42);
const markers = test.markers();
verify.getInlayHints([
{
text: 'x:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,17 @@
/// <reference path="fourslash.ts" />
//// type Args = [number, number]
//// declare function foo(c: number, ...args: Args);
//// foo(/*a*/1, 2, 3)
const markers = test.markers();
verify.getInlayHints([
{
text: 'c:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
//// interface Call {
//// (a: number): void
//// (b: number, c: number): void
//// }
//// declare const call: Call;
//// call(/*a*/1);
//// call(/*b*/1, /*c*/2);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'c:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,33 @@
/// <reference path="fourslash.ts" />
//// class Class {
//// constructor(a: number);
//// constructor(b: number, c: number);
//// constructor(b: number, c?: number) { }
//// }
//// new Class(/*a*/1)
//// new Class(/*b*/1, /*c*/2)
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'c:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});
@@ -0,0 +1,41 @@
/// <reference path="fourslash.ts" />
//// interface Call {
//// (a: number): void
//// (b: number, c: number): void
//// new (d: number): Call
//// }
//// declare const call: Call;
//// call(/*a*/1);
//// call(/*b*/1, /*c*/2);
//// new call(/*d*/1);
const markers = test.markers();
verify.getInlayHints([
{
text: 'a:',
position: markers[0].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'b:',
position: markers[1].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'c:',
position: markers[2].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
},
{
text: 'd:',
position: markers[3].position,
kind: ts.InlayHintKind.Parameter,
whitespaceAfter: true
}
], undefined, {
includeInlayParameterNameHints: "literals"
});