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:
Sheetal Nandi
2023-10-16 16:08:13 -07:00
committed by GitHub
parent d6171b9d0b
commit 508e52d13a
735 changed files with 110586 additions and 1046 deletions
+294 -256
View File
@@ -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 {
+17 -43
View File
@@ -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;
};
+1 -1
View File
@@ -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)) {
+58 -49
View File
@@ -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;
}
+150
View File
@@ -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,
});
}
+147
View File
@@ -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;
}
}
+11 -2
View File
@@ -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?.();
});
}
}
});
});
+7 -129
View File
@@ -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";
+3 -1
View File
@@ -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";
+3 -1
View File
@@ -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,
+3 -1
View File
@@ -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";
+3 -1
View File
@@ -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,
+3 -1
View File
@@ -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,
+3 -3
View File
@@ -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