Merge pull request #1937 from Microsoft/fourslashCleanup

Fourslash cleanup
This commit is contained in:
Mohamed Hegazy 2015-02-09 10:37:14 -08:00
commit a710902a5f
46 changed files with 1138 additions and 680 deletions

View File

@ -16,6 +16,7 @@
/// <reference path='..\services\services.ts' />
/// <reference path='harnessLanguageService.ts' />
/// <reference path='harness.ts' />
/// <reference path='fourslashRunner.ts' />
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 = (<any>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('<MoveCaretToLineAndChar LineNumber="' + lineCharPos.line + '" CharNumber="' + lineCharPos.character + '" />');
}
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('<MoveCaretRight NumberOfChars="' + count + '" />');
} 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 = <string>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

View File

@ -2,19 +2,35 @@
///<reference path='harness.ts'/>
///<reference path='runnerbase.ts' />
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/';
}
}

View File

@ -19,6 +19,7 @@
/// <reference path='external\mocha.d.ts'/>
/// <reference path='external\chai.d.ts'/>
/// <reference path='sourceMapRecorder.ts'/>
/// <reference path='runnerbase.ts'/>
declare var require: any;
declare var process: any;
@ -691,8 +692,6 @@ module Harness {
}
}
module Harness {
var tcServicesFileName = "typescriptServices.js";

View File

@ -1,5 +1,6 @@
/// <reference path='..\services\services.ts' />
/// <reference path='..\services\shims.ts' />
/// <reference path='harness.ts' />
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 = <ScriptSnapshot>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 = <ScriptSnapshotShim>oldScript;
var range = this.scriptInfo.getTextChangeRangeBetweenVersions(oldShim.version, this.version);
var oldShim = <ScriptSnapshotProxy>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<ScriptInfo> = {};
private settings: ts.CompilerOptions = {};
constructor(private cancellationToken: ts.CancellationToken = CancellationToken.None) {
}
public trace(s: string) {
export class LanguageServiceAdapterHost {
protected fileNameToScript: ts.Map<ScriptInfo> = {};
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;
}
}
}

View File

@ -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());
}

View File

@ -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;

View File

@ -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)

View File

@ -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;
}

View File

@ -12,4 +12,3 @@ edit.disableFormatting();
goTo.marker('1');
edit.insert(" compareTo(): number;\n");
diagnostics.validateTypesAtPositions(168,84,53,118,22);

View File

@ -14,4 +14,3 @@ edit.disableFormatting();
goTo.marker('1');
edit.insert(", 'world'");
diagnostics.validateTypesAtPositions(78);

View File

@ -23,5 +23,4 @@ goTo.marker('2');
edit.deleteAtCaret(7);
goTo.marker('4');
diagnostics.validateTypesAtPositions(43);

View File

@ -1,31 +0,0 @@
/// <reference path="../fourslash.ts" />
//// interface Iterator<T> {
//// (value: T, index: any): U;
//// }
////
//// interface WrappedArray<T> {
//// map<U>(iterator: Iterator<T, U>): U[];
//// }
////
//// interface Underscore {
//// <T>(list: T[]): WrappedArray<T>;
//// map<T, U>(list: T[], iterator: Iterator<T, U>, 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);

View File

@ -1,7 +0,0 @@
/// <reference path="fourslash.ts" />
//// function foo2<T>(test);
//// function foo2<T>() { }
//// /**/foo2<>("");
goTo.marker();
diagnostics.validateTypeAtCurrentPosition();

View File

@ -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;

View File

@ -1,12 +0,0 @@
/// <reference path="fourslash.ts" />
////declare module A.B {
//// export class C { }
////}
////
////import ab = A.B;
////
////class D extends ab.C/**/{ }
goTo.marker();
diagnostics.validateTypesAtPositions(FourSlash.currentTestState.currentCaretPosition);

View File

@ -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);
});

View File

@ -1,8 +0,0 @@
/// <reference path="fourslash.ts" />
//// var x: Number;
//// var y: Number;
//// var z = x ;
edit.disableFormatting();
diagnostics.validateTypesAtPositions(28);

View File

@ -1,29 +0,0 @@
/// <reference path="fourslash.ts" />
//// class A<T, U extends T> { }
//// class B<T extends Object, U extends T> {
//// data: A<Object, Object>;
//// }
////
//// // Below 2 should compile without error
//// var x: A< { }, { b: number }>;
//// var y: B< { a: string }, { }>;
////
////
//// // Below should be in error
//// var x1: A<{ a: string;}>;
//// var x2: A<{ a: number }>;
//// var x3: B<{ a: string;}, { b: string }>;
//// var x4: B<{ a: string;}>;
//// var x5: A<{ a: string; b: number }, { a: string }>;
//// var x6: B<>;
////
//// interface I1 {
//// a: string;
//// }
//// var x8: B<I2, I1>;
////
edit.disableFormatting();
diagnostics.validateTypesAtPositions(34);

View File

@ -1,12 +0,0 @@
/// <reference path="fourslash.ts" />
//// function f18(a?:string, ...b){}
////
//// function f19(a?:string, b?){}
////
//// function f20(a:string, b?:string, ...c:number[]){}
////
//// function f21(a:string, b?:string, ...d:number[]){}
edit.disableFormatting();
diagnostics.validateTypesAtPositions(133);

View File

@ -0,0 +1,38 @@
/// <reference path="fourslash.ts" />
//@Filename: findAllRefsOnDefinition-import.ts
////export class Test{
////
//// constructor(){
////
//// }
////
//// public /*1*/start(){
//// return this;
//// }
////
//// public stop(){
//// return this;
//// }
////}
//@Filename: findAllRefsOnDefinition.ts
////import Second = require("findAllRefsOnDefinition-import");
////
////var second = new Second.Test()
////second.start();
////second.stop();
goTo.file("findAllRefsOnDefinition-import.ts");
goTo.marker("1");
verify.referencesCountIs(2);
cancellation.setCancelled();
goTo.marker("1");
verifyOperationIsCancelled(() => verify.referencesCountIs(0) );
// verify that internal state is still correct
cancellation.resetCancelled();
goTo.marker("1");
verify.referencesCountIs(2);

View File

@ -0,0 +1,43 @@
/// <reference path="fourslash.ts"/>
//////curly braces
////module Foo [|{
//// class Bar [|{
//// private f() [|{
//// }|]
////
//// private f2() [|{
//// if (true) [|{ }|] [|{ }|];
//// }|]
//// }|]
////}|]
////
//////parenthesis
////class FooBar {
//// private f[|()|] {
//// return [|([|(1 + 1)|])|];
//// }
////
//// private f2[|()|] {
//// if [|(true)|] { }
//// }
////}
////
//////square brackets
////class Baz {
//// private f() {
//// var a: any[|[]|] = [|[[|[1, 2]|], [|[3, 4]|], 5]|];
//// }
////}
////
////// angular brackets
////class TemplateTest [|<T1, T2 extends Array>|] {
//// public foo(a, b) {
//// return [|<any>|] a;
//// }
////}
test.ranges().forEach((range) => {
verify.matchingBracePositionInCurrentFile(range.start, range.end - 1);
verify.matchingBracePositionInCurrentFile(range.end - 1, range.start);
});

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
// @BaselineFile: getBreakpointStatementAtPosition.baseline
// @Filename: getBreakpointStatementAtPosition.ts
////while (true) {
//// break;
////}
////y: while (true) {
//// break y;
////}
////while (true) {
//// continue;
////}
////z: while (true) {
//// continue z;
////}
verify.baselineCurrentFileBreakpointLocations();

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts'/>
////function foo(strOrNum: string | number) {
//// /*1*/
//// if (typeof strOrNum === "number") {
//// /*2*/
//// }
//// else {
//// /*3*/
//// }
////}
goTo.marker('1');
verify.completionListContains("strOrNum", "(parameter) strOrNum: string | number");
goTo.marker('2');
verify.completionListContains("strOrNum", "(parameter) strOrNum: number");
goTo.marker('3');
verify.completionListContains("strOrNum", "(parameter) strOrNum: string");

View File

@ -0,0 +1,29 @@
/// <reference path='fourslash.ts' />
// @Filename: goToDefinitionDifferentFile_Definition.ts
////var /*remoteVariableDefinition*/remoteVariable;
/////*remoteFunctionDefinition*/function remoteFunction() { }
/////*remoteClassDefinition*/class remoteClass { }
/////*remoteInterfaceDefinition*/interface remoteInterface{ }
/////*remoteModuleDefinition*/module remoteModule{ export var foo = 1;}
// @Filename: goToDefinitionDifferentFile_Consumption.ts
/////*remoteVariableReference*/remoteVariable = 1;
/////*remoteFunctionReference*/remoteFunction();
////var foo = new /*remoteClassReference*/remoteClass();
////class fooCls implements /*remoteInterfaceReference*/remoteInterface { }
////var fooVar = /*remoteModuleReference*/remoteModule.foo;
var markerList = [
"remoteVariable",
"remoteFunction",
"remoteClass",
"remoteInterface",
"remoteModule",
];
markerList.forEach((marker) => {
goTo.marker(marker + 'Reference');
goTo.definition();
verify.caretAtMarker(marker + 'Definition');
});

View File

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
// @BaselineFile: getEmitOutput.baseline
// @declaration: true
// @Filename: inputFile1.ts
// @emitThisFile: true
//// var x: number = 5;
//// class Bar {
//// x : string;
//// y : number
//// }
// @Filename: inputFile2.ts
// @emitThisFile: true
//// var x1: string = "hello world";
//// class Foo{
//// x : string;
//// y : number;
//// }
verify.baselineGetEmitOutput();

View File

@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
////class Bar {
//// {| "indentation": 4|}
//// private foo: string = "";
//// {| "indentation": 4|}
//// private f() {
//// var a: any[] = [[1, 2], [3, 4], 5];
//// {| "indentation": 8|}
//// return ((1 + 1));
//// }
//// {| "indentation": 4|}
//// private f2() {
//// if (true) { } { };
//// }
////}
////{| "indentation": 0|}
test.markers().forEach((marker) => {
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indentation);
});

View File

@ -0,0 +1,27 @@
/// <reference path="fourslash.ts"/>
/////// Module
////{| "itemName": "Shapes", "kind": "module", "parentName": "" |}module Shapes {
////
//// // Class
//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point {
//// // Instance member
//// {| "itemName": "origin", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private origin = 0.0;
////
//// {| "itemName": "distFromZero", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private distFromZero = 0.0;
////
//// // Getter
//// {| "itemName": "distance", "kind": "getter", "parentName": "Point", "matchKind": "exact" |}get distance(): number { return 0; }
//// }
////}
////
////// Local variables
////{| "itemName": "point", "kind": "var", "parentName": "", "matchKind": "exact"|}var point = new Shapes.Point();
//// Testing for exact matching of navigationItems
test.markers().forEach((marker) => {
if (marker.data) {
verify.navigationItemsListContains(marker.data.itemName, marker.data.kind, marker.data.itemName, marker.data.matchKind, marker.fileName, marker.data.parentName);
}
});

View File

@ -0,0 +1,13 @@
/// <reference path="fourslash.ts" />
//// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0;
test.markers().forEach(marker => {
verify.getScriptLexicalStructureListContains(
marker.data.itemName,
marker.data.kind,
marker.fileName,
marker.data.parentName,
marker.data.isAdditionalRange,
marker.position);
});

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts'/>
/////*0*/
////interface A {
//// foo: string;
////}
////function foo(x: A) {
//// x.f/*1*/oo
////}
goTo.marker("1");
verify.occurrencesAtPositionCount(2);
goTo.marker("0");
edit.insert("\r\n");
goTo.marker("1");
verify.occurrencesAtPositionCount(2);

View File

@ -0,0 +1,113 @@
/// <reference path="fourslash.ts"/>
////// interface
////interface IFoo[| {
//// getDist(): number;
////}|]
////
////// class members
////class Foo[| {
//// constructor()[| {
//// }|]
////
//// public foo(): number[| {
//// return 0;
//// }|]
////
//// public get X()[| {
//// return 1;
//// }|]
////
//// public set X(v: number)[| {
//// }|]
////
//// public member = function f()[| {
////
//// }|]
////}|]
////switch(1)[| {
//// case 1: break;
////}|]
////
////var array =[| [
//// 1,
//// 2
////]|]
////
////// modules
////module m1[| {
//// module m2[| { }|]
//// module m3[| {
//// function foo()[| {
////
//// }|]
////
//// interface IFoo2[| {
////
//// }|]
////
//// class foo2 implements IFoo2[| {
////
//// }|]
//// }|]
////}|]
////
////// function declaration
////function foo(): number[| {
//// return 0;
////}|]
////
////// function expressions
////(function f()[| {
////
////}|])
////
////// trivia handeling
////class ClassFooWithTrivia[| /* some comments */
//// /* more trivia */ {
////
////
//// /*some trailing trivia */
////}|] /* even more */
////
////// object literals
////var x =[|{
//// a:1,
//// b:2,
//// get foo()[| {
//// return 1;
//// }|]
////}|]
//////outline with deep nesting
////module m1[|{
//// module m2[| {
//// module m3[| {
//// module m4[| {
//// module m5[| {
//// module m6[| {
//// module m7[| {
//// module m8[| {
//// module m9[| {
//// module m10[| {
//// module m11 {
//// module m12 {
//// export interface IFoo {
//// }
//// }
//// }
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
//// }|]
////}|]
////
//////outline after a deeply nested node
////class AfterNestedNodes[| {
////}|]
verify.outliningSpansInCurrentFile(test.ranges());

View File

@ -0,0 +1,32 @@
/// <reference path="fourslash.ts" />
// @Filename: refFile1.ts
//// class D { }
// @Filename: refFile2.ts
//// export class E {}
// @Filename: main.ts
// @ResolveReference: true
//// ///<reference path="refFile1.ts" />
//// /*1*////<reference path = "NotExistRef.ts" />/*2*/
//// /*3*////<reference path "invalidRefFile1.ts" />/*4*/
//// import ref2 = require("refFile2");
//// import noExistref2 = require(/*5*/"NotExistRefFile2"/*6*/);
//// import invalidRef1 /*7*/require/*8*/("refFile2");
//// /*9*/import invalidRef2 = requi/*10*/("refFile2");
//// var obj: /*11*/C/*12*/;
//// var obj1: D;
//// var obj2: ref2.E;
goTo.file("main.ts");
verify.numberOfErrorsInCurrentFile(7);
verify.errorExistsBetweenMarkers("1", "2");
verify.errorExistsBetweenMarkers("3", "4");
verify.errorExistsBetweenMarkers("5", "6");
verify.errorExistsBetweenMarkers("7", "8");
verify.errorExistsBetweenMarkers("9", "10"); // At this position, there are two diagnostic messages: ';' expected, Cannot find name 'requi'
verify.errorExistsBetweenMarkers("11", "12");

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts'/>
////class SS<T>{}
////
////var x/*1*/1 = new SS<number>();
////var x/*2*/2 = new SS();
////var x/*3*/3 = new SS;
goTo.marker('1');
verify.quickInfoIs('(var) x1: SS<number>');
goTo.marker('2');
verify.quickInfoIs('(var) x2: SS<{}>');
goTo.marker('3');
verify.quickInfoIs('(var) x3: SS<{}>');

View File

@ -0,0 +1,29 @@
/// <reference path="fourslash.ts" />
//@Filename: findAllRefsOnDefinition-import.ts
////export class Test{
////
//// constructor(){
////
//// }
////
//// public /*1*/start(){
//// return this;
//// }
////
//// public stop(){
//// return this;
//// }
////}
//@Filename: findAllRefsOnDefinition.ts
////import Second = require("findAllRefsOnDefinition-import");
////
////var second = new Second.Test()
////second.start();
////second.stop();
goTo.file("findAllRefsOnDefinition-import.ts");
goTo.marker("1");
verify.referencesCountIs(2);

View File

@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />
///////<reference path="./Bar.ts" />
////function /**/[|Bar|]() {
//// // This is a reference to Bar in a comment.
//// "this is a reference to Bar in a string"
////}
goTo.marker();
verify.renameLocations(/*findInStrings:*/ false, /*findInComments:*/ false);

View File

@ -0,0 +1,15 @@
/// <reference path="fourslash.ts"/>
//// module /*0*/M {
//// export interface /*1*/I {
//// }
//// }
//// interface /*2*/X extends /*3*/M./*4*/I { }
var c = classification;
verify.semanticClassificationsAre(
c.moduleName("M", test.marker("0").position),
c.interfaceName("I", test.marker("1").position),
c.interfaceName("X", test.marker("2").position),
c.moduleName("M", test.marker("3").position),
c.interfaceName("I", test.marker("4").position));

View File

@ -0,0 +1,11 @@
/// <reference path="fourslash.ts" />
// @module: CommonJS
// @declaration: true
//// interface privateInterface {}
//// export class Bar implements /*1*/privateInterface/*2*/{ }
verify.errorExistsBetweenMarkers("1", "2");
verify.numberOfErrorsInCurrentFile(1);

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file0.ts
////declare function fn(x: string, y: number);
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file1.ts
////declare function fn(x: string);
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file2.ts
////fn(/*1*/
goTo.marker('1');
verify.signatureHelpCountIs(2);

View File

@ -0,0 +1,35 @@
/// <reference path="fourslash.ts"/>
//// // comment
//// module M {
//// var v = 0 + 1;
//// var s = "string";
////
//// class C<T> {
//// }
////
//// enum E {
//// }
////
//// interface I {
//// }
////
//// module M1.M2 {
//// }
//// }
var c = classification;
verify.syntacticClassificationsAre(
c.comment("// comment"),
c.keyword("module"), c.moduleName("M"), c.punctuation("{"),
c.keyword("var"), c.text("v"), c.operator("="), c.numericLiteral("0"), c.operator("+"), c.numericLiteral("1"), c.punctuation(";"),
c.keyword("var"), c.text("s"), c.operator("="), c.stringLiteral('"string"'), c.punctuation(";"),
c.keyword("class"), c.className("C"), c.punctuation("<"), c.typeParameterName("T"), c.punctuation(">"), c.punctuation("{"),
c.punctuation("}"),
c.keyword("enum"), c.enumName("E"), c.punctuation("{"),
c.punctuation("}"),
c.keyword("interface"), c.interfaceName("I"), c.punctuation("{"),
c.punctuation("}"),
c.keyword("module"), c.moduleName("M1"), c.punctuation("."), c.moduleName("M2"), c.punctuation("{"),
c.punctuation("}"),
c.punctuation("}"));

View File

@ -0,0 +1,3 @@
//// // [|TODO|]
verify.todoCommentsInCurrentFile(["TODO"]);

View File

@ -0,0 +1,76 @@
/// <reference path='fourslash.ts'/>
////var /*1*/a = 10;
////function foo() {
//// var /*2*/b = /*3*/a;
////}
////module m {
//// var /*4*/c = 10;
//// export var /*5*/d = 10;
////}
////var /*6*/f: () => number;
////var /*7*/g = /*8*/f;
/////*9*/f();
////var /*10*/h: { (a: string): number; (a: number): string; };
////var /*11*/i = /*12*/h;
/////*13*/h(10);
/////*14*/h("hello");
var marker = 0;
function verifyVar(name: string, isLocal: boolean, typeDisplay: ts.SymbolDisplayPart[], optionalNameDisplay?: ts.SymbolDisplayPart[], optionalKindModifiers?: string) {
marker++;
goTo.marker(marker.toString());
var kind = isLocal ? "local var" : "var";
verify.verifyQuickInfoDisplayParts(kind, optionalKindModifiers || "", { start: test.markerByName(marker.toString()).position, length: name.length },
[{ text: "(", kind: "punctuation" }, { text: kind, kind: "text" }, { text: ")", kind: "punctuation" },
{ text: " ", kind: "space" }].concat(optionalNameDisplay || [{ text: name, kind: "localName" }]).concat(
{ text: ":", kind: "punctuation" }, { text: " ", kind: "space" }).concat(typeDisplay),
[]);
}
var numberTypeDisplay: ts.SymbolDisplayPart[] = [{ text: "number", kind: "keyword" }];
verifyVar("a", /*isLocal*/false, numberTypeDisplay);
verifyVar("b", /*isLocal*/true, numberTypeDisplay);
verifyVar("a", /*isLocal*/false, numberTypeDisplay);
verifyVar("c", /*isLocal*/false, numberTypeDisplay);
verifyVar("d", /*isLocal*/false, numberTypeDisplay, [{ text: "m", kind: "moduleName" }, { text: ".", kind: "punctuation" }, { text: "d", kind: "localName" }], "export");
var functionTypeReturningNumber: ts.SymbolDisplayPart[] = [{ text: "(", kind: "punctuation" }, { text: ")", kind: "punctuation" },
{ text: " ", kind: "space" }, { text: "=>", kind: "punctuation" }, { text: " ", kind: "space" }, { text: "number", kind: "keyword" }];
verifyVar("f", /*isLocal*/ false, functionTypeReturningNumber);
verifyVar("g", /*isLocal*/ false, functionTypeReturningNumber);
verifyVar("f", /*isLocal*/ false, functionTypeReturningNumber);
verifyVar("f", /*isLocal*/ false, functionTypeReturningNumber);
function getFunctionType(parametertype: string, returnType: string, isArrow?: boolean): ts.SymbolDisplayPart[] {
var functionTypeDisplay = [{ text: "(", kind: "punctuation" }, { text: "a", kind: "parameterName" }, { text: ":", kind: "punctuation" },
{ text: " ", kind: "space" }, { text: parametertype, kind: "keyword" }, { text: ")", kind: "punctuation" }];
if (isArrow) {
functionTypeDisplay = functionTypeDisplay.concat({ text: " ", kind: "space" }, { text: "=>", kind: "punctuation" });
}
else {
functionTypeDisplay = functionTypeDisplay.concat({ text: ":", kind: "punctuation" });
}
return functionTypeDisplay.concat({ text: " ", kind: "space" }, { text: returnType, kind: "keyword" });
}
var typeLiteralWithOverloadCall: ts.SymbolDisplayPart[] = [{ text: "{", kind: "punctuation" }, { text: "\n", kind: "lineBreak" },
{ text: " ", kind: "space" }].concat(getFunctionType("string", "number")).concat(
{ text: ";", kind: "punctuation" }, { text: "\n", kind: "lineBreak" },
{ text: " ", kind: "space" }).concat(getFunctionType("number", "string")).concat(
{ text: ";", kind: "punctuation" }, { text: "\n", kind: "lineBreak" }, { text: "}", kind: "punctuation" });
verifyVar("h", /*isLocal*/ false, typeLiteralWithOverloadCall);
verifyVar("i", /*isLocal*/ false, typeLiteralWithOverloadCall);
verifyVar("h", /*isLocal*/ false, typeLiteralWithOverloadCall);
var overloadDisplay: ts.SymbolDisplayPart[] = [{ text: " ", kind: "space" }, { text: "(", kind: "punctuation" },
{ text: "+", kind: "operator" }, { text: "1", kind: "numericLiteral" },
{ text: " ", kind: "space" }, { text: "overload", kind: "text" }, { text: ")", kind: "punctuation" }];
verifyVar("h", /*isLocal*/ false, getFunctionType("number", "string", /*isArrow*/true).concat(overloadDisplay));
verifyVar("h", /*isLocal*/ false, getFunctionType("string", "number", /*isArrow*/true).concat(overloadDisplay));

View File

@ -20,5 +20,3 @@ edit.insert(", X");
goTo.marker('addTypeParam');
edit.insert(", X");
diagnostics.validateTypesAtPositions(91, 163);

View File

@ -1,27 +0,0 @@
/// <reference path="fourslash.ts" />
//// class Point {
////
//// constructor(public x: number) {
////
//// }
//// getDist() {
//// }
//// static origin = new Point(0);
//// }
////
//// class Point3D {
////
//// constructor(x: number, y: number, private z) {
//// super(x, y);
//// }
////
//// getDist() {
//// return Math.sqrt(this.x*this.x + this.z*this.m);
//// }
//// }
////
////
edit.disableFormatting();
diagnostics.validateTypesAtPositions(258);

View File

@ -1,12 +0,0 @@
/// <reference path="fourslash.ts" />
//// interface Sequence<T> {
//// each(iterator: (value: T) => void): void;
//// filter(): Sequence<T>;
//// groupBy<K>(keySelector: () => K): Sequence<{ items: T/**/[]; }>;
//// }
goTo.file(0);
// Marker in above file is placed at position 154
diagnostics.validateTypesAtPositions(154);

View File

@ -1,11 +0,0 @@
/// <reference path="fourslash.ts" />
//// function method() {
//// var dictionary = <{ [index: string]: string; }>{};
//// }
////
edit.disableFormatting(); // Disregard, just here to keep Fourslash happy
diagnostics.validateTypesAtPositions(44);

View File

@ -1,40 +0,0 @@
/// <reference path="fourslash.ts" />
//// // @sourcemap: true
//// module Foo.Bar {
//// "use strict";
////
//// class Greeter {
//// constructor(public greeting: string) {
//// }
////
//// greet() {
//// }
//// }
////
////
//// function foo(greeting: string): Foo.Bar.Greeter {
//// return new Greeter(greeting);
//// }
////
//// var greeter = new Greeter("Hello, world!");
//// var str = greeter.greet();
////
//// function foo2(greeting: string, ...restGreetings) {
//// var greeters: Greeter[] = [];
//// new Greeter(greeting);
//// for (var i = 0; restGreetings.length; i++) {
//// greeters.push(new Greeter(restGreetings[i]));
//// }
////
//// return greeters;
//// }
////
//// var b = foo2("Hello", "World");
//// for (var j = 0; j < b.length; j++) {
//// b[j].greet();
//// }
//// }
edit.disableFormatting();
diagnostics.validateTypesAtPositions(705);

View File

@ -1,84 +1,47 @@
/// <reference path="..\..\..\..\src\harness\external\mocha.d.ts" />
/// <reference path="..\..\..\..\src\harness\harnessLanguageService.ts" />
interface Classification {
position: number;
length: number;
class: ts.TokenClass;
}
interface ClassiferResult {
tuples: Classification[];
finalEndOfLineState: ts.EndOfLineState;
}
interface ClassificationEntry {
value: any;
class: ts.TokenClass;
classification: ts.TokenClass;
}
describe('Colorization', function () {
var mytypescriptLS = new Harness.LanguageService.TypeScriptLS();
var myclassifier = mytypescriptLS.getClassifier();
// Use the shim adapter to ensure test coverage of the shim layer for the classifier
var languageServiceAdabtor = new Harness.LanguageService.ShimLanugageServiceAdapter();
var classifier = languageServiceAdabtor.getClassifier();
function getLexicalClassifications(code: string, initialEndOfLineState: ts.EndOfLineState = ts.EndOfLineState.Start): ClassiferResult {
var classResult = myclassifier.getClassificationsForLine(code, initialEndOfLineState).split('\n');
var tuples: Classification[] = [];
var i = 0;
var position = 0;
for (; i < classResult.length - 1; i += 2) {
var t = tuples[i / 2] = {
position: position,
length: parseInt(classResult[i]),
class: parseInt(classResult[i + 1])
};
assert.isTrue(t.length > 0, "Result length should be greater than 0, got :" + t.length);
position += t.length;
}
var finalEndOfLineState = classResult[classResult.length - 1];
assert.equal(position, code.length, "Expected cumulative length of all entries to match the length of the source. expected: " + code.length + ", but got: " + position);
return {
tuples: tuples,
finalEndOfLineState: parseInt(finalEndOfLineState)
};
}
function verifyClassification(classification: Classification, expectedLength: number, expectedClass: number) {
assert.isNotNull(classification);
assert.equal(classification.length, expectedLength, "Classification length does not match expected. Expected: " + expectedLength + ", Actual: " + classification.length);
assert.equal(classification.class, expectedClass, "Classification class does not match expected. Expected: " + ts.TokenClass[expectedClass] + ", Actual: " + ts.TokenClass[classification.class]);
}
function getEntryAtPosistion(result: ClassiferResult, position: number) {
for (var i = 0, n = result.tuples.length; i < n; i++) {
if (result.tuples[i].position === position) return result.tuples[i];
function getEntryAtPosistion(result: ts.ClassificationResult, position: number) {
var entryPosition = 0;
for (var i = 0, n = result.entries.length; i < n; i++) {
var entry = result.entries[i];
if (entryPosition === position) {
return entry;
}
entryPosition += entry.length;
}
return undefined;
}
function punctuation(text: string) { return { value: text, class: ts.TokenClass.Punctuation }; }
function keyword(text: string) { return { value: text, class: ts.TokenClass.Keyword }; }
function operator(text: string) { return { value: text, class: ts.TokenClass.Operator }; }
function comment(text: string) { return { value: text, class: ts.TokenClass.Comment }; }
function whitespace(text: string) { return { value: text, class: ts.TokenClass.Whitespace }; }
function identifier(text: string) { return { value: text, class: ts.TokenClass.Identifier }; }
function numberLiteral(text: string) { return { value: text, class: ts.TokenClass.NumberLiteral }; }
function stringLiteral(text: string) { return { value: text, class: ts.TokenClass.StringLiteral }; }
function regExpLiteral(text: string) { return { value: text, class: ts.TokenClass.RegExpLiteral }; }
function finalEndOfLineState(value: number) { return { value: value, class: <ts.TokenClass>undefined }; }
function punctuation(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Punctuation }; }
function keyword(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Keyword }; }
function operator(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Operator }; }
function comment(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Comment }; }
function whitespace(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Whitespace }; }
function identifier(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.Identifier }; }
function numberLiteral(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.NumberLiteral }; }
function stringLiteral(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.StringLiteral }; }
function regExpLiteral(text: string): ClassificationEntry { return { value: text, classification: ts.TokenClass.RegExpLiteral }; }
function finalEndOfLineState(value: number): ClassificationEntry { return { value: value, classification: <ts.TokenClass>undefined }; }
function testLexicalClassification(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
var result = getLexicalClassifications(text, initialEndOfLineState);
var result = classifier.getClassificationsForLine(text, initialEndOfLineState);
for (var i = 0, n = expectedEntries.length; i < n; i++) {
var expectedEntry = expectedEntries[i];
if (expectedEntry.class === undefined) {
assert.equal(result.finalEndOfLineState, expectedEntry.value, "final endOfLineState does not match expected.");
if (expectedEntry.classification === undefined) {
assert.equal(result.finalLexState, expectedEntry.value, "final endOfLineState does not match expected.");
}
else {
var actualEntryPosition = text.indexOf(expectedEntry.value);
@ -87,7 +50,7 @@ describe('Colorization', function () {
var actualEntry = getEntryAtPosistion(result, actualEntryPosition);
assert(actualEntry, "Could not find classification entry for '" + expectedEntry.value + "' at position: " + actualEntryPosition);
assert.equal(actualEntry.class, expectedEntry.class, "Classification class does not match expected. Expected: " + ts.TokenClass[expectedEntry.class] + ", Actual: " + ts.TokenClass[actualEntry.class]);
assert.equal(actualEntry.classification, expectedEntry.classification, "Classification class does not match expected. Expected: " + ts.TokenClass[expectedEntry.classification] + ", Actual: " + ts.TokenClass[actualEntry.classification]);
assert.equal(actualEntry.length, expectedEntry.value.length, "Classification length does not match expected. Expected: " + ts.TokenClass[expectedEntry.value.length] + ", Actual: " + ts.TokenClass[actualEntry.length]);
}
}
@ -320,8 +283,6 @@ describe('Colorization', function () {
});
it("LexicallyClassifiesConflictTokens", () => {
debugger;
// Test conflict markers.
testLexicalClassification(
"class C {\r\n\