mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
During fourslash server test baseline tsserver log file so that project updates and watches can be baselined and reasoned about (#56132)
This commit is contained in:
+294
-256
@@ -5,6 +5,9 @@ import * as ts from "./_namespaces/ts";
|
||||
import * as Utils from "./_namespaces/Utils";
|
||||
import * as vfs from "./_namespaces/vfs";
|
||||
import * as vpath from "./_namespaces/vpath";
|
||||
import {
|
||||
Logger,
|
||||
} from "./tsserverLogger";
|
||||
|
||||
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
|
||||
|
||||
@@ -226,6 +229,12 @@ function realizeDiagnostic(diagnostic: ts.Diagnostic, newLine: string): Realized
|
||||
};
|
||||
}
|
||||
|
||||
interface BaselineTest {
|
||||
command: string;
|
||||
actual: string;
|
||||
ext: string | undefined;
|
||||
}
|
||||
|
||||
export class TestState {
|
||||
// Language service instance
|
||||
private languageServiceAdapterHost: Harness.LanguageService.LanguageServiceAdapterHost;
|
||||
@@ -250,6 +259,8 @@ export class TestState {
|
||||
|
||||
private inputFiles = new Map<string, string>(); // Map between inputFile's fileName and its content for easily looking up when resolving references
|
||||
|
||||
private logger: Logger | undefined;
|
||||
|
||||
private static getDisplayPartsJson(displayParts: ts.SymbolDisplayPart[] | undefined) {
|
||||
let result = "";
|
||||
ts.forEach(displayParts, part => {
|
||||
@@ -358,6 +369,7 @@ export class TestState {
|
||||
}
|
||||
|
||||
const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
|
||||
this.logger = languageServiceAdapter.getLogger();
|
||||
this.languageServiceAdapterHost = languageServiceAdapter.getHost();
|
||||
this.languageService = memoWrap(languageServiceAdapter.getLanguageService(), this); // Wrap the LS to cache some expensive operations certain tests call repeatedly
|
||||
if (this.testType === FourSlashTestType.Server) {
|
||||
@@ -445,6 +457,11 @@ export class TestState {
|
||||
|
||||
this.formatCodeSettings = ts.testFormatSettings;
|
||||
|
||||
if (this.logger?.loggingEnabled()) {
|
||||
const patch = this.languageServiceAdapterHost.vfs.diff();
|
||||
this.logger.log(vfs.formatPatch(patch) || "");
|
||||
}
|
||||
|
||||
// Open the first file by default
|
||||
this.openFile(0);
|
||||
|
||||
@@ -482,6 +499,31 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
|
||||
private baselineFromTest: BaselineTest[] | undefined;
|
||||
|
||||
private baseline(command: string, actual: string, ext?: string) {
|
||||
if (!this.baselineFromTest) this.baselineFromTest = [{ command, actual, ext }];
|
||||
else this.baselineFromTest.push({ command, actual, ext });
|
||||
}
|
||||
|
||||
baselineTest() {
|
||||
if (this.baselineFromTest) {
|
||||
Harness.Baseline.runBaseline(
|
||||
this.getBaselineFileNameForContainingTestFile(this.baselineFromTest[0].ext),
|
||||
this.baselineFromTest.map(({ command, actual }) => `// === ${command} ===\n${actual}`).join("\n\n\n\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
baselineTsserverLog() {
|
||||
if (this.logger) {
|
||||
Harness.Baseline.runBaseline(
|
||||
`tsserver/fourslashServer/${ts.getBaseFileName(this.originalInputFileName).replace(".ts", ".js")}`,
|
||||
this.logger.logs!.join("\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private getFileContent(fileName: string): string {
|
||||
return ts.Debug.checkDefined(this.tryGetFileContent(fileName));
|
||||
}
|
||||
@@ -827,6 +869,71 @@ export class TestState {
|
||||
return baseline;
|
||||
}
|
||||
|
||||
public baselineGoToDefinition(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRange("goToDefinition", markerOrRange, rangeText, markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO DEF*/",
|
||||
markerOrRange,
|
||||
() => this.getGoToDefinitionAndBoundSpan(),
|
||||
));
|
||||
}
|
||||
|
||||
public baselineGetDefinitionAtPosition(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRange("getDefinitionAtPosition", markerOrRange, rangeText, markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO DEF POS*/",
|
||||
markerOrRange,
|
||||
() => this.getGoToDefinition(),
|
||||
));
|
||||
}
|
||||
|
||||
public baselineGoToSourceDefinition(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
if (this.testType !== FourSlashTestType.Server) {
|
||||
this.raiseError("goToSourceDefinition may only be used in fourslash/server tests.");
|
||||
}
|
||||
this.baselineEachMarkerOrRange("goToSourceDefinition", markerOrRange, rangeText, markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO SOURCE DEF*/",
|
||||
markerOrRange,
|
||||
() =>
|
||||
(this.languageService as ts.server.SessionClient)
|
||||
.getSourceDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition),
|
||||
));
|
||||
}
|
||||
|
||||
public baselineGoToType(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRange("goToType", markerOrRange, rangeText, markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO TYPE*/",
|
||||
markerOrRange,
|
||||
() => this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition),
|
||||
));
|
||||
}
|
||||
|
||||
public baselineGoToImplementation(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRange("goToImplementation", markerOrRange, rangeText, markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO IMPL*/",
|
||||
markerOrRange,
|
||||
() => this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition),
|
||||
));
|
||||
}
|
||||
|
||||
public verifyGetEmitOutputForCurrentFile(expected: string): void {
|
||||
const emit = this.languageService.getEmitOutput(this.activeFile.fileName);
|
||||
if (emit.outputFiles.length !== 1) {
|
||||
@@ -855,7 +962,6 @@ export class TestState {
|
||||
return a.position - b.position;
|
||||
};
|
||||
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile();
|
||||
const fileName = this.activeFile.fileName;
|
||||
const hints = this.languageService.provideInlayHints(fileName, span, preferences);
|
||||
const annotations = ts.map(hints.sort(sortHints), hint => {
|
||||
@@ -880,7 +986,7 @@ export class TestState {
|
||||
annotations.push("=== No inlay hints ===");
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(baselineFile, annotations.join("\n\n"));
|
||||
this.baseline("Inlay Hints", annotations.join("\n\n"));
|
||||
}
|
||||
|
||||
public verifyCompletions(options: FourSlashInterface.VerifyCompletionsOptions) {
|
||||
@@ -1220,184 +1326,106 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyBaselineCommands(...commands: FourSlashInterface.BaselineCommand[]) {
|
||||
let baselineContent = "";
|
||||
const baselineEachMarkerOrRange = (
|
||||
command: FourSlashInterface.BaselineCommandWithMarkerOrRange,
|
||||
worker: (markerORange: MarkerOrNameOrRange) => string,
|
||||
) => {
|
||||
let done = false;
|
||||
if (command.markerOrRange !== undefined) {
|
||||
done = baselineArrayOrSingle(command, command.markerOrRange, worker);
|
||||
}
|
||||
if (command.rangeText !== undefined) {
|
||||
toArray(command.rangeText).forEach(text => done = baselineArrayOrSingle(command, this.rangesByText().get(text)!, worker) || done);
|
||||
}
|
||||
if (!done) {
|
||||
baselineArrayOrSingle(command, this.getRanges(), worker);
|
||||
}
|
||||
};
|
||||
commands.forEach(command => {
|
||||
switch (command.type) {
|
||||
case "findAllReferences":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange => this.baselineFindAllReferencesWorker(markerOrRange),
|
||||
);
|
||||
case "getFileReferences":
|
||||
return baselineArrayOrSingle(
|
||||
command,
|
||||
command.fileName,
|
||||
fileName => this.baselineGetFileReferences(fileName),
|
||||
);
|
||||
case "findRenameLocations":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange => this.baselineRenameWorker(markerOrRange, command.options),
|
||||
);
|
||||
case "goToDefinition":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO DEF*/",
|
||||
markerOrRange,
|
||||
() => this.getGoToDefinitionAndBoundSpan(),
|
||||
),
|
||||
);
|
||||
case "getDefinitionAtPosition":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO DEF POS*/",
|
||||
markerOrRange,
|
||||
() => this.getGoToDefinition(),
|
||||
),
|
||||
);
|
||||
case "goToSourceDefinition":
|
||||
if (this.testType !== FourSlashTestType.Server) {
|
||||
this.raiseError("goToSourceDefinition may only be used in fourslash/server tests.");
|
||||
private baselineEachMarkerOrRangeArrayOrSingle(
|
||||
command: string,
|
||||
markerOrRange: ArrayOrSingle<MarkerOrNameOrRange> | undefined,
|
||||
rangeText: ArrayOrSingle<string> | undefined,
|
||||
worker: (markerORange: MarkerOrNameOrRange) => string,
|
||||
) {
|
||||
return this.baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange !== undefined ? toArray(markerOrRange) : undefined,
|
||||
rangeText !== undefined ? toArray(rangeText) : undefined,
|
||||
worker,
|
||||
);
|
||||
}
|
||||
|
||||
private baselineEachMarkerOrRange(
|
||||
command: string,
|
||||
markerOrRange: readonly MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: readonly string[] | undefined,
|
||||
worker: (markerORange: MarkerOrNameOrRange) => string,
|
||||
) {
|
||||
let done = false;
|
||||
if (markerOrRange !== undefined) {
|
||||
done = this.baselineArray(command, markerOrRange, worker);
|
||||
}
|
||||
if (rangeText !== undefined) {
|
||||
toArray(rangeText).forEach(text => done = this.baselineArray(command, this.rangesByText().get(text)!, worker) || done);
|
||||
}
|
||||
if (!done) this.baselineArray(command, this.getRanges(), worker);
|
||||
}
|
||||
|
||||
private baselineArray<T>(
|
||||
command: string,
|
||||
array: readonly T[],
|
||||
worker: (single: T) => string,
|
||||
) {
|
||||
array.forEach(single => this.baseline(command, worker(single), ".baseline.jsonc"));
|
||||
return !!array.length;
|
||||
}
|
||||
|
||||
public baselineFindAllReferences(
|
||||
markerOrRange: MarkerOrNameOrRange[] | undefined,
|
||||
rangeText: string[] | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRange("findAllReferences", markerOrRange, rangeText, markerOrRange => {
|
||||
this.goToMarkerOrNameOrRange(markerOrRange);
|
||||
const references = this.findReferencesAtCaret();
|
||||
const defIdMap = new Map<ts.ReferencedSymbolDefinitionInfo | ts.ReferencedSymbolEntry, number>();
|
||||
const markerInfo = { markerOrRange, markerName: "/*FIND ALL REFS*/" };
|
||||
let baseline = this.getBaselineForDocumentSpansWithFileContents(
|
||||
ts.flatMap(references, (r, def) => {
|
||||
if (references!.length > 1) {
|
||||
defIdMap.set(r.definition, def);
|
||||
r.references.forEach(r => defIdMap.set(r, def));
|
||||
}
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO SOURCE DEF*/",
|
||||
markerOrRange,
|
||||
() =>
|
||||
(this.languageService as ts.server.SessionClient)
|
||||
.getSourceDefinitionAndBoundSpan(this.activeFile.fileName, this.currentCaretPosition),
|
||||
),
|
||||
);
|
||||
case "goToType":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO TYPE*/",
|
||||
markerOrRange,
|
||||
() => this.languageService.getTypeDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition),
|
||||
),
|
||||
);
|
||||
case "goToImplementation":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange =>
|
||||
this.baselineGoToDefs(
|
||||
"/*GOTO IMPL*/",
|
||||
markerOrRange,
|
||||
() => this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition),
|
||||
),
|
||||
);
|
||||
case "documentHighlights":
|
||||
return baselineEachMarkerOrRange(
|
||||
command,
|
||||
markerOrRange => this.baselineGetDocumentHighlights(markerOrRange, command.options),
|
||||
);
|
||||
case "customWork":
|
||||
return addToBaseline(command, readableJsoncBaseline(command.work() || ""));
|
||||
default:
|
||||
ts.Debug.assertNever(command);
|
||||
}
|
||||
});
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent);
|
||||
|
||||
function baselineArrayOrSingle<T>(
|
||||
command: FourSlashInterface.BaselineCommand,
|
||||
arrayOrSingle: ArrayOrSingle<T>,
|
||||
worker: (single: T) => string,
|
||||
) {
|
||||
if (ts.isArray(arrayOrSingle)) {
|
||||
arrayOrSingle.forEach(single => addToBaseline(command, worker(single)));
|
||||
return !!arrayOrSingle.length;
|
||||
}
|
||||
else {
|
||||
addToBaseline(command, worker(arrayOrSingle));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function addToBaseline(command: FourSlashInterface.BaselineCommand, text: string) {
|
||||
if (baselineContent) baselineContent += "\n\n\n\n";
|
||||
baselineContent += `// === ${command.type} ===\n` + text;
|
||||
}
|
||||
}
|
||||
|
||||
private baselineFindAllReferencesWorker(markerOrRange: MarkerOrNameOrRange) {
|
||||
this.goToMarkerOrNameOrRange(markerOrRange);
|
||||
const references = this.findReferencesAtCaret();
|
||||
const defIdMap = new Map<ts.ReferencedSymbolDefinitionInfo | ts.ReferencedSymbolEntry, number>();
|
||||
const markerInfo = { markerOrRange, markerName: "/*FIND ALL REFS*/" };
|
||||
let baseline = this.getBaselineForDocumentSpansWithFileContents(
|
||||
ts.flatMap(references, (r, def) => {
|
||||
if (references!.length > 1) {
|
||||
defIdMap.set(r.definition, def);
|
||||
r.references.forEach(r => defIdMap.set(r, def));
|
||||
}
|
||||
return r.references;
|
||||
}),
|
||||
{
|
||||
markerInfo,
|
||||
documentSpanId: defIdMap.size ? ref => `defId: ${defIdMap.get(ref)}` : undefined,
|
||||
},
|
||||
);
|
||||
if (references?.length) {
|
||||
baseline += "\n\n";
|
||||
baseline += indentJsonBaseline(
|
||||
"// === Definitions ===\n" +
|
||||
this.getBaselineForDocumentSpansWithFileContents(
|
||||
references.map(r => r.definition),
|
||||
{
|
||||
markerInfo,
|
||||
documentSpanId: defIdMap.size ? def => `defId: ${defIdMap.get(def)}` : undefined,
|
||||
skipDocumentSpanDetails: true,
|
||||
skipDocumentContainingOnlyMarker: true,
|
||||
},
|
||||
) +
|
||||
"\n\n// === Details ===\n" +
|
||||
JSON.stringify(
|
||||
references.map(r => ({
|
||||
defId: defIdMap.get(r.definition),
|
||||
...r.definition,
|
||||
fileName: undefined,
|
||||
textSpan: undefined,
|
||||
contextSpan: undefined,
|
||||
})),
|
||||
undefined,
|
||||
" ",
|
||||
),
|
||||
return r.references;
|
||||
}),
|
||||
{
|
||||
markerInfo,
|
||||
documentSpanId: defIdMap.size ? ref => `defId: ${defIdMap.get(ref)}` : undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
return baseline;
|
||||
if (references?.length) {
|
||||
baseline += "\n\n";
|
||||
baseline += indentJsonBaseline(
|
||||
"// === Definitions ===\n" +
|
||||
this.getBaselineForDocumentSpansWithFileContents(
|
||||
references.map(r => r.definition),
|
||||
{
|
||||
markerInfo,
|
||||
documentSpanId: defIdMap.size ? def => `defId: ${defIdMap.get(def)}` : undefined,
|
||||
skipDocumentSpanDetails: true,
|
||||
skipDocumentContainingOnlyMarker: true,
|
||||
},
|
||||
) +
|
||||
"\n\n// === Details ===\n" +
|
||||
JSON.stringify(
|
||||
references.map(r => ({
|
||||
defId: defIdMap.get(r.definition),
|
||||
...r.definition,
|
||||
fileName: undefined,
|
||||
textSpan: undefined,
|
||||
contextSpan: undefined,
|
||||
})),
|
||||
undefined,
|
||||
" ",
|
||||
),
|
||||
);
|
||||
}
|
||||
return baseline;
|
||||
});
|
||||
}
|
||||
|
||||
private baselineGetFileReferences(fileName: string) {
|
||||
const references = this.languageService.getFileReferences(fileName);
|
||||
return `// fileName: ${fileName}\n\n` + this.getBaselineForDocumentSpansWithFileContents(
|
||||
references,
|
||||
{ markerInfo: undefined },
|
||||
);
|
||||
public baselineGetFileReferences(fileNames: string[]) {
|
||||
this.baselineArray("getFileReferences", fileNames, fileName => {
|
||||
const references = this.languageService.getFileReferences(fileName);
|
||||
return `// fileName: ${fileName}\n\n` + this.getBaselineForDocumentSpansWithFileContents(
|
||||
references,
|
||||
{ markerInfo: undefined },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private getBaselineForDocumentSpansWithFileContents<T extends ts.DocumentSpan>(
|
||||
@@ -1869,47 +1897,53 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
|
||||
private baselineRenameWorker(markerOrRange: MarkerOrNameOrRange, options?: FourSlashInterface.RenameOptions) {
|
||||
const { fileName, position } = ts.isString(markerOrRange) ?
|
||||
this.getMarkerByName(markerOrRange) :
|
||||
isMarker(markerOrRange) ?
|
||||
markerOrRange :
|
||||
{ fileName: markerOrRange.fileName, position: markerOrRange.pos };
|
||||
const {
|
||||
findInStrings = false,
|
||||
findInComments = false,
|
||||
providePrefixAndSuffixTextForRename = true,
|
||||
quotePreference = "double",
|
||||
} = options || {};
|
||||
const locations = this.languageService.findRenameLocations(
|
||||
fileName,
|
||||
position,
|
||||
findInStrings,
|
||||
findInComments,
|
||||
{ providePrefixAndSuffixTextForRename, quotePreference },
|
||||
);
|
||||
public baselineRename(
|
||||
markerOrRange: ArrayOrSingle<MarkerOrNameOrRange> | undefined,
|
||||
rangeText: ArrayOrSingle<string> | undefined,
|
||||
options: FourSlashInterface.RenameOptions | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRangeArrayOrSingle("findRenameLocations", markerOrRange, rangeText, markerOrRange => {
|
||||
const { fileName, position } = ts.isString(markerOrRange) ?
|
||||
this.getMarkerByName(markerOrRange) :
|
||||
isMarker(markerOrRange) ?
|
||||
markerOrRange :
|
||||
{ fileName: markerOrRange.fileName, position: markerOrRange.pos };
|
||||
const {
|
||||
findInStrings = false,
|
||||
findInComments = false,
|
||||
providePrefixAndSuffixTextForRename = true,
|
||||
quotePreference = "double",
|
||||
} = options || {};
|
||||
const locations = this.languageService.findRenameLocations(
|
||||
fileName,
|
||||
position,
|
||||
findInStrings,
|
||||
findInComments,
|
||||
{ providePrefixAndSuffixTextForRename, quotePreference },
|
||||
);
|
||||
|
||||
if (!locations) {
|
||||
this.raiseError(`baselineRename failed. Could not rename at the provided position.`);
|
||||
}
|
||||
if (!locations) {
|
||||
this.raiseError(`baselineRename failed. Could not rename at the provided position.`);
|
||||
}
|
||||
|
||||
const renameOptions = options ?
|
||||
(options.findInStrings !== undefined ? `// @findInStrings: ${findInStrings}\n` : "") +
|
||||
(options.findInComments !== undefined ? `// @findInComments: ${findInComments}\n` : "") +
|
||||
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") +
|
||||
(options.quotePreference !== undefined ? `// @quotePreference: ${quotePreference}\n` : "") :
|
||||
"";
|
||||
const renameOptions = options ?
|
||||
(options.findInStrings !== undefined ? `// @findInStrings: ${findInStrings}\n` : "") +
|
||||
(options.findInComments !== undefined ? `// @findInComments: ${findInComments}\n` : "") +
|
||||
(options.providePrefixAndSuffixTextForRename !== undefined ? `// @providePrefixAndSuffixTextForRename: ${providePrefixAndSuffixTextForRename}\n` : "") +
|
||||
(options.quotePreference !== undefined ? `// @quotePreference: ${quotePreference}\n` : "") :
|
||||
"";
|
||||
|
||||
return renameOptions + (renameOptions ? "\n" : "") + this.getBaselineForDocumentSpansWithFileContents(
|
||||
locations,
|
||||
{
|
||||
markerInfo: { markerOrRange, markerName: "/*RENAME*/" },
|
||||
endMarker: "RENAME|]",
|
||||
startMarkerPrefix: span => span.prefixText ? `/*START PREFIX*/${span.prefixText}` : "",
|
||||
endMarkerSuffix: span => span.suffixText ? `${span.suffixText}/*END SUFFIX*/` : "",
|
||||
ignoredDocumentSpanProperties: ["prefixText", "suffixText"],
|
||||
},
|
||||
);
|
||||
return renameOptions + (renameOptions ? "\n" : "") + this.getBaselineForDocumentSpansWithFileContents(
|
||||
locations,
|
||||
{
|
||||
markerInfo: { markerOrRange, markerName: "/*RENAME*/" },
|
||||
endMarker: "RENAME|]",
|
||||
startMarkerPrefix: span => span.prefixText ? `/*START PREFIX*/${span.prefixText}` : "",
|
||||
endMarkerSuffix: span => span.suffixText ? `${span.suffixText}/*END SUFFIX*/` : "",
|
||||
ignoredDocumentSpanProperties: ["prefixText", "suffixText"],
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public verifyQuickInfoExists(negative: boolean) {
|
||||
@@ -2228,8 +2262,7 @@ export class TestState {
|
||||
}
|
||||
|
||||
public baselineCurrentFileBreakpointLocations() {
|
||||
const baselineFile = this.getBaselineFileNameForInternalFourslashFile().replace("breakpointValidation", "bpSpan");
|
||||
Harness.Baseline.runBaseline(baselineFile, this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
|
||||
this.baseline("breakpoints", this.baselineCurrentFileLocations(pos => this.getBreakpointStatementLocation(pos)!));
|
||||
}
|
||||
|
||||
private getEmitFiles(): readonly FourSlashFile[] {
|
||||
@@ -2293,7 +2326,7 @@ export class TestState {
|
||||
resultString += Harness.IO.newLine();
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(ts.Debug.checkDefined(this.testData.globalOptions[MetadataOptionNames.baselineFile]), resultString);
|
||||
this.baseline("EmitOutput", resultString);
|
||||
}
|
||||
|
||||
private flattenChainedMessage(diag: ts.DiagnosticMessageChain, indent = " ") {
|
||||
@@ -2310,7 +2343,7 @@ export class TestState {
|
||||
public baselineSyntacticDiagnostics() {
|
||||
const files = this.getCompilerTestFiles();
|
||||
const result = this.getSyntacticDiagnosticBaselineText(files);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
|
||||
this.baseline("Syntax Diagnostics", result);
|
||||
}
|
||||
|
||||
private getCompilerTestFiles() {
|
||||
@@ -2326,7 +2359,7 @@ export class TestState {
|
||||
+ Harness.IO.newLine()
|
||||
+ Harness.IO.newLine()
|
||||
+ this.getSemanticDiagnosticBaselineText(files);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(), result);
|
||||
this.baseline("Syntax and Semantic Diagnostics", result);
|
||||
}
|
||||
|
||||
private getSyntacticDiagnosticBaselineText(files: Harness.Compiler.TestFile[]) {
|
||||
@@ -2346,7 +2379,6 @@ export class TestState {
|
||||
}
|
||||
|
||||
public baselineQuickInfo() {
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile();
|
||||
const result = ts.arrayFrom(this.testData.markerPositions.entries(), ([name, marker]) => ({
|
||||
marker: { ...marker, name },
|
||||
item: this.languageService.getQuickInfoAtPosition(marker.fileName, marker.position),
|
||||
@@ -2361,11 +2393,10 @@ export class TestState {
|
||||
...(tags?.length ? tags.map(p => `@${p.name} ${p.text?.map(dp => dp.text).join("") ?? ""}`).join("\n").split("\n") : []),
|
||||
],
|
||||
);
|
||||
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
|
||||
this.baseline("QuickInfo", annotations + "\n\n" + stringify(result));
|
||||
}
|
||||
|
||||
public baselineSignatureHelp() {
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile();
|
||||
const result = ts.arrayFrom(this.testData.markerPositions.entries(), ([name, marker]) => ({
|
||||
marker: { ...marker, name },
|
||||
item: this.languageService.getSignatureHelpItems(marker.fileName, marker.position, /*options*/ undefined),
|
||||
@@ -2396,11 +2427,10 @@ export class TestState {
|
||||
return tooltip;
|
||||
},
|
||||
);
|
||||
Harness.Baseline.runBaseline(baselineFile, annotations + "\n\n" + stringify(result));
|
||||
this.baseline("SignatureHelp", annotations + "\n\n" + stringify(result));
|
||||
}
|
||||
|
||||
public baselineCompletions(preferences?: ts.UserPreferences) {
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile();
|
||||
const result = ts.arrayFrom(this.testData.markerPositions.entries(), ([name, marker]) => {
|
||||
this.goToMarker(marker);
|
||||
const completions = this.getCompletionListAtCaret(preferences);
|
||||
@@ -2443,8 +2473,8 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
}
|
||||
Harness.Baseline.runBaseline(
|
||||
baselineFile,
|
||||
this.baseline(
|
||||
"Completions",
|
||||
annotations + "\n\n" + stringify(result, (key, value) => {
|
||||
return key === "exportMapKey"
|
||||
? value.replace(/ \d+ /g, " * ")
|
||||
@@ -2503,7 +2533,6 @@ export class TestState {
|
||||
|
||||
public baselineSmartSelection() {
|
||||
const n = "\n";
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile();
|
||||
const markers = this.getMarkers();
|
||||
const fileContent = this.activeFile.content;
|
||||
const text = markers.map(marker => {
|
||||
@@ -2531,7 +2560,7 @@ export class TestState {
|
||||
return baselineContent.join(fileContent.includes("\n") ? n + n : n);
|
||||
}).join(n.repeat(2) + "=".repeat(80) + n.repeat(2));
|
||||
|
||||
Harness.Baseline.runBaseline(baselineFile, text);
|
||||
this.baseline("Smart Selection", text);
|
||||
}
|
||||
|
||||
public printBreakpointLocation(pos: number) {
|
||||
@@ -2589,11 +2618,6 @@ export class TestState {
|
||||
Harness.IO.log(stringify(help.items[help.selectedItemIndex]));
|
||||
}
|
||||
|
||||
private getBaselineFileNameForInternalFourslashFile(ext = ".baseline") {
|
||||
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
|
||||
ts.getBaseFileName(this.activeFile.fileName).replace(ts.Extension.Ts, ext);
|
||||
}
|
||||
|
||||
private getBaselineFileNameForContainingTestFile(ext = ".baseline") {
|
||||
return this.testData.globalOptions[MetadataOptionNames.baselineFile] ||
|
||||
ts.getBaseFileName(this.originalInputFileName).replace(ts.Extension.Ts, ext);
|
||||
@@ -3006,8 +3030,8 @@ export class TestState {
|
||||
}
|
||||
|
||||
public baselineCurrentFileNameOrDottedNameSpans() {
|
||||
Harness.Baseline.runBaseline(
|
||||
this.testData.globalOptions[MetadataOptionNames.baselineFile],
|
||||
this.baseline(
|
||||
"NameOrDottedNameSpans",
|
||||
this.baselineCurrentFileLocations(pos => this.getNameOrDottedNameSpan(pos)!),
|
||||
);
|
||||
}
|
||||
@@ -3583,7 +3607,6 @@ export class TestState {
|
||||
|
||||
public baselineAutoImports(markerName: string, fullNamesForCodeFix?: string[], preferences?: ts.UserPreferences) {
|
||||
const marker = this.getMarkerByName(markerName);
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile(`.baseline.md`);
|
||||
const completionPreferences = {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsWithInsertText: true,
|
||||
@@ -3651,7 +3674,7 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(baselineFile, baselineText);
|
||||
this.baseline("Auto Imports", baselineText, `.baseline.md`);
|
||||
}
|
||||
|
||||
public verifyJsxClosingTag(map: { [markerName: string]: ts.JsxClosingTagInfo | undefined; }): void {
|
||||
@@ -3671,7 +3694,6 @@ export class TestState {
|
||||
}
|
||||
|
||||
public baselineLinkedEditing(): void {
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile(".linkedEditing.txt");
|
||||
const files = this.testData.files;
|
||||
|
||||
let baselineContent = "";
|
||||
@@ -3682,7 +3704,7 @@ export class TestState {
|
||||
offset = result.offset;
|
||||
}
|
||||
|
||||
Harness.Baseline.runBaseline(baselineFile, baselineContent);
|
||||
this.baseline("Linked Editing", baselineContent, ".linkedEditing.txt");
|
||||
|
||||
function getLinkedEditingBaselineWorker(activeFile: FourSlashFile, offset: number, languageService: ts.LanguageService) {
|
||||
const fileName = activeFile.fileName;
|
||||
@@ -3858,19 +3880,30 @@ export class TestState {
|
||||
return this.languageService.getDocumentHighlights(this.activeFile.fileName, this.currentCaretPosition, filesToSearch);
|
||||
}
|
||||
|
||||
private baselineGetDocumentHighlights(markerOrRange: MarkerOrNameOrRange, options: FourSlashInterface.VerifyDocumentHighlightsOptions | undefined) {
|
||||
this.goToMarkerOrNameOrRange(markerOrRange);
|
||||
const highlights = this.getDocumentHighlightsAtCurrentPosition(ts.map(options?.filesToSearch, ts.normalizePath) || [this.activeFile.fileName]);
|
||||
public baselineDocumentHighlights(
|
||||
markerOrRange: ArrayOrSingle<MarkerOrNameOrRange> | undefined,
|
||||
rangeText: ArrayOrSingle<string> | undefined,
|
||||
options: FourSlashInterface.VerifyDocumentHighlightsOptions | undefined,
|
||||
) {
|
||||
this.baselineEachMarkerOrRangeArrayOrSingle(
|
||||
"documentHighlights",
|
||||
markerOrRange,
|
||||
rangeText,
|
||||
markerOrRange => {
|
||||
this.goToMarkerOrNameOrRange(markerOrRange);
|
||||
const highlights = this.getDocumentHighlightsAtCurrentPosition(ts.map(options?.filesToSearch, ts.normalizePath) || [this.activeFile.fileName]);
|
||||
|
||||
// Write input files
|
||||
const filesToSearch = options ? "// filesToSearch:\n" +
|
||||
options.filesToSearch.map(f => "// " + f).join("\n") + "\n\n" :
|
||||
"";
|
||||
const baselineContent = this.getBaselineForGroupedDocumentSpansWithFileContents(
|
||||
highlights?.map(h => h.highlightSpans.map(s => s.fileName ? s as ts.DocumentSpan : { ...s, fileName: h.fileName })) || ts.emptyArray,
|
||||
{ markerInfo: { markerOrRange, markerName: "/*HIGHLIGHTS*/" } },
|
||||
// Write input files
|
||||
const filesToSearch = options ? "// filesToSearch:\n" +
|
||||
options.filesToSearch.map(f => "// " + f).join("\n") + "\n\n" :
|
||||
"";
|
||||
const baselineContent = this.getBaselineForGroupedDocumentSpansWithFileContents(
|
||||
highlights?.map(h => h.highlightSpans.map(s => s.fileName ? s as ts.DocumentSpan : { ...s, fileName: h.fileName })) || ts.emptyArray,
|
||||
{ markerInfo: { markerOrRange, markerName: "/*HIGHLIGHTS*/" } },
|
||||
);
|
||||
return filesToSearch + baselineContent;
|
||||
},
|
||||
);
|
||||
return filesToSearch + baselineContent;
|
||||
}
|
||||
|
||||
public verifyCodeFixAvailable(negative: boolean, expected: FourSlashInterface.VerifyCodeFixAvailableOptions[] | string | undefined): void {
|
||||
@@ -4275,10 +4308,9 @@ export class TestState {
|
||||
}
|
||||
|
||||
public baselineCallHierarchy() {
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile(".callHierarchy.txt");
|
||||
const callHierarchyItem = this.languageService.prepareCallHierarchy(this.activeFile.fileName, this.currentCaretPosition);
|
||||
const text = callHierarchyItem ? ts.mapOneOrMany(callHierarchyItem, item => this.formatCallHierarchy(item), result => result.join("")) : "none";
|
||||
Harness.Baseline.runBaseline(baselineFile, text);
|
||||
this.baseline("Call Hierarchy", text, ".callHierarchy.txt");
|
||||
}
|
||||
|
||||
private getLineContent(index: number) {
|
||||
@@ -4503,12 +4535,16 @@ function renameKeys<T>(obj: { readonly [key: string]: T; }, renameKey: (key: str
|
||||
return res;
|
||||
}
|
||||
|
||||
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
|
||||
const content = Harness.IO.readFile(fileName)!;
|
||||
runFourSlashTestContent(basePath, testType, content, fileName);
|
||||
export interface FourSlashServerLogBaseliner {
|
||||
baseline?: () => void;
|
||||
}
|
||||
|
||||
export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void {
|
||||
export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string, serverLogBaseliner?: FourSlashServerLogBaseliner) {
|
||||
const content = Harness.IO.readFile(fileName)!;
|
||||
runFourSlashTestContent(basePath, testType, content, fileName, serverLogBaseliner);
|
||||
}
|
||||
|
||||
export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string, serverLogBaseliner?: FourSlashServerLogBaseliner): void {
|
||||
// Give file paths an absolute path for the virtual file system
|
||||
const absoluteBasePath = ts.combinePaths(Harness.virtualFileSystemRoot, basePath);
|
||||
const absoluteFileName = ts.combinePaths(Harness.virtualFileSystemRoot, fileName);
|
||||
@@ -4516,12 +4552,14 @@ export function runFourSlashTestContent(basePath: string, testType: FourSlashTes
|
||||
// Parse out the files and their metadata
|
||||
const testData = parseTestData(absoluteBasePath, content, absoluteFileName);
|
||||
const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData);
|
||||
if (serverLogBaseliner) serverLogBaseliner.baseline = () => state.baselineTsserverLog();
|
||||
const actualFileName = Harness.IO.resolvePath(fileName) || absoluteFileName;
|
||||
const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true, inlineSources: true } });
|
||||
if (output.diagnostics!.length > 0) {
|
||||
throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`);
|
||||
}
|
||||
runCode(output.outputText, state, actualFileName);
|
||||
state.baselineTest();
|
||||
}
|
||||
|
||||
function runCode(code: string, state: TestState, fileName: string): void {
|
||||
|
||||
@@ -333,68 +333,64 @@ export class Verify extends VerifyNegatable {
|
||||
this.state.verifyTypeAtLocation(range, expected);
|
||||
}
|
||||
|
||||
public baselineCommands(...commands: BaselineCommand[]) {
|
||||
this.state.verifyBaselineCommands(...commands);
|
||||
}
|
||||
|
||||
public baselineFindAllReferences(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "findAllReferences", markerOrRange });
|
||||
this.state.baselineFindAllReferences(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineFindAllReferencesAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "findAllReferences", rangeText });
|
||||
this.state.baselineFindAllReferences(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineGetFileReferences(...fileName: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "getFileReferences", fileName });
|
||||
this.state.baselineGetFileReferences(fileName);
|
||||
}
|
||||
|
||||
public baselineGoToDefinition(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToDefinition", markerOrRange });
|
||||
this.state.baselineGoToDefinition(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineGoToDefinitionAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToDefinition", rangeText });
|
||||
this.state.baselineGoToDefinition(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineGetDefinitionAtPosition(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "getDefinitionAtPosition", markerOrRange });
|
||||
this.state.baselineGetDefinitionAtPosition(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineGetDefinitionAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "getDefinitionAtPosition", rangeText });
|
||||
this.state.baselineGetDefinitionAtPosition(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineGoToSourceDefinition(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToSourceDefinition", markerOrRange });
|
||||
this.state.baselineGoToSourceDefinition(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineGoToSourceDefinitionAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToSourceDefinition", rangeText });
|
||||
this.state.baselineGoToSourceDefinition(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineGoToType(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToType", markerOrRange });
|
||||
this.state.baselineGoToType(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineGoToTypeAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToType", rangeText });
|
||||
this.state.baselineGoToType(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineGoToImplementation(...markerOrRange: FourSlash.MarkerOrNameOrRange[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToImplementation", markerOrRange });
|
||||
this.state.baselineGoToImplementation(markerOrRange, /*rangeText*/ undefined);
|
||||
}
|
||||
|
||||
public baselineGoToImplementationAtRangesWithText(...rangeText: string[]) {
|
||||
this.state.verifyBaselineCommands({ type: "goToImplementation", rangeText });
|
||||
this.state.baselineGoToImplementation(/*markerOrRange*/ undefined, rangeText);
|
||||
}
|
||||
|
||||
public baselineDocumentHighlights(markerOrRange?: ArrayOrSingle<FourSlash.MarkerOrNameOrRange>, options?: VerifyDocumentHighlightsOptions) {
|
||||
this.state.verifyBaselineCommands({ type: "documentHighlights", markerOrRange, options });
|
||||
this.state.baselineDocumentHighlights(markerOrRange, /*rangeText*/ undefined, options);
|
||||
}
|
||||
|
||||
public baselineDocumentHighlightsAtRangesWithText(rangeText?: ArrayOrSingle<string>, options?: VerifyDocumentHighlightsOptions) {
|
||||
this.state.verifyBaselineCommands({ type: "documentHighlights", rangeText, options });
|
||||
this.state.baselineDocumentHighlights(/*markerOrRange*/ undefined, rangeText, options);
|
||||
}
|
||||
|
||||
public noErrors() {
|
||||
@@ -574,11 +570,11 @@ export class Verify extends VerifyNegatable {
|
||||
}
|
||||
|
||||
public baselineRename(markerOrRange?: ArrayOrSingle<FourSlash.MarkerOrNameOrRange>, options?: RenameOptions) {
|
||||
this.state.verifyBaselineCommands({ type: "findRenameLocations", markerOrRange, options });
|
||||
this.state.baselineRename(markerOrRange, /*rangeText*/ undefined, options);
|
||||
}
|
||||
|
||||
public baselineRenameAtRangesWithText(rangeText?: ArrayOrSingle<string>, options?: RenameOptions) {
|
||||
this.state.verifyBaselineCommands({ type: "findRenameLocations", rangeText, options });
|
||||
this.state.baselineRename(/*markerOrRange*/ undefined, rangeText, options);
|
||||
}
|
||||
|
||||
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: FourSlash.TextSpan, displayParts: ts.SymbolDisplayPart[], documentation: ts.SymbolDisplayPart[], tags: ts.JSDocTagInfo[]) {
|
||||
@@ -1931,25 +1927,3 @@ export interface RenameOptions {
|
||||
readonly providePrefixAndSuffixTextForRename?: boolean;
|
||||
readonly quotePreference?: "auto" | "double" | "single";
|
||||
}
|
||||
export type BaselineCommandWithMarkerOrRange = {
|
||||
type: "findAllReferences" | "goToDefinition" | "getDefinitionAtPosition" | "goToSourceDefinition" | "goToType" | "goToImplementation";
|
||||
markerOrRange?: ArrayOrSingle<FourSlash.MarkerOrNameOrRange>;
|
||||
rangeText?: ArrayOrSingle<string>;
|
||||
} | {
|
||||
type: "findRenameLocations";
|
||||
markerOrRange?: ArrayOrSingle<FourSlash.MarkerOrNameOrRange>;
|
||||
rangeText?: ArrayOrSingle<string>;
|
||||
options?: RenameOptions;
|
||||
} | {
|
||||
type: "documentHighlights";
|
||||
markerOrRange?: ArrayOrSingle<FourSlash.MarkerOrNameOrRange>;
|
||||
rangeText?: ArrayOrSingle<string>;
|
||||
options?: VerifyDocumentHighlightsOptions;
|
||||
};
|
||||
export type BaselineCommand = BaselineCommandWithMarkerOrRange | {
|
||||
type: "getFileReferences";
|
||||
fileName: ArrayOrSingle<string>;
|
||||
} | {
|
||||
type: "customWork";
|
||||
work: () => string | undefined;
|
||||
};
|
||||
|
||||
@@ -262,7 +262,7 @@ export namespace Compiler {
|
||||
export const es2015DefaultLibFileName = "lib.es2015.d.ts";
|
||||
|
||||
// Cache of lib files from "built/local"
|
||||
let libFileNameSourceFileMap: Map<string, ts.SourceFile> | undefined;
|
||||
export let libFileNameSourceFileMap: Map<string, ts.SourceFile> | undefined;
|
||||
|
||||
export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile | undefined {
|
||||
if (!isDefaultLibraryFile(fileName)) {
|
||||
|
||||
@@ -14,6 +14,14 @@ import * as vpath from "./_namespaces/vpath";
|
||||
import {
|
||||
incrementalVerifier,
|
||||
} from "./incrementalUtils";
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
HarnessLSCouldNotResolveModule,
|
||||
Logger,
|
||||
} from "./tsserverLogger";
|
||||
import {
|
||||
createWatchUtils,
|
||||
} from "./watchUtils";
|
||||
|
||||
export function makeDefaultProxy(info: ts.server.PluginCreateInfo): ts.LanguageService {
|
||||
const proxy = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null
|
||||
@@ -122,6 +130,7 @@ export interface LanguageServiceAdapter {
|
||||
getLanguageService(): ts.LanguageService;
|
||||
getClassifier(): ts.Classifier;
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo;
|
||||
getLogger(): Logger | undefined;
|
||||
}
|
||||
|
||||
export abstract class LanguageServiceAdapterHost {
|
||||
@@ -326,6 +335,7 @@ class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts
|
||||
|
||||
export class NativeLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
private host: NativeLanguageServiceHost;
|
||||
getLogger = ts.returnUndefined;
|
||||
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
this.host = new NativeLanguageServiceHost(cancellationToken, options);
|
||||
}
|
||||
@@ -370,10 +380,18 @@ class SessionClientHost extends NativeLanguageServiceHost implements ts.server.S
|
||||
}
|
||||
}
|
||||
|
||||
class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
|
||||
interface ServerHostFileWatcher {
|
||||
cb: ts.FileWatcherCallback;
|
||||
pollingInterval: ts.PollingInterval;
|
||||
}
|
||||
interface ServerHostDirectoryWatcher {
|
||||
cb: ts.DirectoryWatcherCallback;
|
||||
}
|
||||
class SessionServerHost implements ts.server.ServerHost {
|
||||
args: string[] = [];
|
||||
newLine: string;
|
||||
useCaseSensitiveFileNames = false;
|
||||
watchUtils = createWatchUtils<ServerHostFileWatcher, ServerHostDirectoryWatcher>("watchedFiles", "watchedDirectories");
|
||||
|
||||
constructor(private host: NativeLanguageServiceHost) {
|
||||
this.newLine = this.host.getNewLine();
|
||||
@@ -440,61 +458,28 @@ class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
|
||||
return this.host.readDirectory(path, extensions, exclude, include, depth);
|
||||
}
|
||||
|
||||
watchFile(): ts.FileWatcher {
|
||||
return { close: ts.noop };
|
||||
watchFile(file: string, cb: ts.FileWatcherCallback, pollingInterval: ts.PollingInterval) {
|
||||
return this.watchUtils.pollingWatch(file, { cb, pollingInterval });
|
||||
}
|
||||
|
||||
watchDirectory(): ts.FileWatcher {
|
||||
return { close: ts.noop };
|
||||
watchDirectory(dir: string, cb: ts.DirectoryWatcherCallback, recursive: boolean): ts.FileWatcher {
|
||||
return this.watchUtils.fsWatch(dir, recursive, { cb });
|
||||
}
|
||||
|
||||
close = ts.noop;
|
||||
|
||||
info(message: string): void {
|
||||
this.host.log(message);
|
||||
setTimeout(_callback: (...args: any[]) => void, _ms: number, ..._args: any[]): any {
|
||||
// Currently none of the tests use this and we would want something thats deterministic like unittests where we tell which callbacks to invoke
|
||||
}
|
||||
|
||||
msg(message: string): void {
|
||||
this.host.log(message);
|
||||
clearTimeout(_timeoutId: any): void {
|
||||
// Currently none of the tests use this and we would want something thats deterministic like unittests where we tell which callbacks to invoke
|
||||
}
|
||||
|
||||
loggingEnabled() {
|
||||
return true;
|
||||
setImmediate(_callback: (...args: any[]) => void, ..._args: any[]): any {
|
||||
// Currently none of the tests use this and we would want something thats deterministic like unittests where we tell which callbacks to invoke
|
||||
}
|
||||
|
||||
getLogFileName(): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
hasLevel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
startGroup() {
|
||||
throw ts.notImplemented();
|
||||
}
|
||||
endGroup() {
|
||||
throw ts.notImplemented();
|
||||
}
|
||||
|
||||
perftrc(message: string): void {
|
||||
return this.host.log(message);
|
||||
}
|
||||
|
||||
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
|
||||
return setTimeout(callback, ms, ...args);
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId: any): void {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
setImmediate(callback: (...args: any[]) => void, _ms: number, ...args: any[]): any {
|
||||
return setImmediate(callback, args);
|
||||
}
|
||||
|
||||
clearImmediate(timeoutId: any): void {
|
||||
clearImmediate(timeoutId);
|
||||
clearImmediate(_timeoutId: any): void {
|
||||
// Currently none of the tests use this and we would want something thats deterministic like unittests where we tell which callbacks to invoke
|
||||
}
|
||||
|
||||
createHash(s: string) {
|
||||
@@ -597,22 +582,36 @@ class SessionServerHost implements ts.server.ServerHost, ts.server.Logger {
|
||||
default:
|
||||
return {
|
||||
module: undefined,
|
||||
error: new Error("Could not resolve module"),
|
||||
error: new Error(HarnessLSCouldNotResolveModule),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FourslashSession extends ts.server.Session {
|
||||
constructor(opts: ts.server.SessionOptions, readonly baselineHost: (when: string) => void) {
|
||||
super(opts);
|
||||
}
|
||||
getText(fileName: string) {
|
||||
return ts.getSnapshotText(this.projectService.getDefaultProjectForFile(ts.server.toNormalizedPath(fileName), /*ensureProject*/ true)!.getScriptSnapshot(fileName)!);
|
||||
}
|
||||
|
||||
protected override toStringMessage(message: string): string {
|
||||
return JSON.stringify(JSON.parse(message), undefined, 2);
|
||||
}
|
||||
|
||||
public override onMessage(message: string): void {
|
||||
this.baselineHost("Before Request");
|
||||
super.onMessage(message);
|
||||
this.baselineHost("After Request");
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
private host: SessionClientHost;
|
||||
private client: ts.server.SessionClient;
|
||||
private server: FourslashSession;
|
||||
private logger: Logger;
|
||||
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
// This is the main host that tests use to direct tests
|
||||
const clientHost = new SessionClientHost(cancellationToken, options);
|
||||
@@ -621,6 +620,7 @@ export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
// This host is just a proxy for the clientHost, it uses the client
|
||||
// host to answer server queries about files on disk
|
||||
const serverHost = new SessionServerHost(clientHost);
|
||||
this.logger = createLoggerWithInMemoryLogs(serverHost, /*sanitizeLibs*/ true);
|
||||
const opts: ts.server.SessionOptions = {
|
||||
host: serverHost,
|
||||
cancellationToken: ts.server.nullCancellationToken,
|
||||
@@ -629,11 +629,17 @@ export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
typingsInstaller: { ...ts.server.nullTypingsInstaller, globalTypingsCacheLocation: "/Library/Caches/typescript" },
|
||||
byteLength: Buffer.byteLength,
|
||||
hrtime: process.hrtime,
|
||||
logger: serverHost,
|
||||
logger: this.logger,
|
||||
canUseEvents: true,
|
||||
incrementalVerifier,
|
||||
};
|
||||
this.server = new FourslashSession(opts);
|
||||
this.server = new FourslashSession(opts, when => {
|
||||
const baseline = serverHost.watchUtils.serializeWatches();
|
||||
if (baseline.length) {
|
||||
this.logger.log(when);
|
||||
baseline.forEach(s => this.logger.log(s));
|
||||
}
|
||||
});
|
||||
|
||||
// Fake the connection between the client and the server
|
||||
serverHost.writeMessage = client.onMessage.bind(client);
|
||||
@@ -647,6 +653,9 @@ export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
this.client = client;
|
||||
this.host = clientHost;
|
||||
}
|
||||
getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import * as ts from "./_namespaces/ts";
|
||||
import {
|
||||
Compiler,
|
||||
} from "./harnessIO";
|
||||
|
||||
export const HarnessLSCouldNotResolveModule = "HarnessLanguageService:: Could not resolve module";
|
||||
|
||||
export function replaceAll(source: string, searchValue: string, replaceValue: string): string {
|
||||
let result: string | undefined = (source as string & { replaceAll: typeof source.replace; }).replaceAll?.(searchValue, replaceValue);
|
||||
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = "";
|
||||
const searchLength = searchValue.length;
|
||||
while (true) {
|
||||
const index = source.indexOf(searchValue);
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
result += source.slice(0, index);
|
||||
result += replaceValue;
|
||||
source = source.slice(index + searchLength);
|
||||
}
|
||||
result += source;
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface Logger extends ts.server.Logger {
|
||||
logs?: string[];
|
||||
log(s: string): void;
|
||||
host?: ts.server.ServerHost;
|
||||
}
|
||||
|
||||
export function nullLogger(): Logger {
|
||||
return {
|
||||
close: ts.noop,
|
||||
hasLevel: ts.returnFalse,
|
||||
loggingEnabled: ts.returnFalse,
|
||||
perftrc: ts.noop,
|
||||
info: ts.noop,
|
||||
msg: ts.noop,
|
||||
startGroup: ts.noop,
|
||||
endGroup: ts.noop,
|
||||
getLogFileName: ts.returnUndefined,
|
||||
log: ts.noop,
|
||||
isTestLogger: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function createHasErrorMessageLogger(): Logger {
|
||||
return {
|
||||
...nullLogger(),
|
||||
msg: (s, type) => ts.Debug.fail(`Error: ${s}, type: ${type}`),
|
||||
};
|
||||
}
|
||||
function handleLoggerGroup(logger: Logger): Logger {
|
||||
let inGroup = false;
|
||||
let firstInGroup = false;
|
||||
logger.startGroup = () => {
|
||||
inGroup = true;
|
||||
firstInGroup = true;
|
||||
};
|
||||
logger.endGroup = () => inGroup = false;
|
||||
const originalInfo = logger.info;
|
||||
logger.info = s => msg(s, ts.server.Msg.Info, s => originalInfo.call(logger, s));
|
||||
logger.log = s => originalInfo.call(logger, s);
|
||||
return logger;
|
||||
|
||||
function msg(s: string, type = ts.server.Msg.Err, write: (s: string) => void) {
|
||||
s = `[${nowString(logger)}] ${s}`;
|
||||
if (!inGroup || firstInGroup) s = padStringRight(type + " seq", " ") + s;
|
||||
if (ts.Debug.isDebugging) console.log(s);
|
||||
write(s);
|
||||
}
|
||||
|
||||
function padStringRight(str: string, padding: string) {
|
||||
return (str + padding).slice(0, padding.length);
|
||||
}
|
||||
}
|
||||
|
||||
export function nowString(logger: Logger) {
|
||||
// E.g. "12:34:56.789"
|
||||
logger.host?.now?.(); // To increment the time but not print it to avoid the baseline updates
|
||||
return `hh:mm:ss:mss`;
|
||||
}
|
||||
|
||||
export function createLoggerWritingToConsole(host: ts.server.ServerHost) {
|
||||
return handleLoggerGroup({
|
||||
...nullLogger(),
|
||||
hasLevel: ts.returnTrue,
|
||||
loggingEnabled: ts.returnTrue,
|
||||
perftrc: s => console.log(s),
|
||||
info: s => console.log(s),
|
||||
msg: (s, type) => console.log(`${type}:: ${s}`),
|
||||
host,
|
||||
});
|
||||
}
|
||||
|
||||
export function sanitizeLog(s: string): string {
|
||||
s = s.replace(/Elapsed::?\s*\d+(?:\.\d+)?ms/g, "Elapsed:: *ms");
|
||||
s = s.replace(/"updateGraphDurationMs":\s*\d+(?:\.\d+)?/g, `"updateGraphDurationMs": *`);
|
||||
s = s.replace(/"createAutoImportProviderProgramDurationMs":\s*\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs": *`);
|
||||
s = replaceAll(s, ts.version, "FakeVersion");
|
||||
s = s.replace(/getCompletionData: Get current token: \d+(?:\.\d+)?/g, `getCompletionData: Get current token: *`);
|
||||
s = s.replace(/getCompletionData: Is inside comment: \d+(?:\.\d+)?/g, `getCompletionData: Is inside comment: *`);
|
||||
s = s.replace(/getCompletionData: Get previous token: \d+(?:\.\d+)?/g, `getCompletionData: Get previous token: *`);
|
||||
s = s.replace(/getCompletionsAtPosition: isCompletionListBlocker: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: isCompletionListBlocker: *`);
|
||||
s = s.replace(/getCompletionData: Semantic work: \d+(?:\.\d+)?/g, `getCompletionData: Semantic work: *`);
|
||||
s = s.replace(/getCompletionsAtPosition: getCompletionEntriesFromSymbols: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: getCompletionEntriesFromSymbols: *`);
|
||||
s = s.replace(/forEachExternalModuleToImportFrom autoImportProvider: \d+(?:\.\d+)?/g, `forEachExternalModuleToImportFrom autoImportProvider: *`);
|
||||
s = s.replace(/getExportInfoMap: done in \d+(?:\.\d+)?/g, `getExportInfoMap: done in *`);
|
||||
s = s.replace(/collectAutoImports: \d+(?:\.\d+)?/g, `collectAutoImports: *`);
|
||||
s = s.replace(/continuePreviousIncompleteResponse: \d+(?:\.\d+)?/g, `continuePreviousIncompleteResponse: *`);
|
||||
s = s.replace(/dependencies in \d+(?:\.\d+)?/g, `dependencies in *`);
|
||||
s = s.replace(/"exportMapKey":\s*"\d+ \d+ /g, match => match.replace(/ \d+ /, ` * `));
|
||||
s = s.replace(/getIndentationAtPosition: getCurrentSourceFile: \d+(?:\.\d+)?/, `getIndentationAtPosition: getCurrentSourceFile: *`);
|
||||
s = s.replace(/getIndentationAtPosition: computeIndentation\s*: \d+(?:\.\d+)?/, `getIndentationAtPosition: computeIndentation: *`);
|
||||
s = sanitizeHarnessLSException(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
function sanitizeHarnessLSException(s: string) {
|
||||
const index = s.indexOf(HarnessLSCouldNotResolveModule);
|
||||
if (index > 0) s = s.substring(0, index) + HarnessLSCouldNotResolveModule;
|
||||
return s;
|
||||
}
|
||||
|
||||
export function sanitizeLibFileText(s: string): string {
|
||||
Compiler.libFileNameSourceFileMap?.forEach((lib, fileName) => {
|
||||
s = replaceAll(s, JSON.stringify(lib.text), `${fileName}-Text`);
|
||||
s = replaceAll(s, lib.text, `${fileName}-Text`);
|
||||
});
|
||||
return s;
|
||||
}
|
||||
|
||||
export function createLoggerWithInMemoryLogs(host: ts.server.ServerHost, sanitizeLibs?: true): Logger {
|
||||
const logger = createHasErrorMessageLogger();
|
||||
const logs: string[] = [];
|
||||
if (host) logs.push(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
|
||||
return handleLoggerGroup({
|
||||
...logger,
|
||||
logs,
|
||||
hasLevel: ts.returnTrue,
|
||||
loggingEnabled: ts.returnTrue,
|
||||
info: s => logs.push((sanitizeLibs ? sanitizeLibFileText : ts.identity)(sanitizeLog(s))),
|
||||
host,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
arrayFrom,
|
||||
compareStringsCaseSensitive,
|
||||
contains,
|
||||
createMultiMap,
|
||||
Debug,
|
||||
FileWatcher,
|
||||
FileWatcherCallback,
|
||||
MultiMap,
|
||||
PollingInterval,
|
||||
} from "./_namespaces/ts";
|
||||
|
||||
export interface TestFileWatcher {
|
||||
cb: FileWatcherCallback;
|
||||
pollingInterval: PollingInterval;
|
||||
}
|
||||
|
||||
export interface TestFsWatcher<DirCallback> {
|
||||
cb: DirCallback;
|
||||
inode: number | undefined;
|
||||
}
|
||||
|
||||
export interface WatchUtils<PollingWatcherData, FsWatcherData, Path extends string = string> {
|
||||
pollingWatches: MultiMap<Path, PollingWatcherData>;
|
||||
fsWatches: MultiMap<Path, FsWatcherData>;
|
||||
fsWatchesRecursive: MultiMap<Path, FsWatcherData>;
|
||||
pollingWatch(path: Path, data: PollingWatcherData): FileWatcher;
|
||||
fsWatch(path: Path, recursive: boolean, data: FsWatcherData): FileWatcher;
|
||||
serializeWatches(baseline?: string[]): string[];
|
||||
getHasWatchChanges(): boolean;
|
||||
setHasWatchChanges(): void;
|
||||
}
|
||||
|
||||
export function createWatchUtils<PollingWatcherData, FsWatcherData, Path extends string = string>(
|
||||
pollingWatchesName: string,
|
||||
fsWatchesName: string,
|
||||
): WatchUtils<PollingWatcherData, FsWatcherData, Path> {
|
||||
const pollingWatches = createMultiMap<Path, PollingWatcherData>();
|
||||
const fsWatches = createMultiMap<Path, FsWatcherData>();
|
||||
const fsWatchesRecursive = createMultiMap<Path, FsWatcherData>();
|
||||
|
||||
let hasWatchChanges = false;
|
||||
|
||||
let serializedPollingWatches: Map<string, PollingWatcherData[]> | undefined;
|
||||
let serializedFsWatches: Map<string, FsWatcherData[]> | undefined;
|
||||
let serializedFsWatchesRecursive: Map<string, FsWatcherData[]> | undefined;
|
||||
|
||||
return {
|
||||
pollingWatches,
|
||||
fsWatches,
|
||||
fsWatchesRecursive,
|
||||
pollingWatch,
|
||||
fsWatch,
|
||||
serializeWatches,
|
||||
getHasWatchChanges: () => hasWatchChanges,
|
||||
setHasWatchChanges: () => hasWatchChanges = true,
|
||||
};
|
||||
|
||||
function createWatcher<T>(map: MultiMap<Path, T>, path: Path, callback: T): FileWatcher {
|
||||
hasWatchChanges = true;
|
||||
map.add(path, callback);
|
||||
let closed = false;
|
||||
return {
|
||||
close: () => {
|
||||
Debug.assert(!closed);
|
||||
map.remove(path, callback);
|
||||
hasWatchChanges = true;
|
||||
closed = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function pollingWatch(path: Path, data: PollingWatcherData) {
|
||||
return createWatcher(
|
||||
pollingWatches,
|
||||
path,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
function fsWatch(path: Path, recursive: boolean, data: FsWatcherData) {
|
||||
return createWatcher(
|
||||
recursive ? fsWatchesRecursive : fsWatches,
|
||||
path,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
function serializeWatches(baseline: string[] = []) {
|
||||
if (!hasWatchChanges) return baseline;
|
||||
serializedPollingWatches = serializeMultiMap(baseline, pollingWatchesName, pollingWatches, serializedPollingWatches);
|
||||
serializedFsWatches = serializeMultiMap(baseline, fsWatchesName, fsWatches, serializedFsWatches);
|
||||
serializedFsWatchesRecursive = serializeMultiMap(baseline, `${fsWatchesName}Recursive`, fsWatchesRecursive, serializedFsWatchesRecursive);
|
||||
hasWatchChanges = false;
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeMultiMap<T>(baseline: string[], caption: string, multiMap: MultiMap<string, T>, serialized: Map<string, T[]> | undefined) {
|
||||
let hasChange = diffMap(baseline, caption, multiMap, serialized, /*deleted*/ false);
|
||||
hasChange = diffMap(baseline, caption, serialized, multiMap, /*deleted*/ true) || hasChange;
|
||||
if (hasChange) {
|
||||
serialized = new Map();
|
||||
multiMap.forEach((value, key) => serialized!.set(key, new Array(...value)));
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
function diffMap<T>(
|
||||
baseline: string[],
|
||||
caption: string,
|
||||
map: Map<string, T[]> | undefined,
|
||||
old: Map<string, T[]> | undefined,
|
||||
deleted: boolean,
|
||||
) {
|
||||
let captionAdded = false;
|
||||
let baselineChanged = false;
|
||||
let hasChange = false;
|
||||
if (map) {
|
||||
for (const key of arrayFrom(map.keys()).sort(compareStringsCaseSensitive)) {
|
||||
const existing = old?.get(key);
|
||||
let addedKey = false;
|
||||
const values = map.get(key)!;
|
||||
for (const value of values) {
|
||||
const hasExisting = contains(existing, value);
|
||||
if (deleted && hasExisting) continue;
|
||||
if (!hasExisting) hasChange = true;
|
||||
if (!addedKey) {
|
||||
addBaseline(`${key}:${deleted || existing ? "" : " *new*"}`);
|
||||
addedKey = true;
|
||||
}
|
||||
addBaseline(` ${JSON.stringify(value)}${deleted || hasExisting || !existing ? "" : " *new*"}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (baselineChanged) baseline.push("");
|
||||
return hasChange;
|
||||
|
||||
function addBaseline(s: string) {
|
||||
if (!captionAdded) {
|
||||
baseline.push(`${caption}${deleted ? " *deleted*" : ""}::`);
|
||||
captionAdded = true;
|
||||
}
|
||||
baseline.push(s);
|
||||
baselineChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,18 @@ export class FourSlashRunner extends RunnerBase {
|
||||
if (testIndex >= 0) fn = fn.substr(testIndex);
|
||||
|
||||
if (justName !== "fourslash.ts") {
|
||||
it(this.testSuiteName + " test " + justName + " runs correctly", () => {
|
||||
FourSlash.runFourSlashTest(this.basePath, this.testType, fn);
|
||||
let serverLogBaseliner: FourSlash.FourSlashServerLogBaseliner = {};
|
||||
after(() => {
|
||||
serverLogBaseliner = undefined!;
|
||||
});
|
||||
it(this.testSuiteName + " test " + justName + " runs correctly", () => {
|
||||
FourSlash.runFourSlashTest(this.basePath, this.testType, fn, serverLogBaseliner);
|
||||
});
|
||||
if (this.testType === FourSlash.FourSlashTestType.Server) {
|
||||
it(this.testSuiteName + " test " + justName + " tsserver log", () => {
|
||||
serverLogBaseliner.baseline?.();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import {
|
||||
incrementalVerifier,
|
||||
} from "../../../harness/incrementalUtils";
|
||||
import {
|
||||
createHasErrorMessageLogger,
|
||||
createLoggerWithInMemoryLogs,
|
||||
Logger,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
@@ -21,133 +26,6 @@ import {
|
||||
TestServerHostTrackingWrittenFiles,
|
||||
} from "./virtualFileSystemWithWatch";
|
||||
|
||||
export function replaceAll(source: string, searchValue: string, replaceValue: string): string {
|
||||
let result: string | undefined = (source as string & { replaceAll: typeof source.replace; }).replaceAll?.(searchValue, replaceValue);
|
||||
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = "";
|
||||
const searchLength = searchValue.length;
|
||||
while (true) {
|
||||
const index = source.indexOf(searchValue);
|
||||
if (index < 0) {
|
||||
break;
|
||||
}
|
||||
result += source.slice(0, index);
|
||||
result += replaceValue;
|
||||
source = source.slice(index + searchLength);
|
||||
}
|
||||
result += source;
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface Logger extends ts.server.Logger {
|
||||
logs?: string[];
|
||||
log(s: string): void;
|
||||
host?: TestServerHost;
|
||||
}
|
||||
|
||||
export function nullLogger(): Logger {
|
||||
return {
|
||||
close: ts.noop,
|
||||
hasLevel: ts.returnFalse,
|
||||
loggingEnabled: ts.returnFalse,
|
||||
perftrc: ts.noop,
|
||||
info: ts.noop,
|
||||
msg: ts.noop,
|
||||
startGroup: ts.noop,
|
||||
endGroup: ts.noop,
|
||||
getLogFileName: ts.returnUndefined,
|
||||
log: ts.noop,
|
||||
isTestLogger: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function createHasErrorMessageLogger(): Logger {
|
||||
return {
|
||||
...nullLogger(),
|
||||
msg: (s, type) => ts.Debug.fail(`Error: ${s}, type: ${type}`),
|
||||
};
|
||||
}
|
||||
|
||||
function handleLoggerGroup(logger: Logger, host: TestServerHost | undefined): Logger {
|
||||
let inGroup = false;
|
||||
let firstInGroup = false;
|
||||
logger.startGroup = () => {
|
||||
inGroup = true;
|
||||
firstInGroup = true;
|
||||
};
|
||||
logger.endGroup = () => inGroup = false;
|
||||
logger.host = host;
|
||||
const originalInfo = logger.info;
|
||||
logger.info = s => msg(s, ts.server.Msg.Info, s => originalInfo.call(logger, s));
|
||||
logger.log = s => originalInfo.call(logger, s);
|
||||
return logger;
|
||||
|
||||
function msg(s: string, type = ts.server.Msg.Err, write: (s: string) => void) {
|
||||
s = `[${nowString(logger.host!)}] ${s}`;
|
||||
if (!inGroup || firstInGroup) s = padStringRight(type + " seq", " ") + s;
|
||||
if (ts.Debug.isDebugging) console.log(s);
|
||||
write(s);
|
||||
}
|
||||
|
||||
function padStringRight(str: string, padding: string) {
|
||||
return (str + padding).slice(0, padding.length);
|
||||
}
|
||||
}
|
||||
|
||||
export function nowString(host: TestServerHost) {
|
||||
// E.g. "12:34:56.789"
|
||||
host.now(); // To increment the time but not print it to avoid the baseline updates
|
||||
return `hh:mm:ss:mss`;
|
||||
}
|
||||
|
||||
export function createLoggerWritingToConsole(host: TestServerHost): Logger {
|
||||
return handleLoggerGroup({
|
||||
...nullLogger(),
|
||||
hasLevel: ts.returnTrue,
|
||||
loggingEnabled: ts.returnTrue,
|
||||
perftrc: s => console.log(s),
|
||||
info: s => console.log(s),
|
||||
msg: (s, type) => console.log(`${type}:: ${s}`),
|
||||
}, host);
|
||||
}
|
||||
|
||||
export function sanitizeLog(s: string): string {
|
||||
s = s.replace(/Elapsed::?\s*\d+(?:\.\d+)?ms/g, "Elapsed:: *ms");
|
||||
s = s.replace(/"updateGraphDurationMs":\s*\d+(?:\.\d+)?/g, `"updateGraphDurationMs": *`);
|
||||
s = s.replace(/"createAutoImportProviderProgramDurationMs":\s*\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs": *`);
|
||||
s = replaceAll(s, ts.version, "FakeVersion");
|
||||
s = s.replace(/getCompletionData: Get current token: \d+(?:\.\d+)?/g, `getCompletionData: Get current token: *`);
|
||||
s = s.replace(/getCompletionData: Is inside comment: \d+(?:\.\d+)?/g, `getCompletionData: Is inside comment: *`);
|
||||
s = s.replace(/getCompletionData: Get previous token: \d+(?:\.\d+)?/g, `getCompletionData: Get previous token: *`);
|
||||
s = s.replace(/getCompletionsAtPosition: isCompletionListBlocker: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: isCompletionListBlocker: *`);
|
||||
s = s.replace(/getCompletionData: Semantic work: \d+(?:\.\d+)?/g, `getCompletionData: Semantic work: *`);
|
||||
s = s.replace(/getCompletionsAtPosition: getCompletionEntriesFromSymbols: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: getCompletionEntriesFromSymbols: *`);
|
||||
s = s.replace(/forEachExternalModuleToImportFrom autoImportProvider: \d+(?:\.\d+)?/g, `forEachExternalModuleToImportFrom autoImportProvider: *`);
|
||||
s = s.replace(/getExportInfoMap: done in \d+(?:\.\d+)?/g, `getExportInfoMap: done in *`);
|
||||
s = s.replace(/collectAutoImports: \d+(?:\.\d+)?/g, `collectAutoImports: *`);
|
||||
s = s.replace(/continuePreviousIncompleteResponse: \d+(?:\.\d+)?/g, `continuePreviousIncompleteResponse: *`);
|
||||
s = s.replace(/dependencies in \d+(?:\.\d+)?/g, `dependencies in *`);
|
||||
s = s.replace(/"exportMapKey":\s*"\d+ \d+ /g, match => match.replace(/ \d+ /, ` * `));
|
||||
return s;
|
||||
}
|
||||
|
||||
export function createLoggerWithInMemoryLogs(host: TestServerHost): Logger {
|
||||
const logger = createHasErrorMessageLogger();
|
||||
const logs: string[] = [];
|
||||
if (host) logs.push(`currentDirectory:: ${host.getCurrentDirectory()} useCaseSensitiveFileNames: ${host.useCaseSensitiveFileNames}`);
|
||||
return handleLoggerGroup({
|
||||
...logger,
|
||||
logs,
|
||||
hasLevel: ts.returnTrue,
|
||||
loggingEnabled: ts.returnTrue,
|
||||
info: s => logs.push(sanitizeLog(s)),
|
||||
}, host);
|
||||
}
|
||||
|
||||
export function baselineTsserverLogs(scenario: string, subScenario: string, sessionOrService: { logger: Logger; }) {
|
||||
ts.Debug.assert(sessionOrService.logger.logs?.length); // Ensure caller used in memory logger
|
||||
Harness.Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.js`, sessionOrService.logger.logs.join("\r\n"));
|
||||
@@ -269,11 +147,11 @@ export class TestSession extends ts.server.Session {
|
||||
public override executeCommand(request: ts.server.protocol.Request) {
|
||||
if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
|
||||
this.testhost.baselineHost("Before request");
|
||||
this.logger.info(`request:${ts.server.indent(JSON.stringify(request, undefined, 2))}`);
|
||||
this.logger.info(`request:${ts.server.stringifyIndented(request)}`);
|
||||
}
|
||||
const response = super.executeCommand(request);
|
||||
if (this.logger.hasLevel(ts.server.LogLevel.verbose)) {
|
||||
this.logger.info(`response:${ts.server.indent(JSON.stringify(response.response === ts.getSupportedCodeFixes() ? { ...response, response: "ts.getSupportedCodeFixes()" } : response, undefined, 2))}`);
|
||||
this.logger.info(`response:${ts.server.stringifyIndented(response.response === ts.getSupportedCodeFixes() ? { ...response, response: "ts.getSupportedCodeFixes()" } : response)}`);
|
||||
this.testhost.baselineHost("After request");
|
||||
}
|
||||
return response;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import {
|
||||
Logger,
|
||||
nowString,
|
||||
replaceAll,
|
||||
sanitizeLog,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
ActionWatchTypingLocations,
|
||||
stringifyIndented,
|
||||
} from "../../_namespaces/ts.server";
|
||||
import {
|
||||
Logger,
|
||||
nowString,
|
||||
patchHostTimeouts,
|
||||
replaceAll,
|
||||
sanitizeLog,
|
||||
TestSessionAndServiceHost,
|
||||
} from "./tsserver";
|
||||
import {
|
||||
@@ -61,7 +63,7 @@ export function loggerToTypingsInstallerLog(logger: Logger): ts.server.typingsIn
|
||||
//
|
||||
const initialLog = sanitizeLog(s);
|
||||
const pseudoSanitizedLog = replaceAll(initialLog, `@ts${ts.versionMajorMinor}`, `@tsFakeMajor.Minor`);
|
||||
return logger.log(`TI:: [${nowString(logger.host!)}] ${pseudoSanitizedLog}`);
|
||||
return logger.log(`TI:: [${nowString(logger)}] ${pseudoSanitizedLog}`);
|
||||
},
|
||||
} : undefined;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import {
|
||||
createWatchUtils,
|
||||
} from "../../../harness/watchUtils";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import {
|
||||
arrayFrom,
|
||||
clear,
|
||||
clone,
|
||||
combinePaths,
|
||||
compareStringsCaseSensitive,
|
||||
contains,
|
||||
createGetCanonicalFileName,
|
||||
createMultiMap,
|
||||
createSystemWatchFunctions,
|
||||
Debug,
|
||||
directorySeparator,
|
||||
FileSystemEntryKind,
|
||||
FileWatcher,
|
||||
FileWatcherCallback,
|
||||
FileWatcherEventKind,
|
||||
filterMutate,
|
||||
@@ -285,9 +284,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
readonly immediateCallbacks = new Callbacks(this, "Immedidate");
|
||||
readonly screenClears: number[] = [];
|
||||
|
||||
readonly watchedFiles = createMultiMap<Path, TestFileWatcher>();
|
||||
readonly fsWatches = createMultiMap<Path, TestFsWatcher>();
|
||||
readonly fsWatchesRecursive = createMultiMap<Path, TestFsWatcher>();
|
||||
readonly watchUtils = createWatchUtils<TestFileWatcher, TestFsWatcher, Path>("PolledWatches", "FsWatches");
|
||||
runWithFallbackPolling: boolean;
|
||||
public readonly useCaseSensitiveFileNames: boolean;
|
||||
public readonly newLine: string;
|
||||
@@ -623,24 +620,8 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
this.removeFileOrFolder(currentEntry);
|
||||
}
|
||||
|
||||
hasWatchChanges?: boolean;
|
||||
private createWatcher<T>(map: MultiMap<Path, T>, path: Path, callback: T): FileWatcher {
|
||||
this.hasWatchChanges = true;
|
||||
map.add(path, callback);
|
||||
let closed = false;
|
||||
return {
|
||||
close: () => {
|
||||
Debug.assert(!closed);
|
||||
map.remove(path, callback);
|
||||
this.hasWatchChanges = true;
|
||||
closed = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private watchFileWorker(fileName: string, cb: FileWatcherCallback, pollingInterval: PollingInterval) {
|
||||
return this.createWatcher(
|
||||
this.watchedFiles,
|
||||
return this.watchUtils.pollingWatch(
|
||||
this.toFullPath(fileName),
|
||||
{ cb, pollingInterval },
|
||||
);
|
||||
@@ -655,9 +636,9 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
const path = this.toFullPath(fileOrDirectory);
|
||||
// Error if the path does not exist
|
||||
if (this.inodeWatching && !this.inodes?.has(path)) throw new Error();
|
||||
const result = this.createWatcher(
|
||||
recursive ? this.fsWatchesRecursive : this.fsWatches,
|
||||
const result = this.watchUtils.fsWatch(
|
||||
path,
|
||||
recursive,
|
||||
{
|
||||
cb,
|
||||
inode: this.inodes?.get(path),
|
||||
@@ -668,7 +649,7 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
}
|
||||
|
||||
invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind, modifiedTime: Date | undefined) {
|
||||
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb }) => cb(fileFullPath, eventKind, modifiedTime));
|
||||
invokeWatcherCallbacks(this.watchUtils.pollingWatches.get(this.toPath(fileFullPath)), ({ cb }) => cb(fileFullPath, eventKind, modifiedTime));
|
||||
}
|
||||
|
||||
private fsWatchCallback(map: MultiMap<Path, TestFsWatcher>, fullPath: string, eventName: "rename" | "change", modifiedTime: Date | undefined, entryFullPath: string | undefined, useTildeSuffix: boolean | undefined) {
|
||||
@@ -684,11 +665,11 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
}
|
||||
|
||||
invokeFsWatchesCallbacks(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string, useTildeSuffix?: boolean) {
|
||||
this.fsWatchCallback(this.fsWatches, fullPath, eventName, modifiedTime, entryFullPath, useTildeSuffix);
|
||||
this.fsWatchCallback(this.watchUtils.fsWatches, fullPath, eventName, modifiedTime, entryFullPath, useTildeSuffix);
|
||||
}
|
||||
|
||||
invokeFsWatchesRecursiveCallbacks(fullPath: string, eventName: "rename" | "change", modifiedTime?: Date, entryFullPath?: string, useTildeSuffix?: boolean) {
|
||||
this.fsWatchCallback(this.fsWatchesRecursive, fullPath, eventName, modifiedTime, entryFullPath, useTildeSuffix);
|
||||
this.fsWatchCallback(this.watchUtils.fsWatchesRecursive, fullPath, eventName, modifiedTime, entryFullPath, useTildeSuffix);
|
||||
}
|
||||
|
||||
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
|
||||
@@ -1004,16 +985,8 @@ export class TestServerHost implements server.ServerHost, FormatDiagnosticsHost,
|
||||
baseline.push("");
|
||||
}
|
||||
|
||||
private serializedWatchedFiles: Map<string, TestFileWatcher[]> | undefined;
|
||||
private serializedFsWatches: Map<string, TestFsWatcher[]> | undefined;
|
||||
private serializedFsWatchesRecursive: Map<string, TestFsWatcher[]> | undefined;
|
||||
serializeWatches(baseline: string[] = []) {
|
||||
if (!this.hasWatchChanges) return baseline;
|
||||
this.serializedWatchedFiles = serializeMultiMap(baseline, "PolledWatches", this.watchedFiles, this.serializedWatchedFiles);
|
||||
this.serializedFsWatches = serializeMultiMap(baseline, "FsWatches", this.fsWatches, this.serializedFsWatches);
|
||||
this.serializedFsWatchesRecursive = serializeMultiMap(baseline, "FsWatchesRecursive", this.fsWatchesRecursive, this.serializedFsWatchesRecursive);
|
||||
this.hasWatchChanges = false;
|
||||
return baseline;
|
||||
serializeWatches(baseline?: string[]) {
|
||||
return this.watchUtils.serializeWatches(baseline);
|
||||
}
|
||||
|
||||
realpath(s: string): string {
|
||||
@@ -1115,56 +1088,6 @@ function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsE
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeMultiMap<T>(baseline: string[], caption: string, multiMap: MultiMap<string, T>, serialized: Map<string, T[]> | undefined) {
|
||||
let hasChange = diffMap(baseline, caption, multiMap, serialized, /*deleted*/ false);
|
||||
hasChange = diffMap(baseline, caption, serialized, multiMap, /*deleted*/ true) || hasChange;
|
||||
if (hasChange) {
|
||||
serialized = new Map();
|
||||
multiMap.forEach((value, key) => serialized!.set(key, new Array(...value)));
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
function diffMap<T>(
|
||||
baseline: string[],
|
||||
caption: string,
|
||||
map: Map<string, T[]> | undefined,
|
||||
old: Map<string, T[]> | undefined,
|
||||
deleted: boolean,
|
||||
) {
|
||||
let captionAdded = false;
|
||||
let baselineChanged = false;
|
||||
let hasChange = false;
|
||||
if (map) {
|
||||
for (const key of arrayFrom(map.keys()).sort(compareStringsCaseSensitive)) {
|
||||
const existing = old?.get(key);
|
||||
let addedKey = false;
|
||||
const values = map.get(key)!;
|
||||
for (const value of values) {
|
||||
const hasExisting = contains(existing, value);
|
||||
if (deleted && hasExisting) continue;
|
||||
if (!hasExisting) hasChange = true;
|
||||
if (!addedKey) {
|
||||
addBaseline(`${key}:${deleted || existing ? "" : " *new*"}`);
|
||||
addedKey = true;
|
||||
}
|
||||
addBaseline(` ${JSON.stringify(value)}${deleted || hasExisting || !existing ? "" : " *new*"}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (baselineChanged) baseline.push("");
|
||||
return hasChange;
|
||||
|
||||
function addBaseline(s: string) {
|
||||
if (!captionAdded) {
|
||||
baseline.push(`${caption}${deleted ? " *deleted*" : ""}::`);
|
||||
captionAdded = true;
|
||||
}
|
||||
baseline.push(s);
|
||||
baselineChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
function baselineOutputs(baseline: string[], output: readonly string[], start: number, end = output.length) {
|
||||
let baselinedOutput: string[] | undefined;
|
||||
for (let i = start; i < end; i++) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
commonFile1,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
} from "../helpers/tscWatch";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent,
|
||||
} from "../../_namespaces/Utils";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import {
|
||||
IncrementalVerifierCallbacks,
|
||||
} from "../../../harness/incrementalUtils";
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
Logger,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
logDiagnostics,
|
||||
Logger,
|
||||
openFilesForSession,
|
||||
TestProjectService,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
TestServerCancellationToken,
|
||||
TestSessionRequest,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
Logger,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
Logger,
|
||||
openExternalProjectForSession,
|
||||
openFilesForSession,
|
||||
protocolTextSpanFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
ensureErrorFreeBuild,
|
||||
@@ -8,7 +11,6 @@ import {
|
||||
} from "../helpers/tscWatch";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
logConfiguredProjectsHasOpenRefStatus,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import {
|
||||
reportDocumentRegistryStats,
|
||||
} from "../../../harness/incrementalUtils";
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
createSessionWithCustomEventHandler,
|
||||
openExternalProjectForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
createSessionWithCustomEventHandler,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
Logger,
|
||||
} from "../../../../harness/tsserverLogger";
|
||||
import {
|
||||
createWatchUtils,
|
||||
WatchUtils,
|
||||
} from "../../../../harness/watchUtils";
|
||||
import * as ts from "../../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
createSessionWithCustomEventHandler,
|
||||
Logger,
|
||||
openFilesForSession,
|
||||
TestSession,
|
||||
} from "../../helpers/tsserver";
|
||||
import {
|
||||
createServerHost,
|
||||
libFile,
|
||||
serializeMultiMap,
|
||||
TestServerHost,
|
||||
} from "../../helpers/virtualFileSystemWithWatch";
|
||||
|
||||
describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
interface TestServerHostWithCustomWatch extends TestServerHost {
|
||||
factoryData: {
|
||||
watchedFiles: ts.MultiMap<string, ts.server.protocol.CreateFileWatcherEventBody>;
|
||||
watchedDirectories: ts.MultiMap<string, ts.server.protocol.CreateDirectoryWatcherEventBody>;
|
||||
watchedDirectoriesRecursive: ts.MultiMap<string, ts.server.protocol.CreateDirectoryWatcherEventBody>;
|
||||
watchUtils: WatchUtils<ts.server.protocol.CreateFileWatcherEventBody, ts.server.protocol.CreateDirectoryWatcherEventBody>;
|
||||
watchFile(data: ts.server.protocol.CreateFileWatcherEventBody): void;
|
||||
watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody): void;
|
||||
closeWatcher(data: ts.server.protocol.CloseFileWatcherEventBody): void;
|
||||
@@ -32,16 +35,11 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
logger: Logger,
|
||||
) {
|
||||
const idToClose = new Map<number, () => void>();
|
||||
let serializedWatchedFiles: Map<string, ts.server.protocol.CreateFileWatcherEventBody[]> | undefined;
|
||||
let serializedWatchedDirectories: Map<string, ts.server.protocol.CreateDirectoryWatcherEventBody[]> | undefined;
|
||||
let serializedWatchedDirectoriesRecursive: Map<string, ts.server.protocol.CreateDirectoryWatcherEventBody[]> | undefined;
|
||||
const host = logger.host as TestServerHostWithCustomWatch;
|
||||
const originalSerializeWatches = host.serializeWatches;
|
||||
host.serializeWatches = serializeWatches;
|
||||
host.factoryData = {
|
||||
watchedFiles: ts.createMultiMap(),
|
||||
watchedDirectories: ts.createMultiMap(),
|
||||
watchedDirectoriesRecursive: ts.createMultiMap(),
|
||||
watchUtils: createWatchUtils<ts.server.protocol.CreateFileWatcherEventBody, ts.server.protocol.CreateDirectoryWatcherEventBody>(`Custom WatchedFiles`, `Custom WatchedDirectories`),
|
||||
watchFile,
|
||||
watchDirectory,
|
||||
closeWatcher,
|
||||
@@ -51,22 +49,20 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
function watchFile(data: ts.server.protocol.CreateFileWatcherEventBody) {
|
||||
logger.log(`Custom watchFile: ${data.id}: ${data.path}`);
|
||||
ts.Debug.assert(!idToClose.has(data.id));
|
||||
host.factoryData.watchedFiles.add(data.path, data);
|
||||
host.hasWatchChanges = true;
|
||||
const result = host.factoryData.watchUtils.pollingWatch(data.path, data);
|
||||
idToClose.set(data.id, () => {
|
||||
logger.log(`Custom watchFile:: Close:: ${data.id}: ${data.path}`);
|
||||
host.factoryData.watchedFiles.remove(data.path, data);
|
||||
result.close();
|
||||
});
|
||||
}
|
||||
|
||||
function watchDirectory(data: ts.server.protocol.CreateDirectoryWatcherEventBody) {
|
||||
logger.log(`Custom watchDirectory: ${data.id}: ${data.path} ${data.recursive}`);
|
||||
ts.Debug.assert(!idToClose.has(data.id));
|
||||
(data.recursive ? host.factoryData.watchedDirectoriesRecursive : host.factoryData.watchedDirectories).add(data.path, data);
|
||||
host.hasWatchChanges = true;
|
||||
const result = host.factoryData.watchUtils.fsWatch(data.path, data.recursive, data);
|
||||
idToClose.set(data.id, () => {
|
||||
logger.log(`Custom watchDirectory:: Close:: ${data.id}: ${data.path} ${data.recursive}`);
|
||||
(data.recursive ? host.factoryData.watchedDirectoriesRecursive : host.factoryData.watchedDirectories).remove(data.path, data);
|
||||
result.close();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,18 +70,18 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
const close = idToClose.get(data.id);
|
||||
if (close) {
|
||||
idToClose.delete(data.id);
|
||||
host.hasWatchChanges = true;
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
function serializeWatches(baseline: string[] = []) {
|
||||
const hasWatchChanges = host.hasWatchChanges;
|
||||
if (host.factoryData.watchUtils.getHasWatchChanges()) host.watchUtils.setHasWatchChanges();
|
||||
const hasWatchChanges = host.watchUtils.getHasWatchChanges();
|
||||
originalSerializeWatches.call(host, baseline);
|
||||
if (!hasWatchChanges) return baseline;
|
||||
serializedWatchedFiles = serializeMultiMap(baseline, `Custom WatchedFiles`, host.factoryData.watchedFiles, serializedWatchedFiles);
|
||||
serializedWatchedDirectories = serializeMultiMap(baseline, `Custom WatchedDirectories:Recursive`, host.factoryData.watchedDirectoriesRecursive, serializedWatchedDirectories);
|
||||
serializedWatchedDirectoriesRecursive = serializeMultiMap(baseline, `Custom WatchedDirectories`, host.factoryData.watchedDirectories, serializedWatchedDirectoriesRecursive);
|
||||
if (hasWatchChanges) {
|
||||
host.factoryData.watchUtils.setHasWatchChanges();
|
||||
host.factoryData.watchUtils.serializeWatches(baseline);
|
||||
}
|
||||
return baseline;
|
||||
}
|
||||
}
|
||||
@@ -100,7 +96,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
function addFile(session: TestSession, path: string) {
|
||||
updateFileOnHost(session, path, "Add file");
|
||||
session.logger.log("Custom watch");
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchedDirectoriesRecursive.get("/user/username/projects/myproject")?.forEach(data =>
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.fsWatchesRecursive.get("/user/username/projects/myproject")?.forEach(data =>
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: { id: data.id, path, eventType: "create" },
|
||||
@@ -112,7 +108,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
function changeFile(session: TestSession, path: string) {
|
||||
updateFileOnHost(session, path, "Change File");
|
||||
session.logger.log("Custom watch");
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchedFiles.get(path)?.forEach(data =>
|
||||
(session.logger.host as TestServerHostWithCustomWatch).factoryData.watchUtils.pollingWatches.get(path)?.forEach(data =>
|
||||
session.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
|
||||
command: ts.server.protocol.CommandTypes.WatchChange,
|
||||
arguments: { id: data.id, path, eventType: "update" },
|
||||
@@ -135,7 +131,7 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
|
||||
it("canUseWatchEvents", () => {
|
||||
const { host, logger } = setup();
|
||||
const session = createSessionWithCustomEventHandler(logger.host!, { canUseWatchEvents: true, logger }, handleWatchEvents);
|
||||
const session = createSessionWithCustomEventHandler(host, { canUseWatchEvents: true, logger }, handleWatchEvents);
|
||||
openFilesForSession(["/user/username/projects/myproject/a.ts"], session);
|
||||
|
||||
// Directory watcher
|
||||
@@ -169,8 +165,8 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
|
||||
});
|
||||
|
||||
it("canUseWatchEvents without canUseEvents", () => {
|
||||
const { logger } = setup();
|
||||
const session = createSession(logger.host!, { canUseEvents: false, logger });
|
||||
const { host, logger } = setup();
|
||||
const session = createSession(host, { canUseEvents: false, logger });
|
||||
openFilesForSession(["/user/username/projects/myproject/a.ts"], session);
|
||||
|
||||
// Directory watcher
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
getSymlinkedExtendsSys,
|
||||
} from "../helpers/extends";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
logConfiguredProjectsHasOpenRefStatus,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
protocol,
|
||||
} from "../../_namespaces/ts.server";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolTextSpanFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
textSpanFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
protocol,
|
||||
} from "../../_namespaces/ts.server";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
toExternalFile,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
verifyGetErrRequest,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent,
|
||||
@@ -8,7 +11,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
logInferredProjectsOrphanStatus,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
commonFile1,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
} from "../helpers/tscWatch";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
TestSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
logDiagnostics,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
getServerHosForLibResolution,
|
||||
} from "../helpers/libraryResolution";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
setCompilerOptionsForInferredProjectsRequestForSession,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent,
|
||||
@@ -16,7 +19,6 @@ import {
|
||||
} from "../helpers/solutionBuilder";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolTextSpanFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
TestSessionOptions,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
defer,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
appendAllScriptInfos,
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
ensureErrorFreeBuild,
|
||||
} from "../helpers/solutionBuilder";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolToLocation,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
solutionBuildWithBaseline,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createHostWithSolutionBuild,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createHostWithSolutionBuild,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
TestSession,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
commonFile1,
|
||||
@@ -6,7 +9,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
logConfiguredProjectsHasOpenRefStatus,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
logInferredProjectsOrphanStatus,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import * as Utils from "../../_namespaces/Utils";
|
||||
import {
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
} from "../helpers/contents";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
|
||||
@@ -5,12 +5,12 @@ import {
|
||||
import {
|
||||
incrementalVerifier,
|
||||
} from "../../../harness/incrementalUtils";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
createHasErrorMessageLogger,
|
||||
nullLogger,
|
||||
} from "../helpers/tsserver";
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as Harness from "../../_namespaces/Harness";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
|
||||
let lastWrittenToHost: string;
|
||||
const noopFileWatcher: ts.FileWatcher = { close: ts.noop };
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
dedent,
|
||||
@@ -5,7 +8,6 @@ import {
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolLocationFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openExternalProjectForSession,
|
||||
openFilesForSession,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
} from "../helpers/tsserver";
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
toExternalFile,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
replaceAll,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
closeFilesForSession,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
openFilesForSession,
|
||||
patchHostTimeouts,
|
||||
replaceAll,
|
||||
TestSessionRequest,
|
||||
toExternalFile,
|
||||
} from "../helpers/tsserver";
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import {
|
||||
createLoggerWithInMemoryLogs,
|
||||
Logger,
|
||||
} from "../../../harness/tsserverLogger";
|
||||
import * as ts from "../../_namespaces/ts";
|
||||
import {
|
||||
commonFile1,
|
||||
@@ -5,10 +9,8 @@ import {
|
||||
} from "../helpers/tscWatch";
|
||||
import {
|
||||
baselineTsserverLogs,
|
||||
createLoggerWithInMemoryLogs,
|
||||
createProjectService,
|
||||
createSession,
|
||||
Logger,
|
||||
openExternalProjectForSession,
|
||||
openFilesForSession,
|
||||
protocolFileLocationFromSubstring,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === Auto Imports ===
|
||||
```ts
|
||||
// @Filename: /main.ts
|
||||
/*|*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === Auto Imports ===
|
||||
```ts
|
||||
// @Filename: /main.ts
|
||||
/*|*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === Auto Imports ===
|
||||
```ts
|
||||
// @Filename: /main.ts
|
||||
import { Component } from "./Component.tsx";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === Auto Imports ===
|
||||
```ts
|
||||
// @Filename: /main.ts
|
||||
import { Component } from "./local.js";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === Auto Imports ===
|
||||
```ts
|
||||
// @Filename: /a.ts
|
||||
/*|*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >var a = [10, 20, 30];
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >var x = 10;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >while (true) {
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >class Greeter {
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >declare class Greeter {
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >module Foo.Bar {
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >var x = 10;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >const c1 = false;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >debugger;
|
||||
~~~~~~~~~ => Pos: (0 to 8) SpanInfo: {"start":0,"length":8}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >declare function ClassDecorator1(target: Function): void;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// === breakpoints ===
|
||||
|
||||
1 >var i = 0;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user