diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 9dad3dddf6..fce4edddf0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -136,10 +136,11 @@ module FourSlash { outDir: 'outDir', sourceMap: 'sourceMap', sourceRoot: 'sourceRoot', + resolveReference: 'ResolveReference', // This flag is used to specify entry file for resolve file references. The flag is only allow once per test file }; // List of allowed metadata names - var fileMetadataNames = [testOptMetadataNames.filename, testOptMetadataNames.emitThisFile]; + var fileMetadataNames = [testOptMetadataNames.filename, testOptMetadataNames.emitThisFile, testOptMetadataNames.resolveReference]; var globalMetadataNames = [testOptMetadataNames.baselineFile, testOptMetadataNames.declaration, testOptMetadataNames.mapRoot, testOptMetadataNames.module, testOptMetadataNames.out, testOptMetadataNames.outDir, testOptMetadataNames.sourceMap, testOptMetadataNames.sourceRoot] @@ -236,6 +237,25 @@ module FourSlash { throw new Error("Operation should be cancelled"); } + // This function creates IScriptSnapshot object for testing getPreProcessedFileInfo + // Return object may lack some functionalities for other purposes. + function createScriptSnapShot(sourceText: string): TypeScript.IScriptSnapshot { + return { + getText: (start: number, end: number) => { + return sourceText.substr(start, end - start); + }, + getLength: () => { + return sourceText.length; + }, + getLineStartPositions: () => { + return []; + }, + getChangeRange: (oldSnapshot: TypeScript.IScriptSnapshot) => { + return undefined; + } + }; + } + export class TestState { // Language service instance public languageServiceShimHost: Harness.LanguageService.TypeScriptLS; @@ -274,9 +294,15 @@ module FourSlash { this.languageServiceShimHost.setCompilationSettings(compilationSettings); var inputFiles: { unitName: string; content: string }[] = []; + var startResolveFileRef: FourSlashFile = undefined; testData.files.forEach(file => { - var fixedPath = file.fileName.substr(file.fileName.indexOf('tests/')); + if (!startResolveFileRef && file.fileOptions[testOptMetadataNames.resolveReference]) { + startResolveFileRef = file; + } else if (startResolveFileRef) { + // If entry point for resolving file references is already specified, report duplication error + throw new Error("There exists a Fourslash file which has resolveReference flag specified; remove duplicated resolveReference flag"); + } }); // NEWTODO: disable resolution for now. @@ -294,33 +320,42 @@ module FourSlash { }); //} + if (startResolveFileRef) { + // Add the entry-point file itself into the languageServiceShimHost + this.languageServiceShimHost.addScript(startResolveFileRef.fileName, startResolveFileRef.content); - // NEWTODO: Re-implement commented-out section - //harnessCompiler.addInputFiles(inputFiles); - //try { - // var resolvedFiles = harnessCompiler.resolve(); - - // resolvedFiles.forEach(file => { - // if (!Harness.isLibraryFile(file.path)) { - // var fixedPath = file.path.substr(file.path.indexOf('tests/')); - // var content = harnessCompiler.getContentForFile(fixedPath); - // this.languageServiceShimHost.addScript(fixedPath, content); - // } - // }); - - // this.languageServiceShimHost.addScript('lib.d.ts', Harness.Compiler.libTextMinimal); - //} - //finally { - // // harness no longer needs the results of the above work, make sure the next test operations are in a clean state - // harnessCompiler.reset(); - //} - - /// NEWTODO: For now do not resolve, just use the input files - inputFiles.forEach(file => { - if (!Harness.isLibraryFile(file.unitName)) { - this.languageServiceShimHost.addScript(file.unitName, file.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; + referencedFiles.forEach(refFile => { + inputFiles.forEach(inputFile => { + // Fourslash insert tests/cases/fourslash into inputFile.unitName so we will properly append the same base directory to refFile path + var appendRefFilePath = "tests/cases/fourslash/" + refFile.path; + if (appendRefFilePath === inputFile.unitName && !Harness.isLibraryFile(inputFile.unitName)) { + this.languageServiceShimHost.addScript(inputFile.unitName, inputFile.content); + } + }); + }); + importedFiles.forEach(importedFile => { + inputFiles.forEach(inputFile => { + // Fourslash insert tests/cases/fourslash into inputFile.unitName and import statement doesn't require ".ts" + // so convert them before making appropriate comparison + var appendRefFilePath = "tests/cases/fourslash/" + importedFile.path + ".ts"; + if (appendRefFilePath === inputFile.unitName && !Harness.isLibraryFile(inputFile.unitName)) { + this.languageServiceShimHost.addScript(inputFile.unitName, inputFile.content); + } + }); + }); + } else { + // resolveReference file-option is not specified then do not resolve any files and include all inputFiles + inputFiles.forEach(file => { + if (!Harness.isLibraryFile(file.unitName)) { + this.languageServiceShimHost.addScript(file.unitName, file.content); + } + }); + } this.languageServiceShimHost.addDefaultLibrary(); diff --git a/src/services/shims.ts b/src/services/shims.ts index c53575812c..6e9c175dcd 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -59,7 +59,7 @@ module ts { /// // Note: This is being using by the host (VS) and is marshaled back and forth. // When changing this make sure the changes are reflected in the managed side as well - interface IFileReference { + export interface IFileReference { path: string; position: number; length: number; diff --git a/tests/cases/fourslash/getPreProcessedFile.ts b/tests/cases/fourslash/getPreProcessedFile.ts new file mode 100644 index 0000000000..43f48dc86a --- /dev/null +++ b/tests/cases/fourslash/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"); +verify.errorExistsBetweenMarkers("11", "12"); + + +