Merge pull request #959 from Microsoft/refactorRefFilesPath

Refactor ref files path
This commit is contained in:
Yui 2014-11-07 13:30:43 -08:00
commit 0bacf8f80f
11 changed files with 414 additions and 305 deletions

View File

@ -82,8 +82,9 @@ var harnessSources = [
].map(function (f) {
return path.join(harnessDirectory, f);
}).concat([
"services/colorization.ts",
"services/documentRegistry.ts"
"services/colorization.ts",
"services/documentRegistry.ts",
"services/preProcessFile.ts"
].map(function (f) {
return path.join(unittestsDirectory, f);
}));

View File

@ -745,6 +745,47 @@ module ts {
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
}
interface ReferencePathMatchResult {
fileReference?: FileReference
diagnostic?: DiagnosticMessage
isNoDefaultLib?: boolean
}
export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult {
var simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
var isNoDefaultLibRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)('|")(.+?)\2\s*\/>/gim;
if (simpleReferenceRegEx.exec(comment)) {
if (isNoDefaultLibRegEx.exec(comment)) {
return {
isNoDefaultLib: true
}
}
else {
var matchResult = fullTripleSlashReferencePathRegEx.exec(comment);
if (matchResult) {
var start = commentRange.pos;
var end = commentRange.end;
var fileRef = {
pos: start,
end: end,
filename: matchResult[3]
};
return {
fileReference: fileRef,
isNoDefaultLib: false
};
}
else {
return {
diagnostic: Diagnostics.Invalid_reference_directive_syntax,
isNoDefaultLib: false
};
}
}
}
return undefined;
}
export function isKeyword(token: SyntaxKind): boolean {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}
@ -4168,28 +4209,16 @@ module ts {
for (var i = 0; i < commentRanges.length; i++) {
var range = commentRanges[i];
var comment = sourceText.substring(range.pos, range.end);
var simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
if (simpleReferenceRegEx.exec(comment)) {
var isNoDefaultLibRegEx = /^(\/\/\/\s*<reference\s+no-default-lib=)('|")(.+?)\2\s*\/>/gim;
if (isNoDefaultLibRegEx.exec(comment)) {
file.hasNoDefaultLib = true;
var referencePathMatchResult = getFileReferenceFromReferencePath(comment, range);
if (referencePathMatchResult) {
var fileReference = referencePathMatchResult.fileReference;
file.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
var diagnostic = referencePathMatchResult.diagnostic;
if (fileReference) {
referencedFiles.push(fileReference);
}
else {
var matchResult = fullTripleSlashReferencePathRegEx.exec(comment);
var start = range.pos;
var end = range.end;
var length = end - start;
if (!matchResult) {
errorAtPos(start, length, Diagnostics.Invalid_reference_directive_syntax);
}
else {
referencedFiles.push({
pos: start,
end: end,
filename: matchResult[3]
});
}
if (diagnostic) {
errorAtPos(range.pos, range.end - range.pos, diagnostic);
}
}
else {

View File

@ -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 <number[]>[];
},
getChangeRange: (oldSnapshot: TypeScript.IScriptSnapshot) => {
return <TypeScript.TextChangeRange>undefined;
}
};
}
export class TestState {
// Language service instance
public languageServiceShimHost: Harness.LanguageService.TypeScriptLS;
@ -264,6 +284,16 @@ module FourSlash {
private scenarioActions: string[] = [];
private taoInvalidReason: string = null;
private inputFiles: ts.Map<string> = {}; // Map between inputFile's filename and its content for easily looking up when resolving references
// Add input file which has matched file name with the given reference-file path.
// This is necessary when resolveReference flag is specified
private addMatchedInputFile(referenceFilePath: string) {
var inputFile = this.inputFiles[referenceFilePath];
if (inputFile && !Harness.isLibraryFile(referenceFilePath)) {
this.languageServiceShimHost.addScript(referenceFilePath, inputFile);
}
}
constructor(public testData: FourSlashData) {
// Initialize the language service with all the scripts
@ -273,57 +303,57 @@ module FourSlash {
var compilationSettings = convertGlobalOptionsToCompilationSettings(this.testData.globalOptions);
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/'));
});
// NEWTODO: disable resolution for now.
// If the last unit contains require( or /// reference then consider it the only input file
// and the rest will be added via resolution. If not, then assume we have multiple files
// with 0 references in any of them. We could be smarter here to allow scenarios like
// 2 files without references and 1 file with a reference but we have 0 tests like that
// at the moment and an exhaustive search of the test files for that content could be quite slow.
var lastFile = testData.files[testData.files.length - 1];
//if (/require\(/.test(lastFile.content) || /reference\spath/.test(lastFile.content)) {
// inputFiles.push({ unitName: lastFile.fileName, content: lastFile.content });
//} else {
inputFiles = testData.files.map(file => {
return { unitName: file.fileName, content: file.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);
ts.forEach(testData.files, file => {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles[file.fileName] = file.content;
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");
}
});
this.languageServiceShimHost.addDefaultLibrary();
if (startResolveFileRef) {
// Add the entry-point file itself into the languageServiceShimHost
this.languageServiceShimHost.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;
// 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;
this.addMatchedInputFile(referenceFilePath);
});
// Add import files into language-service host
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";
this.addMatchedInputFile(importedFilePath);
});
// Check if no-default-lib flag is false and if so add default library
if (!resolvedResult.isLibFile) {
this.languageServiceShimHost.addDefaultLibrary();
}
} 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.languageServiceShimHost.addDefaultLibrary();
}
// Sneak into the language service and get its compiler so we can examine the syntax trees
this.languageService = this.languageServiceShimHost.getLanguageService().languageService;

View File

@ -258,6 +258,10 @@ module Harness.LanguageService {
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: TypeScript.IScriptSnapshot): TypeScript.SourceUnitSyntax {
return TypeScript.Parser.parse(fileName, TypeScript.SimpleText.fromScriptSnapshot(sourceText), ts.ScriptTarget.Latest, TypeScript.isDTSFile(fileName)).sourceUnit();

View File

@ -1,208 +0,0 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
module TypeScript {
export interface ILineAndCharacter {
line: number;
character: number;
}
// 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.
export interface IFileReference extends ILineAndCharacter {
path: string;
isResident: boolean;
position: number;
length: number;
}
///
/// Preprocessing
///
export interface IPreProcessedFileInfo {
referencedFiles: IFileReference[];
importedFiles: IFileReference[];
diagnostics: Diagnostic[];
isLibFile: boolean;
}
interface ITripleSlashDirectiveProperties {
noDefaultLib: boolean;
diagnostics: Diagnostic[];
referencedFiles: IFileReference[];
}
function isNoDefaultLibMatch(comment: string): RegExpExecArray {
var isNoDefaultLibRegex = /^(\/\/\/\s*<reference\s+no-default-lib=)('|")(.+?)\2\s*\/>/gim;
return isNoDefaultLibRegex.exec(comment);
}
export var tripleSlashReferenceRegExp = /^(\/\/\/\s*<reference\s+path=)('|")(.+?)\2\s*(static=('|")(.+?)\5\s*)*\/>/;
function getFileReferenceFromReferencePath(fileName: string, text: ISimpleText, position: number, comment: string, diagnostics: Diagnostic[]): IFileReference {
// First, just see if they've written: /// <reference\s+
// If so, then we'll consider this a reference directive and we'll report errors if it's
// malformed. Otherwise, we'll completely ignore this.
var lineMap = text.lineMap();
var simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
if (simpleReferenceRegEx.exec(comment)) {
var isNoDefaultLib = isNoDefaultLibMatch(comment);
if (!isNoDefaultLib) {
var fullReferenceRegEx = tripleSlashReferenceRegExp;
var fullReference = fullReferenceRegEx.exec(comment);
if (!fullReference) {
// It matched the start of a reference directive, but wasn't well formed. Report
// an appropriate error to the user.
diagnostics.push(new Diagnostic(fileName, lineMap, position, comment.length, DiagnosticCode.Invalid_reference_directive_syntax));
}
else {
var path: string = normalizePath(fullReference[3]);
var adjustedPath = normalizePath(path);
var isResident = fullReference.length >= 7 && fullReference[6] === "true";
return {
line: 0,
character: 0,
position: 0,
length: 0,
path: switchToForwardSlashes(adjustedPath),
isResident: isResident
};
}
}
}
return null;
}
var reportDiagnostic = () => { };
function processImports(text: ISimpleText, scanner: Scanner.IScanner, token: ISyntaxToken, importedFiles: IFileReference[]): void {
var lineChar = { line: -1, character: -1 };
var lineMap = text.lineMap();
var start = new Date().getTime();
// Look for:
// import foo = module("foo")
while (token.kind !== SyntaxKind.EndOfFileToken) {
if (token.kind === SyntaxKind.ImportKeyword) {
var importToken = token;
token = scanner.scan(/*allowRegularExpression:*/ false);
if (SyntaxFacts.isIdentifierNameOrAnyKeyword(token)) {
token = scanner.scan(/*allowRegularExpression:*/ false);
if (token.kind === SyntaxKind.EqualsToken) {
token = scanner.scan(/*allowRegularExpression:*/ false);
if (token.kind === SyntaxKind.ModuleKeyword || token.kind === SyntaxKind.RequireKeyword) {
token = scanner.scan(/*allowRegularExpression:*/ false);
if (token.kind === SyntaxKind.OpenParenToken) {
token = scanner.scan(/*allowRegularExpression:*/ false);
lineMap.fillLineAndCharacterFromPosition(TypeScript.start(importToken, text), lineChar);
if (token.kind === SyntaxKind.StringLiteral) {
var ref = {
line: lineChar.line,
character: lineChar.character,
position: TypeScript.start(token, text),
length: width(token),
path: stripStartAndEndQuotes(switchToForwardSlashes(token.text())),
isResident: false
};
importedFiles.push(ref);
}
}
}
}
}
}
token = scanner.scan(/*allowRegularExpression:*/ false);
}
var totalTime = new Date().getTime() - start;
//TypeScript.fileResolutionScanImportsTime += totalTime;
}
function processTripleSlashDirectives(fileName: string, text: ISimpleText, firstToken: ISyntaxToken): ITripleSlashDirectiveProperties {
var leadingTrivia = firstToken.leadingTrivia(text);
var position = 0;
var lineChar = { line: -1, character: -1 };
var noDefaultLib = false;
var diagnostics: Diagnostic[] = [];
var referencedFiles: IFileReference[] = [];
var lineMap = text.lineMap();
for (var i = 0, n = leadingTrivia.count(); i < n; i++) {
var trivia = leadingTrivia.syntaxTriviaAt(i);
if (trivia.kind() === SyntaxKind.SingleLineCommentTrivia) {
var triviaText = trivia.fullText();
var referencedCode = getFileReferenceFromReferencePath(fileName, text, position, triviaText, diagnostics);
if (referencedCode) {
lineMap.fillLineAndCharacterFromPosition(position, lineChar);
referencedCode.position = position;
referencedCode.length = trivia.fullWidth();
referencedCode.line = lineChar.line;
referencedCode.character = lineChar.character;
referencedFiles.push(referencedCode);
}
// is it a lib file?
var isNoDefaultLib = isNoDefaultLibMatch(triviaText);
if (isNoDefaultLib) {
noDefaultLib = isNoDefaultLib[3] === "true";
}
}
position += trivia.fullWidth();
}
return { noDefaultLib: noDefaultLib, diagnostics: diagnostics, referencedFiles: referencedFiles };
}
export function preProcessFile(fileName: string, sourceText: IScriptSnapshot, readImportFiles = true): IPreProcessedFileInfo {
var text = SimpleText.fromScriptSnapshot(sourceText);
var scanner = Scanner.createScanner(ts.ScriptTarget.Latest, text, reportDiagnostic);
var firstToken = scanner.scan(/*allowRegularExpression:*/ false);
// only search out dynamic mods
// if you find a dynamic mod, ignore every other mod inside, until you balance rcurlies
// var position
var importedFiles: IFileReference[] = [];
if (readImportFiles) {
processImports(text, scanner, firstToken, importedFiles);
}
var properties = processTripleSlashDirectives(fileName, text, firstToken);
return { referencedFiles: properties.referencedFiles, importedFiles: importedFiles, isLibFile: properties.noDefaultLib, diagnostics: properties.diagnostics };
}
export function getReferencedFiles(fileName: string, sourceText: IScriptSnapshot): IFileReference[] {
return preProcessFile(fileName, sourceText, false).referencedFiles;
}
} // Tools

View File

@ -1,6 +1,11 @@
///<reference path='references.ts' />
module TypeScript {
export interface ILineAndCharacter {
line: number;
character: number;
}
export class LineMap {
public static empty = new LineMap(() => [0], 0);
private _lineStarts: number[] = undefined;

View File

@ -76,6 +76,12 @@ module ts {
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: string, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
}
export interface PreProcessedFileInfo {
referencedFiles: FileReference[];
importedFiles: FileReference[];
isLibFile: boolean
}
var scanner: Scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
var emptyArray: any[] = [];
@ -1529,7 +1535,7 @@ module ts {
var filenames = host.getScriptFileNames();
for (var i = 0, n = filenames.length; i < n; i++) {
var filename = filenames[i];
this.filenameToEntry[TypeScript.switchToForwardSlashes(filename)] = {
this.filenameToEntry[switchToForwardSlashes(filename)] = {
filename: filename,
version: host.getScriptVersion(filename),
isOpen: host.getScriptIsOpen(filename)
@ -1544,7 +1550,7 @@ module ts {
}
public getEntry(filename: string): HostFileInformation {
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
return lookUp(this.filenameToEntry, filename);
}
@ -1900,6 +1906,68 @@ module ts {
};
}
export function preProcessFile(sourceText: string, readImportFiles = true): PreProcessedFileInfo {
var referencedFiles: FileReference[] = [];
var importedFiles: FileReference[] = [];
var isNoDefaultLib = false;
function processTripleSlashDirectives(): void {
var commentRanges = getLeadingCommentRanges(sourceText, 0);
forEach(commentRanges, commentRange => {
var comment = sourceText.substring(commentRange.pos, commentRange.end);
var referencePathMatchResult = getFileReferenceFromReferencePath(comment, commentRange);
if (referencePathMatchResult) {
isNoDefaultLib = referencePathMatchResult.isNoDefaultLib;
var fileReference = referencePathMatchResult.fileReference;
if (fileReference) {
referencedFiles.push(fileReference);
}
}
});
}
function processImport(): void {
scanner.setText(sourceText);
var token = scanner.scan();
// Look for:
// import foo = module("foo");
while (token !== SyntaxKind.EndOfFileToken) {
if (token === SyntaxKind.ImportKeyword) {
token = scanner.scan();
if (token === SyntaxKind.Identifier) {
token = scanner.scan();
if (token === SyntaxKind.EqualsToken) {
token = scanner.scan();
if (token === SyntaxKind.RequireKeyword) {
token = scanner.scan();
if (token === SyntaxKind.OpenParenToken) {
token = scanner.scan();
if (token === SyntaxKind.StringLiteral) {
var importPath = scanner.getTokenValue();
var pos = scanner.getTokenPos();
importedFiles.push({
filename: importPath,
pos: pos,
end: pos + importPath.length
});
}
}
}
}
}
}
token = scanner.scan();
}
scanner.setText(undefined);
}
if (readImportFiles) {
processImport();
}
processTripleSlashDirectives();
return { referencedFiles: referencedFiles, importedFiles: importedFiles, isLibFile: isNoDefaultLib };
}
/// Helpers
export function getNodeModifiers(node: Node): string {
var flags = node.flags;
@ -2260,7 +2328,7 @@ module ts {
function getSyntacticDiagnostics(filename: string) {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
return program.getDiagnostics(getSourceFile(filename).getSourceFile());
}
@ -2272,7 +2340,7 @@ module ts {
function getSemanticDiagnostics(filename: string) {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename)
filename = switchToForwardSlashes(filename)
var compilerOptions = program.getCompilerOptions();
var checker = getFullTypeCheckChecker();
var targetSourceFile = getSourceFile(filename);
@ -2353,7 +2421,7 @@ module ts {
function getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var syntacticStart = new Date().getTime();
var sourceFile = getSourceFile(filename);
@ -2698,7 +2766,7 @@ module ts {
function getCompletionEntryDetails(filename: string, position: number, entryName: string): CompletionEntryDetails {
// Note: No need to call synchronizeHostData, as we have captured all the data we need
// in the getCompletionsAtPosition earlier
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
@ -3198,7 +3266,7 @@ module ts {
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo {
synchronizeHostData();
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getTouchingPropertyName(sourceFile, position);
if (!node) {
@ -3300,7 +3368,7 @@ module ts {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
var node = getTouchingPropertyName(sourceFile, position);
@ -3364,7 +3432,7 @@ module ts {
function getOccurrencesAtPosition(filename: string, position: number): ReferenceEntry[] {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var sourceFile = getSourceFile(filename);
var node = getTouchingWord(sourceFile, position);
@ -3817,7 +3885,7 @@ module ts {
function findReferences(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] {
synchronizeHostData();
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getTouchingPropertyName(sourceFile, position);
@ -4538,7 +4606,7 @@ module ts {
function getEmitOutput(filename: string): EmitOutput {
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var compilerOptions = program.getCompilerOptions();
var targetSourceFile = program.getSourceFile(filename); // Current selected file to be output
// If --out flag is not specified, shouldEmitToOwnFile is true. Otherwise shouldEmitToOwnFile is false.
@ -4714,7 +4782,7 @@ module ts {
function getSignatureHelpItems(fileName: string, position: number): SignatureHelpItems {
synchronizeHostData();
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
return SignatureHelp.getSignatureHelpItems(sourceFile, position, typeInfoResolver, cancellationToken);
@ -4784,12 +4852,12 @@ module ts {
/// Syntactic features
function getSyntaxTree(filename: string): TypeScript.SyntaxTree {
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
return syntaxTreeCache.getCurrentFileSyntaxTree(filename);
}
function getCurrentSourceFile(filename: string): SourceFile {
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var currentSourceFile = syntaxTreeCache.getCurrentSourceFile(filename);
return currentSourceFile;
}
@ -4856,14 +4924,14 @@ module ts {
}
function getNavigationBarItems(filename: string): NavigationBarItem[] {
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
return NavigationBar.getNavigationBarItems(getCurrentSourceFile(filename));
}
function getSemanticClassifications(fileName: string, span: TypeScript.TextSpan): ClassifiedSpan[] {
synchronizeHostData();
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
@ -4934,7 +5002,7 @@ module ts {
function getSyntacticClassifications(fileName: string, span: TypeScript.TextSpan): ClassifiedSpan[] {
// doesn't use compiler - no need to synchronize with host
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getCurrentSourceFile(fileName);
var result: ClassifiedSpan[] = [];
@ -5064,7 +5132,7 @@ module ts {
function getOutliningSpans(filename: string): OutliningSpan[] {
// doesn't use compiler - no need to synchronize with host
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var sourceFile = getCurrentSourceFile(filename);
return OutliningElementsCollector.collectElements(sourceFile);
}
@ -5123,7 +5191,7 @@ module ts {
}
function getIndentationAtPosition(filename: string, position: number, editorOptions: EditorOptions) {
filename = TypeScript.switchToForwardSlashes(filename);
filename = switchToForwardSlashes(filename);
var start = new Date().getTime();
var sourceFile = getCurrentSourceFile(filename);
@ -5160,21 +5228,21 @@ module ts {
}
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[] {
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var manager = getFormattingManager(fileName, options);
return manager.formatSelection(start, end);
}
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[] {
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var manager = getFormattingManager(fileName, options);
return manager.formatDocument();
}
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[] {
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var manager = getFormattingManager(fileName, options);
@ -5360,7 +5428,7 @@ module ts {
function getRenameInfo(fileName: string, position: number): RenameInfo {
synchronizeHostData();
fileName = TypeScript.switchToForwardSlashes(fileName);
fileName = switchToForwardSlashes(fileName);
var sourceFile = getSourceFile(fileName);
var node = getTouchingWord(sourceFile, position);

View File

@ -16,7 +16,6 @@
/// <reference path='services.ts' />
/// <reference path='compiler\pathUtils.ts' />
/// <reference path='compiler\precompile.ts' />
var debugObjectHost = (<any>this);
@ -55,6 +54,17 @@ module ts {
getDefaultLibFilename(): string;
}
///
/// Pre-processing
///
// 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
export interface IFileReference {
path: string;
position: number;
length: number;
}
/** Public interface of a language service instance shim. */
export interface ShimFactory {
registerShim(shim: Shim): void;
@ -835,12 +845,33 @@ module ts {
return forwardJSONCall(this.logger, actionDescription, action);
}
public getPreProcessedFileInfo(fileName: string, sourceText: TypeScript.IScriptSnapshot): string {
public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: TypeScript.IScriptSnapshot): string {
return this.forwardJSONCall(
"getPreProcessedFileInfo('" + fileName + "')",
() => {
var result = TypeScript.preProcessFile(fileName, sourceText);
return result;
var result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()));
var convertResult = {
referencedFiles: <IFileReference[]>[],
importedFiles: <IFileReference[]>[],
isLibFile: result.isLibFile
};
forEach(result.referencedFiles, refFile => {
convertResult.referencedFiles.push({
path: switchToForwardSlashes(normalizePath(refFile.filename)),
position: refFile.pos,
length: refFile.end - refFile.pos
});
});
forEach(result.importedFiles, importedFile => {
convertResult.importedFiles.push({
path: switchToForwardSlashes(importedFile.filename),
position: importedFile.pos,
length: importedFile.end - importedFile.pos
});
});
return convertResult;
});
}

View File

@ -259,6 +259,10 @@ module ts {
return n.kind === SyntaxKind.StringLiteral || n.kind === SyntaxKind.NumericLiteral || isWord(n);
}
export var switchToForwardSlashesRegEx = /\\/g;
export function switchToForwardSlashes(path: string) {
return path.replace(switchToForwardSlashesRegEx, "/");
}
export function isComment(n: Node): boolean {
return n.kind === SyntaxKind.SingleLineCommentTrivia || n.kind === SyntaxKind.MultiLineCommentTrivia;
}

View File

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

View File

@ -0,0 +1,113 @@
/// <reference path="..\..\..\..\src\harness\external\mocha.d.ts" />
/// <reference path="..\..\..\..\src\harness\harnessLanguageService.ts" />
describe('PreProcessFile:', function () {
function test(sourceText: string, readImportFile: boolean, expectedPreProcess: ts.PreProcessedFileInfo): void {
var resultPreProcess = ts.preProcessFile(sourceText, readImportFile);
var resultIsLibFile = resultPreProcess.isLibFile;
var resultImportedFiles = resultPreProcess.importedFiles;
var resultReferencedFiles = resultPreProcess.referencedFiles;
var expectedIsLibFile = expectedPreProcess.isLibFile;
var expectedImportedFiles = expectedPreProcess.importedFiles;
var expectedReferencedFiles = expectedPreProcess.referencedFiles;
assert.equal(resultIsLibFile, expectedIsLibFile, "Pre-processed file has different value for isLibFile. Expected: " + expectedPreProcess + ". Actual: " + resultIsLibFile);
assert.equal(resultImportedFiles.length, expectedImportedFiles.length,
"Array's length of imported files does not match expected. Expected: " + expectedImportedFiles.length + ". Actual: " + resultImportedFiles.length);
assert.equal(resultReferencedFiles.length, expectedReferencedFiles.length,
"Array's length of referenced files does not match expected. Expected: " + expectedReferencedFiles.length + ". Actual: " + resultReferencedFiles.length);
for (var i = 0; i < expectedImportedFiles.length; ++i) {
var resultImportedFile = resultImportedFiles[i];
var expectedImportedFile = expectedImportedFiles[i];
assert.equal(resultImportedFile.filename, expectedImportedFile.filename, "Imported file path does not match expected. Expected: " + expectedImportedFile.filename + ". Actual: " + resultImportedFile.filename + ".");
assert.equal(resultImportedFile.pos, expectedImportedFile.pos, "Imported file position does not match expected. Expected: " + expectedImportedFile.pos + ". Actual: " + resultImportedFile.pos + ".");
assert.equal(resultImportedFile.end, expectedImportedFile.end, "Imported file length does not match expected. Expected: " + expectedImportedFile.end + ". Actual: " + resultImportedFile.end + ".");
}
for (var i = 0; i < expectedReferencedFiles.length; ++i) {
var resultReferencedFile = resultReferencedFiles[i];
var expectedReferencedFile = expectedReferencedFiles[i];
assert.equal(resultReferencedFile.filename, expectedReferencedFile.filename, "Referenced file path does not match expected. Expected: " + expectedReferencedFile.filename + ". Actual: " + resultReferencedFile.filename + ".");
assert.equal(resultReferencedFile.pos, expectedReferencedFile.pos, "Referenced file position does not match expected. Expected: " + expectedReferencedFile.pos + ". Actual: " + resultReferencedFile.pos + ".");
assert.equal(resultReferencedFile.end, expectedReferencedFile.end, "Referenced file length does not match expected. Expected: " + expectedReferencedFile.end + ". Actual: " + resultReferencedFile.end + ".");
}
}
describe("Test preProcessFiles,", function () {
it("Correctly return referenced files from triple slash", function () {
test("///<reference path = \"refFile1.ts\" />" + "\n" + "///<reference path =\"refFile2.ts\"/>" + "\n" + "///<reference path=\"refFile3.ts\" />" + "\n" + "///<reference path= \"..\\refFile4d.ts\" />", true,
{
referencedFiles: [{ filename: "refFile1.ts", pos: 0, end: 37 }, { filename: "refFile2.ts", pos: 38, end: 73 },
{ filename: "refFile3.ts", pos: 74, end: 109 }, { filename: "..\\refFile4d.ts", pos: 110, end: 150 }],
importedFiles: <ts.FileReference[]>[],
isLibFile: false
});
}),
it("Do not return reference path because of invalid triple-slash syntax", function () {
test("///<reference path\"refFile1.ts\" />" + "\n" + "///<reference path =\"refFile2.ts\">" + "\n" + "///<referencepath=\"refFile3.ts\" />" + "\n" + "///<reference pat= \"refFile4d.ts\" />", true,
{
referencedFiles: <ts.FileReference[]>[],
importedFiles: <ts.FileReference[]>[],
isLibFile: false
});
}),
it("Correctly return imported files", function () {
test("import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\"); import i3= require(\"r3.ts\"); import i4=require(\"r4.ts\"); import i5 = require (\"r5.ts\");", true,
{
referencedFiles: <ts.FileReference[]>[],
importedFiles: [{ filename: "r1.ts", pos: 20, end: 25 }, { filename: "r2.ts", pos: 49, end: 54 }, { filename: "r3.ts", pos: 78, end: 83 },
{ filename: "r4.ts", pos: 106, end: 111 }, { filename: "r5.ts", pos: 138, end: 143 }],
isLibFile: false
});
}),
it("Do not return imported files if readImportFiles argument is false", function () {
test("import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\"); import i3= require(\"r3.ts\"); import i4=require(\"r4.ts\"); import i5 = require (\"r5.ts\");", false,
{
referencedFiles: <ts.FileReference[]>[],
importedFiles: <ts.FileReference[]>[],
isLibFile: false
});
}),
it("Do not return import path because of invalid import syntax", function () {
test("import i1 require(\"r1.ts\"); import = require(\"r2.ts\") import i3= require(\"r3.ts\"); import i5", true,
{
referencedFiles: <ts.FileReference[]>[],
importedFiles: [{ filename: "r3.ts", pos: 73, end: 78 }],
isLibFile: false
});
}),
it("Correctly return referenced files and import files", function () {
test("///<reference path=\"refFile1.ts\" />" + "\n" + "///<reference path =\"refFile2.ts\"/>" + "\n" + "import i1 = require(\"r1.ts\"); import i2 =require(\"r2.ts\");", true,
{
referencedFiles: [{ filename: "refFile1.ts", pos: 0, end: 35 }, { filename: "refFile2.ts", pos: 36, end: 71 }],
importedFiles: [{ filename: "r1.ts", pos: 92, end: 97 }, { filename: "r2.ts", pos: 121, end: 126 }],
isLibFile: false
});
}),
it("Correctly return referenced files and import files even with some invalid syntax", function () {
test("///<reference path=\"refFile1.ts\" />" + "\n" + "///<reference path \"refFile2.ts\"/>" + "\n" + "import i1 = require(\"r1.ts\"); import = require(\"r2.ts\"); import i2 = require(\"r3.ts\");", true,
{
referencedFiles: [{ filename: "refFile1.ts", pos: 0, end: 35 }],
importedFiles: [{ filename: "r1.ts", pos: 91, end: 96 }, { filename: "r3.ts", pos: 148, end: 153 }],
isLibFile: false
})
});
});
});