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; -//// } -//// -//// // 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; -//// - -edit.disableFormatting(); -diagnostics.validateTypesAtPositions(34); - diff --git a/tests/cases/fourslash/restParametersTypeValidation1.ts b/tests/cases/fourslash/restParametersTypeValidation1.ts deleted file mode 100644 index b598ae7fc84..00000000000 --- a/tests/cases/fourslash/restParametersTypeValidation1.ts +++ /dev/null @@ -1,12 +0,0 @@ -/// - -//// 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); diff --git a/tests/cases/fourslash/shims/cancellationWhenfindingAllRefsOnDefinition.ts b/tests/cases/fourslash/shims/cancellationWhenfindingAllRefsOnDefinition.ts new file mode 100644 index 00000000000..09f580bb965 --- /dev/null +++ b/tests/cases/fourslash/shims/cancellationWhenfindingAllRefsOnDefinition.ts @@ -0,0 +1,38 @@ +/// + +//@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); diff --git a/tests/cases/fourslash/shims/getBraceMatchingAtPosition.ts b/tests/cases/fourslash/shims/getBraceMatchingAtPosition.ts new file mode 100644 index 00000000000..fc8a71197db --- /dev/null +++ b/tests/cases/fourslash/shims/getBraceMatchingAtPosition.ts @@ -0,0 +1,43 @@ +/// + +//////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 [||] { +//// public foo(a, b) { +//// return [||] a; +//// } +////} + +test.ranges().forEach((range) => { + verify.matchingBracePositionInCurrentFile(range.start, range.end - 1); + verify.matchingBracePositionInCurrentFile(range.end - 1, range.start); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getBreakpointStatementAtPosition.ts b/tests/cases/fourslash/shims/getBreakpointStatementAtPosition.ts new file mode 100644 index 00000000000..9e1d075a17f --- /dev/null +++ b/tests/cases/fourslash/shims/getBreakpointStatementAtPosition.ts @@ -0,0 +1,17 @@ +/// + +// @BaselineFile: getBreakpointStatementAtPosition.baseline +// @Filename: getBreakpointStatementAtPosition.ts +////while (true) { +//// break; +////} +////y: while (true) { +//// break y; +////} +////while (true) { +//// continue; +////} +////z: while (true) { +//// continue z; +////} +verify.baselineCurrentFileBreakpointLocations(); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getCompletionsAtPosition.ts b/tests/cases/fourslash/shims/getCompletionsAtPosition.ts new file mode 100644 index 00000000000..714d3390e76 --- /dev/null +++ b/tests/cases/fourslash/shims/getCompletionsAtPosition.ts @@ -0,0 +1,20 @@ +/// + +////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"); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getDefinitionAtPosition.ts b/tests/cases/fourslash/shims/getDefinitionAtPosition.ts new file mode 100644 index 00000000000..54b799ba65f --- /dev/null +++ b/tests/cases/fourslash/shims/getDefinitionAtPosition.ts @@ -0,0 +1,29 @@ +/// + +// @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'); +}); diff --git a/tests/cases/fourslash/shims/getEmitOutput.ts b/tests/cases/fourslash/shims/getEmitOutput.ts new file mode 100644 index 00000000000..699514521ed --- /dev/null +++ b/tests/cases/fourslash/shims/getEmitOutput.ts @@ -0,0 +1,22 @@ +/// + +// @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(); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getIndentationAtPosition.ts b/tests/cases/fourslash/shims/getIndentationAtPosition.ts new file mode 100644 index 00000000000..5e26781357e --- /dev/null +++ b/tests/cases/fourslash/shims/getIndentationAtPosition.ts @@ -0,0 +1,21 @@ +/// + +////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); +}); diff --git a/tests/cases/fourslash/shims/getNavigateToItems.ts b/tests/cases/fourslash/shims/getNavigateToItems.ts new file mode 100644 index 00000000000..9658c52324c --- /dev/null +++ b/tests/cases/fourslash/shims/getNavigateToItems.ts @@ -0,0 +1,27 @@ +/// + +/////// 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); + } +}); diff --git a/tests/cases/fourslash/shims/getNavigationBarItems.ts b/tests/cases/fourslash/shims/getNavigationBarItems.ts new file mode 100644 index 00000000000..6c0738747f3 --- /dev/null +++ b/tests/cases/fourslash/shims/getNavigationBarItems.ts @@ -0,0 +1,13 @@ +/// + +//// {| "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); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts b/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts new file mode 100644 index 00000000000..0654cc3962c --- /dev/null +++ b/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts @@ -0,0 +1,18 @@ +/// + +/////*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); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getOutliningSpans.ts b/tests/cases/fourslash/shims/getOutliningSpans.ts new file mode 100644 index 00000000000..93665889eb4 --- /dev/null +++ b/tests/cases/fourslash/shims/getOutliningSpans.ts @@ -0,0 +1,113 @@ +/// + +////// 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()); diff --git a/tests/cases/fourslash/shims/getPreProcessedFile.ts b/tests/cases/fourslash/shims/getPreProcessedFile.ts new file mode 100644 index 00000000000..abd26bb6e4b --- /dev/null +++ b/tests/cases/fourslash/shims/getPreProcessedFile.ts @@ -0,0 +1,32 @@ +/// + +// @Filename: refFile1.ts +//// class D { } + +// @Filename: refFile2.ts +//// export class E {} + +// @Filename: main.ts +// @ResolveReference: true +//// /// +//// /*1*/////*2*/ +//// /*3*/////*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"); + + + diff --git a/tests/cases/fourslash/shims/getQuickInfoAtPosition.ts b/tests/cases/fourslash/shims/getQuickInfoAtPosition.ts new file mode 100644 index 00000000000..6c9fc7cf795 --- /dev/null +++ b/tests/cases/fourslash/shims/getQuickInfoAtPosition.ts @@ -0,0 +1,16 @@ +/// + +////class SS{} +//// +////var x/*1*/1 = new SS(); +////var x/*2*/2 = new SS(); +////var x/*3*/3 = new SS; + +goTo.marker('1'); +verify.quickInfoIs('(var) x1: SS'); + +goTo.marker('2'); +verify.quickInfoIs('(var) x2: SS<{}>'); + +goTo.marker('3'); +verify.quickInfoIs('(var) x3: SS<{}>'); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getReferencesAtPosition.ts b/tests/cases/fourslash/shims/getReferencesAtPosition.ts new file mode 100644 index 00000000000..34144b74899 --- /dev/null +++ b/tests/cases/fourslash/shims/getReferencesAtPosition.ts @@ -0,0 +1,29 @@ +/// + +//@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); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getRenameInfo.ts b/tests/cases/fourslash/shims/getRenameInfo.ts new file mode 100644 index 00000000000..b5c8ac6aacc --- /dev/null +++ b/tests/cases/fourslash/shims/getRenameInfo.ts @@ -0,0 +1,11 @@ +/// + +/////// + +////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); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getSemanticClassifications.ts b/tests/cases/fourslash/shims/getSemanticClassifications.ts new file mode 100644 index 00000000000..4eb7f2a32ed --- /dev/null +++ b/tests/cases/fourslash/shims/getSemanticClassifications.ts @@ -0,0 +1,15 @@ +/// + +//// 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)); diff --git a/tests/cases/fourslash/shims/getSemanticDiagnostics.ts b/tests/cases/fourslash/shims/getSemanticDiagnostics.ts new file mode 100644 index 00000000000..6345c464213 --- /dev/null +++ b/tests/cases/fourslash/shims/getSemanticDiagnostics.ts @@ -0,0 +1,11 @@ +/// + +// @module: CommonJS +// @declaration: true +//// interface privateInterface {} +//// export class Bar implements /*1*/privateInterface/*2*/{ } + +verify.errorExistsBetweenMarkers("1", "2"); +verify.numberOfErrorsInCurrentFile(1); + + diff --git a/tests/cases/fourslash/shims/getSignatureHelpItems.ts b/tests/cases/fourslash/shims/getSignatureHelpItems.ts new file mode 100644 index 00000000000..846c2d5244a --- /dev/null +++ b/tests/cases/fourslash/shims/getSignatureHelpItems.ts @@ -0,0 +1,13 @@ +/// + +// @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); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getSyntacticClassifications.ts b/tests/cases/fourslash/shims/getSyntacticClassifications.ts new file mode 100644 index 00000000000..88f655683a3 --- /dev/null +++ b/tests/cases/fourslash/shims/getSyntacticClassifications.ts @@ -0,0 +1,35 @@ +/// + +//// // comment +//// module M { +//// var v = 0 + 1; +//// var s = "string"; +//// +//// class C { +//// } +//// +//// 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("}")); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getTodoComments.ts b/tests/cases/fourslash/shims/getTodoComments.ts new file mode 100644 index 00000000000..b1e0086b93f --- /dev/null +++ b/tests/cases/fourslash/shims/getTodoComments.ts @@ -0,0 +1,3 @@ +//// // [|TODO|] + +verify.todoCommentsInCurrentFile(["TODO"]); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/quickInfoDisplayPartsVar.ts b/tests/cases/fourslash/shims/quickInfoDisplayPartsVar.ts new file mode 100644 index 00000000000..56ccceb3ab3 --- /dev/null +++ b/tests/cases/fourslash/shims/quickInfoDisplayPartsVar.ts @@ -0,0 +1,76 @@ +/// + +////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)); \ No newline at end of file diff --git a/tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts b/tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts index e2e978dd7ea..fa312f3c711 100644 --- a/tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts +++ b/tests/cases/fourslash/typeCheckAfterAddingGenericParameter.ts @@ -20,5 +20,3 @@ edit.insert(", X"); goTo.marker('addTypeParam'); edit.insert(", X"); - -diagnostics.validateTypesAtPositions(91, 163); diff --git a/tests/cases/fourslash/typeCheckExpression0.ts b/tests/cases/fourslash/typeCheckExpression0.ts deleted file mode 100644 index 51c6ae75655..00000000000 --- a/tests/cases/fourslash/typeCheckExpression0.ts +++ /dev/null @@ -1,27 +0,0 @@ -/// - -//// 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); diff --git a/tests/cases/fourslash/typeCheckGenericTypeLiteralArgument.ts b/tests/cases/fourslash/typeCheckGenericTypeLiteralArgument.ts deleted file mode 100644 index 107c7a3b525..00000000000 --- a/tests/cases/fourslash/typeCheckGenericTypeLiteralArgument.ts +++ /dev/null @@ -1,12 +0,0 @@ -/// - -//// interface Sequence { -//// each(iterator: (value: T) => void): void; -//// filter(): Sequence; -//// groupBy(keySelector: () => K): Sequence<{ items: T/**/[]; }>; -//// } - -goTo.file(0); - -// Marker in above file is placed at position 154 -diagnostics.validateTypesAtPositions(154); diff --git a/tests/cases/fourslash/typeCheckIndexSignature.ts b/tests/cases/fourslash/typeCheckIndexSignature.ts deleted file mode 100644 index a0c7d049b93..00000000000 --- a/tests/cases/fourslash/typeCheckIndexSignature.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// - -//// function method() { -//// var dictionary = <{ [index: string]: string; }>{}; -//// } -//// - -edit.disableFormatting(); // Disregard, just here to keep Fourslash happy - -diagnostics.validateTypesAtPositions(44); - diff --git a/tests/cases/fourslash/typeCheckIndexerAccess1.ts b/tests/cases/fourslash/typeCheckIndexerAccess1.ts deleted file mode 100644 index 384f6d003e2..00000000000 --- a/tests/cases/fourslash/typeCheckIndexerAccess1.ts +++ /dev/null @@ -1,40 +0,0 @@ -/// - -//// // @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); \ No newline at end of file diff --git a/tests/cases/unittests/services/colorization.ts b/tests/cases/unittests/services/colorization.ts index 20038800a3e..01149764305 100644 --- a/tests/cases/unittests/services/colorization.ts +++ b/tests/cases/unittests/services/colorization.ts @@ -1,84 +1,47 @@ /// /// -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: 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: 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\