Added printer

This commit is contained in:
Ron Buckton 2016-02-09 13:43:40 -08:00
parent 0f2bbb181f
commit 6b381ecdbd
9 changed files with 2730 additions and 53 deletions

View File

@ -44,6 +44,8 @@ var compilerSources = [
"visitor.ts",
"transformer.ts",
"sourcemap.ts",
"comments.ts",
"printer.ts",
"declarationEmitter.ts",
"emitter.ts",
"program.ts",
@ -67,6 +69,8 @@ var servicesSources = [
"visitor.ts",
"transformer.ts",
"sourcemap.ts",
"comments.ts",
"printer.ts",
"declarationEmitter.ts",
"emitter.ts",
"program.ts",

214
src/compiler/comments.ts Normal file
View File

@ -0,0 +1,214 @@
/// <reference path="sourcemap.ts" />
/* @internal */
namespace ts {
export interface CommentWriter {
reset(): void;
setSourceFile(sourceFile: SourceFile): void;
getLeadingCommentsToEmit(node: TextRange): CommentRange[];
getTrailingCommentsToEmit(node: TextRange): CommentRange[];
emitDetachedComments(node: TextRange): void;
emitLeadingComments(node: TextRange, comments?: CommentRange[]): void;
emitTrailingComments(node: TextRange, comments?: CommentRange[]): void;
}
export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
const compilerOptions = host.getCompilerOptions();
const newLine = host.getNewLine();
const { emitPos } = sourceMap;
let currentSourceFile: SourceFile;
let currentText: string;
let currentLineMap: number[];
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
// This maps start->end for a comment range. See `hasConsumedCommentRange` and
// `consumeCommentRange` for usage.
let consumedCommentRanges: number[];
let leadingCommentRangeNodeStarts: boolean[];
let trailingCommentRangeNodeEnds: boolean[];
return compilerOptions.removeComments
? createCommentRemovingWriter()
: createCommentPreservingWriter();
function createCommentRemovingWriter(): CommentWriter {
return {
reset,
setSourceFile,
getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
emitDetachedComments,
emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { },
emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { },
};
function emitDetachedComments(node: TextRange): void {
emitDetachedCommentsAndUpdateCommentsInfo(node, /*removeComments*/ true);
}
}
function createCommentPreservingWriter(): CommentWriter {
const noComments: CommentRange[] = [];
return {
reset,
setSourceFile,
getLeadingCommentsToEmit,
getTrailingCommentsToEmit,
emitDetachedComments,
emitLeadingComments,
emitTrailingComments,
};
function getLeadingCommentsToEmit(node: TextRange) {
if (nodeIsSynthesized(node)) {
return;
}
if (!leadingCommentRangeNodeStarts[node.pos]) {
leadingCommentRangeNodeStarts[node.pos] = true;
const comments = hasDetachedComments(node.pos)
? getLeadingCommentsWithoutDetachedComments()
: getLeadingCommentRanges(currentText, node.pos);
return consumeCommentRanges(comments);
}
return noComments;
}
function getTrailingCommentsToEmit(node: TextRange) {
if (nodeIsSynthesized(node)) {
return;
}
if (!trailingCommentRangeNodeEnds[node.end]) {
trailingCommentRangeNodeEnds[node.end] = true;
const comments = getTrailingCommentRanges(currentText, node.end);
return consumeCommentRanges(comments);
}
return noComments;
}
function hasConsumedCommentRange(comment: CommentRange) {
return comment.end === consumedCommentRanges[comment.pos];
}
function consumeCommentRange(comment: CommentRange) {
if (!hasConsumedCommentRange(comment)) {
consumedCommentRanges[comment.pos] = comment.end;
return true;
}
return false;
}
function consumeCommentRanges(comments: CommentRange[]) {
let consumed: CommentRange[];
if (comments) {
let commentsSkipped = 0;
let commentsConsumed = 0;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
if (consumeCommentRange(comment)) {
commentsConsumed++;
if (commentsSkipped !== 0) {
if (consumed === undefined) {
consumed = [comment];
}
else {
consumed.push(comment);
}
}
}
else {
commentsSkipped++;
if (commentsConsumed !== 0 && consumed === undefined) {
consumed = comments.slice(0, i);
}
}
}
if (commentsConsumed) {
return consumed || comments;
}
}
return noComments;
}
function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) {
emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments);
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
}
function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) {
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment);
}
function emitDetachedComments(range: TextRange) {
emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false);
}
}
function reset() {
currentSourceFile = undefined;
currentText = undefined;
currentLineMap = undefined;
detachedCommentsInfo = undefined;
consumedCommentRanges = undefined;
trailingCommentRangeNodeEnds = undefined;
leadingCommentRangeNodeStarts = undefined;
}
function setSourceFile(sourceFile: SourceFile) {
currentSourceFile = sourceFile;
currentText = sourceFile.text;
currentLineMap = getLineStarts(sourceFile);
detachedCommentsInfo = undefined;
consumedCommentRanges = [];
leadingCommentRangeNodeStarts = [];
trailingCommentRangeNodeEnds = [];
}
function hasDetachedComments(pos: number) {
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
}
function getLeadingCommentsWithoutDetachedComments() {
// get the leading comments from detachedPos
const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
const leadingComments = getLeadingCommentRanges(currentText, pos);
if (detachedCommentsInfo.length - 1) {
detachedCommentsInfo.pop();
}
else {
detachedCommentsInfo = undefined;
}
return leadingComments;
}
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) {
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments);
if (currentDetachedCommentInfo) {
if (detachedCommentsInfo) {
detachedCommentsInfo.push(currentDetachedCommentInfo);
}
else {
detachedCommentsInfo = [currentDetachedCommentInfo];
}
}
}
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
emitPos(comment.pos);
writeCommentRange(text, lineMap, writer, comment, newLine);
emitPos(comment.end);
}
}
}

View File

@ -895,7 +895,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
const text = getLiteralText(node);
const text = getLiteralText(node, currentSourceFile, languageVersion);
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
writer.writeLiteral(text);
@ -909,43 +909,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}
}
function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
// Any template literal or string literal with an extended escape
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
return getQuotedEscapedLiteralText("\"", node.text, "\"");
}
// If we don't need to downlevel and we can reach the original source text using
// the node's parent reference, then simply get the text as it was originally written.
if (node.parent) {
return getTextOfNodeFromSourceText(currentText, node);
}
// If we can't reach the original source text, use the canonical form if it's a number,
// or an escaped quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return getQuotedEscapedLiteralText("\"", node.text, "\"");
case SyntaxKind.NoSubstitutionTemplateLiteral:
return getQuotedEscapedLiteralText("`", node.text, "`");
case SyntaxKind.TemplateHead:
return getQuotedEscapedLiteralText("`", node.text, "${");
case SyntaxKind.TemplateMiddle:
return getQuotedEscapedLiteralText("}", node.text, "${");
case SyntaxKind.TemplateTail:
return getQuotedEscapedLiteralText("}", node.text, "`");
case SyntaxKind.NumericLiteral:
return node.text;
}
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
}
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
}
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
// Find original source text, since we need to emit the raw strings of the tagged template.
// The raw strings contain the (escaped) strings of what the user wrote.
@ -6590,7 +6553,8 @@ const _super = (function (geti, seti) {
}
const moduleName = getExternalModuleName(importNode);
if (moduleName.kind === SyntaxKind.StringLiteral) {
return tryRenameExternalModule(<LiteralExpression>moduleName) || getLiteralText(<LiteralExpression>moduleName);
return tryRenameExternalModule(<LiteralExpression>moduleName)
|| getLiteralText(<LiteralExpression>moduleName, currentSourceFile, languageVersion);
}
return undefined;

View File

@ -144,14 +144,9 @@ namespace ts {
export function createLiteral(value: string): StringLiteral;
export function createLiteral(value: number): LiteralExpression;
export function createLiteral(value: string | number | boolean | void): PrimaryExpression;
export function createLiteral<T extends PrimaryExpression>(value: string | number | boolean | void): T {
if (typeof value === "string") {
const node = <T & StringLiteral>createNode(SyntaxKind.StringLiteral);
node.text = value;
return node;
}
else if (typeof value === "number") {
export function createLiteral(value: string | number | boolean): PrimaryExpression;
export function createLiteral<T extends PrimaryExpression>(value: string | number | boolean): T {
if (typeof value === "number") {
const node = <T & LiteralExpression>createNode(SyntaxKind.NumericLiteral);
node.text = value.toString();
return node;
@ -159,8 +154,10 @@ namespace ts {
else if (typeof value === "boolean") {
return <T>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
}
else if (value === null) {
return <T>createNode(SyntaxKind.NullKeyword);
else {
const node = <T & StringLiteral>createNode(SyntaxKind.StringLiteral);
node.text = String(value);
return node;
}
}
@ -254,9 +251,6 @@ namespace ts {
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return createLiteral(value);
}
else if (value === null) {
return createLiteral(null);
}
else {
return value;
}

2421
src/compiler/printer.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,12 @@
/* @internal */
namespace ts {
export function getTransformers(compilerOptions: CompilerOptions) {
const transformers: Transformer[] = [];
// TODO(rbuckton): Add transformers
return transformers;
}
/**
* Transforms an array of SourceFiles by passing them through each transformer.
*

View File

@ -20,6 +20,9 @@
"factory.ts",
"visitor.ts",
"transformer.ts",
"comments.ts",
"printer.ts",
"declarationEmitter.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",

View File

@ -2795,6 +2795,7 @@ namespace ts {
UMDDefine = 1 << 2, // This node should be replaced with the UMD define helper.
NoLexicalEnvironment = 1 << 3, // A new LexicalEnvironment should *not* be introduced when emitting this node.
SingleLine = 1 << 4, // The contents of this node should be emit on a single line.
MultiLine = 1 << 5, // The contents of this node should be emit on multiple lines.
}
/** Additional context provided to `visitEachChild` */

View File

@ -102,6 +102,22 @@ namespace ts {
return true;
}
export function getLanguageVersion(compilerOptions: CompilerOptions) {
return compilerOptions.target || ScriptTarget.ES3;
}
export function getModuleKind(compilerOptions: CompilerOptions) {
if (compilerOptions.module) {
return compilerOptions.module;
}
if (getLanguageVersion(compilerOptions) === ScriptTarget.ES6) {
return ModuleKind.ES6;
}
return ModuleKind.None;
}
export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean {
return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText);
}
@ -242,6 +258,60 @@ namespace ts {
return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node, includeTrivia);
}
export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, languageVersion: ScriptTarget) {
// Any template literal or string literal with an extended escape
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
return getQuotedEscapedLiteralText("\"", node.text, "\"");
}
// If we don't need to downlevel and we can reach the original source text using
// the node's parent reference, then simply get the text as it was originally written.
if (!nodeIsSynthesized(node) && node.parent) {
const text = getSourceTextOfNodeFromSourceFile(sourceFile, node);
if (languageVersion < ScriptTarget.ES6 && isBinaryOrOctalIntegerLiteral(node, text)) {
return node.text;
}
return text;
}
// If we can't reach the original source text, use the canonical form if it's a number,
// or an escaped quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return getQuotedEscapedLiteralText("\"", node.text, "\"");
case SyntaxKind.NoSubstitutionTemplateLiteral:
return getQuotedEscapedLiteralText("`", node.text, "`");
case SyntaxKind.TemplateHead:
return getQuotedEscapedLiteralText("`", node.text, "${");
case SyntaxKind.TemplateMiddle:
return getQuotedEscapedLiteralText("}", node.text, "${");
case SyntaxKind.TemplateTail:
return getQuotedEscapedLiteralText("}", node.text, "`");
case SyntaxKind.NumericLiteral:
return node.text;
}
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
}
export function isBinaryOrOctalIntegerLiteral(node: LiteralLikeNode, text: string) {
if (node.kind === SyntaxKind.NumericLiteral && text.length > 1) {
switch (text.charCodeAt(1)) {
case CharacterCodes.b:
case CharacterCodes.B:
case CharacterCodes.o:
case CharacterCodes.O:
return true;
}
}
return false;
}
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
}
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
export function escapeIdentifier(identifier: string): string {
return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier;
@ -2765,7 +2835,7 @@ namespace ts {
return isUnaryExpressionKind(node.kind);
}
function isExpressionKind(kind: SyntaxKind): boolean {
export function isExpressionKind(kind: SyntaxKind): boolean {
return kind === SyntaxKind.ConditionalExpression
|| kind === SyntaxKind.YieldExpression
|| kind === SyntaxKind.ArrowFunction