Skip to content

Commit abd6cb4

Browse files
authored
Improve quickinfo/signature/completions baselines (#52308)
1 parent 6622308 commit abd6cb4

File tree

155 files changed

+15444
-1122
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

155 files changed

+15444
-1122
lines changed

src/harness/fourslashImpl.ts

+94-6
Original file line numberDiff line numberDiff line change
@@ -1972,18 +1972,53 @@ export class TestState {
19721972
const baselineFile = this.getBaselineFileNameForContainingTestFile();
19731973
const result = ts.arrayFrom(this.testData.markerPositions.entries(), ([name, marker]) => ({
19741974
marker: { ...marker, name },
1975-
quickInfo: this.languageService.getQuickInfoAtPosition(marker.fileName, marker.position)
1975+
item: this.languageService.getQuickInfoAtPosition(marker.fileName, marker.position)
19761976
}));
1977-
Harness.Baseline.runBaseline(baselineFile, stringify(result));
1977+
const annotations = this.annotateContentWithTooltips(
1978+
result,
1979+
"quickinfo",
1980+
item => item.textSpan,
1981+
({ displayParts, documentation, tags }) => [
1982+
...(displayParts ? displayParts.map(p => p.text).join("").split("\n") : []),
1983+
...(documentation?.length ? documentation.map(p => p.text).join("").split("\n") : []),
1984+
...(tags?.length ? tags.map(p => `@${p.name} ${p.text?.map(dp => dp.text).join("") ?? ""}`).join("\n").split("\n") : [])
1985+
]);
1986+
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
19781987
}
19791988

19801989
public baselineSignatureHelp() {
19811990
const baselineFile = this.getBaselineFileNameForContainingTestFile();
19821991
const result = ts.arrayFrom(this.testData.markerPositions.entries(), ([name, marker]) => ({
19831992
marker: { ...marker, name },
1984-
signatureHelp: this.languageService.getSignatureHelpItems(marker.fileName, marker.position, /*options*/ undefined)
1993+
item: this.languageService.getSignatureHelpItems(marker.fileName, marker.position, /*options*/ undefined)
19851994
}));
1986-
Harness.Baseline.runBaseline(baselineFile, stringify(result));
1995+
const annotations = this.annotateContentWithTooltips(
1996+
result,
1997+
"signature help",
1998+
() => undefined, // use default: marker.position
1999+
(item, previous) => {
2000+
const { documentation, tags, prefixDisplayParts, suffixDisplayParts, separatorDisplayParts, parameters } = item.items[item.selectedItemIndex];
2001+
const tooltip = [];
2002+
let signature = "";
2003+
if (prefixDisplayParts.length) signature += prefixDisplayParts.map(p => p.text).join("");
2004+
const separator = separatorDisplayParts.map(p => p.text).join("");
2005+
signature += parameters.map((p, i) => {
2006+
const text = p.displayParts.map(dp => dp.text).join("");
2007+
return i === item.argumentIndex ? "**" + text + "**" : text;
2008+
}).join(separator);
2009+
if (suffixDisplayParts.length) signature += suffixDisplayParts.map(p => p.text).join("");
2010+
tooltip.push(signature);
2011+
// only display signature documentation on the last argument when multiple arguments are marked
2012+
if (previous?.applicableSpan.start !== item.applicableSpan.start) {
2013+
if (documentation?.length) tooltip.push(...documentation.map(p => p.text).join("").split("\n"));
2014+
if (tags?.length) {
2015+
tooltip.push(...tags.map(p => `@${p.name} ${p.text?.map(dp => dp.text).join("") ?? ""}`).join("\n").split("\n"));
2016+
}
2017+
}
2018+
return tooltip;
2019+
}
2020+
);
2021+
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
19872022
}
19882023

19892024
public baselineCompletions(preferences?: ts.UserPreferences) {
@@ -1993,7 +2028,7 @@ export class TestState {
19932028
const completions = this.getCompletionListAtCaret(preferences);
19942029
return {
19952030
marker: { ...marker, name },
1996-
completionList: {
2031+
item: {
19972032
...completions,
19982033
entries: completions?.entries.map(entry => ({
19992034
...entry,
@@ -2002,7 +2037,60 @@ export class TestState {
20022037
}
20032038
};
20042039
});
2005-
Harness.Baseline.runBaseline(baselineFile, stringify(result));
2040+
const annotations = this.annotateContentWithTooltips(
2041+
result,
2042+
"completions",
2043+
item => item.optionalReplacementSpan,
2044+
item => item.entries?.flatMap(
2045+
entry => entry.displayParts
2046+
? entry.displayParts.map(p => p.text).join("").split("\n")
2047+
: [`(${entry.kindModifiers}${entry.kind}) ${entry.name}`])
2048+
);
2049+
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
2050+
}
2051+
2052+
private annotateContentWithTooltips<T extends ts.QuickInfo | ts.SignatureHelpItems | {
2053+
optionalReplacementSpan?: ts.TextSpan,
2054+
entries?: {
2055+
name: string,
2056+
kind: string,
2057+
kindModifiers?: string,
2058+
displayParts?: unknown,
2059+
}[]
2060+
}>(
2061+
items: ({
2062+
marker: Marker & { name: string },
2063+
item: T | undefined
2064+
})[],
2065+
opName: "completions" | "quickinfo" | "signature help",
2066+
getSpan: (t: T) => ts.TextSpan | undefined,
2067+
getToolTipContents: (t: T, prev: T | undefined) => string[] | undefined): string {
2068+
const bar = "-".repeat(70);
2069+
const sorted = items.slice();
2070+
// sort by file, then *backwards* by position in the file so I can insert multiple times on a line without counting
2071+
sorted.sort((q1, q2) =>
2072+
q1.marker.fileName === q1.marker.fileName
2073+
? (q1.marker.position > q2.marker.position ? -1 : 1)
2074+
: (q1.marker.fileName > q1.marker.fileName ? 1 : -1));
2075+
const files: Map<string, string[]> = new Map();
2076+
let previous: T | undefined;
2077+
for (const { marker, item } of sorted) {
2078+
const span = (item ? getSpan(item) : undefined) ?? { start: marker.position, length: 1 };
2079+
const startLc = this.languageServiceAdapterHost.positionToLineAndCharacter(marker.fileName, span.start);
2080+
const underline = " ".repeat(startLc.character) + "^".repeat(span.length);
2081+
let tooltip = [
2082+
bar,
2083+
...(item ? getToolTipContents(item, previous) : undefined) ?? [`No ${opName} at /*${marker.name}*/.`],
2084+
bar,
2085+
];
2086+
tooltip = tooltip.map(l => "| " + l);
2087+
const lines = files.get(marker.fileName) ?? this.getFileContent(marker.fileName).split(/\r?\n/);
2088+
lines.splice(startLc.line + 1, 0, underline, ...tooltip);
2089+
files.set(marker.fileName, lines);
2090+
previous = item;
2091+
}
2092+
return Array.from(files.entries(), ([fileName, lines]) => `=== ${fileName} ===\n` + lines.map(l => "// " + l).join("\n"))
2093+
.join("\n\n");
20062094
}
20072095

20082096
public baselineSmartSelection() {

tests/baselines/reference/completionDetailsOfContextSensitiveParameterNoCrash.baseline

+93-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,103 @@
1+
=== /tests/cases/fourslash/completionDetailsOfContextSensitiveParameterNoCrash.ts ===
2+
// type __ = never;
3+
//
4+
// interface CurriedFunction1<T1, R> {
5+
// (): CurriedFunction1<T1, R>;
6+
// (t1: T1): R;
7+
// }
8+
// interface CurriedFunction2<T1, T2, R> {
9+
// (): CurriedFunction2<T1, T2, R>;
10+
// (t1: T1): CurriedFunction1<T2, R>;
11+
// (t1: __, t2: T2): CurriedFunction1<T1, R>;
12+
// (t1: T1, t2: T2): R;
13+
// }
14+
//
15+
// interface CurriedFunction3<T1, T2, T3, R> {
16+
// (): CurriedFunction3<T1, T2, T3, R>;
17+
// (t1: T1): CurriedFunction2<T2, T3, R>;
18+
// (t1: __, t2: T2): CurriedFunction2<T1, T3, R>;
19+
// (t1: T1, t2: T2): CurriedFunction1<T3, R>;
20+
// (t1: __, t2: __, t3: T3): CurriedFunction2<T1, T2, R>;
21+
// (t1: T1, t2: __, t3: T3): CurriedFunction1<T2, R>;
22+
// (t1: __, t2: T2, t3: T3): CurriedFunction1<T1, R>;
23+
// (t1: T1, t2: T2, t3: T3): R;
24+
// }
25+
//
26+
// interface CurriedFunction4<T1, T2, T3, T4, R> {
27+
// (): CurriedFunction4<T1, T2, T3, T4, R>;
28+
// (t1: T1): CurriedFunction3<T2, T3, T4, R>;
29+
// (t1: __, t2: T2): CurriedFunction3<T1, T3, T4, R>;
30+
// (t1: T1, t2: T2): CurriedFunction2<T3, T4, R>;
31+
// (t1: __, t2: __, t3: T3): CurriedFunction3<T1, T2, T4, R>;
32+
// (t1: __, t2: __, t3: T3): CurriedFunction2<T2, T4, R>;
33+
// (t1: __, t2: T2, t3: T3): CurriedFunction2<T1, T4, R>;
34+
// (t1: T1, t2: T2, t3: T3): CurriedFunction1<T4, R>;
35+
// (t1: __, t2: __, t3: __, t4: T4): CurriedFunction3<T1, T2, T3, R>;
36+
// (t1: T1, t2: __, t3: __, t4: T4): CurriedFunction2<T2, T3, R>;
37+
// (t1: __, t2: T2, t3: __, t4: T4): CurriedFunction2<T1, T3, R>;
38+
// (t1: __, t2: __, t3: T3, t4: T4): CurriedFunction2<T1, T2, R>;
39+
// (t1: T1, t2: T2, t3: __, t4: T4): CurriedFunction1<T3, R>;
40+
// (t1: T1, t2: __, t3: T3, t4: T4): CurriedFunction1<T2, R>;
41+
// (t1: __, t2: T2, t3: T3, t4: T4): CurriedFunction1<T1, R>;
42+
// (t1: T1, t2: T2, t3: T3, t4: T4): R;
43+
// }
44+
//
45+
// declare var curry: {
46+
// <T1, R>(func: (t1: T1) => R, arity?: number): CurriedFunction1<T1, R>;
47+
// <T1, T2, R>(func: (t1: T1, t2: T2) => R, arity?: number): CurriedFunction2<T1, T2, R>;
48+
// <T1, T2, T3, R>(func: (t1: T1, t2: T2, t3: T3) => R, arity?: number): CurriedFunction3<T1, T2, T3, R>;
49+
// <T1, T2, T3, T4, R>(func: (t1: T1, t2: T2, t3: T3, t4: T4) => R, arity?: number): CurriedFunction4<T1, T2, T3, T4, R>;
50+
// (func: (...args: any[]) => any, arity?: number): (...args: any[]) => any;
51+
// placeholder: __;
52+
// };
53+
//
54+
// export type StylingFunction = (
55+
// keys: (string | false | undefined) | (string | false | undefined)[],
56+
// ...rest: unknown[]
57+
// ) => object;
58+
//
59+
// declare const getStylingByKeys: (
60+
// mergedStyling: object,
61+
// keys: (string | false | undefined) | (string | false | undefined)[],
62+
// ...args: unknown[]
63+
// ) => object;
64+
//
65+
// declare var mergedStyling: object;
66+
//
67+
// export const createStyling: CurriedFunction3<
68+
// (base16Theme: object) => unknown,
69+
// object | undefined,
70+
// object | undefined,
71+
// StylingFunction
72+
// > = curry<
73+
// (base16Theme: object) => unknown,
74+
// object | undefined,
75+
// object | undefined,
76+
// StylingFunction
77+
// >(
78+
// (
79+
// getStylingFromBase16: (base16Theme: object) => unknown,
80+
// options: object = {},
81+
// themeOrStyling: object = {},
82+
// ...args
83+
// ): StylingFunction => {
84+
// return curry(getStylingByKeys, 2)(mergedStyling, ...args);
85+
// ^^^^
86+
// | ----------------------------------------------------------------------
87+
// | (parameter) args: any
88+
// | ----------------------------------------------------------------------
89+
// },
90+
// 3
91+
// );
92+
193
[
294
{
395
"marker": {
496
"fileName": "/tests/cases/fourslash/completionDetailsOfContextSensitiveParameterNoCrash.ts",
597
"position": 3101,
698
"name": ""
799
},
8-
"quickInfo": {
100+
"item": {
9101
"kind": "parameter",
10102
"kindModifiers": "",
11103
"textSpan": {

tests/baselines/reference/completionEntryForUnionMethod.baseline

+58-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,68 @@
1+
=== /tests/cases/fourslash/completionEntryForUnionMethod.ts ===
2+
// var y: Array<string>|Array<number>;
3+
// y.map(
4+
// ^^^
5+
// | ----------------------------------------------------------------------
6+
// | (property) Array<T>.concat: {
7+
// | (...items: ConcatArray<string>[]): string[];
8+
// | (...items: (string | ConcatArray<string>)[]): string[];
9+
// | } | {
10+
// | (...items: ConcatArray<number>[]): number[];
11+
// | (...items: (number | ConcatArray<...>)[]): number[];
12+
// | }
13+
// | (property) Array<T>.every: {
14+
// | <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[];
15+
// | (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
16+
// | } | {
17+
// | ...;
18+
// | }
19+
// | (property) Array<T>.filter: {
20+
// | <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S[];
21+
// | (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[];
22+
// | } | {
23+
// | ...;
24+
// | }
25+
// | (method) Array<T>.forEach(callbackfn: ((value: string, index: number, array: string[]) => void) & ((value: number, index: number, array: number[]) => void), thisArg: any): void
26+
// | (method) Array<T>.indexOf(searchElement: never, fromIndex: number): number
27+
// | (method) Array<T>.join(separator?: string): string
28+
// | (method) Array<T>.lastIndexOf(searchElement: never, fromIndex: number): number
29+
// | (property) Array<T>.length: number
30+
// | (method) Array<T>.map<unknown>(callbackfn: ((value: string, index: number, array: string[]) => unknown) & ((value: number, index: number, array: number[]) => unknown), thisArg: any): unknown[]
31+
// | (method) Array<T>.pop(): string | number
32+
// | (method) Array<T>.push(items: never[]): number
33+
// | (property) Array<T>.reduce: {
34+
// | (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
35+
// | (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
36+
// | <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U;
37+
// | } | {
38+
// | ...;
39+
// | }
40+
// | (property) Array<T>.reduceRight: {
41+
// | (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
42+
// | (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
43+
// | <U>(callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U;
44+
// | } | {
45+
// | ...;
46+
// | }
47+
// | (method) Array<T>.reverse(): string[] | number[]
48+
// | (method) Array<T>.shift(): string | number
49+
// | (method) Array<T>.slice(start?: number, end?: number): string[] | number[]
50+
// | (method) Array<T>.some(predicate: ((value: string, index: number, array: string[]) => unknown) & ((value: number, index: number, array: number[]) => unknown), thisArg: any): boolean
51+
// | (method) Array<T>.sort(compareFn: ((a: string, b: string) => number) & ((a: number, b: number) => number)): string[] | number[]
52+
// | (method) Array<T>.splice(start: number, deleteCount?: number): string[] | number[] (+2 overloads)
53+
// | (method) Array<T>.toLocaleString(): string
54+
// | (method) Array<T>.toString(): string
55+
// | (method) Array<T>.unshift(items: never[]): number
56+
// | ----------------------------------------------------------------------
57+
158
[
259
{
360
"marker": {
461
"fileName": "/tests/cases/fourslash/completionEntryForUnionMethod.ts",
562
"position": 41,
663
"name": ""
764
},
8-
"completionList": {
65+
"item": {
966
"flags": 0,
1067
"isGlobalCompletion": false,
1168
"isMemberCompletion": true,

tests/baselines/reference/completionForStringLiteralImport3.baseline

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
=== /a.ts ===
2+
// import * as foo from "";
3+
// ^
4+
// | ----------------------------------------------------------------------
5+
// | ----------------------------------------------------------------------
6+
17
[
28
{
39
"marker": {
410
"fileName": "/a.ts",
511
"position": 22,
612
"name": ""
713
},
8-
"completionList": {
14+
"item": {
915
"isGlobalCompletion": false,
1016
"isMemberCompletion": false,
1117
"isNewIdentifierLocation": true,

tests/baselines/reference/completionImportCallAssertion.baseline

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1+
=== /tests/cases/fourslash/main.ts ===
2+
// import("./other.json", {});
3+
// ^
4+
// | ----------------------------------------------------------------------
5+
// | (property) ImportCallOptions.assert?: ImportAssertions
6+
// | ----------------------------------------------------------------------
7+
// import("./other.json", { asse});
8+
// ^^^^
9+
// | ----------------------------------------------------------------------
10+
// | (property) ImportCallOptions.assert?: ImportAssertions
11+
// | ----------------------------------------------------------------------
12+
113
[
214
{
315
"marker": {
416
"fileName": "/tests/cases/fourslash/main.ts",
517
"position": 24,
618
"name": "0"
719
},
8-
"completionList": {
20+
"item": {
921
"flags": 0,
1022
"isGlobalCompletion": false,
1123
"isMemberCompletion": true,
@@ -73,7 +85,7 @@
7385
"position": 57,
7486
"name": "1"
7587
},
76-
"completionList": {
88+
"item": {
7789
"flags": 0,
7890
"isGlobalCompletion": false,
7991
"isMemberCompletion": true,

tests/baselines/reference/completionsClassMembers1.baseline

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
1+
=== /tests/cases/fourslash/completionsClassMembers1.ts ===
2+
// interface I {
3+
// method(): void;
4+
// }
5+
//
6+
// export class C implements I {
7+
// property = "foo"
8+
//
9+
// ^
10+
// | ----------------------------------------------------------------------
11+
// | (method) I.method(): void
12+
// | abstract
13+
// | accessor
14+
// | async
15+
// | constructor
16+
// | declare
17+
// | get
18+
// | override
19+
// | private
20+
// | protected
21+
// | public
22+
// | readonly
23+
// | set
24+
// | static
25+
// | ----------------------------------------------------------------------
26+
// }
27+
128
[
229
{
330
"marker": {
431
"fileName": "/tests/cases/fourslash/completionsClassMembers1.ts",
5-
"position": 93,
32+
"position": 92,
633
"name": ""
734
},
8-
"completionList": {
35+
"item": {
936
"flags": 0,
1037
"isGlobalCompletion": false,
1138
"isMemberCompletion": true,

0 commit comments

Comments
 (0)