diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index d829ce0b83e..603a78b5f75 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -16,6 +16,7 @@
///
///
///
+///
module FourSlash {
ts.disableIncrementalParsing = false;
@@ -245,11 +246,9 @@ module FourSlash {
export class TestState {
// Language service instance
- public languageServiceShimHost: Harness.LanguageService.TypeScriptLS;
+ private languageServiceAdapterHost: Harness.LanguageService.LanguageServiceAdapterHost;
private languageService: ts.LanguageService;
-
- // A reference to the language service's compiler state's compiler instance
- private compiler: () => { getSyntaxTree(fileName: string): ts.SourceFile };
+ private cancellationToken: TestCancellationToken;
// The current caret position in the active file
public currentCaretPosition = 0;
@@ -263,8 +262,6 @@ module FourSlash {
public formatCodeOptions: ts.FormatCodeOptions;
- public cancellationToken: TestCancellationToken;
-
private scenarioActions: string[] = [];
private taoInvalidReason: string = null;
@@ -275,19 +272,31 @@ module FourSlash {
private addMatchedInputFile(referenceFilePath: string) {
var inputFile = this.inputFiles[referenceFilePath];
if (inputFile && !Harness.isLibraryFile(referenceFilePath)) {
- this.languageServiceShimHost.addScript(referenceFilePath, inputFile);
+ this.languageServiceAdapterHost.addScript(referenceFilePath, inputFile);
}
}
- constructor(public testData: FourSlashData) {
- // Initialize the language service with all the scripts
+ private getLanguageServiceAdapter(testType: FourSlashTestType, cancellationToken: TestCancellationToken, compilationOptions: ts.CompilerOptions): Harness.LanguageService.LanguageServiceAdapter {
+ switch (testType) {
+ case FourSlashTestType.Native:
+ return new Harness.LanguageService.NativeLanugageServiceAdapter(cancellationToken, compilationOptions);
+ case FourSlashTestType.Shims:
+ return new Harness.LanguageService.ShimLanugageServiceAdapter(cancellationToken, compilationOptions);
+ default:
+ throw new Error("Unknown FourSlash test type: ");
+ }
+ }
+
+ constructor(private basePath: string, private testType: FourSlashTestType, public testData: FourSlashData) {
+ // Create a new Services Adapter
this.cancellationToken = new TestCancellationToken();
- this.languageServiceShimHost = new Harness.LanguageService.TypeScriptLS(this.cancellationToken);
+ var compilationOptions = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
+ var languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions);
+ this.languageServiceAdapterHost = languageServiceAdapter.getHost();
+ this.languageService = languageServiceAdapter.getLanguageService();
- var compilationSettings = convertGlobalOptionsToCompilerOptions(this.testData.globalOptions);
- this.languageServiceShimHost.setCompilationSettings(compilationSettings);
-
- var startResolveFileRef: FourSlashFile = undefined;
+ // Initialize the language service with all the scripts
+ var startResolveFileRef: FourSlashFile;
ts.forEach(testData.files, file => {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
@@ -302,18 +311,16 @@ module FourSlash {
if (startResolveFileRef) {
// Add the entry-point file itself into the languageServiceShimHost
- this.languageServiceShimHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content);
+ this.languageServiceAdapterHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content);
- var jsonResolvedResult = JSON.parse(this.languageServiceShimHost.getCoreService().getPreProcessedFileInfo(startResolveFileRef.fileName,
- createScriptSnapShot(startResolveFileRef.content)));
- var resolvedResult = jsonResolvedResult.result;
- var referencedFiles: ts.IFileReference[] = resolvedResult.referencedFiles;
- var importedFiles: ts.IFileReference[] = resolvedResult.importedFiles;
+ var resolvedResult = languageServiceAdapter.getPreProcessedFileInfo(startResolveFileRef.fileName, startResolveFileRef.content);
+ var referencedFiles: ts.FileReference[] = resolvedResult.referencedFiles;
+ var importedFiles: ts.FileReference[] = resolvedResult.importedFiles;
// Add triple reference files into language-service host
ts.forEach(referencedFiles, referenceFile => {
// Fourslash insert tests/cases/fourslash into inputFile.unitName so we will properly append the same base directory to refFile path
- var referenceFilePath = "tests/cases/fourslash/" + referenceFile.path;
+ var referenceFilePath = this.basePath + '/' + referenceFile.fileName;
this.addMatchedInputFile(referenceFilePath);
});
@@ -321,29 +328,24 @@ module FourSlash {
ts.forEach(importedFiles, importedFile => {
// Fourslash insert tests/cases/fourslash into inputFile.unitName and import statement doesn't require ".ts"
// so convert them before making appropriate comparison
- var importedFilePath = "tests/cases/fourslash/" + importedFile.path + ".ts";
+ var importedFilePath = this.basePath + '/' + importedFile.fileName + ".ts";
this.addMatchedInputFile(importedFilePath);
});
// Check if no-default-lib flag is false and if so add default library
if (!resolvedResult.isLibFile) {
- this.languageServiceShimHost.addDefaultLibrary();
+ this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.defaultLibSourceFile.text);
}
} else {
// resolveReference file-option is not specified then do not resolve any files and include all inputFiles
ts.forEachKey(this.inputFiles, fileName => {
if (!Harness.isLibraryFile(fileName)) {
- this.languageServiceShimHost.addScript(fileName, this.inputFiles[fileName]);
+ this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName]);
}
});
- this.languageServiceShimHost.addDefaultLibrary();
+ this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.defaultLibSourceFile.text);
}
- // Sneak into the language service and get its compiler so we can examine the syntax trees
- this.languageService = this.languageServiceShimHost.getLanguageService().languageService;
- var compilerState = (this.languageService).compiler;
- this.compiler = () => compilerState.compiler;
-
this.formatCodeOptions = {
IndentSize: 4,
TabSize: 4,
@@ -369,6 +371,11 @@ module FourSlash {
this.openFile(0);
}
+ private getFileContent(fileName: string): string {
+ var script = this.languageServiceAdapterHost.getScriptInfo(fileName);
+ return script.content;
+ }
+
// Entry points from fourslash.ts
public goToMarker(name = '') {
var marker = this.getMarkerByName(name);
@@ -376,8 +383,8 @@ module FourSlash {
this.openFile(marker.fileName);
}
- var scriptSnapshot = this.languageServiceShimHost.getScriptSnapshot(marker.fileName);
- if (marker.position === -1 || marker.position > scriptSnapshot.getLength()) {
+ var content = this.getFileContent(marker.fileName);
+ if (marker.position === -1 || marker.position > content.length) {
throw new Error('Marker "' + name + '" has been invalidated by unrecoverable edits to the file.');
}
this.lastKnownMarker = name;
@@ -387,14 +394,14 @@ module FourSlash {
public goToPosition(pos: number) {
this.currentCaretPosition = pos;
- var lineStarts = ts.computeLineStarts(this.getCurrentFileContent());
+ var lineStarts = ts.computeLineStarts(this.getFileContent(this.activeFile.fileName));
var lineCharPos = ts.computeLineAndCharacterOfPosition(lineStarts, pos);
this.scenarioActions.push('');
}
public moveCaretRight(count = 1) {
this.currentCaretPosition += count;
- this.currentCaretPosition = Math.min(this.currentCaretPosition, this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getLength());
+ this.currentCaretPosition = Math.min(this.currentCaretPosition, this.getFileContent(this.activeFile.fileName).length);
if (count > 0) {
this.scenarioActions.push('');
} else {
@@ -453,7 +460,7 @@ module FourSlash {
private getAllDiagnostics(): ts.Diagnostic[] {
var diagnostics: ts.Diagnostic[] = [];
- var fileNames = JSON.parse(this.languageServiceShimHost.getScriptFileNames());
+ var fileNames = this.languageServiceAdapterHost.getFilenames();
for (var i = 0, n = fileNames.length; i < n; i++) {
diagnostics.push.apply(this.getDiagnostics(fileNames[i]));
}
@@ -805,7 +812,6 @@ module FourSlash {
}
}
-
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
displayParts: ts.SymbolDisplayPart[],
documentation: ts.SymbolDisplayPart[]) {
@@ -1211,8 +1217,7 @@ module FourSlash {
var file = this.testData.files[i];
var active = (this.activeFile === file);
Harness.IO.log('=== Script (' + file.fileName + ') ' + (active ? '(active, cursor at |)' : '') + ' ===');
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(file.fileName);
- var content = snapshot.getText(0, snapshot.getLength());
+ var content = this.getFileContent(file.fileName);
if (active) {
content = content.substr(0, this.currentCaretPosition) + (makeCaretVisible ? '|' : "") + content.substr(this.currentCaretPosition);
}
@@ -1246,8 +1251,7 @@ module FourSlash {
}
public printContext() {
- var fileNames: string[] = JSON.parse(this.languageServiceShimHost.getScriptFileNames());
- ts.forEach(fileNames, Harness.IO.log);
+ ts.forEach(this.languageServiceAdapterHost.getFilenames(), Harness.IO.log);
}
public deleteChar(count = 1) {
@@ -1260,7 +1264,7 @@ module FourSlash {
for (var i = 0; i < count; i++) {
// Make the edit
- this.languageServiceShimHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
+ this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset + 1, ch);
if (i % checkCadence === 0) {
@@ -1287,7 +1291,7 @@ module FourSlash {
public replace(start: number, length: number, text: string) {
this.taoInvalidReason = 'replace NYI';
- this.languageServiceShimHost.editScript(this.activeFile.fileName, start, start + length, text);
+ this.languageServiceAdapterHost.editScript(this.activeFile.fileName, start, start + length, text);
this.updateMarkersForEdit(this.activeFile.fileName, start, start + length, text);
this.checkPostEditInvariants();
}
@@ -1302,7 +1306,7 @@ module FourSlash {
for (var i = 0; i < count; i++) {
offset--;
// Make the edit
- this.languageServiceShimHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
+ this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset + 1, ch);
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset + 1, ch);
if (i % checkCadence === 0) {
@@ -1347,7 +1351,7 @@ module FourSlash {
for (var i = 0; i < text.length; i++) {
// Make the edit
var ch = text.charAt(i);
- this.languageServiceShimHost.editScript(this.activeFile.fileName, offset, offset, ch);
+ this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, ch);
this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset);
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, ch);
@@ -1390,7 +1394,7 @@ module FourSlash {
var start = this.currentCaretPosition;
var offset = this.currentCaretPosition;
- this.languageServiceShimHost.editScript(this.activeFile.fileName, offset, offset, text);
+ this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, text);
this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, text);
this.checkPostEditInvariants();
offset += text.length;
@@ -1412,14 +1416,19 @@ module FourSlash {
}
private checkPostEditInvariants() {
+ if (this.testType !== FourSlashTestType.Native) {
+ // getSourcefile() results can not be serialized. Only perform these verifications
+ // if running against a native LS object.
+ return;
+ }
+
var incrementalSourceFile = this.languageService.getSourceFile(this.activeFile.fileName);
Utils.assertInvariants(incrementalSourceFile, /*parent:*/ undefined);
var incrementalSyntaxDiagnostics = incrementalSourceFile.parseDiagnostics;
// Check syntactic structure
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName);
- var content = snapshot.getText(0, snapshot.getLength());
+ var content = this.getFileContent(this.activeFile.fileName);
var referenceSourceFile = ts.createLanguageServiceSourceFile(
this.activeFile.fileName, createScriptSnapShot(content), ts.ScriptTarget.Latest, /*version:*/ "0", /*setNodeParents:*/ false);
@@ -1433,7 +1442,7 @@ module FourSlash {
// The caret can potentially end up between the \r and \n, which is confusing. If
// that happens, move it back one character
if (this.currentCaretPosition > 0) {
- var ch = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(this.currentCaretPosition - 1, this.currentCaretPosition);
+ var ch = this.getFileContent(this.activeFile.fileName).substring(this.currentCaretPosition - 1, this.currentCaretPosition);
if (ch === '\r') {
this.currentCaretPosition--;
}
@@ -1446,10 +1455,9 @@ module FourSlash {
var runningOffset = 0;
edits = edits.sort((a, b) => a.span.start - b.span.start);
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(fileName);
- var oldContent = snapshot.getText(0, snapshot.getLength());
+ var oldContent = this.getFileContent(this.activeFile.fileName);
for (var j = 0; j < edits.length; j++) {
- this.languageServiceShimHost.editScript(fileName, edits[j].span.start + runningOffset, ts.textSpanEnd(edits[j].span) + runningOffset, edits[j].newText);
+ this.languageServiceAdapterHost.editScript(fileName, edits[j].span.start + runningOffset, ts.textSpanEnd(edits[j].span) + runningOffset, edits[j].newText);
this.updateMarkersForEdit(fileName, edits[j].span.start + runningOffset, ts.textSpanEnd(edits[j].span) + runningOffset, edits[j].newText);
var change = (edits[j].span.start - ts.textSpanEnd(edits[j].span)) + edits[j].newText.length;
runningOffset += change;
@@ -1458,8 +1466,7 @@ module FourSlash {
}
if (isFormattingEdit) {
- snapshot = this.languageServiceShimHost.getScriptSnapshot(fileName);
- var newContent = snapshot.getText(0, snapshot.getLength());
+ var newContent = this.getFileContent(fileName);
if (newContent.replace(/\s/g, '') !== oldContent.replace(/\s/g, '')) {
this.raiseError('Formatting operation destroyed non-whitespace content');
@@ -1506,7 +1513,7 @@ module FourSlash {
}
public goToEOF() {
- var len = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getLength();
+ var len = this.getFileContent(this.activeFile.fileName).length;
this.goToPosition(len);
}
@@ -1627,7 +1634,7 @@ module FourSlash {
public verifyCurrentFileContent(text: string) {
this.taoInvalidReason = 'verifyCurrentFileContent NYI';
- var actual = this.getCurrentFileContent();
+ var actual = this.getFileContent(this.activeFile.fileName);
var replaceNewlines = (str: string) => str.replace(/\r\n/g, "\n");
if (replaceNewlines(actual) !== replaceNewlines(text)) {
throw new Error('verifyCurrentFileContent\n' +
@@ -1639,7 +1646,7 @@ module FourSlash {
public verifyTextAtCaretIs(text: string) {
this.taoInvalidReason = 'verifyCurrentFileContent NYI';
- var actual = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(this.currentCaretPosition, this.currentCaretPosition + text.length);
+ var actual = this.getFileContent(this.activeFile.fileName).substring(this.currentCaretPosition, this.currentCaretPosition + text.length);
if (actual !== text) {
throw new Error('verifyTextAtCaretIs\n' +
'\tExpected: "' + text + '"\n' +
@@ -1657,7 +1664,7 @@ module FourSlash {
'\t Actual: undefined');
}
- var actual = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName).getText(span.start, ts.textSpanEnd(span));
+ var actual = this.getFileContent(this.activeFile.fileName).substring(span.start, ts.textSpanEnd(span));
if (actual !== text) {
this.raiseError('verifyCurrentNameOrDottedNameSpanText\n' +
'\tExpected: "' + text + '"\n' +
@@ -1731,8 +1738,8 @@ module FourSlash {
function jsonMismatchString() {
return ts.sys.newLine +
- "expected: '" + ts.sys.newLine + JSON.stringify(expected,(k, v) => v, 2) + "'" + ts.sys.newLine +
- "actual: '" + ts.sys.newLine + JSON.stringify(actual,(k, v) => v, 2) + "'";
+ "expected: '" + ts.sys.newLine + JSON.stringify(expected, (k, v) => v, 2) + "'" + ts.sys.newLine +
+ "actual: '" + ts.sys.newLine + JSON.stringify(actual, (k, v) => v, 2) + "'";
}
}
@@ -1744,7 +1751,7 @@ module FourSlash {
}
public verifySyntacticClassifications(expected: { classificationType: string; text: string }[]) {
- var actual = this.languageService.getSyntacticClassifications(this.activeFile.fileName,
+ var actual = this.languageService.getSyntacticClassifications(this.activeFile.fileName,
ts.createTextSpan(0, this.activeFile.content.length));
this.verifyClassifications(expected, actual);
@@ -1820,69 +1827,6 @@ module FourSlash {
}
}
- public verifyTypesAgainstFullCheckAtPositions(positions: number[]) {
- this.taoInvalidReason = 'verifyTypesAgainstFullCheckAtPositions impossible';
-
- // Create a from-scratch LS to check against
- var referenceLanguageServiceShimHost = new Harness.LanguageService.TypeScriptLS();
- var referenceLanguageServiceShim = referenceLanguageServiceShimHost.getLanguageService();
- var referenceLanguageService = referenceLanguageServiceShim.languageService;
-
- // Add lib.d.ts to the reference language service
- referenceLanguageServiceShimHost.addDefaultLibrary();
-
- for (var i = 0; i < this.testData.files.length; i++) {
- var file = this.testData.files[i];
-
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(file.fileName);
- var content = snapshot.getText(0, snapshot.getLength());
- referenceLanguageServiceShimHost.addScript(this.testData.files[i].fileName, content);
- }
-
- for (i = 0; i < positions.length; i++) {
- var nameOf = (type: ts.QuickInfo) => type ? ts.displayPartsToString(type.displayParts) : '(none)';
-
- var pullName: string, refName: string;
- var anyFailed = false;
-
- var errMsg = '';
-
- try {
- var pullType = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, positions[i]);
- pullName = nameOf(pullType);
- } catch (err1) {
- errMsg = 'Failed to get pull type check. Exception: ' + err1 + '\r\n';
- if (err1.stack) errMsg = errMsg + err1.stack;
- pullName = '(failed)';
- anyFailed = true;
- }
-
- try {
- var referenceType = referenceLanguageService.getQuickInfoAtPosition(this.activeFile.fileName, positions[i]);
- refName = nameOf(referenceType);
- } catch (err2) {
- errMsg = 'Failed to get full type check. Exception: ' + err2 + '\r\n';
- if (err2.stack) errMsg = errMsg + err2.stack;
- refName = '(failed)';
- anyFailed = true;
- }
-
- var failure = anyFailed || (refName !== pullName);
- if (failure) {
- snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName);
- content = snapshot.getText(0, snapshot.getLength());
- var textAtPosition = content.substr(positions[i], 10);
- var positionDescription = 'Position ' + positions[i] + ' ("' + textAtPosition + '"...)';
-
- if (anyFailed) {
- throw new Error('Exception thrown in language service for ' + positionDescription + '\r\n' + errMsg);
- } else if (refName !== pullName) {
- throw new Error('Pull/Full disagreement failed at ' + positionDescription + ' - expected full typecheck type "' + refName + '" to equal pull type "' + pullName + '".');
- }
- }
- }
- }
-
/*
Check number of navigationItems which match both searchValue and matchKind.
Report an error if expected value and actual value do not match.
@@ -2069,12 +2013,11 @@ module FourSlash {
// The current caret position (in line/col terms)
var line = this.getCurrentCaretFilePosition().line;
// The line/col of the start of this line
- var pos = this.languageServiceShimHost.lineColToPosition(this.activeFile.fileName, line, 1);
+ var pos = this.languageServiceAdapterHost.lineColToPosition(this.activeFile.fileName, line, 1);
// The index of the current file
// The text from the start of the line to the end of the file
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName);
- var text = snapshot.getText(pos, snapshot.getLength());
+ var text = this.getFileContent(this.activeFile.fileName).substring(pos);
// Truncate to the first newline
var newlinePos = text.indexOf('\n');
@@ -2089,13 +2032,8 @@ module FourSlash {
}
}
- private getCurrentFileContent() {
- var snapshot = this.languageServiceShimHost.getScriptSnapshot(this.activeFile.fileName);
- return snapshot.getText(0, snapshot.getLength());
- }
-
private getCurrentCaretFilePosition() {
- var result = this.languageServiceShimHost.positionToZeroBasedLineCol(this.activeFile.fileName, this.currentCaretPosition);
+ var result = this.languageServiceAdapterHost.positionToZeroBasedLineCol(this.activeFile.fileName, this.currentCaretPosition);
if (result.line >= 0) {
result.line++;
}
@@ -2153,8 +2091,10 @@ module FourSlash {
}
} else if (typeof indexOrName === 'string') {
var name = indexOrName;
+
// names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName
- name = name.indexOf('/') === -1 ? 'tests/cases/fourslash/' + name : name;
+ name = name.indexOf('/') === -1 ? (this.basePath + '/' + name) : name;
+
var availableNames: string[] = [];
var foundIt = false;
for (var i = 0; i < this.testData.files.length; i++) {
@@ -2180,7 +2120,7 @@ module FourSlash {
}
private getLineColStringAtPosition(position: number) {
- var pos = this.languageServiceShimHost.positionToZeroBasedLineCol(this.activeFile.fileName, position);
+ var pos = this.languageServiceAdapterHost.positionToZeroBasedLineCol(this.activeFile.fileName, position);
return 'line ' + (pos.line + 1) + ', col ' + pos.character;
}
@@ -2206,23 +2146,31 @@ module FourSlash {
originalName: ''
};
}
+
+ public setCancelled(numberOfCalls: number): void {
+ this.cancellationToken.setCancelled(numberOfCalls)
+ }
+
+ public resetCancelled(): void {
+ this.cancellationToken.resetCancelled();
+ }
}
// TOOD: should these just use the Harness's stdout/stderr?
var fsOutput = new Harness.Compiler.WriterAggregator();
var fsErrors = new Harness.Compiler.WriterAggregator();
export var xmlData: TestXmlData[] = [];
- export function runFourSlashTest(fileName: string) {
+ export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) {
var content = Harness.IO.readFile(fileName);
- var xml = runFourSlashTestContent(content, fileName);
+ var xml = runFourSlashTestContent(basePath, testType, content, fileName);
xmlData.push(xml);
}
- export function runFourSlashTestContent(content: string, fileName: string): TestXmlData {
+ export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): TestXmlData {
// Parse out the files and their metadata
- var testData = parseTestData(content, fileName);
+ var testData = parseTestData(basePath, content, fileName);
- currentTestState = new TestState(testData);
+ currentTestState = new TestState(basePath, testType, testData);
var result = '';
var host = Harness.Compiler.createCompilerHost([{ unitName: Harness.Compiler.fourslashFileName, content: undefined },
@@ -2265,7 +2213,7 @@ module FourSlash {
return lines.map(s => s.substr(1)).join('\n');
}
- function parseTestData(contents: string, fileName: string): FourSlashData {
+ function parseTestData(basePath: string, contents: string, fileName: string): FourSlashData {
// Regex for parsing options in the format "@Alpha: Value of any sort"
var optionRegex = /^\s*@(\w+): (.*)\s*/;
@@ -2333,7 +2281,7 @@ module FourSlash {
currentFileName = fileName;
}
- currentFileName = 'tests/cases/fourslash/' + match[2];
+ currentFileName = basePath + '/' + match[2];
currentFileOptions[match[1]] = match[2];
} else {
// Add other fileMetadata flag
diff --git a/src/harness/fourslashRunner.ts b/src/harness/fourslashRunner.ts
index 1ecb02df292..fe3b6c7a91f 100644
--- a/src/harness/fourslashRunner.ts
+++ b/src/harness/fourslashRunner.ts
@@ -2,19 +2,35 @@
///
///
-class FourslashRunner extends RunnerBase {
- public basePath = 'tests/cases/fourslash';
+const enum FourSlashTestType {
+ Native,
+ Shims
+}
- constructor() {
+class FourSlashRunner extends RunnerBase {
+ protected basePath: string;
+ protected testSuiteName: string;
+
+ constructor(private testType: FourSlashTestType) {
super();
+ switch (testType) {
+ case FourSlashTestType.Native:
+ this.basePath = 'tests/cases/fourslash';
+ this.testSuiteName = 'fourslash';
+ break;
+ case FourSlashTestType.Shims:
+ this.basePath = 'tests/cases/fourslash/shims';
+ this.testSuiteName = 'fourslash-shims';
+ break;
+ }
}
public initializeTests() {
if (this.tests.length === 0) {
- this.tests = this.enumerateFiles(this.basePath, /\.ts/i);
+ this.tests = this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
}
- describe("fourslash tests", () => {
+ describe(this.testSuiteName, () => {
this.tests.forEach((fn: string) => {
fn = ts.normalizeSlashes(fn);
var justName = fn.replace(/^.*[\\\/]/, '');
@@ -24,8 +40,8 @@ class FourslashRunner extends RunnerBase {
if (testIndex >= 0) fn = fn.substr(testIndex);
if (justName && !justName.match(/fourslash\.ts$/i) && !justName.match(/\.d\.ts$/i)) {
- it('FourSlash test ' + justName + ' runs correctly', function () {
- FourSlash.runFourSlashTest(fn);
+ it(this.testSuiteName + ' test ' + justName + ' runs correctly',() => {
+ FourSlash.runFourSlashTest(this.basePath, this.testType, fn);
});
}
});
@@ -82,9 +98,9 @@ class FourslashRunner extends RunnerBase {
}
}
-class GeneratedFourslashRunner extends FourslashRunner {
- constructor() {
- super();
+class GeneratedFourslashRunner extends FourSlashRunner {
+ constructor(testType: FourSlashTestType) {
+ super(testType);
this.basePath += '/generated/';
}
}
\ No newline at end of file
diff --git a/src/harness/harness.ts b/src/harness/harness.ts
index 5fbf7d976ff..534bf85d119 100644
--- a/src/harness/harness.ts
+++ b/src/harness/harness.ts
@@ -19,6 +19,7 @@
///
///
///
+///
declare var require: any;
declare var process: any;
@@ -691,8 +692,6 @@ module Harness {
}
}
-
-
module Harness {
var tcServicesFileName = "typescriptServices.js";
diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts
index 595ef7af3f1..2f95f5072d0 100644
--- a/src/harness/harnessLanguageService.ts
+++ b/src/harness/harnessLanguageService.ts
@@ -1,5 +1,6 @@
///
///
+///
module Harness.LanguageService {
export class ScriptInfo {
@@ -54,12 +55,11 @@ module Harness.LanguageService {
}
}
- class ScriptSnapshotShim implements ts.ScriptSnapshotShim {
- private lineMap: number[] = null;
- private textSnapshot: string;
- private version: number;
+ class ScriptSnapshot implements ts.IScriptSnapshot {
+ public textSnapshot: string;
+ public version: number;
- constructor(private scriptInfo: ScriptInfo) {
+ constructor(public scriptInfo: ScriptInfo) {
this.textSnapshot = scriptInfo.content;
this.version = scriptInfo.version;
}
@@ -72,9 +72,28 @@ module Harness.LanguageService {
return this.textSnapshot.length;
}
+ public getChangeRange(oldScript: ts.IScriptSnapshot): ts.TextChangeRange {
+ var oldShim = oldScript;
+ return this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version);
+ }
+ }
+
+ class ScriptSnapshotProxy implements ts.ScriptSnapshotShim {
+ constructor(public scriptSnapshot: ts.IScriptSnapshot) {
+ }
+
+ public getText(start: number, end: number): string {
+ return this.scriptSnapshot.getText(start, end);
+ }
+
+ public getLength(): number {
+ return this.scriptSnapshot.getLength();
+ }
+
public getChangeRange(oldScript: ts.ScriptSnapshotShim): string {
- var oldShim = oldScript;
- var range = this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version);
+ var oldShim = oldScript;
+
+ var range = this.scriptSnapshot.getChangeRange(oldShim.scriptSnapshot);
if (range === null) {
return null;
}
@@ -94,77 +113,38 @@ module Harness.LanguageService {
}
}
- export class NonCachingDocumentRegistry implements ts.DocumentRegistry {
- public static Instance: ts.DocumentRegistry = new NonCachingDocumentRegistry();
-
- public acquireDocument(
- fileName: string,
- compilationSettings: ts.CompilerOptions,
- scriptSnapshot: ts.IScriptSnapshot,
- version: string): ts.SourceFile {
- var sourceFile = Compiler.createSourceFileAndAssertInvariants(fileName, scriptSnapshot.getText(0, scriptSnapshot.getLength()), compilationSettings.target);
- sourceFile.version = version;
- return sourceFile;
- }
-
- public updateDocument(
- document: ts.SourceFile,
- fileName: string,
- compilationSettings: ts.CompilerOptions,
- scriptSnapshot: ts.IScriptSnapshot,
- version: string,
- textChangeRange: ts.TextChangeRange
- ): ts.SourceFile {
- var result = ts.updateLanguageServiceSourceFile(document, scriptSnapshot, version, textChangeRange, /*aggressiveChecks:*/ true);
- Utils.assertInvariants(result, /*parent:*/ undefined);
- return result;
- }
-
- public releaseDocument(fileName: string, compilationSettings: ts.CompilerOptions): void {
- // no op since this class doesn't cache anything
- }
+ export interface LanguageServiceAdapter {
+ getHost(): LanguageServiceAdapterHost;
+ getLanguageService(): ts.LanguageService;
+ getClassifier(): ts.Classifier;
+ getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo;
}
- export class TypeScriptLS implements ts.LanguageServiceShimHost {
- private ls: ts.LanguageServiceShim = null;
- private fileNameToScript: ts.Map = {};
- private settings: ts.CompilerOptions = {};
-
- constructor(private cancellationToken: ts.CancellationToken = CancellationToken.None) {
- }
-
- public trace(s: string) {
+ export class LanguageServiceAdapterHost {
+ protected fileNameToScript: ts.Map = {};
+
+ constructor(protected cancellationToken: ts.CancellationToken = CancellationToken.None,
+ protected settings = ts.getDefaultCompilerOptions()) {
}
public getNewLine(): string {
return "\r\n";
}
- public addDefaultLibrary() {
- this.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.defaultLibSourceFile.text);
+ public getFilenames(): string[] {
+ var fileNames: string[] = [];
+ ts.forEachKey(this.fileNameToScript,(fileName) => { fileNames.push(fileName); });
+ return fileNames;
}
- public getHostIdentifier(): string {
- return "TypeScriptLS";
- }
-
- public addFile(fileName: string) {
- var code = Harness.IO.readFile(fileName);
- this.addScript(fileName, code);
- }
-
- private getScriptInfo(fileName: string): ScriptInfo {
+ public getScriptInfo(fileName: string): ScriptInfo {
return ts.lookUp(this.fileNameToScript, fileName);
}
- public addScript(fileName: string, content: string) {
+ public addScript(fileName: string, content: string): void {
this.fileNameToScript[fileName] = new ScriptInfo(fileName, content);
}
- private contains(fileName: string): boolean {
- return ts.hasProperty(this.fileNameToScript, fileName);
- }
-
public updateScript(fileName: string, content: string) {
var script = this.getScriptInfo(fileName);
if (script !== null) {
@@ -185,107 +165,10 @@ module Harness.LanguageService {
throw new Error("No script with name '" + fileName + "'");
}
- //////////////////////////////////////////////////////////////////////
- // ILogger implementation
- //
- public information(): boolean { return false; }
- public debug(): boolean { return true; }
- public warning(): boolean { return true; }
- public error(): boolean { return true; }
- public fatal(): boolean { return true; }
-
- public log(s: string): void {
- // For debugging...
- //TypeScript.Environment.standardOut.WriteLine("TypeScriptLS:" + s);
- }
-
- //////////////////////////////////////////////////////////////////////
- // LanguageServiceShimHost implementation
- //
-
- /// Returns json for Tools.CompilationSettings
- public getCompilationSettings(): string {
- return JSON.stringify(this.settings);
- }
-
- public getCancellationToken(): ts.CancellationToken {
- return this.cancellationToken;
- }
-
- public getCurrentDirectory(): string {
- return "";
- }
-
- public getDefaultLibFileName(): string {
- return "";
- }
-
- public getScriptFileNames(): string {
- var fileNames: string[] = [];
- ts.forEachKey(this.fileNameToScript,(fileName) => { fileNames.push(fileName); });
- return JSON.stringify(fileNames);
- }
-
- public getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim {
- if (this.contains(fileName)) {
- return new ScriptSnapshotShim(this.getScriptInfo(fileName));
- }
- return undefined;
- }
-
- public getScriptVersion(fileName: string): string {
- if (this.contains(fileName)) {
- return this.getScriptInfo(fileName).version.toString();
- }
- return undefined;
- }
-
- public getLocalizedDiagnosticMessages(): string {
- return JSON.stringify({});
- }
-
- /** Return a new instance of the language service shim, up-to-date wrt to typecheck.
- * To access the non-shim (i.e. actual) language service, use the "ls.languageService" property.
- */
- public getLanguageService(): ts.LanguageServiceShim {
- this.ls = new TypeScript.Services.TypeScriptServicesFactory().createLanguageServiceShim(this);
- return this.ls;
- }
-
- public setCompilationSettings(settings: ts.CompilerOptions) {
- for (var key in settings) {
- if (settings.hasOwnProperty(key)) {
- this.settings[key] = settings[key];
- }
- }
- }
-
- /** Return a new instance of the classifier service shim */
- public getClassifier(): ts.ClassifierShim {
- return new TypeScript.Services.TypeScriptServicesFactory().createClassifierShim(this);
- }
-
- public getCoreService(): ts.CoreServicesShim {
- return new TypeScript.Services.TypeScriptServicesFactory().createCoreServicesShim(this);
- }
-
- /** Parse file given its source text */
- public parseSourceText(fileName: string, sourceText: ts.IScriptSnapshot): ts.SourceFile {
- var result = Compiler.createSourceFileAndAssertInvariants(fileName, sourceText.getText(0, sourceText.getLength()), ts.ScriptTarget.Latest);
- result.version = "1";
- return result;
- }
-
- /** Parse a file on disk given its fileName */
- public parseFile(fileName: string) {
- var sourceText = ts.ScriptSnapshot.fromString(Harness.IO.readFile(fileName));
- return this.parseSourceText(fileName, sourceText);
- }
-
/**
- * @param line 1 based index
- * @param col 1 based index
- */
+ * @param line 1 based index
+ * @param col 1 based index
+ */
public lineColToPosition(fileName: string, line: number, col: number): number {
var script: ScriptInfo = this.fileNameToScript[fileName];
assert.isNotNull(script);
@@ -296,9 +179,9 @@ module Harness.LanguageService {
}
/**
- * @param line 0 based index
- * @param col 0 based index
- */
+ * @param line 0 based index
+ * @param col 0 based index
+ */
public positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter {
var script: ScriptInfo = this.fileNameToScript[fileName];
assert.isNotNull(script);
@@ -309,105 +192,254 @@ module Harness.LanguageService {
assert.isTrue(result.character >= 1);
return { line: result.line - 1, character: result.character - 1 };
}
+ }
- /** Verify that applying edits to sourceFileName result in the content of the file baselineFileName */
- public checkEdits(sourceFileName: string, baselineFileName: string, edits: ts.TextChange[]) {
- var script = Harness.IO.readFile(sourceFileName);
- var formattedScript = this.applyEdits(script, edits);
- var baseline = Harness.IO.readFile(baselineFileName);
+ /// Native adapter
+ class NativeLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceHost {
+ getCompilationSettings(): ts.CompilerOptions { return this.settings; }
+ getCancellationToken(): ts.CancellationToken { return this.cancellationToken; }
+ getCurrentDirectory(): string { return ""; }
+ getDefaultLibFileName(): string { return ""; }
+ getScriptFileNames(): string[] { return this.getFilenames(); }
+ getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
+ var script = this.getScriptInfo(fileName);
+ return script ? new ScriptSnapshot(script) : undefined;
+ }
+ getScriptVersion(fileName: string): string {
+ var script = this.getScriptInfo(fileName);
+ return script ? script.version.toString() : undefined;
+ }
+ log(s: string): void { }
+ trace(s: string): void { }
+ error(s: string): void { }
+ }
- function noDiff(text1: string, text2: string) {
- text1 = text1.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
- text2 = text2.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
+ export class NativeLanugageServiceAdapter implements LanguageServiceAdapter {
+ private host: NativeLanguageServiceHost;
+ constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
+ this.host = new NativeLanguageServiceHost(cancellationToken, options);
+ }
+ getHost() { return this.host; }
+ getLanguageService(): ts.LanguageService { return ts.createLanguageService(this.host); }
+ getClassifier(): ts.Classifier { return ts.createClassifier(); }
+ getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.preProcessFile(fileContents); }
+ }
- if (text1 !== text2) {
- var errorString = "";
- var text1Lines = text1.split(/\n/);
- var text2Lines = text2.split(/\n/);
- for (var i = 0; i < text1Lines.length; i++) {
- if (text1Lines[i] !== text2Lines[i]) {
- errorString += "Difference at line " + (i + 1) + ":\n";
- errorString += " Left File: " + text1Lines[i] + "\n";
- errorString += " Right File: " + text2Lines[i] + "\n\n";
- }
- }
- throw (new Error(errorString));
- }
- }
- assert.isTrue(noDiff(formattedScript, baseline));
- assert.equal(formattedScript, baseline);
+ /// Shim adapter
+ class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost {
+ private nativeHost: NativeLanguageServiceHost;
+ constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
+ super(cancellationToken, options);
+ this.nativeHost = new NativeLanguageServiceHost(cancellationToken, options);
}
+ getFilenames(): string[] { return this.nativeHost.getFilenames(); }
+ getScriptInfo(fileName: string): ScriptInfo { return this.nativeHost.getScriptInfo(fileName); }
+ addScript(fileName: string, content: string): void { this.nativeHost.addScript(fileName, content); }
+ updateScript(fileName: string, content: string): void { return this.nativeHost.updateScript(fileName, content); }
+ editScript(fileName: string, minChar: number, limChar: number, newText: string): void { this.nativeHost.editScript(fileName, minChar, limChar, newText); }
+ lineColToPosition(fileName: string, line: number, col: number): number { return this.nativeHost.lineColToPosition(fileName, line, col); }
+ positionToZeroBasedLineCol(fileName: string, position: number): ts.LineAndCharacter { return this.nativeHost.positionToZeroBasedLineCol(fileName, position); }
- /** Apply an array of text edits to a string, and return the resulting string. */
- public applyEdits(content: string, edits: ts.TextChange[]): string {
- var result = content;
- edits = this.normalizeEdits(edits);
-
- for (var i = edits.length - 1; i >= 0; i--) {
- var edit = edits[i];
- var prefix = result.substring(0, edit.span.start);
- var middle = edit.newText;
- var suffix = result.substring(ts.textSpanEnd(edit.span));
- result = prefix + middle + suffix;
- }
- return result;
+ getCompilationSettings(): string { return JSON.stringify(this.nativeHost.getCompilationSettings()); }
+ getCancellationToken(): ts.CancellationToken { return this.nativeHost.getCancellationToken(); }
+ getCurrentDirectory(): string { return this.nativeHost.getCurrentDirectory(); }
+ getDefaultLibFileName(): string { return this.nativeHost.getDefaultLibFileName(); }
+ getScriptFileNames(): string { return JSON.stringify(this.nativeHost.getScriptFileNames()); }
+ getScriptSnapshot(fileName: string): ts.ScriptSnapshotShim {
+ var nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName);
+ return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot);
}
+ getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
+ getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
+ log(s: string): void { this.nativeHost.log(s); }
+ trace(s: string): void { this.nativeHost.trace(s); }
+ error(s: string): void { this.nativeHost.error(s); }
+ }
- /** Normalize an array of edits by removing overlapping entries and sorting entries on the minChar position. */
- private normalizeEdits(edits: ts.TextChange[]): ts.TextChange[] {
- var result: ts.TextChange[] = [];
+ class ClassifierShimProxy implements ts.Classifier {
+ constructor(private shim: ts.ClassifierShim) {
+ }
+ getClassificationsForLine(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.ClassificationResult {
+ var result = this.shim.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics).split('\n');
+ var entries: ts.ClassificationInfo[] = [];
+ var i = 0;
+ var position = 0;
- function mapEdits(edits: ts.TextChange[]): { edit: ts.TextChange; index: number; }[] {
- var result: { edit: ts.TextChange; index: number; }[] = [];
- for (var i = 0; i < edits.length; i++) {
- result.push({ edit: edits[i], index: i });
- }
- return result;
+ for (; i < result.length - 1; i += 2) {
+ var t = entries[i / 2] = {
+ length: parseInt(result[i]),
+ classification: parseInt(result[i + 1])
+ };
+
+ assert.isTrue(t.length > 0, "Result length should be greater than 0, got :" + t.length);
+ position += t.length;
}
+ var finalLexState = parseInt(result[result.length - 1]);
- var temp = mapEdits(edits).sort(function (a, b) {
- var result = a.edit.span.start - b.edit.span.start;
- if (result === 0)
- result = a.index - b.index;
- return result;
+ assert.equal(position, text.length, "Expected cumulative length of all entries to match the length of the source. expected: " + text.length + ", but got: " + position);
+
+ return {
+ finalLexState,
+ entries
+ };
+ }
+ }
+
+ function unwrapJSONCallResult(result: string): any {
+ var parsedResult = JSON.parse(result);
+ if (parsedResult.error) {
+ throw new Error("Language Service Shim Error: " + JSON.stringify(parsedResult.error));
+ }
+ else if (parsedResult.canceled) {
+ throw new ts.OperationCanceledException();
+ }
+ return parsedResult.result;
+ }
+
+ class LanguageServiceShimProxy implements ts.LanguageService {
+ constructor(private shim: ts.LanguageServiceShim) {
+ }
+ private unwrappJSONCallResult(result: string): any {
+ var parsedResult = JSON.parse(result);
+ if (parsedResult.error) {
+ throw new Error("Language Service Shim Error: " + JSON.stringify(parsedResult.error));
+ }
+ return parsedResult.result;
+ }
+ cleanupSemanticCache(): void {
+ this.shim.cleanupSemanticCache();
+ }
+ getSyntacticDiagnostics(fileName: string): ts.Diagnostic[] {
+ return unwrapJSONCallResult(this.shim.getSyntacticDiagnostics(fileName));
+ }
+ getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
+ return unwrapJSONCallResult(this.shim.getSemanticDiagnostics(fileName));
+ }
+ getCompilerOptionsDiagnostics(): ts.Diagnostic[] {
+ return unwrapJSONCallResult(this.shim.getCompilerOptionsDiagnostics());
+ }
+ getSyntacticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] {
+ return unwrapJSONCallResult(this.shim.getSyntacticClassifications(fileName, span.start, span.length));
+ }
+ getSemanticClassifications(fileName: string, span: ts.TextSpan): ts.ClassifiedSpan[] {
+ return unwrapJSONCallResult(this.shim.getSemanticClassifications(fileName, span.start, span.length));
+ }
+ getCompletionsAtPosition(fileName: string, position: number): ts.CompletionInfo {
+ return unwrapJSONCallResult(this.shim.getCompletionsAtPosition(fileName, position));
+ }
+ getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
+ return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
+ }
+ getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo {
+ return unwrapJSONCallResult(this.shim.getQuickInfoAtPosition(fileName, position));
+ }
+ getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): ts.TextSpan {
+ return unwrapJSONCallResult(this.shim.getNameOrDottedNameSpan(fileName, startPos, endPos));
+ }
+ getBreakpointStatementAtPosition(fileName: string, position: number): ts.TextSpan {
+ return unwrapJSONCallResult(this.shim.getBreakpointStatementAtPosition(fileName, position));
+ }
+ getSignatureHelpItems(fileName: string, position: number): ts.SignatureHelpItems {
+ return unwrapJSONCallResult(this.shim.getSignatureHelpItems(fileName, position));
+ }
+ getRenameInfo(fileName: string, position: number): ts.RenameInfo {
+ return unwrapJSONCallResult(this.shim.getRenameInfo(fileName, position));
+ }
+ findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ts.RenameLocation[] {
+ return unwrapJSONCallResult(this.shim.findRenameLocations(fileName, position, findInStrings, findInComments));
+ }
+ getDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
+ return unwrapJSONCallResult(this.shim.getDefinitionAtPosition(fileName, position));
+ }
+ getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
+ return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position));
+ }
+ getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
+ return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position));
+ }
+ getNavigateToItems(searchValue: string): ts.NavigateToItem[] {
+ return unwrapJSONCallResult(this.shim.getNavigateToItems(searchValue));
+ }
+ getNavigationBarItems(fileName: string): ts.NavigationBarItem[] {
+ return unwrapJSONCallResult(this.shim.getNavigationBarItems(fileName));
+ }
+ getOutliningSpans(fileName: string): ts.OutliningSpan[] {
+ return unwrapJSONCallResult(this.shim.getOutliningSpans(fileName));
+ }
+ getTodoComments(fileName: string, descriptors: ts.TodoCommentDescriptor[]): ts.TodoComment[] {
+ return unwrapJSONCallResult(this.shim.getTodoComments(fileName, JSON.stringify(descriptors)));
+ }
+ getBraceMatchingAtPosition(fileName: string, position: number): ts.TextSpan[] {
+ return unwrapJSONCallResult(this.shim.getBraceMatchingAtPosition(fileName, position));
+ }
+ getIndentationAtPosition(fileName: string, position: number, options: ts.EditorOptions): number {
+ return unwrapJSONCallResult(this.shim.getIndentationAtPosition(fileName, position, JSON.stringify(options)));
+ }
+ getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): ts.TextChange[] {
+ return unwrapJSONCallResult(this.shim.getFormattingEditsForRange(fileName, start, end, JSON.stringify(options)));
+ }
+ getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): ts.TextChange[] {
+ return unwrapJSONCallResult(this.shim.getFormattingEditsForDocument(fileName, JSON.stringify(options)));
+ }
+ getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: ts.FormatCodeOptions): ts.TextChange[] {
+ return unwrapJSONCallResult(this.shim.getFormattingEditsAfterKeystroke(fileName, position, key, JSON.stringify(options)));
+ }
+ getEmitOutput(fileName: string): ts.EmitOutput {
+ return unwrapJSONCallResult(this.shim.getEmitOutput(fileName));
+ }
+ getProgram(): ts.Program {
+ throw new Error("Program can not be marshaled across the shim layer.");
+ }
+ getSourceFile(fileName: string): ts.SourceFile {
+ throw new Error("SourceFile can not be marshaled across the shim layer.");
+ }
+ dispose(): void { this.shim.dispose({}); }
+ }
+
+ export class ShimLanugageServiceAdapter implements LanguageServiceAdapter {
+ private host: ShimLanguageServiceHost;
+ private factory: ts.TypeScriptServicesFactory;
+ constructor(cancellationToken?: ts.CancellationToken, options?: ts.CompilerOptions) {
+ this.host = new ShimLanguageServiceHost(cancellationToken, options);
+ this.factory = new TypeScript.Services.TypeScriptServicesFactory();
+ }
+ getHost() { return this.host; }
+ getLanguageService(): ts.LanguageService { return new LanguageServiceShimProxy(this.factory.createLanguageServiceShim(this.host)); }
+ getClassifier(): ts.Classifier { return new ClassifierShimProxy(this.factory.createClassifierShim(this.host)); }
+ getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo {
+ var shimResult: {
+ referencedFiles: ts.IFileReference[];
+ importedFiles: ts.IFileReference[];
+ isLibFile: boolean;
+ };
+
+ var coreServicesShim = this.factory.createCoreServicesShim(this.host);
+ shimResult = unwrapJSONCallResult(coreServicesShim.getPreProcessedFileInfo(fileName, ts.ScriptSnapshot.fromString(fileContents)));
+
+ var convertResult: ts.PreProcessedFileInfo = {
+ referencedFiles: [],
+ importedFiles: [],
+ isLibFile: shimResult.isLibFile
+ };
+
+ ts.forEach(shimResult.referencedFiles, refFile => {
+ convertResult.referencedFiles.push({
+ fileName: refFile.path,
+ pos: refFile.position,
+ end: refFile.position + refFile.length
+ });
});
- var current = 0;
- var next = 1;
- while (current < temp.length) {
- var currentEdit = temp[current].edit;
+ ts.forEach(shimResult.importedFiles, importedFile => {
+ convertResult.importedFiles.push({
+ fileName: importedFile.path,
+ pos: importedFile.position,
+ end: importedFile.position + importedFile.length
+ });
+ });
- // Last edit
- if (next >= temp.length) {
- result.push(currentEdit);
- current++;
- continue;
- }
- var nextEdit = temp[next].edit;
-
- var gap = nextEdit.span.start - ts.textSpanEnd(currentEdit.span);
-
- // non-overlapping edits
- if (gap >= 0) {
- result.push(currentEdit);
- current = next;
- next++;
- continue;
- }
-
- // overlapping edits: for now, we only support ignoring an next edit
- // entirely contained in the current edit.
- if (ts.textSpanEnd(currentEdit.span) >= ts.textSpanEnd(nextEdit.span)) {
- next++;
- continue;
- }
- else {
- throw new Error("Trying to apply overlapping edits");
- }
- }
-
- return result;
+ return convertResult;
}
}
}
diff --git a/src/harness/runner.ts b/src/harness/runner.ts
index 24349ada3fd..942ae56bf6f 100644
--- a/src/harness/runner.ts
+++ b/src/harness/runner.ts
@@ -61,10 +61,13 @@ if (testConfigFile !== '') {
runners.push(new ProjectRunner());
break;
case 'fourslash':
- runners.push(new FourslashRunner());
+ runners.push(new FourSlashRunner(FourSlashTestType.Native));
+ break;
+ case 'fourslash-shims':
+ runners.push(new FourSlashRunner(FourSlashTestType.Shims));
break;
case 'fourslash-generated':
- runners.push(new GeneratedFourslashRunner());
+ runners.push(new GeneratedFourslashRunner(FourSlashTestType.Native));
break;
case 'rwc':
runners.push(new RWCRunner());
@@ -90,7 +93,8 @@ if (runners.length === 0) {
}
// language services
- runners.push(new FourslashRunner());
+ runners.push(new FourSlashRunner(FourSlashTestType.Native));
+ runners.push(new FourSlashRunner(FourSlashTestType.Shims));
//runners.push(new GeneratedFourslashRunner());
}
diff --git a/src/services/shims.ts b/src/services/shims.ts
index 759c1078d90..0f9f1a12cd1 100644
--- a/src/services/shims.ts
+++ b/src/services/shims.ts
@@ -90,6 +90,7 @@ module ts {
getCompilerOptionsDiagnostics(): string;
getSyntacticClassifications(fileName: string, start: number, length: number): string;
+ getSemanticClassifications(fileName: string, start: number, length: number): string;
getCompletionsAtPosition(fileName: string, position: number): string;
getCompletionEntryDetails(fileName: string, position: number, entryName: string): string;
diff --git a/tests/baselines/reference/getBreakpointStatementAtPosition.baseline b/tests/baselines/reference/getBreakpointStatementAtPosition.baseline
new file mode 100644
index 00000000000..617589b9047
--- /dev/null
+++ b/tests/baselines/reference/getBreakpointStatementAtPosition.baseline
@@ -0,0 +1,71 @@
+
+1 >while (true) {
+
+ ~~~~~~~~~~~~~~~ => Pos: (0 to 14) SpanInfo: {"start":0,"length":12}
+ >while (true)
+ >:=> (line 1, col 0) to (line 1, col 12)
+--------------------------------
+2 > break;
+
+ ~~~~~~~~~~~ => Pos: (15 to 25) SpanInfo: {"start":19,"length":5}
+ >break
+ >:=> (line 2, col 4) to (line 2, col 9)
+--------------------------------
+3 >}
+
+ ~~ => Pos: (26 to 27) SpanInfo: {"start":19,"length":5}
+ >break
+ >:=> (line 2, col 4) to (line 2, col 9)
+--------------------------------
+4 >y: while (true) {
+
+ ~~~~~~~~~~~~~~~~~~ => Pos: (28 to 45) SpanInfo: {"start":31,"length":12}
+ >while (true)
+ >:=> (line 4, col 3) to (line 4, col 15)
+--------------------------------
+5 > break y;
+
+ ~~~~~~~~~~~~~ => Pos: (46 to 58) SpanInfo: {"start":50,"length":7}
+ >break y
+ >:=> (line 5, col 4) to (line 5, col 11)
+--------------------------------
+6 >}
+
+ ~~ => Pos: (59 to 60) SpanInfo: {"start":50,"length":7}
+ >break y
+ >:=> (line 5, col 4) to (line 5, col 11)
+--------------------------------
+7 >while (true) {
+
+ ~~~~~~~~~~~~~~~ => Pos: (61 to 75) SpanInfo: {"start":61,"length":12}
+ >while (true)
+ >:=> (line 7, col 0) to (line 7, col 12)
+--------------------------------
+8 > continue;
+
+ ~~~~~~~~~~~~~~ => Pos: (76 to 89) SpanInfo: {"start":80,"length":8}
+ >continue
+ >:=> (line 8, col 4) to (line 8, col 12)
+--------------------------------
+9 >}
+
+ ~~ => Pos: (90 to 91) SpanInfo: {"start":80,"length":8}
+ >continue
+ >:=> (line 8, col 4) to (line 8, col 12)
+--------------------------------
+10 >z: while (true) {
+
+ ~~~~~~~~~~~~~~~~~~ => Pos: (92 to 109) SpanInfo: {"start":95,"length":12}
+ >while (true)
+ >:=> (line 10, col 3) to (line 10, col 15)
+--------------------------------
+11 > continue z;
+
+ ~~~~~~~~~~~~~~~~ => Pos: (110 to 125) SpanInfo: {"start":114,"length":10}
+ >continue z
+ >:=> (line 11, col 4) to (line 11, col 14)
+--------------------------------
+12 >}
+ ~ => Pos: (126 to 126) SpanInfo: {"start":114,"length":10}
+ >continue z
+ >:=> (line 11, col 4) to (line 11, col 14)
\ No newline at end of file
diff --git a/tests/baselines/reference/getEmitOutput.baseline b/tests/baselines/reference/getEmitOutput.baseline
new file mode 100644
index 00000000000..fbc3296c7c4
--- /dev/null
+++ b/tests/baselines/reference/getEmitOutput.baseline
@@ -0,0 +1,30 @@
+EmitSkipped: false
+FileName : tests/cases/fourslash/shims/inputFile1.js
+var x = 5;
+var Bar = (function () {
+ function Bar() {
+ }
+ return Bar;
+})();
+FileName : tests/cases/fourslash/shims/inputFile1.d.ts
+declare var x: number;
+declare class Bar {
+ x: string;
+ y: number;
+}
+
+EmitSkipped: false
+FileName : tests/cases/fourslash/shims/inputFile2.js
+var x1 = "hello world";
+var Foo = (function () {
+ function Foo() {
+ }
+ return Foo;
+})();
+FileName : tests/cases/fourslash/shims/inputFile2.d.ts
+declare var x1: string;
+declare class Foo {
+ x: string;
+ y: number;
+}
+
diff --git a/tests/cases/fourslash/addMethodToInterface1.ts b/tests/cases/fourslash/addMethodToInterface1.ts
index 749f5601173..07cd4700583 100644
--- a/tests/cases/fourslash/addMethodToInterface1.ts
+++ b/tests/cases/fourslash/addMethodToInterface1.ts
@@ -12,4 +12,3 @@ edit.disableFormatting();
goTo.marker('1');
edit.insert(" compareTo(): number;\n");
-diagnostics.validateTypesAtPositions(168,84,53,118,22);
diff --git a/tests/cases/fourslash/arrayConcatTypeCheck0.ts b/tests/cases/fourslash/arrayConcatTypeCheck0.ts
index ac5404266a6..9250eb5b6fd 100644
--- a/tests/cases/fourslash/arrayConcatTypeCheck0.ts
+++ b/tests/cases/fourslash/arrayConcatTypeCheck0.ts
@@ -14,4 +14,3 @@ edit.disableFormatting();
goTo.marker('1');
edit.insert(", 'world'");
-diagnostics.validateTypesAtPositions(78);
diff --git a/tests/cases/fourslash/arrayConcatTypeCheck1.ts b/tests/cases/fourslash/arrayConcatTypeCheck1.ts
index 42d9cdb8a1c..c62bd5b7f14 100644
--- a/tests/cases/fourslash/arrayConcatTypeCheck1.ts
+++ b/tests/cases/fourslash/arrayConcatTypeCheck1.ts
@@ -23,5 +23,4 @@ goTo.marker('2');
edit.deleteAtCaret(7);
goTo.marker('4');
-diagnostics.validateTypesAtPositions(43);
diff --git a/tests/cases/fourslash/arrayTypeMismatchIncrementalTypeCheck.ts b/tests/cases/fourslash/arrayTypeMismatchIncrementalTypeCheck.ts
deleted file mode 100644
index 778f20db219..00000000000
--- a/tests/cases/fourslash/arrayTypeMismatchIncrementalTypeCheck.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-///
-
-//// interface Iterator {
-//// (value: T, index: any): U;
-//// }
-////
-//// interface WrappedArray {
-//// map(iterator: Iterator): U[];
-//// }
-////
-//// interface Underscore {
-//// (list: T[]): WrappedArray;
-//// map(list: T[], iterator: Iterator, context?: any): U[];
-//// }
-////
-//// declare var _: Underscore;
-////
-//// var a: string[];
-//// var b = _.map(a, x => x.length); // Type any[], should be number[]
-//// var c = _(a).map();
-//// var d = a.map(x => x.length);
-//// var bb = _.map(aa, x => x.length);
-//// var cc = _(aa).map(x => x.length); // Error, could not select overload
-//// var dd = aa.map(x => x.length); // Error, could not select overload
-////
-////
-////
-////
-
-edit.disableFormatting();
-diagnostics.validateTypesAtPositions(364);
diff --git a/tests/cases/fourslash/emptyTypeArgumentList.ts b/tests/cases/fourslash/emptyTypeArgumentList.ts
deleted file mode 100644
index d8f7947559b..00000000000
--- a/tests/cases/fourslash/emptyTypeArgumentList.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-///
-
-//// function foo2(test);
-//// function foo2() { }
-//// /**/foo2<>("");
-goTo.marker();
-diagnostics.validateTypeAtCurrentPosition();
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 267f2c14341..4091815ce59 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -80,16 +80,6 @@ module FourSlashInterface {
}
}
- export class diagnostics {
- public validateTypeAtCurrentPosition() {
- return this.validateTypesAtPositions(FourSlash.currentTestState.currentCaretPosition);
- }
-
- public validateTypesAtPositions(...positions: number[]) {
- return FourSlash.currentTestState.verifyTypesAgainstFullCheckAtPositions(positions);
- }
- }
-
export class goTo {
// Moves the caret to the specified marker,
// or the anonymous marker ('/**/') if no name
@@ -565,11 +555,11 @@ module FourSlashInterface {
export class cancellation {
public resetCancelled() {
- FourSlash.currentTestState.cancellationToken.resetCancelled();
+ FourSlash.currentTestState.resetCancelled();
}
public setCancelled(numberOfCalls: number = 0) {
- FourSlash.currentTestState.cancellationToken.setCancelled(numberOfCalls);
+ FourSlash.currentTestState.setCancelled(numberOfCalls);
}
}
@@ -651,7 +641,6 @@ module fs {
export var edit = new FourSlashInterface.edit();
export var debug = new FourSlashInterface.debug();
export var format = new FourSlashInterface.format();
- export var diagnostics = new FourSlashInterface.diagnostics();
export var cancellation = new FourSlashInterface.cancellation();
}
module ts {
@@ -670,6 +659,5 @@ var verify = new FourSlashInterface.verify();
var edit = new FourSlashInterface.edit();
var debug = new FourSlashInterface.debug();
var format = new FourSlashInterface.format();
-var diagnostics = new FourSlashInterface.diagnostics();
var cancellation = new FourSlashInterface.cancellation();
var classification = FourSlashInterface.classification;
diff --git a/tests/cases/fourslash/getTypeAtModuleExtends.ts b/tests/cases/fourslash/getTypeAtModuleExtends.ts
deleted file mode 100644
index 8114ee94c1b..00000000000
--- a/tests/cases/fourslash/getTypeAtModuleExtends.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-///
-
-////declare module A.B {
-//// export class C { }
-////}
-////
-////import ab = A.B;
-////
-////class D extends ab.C/**/{ }
-
-goTo.marker();
-diagnostics.validateTypesAtPositions(FourSlash.currentTestState.currentCaretPosition);
diff --git a/tests/cases/fourslash/indentation.ts b/tests/cases/fourslash/indentation.ts
index 32319ae282d..2a2090c1d87 100644
--- a/tests/cases/fourslash/indentation.ts
+++ b/tests/cases/fourslash/indentation.ts
@@ -179,5 +179,5 @@
////{| "indent": 0 |}
test.markers().forEach((marker) => {
- verify.indentationAtPositionIs('tests/cases/fourslash/indentation.ts', marker.position, marker.data.indent);
+ verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent);
});
diff --git a/tests/cases/fourslash/numberAssignement0.ts b/tests/cases/fourslash/numberAssignement0.ts
deleted file mode 100644
index 540a8d91f3d..00000000000
--- a/tests/cases/fourslash/numberAssignement0.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-
-//// var x: Number;
-//// var y: Number;
-//// var z = x ;
-
-edit.disableFormatting();
-diagnostics.validateTypesAtPositions(28);
diff --git a/tests/cases/fourslash/pullFullDiffTypeParameterExtends0.ts b/tests/cases/fourslash/pullFullDiffTypeParameterExtends0.ts
deleted file mode 100644
index 990d65404d3..00000000000
--- a/tests/cases/fourslash/pullFullDiffTypeParameterExtends0.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-///
-
-//// class A { }
-//// class B {
-//// data: A