mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #4154 from Microsoft/reuseAndCache
Revised module resolution
This commit is contained in:
+3
-1
@@ -141,7 +141,9 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"session.ts",
|
||||
"versionCache.ts",
|
||||
"convertToBase64.ts",
|
||||
"transpile.ts"
|
||||
"transpile.ts",
|
||||
"reuseProgramStructure.ts",
|
||||
"cachingInServerLSHost.ts"
|
||||
].map(function (f) {
|
||||
return path.join(unittestsDirectory, f);
|
||||
})).concat([
|
||||
|
||||
+6
-15
@@ -965,7 +965,9 @@ namespace ts {
|
||||
// Escape the name in the "require(...)" clause to ensure we find the right symbol.
|
||||
let moduleName = escapeIdentifier(moduleReferenceLiteral.text);
|
||||
|
||||
if (!moduleName) return;
|
||||
if (!moduleName) {
|
||||
return;
|
||||
}
|
||||
let isRelative = isExternalModuleNameRelative(moduleName);
|
||||
if (!isRelative) {
|
||||
let symbol = getSymbol(globals, "\"" + moduleName + "\"", SymbolFlags.ValueModule);
|
||||
@@ -973,20 +975,9 @@ namespace ts {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
let fileName: string;
|
||||
let sourceFile: SourceFile;
|
||||
while (true) {
|
||||
fileName = normalizePath(combinePaths(searchPath, moduleName));
|
||||
sourceFile = forEach(supportedExtensions, extension => host.getSourceFile(fileName + extension));
|
||||
if (sourceFile || isRelative) {
|
||||
break;
|
||||
}
|
||||
let parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
|
||||
let fileName = getResolvedModuleFileName(getSourceFile(location), moduleReferenceLiteral.text);
|
||||
let sourceFile = fileName && host.getSourceFile(fileName);
|
||||
if (sourceFile) {
|
||||
if (sourceFile.symbol) {
|
||||
return sourceFile.symbol;
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace ts {
|
||||
set,
|
||||
contains,
|
||||
remove,
|
||||
clear,
|
||||
forEachValue: forEachValueInMap
|
||||
};
|
||||
|
||||
@@ -51,6 +52,10 @@ namespace ts {
|
||||
function normalizeKey(key: string) {
|
||||
return getCanonicalFileName(normalizeSlashes(key));
|
||||
}
|
||||
|
||||
function clear() {
|
||||
files = {};
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Comparison {
|
||||
@@ -716,7 +721,7 @@ namespace ts {
|
||||
/**
|
||||
* List of supported extensions in order of file resolution precedence.
|
||||
*/
|
||||
export const supportedExtensions = [".tsx", ".ts", ".d.ts"];
|
||||
export const supportedExtensions = [".ts", ".tsx", ".d.ts"];
|
||||
|
||||
const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
|
||||
export function removeFileExtension(path: string): string {
|
||||
|
||||
+261
-60
@@ -8,6 +8,9 @@ namespace ts {
|
||||
/* @internal */ export let ioWriteTime = 0;
|
||||
|
||||
/** The version of the TypeScript compiler release */
|
||||
|
||||
let emptyArray: any[] = [];
|
||||
|
||||
export const version = "1.6.0";
|
||||
|
||||
export function findConfigFile(searchPath: string): string {
|
||||
@@ -25,6 +28,62 @@ namespace ts {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function resolveTripleslashReference(moduleName: string, containingFile: string): string {
|
||||
let basePath = getDirectoryPath(containingFile);
|
||||
let referencedFileName = isRootedDiskPath(moduleName) ? moduleName : combinePaths(basePath, moduleName);
|
||||
return normalizePath(referencedFileName);
|
||||
}
|
||||
|
||||
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule {
|
||||
// TODO: use different resolution strategy based on compiler options
|
||||
return legacyNameResolver(moduleName, containingFile, compilerOptions, host);
|
||||
}
|
||||
|
||||
function legacyNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule {
|
||||
|
||||
// module names that contain '!' are used to reference resources and are not resolved to actual files on disk
|
||||
if (moduleName.indexOf('!') != -1) {
|
||||
return { resolvedFileName: undefined, failedLookupLocations: [] };
|
||||
}
|
||||
|
||||
let searchPath = getDirectoryPath(containingFile);
|
||||
let searchName: string;
|
||||
|
||||
let failedLookupLocations: string[] = [];
|
||||
|
||||
let referencedSourceFile: string;
|
||||
while (true) {
|
||||
searchName = normalizePath(combinePaths(searchPath, moduleName));
|
||||
referencedSourceFile = forEach(supportedExtensions, extension => {
|
||||
if (extension === ".tsx" && !compilerOptions.jsx) {
|
||||
// resolve .tsx files only if jsx support is enabled
|
||||
// 'logical not' handles both undefined and None cases
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let candidate = searchName + extension;
|
||||
if (host.fileExists(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
else {
|
||||
failedLookupLocations.push(candidate);
|
||||
}
|
||||
});
|
||||
|
||||
if (referencedSourceFile) {
|
||||
break;
|
||||
}
|
||||
|
||||
let parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
|
||||
return { resolvedFileName: referencedSourceFile, failedLookupLocations };
|
||||
}
|
||||
|
||||
export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost {
|
||||
let currentDirectory: string;
|
||||
@@ -92,7 +151,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
const newLine = getNewLineCharacter(options);
|
||||
|
||||
|
||||
|
||||
return {
|
||||
getSourceFile,
|
||||
getDefaultLibFileName: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), getDefaultLibFileName(options)),
|
||||
@@ -100,7 +160,9 @@ namespace ts {
|
||||
getCurrentDirectory: () => currentDirectory || (currentDirectory = sys.getCurrentDirectory()),
|
||||
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
|
||||
getCanonicalFileName,
|
||||
getNewLine: () => newLine
|
||||
getNewLine: () => newLine,
|
||||
fileExists: fileName => sys.fileExists(fileName),
|
||||
readFile: fileName => sys.readFile(fileName)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,7 +205,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost): Program {
|
||||
export function createProgram(rootNames: string[], options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program {
|
||||
let program: Program;
|
||||
let files: SourceFile[] = [];
|
||||
let diagnostics = createDiagnosticCollection();
|
||||
@@ -158,24 +220,56 @@ namespace ts {
|
||||
let start = new Date().getTime();
|
||||
|
||||
host = host || createCompilerHost(options);
|
||||
|
||||
// initialize resolveModuleNameWorker only if noResolve is false
|
||||
let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => string[];
|
||||
if (!options.noResolve) {
|
||||
resolveModuleNamesWorker = host.resolveModuleNames;
|
||||
if (!resolveModuleNamesWorker) {
|
||||
resolveModuleNamesWorker = (moduleNames, containingFile) => {
|
||||
return map(moduleNames, moduleName => {
|
||||
let moduleResolution = resolveModuleName(moduleName, containingFile, options, host);
|
||||
return moduleResolution.resolvedFileName;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let filesByName = createFileMap<SourceFile>(fileName => host.getCanonicalFileName(fileName));
|
||||
|
||||
forEach(rootNames, name => processRootFile(name, /*isDefaultLib:*/ false));
|
||||
|
||||
// Do not process the default library if:
|
||||
// - The '--noLib' flag is used.
|
||||
// - A 'no-default-lib' reference comment is encountered in
|
||||
// processing the root files.
|
||||
if (!skipDefaultLib) {
|
||||
processRootFile(host.getDefaultLibFileName(options), /*isDefaultLib:*/ true);
|
||||
|
||||
if (oldProgram) {
|
||||
// check properties that can affect structure of the program or module resolution strategy
|
||||
// if any of these properties has changed - structure cannot be reused
|
||||
let oldOptions = oldProgram.getCompilerOptions();
|
||||
if ((oldOptions.module !== options.module) ||
|
||||
(oldOptions.noResolve !== options.noResolve) ||
|
||||
(oldOptions.target !== options.target) ||
|
||||
(oldOptions.noLib !== options.noLib) ||
|
||||
(oldOptions.jsx !== options.jsx)) {
|
||||
oldProgram = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryReuseStructureFromOldProgram()) {
|
||||
forEach(rootNames, name => processRootFile(name, false));
|
||||
// Do not process the default library if:
|
||||
// - The '--noLib' flag is used.
|
||||
// - A 'no-default-lib' reference comment is encountered in
|
||||
// processing the root files.
|
||||
if (!skipDefaultLib) {
|
||||
processRootFile(host.getDefaultLibFileName(options), true);
|
||||
}
|
||||
}
|
||||
|
||||
verifyCompilerOptions();
|
||||
|
||||
// unconditionally set oldProgram to undefined to prevent it from being captured in closure
|
||||
oldProgram = undefined;
|
||||
|
||||
programTime += new Date().getTime() - start;
|
||||
|
||||
program = {
|
||||
getRootFileNames: () => rootNames,
|
||||
getSourceFile: getSourceFile,
|
||||
getSourceFiles: () => files,
|
||||
getCompilerOptions: () => options,
|
||||
@@ -211,6 +305,82 @@ namespace ts {
|
||||
return classifiableNames;
|
||||
}
|
||||
|
||||
function tryReuseStructureFromOldProgram(): boolean {
|
||||
if (!oldProgram) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.assert(!oldProgram.structureIsReused);
|
||||
|
||||
// there is an old program, check if we can reuse its structure
|
||||
let oldRootNames = oldProgram.getRootFileNames();
|
||||
if (!arrayIsEqualTo(oldRootNames, rootNames)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if program source files has changed in the way that can affect structure of the program
|
||||
let newSourceFiles: SourceFile[] = [];
|
||||
for (let oldSourceFile of oldProgram.getSourceFiles()) {
|
||||
let newSourceFile = host.getSourceFile(oldSourceFile.fileName, options.target);
|
||||
if (!newSourceFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldSourceFile !== newSourceFile) {
|
||||
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
|
||||
// value of no-default-lib has changed
|
||||
// this will affect if default library is injected into the list of files
|
||||
return false;
|
||||
}
|
||||
|
||||
// check tripleslash references
|
||||
if (!arrayIsEqualTo(oldSourceFile.referencedFiles, newSourceFile.referencedFiles, fileReferenceIsEqualTo)) {
|
||||
// tripleslash references has changed
|
||||
return false;
|
||||
}
|
||||
|
||||
// check imports
|
||||
collectExternalModuleReferences(newSourceFile);
|
||||
if (!arrayIsEqualTo(oldSourceFile.imports, newSourceFile.imports, moduleNameIsEqualTo)) {
|
||||
// imports has changed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolveModuleNamesWorker) {
|
||||
let moduleNames = map(newSourceFile.imports, name => name.text);
|
||||
let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName);
|
||||
// ensure that module resolution results are still correct
|
||||
for (let i = 0; i < moduleNames.length; ++i) {
|
||||
let oldResolution = getResolvedModuleFileName(oldSourceFile, moduleNames[i]);
|
||||
if (oldResolution !== resolutions[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// pass the cache of module resolutions from the old source file
|
||||
newSourceFile.resolvedModules = oldSourceFile.resolvedModules;
|
||||
}
|
||||
else {
|
||||
// file has no changes - use it as is
|
||||
newSourceFile = oldSourceFile;
|
||||
}
|
||||
|
||||
// if file has passed all checks it should be safe to reuse it
|
||||
newSourceFiles.push(newSourceFile);
|
||||
}
|
||||
|
||||
// update fileName -> file mapping
|
||||
for (let file of newSourceFiles) {
|
||||
filesByName.set(file.fileName, file);
|
||||
}
|
||||
|
||||
files = newSourceFiles;
|
||||
|
||||
oldProgram.structureIsReused = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function getEmitHost(writeFileCallback?: WriteFileCallback): EmitHost {
|
||||
return {
|
||||
getCanonicalFileName: fileName => host.getCanonicalFileName(fileName),
|
||||
@@ -370,6 +540,62 @@ namespace ts {
|
||||
|
||||
function processRootFile(fileName: string, isDefaultLib: boolean) {
|
||||
processSourceFile(normalizePath(fileName), isDefaultLib);
|
||||
}
|
||||
|
||||
function fileReferenceIsEqualTo(a: FileReference, b: FileReference): boolean {
|
||||
return a.fileName === b.fileName;
|
||||
}
|
||||
|
||||
function moduleNameIsEqualTo(a: LiteralExpression, b: LiteralExpression): boolean {
|
||||
return a.text === b.text;
|
||||
}
|
||||
|
||||
function collectExternalModuleReferences(file: SourceFile): void {
|
||||
if (file.imports) {
|
||||
return;
|
||||
}
|
||||
|
||||
let imports: LiteralExpression[];
|
||||
for (let node of file.statements) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.ExportDeclaration:
|
||||
let moduleNameExpr = getExternalModuleName(node);
|
||||
if (!moduleNameExpr || moduleNameExpr.kind !== SyntaxKind.StringLiteral) {
|
||||
break;
|
||||
}
|
||||
if (!(<LiteralExpression>moduleNameExpr).text) {
|
||||
break;
|
||||
}
|
||||
|
||||
(imports || (imports = [])).push(<LiteralExpression>moduleNameExpr);
|
||||
break;
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// This type of declaration is permitted only in the global module.
|
||||
// The StringLiteral must specify a top - level external module name.
|
||||
// Relative external module names are not permitted
|
||||
forEachChild((<ModuleDeclaration>node).body, node => {
|
||||
if (isExternalModuleImportEqualsDeclaration(node) &&
|
||||
getExternalModuleImportEqualsDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
let moduleName = <LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(node);
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
|
||||
// only through top - level external module names. Relative external module names are not permitted.
|
||||
if (moduleName) {
|
||||
(imports || (imports = [])).push(moduleName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
file.imports = imports || emptyArray;
|
||||
}
|
||||
|
||||
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
|
||||
@@ -483,57 +709,32 @@ namespace ts {
|
||||
|
||||
function processReferencedFiles(file: SourceFile, basePath: string) {
|
||||
forEach(file.referencedFiles, ref => {
|
||||
let referencedFileName = isRootedDiskPath(ref.fileName) ? ref.fileName : combinePaths(basePath, ref.fileName);
|
||||
processSourceFile(normalizePath(referencedFileName), /* isDefaultLib */ false, file, ref.pos, ref.end);
|
||||
let referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
|
||||
processSourceFile(referencedFileName, /* isDefaultLib */ false, file, ref.pos, ref.end);
|
||||
});
|
||||
}
|
||||
|
||||
function processImportedModules(file: SourceFile, basePath: string) {
|
||||
forEach(file.statements, node => {
|
||||
if (node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration) {
|
||||
let moduleNameExpr = getExternalModuleName(node);
|
||||
if (moduleNameExpr && moduleNameExpr.kind === SyntaxKind.StringLiteral) {
|
||||
let moduleNameText = (<LiteralExpression>moduleNameExpr).text;
|
||||
if (moduleNameText) {
|
||||
let searchPath = basePath;
|
||||
let searchName: string;
|
||||
while (true) {
|
||||
searchName = normalizePath(combinePaths(searchPath, moduleNameText));
|
||||
if (forEach(supportedExtensions, extension => findModuleSourceFile(searchName + extension, moduleNameExpr))) {
|
||||
break;
|
||||
}
|
||||
let parentPath = getDirectoryPath(searchPath);
|
||||
if (parentPath === searchPath) {
|
||||
break;
|
||||
}
|
||||
searchPath = parentPath;
|
||||
}
|
||||
}
|
||||
|
||||
function processImportedModules(file: SourceFile, basePath: string) {
|
||||
collectExternalModuleReferences(file);
|
||||
if (file.imports.length) {
|
||||
file.resolvedModules = {};
|
||||
let oldSourceFile = oldProgram && oldProgram.getSourceFile(file.fileName);
|
||||
|
||||
let moduleNames = map(file.imports, name => name.text);
|
||||
let resolutions = resolveModuleNamesWorker(moduleNames, file.fileName);
|
||||
for (let i = 0; i < file.imports.length; ++i) {
|
||||
let resolution = resolutions[i];
|
||||
setResolvedModuleName(file, moduleNames[i], resolution);
|
||||
if (resolution) {
|
||||
findModuleSourceFile(resolution, file.imports[i]);
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) {
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An AmbientExternalModuleDeclaration declares an external module.
|
||||
// This type of declaration is permitted only in the global module.
|
||||
// The StringLiteral must specify a top - level external module name.
|
||||
// Relative external module names are not permitted
|
||||
forEachChild((<ModuleDeclaration>node).body, node => {
|
||||
if (isExternalModuleImportEqualsDeclaration(node) &&
|
||||
getExternalModuleImportEqualsDeclarationExpression(node).kind === SyntaxKind.StringLiteral) {
|
||||
|
||||
let nameLiteral = <LiteralExpression>getExternalModuleImportEqualsDeclarationExpression(node);
|
||||
let moduleName = nameLiteral.text;
|
||||
if (moduleName) {
|
||||
// TypeScript 1.0 spec (April 2014): 12.1.6
|
||||
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
|
||||
// only through top - level external module names. Relative external module names are not permitted.
|
||||
let searchName = normalizePath(combinePaths(basePath, moduleName));
|
||||
forEach(supportedExtensions, extension => findModuleSourceFile(searchName + extension, nameLiteral));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no imports - drop cached module resolutions
|
||||
file.resolvedModules = undefined;
|
||||
}
|
||||
return;
|
||||
|
||||
function findModuleSourceFile(fileName: string, nameLiteral: Expression) {
|
||||
return findSourceFile(fileName, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
|
||||
|
||||
+40
-3
@@ -9,6 +9,7 @@ namespace ts {
|
||||
contains(fileName: string): boolean;
|
||||
remove(fileName: string): void;
|
||||
forEachValue(f: (v: T) => void): void;
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
export interface TextRange {
|
||||
@@ -1279,8 +1280,12 @@ namespace ts {
|
||||
// Stores a line map for the file.
|
||||
// This field should never be used directly to obtain line map, use getLineMap function instead.
|
||||
/* @internal */ lineMap: number[];
|
||||
|
||||
/* @internal */ classifiableNames?: Map<string>;
|
||||
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
|
||||
// It is used to resolve module names in the checker.
|
||||
// Content of this fiels should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
|
||||
/* @internal */ resolvedModules: Map<string>;
|
||||
/* @internal */ imports: LiteralExpression[];
|
||||
}
|
||||
|
||||
export interface ScriptReferenceHost {
|
||||
@@ -1289,7 +1294,7 @@ namespace ts {
|
||||
getCurrentDirectory(): string;
|
||||
}
|
||||
|
||||
export interface ParseConfigHost {
|
||||
export interface ParseConfigHost extends ModuleResolutionHost {
|
||||
readDirectory(rootDir: string, extension: string, exclude: string[]): string[];
|
||||
}
|
||||
|
||||
@@ -1307,6 +1312,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface Program extends ScriptReferenceHost {
|
||||
|
||||
/**
|
||||
* Get a list of root file names that were passed to a 'createProgram'
|
||||
*/
|
||||
getRootFileNames(): string[]
|
||||
|
||||
/**
|
||||
* Get a list of files in the program
|
||||
*/
|
||||
@@ -1347,6 +1358,9 @@ namespace ts {
|
||||
/* @internal */ getIdentifierCount(): number;
|
||||
/* @internal */ getSymbolCount(): number;
|
||||
/* @internal */ getTypeCount(): number;
|
||||
|
||||
// For testing purposes only.
|
||||
/* @internal */ structureIsReused?: boolean;
|
||||
}
|
||||
|
||||
export interface SourceMapSpan {
|
||||
@@ -2233,9 +2247,23 @@ namespace ts {
|
||||
byteOrderMark = 0xFEFF,
|
||||
tab = 0x09, // \t
|
||||
verticalTab = 0x0B, // \v
|
||||
}
|
||||
|
||||
export interface ModuleResolutionHost {
|
||||
fileExists(fileName: string): boolean;
|
||||
// readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json'
|
||||
// to determine location of bundled typings for node module
|
||||
readFile(fileName: string): string;
|
||||
}
|
||||
|
||||
export interface ResolvedModule {
|
||||
resolvedFileName: string;
|
||||
failedLookupLocations: string[];
|
||||
}
|
||||
|
||||
export type ModuleNameResolver = (moduleName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => ResolvedModule;
|
||||
|
||||
export interface CompilerHost {
|
||||
export interface CompilerHost extends ModuleResolutionHost {
|
||||
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
|
||||
getCancellationToken?(): CancellationToken;
|
||||
getDefaultLibFileName(options: CompilerOptions): string;
|
||||
@@ -2244,6 +2272,15 @@ namespace ts {
|
||||
getCanonicalFileName(fileName: string): string;
|
||||
useCaseSensitiveFileNames(): boolean;
|
||||
getNewLine(): string;
|
||||
|
||||
/*
|
||||
* CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of
|
||||
* module name resolution) or provide implementation for methods from ModuleResolutionHost (in this case compiler
|
||||
* will appply built-in module resolution logic and use members of ModuleResolutionHost to ask host specific questions).
|
||||
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
|
||||
* 'throw new Error("NotImplemented")'
|
||||
*/
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
|
||||
}
|
||||
|
||||
export interface TextSpan {
|
||||
|
||||
@@ -80,6 +80,41 @@ namespace ts {
|
||||
return node.end - node.pos;
|
||||
}
|
||||
|
||||
export function arrayIsEqualTo<T>(arr1: T[], arr2: T[], comparer?: (a: T, b: T) => boolean): boolean {
|
||||
if (!arr1 || !arr2) {
|
||||
return arr1 === arr2;
|
||||
}
|
||||
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < arr1.length; ++i) {
|
||||
let equals = comparer ? comparer(arr1[i], arr2[i]) : arr1[i] === arr2[i];
|
||||
if (!equals) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function hasResolvedModuleName(sourceFile: SourceFile, moduleNameText: string): boolean {
|
||||
return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText);
|
||||
}
|
||||
|
||||
export function getResolvedModuleFileName(sourceFile: SourceFile, moduleNameText: string): string {
|
||||
return hasResolvedModuleName(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined;
|
||||
}
|
||||
|
||||
export function setResolvedModuleName(sourceFile: SourceFile, moduleNameText: string, resolvedFileName: string): void {
|
||||
if (!sourceFile.resolvedModules) {
|
||||
sourceFile.resolvedModules = {};
|
||||
}
|
||||
|
||||
sourceFile.resolvedModules[moduleNameText] = resolvedFileName;
|
||||
}
|
||||
|
||||
// Returns true if this node contains a parse error anywhere underneath it.
|
||||
export function containsParseError(node: Node): boolean {
|
||||
aggregateChildData(node);
|
||||
|
||||
@@ -285,7 +285,9 @@ module FourSlash {
|
||||
case FourSlashTestType.Native:
|
||||
return new Harness.LanguageService.NativeLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
case FourSlashTestType.Shims:
|
||||
return new Harness.LanguageService.ShimLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
return new Harness.LanguageService.ShimLanugageServiceAdapter(/*preprocessToResolve*/ false, cancellationToken, compilationOptions);
|
||||
case FourSlashTestType.ShimsWithPreprocess:
|
||||
return new Harness.LanguageService.ShimLanugageServiceAdapter(/*preprocessToResolve*/ true, cancellationToken, compilationOptions);
|
||||
case FourSlashTestType.Server:
|
||||
return new Harness.LanguageService.ServerLanugageServiceAdapter(cancellationToken, compilationOptions);
|
||||
default:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
const enum FourSlashTestType {
|
||||
Native,
|
||||
Shims,
|
||||
ShimsWithPreprocess,
|
||||
Server
|
||||
}
|
||||
|
||||
@@ -23,6 +24,10 @@ class FourSlashRunner extends RunnerBase {
|
||||
this.basePath = "tests/cases/fourslash/shims";
|
||||
this.testSuiteName = "fourslash-shims";
|
||||
break;
|
||||
case FourSlashTestType.ShimsWithPreprocess:
|
||||
this.basePath = 'tests/cases/fourslash/shims-pp';
|
||||
this.testSuiteName = 'fourslash-shims-pp';
|
||||
break;
|
||||
case FourSlashTestType.Server:
|
||||
this.basePath = "tests/cases/fourslash/server";
|
||||
this.testSuiteName = "fourslash-server";
|
||||
|
||||
+27
-23
@@ -868,6 +868,29 @@ module Harness {
|
||||
}
|
||||
};
|
||||
inputFiles.forEach(register);
|
||||
|
||||
function getSourceFile(fn: string, languageVersion: ts.ScriptTarget) {
|
||||
fn = ts.normalizePath(fn);
|
||||
if (Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(fn))) {
|
||||
return filemap[getCanonicalFileName(fn)];
|
||||
}
|
||||
else if (currentDirectory) {
|
||||
let canonicalAbsolutePath = getCanonicalFileName(ts.getNormalizedAbsolutePath(fn, currentDirectory));
|
||||
return Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(canonicalAbsolutePath)) ? filemap[canonicalAbsolutePath] : undefined;
|
||||
}
|
||||
else if (fn === fourslashFileName) {
|
||||
let tsFn = "tests/cases/fourslash/" + fourslashFileName;
|
||||
fourslashSourceFile = fourslashSourceFile || createSourceFileAndAssertInvariants(tsFn, Harness.IO.readFile(tsFn), scriptTarget);
|
||||
return fourslashSourceFile;
|
||||
}
|
||||
else {
|
||||
if (fn === defaultLibFileName) {
|
||||
return languageVersion === ts.ScriptTarget.ES6 ? defaultES6LibSourceFile : defaultLibSourceFile;
|
||||
}
|
||||
// Don't throw here -- the compiler might be looking for a test that actually doesn't exist as part of the TC
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let newLine =
|
||||
newLineKind === ts.NewLineKind.CarriageReturnLineFeed ? carriageReturnLineFeed :
|
||||
@@ -876,33 +899,14 @@ module Harness {
|
||||
|
||||
return {
|
||||
getCurrentDirectory,
|
||||
getSourceFile: (fn, languageVersion) => {
|
||||
fn = ts.normalizePath(fn);
|
||||
if (Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(fn))) {
|
||||
return filemap[getCanonicalFileName(fn)];
|
||||
}
|
||||
else if (currentDirectory) {
|
||||
let canonicalAbsolutePath = getCanonicalFileName(ts.getNormalizedAbsolutePath(fn, currentDirectory));
|
||||
return Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(canonicalAbsolutePath)) ? filemap[canonicalAbsolutePath] : undefined;
|
||||
}
|
||||
else if (fn === fourslashFileName) {
|
||||
let tsFn = "tests/cases/fourslash/" + fourslashFileName;
|
||||
fourslashSourceFile = fourslashSourceFile || createSourceFileAndAssertInvariants(tsFn, Harness.IO.readFile(tsFn), scriptTarget);
|
||||
return fourslashSourceFile;
|
||||
}
|
||||
else {
|
||||
if (fn === defaultLibFileName) {
|
||||
return languageVersion === ts.ScriptTarget.ES6 ? defaultES6LibSourceFile : defaultLibSourceFile;
|
||||
}
|
||||
// Don't throw here -- the compiler might be looking for a test that actually doesn't exist as part of the TC
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
getSourceFile,
|
||||
getDefaultLibFileName: options => defaultLibFileName,
|
||||
writeFile,
|
||||
getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getNewLine: () => newLine
|
||||
getNewLine: () => newLine,
|
||||
fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined,
|
||||
readFile: (fileName: string): string => { throw new Error("NotYetImplemented"); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -203,9 +203,35 @@ module Harness.LanguageService {
|
||||
/// Shim adapter
|
||||
class ShimLanguageServiceHost extends LanguageServiceAdapterHost implements ts.LanguageServiceShimHost, ts.CoreServicesShimHost {
|
||||
private nativeHost: NativeLanguageServiceHost;
|
||||
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
|
||||
public getModuleResolutionsForFile: (fileName: string)=> string;
|
||||
|
||||
constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
super(cancellationToken, options);
|
||||
this.nativeHost = new NativeLanguageServiceHost(cancellationToken, options);
|
||||
|
||||
if (preprocessToResolve) {
|
||||
let compilerOptions = this.nativeHost.getCompilationSettings()
|
||||
let moduleResolutionHost: ts.ModuleResolutionHost = {
|
||||
fileExists: fileName => this.getScriptInfo(fileName) !== undefined,
|
||||
readFile: fileName => {
|
||||
let scriptInfo = this.getScriptInfo(fileName);
|
||||
return scriptInfo && scriptInfo.content;
|
||||
}
|
||||
};
|
||||
this.getModuleResolutionsForFile = (fileName) => {
|
||||
let scriptInfo = this.getScriptInfo(fileName);
|
||||
let preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
|
||||
let imports: ts.Map<string> = {};
|
||||
for (let module of preprocessInfo.importedFiles) {
|
||||
let resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
|
||||
if (resolutionInfo.resolvedFileName) {
|
||||
imports[module.fileName] = resolutionInfo.resolvedFileName;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(imports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getFilenames(): string[] { return this.nativeHost.getFilenames(); }
|
||||
@@ -229,7 +255,11 @@ module Harness.LanguageService {
|
||||
readDirectory(rootDir: string, extension: string): string {
|
||||
throw new Error("NYI");
|
||||
}
|
||||
|
||||
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
|
||||
readFile(fileName: string) {
|
||||
let snapshot = this.nativeHost.getScriptSnapshot(fileName);
|
||||
return snapshot && snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
log(s: string): void { this.nativeHost.log(s); }
|
||||
trace(s: string): void { this.nativeHost.trace(s); }
|
||||
error(s: string): void { this.nativeHost.error(s); }
|
||||
@@ -399,8 +429,8 @@ module Harness.LanguageService {
|
||||
export class ShimLanugageServiceAdapter implements LanguageServiceAdapter {
|
||||
private host: ShimLanguageServiceHost;
|
||||
private factory: ts.TypeScriptServicesFactory;
|
||||
constructor(cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
this.host = new ShimLanguageServiceHost(cancellationToken, options);
|
||||
constructor(preprocessToResolve: boolean, cancellationToken?: ts.HostCancellationToken, options?: ts.CompilerOptions) {
|
||||
this.host = new ShimLanguageServiceHost(preprocessToResolve, cancellationToken, options);
|
||||
this.factory = new TypeScript.Services.TypeScriptServicesFactory();
|
||||
}
|
||||
getHost() { return this.host; }
|
||||
@@ -419,6 +449,7 @@ module Harness.LanguageService {
|
||||
let convertResult: ts.PreProcessedFileInfo = {
|
||||
referencedFiles: [],
|
||||
importedFiles: [],
|
||||
ambientExternalModules: [],
|
||||
isLibFile: shimResult.isLibFile
|
||||
};
|
||||
|
||||
|
||||
@@ -191,7 +191,9 @@ class ProjectRunner extends RunnerBase {
|
||||
getCurrentDirectory,
|
||||
getCanonicalFileName: Harness.Compiler.getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
|
||||
getNewLine: () => ts.sys.newLine
|
||||
getNewLine: () => ts.sys.newLine,
|
||||
fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined,
|
||||
readFile: fileName => Harness.IO.readFile(fileName)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,10 @@ if (testConfigFile !== "") {
|
||||
case "fourslash-shims":
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
break;
|
||||
case "fourslash-server":
|
||||
case 'fourslash-shims-pp':
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
break;
|
||||
case 'fourslash-server':
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
break;
|
||||
case "fourslash-generated":
|
||||
@@ -98,6 +101,7 @@ if (runners.length === 0) {
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess));
|
||||
runners.push(new FourSlashRunner(FourSlashTestType.Server));
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
}
|
||||
|
||||
@@ -78,15 +78,76 @@ namespace ts.server {
|
||||
return this.snap().getChangeRange(oldSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface TimestampedResolvedModule extends ResolvedModule {
|
||||
lastCheckTime: number;
|
||||
}
|
||||
|
||||
export class LSHost implements ts.LanguageServiceHost {
|
||||
ls: ts.LanguageService = null;
|
||||
compilationSettings: ts.CompilerOptions;
|
||||
filenameToScript: ts.Map<ScriptInfo> = {};
|
||||
roots: ScriptInfo[] = [];
|
||||
|
||||
private resolvedModuleNames: ts.FileMap<Map<TimestampedResolvedModule>>;
|
||||
private moduleResolutionHost: ts.ModuleResolutionHost;
|
||||
|
||||
constructor(public host: ServerHost, public project: Project) {
|
||||
this.resolvedModuleNames = ts.createFileMap<Map<TimestampedResolvedModule>>(ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames))
|
||||
this.moduleResolutionHost = {
|
||||
fileExists: fileName => this.fileExists(fileName),
|
||||
readFile: fileName => this.host.readFile(fileName)
|
||||
}
|
||||
}
|
||||
|
||||
resolveModuleNames(moduleNames: string[], containingFile: string): string[] {
|
||||
let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile);
|
||||
|
||||
let newResolutions: Map<TimestampedResolvedModule> = {};
|
||||
let resolvedFileNames: string[] = [];
|
||||
|
||||
let compilerOptions = this.getCompilationSettings();
|
||||
|
||||
for (let moduleName of moduleNames) {
|
||||
// check if this is a duplicate entry in the list
|
||||
let resolution = lookUp(newResolutions, moduleName);
|
||||
if (!resolution) {
|
||||
let existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, moduleName);
|
||||
if (moduleResolutionIsValid(existingResolution)) {
|
||||
// ok, it is safe to use existing module resolution results
|
||||
resolution = existingResolution;
|
||||
}
|
||||
else {
|
||||
resolution = <TimestampedResolvedModule>resolveModuleName(moduleName, containingFile, compilerOptions, this.moduleResolutionHost);
|
||||
resolution.lastCheckTime = Date.now();
|
||||
newResolutions[moduleName] = resolution;
|
||||
}
|
||||
}
|
||||
|
||||
ts.Debug.assert(resolution !== undefined);
|
||||
|
||||
resolvedFileNames.push(resolution.resolvedFileName);
|
||||
}
|
||||
|
||||
// replace old results with a new one
|
||||
this.resolvedModuleNames.set(containingFile, newResolutions);
|
||||
return resolvedFileNames;
|
||||
|
||||
function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean {
|
||||
if (!resolution) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resolution.resolvedFileName) {
|
||||
// TODO: consider checking failedLookupLocations
|
||||
// TODO: use lastCheckTime to track expiration for module name resolution
|
||||
return true;
|
||||
}
|
||||
|
||||
// consider situation if we have no candidate locations as valid resolution.
|
||||
// after all there is no point to invalidate it if we have no idea where to look for the module.
|
||||
return resolution.failedLookupLocations.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultLibFileName() {
|
||||
var nodeModuleBinDir = ts.getDirectoryPath(ts.normalizePath(this.host.getExecutingFilePath()));
|
||||
@@ -102,6 +163,8 @@ namespace ts.server {
|
||||
|
||||
setCompilationSettings(opt: ts.CompilerOptions) {
|
||||
this.compilationSettings = opt;
|
||||
// conservatively assume that changing compiler options might affect module resolution strategy
|
||||
this.resolvedModuleNames.clear();
|
||||
}
|
||||
|
||||
lineAffectsRefs(filename: string, line: number) {
|
||||
@@ -137,6 +200,7 @@ namespace ts.server {
|
||||
removeReferencedFile(info: ScriptInfo) {
|
||||
if (!info.isOpen) {
|
||||
this.filenameToScript[info.fileName] = undefined;
|
||||
this.resolvedModuleNames.remove(info.fileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+177
-128
@@ -73,7 +73,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an immutable snapshot of a script at a specified time.Once acquired, the
|
||||
* Represents an immutable snapshot of a script at a specified time.Once acquired, the
|
||||
* snapshot is observably immutable. i.e. the same calls with the same parameters will return
|
||||
* the same values.
|
||||
*/
|
||||
@@ -85,9 +85,9 @@ namespace ts {
|
||||
getLength(): number;
|
||||
|
||||
/**
|
||||
* Gets the TextChangeRange that describe how the text changed between this text and
|
||||
* Gets the TextChangeRange that describe how the text changed between this text and
|
||||
* an older version. This information is used by the incremental parser to determine
|
||||
* what sections of the script need to be re-parsed. 'undefined' can be returned if the
|
||||
* what sections of the script need to be re-parsed. 'undefined' can be returned if the
|
||||
* change range cannot be determined. However, in that case, incremental parsing will
|
||||
* not happen and the entire document will be re - parsed.
|
||||
*/
|
||||
@@ -125,6 +125,7 @@ namespace ts {
|
||||
export interface PreProcessedFileInfo {
|
||||
referencedFiles: FileReference[];
|
||||
importedFiles: FileReference[];
|
||||
ambientExternalModules: string[];
|
||||
isLibFile: boolean
|
||||
}
|
||||
|
||||
@@ -275,8 +276,8 @@ namespace ts {
|
||||
let children = this.getChildren(sourceFile);
|
||||
|
||||
let child = lastOrUndefined(children);
|
||||
if (!child) {
|
||||
return undefined;
|
||||
if (!child) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile);
|
||||
@@ -336,10 +337,10 @@ namespace ts {
|
||||
|
||||
ts.forEach(declarations, (declaration, indexOfDeclaration) => {
|
||||
// Make sure we are collecting doc comment from declaration once,
|
||||
// In case of union property there might be same declaration multiple times
|
||||
// In case of union property there might be same declaration multiple times
|
||||
// which only varies in type parameter
|
||||
// Eg. let a: Array<string> | Array<number>; a.length
|
||||
// The property length will have two declarations of property length coming
|
||||
// The property length will have two declarations of property length coming
|
||||
// from Array<T> - Array<string> and Array<number>
|
||||
if (indexOf(declarations, declaration) === indexOfDeclaration) {
|
||||
let sourceFileOfDeclaration = getSourceFileOfNode(declaration);
|
||||
@@ -361,7 +362,7 @@ namespace ts {
|
||||
// If this is dotted module name, get the doc comments from the parent
|
||||
while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) {
|
||||
declaration = <ModuleDeclaration>declaration.parent;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the cleaned js doc comment text from the declaration
|
||||
ts.forEach(getJsDocCommentTextRange(
|
||||
@@ -381,7 +382,7 @@ namespace ts {
|
||||
jsDocComment => {
|
||||
return {
|
||||
pos: jsDocComment.pos + "/*".length, // Consume /* from the comment
|
||||
end: jsDocComment.end - "*/".length // Trim off comment end indicator
|
||||
end: jsDocComment.end - "*/".length // Trim off comment end indicator
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -486,7 +487,7 @@ namespace ts {
|
||||
pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount);
|
||||
blankLineCount = 0;
|
||||
}
|
||||
else if (!isInParamTag && docComments.length) {
|
||||
else if (!isInParamTag && docComments.length) {
|
||||
// This is blank line when there is text already parsed
|
||||
blankLineCount++;
|
||||
}
|
||||
@@ -502,7 +503,7 @@ namespace ts {
|
||||
if (isParamTag(pos, end, sourceFile)) {
|
||||
let blankLineCount = 0;
|
||||
let recordedParamTag = false;
|
||||
// Consume leading spaces
|
||||
// Consume leading spaces
|
||||
pos = consumeWhiteSpaces(pos + paramTag.length);
|
||||
if (pos >= end) {
|
||||
break;
|
||||
@@ -560,7 +561,7 @@ namespace ts {
|
||||
while (pos < end) {
|
||||
let ch = sourceFile.text.charCodeAt(pos);
|
||||
|
||||
// at line break, set this comment line text and go to next line
|
||||
// at line break, set this comment line text and go to next line
|
||||
if (isLineBreak(ch)) {
|
||||
if (paramHelpString) {
|
||||
pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount);
|
||||
@@ -626,7 +627,7 @@ namespace ts {
|
||||
paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character;
|
||||
}
|
||||
|
||||
// Now consume white spaces max
|
||||
// Now consume white spaces max
|
||||
let startOfLinePos = pos;
|
||||
pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin);
|
||||
if (pos >= end) {
|
||||
@@ -761,7 +762,8 @@ namespace ts {
|
||||
public languageVariant: LanguageVariant;
|
||||
public identifiers: Map<string>;
|
||||
public nameTable: Map<string>;
|
||||
|
||||
public resolvedModules: Map<string>;
|
||||
public imports: LiteralExpression[];
|
||||
private namedDeclarations: Map<Declaration[]>;
|
||||
|
||||
public update(newText: string, textChangeRange: TextChangeRange): SourceFile {
|
||||
@@ -974,6 +976,13 @@ namespace ts {
|
||||
trace? (s: string): void;
|
||||
error? (s: string): void;
|
||||
useCaseSensitiveFileNames? (): boolean;
|
||||
|
||||
/*
|
||||
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
|
||||
* if implementation is omitted then language service will use built-in module resolution logic and get answers to
|
||||
* host specific questions using 'getScriptSnapshot'.
|
||||
*/
|
||||
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
|
||||
}
|
||||
|
||||
//
|
||||
@@ -990,17 +999,17 @@ namespace ts {
|
||||
// diagnostics present for the program level, and not just 'options' diagnostics.
|
||||
getCompilerOptionsDiagnostics(): Diagnostic[];
|
||||
|
||||
/**
|
||||
/**
|
||||
* @deprecated Use getEncodedSyntacticClassifications instead.
|
||||
*/
|
||||
getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
|
||||
|
||||
/**
|
||||
/**
|
||||
* @deprecated Use getEncodedSemanticClassifications instead.
|
||||
*/
|
||||
getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[];
|
||||
|
||||
// Encoded as triples of [start, length, ClassificationType].
|
||||
// Encoded as triples of [start, length, ClassificationType].
|
||||
getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications;
|
||||
getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications;
|
||||
|
||||
@@ -1228,7 +1237,7 @@ namespace ts {
|
||||
* Represents a single signature to show in signature help.
|
||||
* The id is used for subsequent calls into the language service to ask questions about the
|
||||
* signature help item in the context of any documents that have been updated. i.e. after
|
||||
* an edit has happened, while signature help is still active, the host can ask important
|
||||
* an edit has happened, while signature help is still active, the host can ask important
|
||||
* questions like 'what parameter is the user currently contained within?'.
|
||||
*/
|
||||
export interface SignatureHelpItem {
|
||||
@@ -1282,8 +1291,8 @@ namespace ts {
|
||||
/** The text to display in the editor for the collapsed region. */
|
||||
bannerText: string;
|
||||
|
||||
/**
|
||||
* Whether or not this region should be automatically collapsed when
|
||||
/**
|
||||
* Whether or not this region should be automatically collapsed when
|
||||
* the 'Collapse to Definitions' command is invoked.
|
||||
*/
|
||||
autoCollapse: boolean;
|
||||
@@ -1364,18 +1373,18 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* The document registry represents a store of SourceFile objects that can be shared between
|
||||
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* of files in the context.
|
||||
* SourceFile objects account for most of the memory usage by the language service. Sharing
|
||||
* the same DocumentRegistry instance between different instances of LanguageService allow
|
||||
* for more efficient memory utilization since all projects will share at least the library
|
||||
* file (lib.d.ts).
|
||||
*
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* A more advanced use of the document registry is to serialize sourceFile objects to disk
|
||||
* and re-hydrate them when needed.
|
||||
*
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
|
||||
* to all subsequent createLanguageService calls.
|
||||
*/
|
||||
export interface DocumentRegistry {
|
||||
@@ -1385,7 +1394,7 @@ namespace ts {
|
||||
* the SourceFile if was not found in the registry.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @parm scriptSnapshot Text of the file. Only used if the file was not found
|
||||
@@ -1405,10 +1414,10 @@ namespace ts {
|
||||
* to get an updated SourceFile.
|
||||
*
|
||||
* @param fileName The name of the file requested
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* @param compilationSettings Some compilation settings like target affects the
|
||||
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
|
||||
* multiple copies of the same file for different compilation settings.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param scriptSnapshot Text of the file.
|
||||
* @param version Current version of the file.
|
||||
*/
|
||||
updateDocument(
|
||||
@@ -1580,7 +1589,7 @@ namespace ts {
|
||||
sourceFile: SourceFile;
|
||||
|
||||
// The number of language services that this source file is referenced in. When no more
|
||||
// language services are referencing the file, then the file can be removed from the
|
||||
// language services are referencing the file, then the file can be removed from the
|
||||
// registry.
|
||||
languageServiceRefCount: number;
|
||||
owners: string[];
|
||||
@@ -1635,8 +1644,8 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
// Cache host information about scrip Should be refreshed
|
||||
// at each language service public entry point, since we don't know when
|
||||
// Cache host information about scrip Should be refreshed
|
||||
// at each language service public entry point, since we don't know when
|
||||
// set of scripts handled by the host changes.
|
||||
class HostCache {
|
||||
private fileNameToEntry: FileMap<HostFileInformation>;
|
||||
@@ -1715,8 +1724,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
class SyntaxTreeCache {
|
||||
// For our syntactic only features, we also keep a cache of the syntax tree for the
|
||||
// currently edited file.
|
||||
// For our syntactic only features, we also keep a cache of the syntax tree for the
|
||||
// currently edited file.
|
||||
private currentFileName: string;
|
||||
private currentFileVersion: string;
|
||||
private currentFileScriptSnapshot: IScriptSnapshot;
|
||||
@@ -1761,7 +1770,7 @@ namespace ts {
|
||||
sourceFile.version = version;
|
||||
sourceFile.scriptSnapshot = scriptSnapshot;
|
||||
}
|
||||
|
||||
|
||||
export interface TranspileOptions {
|
||||
compilerOptions?: CompilerOptions;
|
||||
fileName?: string;
|
||||
@@ -1769,13 +1778,13 @@ namespace ts {
|
||||
moduleName?: string;
|
||||
renamedDependencies?: Map<string>;
|
||||
}
|
||||
|
||||
|
||||
export interface TranspileOutput {
|
||||
outputText: string;
|
||||
diagnostics?: Diagnostic[];
|
||||
sourceMapText?: string;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function will compile source text from 'input' argument using specified compiler options.
|
||||
* If not options are provided - it will use a set of default compiler options.
|
||||
@@ -1793,7 +1802,7 @@ namespace ts {
|
||||
// Filename can be non-ts file.
|
||||
options.allowNonTsExtensions = true;
|
||||
|
||||
// We are not returning a sourceFile for lib file when asked by the program,
|
||||
// We are not returning a sourceFile for lib file when asked by the program,
|
||||
// so pass --noLib to avoid reporting a file not found error.
|
||||
options.noLib = true;
|
||||
|
||||
@@ -1815,7 +1824,6 @@ namespace ts {
|
||||
// Output
|
||||
let outputText: string;
|
||||
let sourceMapText: string;
|
||||
|
||||
// Create a compilerHost object to allow the compiler to read and write files
|
||||
let compilerHost: CompilerHost = {
|
||||
getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined,
|
||||
@@ -1833,11 +1841,14 @@ namespace ts {
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
getCanonicalFileName: fileName => fileName,
|
||||
getCurrentDirectory: () => "",
|
||||
getNewLine: () => newLine
|
||||
getNewLine: () => newLine,
|
||||
// these two methods should never be called in transpile scenarios since 'noResolve' is set to 'true'
|
||||
fileExists: (fileName): boolean => { throw new Error("Should never be called."); },
|
||||
readFile: (fileName): string => { throw new Error("Should never be called."); }
|
||||
};
|
||||
|
||||
let program = createProgram([inputFileName], options, compilerHost);
|
||||
|
||||
|
||||
let diagnostics: Diagnostic[];
|
||||
if (transpileOptions.reportDiagnostics) {
|
||||
diagnostics = [];
|
||||
@@ -1849,11 +1860,11 @@ namespace ts {
|
||||
|
||||
Debug.assert(outputText !== undefined, "Output generation failed");
|
||||
|
||||
return { outputText, diagnostics, sourceMapText };
|
||||
return { outputText, diagnostics, sourceMapText };
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result.
|
||||
* This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result.
|
||||
*/
|
||||
export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string {
|
||||
let output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName });
|
||||
@@ -1874,19 +1885,19 @@ namespace ts {
|
||||
export let disableIncrementalParsing = false;
|
||||
|
||||
export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile {
|
||||
// If we were given a text change range, and our version or open-ness changed, then
|
||||
// If we were given a text change range, and our version or open-ness changed, then
|
||||
// incrementally parse this file.
|
||||
if (textChangeRange) {
|
||||
if (version !== sourceFile.version) {
|
||||
// Once incremental parsing is ready, then just call into this function.
|
||||
if (!disableIncrementalParsing) {
|
||||
let newText: string;
|
||||
|
||||
|
||||
// grab the fragment from the beginning of the original text to the beginning of the span
|
||||
let prefix = textChangeRange.span.start !== 0
|
||||
? sourceFile.text.substr(0, textChangeRange.span.start)
|
||||
: "";
|
||||
|
||||
|
||||
// grab the fragment from the end of the span till the end of the original text
|
||||
let suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length
|
||||
? sourceFile.text.substr(textSpanEnd(textChangeRange.span))
|
||||
@@ -1900,10 +1911,10 @@ namespace ts {
|
||||
// it was actual edit, fetch the fragment of new text that correspond to new span
|
||||
let changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength);
|
||||
// combine prefix, changed text and suffix
|
||||
newText = prefix && suffix
|
||||
newText = prefix && suffix
|
||||
? prefix + changedText + suffix
|
||||
: prefix
|
||||
? (prefix + changedText)
|
||||
? (prefix + changedText)
|
||||
: (changedText + suffix);
|
||||
}
|
||||
|
||||
@@ -1931,7 +1942,7 @@ namespace ts {
|
||||
return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents:*/ true);
|
||||
}
|
||||
|
||||
function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
|
||||
export function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
|
||||
return useCaseSensitivefileNames
|
||||
? ((fileName) => fileName)
|
||||
: ((fileName) => fileName.toLowerCase());
|
||||
@@ -1945,7 +1956,7 @@ namespace ts {
|
||||
let getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
|
||||
|
||||
function getKeyFromCompilationSettings(settings: CompilerOptions): string {
|
||||
return "_" + settings.target; // + "|" + settings.propagateEnumConstantoString()
|
||||
return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve + "|" + settings.jsx;
|
||||
}
|
||||
|
||||
function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
|
||||
@@ -2009,7 +2020,7 @@ namespace ts {
|
||||
bucket.set(fileName, entry);
|
||||
}
|
||||
else {
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
// We have an entry for this file. However, it may be for a different version of
|
||||
// the script snapshot. If so, update it appropriately. Otherwise, we can just
|
||||
// return it as is.
|
||||
if (entry.sourceFile.version !== version) {
|
||||
@@ -2054,6 +2065,7 @@ namespace ts {
|
||||
export function preProcessFile(sourceText: string, readImportFiles = true): PreProcessedFileInfo {
|
||||
let referencedFiles: FileReference[] = [];
|
||||
let importedFiles: FileReference[] = [];
|
||||
let ambientExternalModules: string[];
|
||||
let isNoDefaultLib = false;
|
||||
|
||||
function processTripleSlashDirectives(): void {
|
||||
@@ -2071,6 +2083,13 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function recordAmbientExternalModule(): void {
|
||||
if (!ambientExternalModules) {
|
||||
ambientExternalModules = [];
|
||||
}
|
||||
ambientExternalModules.push(scanner.getTokenValue());
|
||||
}
|
||||
|
||||
function recordModuleName() {
|
||||
let importPath = scanner.getTokenValue();
|
||||
let pos = scanner.getTokenPos();
|
||||
@@ -2096,7 +2115,18 @@ namespace ts {
|
||||
// export {a as b} from "mod"
|
||||
|
||||
while (token !== SyntaxKind.EndOfFileToken) {
|
||||
if (token === SyntaxKind.ImportKeyword) {
|
||||
if (token === SyntaxKind.DeclareKeyword) {
|
||||
// declare module "mod"
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.ModuleKeyword) {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
recordAmbientExternalModule();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (token === SyntaxKind.ImportKeyword) {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
// import "mod";
|
||||
@@ -2104,7 +2134,7 @@ namespace ts {
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (token === SyntaxKind.Identifier) {
|
||||
if (token === SyntaxKind.Identifier || isKeyword(token)) {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = scanner.scan();
|
||||
@@ -2161,7 +2191,7 @@ namespace ts {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.AsKeyword) {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.Identifier) {
|
||||
if (token === SyntaxKind.Identifier || isKeyword(token)) {
|
||||
token = scanner.scan();
|
||||
if (token === SyntaxKind.FromKeyword) {
|
||||
token = scanner.scan();
|
||||
@@ -2217,7 +2247,7 @@ namespace ts {
|
||||
processImport();
|
||||
}
|
||||
processTripleSlashDirectives();
|
||||
return { referencedFiles, importedFiles, isLibFile: isNoDefaultLib };
|
||||
return { referencedFiles, importedFiles, isLibFile: isNoDefaultLib, ambientExternalModules };
|
||||
}
|
||||
|
||||
/// Helpers
|
||||
@@ -2524,18 +2554,22 @@ namespace ts {
|
||||
return;
|
||||
}
|
||||
|
||||
// IMPORTANT - It is critical from this moment onward that we do not check
|
||||
// IMPORTANT - It is critical from this moment onward that we do not check
|
||||
// cancellation tokens. We are about to mutate source files from a previous program
|
||||
// instance. If we cancel midway through, we may end up in an inconsistent state where
|
||||
// the program points to old source files that have been invalidated because of
|
||||
// the program points to old source files that have been invalidated because of
|
||||
// incremental parsing.
|
||||
|
||||
let oldSettings = program && program.getCompilerOptions();
|
||||
let newSettings = hostCache.compilationSettings();
|
||||
let changesInCompilationSettingsAffectSyntax = oldSettings && oldSettings.target !== newSettings.target;
|
||||
let changesInCompilationSettingsAffectSyntax = oldSettings &&
|
||||
(oldSettings.target !== newSettings.target ||
|
||||
oldSettings.module !== newSettings.module ||
|
||||
oldSettings.noResolve !== newSettings.noResolve ||
|
||||
oldSettings.jsx !== newSettings.jsx);
|
||||
|
||||
// Now create a new compiler
|
||||
let newProgram = createProgram(hostCache.getRootFileNames(), newSettings, {
|
||||
let compilerHost: CompilerHost = {
|
||||
getSourceFile: getOrCreateSourceFile,
|
||||
getCancellationToken: () => cancellationToken,
|
||||
getCanonicalFileName,
|
||||
@@ -2543,10 +2577,26 @@ namespace ts {
|
||||
getNewLine: () => getNewLineOrDefaultFromHost(host),
|
||||
getDefaultLibFileName: (options) => host.getDefaultLibFileName(options),
|
||||
writeFile: (fileName, data, writeByteOrderMark) => { },
|
||||
getCurrentDirectory: () => host.getCurrentDirectory()
|
||||
});
|
||||
getCurrentDirectory: () => host.getCurrentDirectory(),
|
||||
fileExists: (fileName): boolean => {
|
||||
// stub missing host functionality
|
||||
Debug.assert(!host.resolveModuleNames);
|
||||
return hostCache.getOrCreateEntry(fileName) !== undefined;
|
||||
},
|
||||
readFile: (fileName): string => {
|
||||
// stub missing host functionality
|
||||
let entry = hostCache.getOrCreateEntry(fileName);
|
||||
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
|
||||
}
|
||||
};
|
||||
|
||||
// Release any files we have acquired in the old program but are
|
||||
if (host.resolveModuleNames) {
|
||||
compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile)
|
||||
}
|
||||
|
||||
let newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program);
|
||||
|
||||
// Release any files we have acquired in the old program but are
|
||||
// not part of the new program.
|
||||
if (program) {
|
||||
let oldSourceFiles = program.getSourceFiles();
|
||||
@@ -2564,7 +2614,7 @@ namespace ts {
|
||||
|
||||
program = newProgram;
|
||||
|
||||
// Make sure all the nodes in the program are both bound, and have their parent
|
||||
// Make sure all the nodes in the program are both bound, and have their parent
|
||||
// pointers set property.
|
||||
program.getTypeChecker();
|
||||
return;
|
||||
@@ -2586,7 +2636,7 @@ namespace ts {
|
||||
// Check if the old program had this file already
|
||||
let oldSourceFile = program && program.getSourceFile(fileName);
|
||||
if (oldSourceFile) {
|
||||
// We already had a source file for this file name. Go to the registry to
|
||||
// We already had a source file for this file name. Go to the registry to
|
||||
// ensure that we get the right up to date version of it. We need this to
|
||||
// address the following 'race'. Specifically, say we have the following:
|
||||
//
|
||||
@@ -2597,15 +2647,15 @@ namespace ts {
|
||||
// LS2
|
||||
//
|
||||
// Each LS has a reference to file 'foo.ts' at version 1. LS2 then updates
|
||||
// it's version of 'foo.ts' to version 2. This will cause LS2 and the
|
||||
// DocumentRegistry to have version 2 of the document. HOwever, LS1 will
|
||||
// it's version of 'foo.ts' to version 2. This will cause LS2 and the
|
||||
// DocumentRegistry to have version 2 of the document. HOwever, LS1 will
|
||||
// have version 1. And *importantly* this source file will be *corrupt*.
|
||||
// The act of creating version 2 of the file irrevocably damages the version
|
||||
// 1 file.
|
||||
//
|
||||
// So, later when we call into LS1, we need to make sure that it doesn't use
|
||||
// it's source file any more, and instead defers to DocumentRegistry to get
|
||||
// either version 1, version 2 (or some other version) depending on what the
|
||||
// either version 1, version 2 (or some other version) depending on what the
|
||||
// host says should be used.
|
||||
return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version);
|
||||
}
|
||||
@@ -2671,7 +2721,7 @@ namespace ts {
|
||||
|
||||
/**
|
||||
* getSemanticDiagnostiscs return array of Diagnostics. If '-d' is not enabled, only report semantic errors
|
||||
* If '-d' enabled, report both semantic and emitter errors
|
||||
* If '-d' enabled, report both semantic and emitter errors
|
||||
*/
|
||||
function getSemanticDiagnostics(fileName: string): Diagnostic[] {
|
||||
synchronizeHostData();
|
||||
@@ -2679,7 +2729,7 @@ namespace ts {
|
||||
let targetSourceFile = getValidSourceFile(fileName);
|
||||
|
||||
// For JavaScript files, we don't want to report the normal typescript semantic errors.
|
||||
// Instead, we just report errors for using TypeScript-only constructs from within a
|
||||
// Instead, we just report errors for using TypeScript-only constructs from within a
|
||||
// JavaScript file.
|
||||
if (isJavaScript(fileName)) {
|
||||
return getJavaScriptSemanticDiagnostics(targetSourceFile);
|
||||
@@ -3059,7 +3109,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (isJavaScriptFile && type.flags & TypeFlags.Union) {
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// In javascript files, for union types, we don't just get the members that
|
||||
// the individual types have in common, we also include all the members that
|
||||
// each individual type has. This is because we're going to add all identifiers
|
||||
// anyways. So we might as well elevate the members that were at least part
|
||||
@@ -3114,10 +3164,10 @@ namespace ts {
|
||||
// aggregating completion candidates. This is achieved in 'getScopeNode'
|
||||
// by finding the first node that encompasses a position, accounting for whether a node
|
||||
// is "complete" to decide whether a position belongs to the node.
|
||||
//
|
||||
//
|
||||
// However, at the end of an identifier, we are interested in the scope of the identifier
|
||||
// itself, but fall outside of the identifier. For instance:
|
||||
//
|
||||
//
|
||||
// xyz => x$
|
||||
//
|
||||
// the cursor is outside of both the 'x' and the arrow function 'xyz => x',
|
||||
@@ -3144,7 +3194,7 @@ namespace ts {
|
||||
/// TODO filter meaning based on the current context
|
||||
let symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias;
|
||||
symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3282,7 +3332,7 @@ namespace ts {
|
||||
|
||||
let rootDeclaration = getRootDeclaration(objectLikeContainer.parent);
|
||||
if (isVariableLike(rootDeclaration)) {
|
||||
// We don't want to complete using the type acquired by the shape
|
||||
// We don't want to complete using the type acquired by the shape
|
||||
// of the binding pattern; we are only interested in types acquired
|
||||
// through type declaration or inference.
|
||||
if (rootDeclaration.initializer || rootDeclaration.type) {
|
||||
@@ -3419,7 +3469,6 @@ namespace ts {
|
||||
parent.kind === SyntaxKind.JsxExpression &&
|
||||
parent.parent &&
|
||||
(parent.parent.kind === SyntaxKind.JsxAttribute)) {
|
||||
|
||||
return <JsxOpeningLikeElement>parent.parent.parent;
|
||||
}
|
||||
|
||||
@@ -3466,16 +3515,16 @@ namespace ts {
|
||||
containingNodeKind === SyntaxKind.FunctionDeclaration || // function A<T, |
|
||||
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface A<T, |
|
||||
containingNodeKind === SyntaxKind.ArrayBindingPattern; // var [x, y|
|
||||
|
||||
|
||||
case SyntaxKind.DotToken:
|
||||
return containingNodeKind === SyntaxKind.ArrayBindingPattern; // var [.|
|
||||
|
||||
|
||||
case SyntaxKind.ColonToken:
|
||||
return containingNodeKind === SyntaxKind.BindingElement; // var {x :html|
|
||||
|
||||
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
return containingNodeKind === SyntaxKind.ArrayBindingPattern; // var [x|
|
||||
|
||||
|
||||
case SyntaxKind.OpenParenToken:
|
||||
return containingNodeKind === SyntaxKind.CatchClause ||
|
||||
isFunction(containingNodeKind);
|
||||
@@ -3627,7 +3676,7 @@ namespace ts {
|
||||
|
||||
return filter(contextualMemberSymbols, m => !lookUp(existingMemberNames, m.name));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters out completion suggestions from 'symbols' according to existing JSX attributes.
|
||||
*
|
||||
@@ -3710,7 +3759,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry {
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||
// not be accessed with a dot (a.1 <- invalid)
|
||||
let displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks:*/ true, location);
|
||||
@@ -3718,10 +3767,10 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO(drosen): Right now we just permit *all* semantic meanings when calling
|
||||
// 'getSymbolKind' which is permissible given that it is backwards compatible; but
|
||||
// TODO(drosen): Right now we just permit *all* semantic meanings when calling
|
||||
// 'getSymbolKind' which is permissible given that it is backwards compatible; but
|
||||
// really we should consider passing the meaning for the node so that we don't report
|
||||
// that a suggestion for a value is an interface. We COULD also just do what
|
||||
// that a suggestion for a value is an interface. We COULD also just do what
|
||||
// 'getSymbolModifiers' does, which is to use the first declaration.
|
||||
|
||||
// Use a 'sortText' of 0' so that all symbol completion entries come before any other
|
||||
@@ -3767,8 +3816,8 @@ namespace ts {
|
||||
|
||||
// Find the symbol with the matching entry name.
|
||||
let target = program.getCompilerOptions().target;
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// We don't need to perform character checks here because we're only comparing the
|
||||
// name against 'entryName' (which is known to be good), not building a new
|
||||
// completion entry.
|
||||
let symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, target, /*performCharacterChecks:*/ false, location) === entryName ? s : undefined);
|
||||
|
||||
@@ -3783,7 +3832,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Didn't find a symbol with this name. See if we can find a keyword instead.
|
||||
let keywordCompletion = forEach(keywordCompletions, c => c.name === entryName);
|
||||
if (keywordCompletion) {
|
||||
@@ -3859,7 +3908,7 @@ namespace ts {
|
||||
Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method));
|
||||
});
|
||||
if (!unionPropertyKind) {
|
||||
// If this was union of all methods,
|
||||
// If this was union of all methods,
|
||||
//make sure it has call signatures before we can label it as method
|
||||
let typeOfUnionProperty = typeChecker.getTypeOfSymbolAtLocation(symbol, location);
|
||||
if (typeOfUnionProperty.getCallSignatures().length) {
|
||||
@@ -3933,7 +3982,7 @@ namespace ts {
|
||||
let allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
|
||||
if (!contains(allSignatures, signature.target || signature)) {
|
||||
// Get the first signature if there
|
||||
// Get the first signature if there
|
||||
signature = allSignatures.length ? allSignatures[0] : undefined;
|
||||
}
|
||||
|
||||
@@ -4309,7 +4358,7 @@ namespace ts {
|
||||
|
||||
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
|
||||
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
|
||||
// Just add all the declarations.
|
||||
// Just add all the declarations.
|
||||
forEach(declarations, declaration => {
|
||||
result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName));
|
||||
});
|
||||
@@ -4420,7 +4469,7 @@ namespace ts {
|
||||
|
||||
// Because name in short-hand property assignment has two different meanings: property name and property value,
|
||||
// using go-to-definition at such position should go to the variable declaration of the property value rather than
|
||||
// go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
|
||||
// go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
|
||||
// is performed at the location of property access, we would like to go to definition of the property in the short-hand
|
||||
// assignment. This case and others are handled by the following code.
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
@@ -4486,7 +4535,7 @@ namespace ts {
|
||||
if (results) {
|
||||
let sourceFile = getCanonicalFileName(normalizeSlashes(fileName));
|
||||
|
||||
// Get occurrences only supports reporting occurrences for the file queried. So
|
||||
// Get occurrences only supports reporting occurrences for the file queried. So
|
||||
// filter down to that list.
|
||||
results = filter(results, r => getCanonicalFileName(ts.normalizeSlashes(r.fileName)) === sourceFile);
|
||||
}
|
||||
@@ -4713,7 +4762,7 @@ namespace ts {
|
||||
if (isFunctionBlock(parent) || parent.kind === SyntaxKind.SourceFile) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
// A throw-statement is only owned by a try-statement if the try-statement has
|
||||
// a catch clause, and if the throw-statement occurs within the try block.
|
||||
if (parent.kind === SyntaxKind.TryStatement) {
|
||||
@@ -4807,7 +4856,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else {
|
||||
// unsupported modifier
|
||||
return undefined;
|
||||
}
|
||||
@@ -5403,13 +5452,13 @@ namespace ts {
|
||||
// If we are past the end, stop looking
|
||||
if (position > end) break;
|
||||
|
||||
// We found a match. Make sure it's not part of a larger word (i.e. the char
|
||||
// We found a match. Make sure it's not part of a larger word (i.e. the char
|
||||
// before and after it have to be a non-identifier char).
|
||||
let endPosition = position + symbolNameLength;
|
||||
|
||||
if ((position === 0 || !isIdentifierPart(text.charCodeAt(position - 1), ScriptTarget.Latest)) &&
|
||||
(endPosition === sourceLength || !isIdentifierPart(text.charCodeAt(endPosition), ScriptTarget.Latest))) {
|
||||
// Found a real match. Keep searching.
|
||||
// Found a real match. Keep searching.
|
||||
positions.push(position);
|
||||
}
|
||||
position = text.indexOf(symbolName, position + symbolNameLength + 1);
|
||||
@@ -5476,9 +5525,9 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Search within node "container" for references for a search value, where the search value is defined as a
|
||||
/** Search within node "container" for references for a search value, where the search value is defined as a
|
||||
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
|
||||
* searchLocation: a node where the search value
|
||||
* searchLocation: a node where the search value
|
||||
*/
|
||||
function getReferencesInNode(container: Node,
|
||||
searchSymbol: Symbol,
|
||||
@@ -5504,7 +5553,7 @@ namespace ts {
|
||||
|
||||
let referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (!isValidReferencePosition(referenceLocation, searchText)) {
|
||||
// This wasn't the start of a token. Check to see if it might be a
|
||||
// This wasn't the start of a token. Check to see if it might be a
|
||||
// match in a comment or string if that's what the caller is asking
|
||||
// for.
|
||||
if ((findInStrings && isInString(sourceFile, position)) ||
|
||||
@@ -5829,7 +5878,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// If the reference location is in an object literal, try to get the contextual type for the
|
||||
// If the reference location is in an object literal, try to get the contextual type for the
|
||||
// object literal, lookup the property symbol in the contextual type, and use this symbol to
|
||||
// compare to our searchSymbol
|
||||
if (isNameOfPropertyAssignment(referenceLocation)) {
|
||||
@@ -5846,7 +5895,7 @@ namespace ts {
|
||||
return rootSymbol;
|
||||
}
|
||||
|
||||
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
|
||||
// Finally, try all properties with the same name in any type the containing type extended or implemented, and
|
||||
// see if any is in the list
|
||||
if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
||||
let result: Symbol[] = [];
|
||||
@@ -6197,7 +6246,7 @@ namespace ts {
|
||||
}
|
||||
else if (isNameOfModuleDeclaration(nodeForStartPos)) {
|
||||
// If this is name of a module declarations, check if this is right side of dotted module name
|
||||
// If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of
|
||||
// If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of
|
||||
// Then this name is name from dotted module
|
||||
if (nodeForStartPos.parent.parent.kind === SyntaxKind.ModuleDeclaration &&
|
||||
(<ModuleDeclaration>nodeForStartPos.parent.parent).body === nodeForStartPos.parent) {
|
||||
@@ -6240,7 +6289,7 @@ namespace ts {
|
||||
// been canceled. That would be an enormous amount of chattyness, along with the all
|
||||
// the overhead of marshalling the data to/from the host. So instead we pick a few
|
||||
// reasonable node kinds to bother checking on. These node kinds represent high level
|
||||
// constructs that we would expect to see commonly, but just at a far less frequent
|
||||
// constructs that we would expect to see commonly, but just at a far less frequent
|
||||
// interval.
|
||||
//
|
||||
// For example, in checker.ts (around 750k) we only have around 600 of these constructs.
|
||||
@@ -6313,7 +6362,7 @@ namespace ts {
|
||||
*/
|
||||
function hasValueSideModule(symbol: Symbol): boolean {
|
||||
return forEach(symbol.declarations, declaration => {
|
||||
return declaration.kind === SyntaxKind.ModuleDeclaration &&
|
||||
return declaration.kind === SyntaxKind.ModuleDeclaration &&
|
||||
getModuleInstanceState(declaration) === ModuleInstanceState.Instantiated;
|
||||
});
|
||||
}
|
||||
@@ -6434,8 +6483,8 @@ namespace ts {
|
||||
// Only bother with the trivia if it at least intersects the span of interest.
|
||||
if (isComment(kind)) {
|
||||
classifyComment(token, kind, start, width);
|
||||
|
||||
// Classifying a comment might cause us to reuse the trivia scanner
|
||||
|
||||
// Classifying a comment might cause us to reuse the trivia scanner
|
||||
// (because of jsdoc comments). So after we classify the comment make
|
||||
// sure we set the scanner position back to where it needs to be.
|
||||
triviaScanner.setTextPos(end);
|
||||
@@ -6486,7 +6535,7 @@ namespace ts {
|
||||
|
||||
for (let tag of docComment.tags) {
|
||||
// As we walk through each tag, classify the portion of text from the end of
|
||||
// the last tag (or the start of the entire doc comment) as 'comment'.
|
||||
// the last tag (or the start of the entire doc comment) as 'comment'.
|
||||
if (tag.pos !== pos) {
|
||||
pushCommentRange(pos, tag.pos - pos);
|
||||
}
|
||||
@@ -6548,7 +6597,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function classifyDisabledMergeCode(text: string, start: number, end: number) {
|
||||
// Classify the line that the ======= marker is on as a comment. Then just lex
|
||||
// Classify the line that the ======= marker is on as a comment. Then just lex
|
||||
// all further tokens and add them to the result.
|
||||
for (var i = start; i < end; i++) {
|
||||
if (isLineBreak(text.charCodeAt(i))) {
|
||||
@@ -6591,7 +6640,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// for accurate classification, the actual token should be passed in. however, for
|
||||
// for accurate classification, the actual token should be passed in. however, for
|
||||
// cases like 'disabled merge code' classification, we just get the token kind and
|
||||
// classify based on that instead.
|
||||
function classifyTokenType(tokenKind: SyntaxKind, token?: Node): ClassificationType {
|
||||
@@ -6878,11 +6927,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] {
|
||||
// Note: while getting todo comments seems like a syntactic operation, we actually
|
||||
// Note: while getting todo comments seems like a syntactic operation, we actually
|
||||
// treat it as a semantic operation here. This is because we expect our host to call
|
||||
// this on every single file. If we treat this syntactically, then that will cause
|
||||
// us to populate and throw away the tree in our syntax tree cache for each file. By
|
||||
// treating this as a semantic operation, we can access any tree without throwing
|
||||
// treating this as a semantic operation, we can access any tree without throwing
|
||||
// anything away.
|
||||
synchronizeHostData();
|
||||
|
||||
@@ -6912,7 +6961,7 @@ namespace ts {
|
||||
// 0) The full match for the entire regexp.
|
||||
// 1) The preamble to the message portion.
|
||||
// 2) The message portion.
|
||||
// 3...N) The descriptor that was matched - by index. 'undefined' for each
|
||||
// 3...N) The descriptor that was matched - by index. 'undefined' for each
|
||||
// descriptor that didn't match. an actual value if it did match.
|
||||
//
|
||||
// i.e. 'undefined' in position 3 above means TODO(jason) didn't match.
|
||||
@@ -6938,7 +6987,7 @@ namespace ts {
|
||||
}
|
||||
Debug.assert(descriptor !== undefined);
|
||||
|
||||
// We don't want to match something like 'TODOBY', so we make sure a non
|
||||
// We don't want to match something like 'TODOBY', so we make sure a non
|
||||
// letter/digit follows the match.
|
||||
if (isLetterOrDigit(fileContents.charCodeAt(matchPosition + descriptor.text.length))) {
|
||||
continue;
|
||||
@@ -6990,11 +7039,11 @@ namespace ts {
|
||||
// (?:(TODO\(jason\))|(HACK))
|
||||
//
|
||||
// Note that the outermost group is *not* a capture group, but the innermost groups
|
||||
// *are* capture groups. By capturing the inner literals we can determine after
|
||||
// *are* capture groups. By capturing the inner literals we can determine after
|
||||
// matching which descriptor we are dealing with.
|
||||
let literals = "(?:" + map(descriptors, d => "(" + escapeRegExp(d.text) + ")").join("|") + ")";
|
||||
|
||||
// After matching a descriptor literal, the following regexp matches the rest of the
|
||||
// After matching a descriptor literal, the following regexp matches the rest of the
|
||||
// text up to the end of the line (or */).
|
||||
let endOfLineOrEndOfComment = /(?:$|\*\/)/.source
|
||||
let messageRemainder = /(?:.*?)/.source
|
||||
@@ -7177,7 +7226,7 @@ namespace ts {
|
||||
|
||||
/// We do not have a full parser support to know when we should parse a regex or not
|
||||
/// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where
|
||||
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
|
||||
/// we have a series of divide operator. this list allows us to be more accurate by ruling out
|
||||
/// locations where a regexp cannot exist.
|
||||
let noRegexTable: boolean[] = [];
|
||||
noRegexTable[SyntaxKind.Identifier] = true;
|
||||
@@ -7223,7 +7272,7 @@ namespace ts {
|
||||
keyword2 === SyntaxKind.ConstructorKeyword ||
|
||||
keyword2 === SyntaxKind.StaticKeyword) {
|
||||
|
||||
// Allow things like "public get", "public constructor" and "public static".
|
||||
// Allow things like "public get", "public constructor" and "public static".
|
||||
// These are all legal.
|
||||
return true;
|
||||
}
|
||||
@@ -7294,7 +7343,7 @@ namespace ts {
|
||||
function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult {
|
||||
return convertClassifications(getEncodedLexicalClassifications(text, lexState, syntacticClassifierAbsent), text);
|
||||
}
|
||||
|
||||
|
||||
// If there is a syntactic classifier ('syntacticClassifierAbsent' is false),
|
||||
// we will be more conservative in order to avoid conflicting with the syntactic classifier.
|
||||
function getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications {
|
||||
@@ -7356,12 +7405,12 @@ namespace ts {
|
||||
// token. So the classification will go back to being an identifier. The moment the user
|
||||
// types again, number will become a keyword, then an identifier, etc. etc.
|
||||
//
|
||||
// To try to avoid this problem, we avoid classifying contextual keywords as keywords
|
||||
// To try to avoid this problem, we avoid classifying contextual keywords as keywords
|
||||
// when the user is potentially typing something generic. We just can't do a good enough
|
||||
// job at the lexical level, and so well leave it up to the syntactic classifier to make
|
||||
// the determination.
|
||||
//
|
||||
// In order to determine if the user is potentially typing something generic, we use a
|
||||
// In order to determine if the user is potentially typing something generic, we use a
|
||||
// weak heuristic where we track < and > tokens. It's a weak heuristic, but should
|
||||
// work well enough in practice.
|
||||
let angleBracketStack = 0;
|
||||
@@ -7379,7 +7428,7 @@ namespace ts {
|
||||
token = SyntaxKind.Identifier;
|
||||
}
|
||||
else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) {
|
||||
// We have two keywords in a row. Only treat the second as a keyword if
|
||||
// We have two keywords in a row. Only treat the second as a keyword if
|
||||
// it's a sequence that could legally occur in the language. Otherwise
|
||||
// treat it as an identifier. This way, if someone writes "private var"
|
||||
// we recognize that 'var' is actually an identifier here.
|
||||
@@ -7387,7 +7436,7 @@ namespace ts {
|
||||
}
|
||||
else if (lastNonTriviaToken === SyntaxKind.Identifier &&
|
||||
token === SyntaxKind.LessThanToken) {
|
||||
// Could be the start of something generic. Keep track of that by bumping
|
||||
// Could be the start of something generic. Keep track of that by bumping
|
||||
// up the current count of generic contexts we may be in.
|
||||
angleBracketStack++;
|
||||
}
|
||||
@@ -7402,7 +7451,7 @@ namespace ts {
|
||||
token === SyntaxKind.BooleanKeyword ||
|
||||
token === SyntaxKind.SymbolKeyword) {
|
||||
if (angleBracketStack > 0 && !syntacticClassifierAbsent) {
|
||||
// If it looks like we're could be in something generic, don't classify this
|
||||
// If it looks like we're could be in something generic, don't classify this
|
||||
// as a keyword. We may just get overwritten by the syntactic classifier,
|
||||
// causing a noisy experience for the user.
|
||||
token = SyntaxKind.Identifier;
|
||||
@@ -7510,8 +7559,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (start === 0 && offset > 0) {
|
||||
// We're classifying the first token, and this was a case where we prepended
|
||||
// text. We should consider the start of this token to be at the start of
|
||||
// We're classifying the first token, and this was a case where we prepended
|
||||
// text. We should consider the start of this token to be at the start of
|
||||
// the original text.
|
||||
start += offset;
|
||||
}
|
||||
@@ -7634,11 +7683,11 @@ namespace ts {
|
||||
|
||||
/// getDefaultLibraryFilePath
|
||||
declare let __dirname: string;
|
||||
|
||||
|
||||
/**
|
||||
* Get the path of the default library files (lib.d.ts) as distributed with the typescript
|
||||
* node package.
|
||||
* The functionality is not supported if the ts module is consumed outside of a node module.
|
||||
* The functionality is not supported if the ts module is consumed outside of a node module.
|
||||
*/
|
||||
export function getDefaultLibFilePath(options: CompilerOptions): string {
|
||||
// Check __dirname is defined and that we are on a node.js system.
|
||||
|
||||
+30
-2
@@ -60,10 +60,12 @@ namespace ts {
|
||||
getNewLine?(): string;
|
||||
getProjectVersion?(): string;
|
||||
useCaseSensitiveFileNames?(): boolean;
|
||||
|
||||
getModuleResolutionsForFile?(fileName: string): string;
|
||||
}
|
||||
|
||||
/** Public interface of the the of a config service shim instance.*/
|
||||
export interface CoreServicesShimHost extends Logger {
|
||||
export interface CoreServicesShimHost extends Logger, ModuleResolutionHost {
|
||||
/**
|
||||
* Returns a JSON-encoded value of the type: string[]
|
||||
*
|
||||
@@ -270,8 +272,18 @@ namespace ts {
|
||||
private files: string[];
|
||||
private loggingEnabled = false;
|
||||
private tracingEnabled = false;
|
||||
|
||||
|
||||
public resolveModuleNames: (moduleName: string[], containingFile: string) => string[];
|
||||
|
||||
constructor(private shimHost: LanguageServiceShimHost) {
|
||||
// if shimHost is a COM object then property check will become method call with no arguments.
|
||||
// 'in' does not have this effect.
|
||||
if ("getModuleResolutionsForFile" in this.shimHost) {
|
||||
this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
|
||||
let resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
|
||||
return map(moduleNames, name => lookUp(resolutionsInFile, name));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public log(s: string): void {
|
||||
@@ -410,6 +422,14 @@ namespace ts {
|
||||
}
|
||||
return JSON.parse(encoded);
|
||||
}
|
||||
|
||||
public fileExists(fileName: string): boolean {
|
||||
return this.shimHost.fileExists(fileName);
|
||||
}
|
||||
|
||||
public readFile(fileName: string): string {
|
||||
return this.shimHost.readFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any {
|
||||
@@ -918,6 +938,13 @@ namespace ts {
|
||||
private forwardJSONCall(actionDescription: string, action: () => any): any {
|
||||
return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
|
||||
}
|
||||
|
||||
public resolveModuleName(fileName: string, moduleName: string, compilerOptionsJson: string): string {
|
||||
return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
|
||||
let compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
|
||||
return resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
|
||||
});
|
||||
}
|
||||
|
||||
public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
|
||||
return this.forwardJSONCall(
|
||||
@@ -927,6 +954,7 @@ namespace ts {
|
||||
var convertResult = {
|
||||
referencedFiles: <IFileReference[]>[],
|
||||
importedFiles: <IFileReference[]>[],
|
||||
ambientExternalModules: result.ambientExternalModules,
|
||||
isLibFile: result.isLibFile
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
EmitSkipped: false
|
||||
FileName : tests/cases/fourslash/shims-pp/inputFile1.js
|
||||
var x = 5;
|
||||
var Bar = (function () {
|
||||
function Bar() {
|
||||
}
|
||||
return Bar;
|
||||
})();
|
||||
FileName : tests/cases/fourslash/shims-pp/inputFile1.d.ts
|
||||
declare var x: number;
|
||||
declare class Bar {
|
||||
x: string;
|
||||
y: number;
|
||||
}
|
||||
|
||||
EmitSkipped: false
|
||||
FileName : tests/cases/fourslash/shims-pp/inputFile2.js
|
||||
var x1 = "hello world";
|
||||
var Foo = (function () {
|
||||
function Foo() {
|
||||
}
|
||||
return Foo;
|
||||
})();
|
||||
FileName : tests/cases/fourslash/shims-pp/inputFile2.d.ts
|
||||
declare var x1: string;
|
||||
declare class Foo {
|
||||
x: string;
|
||||
y: number;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
error TS6053: File 'a.ts' not found.
|
||||
error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.tsx', '.ts', '.d.ts'.
|
||||
error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
|
||||
|
||||
|
||||
!!! error TS6053: File 'a.ts' not found.
|
||||
!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.tsx', '.ts', '.d.ts'.
|
||||
!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
|
||||
@@ -1,6 +1,6 @@
|
||||
error TS6053: File 'a.ts' not found.
|
||||
error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.tsx', '.ts', '.d.ts'.
|
||||
error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
|
||||
|
||||
|
||||
!!! error TS6053: File 'a.ts' not found.
|
||||
!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.tsx', '.ts', '.d.ts'.
|
||||
!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
|
||||
@@ -0,0 +1,43 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//////curly braces
|
||||
////module Foo [|{
|
||||
//// class Bar [|{
|
||||
//// private f() [|{
|
||||
//// }|]
|
||||
////
|
||||
//// private f2() [|{
|
||||
//// if (true) [|{ }|] [|{ }|];
|
||||
//// }|]
|
||||
//// }|]
|
||||
////}|]
|
||||
////
|
||||
//////parenthesis
|
||||
////class FooBar {
|
||||
//// private f[|()|] {
|
||||
//// return [|([|(1 + 1)|])|];
|
||||
//// }
|
||||
////
|
||||
//// private f2[|()|] {
|
||||
//// if [|(true)|] { }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
//////square brackets
|
||||
////class Baz {
|
||||
//// private f() {
|
||||
//// var a: any[|[]|] = [|[[|[1, 2]|], [|[3, 4]|], 5]|];
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////// angular brackets
|
||||
////class TemplateTest [|<T1, T2 extends Array>|] {
|
||||
//// public foo(a, b) {
|
||||
//// return [|<any>|] a;
|
||||
//// }
|
||||
////}
|
||||
|
||||
test.ranges().forEach((range) => {
|
||||
verify.matchingBracePositionInCurrentFile(range.start, range.end - 1);
|
||||
verify.matchingBracePositionInCurrentFile(range.end - 1, range.start);
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @BaselineFile: getBreakpointStatementAtPosition.baseline
|
||||
// @Filename: getBreakpointStatementAtPosition.ts
|
||||
////while (true) {
|
||||
//// break;
|
||||
////}
|
||||
////y: while (true) {
|
||||
//// break y;
|
||||
////}
|
||||
////while (true) {
|
||||
//// continue;
|
||||
////}
|
||||
////z: while (true) {
|
||||
//// continue z;
|
||||
////}
|
||||
verify.baselineCurrentFileBreakpointLocations();
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////function foo(strOrNum: string | number) {
|
||||
//// /*1*/
|
||||
//// if (typeof strOrNum === "number") {
|
||||
//// /*2*/
|
||||
//// }
|
||||
//// else {
|
||||
//// /*3*/
|
||||
//// }
|
||||
////}
|
||||
|
||||
goTo.marker('1');
|
||||
verify.completionListContains("strOrNum", "(parameter) strOrNum: string | number");
|
||||
|
||||
goTo.marker('2');
|
||||
verify.completionListContains("strOrNum", "(parameter) strOrNum: number");
|
||||
|
||||
goTo.marker('3');
|
||||
verify.completionListContains("strOrNum", "(parameter) strOrNum: string");
|
||||
@@ -0,0 +1,29 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: goToDefinitionDifferentFile_Definition.ts
|
||||
////var /*remoteVariableDefinition*/remoteVariable;
|
||||
/////*remoteFunctionDefinition*/function remoteFunction() { }
|
||||
/////*remoteClassDefinition*/class remoteClass { }
|
||||
/////*remoteInterfaceDefinition*/interface remoteInterface{ }
|
||||
/////*remoteModuleDefinition*/module remoteModule{ export var foo = 1;}
|
||||
|
||||
// @Filename: goToDefinitionDifferentFile_Consumption.ts
|
||||
/////*remoteVariableReference*/remoteVariable = 1;
|
||||
/////*remoteFunctionReference*/remoteFunction();
|
||||
////var foo = new /*remoteClassReference*/remoteClass();
|
||||
////class fooCls implements /*remoteInterfaceReference*/remoteInterface { }
|
||||
////var fooVar = /*remoteModuleReference*/remoteModule.foo;
|
||||
|
||||
var markerList = [
|
||||
"remoteVariable",
|
||||
"remoteFunction",
|
||||
"remoteClass",
|
||||
"remoteInterface",
|
||||
"remoteModule",
|
||||
];
|
||||
|
||||
markerList.forEach((marker) => {
|
||||
goTo.marker(marker + 'Reference');
|
||||
goTo.definition();
|
||||
verify.caretAtMarker(marker + 'Definition');
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @BaselineFile: getEmitOutput-pp.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();
|
||||
@@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////class Bar {
|
||||
//// {| "indentation": 4|}
|
||||
//// private foo: string = "";
|
||||
//// {| "indentation": 4|}
|
||||
//// private f() {
|
||||
//// var a: any[] = [[1, 2], [3, 4], 5];
|
||||
//// {| "indentation": 8|}
|
||||
//// return ((1 + 1));
|
||||
//// }
|
||||
//// {| "indentation": 4|}
|
||||
//// private f2() {
|
||||
//// if (true) { } { };
|
||||
//// }
|
||||
////}
|
||||
////{| "indentation": 0|}
|
||||
|
||||
test.markers().forEach((marker) => {
|
||||
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indentation);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
/////// Module
|
||||
////{| "itemName": "Shapes", "kind": "module", "parentName": "" |}module Shapes {
|
||||
////
|
||||
//// // Class
|
||||
//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point {
|
||||
//// // Instance member
|
||||
//// {| "itemName": "origin", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private origin = 0.0;
|
||||
////
|
||||
//// {| "itemName": "distFromZero", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private distFromZero = 0.0;
|
||||
////
|
||||
//// // Getter
|
||||
//// {| "itemName": "distance", "kind": "getter", "parentName": "Point", "matchKind": "exact" |}get distance(): number { return 0; }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////// Local variables
|
||||
////{| "itemName": "point", "kind": "var", "parentName": "", "matchKind": "exact"|}var point = new Shapes.Point();
|
||||
|
||||
//// Testing for exact matching of navigationItems
|
||||
|
||||
test.markers().forEach((marker) => {
|
||||
if (marker.data) {
|
||||
verify.navigationItemsListContains(marker.data.itemName, marker.data.kind, marker.data.itemName, marker.data.matchKind, marker.fileName, marker.data.parentName);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
//// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0;
|
||||
|
||||
test.markers().forEach(marker => {
|
||||
verify.getScriptLexicalStructureListContains(
|
||||
marker.data.itemName,
|
||||
marker.data.kind,
|
||||
marker.fileName,
|
||||
marker.data.parentName,
|
||||
marker.data.isAdditionalRange,
|
||||
marker.position);
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
/////*0*/
|
||||
////interface A {
|
||||
//// foo: string;
|
||||
////}
|
||||
////function foo(x: A) {
|
||||
//// x.f/*1*/oo
|
||||
////}
|
||||
|
||||
goTo.marker("1");
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
|
||||
goTo.marker("0");
|
||||
edit.insert("\r\n");
|
||||
|
||||
goTo.marker("1");
|
||||
verify.occurrencesAtPositionCount(2);
|
||||
@@ -0,0 +1,113 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
////// interface
|
||||
////interface IFoo[| {
|
||||
//// getDist(): number;
|
||||
////}|]
|
||||
////
|
||||
////// class members
|
||||
////class Foo[| {
|
||||
//// constructor()[| {
|
||||
//// }|]
|
||||
////
|
||||
//// public foo(): number[| {
|
||||
//// return 0;
|
||||
//// }|]
|
||||
////
|
||||
//// public get X()[| {
|
||||
//// return 1;
|
||||
//// }|]
|
||||
////
|
||||
//// public set X(v: number)[| {
|
||||
//// }|]
|
||||
////
|
||||
//// public member = function f()[| {
|
||||
////
|
||||
//// }|]
|
||||
////}|]
|
||||
////switch(1)[| {
|
||||
//// case 1: break;
|
||||
////}|]
|
||||
////
|
||||
////var array =[| [
|
||||
//// 1,
|
||||
//// 2
|
||||
////]|]
|
||||
////
|
||||
////// modules
|
||||
////module m1[| {
|
||||
//// module m2[| { }|]
|
||||
//// module m3[| {
|
||||
//// function foo()[| {
|
||||
////
|
||||
//// }|]
|
||||
////
|
||||
//// interface IFoo2[| {
|
||||
////
|
||||
//// }|]
|
||||
////
|
||||
//// class foo2 implements IFoo2[| {
|
||||
////
|
||||
//// }|]
|
||||
//// }|]
|
||||
////}|]
|
||||
////
|
||||
////// function declaration
|
||||
////function foo(): number[| {
|
||||
//// return 0;
|
||||
////}|]
|
||||
////
|
||||
////// function expressions
|
||||
////(function f()[| {
|
||||
////
|
||||
////}|])
|
||||
////
|
||||
////// trivia handeling
|
||||
////class ClassFooWithTrivia[| /* some comments */
|
||||
//// /* more trivia */ {
|
||||
////
|
||||
////
|
||||
//// /*some trailing trivia */
|
||||
////}|] /* even more */
|
||||
////
|
||||
////// object literals
|
||||
////var x =[|{
|
||||
//// a:1,
|
||||
//// b:2,
|
||||
//// get foo()[| {
|
||||
//// return 1;
|
||||
//// }|]
|
||||
////}|]
|
||||
//////outline with deep nesting
|
||||
////module m1[|{
|
||||
//// module m2[| {
|
||||
//// module m3[| {
|
||||
//// module m4[| {
|
||||
//// module m5[| {
|
||||
//// module m6[| {
|
||||
//// module m7[| {
|
||||
//// module m8[| {
|
||||
//// module m9[| {
|
||||
//// module m10[| {
|
||||
//// module m11 {
|
||||
//// module m12 {
|
||||
//// export interface IFoo {
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
//// }|]
|
||||
////}|]
|
||||
////
|
||||
//////outline after a deeply nested node
|
||||
////class AfterNestedNodes[| {
|
||||
////}|]
|
||||
|
||||
verify.outliningSpansInCurrentFile(test.ranges());
|
||||
@@ -0,0 +1,30 @@
|
||||
/// <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");
|
||||
//// import invalidRef2 = /*9*/requi/*10*/(/*10A*/"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("10", "10A");
|
||||
verify.errorExistsBetweenMarkers("11", "12");
|
||||
@@ -0,0 +1,16 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////class SS<T>{}
|
||||
////
|
||||
////var x/*1*/1 = new SS<number>();
|
||||
////var x/*2*/2 = new SS();
|
||||
////var x/*3*/3 = new SS;
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs('var x1: SS<number>');
|
||||
|
||||
goTo.marker('2');
|
||||
verify.quickInfoIs('var x2: SS<{}>');
|
||||
|
||||
goTo.marker('3');
|
||||
verify.quickInfoIs('var x3: SS<{}>');
|
||||
@@ -0,0 +1,29 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
//@Filename: findAllRefsOnDefinition-import.ts
|
||||
////export class Test{
|
||||
////
|
||||
//// constructor(){
|
||||
////
|
||||
//// }
|
||||
////
|
||||
//// public /*1*/start(){
|
||||
//// return this;
|
||||
//// }
|
||||
////
|
||||
//// public stop(){
|
||||
//// return this;
|
||||
//// }
|
||||
////}
|
||||
|
||||
//@Filename: findAllRefsOnDefinition.ts
|
||||
////import Second = require("findAllRefsOnDefinition-import");
|
||||
////
|
||||
////var second = new Second.Test()
|
||||
////second.start();
|
||||
////second.stop();
|
||||
|
||||
goTo.file("findAllRefsOnDefinition-import.ts");
|
||||
goTo.marker("1");
|
||||
|
||||
verify.referencesCountIs(2);
|
||||
@@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
///////<reference path="./Bar.ts" />
|
||||
|
||||
////function /**/[|Bar|]() {
|
||||
//// // This is a reference to Bar in a comment.
|
||||
//// "this is a reference to Bar in a string"
|
||||
////}
|
||||
|
||||
goTo.marker();
|
||||
verify.renameLocations(/*findInStrings:*/ false, /*findInComments:*/ false);
|
||||
@@ -0,0 +1,15 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// module /*0*/M {
|
||||
//// export interface /*1*/I {
|
||||
//// }
|
||||
//// }
|
||||
//// interface /*2*/X extends /*3*/M./*4*/I { }
|
||||
|
||||
var c = classification;
|
||||
verify.semanticClassificationsAre(
|
||||
c.moduleName("M", test.marker("0").position),
|
||||
c.interfaceName("I", test.marker("1").position),
|
||||
c.interfaceName("X", test.marker("2").position),
|
||||
c.moduleName("M", test.marker("3").position),
|
||||
c.interfaceName("I", test.marker("4").position));
|
||||
@@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @module: CommonJS
|
||||
// @declaration: true
|
||||
//// interface privateInterface {}
|
||||
//// export class Bar implements /*1*/privateInterface/*2*/{ }
|
||||
|
||||
verify.errorExistsBetweenMarkers("1", "2");
|
||||
verify.numberOfErrorsInCurrentFile(1);
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file0.ts
|
||||
////declare function fn(x: string, y: number);
|
||||
|
||||
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file1.ts
|
||||
////declare function fn(x: string);
|
||||
|
||||
// @Filename: signatureHelpInFunctionCallOnFunctionDeclarationInMultipleFiles_file2.ts
|
||||
////fn(/*1*/
|
||||
|
||||
goTo.marker('1');
|
||||
verify.signatureHelpCountIs(2);
|
||||
@@ -0,0 +1,35 @@
|
||||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//// // comment
|
||||
//// module M {
|
||||
//// var v = 0 + 1;
|
||||
//// var s = "string";
|
||||
////
|
||||
//// class C<T> {
|
||||
//// }
|
||||
////
|
||||
//// enum E {
|
||||
//// }
|
||||
////
|
||||
//// interface I {
|
||||
//// }
|
||||
////
|
||||
//// module M1.M2 {
|
||||
//// }
|
||||
//// }
|
||||
|
||||
var c = classification;
|
||||
verify.syntacticClassificationsAre(
|
||||
c.comment("// comment"),
|
||||
c.keyword("module"), c.moduleName("M"), c.punctuation("{"),
|
||||
c.keyword("var"), c.identifier("v"), c.operator("="), c.numericLiteral("0"), c.operator("+"), c.numericLiteral("1"), c.punctuation(";"),
|
||||
c.keyword("var"), c.identifier("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("}"));
|
||||
@@ -0,0 +1,3 @@
|
||||
//// // [|TODO|]
|
||||
|
||||
verify.todoCommentsInCurrentFile(["TODO"]);
|
||||
@@ -0,0 +1,14 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: goToTypeDefinition_Definition.ts
|
||||
/////*definition*/class C {
|
||||
//// p;
|
||||
////}
|
||||
////var c: C;
|
||||
|
||||
// @Filename: goToTypeDefinition_Consumption.ts
|
||||
/////*reference*/c = undefined;
|
||||
|
||||
goTo.marker('reference');
|
||||
goTo.type();
|
||||
verify.caretAtMarker('definition');
|
||||
@@ -0,0 +1,86 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////var /*1*/a = 10;
|
||||
////function foo() {
|
||||
//// var /*2*/b = /*3*/a;
|
||||
////}
|
||||
////module m {
|
||||
//// var /*4*/c = 10;
|
||||
//// export var /*5*/d = 10;
|
||||
////}
|
||||
////var /*6*/f: () => number;
|
||||
////var /*7*/g = /*8*/f;
|
||||
/////*9*/f();
|
||||
////var /*10*/h: { (a: string): number; (a: number): string; };
|
||||
////var /*11*/i = /*12*/h;
|
||||
/////*13*/h(10);
|
||||
/////*14*/h("hello");
|
||||
|
||||
var marker = 0;
|
||||
function verifyVar(name: string, typeDisplay: ts.SymbolDisplayPart[], optionalNameDisplay?: ts.SymbolDisplayPart[], optionalKindModifiers?: string) {
|
||||
marker++;
|
||||
goTo.marker(marker.toString());
|
||||
var kind = "var";
|
||||
verify.verifyQuickInfoDisplayParts(kind, optionalKindModifiers || "", { start: test.markerByName(marker.toString()).position, length: name.length },
|
||||
[{ text: kind, kind: "keyword" },
|
||||
{ text: " ", kind: "space" }].concat(optionalNameDisplay || [{ text: name, kind: "localName" }]).concat(
|
||||
{ text: ":", kind: "punctuation" }, { text: " ", kind: "space" }).concat(typeDisplay),
|
||||
[]);
|
||||
}
|
||||
function verifyLocalVar(name: string, typeDisplay: ts.SymbolDisplayPart[], optionalNameDisplay?: ts.SymbolDisplayPart[], optionalKindModifiers?: string) {
|
||||
marker++;
|
||||
goTo.marker(marker.toString());
|
||||
var kind = "local 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", numberTypeDisplay);
|
||||
verifyLocalVar("b", numberTypeDisplay);
|
||||
verifyVar("a", numberTypeDisplay);
|
||||
verifyVar("c", numberTypeDisplay);
|
||||
verifyVar("d", 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", functionTypeReturningNumber);
|
||||
verifyVar("g", functionTypeReturningNumber);
|
||||
verifyVar("f", functionTypeReturningNumber);
|
||||
verifyVar("f", 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", typeLiteralWithOverloadCall);
|
||||
verifyVar("i", typeLiteralWithOverloadCall);
|
||||
verifyVar("h", 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", getFunctionType("number", "string", /*isArrow*/true).concat(overloadDisplay));
|
||||
verifyVar("h", getFunctionType("string", "number", /*isArrow*/true).concat(overloadDisplay));
|
||||
@@ -0,0 +1,206 @@
|
||||
/// <reference path="..\..\..\src\harness\harness.ts" />
|
||||
|
||||
module ts {
|
||||
interface File {
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
function createDefaultServerHost(fileMap: Map<File>): server.ServerHost {
|
||||
return {
|
||||
args: <string[]>[],
|
||||
newLine: "\r\n",
|
||||
useCaseSensitiveFileNames: false,
|
||||
write: (s: string) => {
|
||||
},
|
||||
readFile: (path: string, encoding?: string): string => {
|
||||
return hasProperty(fileMap, path) && fileMap[path].content;
|
||||
},
|
||||
writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
resolvePath: (path: string): string => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
fileExists: (path: string): boolean => {
|
||||
return hasProperty(fileMap, path);
|
||||
},
|
||||
directoryExists: (path: string): boolean => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
createDirectory: (path: string) => {
|
||||
},
|
||||
getExecutingFilePath: (): string => {
|
||||
return "";
|
||||
},
|
||||
getCurrentDirectory: (): string => {
|
||||
return "";
|
||||
},
|
||||
readDirectory: (path: string, extension?: string, exclude?: string[]): string[] => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
exit: (exitCode?: number) => {
|
||||
},
|
||||
watchFile: (path, callback) => {
|
||||
return {
|
||||
close: () => { }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } {
|
||||
let logger: server.Logger = {
|
||||
close() { },
|
||||
isVerbose: () => false,
|
||||
loggingEnabled: () => false,
|
||||
perftrc: (s: string) => { },
|
||||
info: (s: string) => { },
|
||||
startGroup: () => { },
|
||||
endGroup: () => { },
|
||||
msg: (s: string, type?: string) => { }
|
||||
};
|
||||
|
||||
let projectService = new server.ProjectService(serverHost, logger);
|
||||
let rootScriptInfo = projectService.openFile(rootFile, /* openedByClient */true);
|
||||
let project = projectService.createInferredProject(rootScriptInfo);
|
||||
project.setProjectOptions( {files: [rootScriptInfo.fileName], compilerOptions: {module: ts.ModuleKind.AMD} } );
|
||||
return {
|
||||
project,
|
||||
rootScriptInfo
|
||||
};
|
||||
}
|
||||
|
||||
describe("Caching in LSHost", () => {
|
||||
it("works using legacy resolution logic", () => {
|
||||
let root: File = {
|
||||
name: "c:/d/f0.ts",
|
||||
content: `import {x} from "f1"`
|
||||
};
|
||||
|
||||
let imported: File = {
|
||||
name: "c:/f1.ts",
|
||||
content: `foo()`
|
||||
};
|
||||
|
||||
let serverHost = createDefaultServerHost({ [root.name]: root, [imported.name]: imported });
|
||||
let { project, rootScriptInfo } = createProject(root.name, serverHost);
|
||||
|
||||
// ensure that imported file was found
|
||||
let diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
|
||||
assert.equal(diags.length, 1);
|
||||
|
||||
let originalFileExists = serverHost.fileExists;
|
||||
{
|
||||
// patch fileExists to make sure that disk is not touched
|
||||
serverHost.fileExists = (fileName): boolean => {
|
||||
assert.isTrue(false, "fileExists should not be called");
|
||||
return false;
|
||||
};
|
||||
|
||||
let newContent = `import {x} from "f1"
|
||||
var x: string = 1;`;
|
||||
rootScriptInfo.editContent(0, rootScriptInfo.content.length, newContent);
|
||||
// trigger synchronization to make sure that import will be fetched from the cache
|
||||
diags = project.compilerService.languageService.getSemanticDiagnostics(imported.name);
|
||||
// ensure file has correct number of errors after edit
|
||||
assert.equal(diags.length, 1);
|
||||
}
|
||||
{
|
||||
let fileExistsIsCalled = false;
|
||||
serverHost.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsIsCalled = true;
|
||||
assert.isTrue(fileName.indexOf('/f2.') !== -1);
|
||||
return originalFileExists(fileName);
|
||||
};
|
||||
let newContent = `import {x} from "f2"`;
|
||||
rootScriptInfo.editContent(0, rootScriptInfo.content.length, newContent);
|
||||
|
||||
try {
|
||||
// trigger synchronization to make sure that LSHost will try to find 'f2' module on disk
|
||||
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
|
||||
assert.isTrue(false, `should not find file '${imported.name}'`)
|
||||
}
|
||||
catch(e) {
|
||||
assert.isTrue(e.message.indexOf(`Could not find file: '${imported.name}'.`) === 0);
|
||||
}
|
||||
|
||||
assert.isTrue(fileExistsIsCalled);
|
||||
}
|
||||
{
|
||||
let fileExistsCalled = false;
|
||||
serverHost.fileExists = (fileName): boolean => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
fileExistsCalled = true;
|
||||
assert.isTrue(fileName.indexOf('/f1.') !== -1);
|
||||
return originalFileExists(fileName);
|
||||
};
|
||||
|
||||
let newContent = `import {x} from "f1"`;
|
||||
rootScriptInfo.editContent(0, rootScriptInfo.content.length, newContent);
|
||||
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
|
||||
assert.isTrue(fileExistsCalled);
|
||||
|
||||
// setting compiler options discards module resolution cache
|
||||
fileExistsCalled = false;
|
||||
|
||||
let opts = ts.clone(project.projectOptions);
|
||||
opts.compilerOptions = ts.clone(opts.compilerOptions);
|
||||
opts.compilerOptions.target = ts.ScriptTarget.ES5;
|
||||
project.setProjectOptions(opts);
|
||||
|
||||
project.compilerService.languageService.getSemanticDiagnostics(imported.name);
|
||||
assert.isTrue(fileExistsCalled);
|
||||
}
|
||||
});
|
||||
|
||||
it("loads missing files from disk", () => {
|
||||
let root: File = {
|
||||
name: 'c:/foo.ts',
|
||||
content: `import {x} from "bar"`
|
||||
};
|
||||
|
||||
let imported: File = {
|
||||
name: 'c:/bar.d.ts',
|
||||
content: `export var y = 1`
|
||||
};
|
||||
|
||||
let fileMap: Map<File> = { [root.name]: root };
|
||||
let serverHost = createDefaultServerHost(fileMap);
|
||||
let originalFileExists = serverHost.fileExists;
|
||||
|
||||
let fileExistsCalledForBar = false;
|
||||
serverHost.fileExists = fileName => {
|
||||
if (fileName === "lib.d.ts") {
|
||||
return false;
|
||||
}
|
||||
if (!fileExistsCalledForBar) {
|
||||
fileExistsCalledForBar = fileName.indexOf("/bar.") !== -1;
|
||||
}
|
||||
|
||||
return originalFileExists(fileName);
|
||||
};
|
||||
|
||||
let { project, rootScriptInfo } = createProject(root.name, serverHost);
|
||||
|
||||
let diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
assert.isTrue(diags.length === 1, "one diagnostic expected");
|
||||
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
|
||||
|
||||
// assert that import will success once file appear on disk
|
||||
fileMap[imported.name] = imported;
|
||||
fileExistsCalledForBar = false;
|
||||
rootScriptInfo.editContent(0, rootScriptInfo.content.length, `import {y} from "bar"`)
|
||||
|
||||
diags = project.compilerService.languageService.getSemanticDiagnostics(root.name);
|
||||
assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called");
|
||||
assert.isTrue(diags.length === 0);
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/// <reference path="..\..\..\src\harness\external\mocha.d.ts" />
|
||||
/// <reference path='..\..\..\src\harness\harness.ts' />
|
||||
/// <reference path="..\..\..\src\harness\harnessLanguageService.ts" />
|
||||
|
||||
module ts {
|
||||
|
||||
const enum ChangedPart {
|
||||
references = 1 << 0,
|
||||
importsAndExports = 1 << 1,
|
||||
program = 1 << 2
|
||||
}
|
||||
|
||||
let newLine = "\r\n";
|
||||
|
||||
interface SourceFileWithText extends SourceFile {
|
||||
sourceText?: SourceText;
|
||||
}
|
||||
|
||||
interface NamedSourceText {
|
||||
name: string;
|
||||
text: SourceText
|
||||
}
|
||||
|
||||
interface ProgramWithSourceTexts extends Program {
|
||||
sourceTexts?: NamedSourceText[];
|
||||
}
|
||||
|
||||
class SourceText implements IScriptSnapshot {
|
||||
private fullText: string;
|
||||
|
||||
constructor(private references: string,
|
||||
private importsAndExports: string,
|
||||
private program: string,
|
||||
private changedPart: ChangedPart = 0,
|
||||
private version = 0) {
|
||||
}
|
||||
|
||||
static New(references: string, importsAndExports: string, program: string): SourceText {
|
||||
Debug.assert(references !== undefined);
|
||||
Debug.assert(importsAndExports !== undefined);
|
||||
Debug.assert(program !== undefined);
|
||||
return new SourceText(references + newLine, importsAndExports + newLine, program || "");
|
||||
}
|
||||
|
||||
public getVersion(): number {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public updateReferences(newReferences: string): SourceText {
|
||||
Debug.assert(newReferences !== undefined);
|
||||
return new SourceText(newReferences + newLine, this.importsAndExports, this.program, this.changedPart | ChangedPart.references, this.version + 1);
|
||||
}
|
||||
public updateImportsAndExports(newImportsAndExports: string): SourceText {
|
||||
Debug.assert(newImportsAndExports !== undefined);
|
||||
return new SourceText(this.references, newImportsAndExports + newLine, this.program, this.changedPart | ChangedPart.importsAndExports, this.version + 1);
|
||||
}
|
||||
public updateProgram(newProgram: string): SourceText {
|
||||
Debug.assert(newProgram !== undefined);
|
||||
return new SourceText(this.references, this.importsAndExports, newProgram, this.changedPart | ChangedPart.program, this.version + 1);
|
||||
}
|
||||
|
||||
public getFullText() {
|
||||
return this.fullText || (this.fullText = this.references + this.importsAndExports + this.program);
|
||||
}
|
||||
|
||||
public getText(start: number, end: number): string {
|
||||
return this.getFullText().substring(start, end);
|
||||
}
|
||||
|
||||
getLength(): number {
|
||||
return this.getFullText().length;
|
||||
}
|
||||
|
||||
getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
|
||||
var oldText = <SourceText>oldSnapshot;
|
||||
var oldSpan: TextSpan;
|
||||
var newLength: number;
|
||||
switch (oldText.changedPart ^ this.changedPart) {
|
||||
case ChangedPart.references:
|
||||
oldSpan = createTextSpan(0, oldText.references.length);
|
||||
newLength = this.references.length;
|
||||
break;
|
||||
case ChangedPart.importsAndExports:
|
||||
oldSpan = createTextSpan(oldText.references.length, oldText.importsAndExports.length);
|
||||
newLength = this.importsAndExports.length
|
||||
break;
|
||||
case ChangedPart.program:
|
||||
oldSpan = createTextSpan(oldText.references.length + oldText.importsAndExports.length, oldText.program.length);
|
||||
newLength = this.program.length;
|
||||
break;
|
||||
default:
|
||||
Debug.assert(false, "Unexpected change");
|
||||
}
|
||||
|
||||
return createTextChangeRange(oldSpan, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget): CompilerHost {
|
||||
let files: Map<SourceFileWithText> = {};
|
||||
for (let t of texts) {
|
||||
let file = <SourceFileWithText>createSourceFile(t.name, t.text.getFullText(), target);
|
||||
file.sourceText = t.text;
|
||||
files[t.name] = file;
|
||||
}
|
||||
|
||||
return {
|
||||
getSourceFile(fileName): SourceFile {
|
||||
return files[fileName];
|
||||
},
|
||||
getDefaultLibFileName(): string {
|
||||
return "lib.d.ts"
|
||||
},
|
||||
writeFile(file, text) {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
getCurrentDirectory(): string {
|
||||
return "";
|
||||
},
|
||||
getCanonicalFileName(fileName): string {
|
||||
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
},
|
||||
useCaseSensitiveFileNames(): boolean {
|
||||
return sys.useCaseSensitiveFileNames;
|
||||
},
|
||||
getNewLine(): string {
|
||||
return sys.newLine;
|
||||
},
|
||||
fileExists: fileName => hasProperty(files, fileName),
|
||||
readFile: fileName => {
|
||||
let file = lookUp(files, fileName);
|
||||
return file && file.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newProgram(texts: NamedSourceText[], rootNames: string[], options: CompilerOptions): Program {
|
||||
var host = createTestCompilerHost(texts, options.target);
|
||||
let program = <ProgramWithSourceTexts>createProgram(rootNames, options, host);
|
||||
program.sourceTexts = texts;
|
||||
return program;
|
||||
}
|
||||
|
||||
function updateProgram(oldProgram: Program, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
|
||||
var texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
||||
updater(texts);
|
||||
var host = createTestCompilerHost(texts, options.target);
|
||||
var program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
|
||||
program.sourceTexts = texts;
|
||||
return program;
|
||||
}
|
||||
|
||||
function getSizeOfMap(map: Map<any>): number {
|
||||
let size = 0;
|
||||
for (let id in map) {
|
||||
if (hasProperty(map, id)) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<string>): void {
|
||||
let file = program.getSourceFile(fileName);
|
||||
assert.isTrue(file !== undefined, `cannot find file ${fileName}`);
|
||||
if (expectedContent === undefined) {
|
||||
assert.isTrue(file.resolvedModules === undefined, "expected resolvedModules to be undefined");
|
||||
}
|
||||
else {
|
||||
assert.isTrue(file.resolvedModules !== undefined, "expected resolvedModuled to be set");
|
||||
let actualCacheSize = getSizeOfMap(file.resolvedModules);
|
||||
let expectedSize = getSizeOfMap(expectedContent);
|
||||
assert.isTrue(actualCacheSize === expectedSize, `expected actual size: ${actualCacheSize} to be equal to ${expectedSize}`);
|
||||
|
||||
for (let id in expectedContent) {
|
||||
if (hasProperty(expectedContent, id)) {
|
||||
assert.isTrue(hasProperty(file.resolvedModules, id), `expected ${id} to be found in resolved modules`);
|
||||
assert.isTrue(expectedContent[id] === file.resolvedModules[id], `expected '${expectedContent[id]}' to be equal to '${file.resolvedModules[id]}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("Reuse program structure", () => {
|
||||
let target = ScriptTarget.Latest;
|
||||
let files = [
|
||||
{ name: "a.ts", text: SourceText.New(`/// <reference path='b.ts'/>`, "", `var x = 1`) },
|
||||
{ name: "b.ts", text: SourceText.New(`/// <reference path='c.ts'/>`, "", `var y = 2`) },
|
||||
{ name: "c.ts", text: SourceText.New("", "", `var z = 1;`) },
|
||||
]
|
||||
|
||||
it("successful if change does not affect imports", () => {
|
||||
var program_1 = newProgram(files, ["a.ts"], { target });
|
||||
var program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 100");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("fails if change affects tripleslash references", () => {
|
||||
var program_1 = newProgram(files, ["a.ts"], { target });
|
||||
var program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
let newReferences = `/// <reference path='b.ts'/>
|
||||
/// <reference path='c.ts'/>
|
||||
`;
|
||||
files[0].text = files[0].text.updateReferences(newReferences);
|
||||
});
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("fails if change affects imports", () => {
|
||||
var program_1 = newProgram(files, ["a.ts"], { target });
|
||||
var program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
|
||||
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
|
||||
});
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("fails if module kind changes", () => {
|
||||
var program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
|
||||
var program_2 = updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, files => void 0);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
it("resolution cache follows imports", () => {
|
||||
let files = [
|
||||
{ name: "a.ts", text: SourceText.New("", "import {_} from 'b'", "var x = 1") },
|
||||
{ name: "b.ts", text: SourceText.New("", "", "var y = 2") },
|
||||
];
|
||||
var options: CompilerOptions = { target };
|
||||
|
||||
var program_1 = newProgram(files, ["a.ts"], options);
|
||||
checkResolvedModulesCache(program_1, "a.ts", { "b": "b.ts" });
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
var program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateProgram("var x = 2");
|
||||
});
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedModulesCache(program_1, "a.ts", { "b": "b.ts" });
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
// imports has changed - program is not reused
|
||||
var program_3 = updateProgram(program_2, ["a.ts"], options, files => {
|
||||
files[0].text = files[0].text.updateImportsAndExports("");
|
||||
});
|
||||
assert.isTrue(!program_2.structureIsReused);
|
||||
checkResolvedModulesCache(program_3, "a.ts", undefined);
|
||||
|
||||
var program_4 = updateProgram(program_3, ["a.ts"], options, files => {
|
||||
let newImports = `import x from 'b'
|
||||
import y from 'c'
|
||||
`;
|
||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
checkResolvedModulesCache(program_4, "a.ts", { "b": "b.ts", "c": undefined });
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -9,8 +9,8 @@ interface ClassificationEntry {
|
||||
|
||||
describe('Colorization', function () {
|
||||
// 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();
|
||||
var languageServiceAdapter = new Harness.LanguageService.ShimLanugageServiceAdapter(/*preprocessToResolve*/ false);
|
||||
var classifier = languageServiceAdapter.getClassifier();
|
||||
|
||||
function getEntryAtPosistion(result: ts.ClassificationResult, position: number) {
|
||||
var entryPosition = 0;
|
||||
|
||||
@@ -30,10 +30,15 @@ describe("DocumentRegistry", () => {
|
||||
|
||||
assert(f1 !== f3, "Changed target: Expected to have different instances of document");
|
||||
|
||||
compilerOptions.module = ts.ModuleKind.CommonJS;
|
||||
compilerOptions.preserveConstEnums = true;
|
||||
var f4 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
|
||||
|
||||
assert(f3 === f4, "Changed module: Expected to have the same instance of the document");
|
||||
assert(f3 === f4, "Changed preserveConstEnums: Expected to have the same instance of the document");
|
||||
|
||||
compilerOptions.module = ts.ModuleKind.System;
|
||||
var f5 = documentRegistry.acquireDocument("file1.ts", compilerOptions, ts.ScriptSnapshot.fromString("var x = 1;"), /* version */ "1");
|
||||
|
||||
assert(f4 !== f5, "Changed module: Expected to have different instances of the document");
|
||||
});
|
||||
|
||||
it("Acquiring document gets correct version 1", () => {
|
||||
|
||||
@@ -50,6 +50,7 @@ describe('PreProcessFile:', function () {
|
||||
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[]>[],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -59,6 +60,7 @@ describe('PreProcessFile:', function () {
|
||||
{
|
||||
referencedFiles: <ts.FileReference[]>[],
|
||||
importedFiles: <ts.FileReference[]>[],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -69,6 +71,7 @@ describe('PreProcessFile:', function () {
|
||||
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 }],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -78,6 +81,7 @@ describe('PreProcessFile:', function () {
|
||||
{
|
||||
referencedFiles: <ts.FileReference[]>[],
|
||||
importedFiles: <ts.FileReference[]>[],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -87,6 +91,7 @@ describe('PreProcessFile:', function () {
|
||||
{
|
||||
referencedFiles: <ts.FileReference[]>[],
|
||||
importedFiles: [{ fileName: "r3.ts", pos: 73, end: 78 }],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -96,6 +101,7 @@ describe('PreProcessFile:', function () {
|
||||
{
|
||||
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 }],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
});
|
||||
}),
|
||||
@@ -105,6 +111,7 @@ describe('PreProcessFile:', function () {
|
||||
{
|
||||
referencedFiles: [{ fileName: "refFile1.ts", pos: 0, end: 35 }],
|
||||
importedFiles: [{ fileName: "r1.ts", pos: 91, end: 96 }, { fileName: "r3.ts", pos: 148, end: 153 }],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
})
|
||||
});
|
||||
@@ -129,6 +136,7 @@ describe('PreProcessFile:', function () {
|
||||
{ fileName: "m6", pos: 160, end: 162 },
|
||||
{ fileName: "m7", pos: 199, end: 201 }
|
||||
],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
})
|
||||
});
|
||||
@@ -147,9 +155,24 @@ describe('PreProcessFile:', function () {
|
||||
{ fileName: "m3", pos: 63, end: 65 },
|
||||
{ fileName: "m4", pos: 101, end: 103 },
|
||||
],
|
||||
ambientExternalModules: undefined,
|
||||
isLibFile: false
|
||||
})
|
||||
});
|
||||
|
||||
it("Correctly return ambient external modules", () => {
|
||||
test(`
|
||||
declare module A {}
|
||||
declare module "B" {}
|
||||
function foo() {
|
||||
}
|
||||
`, false, {
|
||||
referencedFiles: [],
|
||||
importedFiles: [],
|
||||
ambientExternalModules: ["B"],
|
||||
isLibFile: false
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user