Skip to content

Commit 6b381ec

Browse files
committed
Added printer
1 parent 0f2bbb1 commit 6b381ec

9 files changed

Lines changed: 2730 additions & 53 deletions

File tree

Jakefile.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ var compilerSources = [
4444
"visitor.ts",
4545
"transformer.ts",
4646
"sourcemap.ts",
47+
"comments.ts",
48+
"printer.ts",
4749
"declarationEmitter.ts",
4850
"emitter.ts",
4951
"program.ts",
@@ -67,6 +69,8 @@ var servicesSources = [
6769
"visitor.ts",
6870
"transformer.ts",
6971
"sourcemap.ts",
72+
"comments.ts",
73+
"printer.ts",
7074
"declarationEmitter.ts",
7175
"emitter.ts",
7276
"program.ts",

src/compiler/comments.ts

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/// <reference path="sourcemap.ts" />
2+
3+
/* @internal */
4+
namespace ts {
5+
export interface CommentWriter {
6+
reset(): void;
7+
setSourceFile(sourceFile: SourceFile): void;
8+
getLeadingCommentsToEmit(node: TextRange): CommentRange[];
9+
getTrailingCommentsToEmit(node: TextRange): CommentRange[];
10+
emitDetachedComments(node: TextRange): void;
11+
emitLeadingComments(node: TextRange, comments?: CommentRange[]): void;
12+
emitTrailingComments(node: TextRange, comments?: CommentRange[]): void;
13+
}
14+
15+
export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
16+
const compilerOptions = host.getCompilerOptions();
17+
const newLine = host.getNewLine();
18+
const { emitPos } = sourceMap;
19+
20+
let currentSourceFile: SourceFile;
21+
let currentText: string;
22+
let currentLineMap: number[];
23+
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
24+
25+
// This maps start->end for a comment range. See `hasConsumedCommentRange` and
26+
// `consumeCommentRange` for usage.
27+
let consumedCommentRanges: number[];
28+
let leadingCommentRangeNodeStarts: boolean[];
29+
let trailingCommentRangeNodeEnds: boolean[];
30+
31+
return compilerOptions.removeComments
32+
? createCommentRemovingWriter()
33+
: createCommentPreservingWriter();
34+
35+
function createCommentRemovingWriter(): CommentWriter {
36+
return {
37+
reset,
38+
setSourceFile,
39+
getLeadingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
40+
getTrailingCommentsToEmit(node: TextRange): CommentRange[] { return undefined; },
41+
emitDetachedComments,
42+
emitLeadingComments(node: TextRange, comments?: CommentRange[]): void { },
43+
emitTrailingComments(node: TextRange, comments?: CommentRange[]): void { },
44+
};
45+
46+
function emitDetachedComments(node: TextRange): void {
47+
emitDetachedCommentsAndUpdateCommentsInfo(node, /*removeComments*/ true);
48+
}
49+
}
50+
51+
function createCommentPreservingWriter(): CommentWriter {
52+
const noComments: CommentRange[] = [];
53+
return {
54+
reset,
55+
setSourceFile,
56+
getLeadingCommentsToEmit,
57+
getTrailingCommentsToEmit,
58+
emitDetachedComments,
59+
emitLeadingComments,
60+
emitTrailingComments,
61+
};
62+
63+
function getLeadingCommentsToEmit(node: TextRange) {
64+
if (nodeIsSynthesized(node)) {
65+
return;
66+
}
67+
68+
if (!leadingCommentRangeNodeStarts[node.pos]) {
69+
leadingCommentRangeNodeStarts[node.pos] = true;
70+
const comments = hasDetachedComments(node.pos)
71+
? getLeadingCommentsWithoutDetachedComments()
72+
: getLeadingCommentRanges(currentText, node.pos);
73+
return consumeCommentRanges(comments);
74+
}
75+
76+
return noComments;
77+
}
78+
79+
function getTrailingCommentsToEmit(node: TextRange) {
80+
if (nodeIsSynthesized(node)) {
81+
return;
82+
}
83+
84+
if (!trailingCommentRangeNodeEnds[node.end]) {
85+
trailingCommentRangeNodeEnds[node.end] = true;
86+
const comments = getTrailingCommentRanges(currentText, node.end);
87+
return consumeCommentRanges(comments);
88+
}
89+
90+
return noComments;
91+
}
92+
93+
function hasConsumedCommentRange(comment: CommentRange) {
94+
return comment.end === consumedCommentRanges[comment.pos];
95+
}
96+
97+
function consumeCommentRange(comment: CommentRange) {
98+
if (!hasConsumedCommentRange(comment)) {
99+
consumedCommentRanges[comment.pos] = comment.end;
100+
return true;
101+
}
102+
103+
return false;
104+
}
105+
106+
function consumeCommentRanges(comments: CommentRange[]) {
107+
let consumed: CommentRange[];
108+
if (comments) {
109+
let commentsSkipped = 0;
110+
let commentsConsumed = 0;
111+
for (let i = 0; i < comments.length; i++) {
112+
const comment = comments[i];
113+
if (consumeCommentRange(comment)) {
114+
commentsConsumed++;
115+
if (commentsSkipped !== 0) {
116+
if (consumed === undefined) {
117+
consumed = [comment];
118+
}
119+
else {
120+
consumed.push(comment);
121+
}
122+
}
123+
}
124+
else {
125+
commentsSkipped++;
126+
if (commentsConsumed !== 0 && consumed === undefined) {
127+
consumed = comments.slice(0, i);
128+
}
129+
}
130+
}
131+
132+
if (commentsConsumed) {
133+
return consumed || comments;
134+
}
135+
}
136+
137+
return noComments;
138+
}
139+
140+
function emitLeadingComments(range: TextRange, leadingComments: CommentRange[] = getLeadingCommentsToEmit(range)) {
141+
emitNewLineBeforeLeadingComments(currentLineMap, writer, range, leadingComments);
142+
143+
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
144+
emitComments(currentText, currentLineMap, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
145+
}
146+
147+
function emitTrailingComments(range: TextRange, trailingComments = getTrailingCommentsToEmit(range)) {
148+
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
149+
emitComments(currentText, currentLineMap, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment);
150+
}
151+
152+
function emitDetachedComments(range: TextRange) {
153+
emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false);
154+
}
155+
}
156+
157+
function reset() {
158+
currentSourceFile = undefined;
159+
currentText = undefined;
160+
currentLineMap = undefined;
161+
detachedCommentsInfo = undefined;
162+
consumedCommentRanges = undefined;
163+
trailingCommentRangeNodeEnds = undefined;
164+
leadingCommentRangeNodeStarts = undefined;
165+
}
166+
167+
function setSourceFile(sourceFile: SourceFile) {
168+
currentSourceFile = sourceFile;
169+
currentText = sourceFile.text;
170+
currentLineMap = getLineStarts(sourceFile);
171+
detachedCommentsInfo = undefined;
172+
consumedCommentRanges = [];
173+
leadingCommentRangeNodeStarts = [];
174+
trailingCommentRangeNodeEnds = [];
175+
}
176+
177+
function hasDetachedComments(pos: number) {
178+
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
179+
}
180+
181+
function getLeadingCommentsWithoutDetachedComments() {
182+
// get the leading comments from detachedPos
183+
const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
184+
const leadingComments = getLeadingCommentRanges(currentText, pos);
185+
if (detachedCommentsInfo.length - 1) {
186+
detachedCommentsInfo.pop();
187+
}
188+
else {
189+
detachedCommentsInfo = undefined;
190+
}
191+
192+
return leadingComments;
193+
}
194+
195+
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) {
196+
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments);
197+
198+
if (currentDetachedCommentInfo) {
199+
if (detachedCommentsInfo) {
200+
detachedCommentsInfo.push(currentDetachedCommentInfo);
201+
}
202+
else {
203+
detachedCommentsInfo = [currentDetachedCommentInfo];
204+
}
205+
}
206+
}
207+
208+
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
209+
emitPos(comment.pos);
210+
writeCommentRange(text, lineMap, writer, comment, newLine);
211+
emitPos(comment.end);
212+
}
213+
}
214+
}

src/compiler/emitter.ts

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
895895
}
896896

897897
function emitLiteral(node: LiteralExpression | TemplateLiteralFragment) {
898-
const text = getLiteralText(node);
898+
const text = getLiteralText(node, currentSourceFile, languageVersion);
899899

900900
if ((compilerOptions.sourceMap || compilerOptions.inlineSourceMap) && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
901901
writer.writeLiteral(text);
@@ -909,43 +909,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
909909
}
910910
}
911911

912-
function getLiteralText(node: LiteralExpression | TemplateLiteralFragment) {
913-
// Any template literal or string literal with an extended escape
914-
// (e.g. "\u{0067}") will need to be downleveled as a escaped string literal.
915-
if (languageVersion < ScriptTarget.ES6 && (isTemplateLiteralKind(node.kind) || node.hasExtendedUnicodeEscape)) {
916-
return getQuotedEscapedLiteralText("\"", node.text, "\"");
917-
}
918-
919-
// If we don't need to downlevel and we can reach the original source text using
920-
// the node's parent reference, then simply get the text as it was originally written.
921-
if (node.parent) {
922-
return getTextOfNodeFromSourceText(currentText, node);
923-
}
924-
925-
// If we can't reach the original source text, use the canonical form if it's a number,
926-
// or an escaped quoted form of the original text if it's string-like.
927-
switch (node.kind) {
928-
case SyntaxKind.StringLiteral:
929-
return getQuotedEscapedLiteralText("\"", node.text, "\"");
930-
case SyntaxKind.NoSubstitutionTemplateLiteral:
931-
return getQuotedEscapedLiteralText("`", node.text, "`");
932-
case SyntaxKind.TemplateHead:
933-
return getQuotedEscapedLiteralText("`", node.text, "${");
934-
case SyntaxKind.TemplateMiddle:
935-
return getQuotedEscapedLiteralText("}", node.text, "${");
936-
case SyntaxKind.TemplateTail:
937-
return getQuotedEscapedLiteralText("}", node.text, "`");
938-
case SyntaxKind.NumericLiteral:
939-
return node.text;
940-
}
941-
942-
Debug.fail(`Literal kind '${node.kind}' not accounted for.`);
943-
}
944-
945-
function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) {
946-
return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote;
947-
}
948-
949912
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
950913
// Find original source text, since we need to emit the raw strings of the tagged template.
951914
// The raw strings contain the (escaped) strings of what the user wrote.
@@ -6590,7 +6553,8 @@ const _super = (function (geti, seti) {
65906553
}
65916554
const moduleName = getExternalModuleName(importNode);
65926555
if (moduleName.kind === SyntaxKind.StringLiteral) {
6593-
return tryRenameExternalModule(<LiteralExpression>moduleName) || getLiteralText(<LiteralExpression>moduleName);
6556+
return tryRenameExternalModule(<LiteralExpression>moduleName)
6557+
|| getLiteralText(<LiteralExpression>moduleName, currentSourceFile, languageVersion);
65946558
}
65956559

65966560
return undefined;

src/compiler/factory.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,23 +144,20 @@ namespace ts {
144144

145145
export function createLiteral(value: string): StringLiteral;
146146
export function createLiteral(value: number): LiteralExpression;
147-
export function createLiteral(value: string | number | boolean | void): PrimaryExpression;
148-
export function createLiteral<T extends PrimaryExpression>(value: string | number | boolean | void): T {
149-
if (typeof value === "string") {
150-
const node = <T & StringLiteral>createNode(SyntaxKind.StringLiteral);
151-
node.text = value;
152-
return node;
153-
}
154-
else if (typeof value === "number") {
147+
export function createLiteral(value: string | number | boolean): PrimaryExpression;
148+
export function createLiteral<T extends PrimaryExpression>(value: string | number | boolean): T {
149+
if (typeof value === "number") {
155150
const node = <T & LiteralExpression>createNode(SyntaxKind.NumericLiteral);
156151
node.text = value.toString();
157152
return node;
158153
}
159154
else if (typeof value === "boolean") {
160155
return <T>createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
161156
}
162-
else if (value === null) {
163-
return <T>createNode(SyntaxKind.NullKeyword);
157+
else {
158+
const node = <T & StringLiteral>createNode(SyntaxKind.StringLiteral);
159+
node.text = String(value);
160+
return node;
164161
}
165162
}
166163

@@ -254,9 +251,6 @@ namespace ts {
254251
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
255252
return createLiteral(value);
256253
}
257-
else if (value === null) {
258-
return createLiteral(null);
259-
}
260254
else {
261255
return value;
262256
}

0 commit comments

Comments
 (0)