Skip to content

Commit 0fbe983

Browse files
committed
Merge branch 'master' into strictObjectLiterals
2 parents e483ea5 + 940175e commit 0fbe983

50 files changed

Lines changed: 747 additions & 271 deletions

File tree

Some content is hidden

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

src/harness/fourslash.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -783,13 +783,13 @@ namespace FourSlash {
783783
});
784784
}
785785

786-
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
786+
public verifyCompletionListContains(entryId: ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
787787
const completions = this.getCompletionListAtCaret();
788788
if (completions) {
789-
this.assertItemInCompletionList(completions.entries, symbol, text, documentation, kind, spanIndex, hasAction);
789+
this.assertItemInCompletionList(completions.entries, entryId, text, documentation, kind, spanIndex, hasAction);
790790
}
791791
else {
792-
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${symbol}'.`);
792+
this.raiseError(`No completions at position '${this.currentCaretPosition}' when looking for '${JSON.stringify(entryId)}'.`);
793793
}
794794
}
795795

@@ -804,7 +804,7 @@ namespace FourSlash {
804804
* @param expectedKind the kind of symbol (see ScriptElementKind)
805805
* @param spanIndex the index of the range that the completion item's replacement text span should match
806806
*/
807-
public verifyCompletionListDoesNotContain(symbol: string, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) {
807+
public verifyCompletionListDoesNotContain(entryId: ts.Completions.CompletionEntryIdentifier, expectedText?: string, expectedDocumentation?: string, expectedKind?: string, spanIndex?: number) {
808808
const that = this;
809809
let replacementSpan: ts.TextSpan;
810810
if (spanIndex !== undefined) {
@@ -833,14 +833,14 @@ namespace FourSlash {
833833

834834
const completions = this.getCompletionListAtCaret();
835835
if (completions) {
836-
let filterCompletions = completions.entries.filter(e => e.name === symbol);
836+
let filterCompletions = completions.entries.filter(e => e.name === entryId.name && e.source === entryId.source);
837837
filterCompletions = expectedKind ? filterCompletions.filter(e => e.kind === expectedKind) : filterCompletions;
838838
filterCompletions = filterCompletions.filter(filterByTextOrDocumentation);
839839
if (filterCompletions.length !== 0) {
840840
// After filtered using all present criterion, if there are still symbol left in the list
841841
// then these symbols must meet the criterion for Not supposed to be in the list. So we
842842
// raise an error
843-
let error = "Completion list did contain \'" + symbol + "\'.";
843+
let error = `Completion list did contain '${JSON.stringify(entryId)}\'.`;
844844
const details = this.getCompletionEntryDetails(filterCompletions[0].name);
845845
if (expectedText) {
846846
error += "Expected text: " + expectedText + " to equal: " + ts.displayPartsToString(details.displayParts) + ".";
@@ -1130,8 +1130,8 @@ Actual: ${stringify(fullActual)}`);
11301130
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
11311131
}
11321132

1133-
private getCompletionEntryDetails(entryName: string) {
1134-
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings);
1133+
private getCompletionEntryDetails(entryName: string, source?: string) {
1134+
return this.languageService.getCompletionEntryDetails(this.activeFile.fileName, this.currentCaretPosition, entryName, this.formatCodeSettings, source);
11351135
}
11361136

11371137
private getReferencesAtCaret() {
@@ -1640,7 +1640,7 @@ Actual: ${stringify(fullActual)}`);
16401640
const longestNameLength = max(entries, m => m.name.length);
16411641
const longestKindLength = max(entries, m => m.kind.length);
16421642
entries.sort((m, n) => m.sortText > n.sortText ? 1 : m.sortText < n.sortText ? -1 : m.name > n.name ? 1 : m.name < n.name ? -1 : 0);
1643-
const membersString = entries.map(m => `${pad(m.name, longestNameLength)} ${pad(m.kind, longestKindLength)} ${m.kindModifiers}`).join("\n");
1643+
const membersString = entries.map(m => `${pad(m.name, longestNameLength)} ${pad(m.kind, longestKindLength)} ${m.kindModifiers} ${m.source === undefined ? "" : m.source}`).join("\n");
16441644
Harness.IO.log(membersString);
16451645
}
16461646

@@ -2296,13 +2296,13 @@ Actual: ${stringify(fullActual)}`);
22962296
public applyCodeActionFromCompletion(markerName: string, options: FourSlashInterface.VerifyCompletionActionOptions) {
22972297
this.goToMarker(markerName);
22982298

2299-
const actualCompletion = this.getCompletionListAtCaret().entries.find(e => e.name === options.name);
2299+
const actualCompletion = this.getCompletionListAtCaret().entries.find(e => e.name === options.name && e.source === options.source);
23002300

23012301
if (!actualCompletion.hasAction) {
23022302
this.raiseError(`Completion for ${options.name} does not have an associated action.`);
23032303
}
23042304

2305-
const details = this.getCompletionEntryDetails(options.name);
2305+
const details = this.getCompletionEntryDetails(options.name, actualCompletion.source);
23062306
if (details.codeActions.length !== 1) {
23072307
this.raiseError(`Expected one code action, got ${details.codeActions.length}`);
23082308
}
@@ -2984,33 +2984,35 @@ Actual: ${stringify(fullActual)}`);
29842984

29852985
private assertItemInCompletionList(
29862986
items: ts.CompletionEntry[],
2987-
name: string,
2987+
entryId: ts.Completions.CompletionEntryIdentifier,
29882988
text: string | undefined,
29892989
documentation: string | undefined,
29902990
kind: string | undefined,
29912991
spanIndex: number | undefined,
29922992
hasAction: boolean | undefined,
29932993
) {
29942994
for (const item of items) {
2995-
if (item.name === name) {
2996-
if (documentation !== undefined || text !== undefined) {
2997-
const details = this.getCompletionEntryDetails(item.name);
2995+
if (item.name === entryId.name && item.source === entryId.source) {
2996+
if (documentation !== undefined || text !== undefined || entryId.source !== undefined) {
2997+
const details = this.getCompletionEntryDetails(item.name, item.source);
29982998

29992999
if (documentation !== undefined) {
3000-
assert.equal(ts.displayPartsToString(details.documentation), documentation, this.assertionMessageAtLastKnownMarker("completion item documentation for " + name));
3000+
assert.equal(ts.displayPartsToString(details.documentation), documentation, this.assertionMessageAtLastKnownMarker("completion item documentation for " + entryId));
30013001
}
30023002
if (text !== undefined) {
3003-
assert.equal(ts.displayPartsToString(details.displayParts), text, this.assertionMessageAtLastKnownMarker("completion item detail text for " + name));
3003+
assert.equal(ts.displayPartsToString(details.displayParts), text, this.assertionMessageAtLastKnownMarker("completion item detail text for " + entryId));
30043004
}
3005+
3006+
assert.deepEqual(details.source, entryId.source === undefined ? undefined : [ts.textPart(entryId.source)]);
30053007
}
30063008

30073009
if (kind !== undefined) {
3008-
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + name));
3010+
assert.equal(item.kind, kind, this.assertionMessageAtLastKnownMarker("completion item kind for " + entryId));
30093011
}
30103012

30113013
if (spanIndex !== undefined) {
30123014
const span = this.getTextSpanForRangeAtIndex(spanIndex);
3013-
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + name));
3015+
assert.isTrue(TestState.textSpansEqual(span, item.replacementSpan), this.assertionMessageAtLastKnownMarker(stringify(span) + " does not equal " + stringify(item.replacementSpan) + " replacement span for " + entryId));
30143016
}
30153017

30163018
assert.equal(item.hasAction, hasAction);
@@ -3021,7 +3023,7 @@ Actual: ${stringify(fullActual)}`);
30213023

30223024
const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
30233025

3024-
this.raiseError(`Expected "${stringify({ name, text, documentation, kind })}" to be in list [${itemsString}]`);
3026+
this.raiseError(`Expected "${stringify({ entryId, text, documentation, kind })}" to be in list [${itemsString}]`);
30253027
}
30263028

30273029
private findFile(indexOrName: any) {
@@ -3732,12 +3734,15 @@ namespace FourSlashInterface {
37323734

37333735
// Verifies the completion list contains the specified symbol. The
37343736
// completion list is brought up if necessary
3735-
public completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
3737+
public completionListContains(entryId: string | ts.Completions.CompletionEntryIdentifier, text?: string, documentation?: string, kind?: string, spanIndex?: number, hasAction?: boolean) {
3738+
if (typeof entryId === "string") {
3739+
entryId = { name: entryId, source: undefined };
3740+
}
37363741
if (this.negative) {
3737-
this.state.verifyCompletionListDoesNotContain(symbol, text, documentation, kind, spanIndex);
3742+
this.state.verifyCompletionListDoesNotContain(entryId, text, documentation, kind, spanIndex);
37383743
}
37393744
else {
3740-
this.state.verifyCompletionListContains(symbol, text, documentation, kind, spanIndex, hasAction);
3745+
this.state.verifyCompletionListContains(entryId, text, documentation, kind, spanIndex, hasAction);
37413746
}
37423747
}
37433748

@@ -4492,6 +4497,7 @@ namespace FourSlashInterface {
44924497

44934498
export interface VerifyCompletionActionOptions extends NewContentOptions {
44944499
name: string;
4500+
source?: string;
44954501
description: string;
44964502
}
44974503
}

src/harness/harness.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,9 @@ namespace Harness {
11961196
traceResults = [];
11971197
compilerHost.trace = text => traceResults.push(text);
11981198
}
1199+
else {
1200+
compilerHost.directoryExists = () => true; // This only visibly affects resolution traces, so to save time we always return true where possible
1201+
}
11991202
const program = ts.createProgram(programFileNames, options, compilerHost);
12001203

12011204
const emitResult = program.emit();

src/harness/loggedIO.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ interface PlaybackControl {
8989
namespace Playback {
9090
let recordLog: IOLog = undefined;
9191
let replayLog: IOLog = undefined;
92+
let replayFilesRead: ts.Map<IOLogFile> | undefined = undefined;
9293
let recordLogFileNameBase = "";
9394

9495
interface Memoized<T> {
@@ -222,10 +223,15 @@ namespace Playback {
222223
replayLog = log;
223224
// Remove non-found files from the log (shouldn't really need them, but we still record them for diagnostic purposes)
224225
replayLog.filesRead = replayLog.filesRead.filter(f => f.result.contents !== undefined);
226+
replayFilesRead = ts.createMap();
227+
for (const file of replayLog.filesRead) {
228+
replayFilesRead.set(ts.normalizeSlashes(file.path).toLowerCase(), file);
229+
}
225230
};
226231

227232
wrapper.endReplay = () => {
228233
replayLog = undefined;
234+
replayFilesRead = undefined;
229235
};
230236

231237
wrapper.startRecord = (fileNameBase) => {
@@ -254,7 +260,7 @@ namespace Playback {
254260
path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }),
255261
memoize(path => {
256262
// If we read from the file, it must exist
257-
if (findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ false)) {
263+
if (findFileByPath(path, /*throwFileNotFoundError*/ false)) {
258264
return true;
259265
}
260266
else {
@@ -298,7 +304,7 @@ namespace Playback {
298304
recordLog.filesRead.push(logEntry);
299305
return result;
300306
},
301-
memoize(path => findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ true).contents));
307+
memoize(path => findFileByPath(path, /*throwFileNotFoundError*/ true).contents));
302308

303309
wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
304310
(path, extensions, exclude, include, depth) => {
@@ -379,14 +385,12 @@ namespace Playback {
379385
return results[0].result;
380386
}
381387

382-
function findFileByPath(logArray: IOLogFile[],
383-
expectedPath: string, throwFileNotFoundError: boolean): FileInformation {
388+
function findFileByPath(expectedPath: string, throwFileNotFoundError: boolean): FileInformation {
384389
const normalizedName = ts.normalizePath(expectedPath).toLowerCase();
385390
// Try to find the result through normal fileName
386-
for (const log of logArray) {
387-
if (ts.normalizeSlashes(log.path).toLowerCase() === normalizedName) {
388-
return log.result;
389-
}
391+
const result = replayFilesRead.get(normalizedName);
392+
if (result) {
393+
return result.result;
390394
}
391395

392396
// If we got here, we didn't find a match

src/harness/rwcRunner.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ namespace RWC {
3939
const baseName = ts.getBaseFileName(jsonPath);
4040
let currentDirectory: string;
4141
let useCustomLibraryFile: boolean;
42-
let skipTypeBaselines = false;
43-
const typeSizeLimit = 10000000;
4442
after(() => {
4543
// Mocha holds onto the closure environment of the describe callback even after the test is done.
4644
// Therefore we have to clean out large objects after the test is done.
@@ -54,7 +52,6 @@ namespace RWC {
5452
// or to use lib.d.ts inside the json object. If the flag is true, use the lib.d.ts inside json file
5553
// otherwise use the lib.d.ts from built/local
5654
useCustomLibraryFile = undefined;
57-
skipTypeBaselines = false;
5855
});
5956

6057
it("can compile", function(this: Mocha.ITestCallbackContext) {
@@ -64,7 +61,6 @@ namespace RWC {
6461
const ioLog: IOLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
6562
currentDirectory = ioLog.currentDirectory;
6663
useCustomLibraryFile = ioLog.useCustomLibraryFile;
67-
skipTypeBaselines = ioLog.filesRead.reduce((acc, elem) => (elem && elem.result && elem.result.contents) ? acc + elem.result.contents.length : acc, 0) > typeSizeLimit;
6864
runWithIOLog(ioLog, () => {
6965
opts = ts.parseCommandLine(ioLog.arguments, fileName => Harness.IO.readFile(fileName));
7066
assert.equal(opts.errors.length, 0);
@@ -199,14 +195,6 @@ namespace RWC {
199195
}, baselineOpts, [".map"]);
200196
});
201197

202-
/*it("has correct source map record", () => {
203-
if (compilerOptions.sourceMap) {
204-
Harness.Baseline.runBaseline(baseName + ".sourcemap.txt", () => {
205-
return compilerResult.getSourceMapRecord();
206-
}, baselineOpts);
207-
}
208-
});*/
209-
210198
it("has the expected errors", () => {
211199
Harness.Baseline.runMultifileBaseline(baseName, ".errors.txt", () => {
212200
if (compilerResult.errors.length === 0) {
@@ -219,14 +207,6 @@ namespace RWC {
219207
}, baselineOpts);
220208
});
221209

222-
it("has the expected types", () => {
223-
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
224-
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult.program, inputFiles
225-
.concat(otherFiles)
226-
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
227-
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts, /*multifile*/ true, skipTypeBaselines, /*skipSymbolbaselines*/ true);
228-
});
229-
230210
// Ideally, a generated declaration file will have no errors. But we allow generated
231211
// declaration file errors as part of the baseline.
232212
it("has the expected errors in generated declaration files", () => {

src/harness/unittests/extractFunctions.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,27 +362,32 @@ function parsePrimaryExpression(): any {
362362
}`);
363363

364364
testExtractFunction("extractFunction_VariableDeclaration_Var", `
365-
[#|var x = 1;|]
365+
[#|var x = 1;
366+
"hello"|]
366367
x;
367368
`);
368369

369370
testExtractFunction("extractFunction_VariableDeclaration_Let_Type", `
370-
[#|let x: number = 1;|]
371+
[#|let x: number = 1;
372+
"hello";|]
371373
x;
372374
`);
373375

374376
testExtractFunction("extractFunction_VariableDeclaration_Let_NoType", `
375-
[#|let x = 1;|]
377+
[#|let x = 1;
378+
"hello";|]
376379
x;
377380
`);
378381

379382
testExtractFunction("extractFunction_VariableDeclaration_Const_Type", `
380-
[#|const x: number = 1;|]
383+
[#|const x: number = 1;
384+
"hello";|]
381385
x;
382386
`);
383387

384388
testExtractFunction("extractFunction_VariableDeclaration_Const_NoType", `
385-
[#|const x = 1;|]
389+
[#|const x = 1;
390+
"hello";|]
386391
x;
387392
`);
388393

@@ -404,7 +409,8 @@ x; y; z;
404409
`);
405410

406411
testExtractFunction("extractFunction_VariableDeclaration_ConsumedTwice", `
407-
[#|const x: number = 1;|]
412+
[#|const x: number = 1;
413+
"hello";|]
408414
x; x;
409415
`);
410416

src/harness/unittests/extractRanges.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ namespace ts {
162162
}|]|]
163163
}
164164
`);
165+
166+
// Variable statements
167+
testExtractRange(`[#|let x = [$|1|];|]`);
168+
testExtractRange(`[#|let x = [$|1|], y;|]`);
169+
testExtractRange(`[#|[$|let x = 1, y = 1;|]|]`);
170+
171+
// Variable declarations
172+
testExtractRange(`let [#|x = [$|1|]|];`);
173+
testExtractRange(`let [#|x = [$|1|]|], y = 2;`);
174+
testExtractRange(`let x = 1, [#|y = [$|2|]|];`);
175+
176+
// Return statements
177+
testExtractRange(`[#|return [$|1|];|]`);
165178
});
166179

167180
testExtractRangeFailed("extractRangeFailed1",
@@ -340,6 +353,18 @@ switch (x) {
340353
refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message
341354
]);
342355

356+
testExtractRangeFailed("extractRangeFailed12",
357+
`let [#|x|];`,
358+
[
359+
refactor.extractSymbol.Messages.StatementOrExpressionExpected.message
360+
]);
361+
362+
testExtractRangeFailed("extractRangeFailed13",
363+
`[#|return;|]`,
364+
[
365+
refactor.extractSymbol.Messages.CannotExtractRange.message
366+
]);
367+
343368
testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]);
344369
});
345370
}

0 commit comments

Comments
 (0)