/// module ts { export interface Map { [index: string]: T; } export function forEach(array: T[], callback: (element: T) => U): U { var result: U; if (array) { for (var i = 0, len = array.length; i < len; i++) { if (result = callback(array[i])) break; } } return result; } export function contains(array: T[], value: T): boolean { if (array) { var len = array.length; for (var i = 0; i < len; i++) { if (array[i] === value) return true; } } return false; } export function indexOf(array: T[], value: T): number { if (array) { var len = array.length; for (var i = 0; i < len; i++) { if (array[i] === value) return i; } } return -1; } export function filter(array: T[], f: (x: T) => boolean): T[] { var result: T[]; if (array) { result = []; for (var i = 0, len = array.length; i < len; i++) { var item = array[i]; if (f(item)) result.push(item); } } return result; } export function map(array: T[], f: (x: T) => U): U[] { var result: U[]; if (array) { result = []; var len = array.length; for (var i = 0; i < len; i++) { result.push(f(array[i])); } } return result; } export function concatenate(array1: T[], array2: T[]): T[] { if (!array2.length) return array1; if (!array1.length) return array2; return array1.concat(array2); } export function sum(array: any[], prop: string): number { var result = 0; for (var i = 0; i < array.length; i++) { result += array[i][prop]; } return result; } export function binarySearch(array: number[], value: number): number { var low = 0; var high = array.length - 1; while (low <= high) { var middle = low + ((high - low) >> 1); var midValue = array[middle]; if (midValue === value) { return middle; } else if (midValue > value) { high = middle - 1; } else { low = middle + 1; } } return ~low; } var hasOwnProperty = Object.prototype.hasOwnProperty; export function hasProperty(map: Map, key: string): boolean { return hasOwnProperty.call(map, key); } export function getProperty(map: Map, key: string): T { return hasOwnProperty.call(map, key) ? map[key] : undefined; } export function isEmpty(map: Map) { for (var id in map) return false; return true; } export function clone(object: T): T { var result: any = {}; for (var id in object) { result[id] = (object)[id]; } return result; } export function forEachValue(map: Map, callback: (value: T) => U): U { var result: U; for (var id in map) { if (result = callback(map[id])) break; } return result; } export function mapToArray(map: Map): T[] { var result: T[] = []; for (var id in map) result.push(map[id]); return result; } function formatStringFromArgs(text: string, args: { [index: number]: any; }, baseIndex?: number): string { baseIndex = baseIndex || 0; return text.replace(/{(\d+)}/g, (match, index?) => args[+index + baseIndex]); } export var localizedDiagnosticMessages: Map = undefined; function getLocaleSpecificMessage(message: string) { if (ts.localizedDiagnosticMessages) { message = localizedDiagnosticMessages[message]; } /* Check to see that we got an actual value back. */ Debug.assert(message, "Diagnostic message does not exist in locale map."); return message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic; export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic { var text = getLocaleSpecificMessage(message.key); if (arguments.length > 4) { text = formatStringFromArgs(text, arguments, 4); } return { file: file, start: start, length: length, messageText: text, category: message.category, code: message.code }; } export function createCompilerDiagnostic(message: DiagnosticMessage, ...args: any[]): Diagnostic; export function createCompilerDiagnostic(message: DiagnosticMessage): Diagnostic { var text = getLocaleSpecificMessage(message.key); if (arguments.length > 1) { text = formatStringFromArgs(text, arguments, 1); } return { file: undefined, start: undefined, length: undefined, messageText: text, category: message.category, code: message.code }; } export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage, ...args: any[]): DiagnosticMessageChain; export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage): DiagnosticMessageChain { var text = getLocaleSpecificMessage(message.key); if (arguments.length > 2) { text = formatStringFromArgs(text, arguments, 2); } return { messageText: text, category: message.category, code: message.code, next: details } } export function flattenDiagnosticChain(file: SourceFile, start: number, length: number, diagnosticChain: DiagnosticMessageChain): Diagnostic { var code = diagnosticChain.code; var category = diagnosticChain.category; var messageText = ""; var indent = 0; while (diagnosticChain) { if (indent) { messageText += sys.newLine; for (var i = 0; i < indent; i++) { messageText += " "; } } messageText += diagnosticChain.messageText; indent++; diagnosticChain = diagnosticChain.next; } return { file: file, start: start, length: length, code: code, category: category, messageText: messageText }; } function compareValues(a: any, b: any): number { if (a === b) return 0; if (a === undefined) return -1; if (b === undefined) return 1; return a < b ? -1 : 1; } function getDiagnosticFilename(diagnostic: Diagnostic): string { return diagnostic.file ? diagnostic.file.filename : undefined; } export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): number { return compareValues(getDiagnosticFilename(d1), getDiagnosticFilename(d2)) || compareValues(d1.start, d2.start) || compareValues(d1.length, d2.length) || compareValues(d1.code, d2.code) || compareValues(d1.messageText, d2.messageText) || 0; } export function deduplicateSortedDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] { if (diagnostics.length < 2) { return diagnostics; } var newDiagnostics = [diagnostics[0]]; var previousDiagnostic = diagnostics[0]; for (var i = 1; i < diagnostics.length; i++) { var currentDiagnostic = diagnostics[i]; var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === 0; if (!isDupe) { newDiagnostics.push(currentDiagnostic); previousDiagnostic = currentDiagnostic; } } return newDiagnostics; } export function normalizeSlashes(path: string): string { return path.replace(/\\/g, "/"); } // Returns length of path root (i.e. length of "/", "x:/", "//server/share/") function getRootLength(path: string): number { if (path.charCodeAt(0) === CharacterCodes.slash) { if (path.charCodeAt(1) !== CharacterCodes.slash) return 1; var p1 = path.indexOf("/", 2); if (p1 < 0) return 2; var p2 = path.indexOf("/", p1 + 1); if (p2 < 0) return p1 + 1; return p2 + 1; } if (path.charCodeAt(1) === CharacterCodes.colon) { if (path.charCodeAt(2) === CharacterCodes.slash) return 3; return 2; } return 0; } export var directorySeparator = "/"; function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) { var parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator); var normalized: string[] = []; for (var i = 0; i < parts.length; i++) { var part = parts[i]; if (part !== ".") { if (part === ".." && normalized.length > 0 && normalized[normalized.length - 1] !== "..") { normalized.pop(); } else { normalized.push(part); } } } return normalized; } export function normalizePath(path: string): string { var path = normalizeSlashes(path); var rootLength = getRootLength(path); var normalized = getNormalizedParts(path, rootLength); return path.substr(0, rootLength) + normalized.join(directorySeparator); } export function getDirectoryPath(path: string) { return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(directorySeparator))); } export function isUrl(path: string) { return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1; } export function isRootedDiskPath(path: string) { return getRootLength(path) !== 0; } function normalizedPathComponents(path: string, rootLength: number) { var normalizedParts = getNormalizedParts(path, rootLength); return [path.substr(0, rootLength)].concat(normalizedParts); } export function getNormalizedPathComponents(path: string, currentDirectory: string) { var path = normalizeSlashes(path); var rootLength = getRootLength(path); if (rootLength == 0) { // If the path is not rooted it is relative to current directory path = combinePaths(normalizeSlashes(currentDirectory), path); rootLength = getRootLength(path); } return normalizedPathComponents(path, rootLength); } export function getNormalizedPathFromPathCompoments(pathComponents: string[]) { if (pathComponents && pathComponents.length) { return pathComponents[0] + pathComponents.slice(1).join(directorySeparator); } } function getNormalizedPathComponentsOfUrl(url: string) { // Get root length of http://www.website.com/folder1/foler2/ // In this example the root is: http://www.website.com/ // normalized path components should be ["http://www.website.com/", "folder1", "folder2"] var urlLength = url.length; // Initial root length is http:// part var rootLength = url.indexOf("://") + "://".length; while (rootLength < urlLength) { // Consume all immediate slashes in the protocol // eg.initial rootlength is just file:// but it needs to consume another "/" in file:/// if (url.charCodeAt(rootLength) === CharacterCodes.slash) { rootLength++; } else { // non slash character means we continue proceeding to next component of root search break; } } // there are no parts after http:// just return current string as the pathComponent if (rootLength === urlLength) { return [url]; } // Find the index of "/" after website.com so the root can be http://www.website.com/ (from existing http://) var indexOfNextSlash = url.indexOf(directorySeparator, rootLength); if (indexOfNextSlash !== -1) { // Found the "/" after the website.com so the root is length of http://www.website.com/ // and get components afetr the root normally like any other folder components rootLength = indexOfNextSlash + 1; return normalizedPathComponents(url, rootLength); } else { // Can't find the host assume the rest of the string as component // but make sure we append "/" to it as root is not joined using "/" // eg. if url passed in was http://website.com we want to use root as [http://website.com/] // so that other path manipulations will be correct and it can be merged with relative paths correctly return [url + directorySeparator]; } } function getNormalizedPathOrUrlComponents(pathOrUrl: string, currentDirectory: string) { if (isUrl(pathOrUrl)) { return getNormalizedPathComponentsOfUrl(pathOrUrl); } else { return getNormalizedPathComponents(pathOrUrl, currentDirectory); } } export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, isAbsolutePathAnUrl: boolean) { var pathComponents = getNormalizedPathOrUrlComponents(relativeOrAbsolutePath, currentDirectory); var directoryComponents = getNormalizedPathOrUrlComponents(directoryPathOrUrl, currentDirectory); if (directoryComponents.length > 1 && directoryComponents[directoryComponents.length - 1] === "") { // If the directory path given was of type test/cases/ then we really need components of directry to be only till its name // that is ["test", "cases", ""] needs to be actually ["test", "cases"] directoryComponents.length--; } // Find the component that differs for (var joinStartIndex = 0; joinStartIndex < pathComponents.length && joinStartIndex < directoryComponents.length; joinStartIndex++) { if (directoryComponents[joinStartIndex] !== pathComponents[joinStartIndex]) { break; } } // Get the relative path if (joinStartIndex) { var relativePath = ""; var relativePathComponents = pathComponents.slice(joinStartIndex, pathComponents.length); for (; joinStartIndex < directoryComponents.length; joinStartIndex++) { if (directoryComponents[joinStartIndex] !== "") { relativePath = relativePath + ".." + directorySeparator; } } return relativePath + relativePathComponents.join(directorySeparator); } // Cant find the relative path, get the absolute path var absolutePath = getNormalizedPathFromPathCompoments(pathComponents); if (isAbsolutePathAnUrl && isRootedDiskPath(absolutePath)) { absolutePath = "file:///" + absolutePath; } return absolutePath; } export function getBaseFilename(path: string) { var i = path.lastIndexOf(directorySeparator); return i < 0 ? path : path.substring(i + 1); } export function combinePaths(path1: string, path2: string) { if (!(path1 && path1.length)) return path2; if (!(path2 && path2.length)) return path1; if (path2.charAt(0) === directorySeparator) return path2; if (path1.charAt(path1.length - 1) === directorySeparator) return path1 + path2; return path1 + directorySeparator + path2; } export function fileExtensionIs(path: string, extension: string): boolean { var pathLen = path.length; var extLen = extension.length; return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } export interface ObjectAllocator { getNodeConstructor(kind: SyntaxKind): new () => Node; getSymbolConstructor(): new (flags: SymbolFlags, name: string) => Symbol; getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type; getSignatureConstructor(): new (checker: TypeChecker) => Signature; } function Symbol(flags: SymbolFlags, name: string) { this.flags = flags; this.name = name; this.declarations = undefined; } function Type(checker: TypeChecker, flags: TypeFlags) { this.flags = flags; } function Signature(checker: TypeChecker) { } export var objectAllocator: ObjectAllocator = { getNodeConstructor: kind => { function Node() { } Node.prototype = { kind: kind, pos: 0, end: 0, flags: 0, parent: undefined, }; return Node; }, getSymbolConstructor: () => Symbol, getTypeConstructor: () => Type, getSignatureConstructor: () => Signature } export enum AssertionLevel { None = 0, Normal = 1, Aggressive = 2, VeryAggressive = 3, } export module Debug { var currentAssertionLevel = AssertionLevel.None; export function shouldAssert(level: AssertionLevel): boolean { return this.currentAssertionLevel >= level; } export function assert(expression: any, message?: string, verboseDebugInfo?: () => string): void { if (!expression) { var verboseDebugString = ""; if (verboseDebugInfo) { verboseDebugString = "\r\nVerbose Debug Information: " + verboseDebugInfo(); } throw new Error("Debug Failure. False expression: " + (message || "") + verboseDebugString); } } export function fail(message?: string): void { Debug.assert(false, message); } } }