From c3e63ee1f1e3bdf6331b5c68ebdd5b117dece80d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 7 Sep 2016 14:25:52 -0700 Subject: [PATCH] Move allocators.ts to services.ts, meaning.ts to utilities.ts, and transpile functions to a new file transpile.ts --- Jakefile.js | 3 +- src/services/allocators.ts | 633 ------------------------ src/services/classifier.ts | 10 +- src/services/completions.ts | 2 +- src/services/findAllReferences.ts | 12 +- src/services/meaning.ts | 160 ------ src/services/services.ts | 789 ++++++++++++++++++++++++------ src/services/symbolDisplay.ts | 6 +- src/services/transpile.ts | 154 ++++++ src/services/tsconfig.json | 3 +- src/services/utilities.ts | 158 ++++++ 11 files changed, 962 insertions(+), 968 deletions(-) delete mode 100644 src/services/allocators.ts delete mode 100644 src/services/meaning.ts create mode 100644 src/services/transpile.ts diff --git a/Jakefile.js b/Jakefile.js index efc7733984..e304ce0e34 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -122,7 +122,6 @@ var servicesSources = [ }).concat([ "types.ts", "utilities.ts", - "allocators.ts", "breakpoints.ts", "classifier.ts", "completions.ts", @@ -131,7 +130,6 @@ var servicesSources = [ "goToDefinition.ts", "jsDoc.ts", "jsTyping.ts", - "meaning.ts", "navigateTo.ts", "navigationBar.ts", "outliningElementsCollector.ts", @@ -142,6 +140,7 @@ var servicesSources = [ "shims.ts", "signatureHelp.ts", "symbolDisplay.ts", + "transpile.ts", "formatting/formatting.ts", "formatting/formattingContext.ts", "formatting/formattingRequestKind.ts", diff --git a/src/services/allocators.ts b/src/services/allocators.ts deleted file mode 100644 index 4bfef21cf1..0000000000 --- a/src/services/allocators.ts +++ /dev/null @@ -1,633 +0,0 @@ -/* @internal */ -namespace ts.Allocators { - function createNode(kind: SyntaxKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject | IdentifierObject { - const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) : - kind === SyntaxKind.Identifier ? new IdentifierObject(kind, pos, end) : - new TokenObject(kind, pos, end); - node.parent = parent; - return node; - } - - class NodeObject implements Node { - public kind: SyntaxKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public parent: Node; - public jsDocComments: JSDocComment[]; - public original: Node; - public transformFlags: TransformFlags; - public excludeTransformFlags: TransformFlags; - private _children: Node[]; - - constructor(kind: SyntaxKind, pos: number, end: number) { - this.pos = pos; - this.end = end; - this.flags = NodeFlags.None; - this.transformFlags = undefined; - this.excludeTransformFlags = undefined; - this.parent = undefined; - this.kind = kind; - } - - public getSourceFile(): SourceFile { - return getSourceFileOfNode(this); - } - - public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this, sourceFile, includeJsDocComment); - } - - public getFullStart(): number { - return this.pos; - } - - public getEnd(): number { - return this.end; - } - - public getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - public getFullWidth(): number { - return this.end - this.pos; - } - - public getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - public getFullText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); - } - - public getText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); - } - - private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number { - scanner.setTextPos(pos); - while (pos < end) { - const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan(); - const textPos = scanner.getTextPos(); - if (textPos <= end) { - nodes.push(createNode(token, pos, textPos, this)); - } - pos = textPos; - } - return pos; - } - - private createSyntaxList(nodes: NodeArray): Node { - const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, this); - list._children = []; - let pos = nodes.pos; - - for (const node of nodes) { - if (pos < node.pos) { - pos = this.addSyntheticNodes(list._children, pos, node.pos); - } - list._children.push(node); - pos = node.end; - } - if (pos < nodes.end) { - this.addSyntheticNodes(list._children, pos, nodes.end); - } - return list; - } - - private createChildren(sourceFile?: SourceFile) { - let children: Node[]; - if (this.kind >= SyntaxKind.FirstNode) { - scanner.setText((sourceFile || this.getSourceFile()).text); - children = []; - let pos = this.pos; - const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; - const processNode = (node: Node) => { - const isJSDocTagNode = isJSDocTag(node); - if (!isJSDocTagNode && pos < node.pos) { - pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); - } - children.push(node); - if (!isJSDocTagNode) { - pos = node.end; - } - }; - const processNodes = (nodes: NodeArray) => { - if (pos < nodes.pos) { - pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner); - } - children.push(this.createSyntaxList(>nodes)); - pos = nodes.end; - }; - // jsDocComments need to be the first children - if (this.jsDocComments) { - for (const jsDocComment of this.jsDocComments) { - processNode(jsDocComment); - } - } - // For syntactic classifications, all trivia are classcified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. - pos = this.pos; - forEachChild(this, processNode, processNodes); - if (pos < this.end) { - this.addSyntheticNodes(children, pos, this.end); - } - scanner.setText(undefined); - } - this._children = children || emptyArray; - } - - public getChildCount(sourceFile?: SourceFile): number { - if (!this._children) this.createChildren(sourceFile); - return this._children.length; - } - - public getChildAt(index: number, sourceFile?: SourceFile): Node { - if (!this._children) this.createChildren(sourceFile); - return this._children[index]; - } - - public getChildren(sourceFile?: SourceFile): Node[] { - if (!this._children) this.createChildren(sourceFile); - return this._children; - } - - public getFirstToken(sourceFile?: SourceFile): Node { - const children = this.getChildren(sourceFile); - if (!children.length) { - return undefined; - } - - const child = children[0]; - - return child.kind < SyntaxKind.FirstNode ? child : child.getFirstToken(sourceFile); - } - - public getLastToken(sourceFile?: SourceFile): Node { - const children = this.getChildren(sourceFile); - - const child = lastOrUndefined(children); - if (!child) { - return undefined; - } - - return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); - } - } - - class TokenOrIdentifierObject implements Token { - public kind: SyntaxKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public parent: Node; - public jsDocComments: JSDocComment[]; - public __tokenTag: any; - - constructor(pos: number, end: number) { - // Set properties in same order as NodeObject - this.pos = pos; - this.end = end; - this.flags = NodeFlags.None; - this.parent = undefined; - } - - public getSourceFile(): SourceFile { - return getSourceFileOfNode(this); - } - - public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this, sourceFile, includeJsDocComment); - } - - public getFullStart(): number { - return this.pos; - } - - public getEnd(): number { - return this.end; - } - - public getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - public getFullWidth(): number { - return this.end - this.pos; - } - - public getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - public getFullText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); - } - - public getText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); - } - - public getChildCount(sourceFile?: SourceFile): number { - return 0; - } - - public getChildAt(index: number, sourceFile?: SourceFile): Node { - return undefined; - } - - public getChildren(sourceFile?: SourceFile): Node[] { - return emptyArray; - } - - public getFirstToken(sourceFile?: SourceFile): Node { - return undefined; - } - - public getLastToken(sourceFile?: SourceFile): Node { - return undefined; - } - } - - class SymbolObject implements Symbol { - flags: SymbolFlags; - name: string; - declarations: Declaration[]; - - // Undefined is used to indicate the value has not been computed. If, after computing, the - // symbol has no doc comment, then the empty string will be returned. - documentationComment: SymbolDisplayPart[]; - - constructor(flags: SymbolFlags, name: string) { - this.flags = flags; - this.name = name; - } - - getFlags(): SymbolFlags { - return this.flags; - } - - getName(): string { - return this.name; - } - - getDeclarations(): Declaration[] { - return this.declarations; - } - - getDocumentationComment(): SymbolDisplayPart[] { - if (this.documentationComment === undefined) { - this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations, this.name, !(this.flags & SymbolFlags.Property)); - } - - return this.documentationComment; - } - } - - class TokenObject extends TokenOrIdentifierObject { - public kind: SyntaxKind; - constructor(kind: SyntaxKind, pos: number, end: number) { - super(pos, end); - this.kind = kind; - } - } - - class IdentifierObject extends TokenOrIdentifierObject { - constructor(kind: SyntaxKind, pos: number, end: number) { - super(pos, end); - } - } - IdentifierObject.prototype.kind = SyntaxKind.Identifier; - - class TypeObject implements Type { - checker: TypeChecker; - flags: TypeFlags; - id: number; - symbol: Symbol; - constructor(checker: TypeChecker, flags: TypeFlags) { - this.checker = checker; - this.flags = flags; - } - getFlags(): TypeFlags { - return this.flags; - } - getSymbol(): Symbol { - return this.symbol; - } - getProperties(): Symbol[] { - return this.checker.getPropertiesOfType(this); - } - getProperty(propertyName: string): Symbol { - return this.checker.getPropertyOfType(this, propertyName); - } - getApparentProperties(): Symbol[] { - return this.checker.getAugmentedPropertiesOfType(this); - } - getCallSignatures(): Signature[] { - return this.checker.getSignaturesOfType(this, SignatureKind.Call); - } - getConstructSignatures(): Signature[] { - return this.checker.getSignaturesOfType(this, SignatureKind.Construct); - } - getStringIndexType(): Type { - return this.checker.getIndexTypeOfType(this, IndexKind.String); - } - getNumberIndexType(): Type { - return this.checker.getIndexTypeOfType(this, IndexKind.Number); - } - getBaseTypes(): ObjectType[] { - return this.flags & (TypeFlags.Class | TypeFlags.Interface) - ? this.checker.getBaseTypes(this) - : undefined; - } - getNonNullableType(): Type { - return this.checker.getNonNullableType(this); - } - } - - class SignatureObject implements Signature { - checker: TypeChecker; - declaration: SignatureDeclaration; - typeParameters: TypeParameter[]; - parameters: Symbol[]; - thisParameter: Symbol; - resolvedReturnType: Type; - minArgumentCount: number; - hasRestParameter: boolean; - hasLiteralTypes: boolean; - - // Undefined is used to indicate the value has not been computed. If, after computing, the - // symbol has no doc comment, then the empty string will be returned. - documentationComment: SymbolDisplayPart[]; - - constructor(checker: TypeChecker) { - this.checker = checker; - } - getDeclaration(): SignatureDeclaration { - return this.declaration; - } - getTypeParameters(): Type[] { - return this.typeParameters; - } - getParameters(): Symbol[] { - return this.parameters; - } - getReturnType(): Type { - return this.checker.getReturnTypeOfSignature(this); - } - - getDocumentationComment(): SymbolDisplayPart[] { - if (this.documentationComment === undefined) { - this.documentationComment = this.declaration ? JsDoc.getJsDocCommentsFromDeclarations( - [this.declaration], - /*name*/ undefined, - /*canUseParsedParamTagComments*/ false) : []; - } - - return this.documentationComment; - } - } - - class SourceFileObject extends NodeObject implements SourceFile { - public _declarationBrand: any; - public fileName: string; - public path: Path; - public text: string; - public scriptSnapshot: IScriptSnapshot; - public lineMap: number[]; - - public statements: NodeArray; - public endOfFileToken: Node; - - public amdDependencies: { name: string; path: string }[]; - public moduleName: string; - public referencedFiles: FileReference[]; - public typeReferenceDirectives: FileReference[]; - - public syntacticDiagnostics: Diagnostic[]; - public referenceDiagnostics: Diagnostic[]; - public parseDiagnostics: Diagnostic[]; - public bindDiagnostics: Diagnostic[]; - - public isDeclarationFile: boolean; - public isDefaultLib: boolean; - public hasNoDefaultLib: boolean; - public externalModuleIndicator: Node; // The first node that causes this file to be an external module - public commonJsModuleIndicator: Node; // The first node that causes this file to be a CommonJS module - public nodeCount: number; - public identifierCount: number; - public symbolCount: number; - public version: string; - public scriptKind: ScriptKind; - public languageVersion: ScriptTarget; - public languageVariant: LanguageVariant; - public identifiers: Map; - public nameTable: Map; - public resolvedModules: Map; - public resolvedTypeReferenceDirectiveNames: Map; - public imports: LiteralExpression[]; - public moduleAugmentations: LiteralExpression[]; - private namedDeclarations: Map; - - constructor(kind: SyntaxKind, pos: number, end: number) { - super(kind, pos, end); - } - - public update(newText: string, textChangeRange: TextChangeRange): SourceFile { - return updateSourceFile(this, newText, textChangeRange); - } - - public getLineAndCharacterOfPosition(position: number): LineAndCharacter { - return ts.getLineAndCharacterOfPosition(this, position); - } - - public getLineStarts(): number[] { - return getLineStarts(this); - } - - public getPositionOfLineAndCharacter(line: number, character: number): number { - return ts.getPositionOfLineAndCharacter(this, line, character); - } - - public getNamedDeclarations(): Map { - if (!this.namedDeclarations) { - this.namedDeclarations = this.computeNamedDeclarations(); - } - - return this.namedDeclarations; - } - - private computeNamedDeclarations(): Map { - const result = createMap(); - - forEachChild(this, visit); - - return result; - - function addDeclaration(declaration: Declaration) { - const name = getDeclarationName(declaration); - if (name) { - multiMapAdd(result, name, declaration); - } - } - - function getDeclarations(name: string) { - return result[name] || (result[name] = []); - } - - function getDeclarationName(declaration: Declaration) { - if (declaration.name) { - const result = getTextOfIdentifierOrLiteral(declaration.name); - if (result !== undefined) { - return result; - } - - if (declaration.name.kind === SyntaxKind.ComputedPropertyName) { - const expr = (declaration.name).expression; - if (expr.kind === SyntaxKind.PropertyAccessExpression) { - return (expr).name.text; - } - - return getTextOfIdentifierOrLiteral(expr); - } - } - - return undefined; - } - - function getTextOfIdentifierOrLiteral(node: Node) { - if (node) { - if (node.kind === SyntaxKind.Identifier || - node.kind === SyntaxKind.StringLiteral || - node.kind === SyntaxKind.NumericLiteral) { - - return (node).text; - } - } - - return undefined; - } - - function visit(node: Node): void { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - const functionDeclaration = node; - const declarationName = getDeclarationName(functionDeclaration); - - if (declarationName) { - const declarations = getDeclarations(declarationName); - const lastDeclaration = lastOrUndefined(declarations); - - // Check whether this declaration belongs to an "overload group". - if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { - // Overwrite the last declaration if it was an overload - // and this one is an implementation. - if (functionDeclaration.body && !(lastDeclaration).body) { - declarations[declarations.length - 1] = functionDeclaration; - } - } - else { - declarations.push(functionDeclaration); - } - - forEachChild(node, visit); - } - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportSpecifier: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.TypeLiteral: - addDeclaration(node); - forEachChild(node, visit); - break; - - case SyntaxKind.Parameter: - // Only consider parameter properties - if (!hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { - break; - } - // fall through - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: { - const decl = node; - if (isBindingPattern(decl.name)) { - forEachChild(decl.name, visit); - break; - } - if (decl.initializer) - visit(decl.initializer); - } - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - addDeclaration(node); - break; - - case SyntaxKind.ExportDeclaration: - // Handle named exports case e.g.: - // export {a, b as B} from "mod"; - if ((node).exportClause) { - forEach((node).exportClause.elements, visit); - } - break; - - case SyntaxKind.ImportDeclaration: - const importClause = (node).importClause; - if (importClause) { - // Handle default import case e.g.: - // import d from "mod"; - if (importClause.name) { - addDeclaration(importClause); - } - - // Handle named bindings in imports e.g.: - // import * as NS from "mod"; - // import {a, b as B} from "mod"; - if (importClause.namedBindings) { - if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - addDeclaration(importClause.namedBindings); - } - else { - forEach((importClause.namedBindings).elements, visit); - } - } - } - break; - - default: - forEachChild(node, visit); - } - } - } - } - - export function getServicesObjectAllocator(): ObjectAllocator { - return { - getNodeConstructor: () => NodeObject, - getTokenConstructor: () => TokenObject, - getIdentifierConstructor: () => IdentifierObject, - getSourceFileConstructor: () => SourceFileObject, - getSymbolConstructor: () => SymbolObject, - getTypeConstructor: () => TypeObject, - getSignatureConstructor: () => SignatureObject, - }; - } -} diff --git a/src/services/classifier.ts b/src/services/classifier.ts index a6c6144317..56c31b08a6 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -498,7 +498,7 @@ namespace ts.Classifier { result.push(type); } - function classifySymbol(symbol: Symbol, meaningAtPosition: Meaning.SemanticMeaning): ClassificationType { + function classifySymbol(symbol: Symbol, meaningAtPosition: SemanticMeaning): ClassificationType { const flags = symbol.getFlags(); if ((flags & SymbolFlags.Classifiable) === SymbolFlags.None) { return; @@ -513,7 +513,7 @@ namespace ts.Classifier { else if (flags & SymbolFlags.TypeAlias) { return ClassificationType.typeAliasName; } - else if (meaningAtPosition & Meaning.SemanticMeaning.Type) { + else if (meaningAtPosition & SemanticMeaning.Type) { if (flags & SymbolFlags.Interface) { return ClassificationType.interfaceName; } @@ -525,8 +525,8 @@ namespace ts.Classifier { // Only classify a module as such if // - It appears in a namespace context. // - There exists a module declaration which actually impacts the value side. - if (meaningAtPosition & Meaning.SemanticMeaning.Namespace || - (meaningAtPosition & Meaning.SemanticMeaning.Value && hasValueSideModule(symbol))) { + if (meaningAtPosition & SemanticMeaning.Namespace || + (meaningAtPosition & SemanticMeaning.Value && hasValueSideModule(symbol))) { return ClassificationType.moduleName; } } @@ -559,7 +559,7 @@ namespace ts.Classifier { if (classifiableNames[identifier.text]) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { - const type = classifySymbol(symbol, Meaning.getMeaningFromLocation(node)); + const type = classifySymbol(symbol, getMeaningFromLocation(node)); if (type) { pushClassification(node.getStart(), node.getWidth(), type); } diff --git a/src/services/completions.ts b/src/services/completions.ts index e0130899e3..d933c89434 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -710,7 +710,7 @@ namespace ts.Completions { const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined); if (symbol) { - const { displayParts, documentation, symbolKind } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, Meaning.SemanticMeaning.All); + const { displayParts, documentation, symbolKind } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All); return { name: entryName, kindModifiers: SymbolDisplay.getSymbolModifiers(symbol), diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index f2e2a8c959..d8bb292926 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -69,7 +69,7 @@ namespace ts.FindAllReferences { let result: ReferencedSymbol[]; // Compute the meaning from the location and the symbol it references - const searchMeaning = getIntersectingMeaningFromDeclarations(Meaning.getMeaningFromLocation(node), declarations); + const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations); // Get the text to search for. // Note: if this is an external module symbol, the name doesn't include quotes. @@ -363,7 +363,7 @@ namespace ts.FindAllReferences { searchSymbol: Symbol, searchText: string, searchLocation: Node, - searchMeaning: Meaning.SemanticMeaning, + searchMeaning: SemanticMeaning, findInStrings: boolean, findInComments: boolean, result: ReferencedSymbol[], @@ -406,7 +406,7 @@ namespace ts.FindAllReferences { return; } - if (!(Meaning.getMeaningFromLocation(referenceLocation) & searchMeaning)) { + if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) { return; } @@ -995,9 +995,9 @@ namespace ts.FindAllReferences { * module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module) * do not intersect in any of the three spaces. */ - function getIntersectingMeaningFromDeclarations(meaning: Meaning.SemanticMeaning, declarations: Declaration[]): Meaning.SemanticMeaning { + function getIntersectingMeaningFromDeclarations(meaning: SemanticMeaning, declarations: Declaration[]): SemanticMeaning { if (declarations) { - let lastIterationMeaning: Meaning.SemanticMeaning; + let lastIterationMeaning: SemanticMeaning; do { // The result is order-sensitive, for instance if initialMeaning === Namespace, and declarations = [class, instantiated module] // we need to consider both as they initialMeaning intersects with the module in the namespace space, and the module @@ -1008,7 +1008,7 @@ namespace ts.FindAllReferences { lastIterationMeaning = meaning; for (const declaration of declarations) { - const declarationMeaning = Meaning.getMeaningFromDeclaration(declaration); + const declarationMeaning = getMeaningFromDeclaration(declaration); if (declarationMeaning & meaning) { meaning |= declarationMeaning; diff --git a/src/services/meaning.ts b/src/services/meaning.ts deleted file mode 100644 index da77ed4bfd..0000000000 --- a/src/services/meaning.ts +++ /dev/null @@ -1,160 +0,0 @@ -/* @internal */ -namespace ts.Meaning { - export const enum SemanticMeaning { - None = 0x0, - Value = 0x1, - Type = 0x2, - Namespace = 0x4, - All = Value | Type | Namespace - } - - export function getMeaningFromDeclaration(node: Node): SemanticMeaning { - switch (node.kind) { - case SyntaxKind.Parameter: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.EnumMember: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.CatchClause: - return SemanticMeaning.Value; - - case SyntaxKind.TypeParameter: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.TypeLiteral: - return SemanticMeaning.Type; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - return SemanticMeaning.Value | SemanticMeaning.Type; - - case SyntaxKind.ModuleDeclaration: - if (isAmbientModule(node)) { - return SemanticMeaning.Namespace | SemanticMeaning.Value; - } - else if (getModuleInstanceState(node) === ModuleInstanceState.Instantiated) { - return SemanticMeaning.Namespace | SemanticMeaning.Value; - } - else { - return SemanticMeaning.Namespace; - } - - case SyntaxKind.NamedImports: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportDeclaration: - case SyntaxKind.ExportAssignment: - case SyntaxKind.ExportDeclaration: - return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; - - // An external module can be a Value - case SyntaxKind.SourceFile: - return SemanticMeaning.Namespace | SemanticMeaning.Value; - } - - return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; - } - - export function getMeaningFromLocation(node: Node): SemanticMeaning { - if (node.parent.kind === SyntaxKind.ExportAssignment) { - return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; - } - else if (isInRightSideOfImport(node)) { - return getMeaningFromRightHandSideOfImportEquals(node); - } - else if (isDeclarationName(node)) { - return getMeaningFromDeclaration(node.parent); - } - else if (isTypeReference(node)) { - return SemanticMeaning.Type; - } - else if (isNamespaceReference(node)) { - return SemanticMeaning.Namespace; - } - else { - return SemanticMeaning.Value; - } - } - - function getMeaningFromRightHandSideOfImportEquals(node: Node) { - Debug.assert(node.kind === SyntaxKind.Identifier); - - // import a = |b|; // Namespace - // import a = |b.c|; // Value, type, namespace - // import a = |b.c|.d; // Namespace - - if (node.parent.kind === SyntaxKind.QualifiedName && - (node.parent).right === node && - node.parent.parent.kind === SyntaxKind.ImportEqualsDeclaration) { - return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; - } - return SemanticMeaning.Namespace; - } - - function isInRightSideOfImport(node: Node) { - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - return isInternalModuleImportEqualsDeclaration(node.parent) && (node.parent).moduleReference === node; - } - - function isNamespaceReference(node: Node): boolean { - return isQualifiedNameNamespaceReference(node) || isPropertyAccessNamespaceReference(node); - } - - function isQualifiedNameNamespaceReference(node: Node): boolean { - let root = node; - let isLastClause = true; - if (root.parent.kind === SyntaxKind.QualifiedName) { - while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) { - root = root.parent; - } - - isLastClause = (root).right === node; - } - - return root.parent.kind === SyntaxKind.TypeReference && !isLastClause; - } - - function isPropertyAccessNamespaceReference(node: Node): boolean { - let root = node; - let isLastClause = true; - if (root.parent.kind === SyntaxKind.PropertyAccessExpression) { - while (root.parent && root.parent.kind === SyntaxKind.PropertyAccessExpression) { - root = root.parent; - } - - isLastClause = (root).name === node; - } - - if (!isLastClause && root.parent.kind === SyntaxKind.ExpressionWithTypeArguments && root.parent.parent.kind === SyntaxKind.HeritageClause) { - const decl = root.parent.parent.parent; - return (decl.kind === SyntaxKind.ClassDeclaration && (root.parent.parent).token === SyntaxKind.ImplementsKeyword) || - (decl.kind === SyntaxKind.InterfaceDeclaration && (root.parent.parent).token === SyntaxKind.ExtendsKeyword); - } - - return false; - } - - function isTypeReference(node: Node): boolean { - if (isRightSideOfQualifiedNameOrPropertyAccess(node)) { - node = node.parent; - } - - return node.parent.kind === SyntaxKind.TypeReference || - (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(node.parent)) || - (node.kind === SyntaxKind.ThisKeyword && !isPartOfExpression(node)) || - node.kind === SyntaxKind.ThisType; - } -} diff --git a/src/services/services.ts b/src/services/services.ts index 1a59acf10d..e0c08a670c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3,7 +3,6 @@ /// /// -/// /// /// /// @@ -12,7 +11,6 @@ /// /// /// -/// /// /// /// @@ -21,6 +19,7 @@ /// /// /// +/// /// /// @@ -28,6 +27,637 @@ namespace ts { /** The version of the language service API */ export const servicesVersion = "0.5"; + function createNode(kind: SyntaxKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject | IdentifierObject { + const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) : + kind === SyntaxKind.Identifier ? new IdentifierObject(kind, pos, end) : + new TokenObject(kind, pos, end); + node.parent = parent; + return node; + } + + class NodeObject implements Node { + public kind: SyntaxKind; + public pos: number; + public end: number; + public flags: NodeFlags; + public parent: Node; + public jsDocComments: JSDocComment[]; + public original: Node; + public transformFlags: TransformFlags; + public excludeTransformFlags: TransformFlags; + private _children: Node[]; + + constructor(kind: SyntaxKind, pos: number, end: number) { + this.pos = pos; + this.end = end; + this.flags = NodeFlags.None; + this.transformFlags = undefined; + this.excludeTransformFlags = undefined; + this.parent = undefined; + this.kind = kind; + } + + public getSourceFile(): SourceFile { + return getSourceFileOfNode(this); + } + + public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { + return getTokenPosOfNode(this, sourceFile, includeJsDocComment); + } + + public getFullStart(): number { + return this.pos; + } + + public getEnd(): number { + return this.end; + } + + public getWidth(sourceFile?: SourceFile): number { + return this.getEnd() - this.getStart(sourceFile); + } + + public getFullWidth(): number { + return this.end - this.pos; + } + + public getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + public getFullText(sourceFile?: SourceFile): string { + return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); + } + + public getText(sourceFile?: SourceFile): string { + return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); + } + + private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number { + scanner.setTextPos(pos); + while (pos < end) { + const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan(); + const textPos = scanner.getTextPos(); + if (textPos <= end) { + nodes.push(createNode(token, pos, textPos, this)); + } + pos = textPos; + } + return pos; + } + + private createSyntaxList(nodes: NodeArray): Node { + const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, this); + list._children = []; + let pos = nodes.pos; + + for (const node of nodes) { + if (pos < node.pos) { + pos = this.addSyntheticNodes(list._children, pos, node.pos); + } + list._children.push(node); + pos = node.end; + } + if (pos < nodes.end) { + this.addSyntheticNodes(list._children, pos, nodes.end); + } + return list; + } + + private createChildren(sourceFile?: SourceFile) { + let children: Node[]; + if (this.kind >= SyntaxKind.FirstNode) { + scanner.setText((sourceFile || this.getSourceFile()).text); + children = []; + let pos = this.pos; + const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; + const processNode = (node: Node) => { + const isJSDocTagNode = isJSDocTag(node); + if (!isJSDocTagNode && pos < node.pos) { + pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); + } + children.push(node); + if (!isJSDocTagNode) { + pos = node.end; + } + }; + const processNodes = (nodes: NodeArray) => { + if (pos < nodes.pos) { + pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner); + } + children.push(this.createSyntaxList(>nodes)); + pos = nodes.end; + }; + // jsDocComments need to be the first children + if (this.jsDocComments) { + for (const jsDocComment of this.jsDocComments) { + processNode(jsDocComment); + } + } + // For syntactic classifications, all trivia are classcified together, including jsdoc comments. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. + pos = this.pos; + forEachChild(this, processNode, processNodes); + if (pos < this.end) { + this.addSyntheticNodes(children, pos, this.end); + } + scanner.setText(undefined); + } + this._children = children || emptyArray; + } + + public getChildCount(sourceFile?: SourceFile): number { + if (!this._children) this.createChildren(sourceFile); + return this._children.length; + } + + public getChildAt(index: number, sourceFile?: SourceFile): Node { + if (!this._children) this.createChildren(sourceFile); + return this._children[index]; + } + + public getChildren(sourceFile?: SourceFile): Node[] { + if (!this._children) this.createChildren(sourceFile); + return this._children; + } + + public getFirstToken(sourceFile?: SourceFile): Node { + const children = this.getChildren(sourceFile); + if (!children.length) { + return undefined; + } + + const child = children[0]; + + return child.kind < SyntaxKind.FirstNode ? child : child.getFirstToken(sourceFile); + } + + public getLastToken(sourceFile?: SourceFile): Node { + const children = this.getChildren(sourceFile); + + const child = lastOrUndefined(children); + if (!child) { + return undefined; + } + + return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); + } + } + + class TokenOrIdentifierObject implements Token { + public kind: SyntaxKind; + public pos: number; + public end: number; + public flags: NodeFlags; + public parent: Node; + public jsDocComments: JSDocComment[]; + public __tokenTag: any; + + constructor(pos: number, end: number) { + // Set properties in same order as NodeObject + this.pos = pos; + this.end = end; + this.flags = NodeFlags.None; + this.parent = undefined; + } + + public getSourceFile(): SourceFile { + return getSourceFileOfNode(this); + } + + public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { + return getTokenPosOfNode(this, sourceFile, includeJsDocComment); + } + + public getFullStart(): number { + return this.pos; + } + + public getEnd(): number { + return this.end; + } + + public getWidth(sourceFile?: SourceFile): number { + return this.getEnd() - this.getStart(sourceFile); + } + + public getFullWidth(): number { + return this.end - this.pos; + } + + public getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + public getFullText(sourceFile?: SourceFile): string { + return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); + } + + public getText(sourceFile?: SourceFile): string { + return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); + } + + public getChildCount(sourceFile?: SourceFile): number { + return 0; + } + + public getChildAt(index: number, sourceFile?: SourceFile): Node { + return undefined; + } + + public getChildren(sourceFile?: SourceFile): Node[] { + return emptyArray; + } + + public getFirstToken(sourceFile?: SourceFile): Node { + return undefined; + } + + public getLastToken(sourceFile?: SourceFile): Node { + return undefined; + } + } + + class SymbolObject implements Symbol { + flags: SymbolFlags; + name: string; + declarations: Declaration[]; + + // Undefined is used to indicate the value has not been computed. If, after computing, the + // symbol has no doc comment, then the empty string will be returned. + documentationComment: SymbolDisplayPart[]; + + constructor(flags: SymbolFlags, name: string) { + this.flags = flags; + this.name = name; + } + + getFlags(): SymbolFlags { + return this.flags; + } + + getName(): string { + return this.name; + } + + getDeclarations(): Declaration[] { + return this.declarations; + } + + getDocumentationComment(): SymbolDisplayPart[] { + if (this.documentationComment === undefined) { + this.documentationComment = JsDoc.getJsDocCommentsFromDeclarations(this.declarations, this.name, !(this.flags & SymbolFlags.Property)); + } + + return this.documentationComment; + } + } + + class TokenObject extends TokenOrIdentifierObject { + public kind: SyntaxKind; + constructor(kind: SyntaxKind, pos: number, end: number) { + super(pos, end); + this.kind = kind; + } + } + + class IdentifierObject extends TokenOrIdentifierObject { + constructor(kind: SyntaxKind, pos: number, end: number) { + super(pos, end); + } + } + IdentifierObject.prototype.kind = SyntaxKind.Identifier; + + class TypeObject implements Type { + checker: TypeChecker; + flags: TypeFlags; + id: number; + symbol: Symbol; + constructor(checker: TypeChecker, flags: TypeFlags) { + this.checker = checker; + this.flags = flags; + } + getFlags(): TypeFlags { + return this.flags; + } + getSymbol(): Symbol { + return this.symbol; + } + getProperties(): Symbol[] { + return this.checker.getPropertiesOfType(this); + } + getProperty(propertyName: string): Symbol { + return this.checker.getPropertyOfType(this, propertyName); + } + getApparentProperties(): Symbol[] { + return this.checker.getAugmentedPropertiesOfType(this); + } + getCallSignatures(): Signature[] { + return this.checker.getSignaturesOfType(this, SignatureKind.Call); + } + getConstructSignatures(): Signature[] { + return this.checker.getSignaturesOfType(this, SignatureKind.Construct); + } + getStringIndexType(): Type { + return this.checker.getIndexTypeOfType(this, IndexKind.String); + } + getNumberIndexType(): Type { + return this.checker.getIndexTypeOfType(this, IndexKind.Number); + } + getBaseTypes(): ObjectType[] { + return this.flags & (TypeFlags.Class | TypeFlags.Interface) + ? this.checker.getBaseTypes(this) + : undefined; + } + getNonNullableType(): Type { + return this.checker.getNonNullableType(this); + } + } + + class SignatureObject implements Signature { + checker: TypeChecker; + declaration: SignatureDeclaration; + typeParameters: TypeParameter[]; + parameters: Symbol[]; + thisParameter: Symbol; + resolvedReturnType: Type; + minArgumentCount: number; + hasRestParameter: boolean; + hasLiteralTypes: boolean; + + // Undefined is used to indicate the value has not been computed. If, after computing, the + // symbol has no doc comment, then the empty string will be returned. + documentationComment: SymbolDisplayPart[]; + + constructor(checker: TypeChecker) { + this.checker = checker; + } + getDeclaration(): SignatureDeclaration { + return this.declaration; + } + getTypeParameters(): Type[] { + return this.typeParameters; + } + getParameters(): Symbol[] { + return this.parameters; + } + getReturnType(): Type { + return this.checker.getReturnTypeOfSignature(this); + } + + getDocumentationComment(): SymbolDisplayPart[] { + if (this.documentationComment === undefined) { + this.documentationComment = this.declaration ? JsDoc.getJsDocCommentsFromDeclarations( + [this.declaration], + /*name*/ undefined, + /*canUseParsedParamTagComments*/ false) : []; + } + + return this.documentationComment; + } + } + + class SourceFileObject extends NodeObject implements SourceFile { + public _declarationBrand: any; + public fileName: string; + public path: Path; + public text: string; + public scriptSnapshot: IScriptSnapshot; + public lineMap: number[]; + + public statements: NodeArray; + public endOfFileToken: Node; + + public amdDependencies: { name: string; path: string }[]; + public moduleName: string; + public referencedFiles: FileReference[]; + public typeReferenceDirectives: FileReference[]; + + public syntacticDiagnostics: Diagnostic[]; + public referenceDiagnostics: Diagnostic[]; + public parseDiagnostics: Diagnostic[]; + public bindDiagnostics: Diagnostic[]; + + public isDeclarationFile: boolean; + public isDefaultLib: boolean; + public hasNoDefaultLib: boolean; + public externalModuleIndicator: Node; // The first node that causes this file to be an external module + public commonJsModuleIndicator: Node; // The first node that causes this file to be a CommonJS module + public nodeCount: number; + public identifierCount: number; + public symbolCount: number; + public version: string; + public scriptKind: ScriptKind; + public languageVersion: ScriptTarget; + public languageVariant: LanguageVariant; + public identifiers: Map; + public nameTable: Map; + public resolvedModules: Map; + public resolvedTypeReferenceDirectiveNames: Map; + public imports: LiteralExpression[]; + public moduleAugmentations: LiteralExpression[]; + private namedDeclarations: Map; + + constructor(kind: SyntaxKind, pos: number, end: number) { + super(kind, pos, end); + } + + public update(newText: string, textChangeRange: TextChangeRange): SourceFile { + return updateSourceFile(this, newText, textChangeRange); + } + + public getLineAndCharacterOfPosition(position: number): LineAndCharacter { + return ts.getLineAndCharacterOfPosition(this, position); + } + + public getLineStarts(): number[] { + return getLineStarts(this); + } + + public getPositionOfLineAndCharacter(line: number, character: number): number { + return ts.getPositionOfLineAndCharacter(this, line, character); + } + + public getNamedDeclarations(): Map { + if (!this.namedDeclarations) { + this.namedDeclarations = this.computeNamedDeclarations(); + } + + return this.namedDeclarations; + } + + private computeNamedDeclarations(): Map { + const result = createMap(); + + forEachChild(this, visit); + + return result; + + function addDeclaration(declaration: Declaration) { + const name = getDeclarationName(declaration); + if (name) { + multiMapAdd(result, name, declaration); + } + } + + function getDeclarations(name: string) { + return result[name] || (result[name] = []); + } + + function getDeclarationName(declaration: Declaration) { + if (declaration.name) { + const result = getTextOfIdentifierOrLiteral(declaration.name); + if (result !== undefined) { + return result; + } + + if (declaration.name.kind === SyntaxKind.ComputedPropertyName) { + const expr = (declaration.name).expression; + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + return (expr).name.text; + } + + return getTextOfIdentifierOrLiteral(expr); + } + } + + return undefined; + } + + function getTextOfIdentifierOrLiteral(node: Node) { + if (node) { + if (node.kind === SyntaxKind.Identifier || + node.kind === SyntaxKind.StringLiteral || + node.kind === SyntaxKind.NumericLiteral) { + + return (node).text; + } + } + + return undefined; + } + + function visit(node: Node): void { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + const functionDeclaration = node; + const declarationName = getDeclarationName(functionDeclaration); + + if (declarationName) { + const declarations = getDeclarations(declarationName); + const lastDeclaration = lastOrUndefined(declarations); + + // Check whether this declaration belongs to an "overload group". + if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { + // Overwrite the last declaration if it was an overload + // and this one is an implementation. + if (functionDeclaration.body && !(lastDeclaration).body) { + declarations[declarations.length - 1] = functionDeclaration; + } + } + else { + declarations.push(functionDeclaration); + } + + forEachChild(node, visit); + } + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ExportSpecifier: + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ImportClause: + case SyntaxKind.NamespaceImport: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.TypeLiteral: + addDeclaration(node); + forEachChild(node, visit); + break; + + case SyntaxKind.Parameter: + // Only consider parameter properties + if (!hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { + break; + } + // fall through + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: { + const decl = node; + if (isBindingPattern(decl.name)) { + forEachChild(decl.name, visit); + break; + } + if (decl.initializer) + visit(decl.initializer); + } + case SyntaxKind.EnumMember: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + addDeclaration(node); + break; + + case SyntaxKind.ExportDeclaration: + // Handle named exports case e.g.: + // export {a, b as B} from "mod"; + if ((node).exportClause) { + forEach((node).exportClause.elements, visit); + } + break; + + case SyntaxKind.ImportDeclaration: + const importClause = (node).importClause; + if (importClause) { + // Handle default import case e.g.: + // import d from "mod"; + if (importClause.name) { + addDeclaration(importClause); + } + + // Handle named bindings in imports e.g.: + // import * as NS from "mod"; + // import {a, b as B} from "mod"; + if (importClause.namedBindings) { + if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + addDeclaration(importClause.namedBindings); + } + else { + forEach((importClause.namedBindings).elements, visit); + } + } + } + break; + + default: + forEachChild(node, visit); + } + } + } + } + + function getServicesObjectAllocator(): ObjectAllocator { + return { + getNodeConstructor: () => NodeObject, + getTokenConstructor: () => TokenObject, + getIdentifierConstructor: () => IdentifierObject, + getSourceFileConstructor: () => SourceFileObject, + getSymbolConstructor: () => SymbolObject, + getTypeConstructor: () => TypeObject, + getSignatureConstructor: () => SignatureObject, + }; + } + /// Language Service // Information about a specific host file. @@ -202,159 +832,6 @@ namespace ts { sourceFile.scriptSnapshot = scriptSnapshot; } - export interface TranspileOptions { - compilerOptions?: CompilerOptions; - fileName?: string; - reportDiagnostics?: boolean; - moduleName?: string; - renamedDependencies?: MapLike; - } - - export interface TranspileOutput { - outputText: string; - diagnostics?: Diagnostic[]; - sourceMapText?: string; - } - - let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[]; - - /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ - function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { - // Lazily create this value to fix module loading errors. - commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number")); - - options = clone(options); - - for (const opt of commandLineOptionsStringToEnum) { - if (!hasProperty(options, opt.name)) { - continue; - } - - const value = options[opt.name]; - // Value should be a key of opt.type - if (typeof value === "string") { - // If value is not a string, this will fail - options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); - } - else { - if (!forEachProperty(opt.type, v => v === value)) { - // Supplied value isn't a valid enum value. - diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); - } - } - } - - return options; - } - - /* - * 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. - * Extra compiler options that will unconditionally be used by this function are: - * - isolatedModules = true - * - allowNonTsExtensions = true - * - noLib = true - * - noResolve = true - */ - export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { - const diagnostics: Diagnostic[] = []; - - const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions(); - - options.isolatedModules = true; - - // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. - options.suppressOutputPathCheck = true; - - // Filename can be non-ts file. - options.allowNonTsExtensions = true; - - // 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; - - // Clear out other settings that would not be used in transpiling this module - options.lib = undefined; - options.types = undefined; - options.noEmit = undefined; - options.noEmitOnError = undefined; - options.paths = undefined; - options.rootDirs = undefined; - options.declaration = undefined; - options.declarationDir = undefined; - options.out = undefined; - options.outFile = undefined; - - // We are not doing a full typecheck, we are not resolving the whole context, - // so pass --noResolve to avoid reporting missing file errors. - options.noResolve = true; - - // if jsx is specified then treat file as .tsx - const inputFileName = transpileOptions.fileName || (options.jsx ? "module.tsx" : "module.ts"); - const sourceFile = createSourceFile(inputFileName, input, options.target); - if (transpileOptions.moduleName) { - sourceFile.moduleName = transpileOptions.moduleName; - } - - if (transpileOptions.renamedDependencies) { - sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); - } - - const newLine = getNewLineCharacter(options); - - // Output - let outputText: string; - let sourceMapText: string; - - // Create a compilerHost object to allow the compiler to read and write files - const compilerHost: CompilerHost = { - getSourceFile: (fileName, target) => fileName === normalizePath(inputFileName) ? sourceFile : undefined, - writeFile: (name, text, writeByteOrderMark) => { - if (fileExtensionIs(name, ".map")) { - Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`); - sourceMapText = text; - } - else { - Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`); - outputText = text; - } - }, - getDefaultLibFileName: () => "lib.d.ts", - useCaseSensitiveFileNames: () => false, - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: () => "", - getNewLine: () => newLine, - fileExists: (fileName): boolean => fileName === inputFileName, - readFile: (fileName): string => "", - directoryExists: directoryExists => true, - getDirectories: (path: string) => [] - }; - - const program = createProgram([inputFileName], options, compilerHost); - - if (transpileOptions.reportDiagnostics) { - addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); - addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); - } - // Emit - program.emit(); - - Debug.assert(outputText !== undefined, "Output generation failed"); - - return { outputText, diagnostics, sourceMapText }; - } - - /* - * 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 { - const output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName }); - // addRange correctly handles cases when wither 'from' or 'to' argument is missing - addRange(diagnostics, output.diagnostics); - return output.outputText; - } - export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile { const text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); @@ -1535,7 +2012,7 @@ namespace ts { } function initializeServices() { - objectAllocator = Allocators.getServicesObjectAllocator(); + objectAllocator = getServicesObjectAllocator(); } initializeServices(); diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 1a2319c272..8b4d986df5 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -85,7 +85,7 @@ namespace ts.SymbolDisplay { // TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node, - location: Node, semanticMeaning = Meaning.getMeaningFromLocation(location)) { + location: Node, semanticMeaning = getMeaningFromLocation(location)) { const displayParts: SymbolDisplayPart[] = []; let documentation: SymbolDisplayPart[]; @@ -229,7 +229,7 @@ namespace ts.SymbolDisplay { addFullSymbolName(symbol); writeTypeParametersOfSymbol(symbol, sourceFile); } - if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & Meaning.SemanticMeaning.Type)) { + if ((symbolFlags & SymbolFlags.Interface) && (semanticMeaning & SemanticMeaning.Type)) { addNewLineIfDisplayPartsExist(); displayParts.push(keywordPart(SyntaxKind.InterfaceKeyword)); displayParts.push(spacePart()); @@ -265,7 +265,7 @@ namespace ts.SymbolDisplay { displayParts.push(spacePart()); addFullSymbolName(symbol); } - if ((symbolFlags & SymbolFlags.TypeParameter) && (semanticMeaning & Meaning.SemanticMeaning.Type)) { + if ((symbolFlags & SymbolFlags.TypeParameter) && (semanticMeaning & SemanticMeaning.Type)) { addNewLineIfDisplayPartsExist(); displayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); displayParts.push(textPart("type parameter")); diff --git a/src/services/transpile.ts b/src/services/transpile.ts new file mode 100644 index 0000000000..303ca0d417 --- /dev/null +++ b/src/services/transpile.ts @@ -0,0 +1,154 @@ +namespace ts { + export interface TranspileOptions { + compilerOptions?: CompilerOptions; + fileName?: string; + reportDiagnostics?: boolean; + moduleName?: string; + renamedDependencies?: MapLike; + } + + 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. + * Extra compiler options that will unconditionally be used by this function are: + * - isolatedModules = true + * - allowNonTsExtensions = true + * - noLib = true + * - noResolve = true + */ + export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { + const diagnostics: Diagnostic[] = []; + + const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions(); + + options.isolatedModules = true; + + // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. + options.suppressOutputPathCheck = true; + + // Filename can be non-ts file. + options.allowNonTsExtensions = true; + + // 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; + + // Clear out other settings that would not be used in transpiling this module + options.lib = undefined; + options.types = undefined; + options.noEmit = undefined; + options.noEmitOnError = undefined; + options.paths = undefined; + options.rootDirs = undefined; + options.declaration = undefined; + options.declarationDir = undefined; + options.out = undefined; + options.outFile = undefined; + + // We are not doing a full typecheck, we are not resolving the whole context, + // so pass --noResolve to avoid reporting missing file errors. + options.noResolve = true; + + // if jsx is specified then treat file as .tsx + const inputFileName = transpileOptions.fileName || (options.jsx ? "module.tsx" : "module.ts"); + const sourceFile = createSourceFile(inputFileName, input, options.target); + if (transpileOptions.moduleName) { + sourceFile.moduleName = transpileOptions.moduleName; + } + + if (transpileOptions.renamedDependencies) { + sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); + } + + const newLine = getNewLineCharacter(options); + + // Output + let outputText: string; + let sourceMapText: string; + + // Create a compilerHost object to allow the compiler to read and write files + const compilerHost: CompilerHost = { + getSourceFile: (fileName, target) => fileName === normalizePath(inputFileName) ? sourceFile : undefined, + writeFile: (name, text, writeByteOrderMark) => { + if (fileExtensionIs(name, ".map")) { + Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`); + sourceMapText = text; + } + else { + Debug.assert(outputText === undefined, `Unexpected multiple outputs for the file: '${name}'`); + outputText = text; + } + }, + getDefaultLibFileName: () => "lib.d.ts", + useCaseSensitiveFileNames: () => false, + getCanonicalFileName: fileName => fileName, + getCurrentDirectory: () => "", + getNewLine: () => newLine, + fileExists: (fileName): boolean => fileName === inputFileName, + readFile: (fileName): string => "", + directoryExists: directoryExists => true, + getDirectories: (path: string) => [] + }; + + const program = createProgram([inputFileName], options, compilerHost); + + if (transpileOptions.reportDiagnostics) { + addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); + addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); + } + // Emit + program.emit(); + + Debug.assert(outputText !== undefined, "Output generation failed"); + + return { outputText, diagnostics, sourceMapText }; + } + + /* + * 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 { + const output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName }); + // addRange correctly handles cases when wither 'from' or 'to' argument is missing + addRange(diagnostics, output.diagnostics); + return output.outputText; + } + + let commandLineOptionsStringToEnum: CommandLineOptionOfCustomType[]; + + /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ + function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { + // Lazily create this value to fix module loading errors. + commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => + typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number")); + + options = clone(options); + + for (const opt of commandLineOptionsStringToEnum) { + if (!hasProperty(options, opt.name)) { + continue; + } + + const value = options[opt.name]; + // Value should be a key of opt.type + if (typeof value === "string") { + // If value is not a string, this will fail + options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); + } + else { + if (!forEachProperty(opt.type, v => v === value)) { + // Supplied value isn't a valid enum value. + diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); + } + } + } + + return options; + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index efcc7df9d4..d0d3c88fbc 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -42,7 +42,6 @@ "../compiler/diagnosticInformationMap.generated.ts", "types.ts", "utilities.ts", - "allocators.ts", "breakpoints.ts", "classifier.ts", "completions.ts", @@ -51,7 +50,6 @@ "goToDefinition.ts", "jsDoc.ts", "jsTyping.ts", - "meaning.ts", "navigateTo.ts", "navigationBar.ts", "outliningElementsCollector.ts", @@ -59,6 +57,7 @@ "preProcess.ts", "rename.ts", "services.ts", + "transpile.ts", "shims.ts", "signatureHelp.ts", "symbolDisplay.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 9e198a8819..ba419e4fec 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -4,6 +4,164 @@ namespace ts { export const scanner: Scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true); export const emptyArray: any[] = []; + export const enum SemanticMeaning { + None = 0x0, + Value = 0x1, + Type = 0x2, + Namespace = 0x4, + All = Value | Type | Namespace + } + + export function getMeaningFromDeclaration(node: Node): SemanticMeaning { + switch (node.kind) { + case SyntaxKind.Parameter: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.EnumMember: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.CatchClause: + return SemanticMeaning.Value; + + case SyntaxKind.TypeParameter: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.TypeLiteral: + return SemanticMeaning.Type; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + return SemanticMeaning.Value | SemanticMeaning.Type; + + case SyntaxKind.ModuleDeclaration: + if (isAmbientModule(node)) { + return SemanticMeaning.Namespace | SemanticMeaning.Value; + } + else if (getModuleInstanceState(node) === ModuleInstanceState.Instantiated) { + return SemanticMeaning.Namespace | SemanticMeaning.Value; + } + else { + return SemanticMeaning.Namespace; + } + + case SyntaxKind.NamedImports: + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportAssignment: + case SyntaxKind.ExportDeclaration: + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + + // An external module can be a Value + case SyntaxKind.SourceFile: + return SemanticMeaning.Namespace | SemanticMeaning.Value; + } + + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + + export function getMeaningFromLocation(node: Node): SemanticMeaning { + if (node.parent.kind === SyntaxKind.ExportAssignment) { + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + else if (isInRightSideOfImport(node)) { + return getMeaningFromRightHandSideOfImportEquals(node); + } + else if (isDeclarationName(node)) { + return getMeaningFromDeclaration(node.parent); + } + else if (isTypeReference(node)) { + return SemanticMeaning.Type; + } + else if (isNamespaceReference(node)) { + return SemanticMeaning.Namespace; + } + else { + return SemanticMeaning.Value; + } + } + + function getMeaningFromRightHandSideOfImportEquals(node: Node) { + Debug.assert(node.kind === SyntaxKind.Identifier); + + // import a = |b|; // Namespace + // import a = |b.c|; // Value, type, namespace + // import a = |b.c|.d; // Namespace + + if (node.parent.kind === SyntaxKind.QualifiedName && + (node.parent).right === node && + node.parent.parent.kind === SyntaxKind.ImportEqualsDeclaration) { + return SemanticMeaning.Value | SemanticMeaning.Type | SemanticMeaning.Namespace; + } + return SemanticMeaning.Namespace; + } + + function isInRightSideOfImport(node: Node) { + while (node.parent.kind === SyntaxKind.QualifiedName) { + node = node.parent; + } + return isInternalModuleImportEqualsDeclaration(node.parent) && (node.parent).moduleReference === node; + } + + function isNamespaceReference(node: Node): boolean { + return isQualifiedNameNamespaceReference(node) || isPropertyAccessNamespaceReference(node); + } + + function isQualifiedNameNamespaceReference(node: Node): boolean { + let root = node; + let isLastClause = true; + if (root.parent.kind === SyntaxKind.QualifiedName) { + while (root.parent && root.parent.kind === SyntaxKind.QualifiedName) { + root = root.parent; + } + + isLastClause = (root).right === node; + } + + return root.parent.kind === SyntaxKind.TypeReference && !isLastClause; + } + + function isPropertyAccessNamespaceReference(node: Node): boolean { + let root = node; + let isLastClause = true; + if (root.parent.kind === SyntaxKind.PropertyAccessExpression) { + while (root.parent && root.parent.kind === SyntaxKind.PropertyAccessExpression) { + root = root.parent; + } + + isLastClause = (root).name === node; + } + + if (!isLastClause && root.parent.kind === SyntaxKind.ExpressionWithTypeArguments && root.parent.parent.kind === SyntaxKind.HeritageClause) { + const decl = root.parent.parent.parent; + return (decl.kind === SyntaxKind.ClassDeclaration && (root.parent.parent).token === SyntaxKind.ImplementsKeyword) || + (decl.kind === SyntaxKind.InterfaceDeclaration && (root.parent.parent).token === SyntaxKind.ExtendsKeyword); + } + + return false; + } + + function isTypeReference(node: Node): boolean { + if (isRightSideOfQualifiedNameOrPropertyAccess(node)) { + node = node.parent; + } + + return node.parent.kind === SyntaxKind.TypeReference || + (node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && !isExpressionWithTypeArgumentsInClassExtendsClause(node.parent)) || + (node.kind === SyntaxKind.ThisKeyword && !isPartOfExpression(node)) || + node.kind === SyntaxKind.ThisType; + } + export function isCallExpressionTarget(node: Node): boolean { return isCallOrNewExpressionTarget(node, SyntaxKind.CallExpression); }