forked from microsoft/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsyntaxIndenter.ts
More file actions
164 lines (130 loc) · 7.67 KB
/
Copy pathsyntaxIndenter.ts
File metadata and controls
164 lines (130 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
///<reference path='references.ts' />
module TypeScript {
export class SyntaxIndenter extends SyntaxRewriter {
private lastTriviaWasNewLine: boolean;
private indentationTrivia: ISyntaxTrivia;
constructor(indentFirstToken: boolean,
private indentationAmount: number,
private options: FormattingOptions) {
super();
this.lastTriviaWasNewLine = indentFirstToken;
this.indentationTrivia = Indentation.indentationTrivia(this.indentationAmount, this.options);
}
public visitToken(token: ISyntaxToken): ISyntaxToken {
if (token.width() === 0) {
return token;
}
var result = token;
if (this.lastTriviaWasNewLine) {
// have to add our indentation to every line that this token hits.
result = token.withLeadingTrivia(this.indentTriviaList(token.leadingTrivia()));
}
this.lastTriviaWasNewLine = token.hasTrailingNewLine();
return result;
}
public indentTriviaList(triviaList: ISyntaxTriviaList): ISyntaxTriviaList {
var result: ISyntaxTrivia[] = [];
// First, update any existing trivia with the indent amount. For example, combine the
// indent with any whitespace trivia, or prepend any comments with the trivia.
var indentNextTrivia = true;
for (var i = 0, n = triviaList.count(); i < n; i++) {
var trivia = triviaList.syntaxTriviaAt(i);
var indentThisTrivia = indentNextTrivia;
indentNextTrivia = false;
switch (trivia.kind()) {
case SyntaxKind.MultiLineCommentTrivia:
this.indentMultiLineComment(trivia, indentThisTrivia, result);
continue;
case SyntaxKind.SingleLineCommentTrivia:
case SyntaxKind.SkippedTokenTrivia:
this.indentSingleLineOrSkippedText(trivia, indentThisTrivia, result);
continue;
case SyntaxKind.WhitespaceTrivia:
this.indentWhitespace(trivia, indentThisTrivia, result);
continue;
case SyntaxKind.NewLineTrivia:
// We hit a newline processing the trivia. We need to add the indentation to the
// next line as well. Note: don't bother indenting the newline itself. This will
// just insert ugly whitespace that most users probably will not want.
result.push(trivia);
indentNextTrivia = true;
continue;
default:
throw Errors.invalidOperation();
}
}
// Then, if the last trivia was a newline (or there was no trivia at all), then just add the
// indentation in right before the token.
if (indentNextTrivia) {
result.push(this.indentationTrivia);
}
return Syntax.triviaList(result);
}
private indentSegment(segment: string): string {
// Find the position of the first non whitespace character in the segment.
var firstNonWhitespacePosition = Indentation.firstNonWhitespacePosition(segment);
if (firstNonWhitespacePosition < segment.length &&
CharacterInfo.isLineTerminator(segment.charCodeAt(firstNonWhitespacePosition))) {
// If this segment was just a newline, then don't bother indenting it. That will just
// leave the user with an ugly indent in their output that they probably do not want.
return segment;
}
// Convert that position to a column.
var firstNonWhitespaceColumn = Indentation.columnForPositionInString(segment, firstNonWhitespacePosition, this.options);
// Find the new column we want the nonwhitespace text to start at.
var newFirstNonWhitespaceColumn = firstNonWhitespaceColumn + this.indentationAmount;
// Compute an indentation string for that.
var indentationString = Indentation.indentationString(newFirstNonWhitespaceColumn, this.options);
// Join the new indentation and the original string without its indentation.
return indentationString + segment.substring(firstNonWhitespacePosition);
}
private indentWhitespace(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void {
if (!indentThisTrivia) {
// Line didn't start with this trivia. So no need to touch it. Just add to the result
// and continue on.
result.push(trivia);
return;
}
// Line started with this trivia. We want to figure out what the final column this
// whitespace goes to will be. To do that we add the column it is at now to the column we
// want to indent to. We then compute the final tabs+whitespace string for that.
var newIndentation = this.indentSegment(trivia.fullText());
result.push(Syntax.whitespace(newIndentation));
}
private indentSingleLineOrSkippedText(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void {
if (indentThisTrivia) {
// The line started with a comment or skipped text. Add an indentation based
// on the desired settings, and then add the trivia itself.
result.push(this.indentationTrivia);
}
result.push(trivia);
}
private indentMultiLineComment(trivia: ISyntaxTrivia, indentThisTrivia: boolean, result: ISyntaxTrivia[]): void {
if (indentThisTrivia) {
// The line started with a multiline comment. Add an indentation based
// on the desired settings, and then add the trivia itself.
result.push(this.indentationTrivia);
}
// If the multiline comment spans multiple lines, we need to add the right indent amount to
// each successive line segment as well.
var segments = Syntax.splitMultiLineCommentTriviaIntoMultipleLines(trivia);
for (var i = 1; i < segments.length; i++) {
segments[i] = this.indentSegment(segments[i]);
}
var newText = segments.join("");
result.push(Syntax.multiLineComment(newText));
}
public static indentNode(node: ISyntaxNode, indentFirstToken: boolean, indentAmount: number, options: FormattingOptions): SyntaxNode {
var indenter = new SyntaxIndenter(indentFirstToken, indentAmount, options);
return node.accept(indenter);
}
public static indentNodes(nodes: SyntaxNode[], indentFirstToken: boolean, indentAmount: number, options: FormattingOptions): SyntaxNode[] {
// Note: it is necessary for correctness that we reuse the same SyntaxIndenter here.
// That's because when working on nodes 1-N, we need to know if the previous node ended
// with a newline. The indenter will track that for us.
var indenter = new SyntaxIndenter(indentFirstToken, indentAmount, options);
var result: SyntaxNode[] = ArrayUtilities.select<any, any>(nodes, n => n.accept(indenter));
return result;
}
}
}