@tslite/services 0.0.0-stage → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +55 -0
- package/NOTICE +13 -0
- package/dist/_namespaces/edit.d.ts +8 -0
- package/dist/_namespaces/edit.js +11 -0
- package/dist/_namespaces/ts.Completions.StringCompletions.d.ts +1 -0
- package/dist/_namespaces/ts.Completions.StringCompletions.js +2 -0
- package/dist/_namespaces/ts.Completions.d.ts +2 -0
- package/dist/_namespaces/ts.Completions.js +3 -0
- package/dist/_namespaces/ts.FindAllReferences.d.ts +2 -0
- package/dist/_namespaces/ts.FindAllReferences.js +3 -0
- package/dist/_namespaces/ts.GoToDefinition.d.ts +1 -0
- package/dist/_namespaces/ts.GoToDefinition.js +2 -0
- package/dist/_namespaces/ts.NavigationBar.d.ts +1 -0
- package/dist/_namespaces/ts.NavigationBar.js +2 -0
- package/dist/_namespaces/ts.OrganizeImports.d.ts +1 -0
- package/dist/_namespaces/ts.OrganizeImports.js +2 -0
- package/dist/_namespaces/ts.OutliningElementsCollector.d.ts +1 -0
- package/dist/_namespaces/ts.OutliningElementsCollector.js +2 -0
- package/dist/_namespaces/ts.Rename.d.ts +1 -0
- package/dist/_namespaces/ts.Rename.js +2 -0
- package/dist/_namespaces/ts.SignatureHelp.d.ts +1 -0
- package/dist/_namespaces/ts.SignatureHelp.js +2 -0
- package/dist/_namespaces/ts.SmartSelectionRange.d.ts +1 -0
- package/dist/_namespaces/ts.SmartSelectionRange.js +2 -0
- package/dist/_namespaces/ts.SymbolDisplay.d.ts +1 -0
- package/dist/_namespaces/ts.SymbolDisplay.js +2 -0
- package/dist/_namespaces/ts.codefix.d.ts +26 -0
- package/dist/_namespaces/ts.codefix.js +27 -0
- package/dist/_namespaces/ts.d.ts +17 -0
- package/dist/_namespaces/ts.formatting.d.ts +7 -0
- package/dist/_namespaces/ts.formatting.js +8 -0
- package/dist/_namespaces/ts.js +20 -0
- package/dist/_namespaces/ts.refactor.addOrRemoveBracesToArrowFunction.d.ts +1 -0
- package/dist/_namespaces/ts.refactor.addOrRemoveBracesToArrowFunction.js +2 -0
- package/dist/_namespaces/ts.refactor.convertParamsToDestructuredObject.d.ts +1 -0
- package/dist/_namespaces/ts.refactor.convertParamsToDestructuredObject.js +2 -0
- package/dist/_namespaces/ts.refactor.convertStringOrTemplateLiteral.d.ts +1 -0
- package/dist/_namespaces/ts.refactor.convertStringOrTemplateLiteral.js +2 -0
- package/dist/_namespaces/ts.refactor.d.ts +9 -0
- package/dist/_namespaces/ts.refactor.extractSymbol.d.ts +1 -0
- package/dist/_namespaces/ts.refactor.extractSymbol.js +2 -0
- package/dist/_namespaces/ts.refactor.js +10 -0
- package/dist/_namespaces/ts.textChanges.d.ts +1 -0
- package/dist/_namespaces/ts.textChanges.js +2 -0
- package/dist/classifier.d.ts +5 -0
- package/dist/classifier.js +439 -0
- package/dist/codeFixProvider.d.ts +13 -0
- package/dist/codeFixProvider.js +84 -0
- package/dist/codefixes/addConvertToUnknownForNonOverlappingTypes.d.ts +1 -0
- package/dist/codefixes/addConvertToUnknownForNonOverlappingTypes.js +21 -0
- package/dist/codefixes/addEmptyExportDeclaration.d.ts +1 -0
- package/dist/codefixes/addEmptyExportDeclaration.js +17 -0
- package/dist/codefixes/addMissingAsync.d.ts +1 -0
- package/dist/codefixes/addMissingAsync.js +75 -0
- package/dist/codefixes/addMissingAwait.d.ts +1 -0
- package/dist/codefixes/addMissingAwait.js +236 -0
- package/dist/codefixes/addMissingConst.d.ts +1 -0
- package/dist/codefixes/addMissingConst.js +91 -0
- package/dist/codefixes/addNameToNamelessParameter.d.ts +1 -0
- package/dist/codefixes/addNameToNamelessParameter.js +29 -0
- package/dist/codefixes/convertConstToLet.d.ts +1 -0
- package/dist/codefixes/convertConstToLet.js +30 -0
- package/dist/codefixes/convertToAsyncFunction.d.ts +1 -0
- package/dist/codefixes/convertToAsyncFunction.js +495 -0
- package/dist/codefixes/convertToMappedObjectType.d.ts +1 -0
- package/dist/codefixes/convertToMappedObjectType.js +48 -0
- package/dist/codefixes/correctQualifiedNameToIndexedAccessType.d.ts +1 -0
- package/dist/codefixes/correctQualifiedNameToIndexedAccessType.js +33 -0
- package/dist/codefixes/fixAddModuleReferTypeMissingTypeof.d.ts +1 -0
- package/dist/codefixes/fixAddModuleReferTypeMissingTypeof.js +27 -0
- package/dist/codefixes/fixAwaitInSyncFunction.d.ts +1 -0
- package/dist/codefixes/fixAwaitInSyncFunction.js +69 -0
- package/dist/codefixes/fixExpectedComma.d.ts +1 -0
- package/dist/codefixes/fixExpectedComma.js +35 -0
- package/dist/codefixes/fixInvalidJsxCharacters.d.ts +1 -0
- package/dist/codefixes/fixInvalidJsxCharacters.js +41 -0
- package/dist/codefixes/fixMissingCallParentheses.d.ts +1 -0
- package/dist/codefixes/fixMissingCallParentheses.js +42 -0
- package/dist/codefixes/fixSpelling.d.ts +1 -0
- package/dist/codefixes/fixSpelling.js +94 -0
- package/dist/codefixes/fixUnreachableCode.d.ts +1 -0
- package/dist/codefixes/fixUnreachableCode.js +65 -0
- package/dist/codefixes/fixUnusedIdentifier.d.ts +1 -0
- package/dist/codefixes/fixUnusedIdentifier.js +224 -0
- package/dist/codefixes/helpers.d.ts +8 -0
- package/dist/codefixes/helpers.js +53 -0
- package/dist/codefixes/importFixes.d.ts +14 -0
- package/dist/codefixes/importFixes.js +441 -0
- package/dist/codefixes/inferFromUsage.d.ts +1 -0
- package/dist/codefixes/inferFromUsage.js +742 -0
- package/dist/codefixes/removeUnnecessaryAwait.d.ts +1 -0
- package/dist/codefixes/removeUnnecessaryAwait.js +41 -0
- package/dist/codefixes/returnValueCorrect.d.ts +1 -0
- package/dist/codefixes/returnValueCorrect.js +132 -0
- package/dist/codefixes/splitTypeOnlyImport.d.ts +1 -0
- package/dist/codefixes/splitTypeOnlyImport.js +32 -0
- package/dist/codefixes/useBigintLiteral.d.ts +1 -0
- package/dist/codefixes/useBigintLiteral.js +29 -0
- package/dist/completions.d.ts +83 -0
- package/dist/completions.js +1720 -0
- package/dist/documentHighlights.d.ts +8 -0
- package/dist/documentHighlights.js +367 -0
- package/dist/documentRegistry.d.ts +36 -0
- package/dist/documentRegistry.js +63 -0
- package/dist/editingServices.d.ts +1 -0
- package/dist/editingServices.js +4 -0
- package/dist/findAllReferences.d.ts +113 -0
- package/dist/findAllReferences.js +1366 -0
- package/dist/formatting/formatting.d.ts +26 -0
- package/dist/formatting/formatting.js +1021 -0
- package/dist/formatting/formattingContext.d.ts +34 -0
- package/dist/formatting/formattingContext.js +78 -0
- package/dist/formatting/formattingScanner.d.ts +14 -0
- package/dist/formatting/formattingScanner.js +228 -0
- package/dist/formatting/rule.d.ts +31 -0
- package/dist/formatting/rule.js +22 -0
- package/dist/formatting/rules.d.ts +7 -0
- package/dist/formatting/rules.js +610 -0
- package/dist/formatting/rulesMap.d.ts +5 -0
- package/dist/formatting/rulesMap.js +127 -0
- package/dist/formatting/smartIndenter.d.ts +44 -0
- package/dist/formatting/smartIndenter.js +518 -0
- package/dist/getEditsForFileRename.d.ts +6 -0
- package/dist/getEditsForFileRename.js +86 -0
- package/dist/goToDefinition.d.ts +8 -0
- package/dist/goToDefinition.js +246 -0
- package/dist/importTracker.d.ts +50 -0
- package/dist/importTracker.js +422 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/navigationBar.d.ts +3 -0
- package/dist/navigationBar.js +549 -0
- package/dist/organizeImports.d.ts +18 -0
- package/dist/organizeImports.js +290 -0
- package/dist/outliningElementsCollector.d.ts +2 -0
- package/dist/outliningElementsCollector.js +261 -0
- package/dist/refactorProvider.d.ts +5 -0
- package/dist/refactorProvider.js +16 -0
- package/dist/refactors/addOrRemoveBracesToArrowFunction.d.ts +1 -0
- package/dist/refactors/addOrRemoveBracesToArrowFunction.js +86 -0
- package/dist/refactors/convertExport.d.ts +1 -0
- package/dist/refactors/convertExport.js +126 -0
- package/dist/refactors/convertImport.d.ts +1 -0
- package/dist/refactors/convertImport.js +116 -0
- package/dist/refactors/convertParamsToDestructuredObject.d.ts +1 -0
- package/dist/refactors/convertParamsToDestructuredObject.js +282 -0
- package/dist/refactors/convertStringOrTemplateLiteral.d.ts +1 -0
- package/dist/refactors/convertStringOrTemplateLiteral.js +148 -0
- package/dist/refactors/extractSymbol.d.ts +60 -0
- package/dist/refactors/extractSymbol.js +1281 -0
- package/dist/refactors/extractType.d.ts +1 -0
- package/dist/refactors/extractType.js +102 -0
- package/dist/refactors/moveToNewFile.d.ts +1 -0
- package/dist/refactors/moveToNewFile.js +477 -0
- package/dist/rename.d.ts +2 -0
- package/dist/rename.js +100 -0
- package/dist/services.d.ts +48 -0
- package/dist/services.js +1480 -0
- package/dist/signatureHelp.d.ts +8 -0
- package/dist/signatureHelp.js +414 -0
- package/dist/smartSelection.d.ts +2 -0
- package/dist/smartSelection.js +243 -0
- package/dist/stringCompletions.d.ts +4 -0
- package/dist/stringCompletions.js +220 -0
- package/dist/suggestionDiagnostics.d.ts +6 -0
- package/dist/suggestionDiagnostics.js +92 -0
- package/dist/symbolDisplay.d.ts +16 -0
- package/dist/symbolDisplay.js +400 -0
- package/dist/textChanges.d.ts +161 -0
- package/dist/textChanges.js +1163 -0
- package/dist/types.d.ts +908 -0
- package/dist/types.js +263 -0
- package/dist/utilities.d.ts +283 -0
- package/dist/utilities.js +1981 -0
- package/package.json +45 -3
- package/README.md +0 -3
|
@@ -0,0 +1,1021 @@
|
|
|
1
|
+
// Modificado de microsoft/TypeScript v3.9.10, src/services/formatting/formatting.ts: o namespace virou módulo ES, com os imports explícitos.
|
|
2
|
+
import { CharacterCodes, Debug, SyntaxKind, createTextChangeFromStartLength, findIndex, findPrecedingToken, forEachChild, forEachRight, getEndLinePosition, getLineStartPositionForPosition, getNewLineOrDefaultFromHost, getStartPositionOfLine, isCallLikeExpression, isComment, isLineBreak, isNodeArray, isStringOrRegularExpressionOrTemplateLiteral, isToken, isWhiteSpaceSingleLine, rangeContainsRange, rangeContainsStartEnd, rangeOverlapsWithStartEnd, repeatString, startEndContainsRange, startEndOverlapsWithStartEnd, } from "../_namespaces/edit.js";
|
|
3
|
+
import { FormattingContext, SmartIndenter, getFormattingScanner, } from "../_namespaces/ts.formatting.js";
|
|
4
|
+
export function createTextRangeWithKind(pos, end, kind) {
|
|
5
|
+
const textRangeWithKind = { pos, end, kind };
|
|
6
|
+
if (Debug.isDebugging) {
|
|
7
|
+
Object.defineProperty(textRangeWithKind, "__debugKind", {
|
|
8
|
+
get: () => Debug.formatSyntaxKind(kind),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return textRangeWithKind;
|
|
12
|
+
}
|
|
13
|
+
var Constants;
|
|
14
|
+
(function (Constants) {
|
|
15
|
+
Constants[Constants["Unknown"] = -1] = "Unknown";
|
|
16
|
+
})(Constants || (Constants = {}));
|
|
17
|
+
export function formatOnEnter(position, sourceFile, formatContext) {
|
|
18
|
+
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
|
|
19
|
+
if (line === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
// After the enter key, the cursor is now at a new line. The new line may or may not contain non-whitespace characters.
|
|
23
|
+
// If the new line has only whitespaces, we won't want to format this line, because that would remove the indentation as
|
|
24
|
+
// trailing whitespaces. So the end of the formatting span should be the later one between:
|
|
25
|
+
// 1. the end of the previous line
|
|
26
|
+
// 2. the last non-whitespace character in the current line
|
|
27
|
+
let endOfFormatSpan = getEndLinePosition(line, sourceFile);
|
|
28
|
+
while (isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(endOfFormatSpan))) {
|
|
29
|
+
endOfFormatSpan--;
|
|
30
|
+
}
|
|
31
|
+
// if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to
|
|
32
|
+
// touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the
|
|
33
|
+
// previous character before the end of format span is line break character as well.
|
|
34
|
+
if (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
|
|
35
|
+
endOfFormatSpan--;
|
|
36
|
+
}
|
|
37
|
+
const span = {
|
|
38
|
+
// get start position for the previous line
|
|
39
|
+
pos: getStartPositionOfLine(line - 1, sourceFile),
|
|
40
|
+
// end value is exclusive so add 1 to the result
|
|
41
|
+
end: endOfFormatSpan + 1
|
|
42
|
+
};
|
|
43
|
+
return formatSpan(span, sourceFile, formatContext, 2 /* FormattingRequestKind.FormatOnEnter */);
|
|
44
|
+
}
|
|
45
|
+
export function formatOnSemicolon(position, sourceFile, formatContext) {
|
|
46
|
+
const semicolon = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.SemicolonToken, sourceFile);
|
|
47
|
+
return formatNodeLines(findOutermostNodeWithinListLevel(semicolon), sourceFile, formatContext, 3 /* FormattingRequestKind.FormatOnSemicolon */);
|
|
48
|
+
}
|
|
49
|
+
export function formatOnOpeningCurly(position, sourceFile, formatContext) {
|
|
50
|
+
const openingCurly = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.OpenBraceToken, sourceFile);
|
|
51
|
+
if (!openingCurly) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const curlyBraceRange = openingCurly.parent;
|
|
55
|
+
const outermostNode = findOutermostNodeWithinListLevel(curlyBraceRange);
|
|
56
|
+
/**
|
|
57
|
+
* We limit the span to end at the opening curly to handle the case where
|
|
58
|
+
* the brace matched to that just typed will be incorrect after further edits.
|
|
59
|
+
* For example, we could type the opening curly for the following method
|
|
60
|
+
* body without brace-matching activated:
|
|
61
|
+
* ```
|
|
62
|
+
* class C {
|
|
63
|
+
* foo()
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
* and we wouldn't want to move the closing brace.
|
|
67
|
+
*/
|
|
68
|
+
const textRange = {
|
|
69
|
+
pos: getLineStartPositionForPosition(outermostNode.getStart(sourceFile), sourceFile), // TODO: GH#18217
|
|
70
|
+
end: position
|
|
71
|
+
};
|
|
72
|
+
return formatSpan(textRange, sourceFile, formatContext, 4 /* FormattingRequestKind.FormatOnOpeningCurlyBrace */);
|
|
73
|
+
}
|
|
74
|
+
export function formatOnClosingCurly(position, sourceFile, formatContext) {
|
|
75
|
+
const precedingToken = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.CloseBraceToken, sourceFile);
|
|
76
|
+
return formatNodeLines(findOutermostNodeWithinListLevel(precedingToken), sourceFile, formatContext, 5 /* FormattingRequestKind.FormatOnClosingCurlyBrace */);
|
|
77
|
+
}
|
|
78
|
+
export function formatDocument(sourceFile, formatContext) {
|
|
79
|
+
const span = {
|
|
80
|
+
pos: 0,
|
|
81
|
+
end: sourceFile.text.length
|
|
82
|
+
};
|
|
83
|
+
return formatSpan(span, sourceFile, formatContext, 0 /* FormattingRequestKind.FormatDocument */);
|
|
84
|
+
}
|
|
85
|
+
export function formatSelection(start, end, sourceFile, formatContext) {
|
|
86
|
+
// format from the beginning of the line
|
|
87
|
+
const span = {
|
|
88
|
+
pos: getLineStartPositionForPosition(start, sourceFile),
|
|
89
|
+
end,
|
|
90
|
+
};
|
|
91
|
+
return formatSpan(span, sourceFile, formatContext, 1 /* FormattingRequestKind.FormatSelection */);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Validating `expectedTokenKind` ensures the token was typed in the context we expect (eg: not a comment).
|
|
95
|
+
* @param expectedTokenKind The kind of the last token constituting the desired parent node.
|
|
96
|
+
*/
|
|
97
|
+
function findImmediatelyPrecedingTokenOfKind(end, expectedTokenKind, sourceFile) {
|
|
98
|
+
const precedingToken = findPrecedingToken(end, sourceFile);
|
|
99
|
+
return precedingToken && precedingToken.kind === expectedTokenKind && end === precedingToken.getEnd() ?
|
|
100
|
+
precedingToken :
|
|
101
|
+
undefined;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Finds the highest node enclosing `node` at the same list level as `node`
|
|
105
|
+
* and whose end does not exceed `node.end`.
|
|
106
|
+
*
|
|
107
|
+
* Consider typing the following
|
|
108
|
+
* ```
|
|
109
|
+
* let x = 1;
|
|
110
|
+
* while (true) {
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
* Upon typing the closing curly, we want to format the entire `while`-statement, but not the preceding
|
|
114
|
+
* variable declaration.
|
|
115
|
+
*/
|
|
116
|
+
function findOutermostNodeWithinListLevel(node) {
|
|
117
|
+
let current = node;
|
|
118
|
+
while (current &&
|
|
119
|
+
current.parent &&
|
|
120
|
+
current.parent.end === node.end &&
|
|
121
|
+
!isListElement(current.parent, current)) {
|
|
122
|
+
current = current.parent;
|
|
123
|
+
}
|
|
124
|
+
return current;
|
|
125
|
+
}
|
|
126
|
+
// Returns true if node is a element in some list in parent
|
|
127
|
+
// i.e. parent is class declaration with the list of members and node is one of members.
|
|
128
|
+
function isListElement(parent, node) {
|
|
129
|
+
switch (parent.kind) {
|
|
130
|
+
// Na tag, também os membros da classe e da interface (CK-02).
|
|
131
|
+
case SyntaxKind.ModuleDeclaration:
|
|
132
|
+
const body = parent.body;
|
|
133
|
+
return !!body && body.kind === SyntaxKind.ModuleBlock && rangeContainsRange(body.statements, node);
|
|
134
|
+
case SyntaxKind.SourceFile:
|
|
135
|
+
case SyntaxKind.Block:
|
|
136
|
+
case SyntaxKind.ModuleBlock:
|
|
137
|
+
return rangeContainsRange(parent.statements, node);
|
|
138
|
+
case SyntaxKind.CatchClause:
|
|
139
|
+
return rangeContainsRange(parent.block.statements, node);
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
/** find node that fully contains given text range */
|
|
144
|
+
function findEnclosingNode(range, sourceFile) {
|
|
145
|
+
return find(sourceFile);
|
|
146
|
+
function find(n) {
|
|
147
|
+
const candidate = forEachChild(n, c => startEndContainsRange(c.getStart(sourceFile), c.end, range) && c);
|
|
148
|
+
if (candidate) {
|
|
149
|
+
const result = find(candidate);
|
|
150
|
+
if (result) {
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return n;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** formatting is not applied to ranges that contain parse errors.
|
|
158
|
+
* This function will return a predicate that for a given text range will tell
|
|
159
|
+
* if there are any parse errors that overlap with the range.
|
|
160
|
+
*/
|
|
161
|
+
function prepareRangeContainsErrorFunction(errors, originalRange) {
|
|
162
|
+
if (!errors.length) {
|
|
163
|
+
return rangeHasNoErrors;
|
|
164
|
+
}
|
|
165
|
+
// pick only errors that fall in range
|
|
166
|
+
const sorted = errors
|
|
167
|
+
.filter(d => rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length)) // TODO: GH#18217
|
|
168
|
+
.sort((e1, e2) => e1.start - e2.start);
|
|
169
|
+
if (!sorted.length) {
|
|
170
|
+
return rangeHasNoErrors;
|
|
171
|
+
}
|
|
172
|
+
let index = 0;
|
|
173
|
+
return r => {
|
|
174
|
+
// in current implementation sequence of arguments [r1, r2...] is monotonically increasing.
|
|
175
|
+
// 'index' tracks the index of the most recent error that was checked.
|
|
176
|
+
while (true) {
|
|
177
|
+
if (index >= sorted.length) {
|
|
178
|
+
// all errors in the range were already checked -> no error in specified range
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const error = sorted[index];
|
|
182
|
+
if (r.end <= error.start) {
|
|
183
|
+
// specified range ends before the error referred by 'index' - no error in range
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if (startEndOverlapsWithStartEnd(r.pos, r.end, error.start, error.start + error.length)) {
|
|
187
|
+
// specified range overlaps with error range
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
index++;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
function rangeHasNoErrors() {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Start of the original range might fall inside the comment - scanner will not yield appropriate results
|
|
199
|
+
* This function will look for token that is located before the start of target range
|
|
200
|
+
* and return its end as start position for the scanner.
|
|
201
|
+
*/
|
|
202
|
+
function getScanStartPosition(enclosingNode, originalRange, sourceFile) {
|
|
203
|
+
const start = enclosingNode.getStart(sourceFile);
|
|
204
|
+
if (start === originalRange.pos && enclosingNode.end === originalRange.end) {
|
|
205
|
+
return start;
|
|
206
|
+
}
|
|
207
|
+
const precedingToken = findPrecedingToken(originalRange.pos, sourceFile);
|
|
208
|
+
if (!precedingToken) {
|
|
209
|
+
// no preceding token found - start from the beginning of enclosing node
|
|
210
|
+
return enclosingNode.pos;
|
|
211
|
+
}
|
|
212
|
+
// preceding token ends after the start of original range (i.e when originalRange.pos falls in the middle of literal)
|
|
213
|
+
// start from the beginning of enclosingNode to handle the entire 'originalRange'
|
|
214
|
+
if (precedingToken.end >= originalRange.pos) {
|
|
215
|
+
return enclosingNode.pos;
|
|
216
|
+
}
|
|
217
|
+
return precedingToken.end;
|
|
218
|
+
}
|
|
219
|
+
/*
|
|
220
|
+
* For cases like
|
|
221
|
+
* if (a ||
|
|
222
|
+
* b ||$
|
|
223
|
+
* c) {...}
|
|
224
|
+
* If we hit Enter at $ we want line ' b ||' to be indented.
|
|
225
|
+
* Formatting will be applied to the last two lines.
|
|
226
|
+
* Node that fully encloses these lines is binary expression 'a ||...'.
|
|
227
|
+
* Initial indentation for this node will be 0.
|
|
228
|
+
* Binary expressions don't introduce new indentation scopes, however it is possible
|
|
229
|
+
* that some parent node on the same line does - like if statement in this case.
|
|
230
|
+
* Note that we are considering parents only from the same line with initial node -
|
|
231
|
+
* if parent is on the different line - its delta was already contributed
|
|
232
|
+
* to the initial indentation.
|
|
233
|
+
*/
|
|
234
|
+
function getOwnOrInheritedDelta(n, options, sourceFile) {
|
|
235
|
+
let previousLine = -1 /* Constants.Unknown */;
|
|
236
|
+
let child;
|
|
237
|
+
while (n) {
|
|
238
|
+
const line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line;
|
|
239
|
+
if (previousLine !== -1 /* Constants.Unknown */ && line !== previousLine) {
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
if (SmartIndenter.shouldIndentChildNode(options, n, child, sourceFile)) {
|
|
243
|
+
return options.indentSize;
|
|
244
|
+
}
|
|
245
|
+
previousLine = line;
|
|
246
|
+
child = n;
|
|
247
|
+
n = n.parent;
|
|
248
|
+
}
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
export function formatNodeGivenIndentation(node, sourceFileLike, languageVariant, initialIndentation, delta, formatContext) {
|
|
252
|
+
const range = { pos: 0, end: sourceFileLike.text.length };
|
|
253
|
+
return getFormattingScanner(sourceFileLike.text, languageVariant, range.pos, range.end, scanner => formatSpanWorker(range, node, initialIndentation, delta, scanner, formatContext, 1 /* FormattingRequestKind.FormatSelection */, _ => false, // assume that node does not have any errors
|
|
254
|
+
sourceFileLike));
|
|
255
|
+
}
|
|
256
|
+
function formatNodeLines(node, sourceFile, formatContext, requestKind) {
|
|
257
|
+
if (!node) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
const span = {
|
|
261
|
+
pos: getLineStartPositionForPosition(node.getStart(sourceFile), sourceFile),
|
|
262
|
+
end: node.end
|
|
263
|
+
};
|
|
264
|
+
return formatSpan(span, sourceFile, formatContext, requestKind);
|
|
265
|
+
}
|
|
266
|
+
function formatSpan(originalRange, sourceFile, formatContext, requestKind) {
|
|
267
|
+
// find the smallest node that fully wraps the range and compute the initial indentation for the node
|
|
268
|
+
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
|
|
269
|
+
return getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end, scanner => formatSpanWorker(originalRange, enclosingNode, SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, formatContext.options), getOwnOrInheritedDelta(enclosingNode, formatContext.options, sourceFile), scanner, formatContext, requestKind, prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange), sourceFile));
|
|
270
|
+
}
|
|
271
|
+
function formatSpanWorker(originalRange, enclosingNode, initialIndentation, delta, formattingScanner, { options, getRules, host }, requestKind, rangeContainsError, sourceFile) {
|
|
272
|
+
// formatting context is used by rules provider
|
|
273
|
+
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
|
|
274
|
+
let previousRange;
|
|
275
|
+
let previousParent;
|
|
276
|
+
let previousRangeStartLine;
|
|
277
|
+
let lastIndentedLine;
|
|
278
|
+
let indentationOnLastIndentedLine = -1 /* Constants.Unknown */;
|
|
279
|
+
const edits = [];
|
|
280
|
+
formattingScanner.advance();
|
|
281
|
+
if (formattingScanner.isOnToken()) {
|
|
282
|
+
const startLine = sourceFile.getLineAndCharacterOfPosition(enclosingNode.getStart(sourceFile)).line;
|
|
283
|
+
// Na tag, a linha do nó com decorators era a do primeiro token depois deles (CK-02).
|
|
284
|
+
const undecoratedStartLine = startLine;
|
|
285
|
+
processNode(enclosingNode, enclosingNode, startLine, undecoratedStartLine, initialIndentation, delta);
|
|
286
|
+
}
|
|
287
|
+
if (!formattingScanner.isOnToken()) {
|
|
288
|
+
const leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
|
|
289
|
+
if (leadingTrivia) {
|
|
290
|
+
indentTriviaItems(leadingTrivia, initialIndentation, /*indentNextTokenOrTrivia*/ false, item => processRange(item, sourceFile.getLineAndCharacterOfPosition(item.pos), enclosingNode, enclosingNode, /*dynamicIndentation*/ undefined));
|
|
291
|
+
if (options.trimTrailingWhitespace !== false) {
|
|
292
|
+
trimTrailingWhitespacesForRemainingRange();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return edits;
|
|
297
|
+
// local functions
|
|
298
|
+
/** Tries to compute the indentation for a list element.
|
|
299
|
+
* If list element is not in range then
|
|
300
|
+
* function will pick its actual indentation
|
|
301
|
+
* so it can be pushed downstream as inherited indentation.
|
|
302
|
+
* If list element is in the range - its indentation will be equal
|
|
303
|
+
* to inherited indentation from its predecessors.
|
|
304
|
+
*/
|
|
305
|
+
function tryComputeIndentationForListItem(startPos, endPos, parentStartLine, range, inheritedIndentation) {
|
|
306
|
+
if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
|
|
307
|
+
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
|
|
308
|
+
if (inheritedIndentation !== -1 /* Constants.Unknown */) {
|
|
309
|
+
return inheritedIndentation;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
const startLine = sourceFile.getLineAndCharacterOfPosition(startPos).line;
|
|
314
|
+
const startLinePosition = getLineStartPositionForPosition(startPos, sourceFile);
|
|
315
|
+
const column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
|
|
316
|
+
if (startLine !== parentStartLine || startPos === column) {
|
|
317
|
+
// Use the base indent size if it is greater than
|
|
318
|
+
// the indentation of the inherited predecessor.
|
|
319
|
+
const baseIndentSize = SmartIndenter.getBaseIndentation(options);
|
|
320
|
+
return baseIndentSize > column ? baseIndentSize : column;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return -1 /* Constants.Unknown */;
|
|
324
|
+
}
|
|
325
|
+
function computeIndentation(node, startLine, inheritedIndentation, parent, parentDynamicIndentation, effectiveParentStartLine) {
|
|
326
|
+
const delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize : 0;
|
|
327
|
+
if (effectiveParentStartLine === startLine) {
|
|
328
|
+
// if node is located on the same line with the parent
|
|
329
|
+
// - inherit indentation from the parent
|
|
330
|
+
// - push children if either parent of node itself has non-zero delta
|
|
331
|
+
return {
|
|
332
|
+
indentation: startLine === lastIndentedLine ? indentationOnLastIndentedLine : parentDynamicIndentation.getIndentation(),
|
|
333
|
+
delta: Math.min(options.indentSize, parentDynamicIndentation.getDelta(node) + delta)
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
else if (inheritedIndentation === -1 /* Constants.Unknown */) {
|
|
337
|
+
if (node.kind === SyntaxKind.OpenParenToken && startLine === lastIndentedLine) {
|
|
338
|
+
// the is used for chaining methods formatting
|
|
339
|
+
// - we need to get the indentation on last line and the delta of parent
|
|
340
|
+
return { indentation: indentationOnLastIndentedLine, delta: parentDynamicIndentation.getDelta(node) };
|
|
341
|
+
}
|
|
342
|
+
else if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) {
|
|
343
|
+
return { indentation: parentDynamicIndentation.getIndentation(), delta };
|
|
344
|
+
}
|
|
345
|
+
else if (SmartIndenter.argumentStartsOnSameLineAsPreviousArgument(parent, node, startLine, sourceFile)) {
|
|
346
|
+
return { indentation: parentDynamicIndentation.getIndentation(), delta };
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return { indentation: parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(node), delta };
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
return { indentation: inheritedIndentation, delta };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Na tag, também o primeiro token de um nó com decorators, que só o recuo depois deles lia (CK-02).
|
|
357
|
+
function getDynamicIndentation(node, nodeStartLine, indentation, delta) {
|
|
358
|
+
return {
|
|
359
|
+
getIndentationForComment: (kind, tokenIndentation, container) => {
|
|
360
|
+
switch (kind) {
|
|
361
|
+
// preceding comment to the token that closes the indentation scope inherits the indentation from the scope
|
|
362
|
+
// .. {
|
|
363
|
+
// // comment
|
|
364
|
+
// }
|
|
365
|
+
case SyntaxKind.CloseBraceToken:
|
|
366
|
+
case SyntaxKind.CloseBracketToken:
|
|
367
|
+
case SyntaxKind.CloseParenToken:
|
|
368
|
+
return indentation + getDelta(container);
|
|
369
|
+
}
|
|
370
|
+
return tokenIndentation !== -1 /* Constants.Unknown */ ? tokenIndentation : indentation;
|
|
371
|
+
},
|
|
372
|
+
// if list end token is LessThanToken '>' then its delta should be explicitly suppressed
|
|
373
|
+
// so that LessThanToken as a binary operator can still be indented.
|
|
374
|
+
// foo.then
|
|
375
|
+
// <
|
|
376
|
+
// number,
|
|
377
|
+
// string,
|
|
378
|
+
// >();
|
|
379
|
+
// vs
|
|
380
|
+
// var a = xValue
|
|
381
|
+
// > yValue;
|
|
382
|
+
getIndentationForToken: (line, kind, container, suppressDelta) => !suppressDelta && shouldAddDelta(line, kind, container) ? indentation + getDelta(container) : indentation,
|
|
383
|
+
getIndentation: () => indentation,
|
|
384
|
+
getDelta,
|
|
385
|
+
recomputeIndentation: (lineAdded, parent) => {
|
|
386
|
+
if (SmartIndenter.shouldIndentChildNode(options, parent, node, sourceFile)) {
|
|
387
|
+
indentation += lineAdded ? options.indentSize : -options.indentSize;
|
|
388
|
+
delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize : 0;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
function shouldAddDelta(line, kind, container) {
|
|
393
|
+
switch (kind) {
|
|
394
|
+
// open and close brace, 'else' and 'while' (in do statement) tokens has indentation of the parent
|
|
395
|
+
case SyntaxKind.OpenBraceToken:
|
|
396
|
+
case SyntaxKind.CloseBraceToken:
|
|
397
|
+
case SyntaxKind.CloseParenToken:
|
|
398
|
+
case SyntaxKind.ElseKeyword:
|
|
399
|
+
case SyntaxKind.WhileKeyword:
|
|
400
|
+
case SyntaxKind.AtToken:
|
|
401
|
+
return false;
|
|
402
|
+
case SyntaxKind.SlashToken:
|
|
403
|
+
case SyntaxKind.GreaterThanToken:
|
|
404
|
+
switch (container.kind) {
|
|
405
|
+
case SyntaxKind.JsxOpeningElement:
|
|
406
|
+
case SyntaxKind.JsxClosingElement:
|
|
407
|
+
case SyntaxKind.JsxSelfClosingElement:
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
break;
|
|
411
|
+
case SyntaxKind.OpenBracketToken:
|
|
412
|
+
case SyntaxKind.CloseBracketToken:
|
|
413
|
+
if (container.kind !== SyntaxKind.MappedType) {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
// if token line equals to the line of containing node (this is a first token in the node) - use node indentation
|
|
419
|
+
// Na tag, também não recuava o primeiro token depois dos decorators (CK-02).
|
|
420
|
+
return nodeStartLine !== line;
|
|
421
|
+
}
|
|
422
|
+
function getDelta(child) {
|
|
423
|
+
// Delta value should be zero when the node explicitly prevents indentation of the child node
|
|
424
|
+
return SmartIndenter.nodeWillIndentChild(options, node, child, sourceFile, /*indentByDefault*/ true) ? delta : 0;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
function processNode(node, contextNode, nodeStartLine, undecoratedNodeStartLine, indentation, delta) {
|
|
428
|
+
if (!rangeOverlapsWithStartEnd(originalRange, node.getStart(sourceFile), node.getEnd())) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const nodeDynamicIndentation = getDynamicIndentation(node, nodeStartLine, indentation, delta);
|
|
432
|
+
// a useful observations when tracking context node
|
|
433
|
+
// /
|
|
434
|
+
// [a]
|
|
435
|
+
// / | \
|
|
436
|
+
// [b] [c] [d]
|
|
437
|
+
// node 'a' is a context node for nodes 'b', 'c', 'd'
|
|
438
|
+
// except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a'
|
|
439
|
+
// this rule can be applied recursively to child nodes of 'a'.
|
|
440
|
+
//
|
|
441
|
+
// context node is set to parent node value after processing every child node
|
|
442
|
+
// context node is set to parent of the token after processing every token
|
|
443
|
+
let childContextNode = contextNode;
|
|
444
|
+
// if there are any tokens that logically belong to node and interleave child nodes
|
|
445
|
+
// such tokens will be consumed in processChildNode for the child that follows them
|
|
446
|
+
forEachChild(node, child => {
|
|
447
|
+
processChildNode(child, /*inheritedIndentation*/ -1 /* Constants.Unknown */, node, nodeDynamicIndentation, nodeStartLine, undecoratedNodeStartLine, /*isListItem*/ false);
|
|
448
|
+
}, nodes => {
|
|
449
|
+
processChildNodes(nodes, node, nodeStartLine, nodeDynamicIndentation);
|
|
450
|
+
});
|
|
451
|
+
// proceed any tokens in the node that are located after child nodes
|
|
452
|
+
while (formattingScanner.isOnToken()) {
|
|
453
|
+
const tokenInfo = formattingScanner.readTokenInfo(node);
|
|
454
|
+
if (tokenInfo.token.end > node.end) {
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
if (node.kind === SyntaxKind.JsxText) {
|
|
458
|
+
// Intentation rules for jsx text are handled by `indentMultilineCommentOrJsxText` inside `processChildNode`; just fastforward past it here
|
|
459
|
+
formattingScanner.advance();
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation, node);
|
|
463
|
+
}
|
|
464
|
+
if (!node.parent && formattingScanner.isOnEOF()) {
|
|
465
|
+
const token = formattingScanner.readEOFTokenRange();
|
|
466
|
+
if (token.end <= node.end && previousRange) {
|
|
467
|
+
processPair(token, sourceFile.getLineAndCharacterOfPosition(token.pos).line, node, previousRange, previousRangeStartLine, previousParent, contextNode, nodeDynamicIndentation);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function processChildNode(child, inheritedIndentation, parent, parentDynamicIndentation, parentStartLine, undecoratedParentStartLine, isListItem, isFirstListItem) {
|
|
471
|
+
const childStartPos = child.getStart(sourceFile);
|
|
472
|
+
const childStartLine = sourceFile.getLineAndCharacterOfPosition(childStartPos).line;
|
|
473
|
+
// Na tag, a linha do filho com decorators era a do primeiro token depois deles (CK-02).
|
|
474
|
+
const undecoratedChildStartLine = childStartLine;
|
|
475
|
+
// if child is a list item - try to get its indentation, only if parent is within the original range.
|
|
476
|
+
let childIndentationAmount = -1 /* Constants.Unknown */;
|
|
477
|
+
if (isListItem && rangeContainsRange(originalRange, parent)) {
|
|
478
|
+
childIndentationAmount = tryComputeIndentationForListItem(childStartPos, child.end, parentStartLine, originalRange, inheritedIndentation);
|
|
479
|
+
if (childIndentationAmount !== -1 /* Constants.Unknown */) {
|
|
480
|
+
inheritedIndentation = childIndentationAmount;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// child node is outside the target range - do not dive inside
|
|
484
|
+
if (!rangeOverlapsWithStartEnd(originalRange, child.pos, child.end)) {
|
|
485
|
+
if (child.end < originalRange.pos) {
|
|
486
|
+
formattingScanner.skipToEndOf(child);
|
|
487
|
+
}
|
|
488
|
+
return inheritedIndentation;
|
|
489
|
+
}
|
|
490
|
+
if (child.getFullWidth() === 0) {
|
|
491
|
+
return inheritedIndentation;
|
|
492
|
+
}
|
|
493
|
+
while (formattingScanner.isOnToken()) {
|
|
494
|
+
// proceed any parent tokens that are located prior to child.getStart()
|
|
495
|
+
const tokenInfo = formattingScanner.readTokenInfo(node);
|
|
496
|
+
if (tokenInfo.token.end > childStartPos) {
|
|
497
|
+
// stop when formatting scanner advances past the beginning of the child
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation, node);
|
|
501
|
+
}
|
|
502
|
+
if (!formattingScanner.isOnToken()) {
|
|
503
|
+
return inheritedIndentation;
|
|
504
|
+
}
|
|
505
|
+
// JSX text shouldn't affect indenting
|
|
506
|
+
if (isToken(child) && child.kind !== SyntaxKind.JsxText) {
|
|
507
|
+
// if child node is a token, it does not impact indentation, proceed it using parent indentation scope rules
|
|
508
|
+
const tokenInfo = formattingScanner.readTokenInfo(child);
|
|
509
|
+
Debug.assert(tokenInfo.token.end === child.end, "Token end is child end");
|
|
510
|
+
consumeTokenAndAdvanceScanner(tokenInfo, node, parentDynamicIndentation, child);
|
|
511
|
+
return inheritedIndentation;
|
|
512
|
+
}
|
|
513
|
+
// Na tag, o decorator se recuava pela linha dele (CK-02).
|
|
514
|
+
const effectiveParentStartLine = undecoratedParentStartLine;
|
|
515
|
+
const childIndentation = computeIndentation(child, childStartLine, childIndentationAmount, node, parentDynamicIndentation, effectiveParentStartLine);
|
|
516
|
+
processNode(child, childContextNode, childStartLine, undecoratedChildStartLine, childIndentation.indentation, childIndentation.delta);
|
|
517
|
+
if (child.kind === SyntaxKind.JsxText) {
|
|
518
|
+
const range = { pos: child.getStart(), end: child.getEnd() };
|
|
519
|
+
if (range.pos !== range.end) { // don't indent zero-width jsx text
|
|
520
|
+
const siblings = parent.getChildren(sourceFile);
|
|
521
|
+
const currentIndex = findIndex(siblings, arg => arg.pos === child.pos);
|
|
522
|
+
const previousNode = siblings[currentIndex - 1];
|
|
523
|
+
if (previousNode) {
|
|
524
|
+
// The jsx text needs no indentation whatsoever if it ends on the same line the previous sibling ends on
|
|
525
|
+
if (sourceFile.getLineAndCharacterOfPosition(range.end).line !== sourceFile.getLineAndCharacterOfPosition(previousNode.end).line) {
|
|
526
|
+
// The first line is (already) "indented" if the text starts on the same line as the previous sibling element ends on
|
|
527
|
+
const firstLineIsIndented = sourceFile.getLineAndCharacterOfPosition(range.pos).line === sourceFile.getLineAndCharacterOfPosition(previousNode.end).line;
|
|
528
|
+
indentMultilineCommentOrJsxText(range, childIndentation.indentation, firstLineIsIndented, /*indentFinalLine*/ false, /*jsxStyle*/ true);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
childContextNode = node;
|
|
534
|
+
if (isFirstListItem && parent.kind === SyntaxKind.ArrayLiteralExpression && inheritedIndentation === -1 /* Constants.Unknown */) {
|
|
535
|
+
inheritedIndentation = childIndentation.indentation;
|
|
536
|
+
}
|
|
537
|
+
return inheritedIndentation;
|
|
538
|
+
}
|
|
539
|
+
function processChildNodes(nodes, parent, parentStartLine, parentDynamicIndentation) {
|
|
540
|
+
Debug.assert(isNodeArray(nodes));
|
|
541
|
+
const listStartToken = getOpenTokenForList(parent, nodes);
|
|
542
|
+
let listDynamicIndentation = parentDynamicIndentation;
|
|
543
|
+
let startLine = parentStartLine;
|
|
544
|
+
if (listStartToken !== SyntaxKind.Unknown) {
|
|
545
|
+
// introduce a new indentation scope for lists (including list start and end tokens)
|
|
546
|
+
while (formattingScanner.isOnToken()) {
|
|
547
|
+
const tokenInfo = formattingScanner.readTokenInfo(parent);
|
|
548
|
+
if (tokenInfo.token.end > nodes.pos) {
|
|
549
|
+
// stop when formatting scanner moves past the beginning of node list
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
else if (tokenInfo.token.kind === listStartToken) {
|
|
553
|
+
// consume list start token
|
|
554
|
+
startLine = sourceFile.getLineAndCharacterOfPosition(tokenInfo.token.pos).line;
|
|
555
|
+
consumeTokenAndAdvanceScanner(tokenInfo, parent, parentDynamicIndentation, parent);
|
|
556
|
+
let indentationOnListStartToken;
|
|
557
|
+
if (indentationOnLastIndentedLine !== -1 /* Constants.Unknown */) {
|
|
558
|
+
// scanner just processed list start token so consider last indentation as list indentation
|
|
559
|
+
// function foo(): { // last indentation was 0, list item will be indented based on this value
|
|
560
|
+
// foo: number;
|
|
561
|
+
// }: {};
|
|
562
|
+
indentationOnListStartToken = indentationOnLastIndentedLine;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
const startLinePosition = getLineStartPositionForPosition(tokenInfo.token.pos, sourceFile);
|
|
566
|
+
indentationOnListStartToken = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, tokenInfo.token.pos, sourceFile, options);
|
|
567
|
+
}
|
|
568
|
+
listDynamicIndentation = getDynamicIndentation(parent, parentStartLine, indentationOnListStartToken, options.indentSize); // TODO: GH#18217
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
// consume any tokens that precede the list as child elements of 'node' using its indentation scope
|
|
572
|
+
consumeTokenAndAdvanceScanner(tokenInfo, parent, parentDynamicIndentation, parent);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
let inheritedIndentation = -1 /* Constants.Unknown */;
|
|
577
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
578
|
+
const child = nodes[i];
|
|
579
|
+
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListItem*/ true, /*isFirstListItem*/ i === 0);
|
|
580
|
+
}
|
|
581
|
+
const listEndToken = getCloseTokenForOpenToken(listStartToken);
|
|
582
|
+
if (listEndToken !== SyntaxKind.Unknown && formattingScanner.isOnToken()) {
|
|
583
|
+
let tokenInfo = formattingScanner.readTokenInfo(parent);
|
|
584
|
+
if (tokenInfo.token.kind === SyntaxKind.CommaToken && isCallLikeExpression(parent)) {
|
|
585
|
+
const commaTokenLine = sourceFile.getLineAndCharacterOfPosition(tokenInfo.token.pos).line;
|
|
586
|
+
if (startLine !== commaTokenLine) {
|
|
587
|
+
formattingScanner.advance();
|
|
588
|
+
tokenInfo = formattingScanner.isOnToken() ? formattingScanner.readTokenInfo(parent) : undefined;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
// consume the list end token only if it is still belong to the parent
|
|
592
|
+
// there might be the case when current token matches end token but does not considered as one
|
|
593
|
+
// function (x: function) <--
|
|
594
|
+
// without this check close paren will be interpreted as list end token for function expression which is wrong
|
|
595
|
+
if (tokenInfo && tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) {
|
|
596
|
+
// consume list end token
|
|
597
|
+
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation, parent, /*isListEndToken*/ true);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
function consumeTokenAndAdvanceScanner(currentTokenInfo, parent, dynamicIndentation, container, isListEndToken) {
|
|
602
|
+
Debug.assert(rangeContainsRange(parent, currentTokenInfo.token));
|
|
603
|
+
const lastTriviaWasNewLine = formattingScanner.lastTrailingTriviaWasNewLine();
|
|
604
|
+
let indentToken = false;
|
|
605
|
+
if (currentTokenInfo.leadingTrivia) {
|
|
606
|
+
processTrivia(currentTokenInfo.leadingTrivia, parent, childContextNode, dynamicIndentation);
|
|
607
|
+
}
|
|
608
|
+
let lineAction = 0 /* LineAction.None */;
|
|
609
|
+
const isTokenInRange = rangeContainsRange(originalRange, currentTokenInfo.token);
|
|
610
|
+
const tokenStart = sourceFile.getLineAndCharacterOfPosition(currentTokenInfo.token.pos);
|
|
611
|
+
if (isTokenInRange) {
|
|
612
|
+
const rangeHasError = rangeContainsError(currentTokenInfo.token);
|
|
613
|
+
// save previousRange since processRange will overwrite this value with current one
|
|
614
|
+
const savePreviousRange = previousRange;
|
|
615
|
+
lineAction = processRange(currentTokenInfo.token, tokenStart, parent, childContextNode, dynamicIndentation);
|
|
616
|
+
// do not indent comments\token if token range overlaps with some error
|
|
617
|
+
if (!rangeHasError) {
|
|
618
|
+
if (lineAction === 0 /* LineAction.None */) {
|
|
619
|
+
// indent token only if end line of previous range does not match start line of the token
|
|
620
|
+
const prevEndLine = savePreviousRange && sourceFile.getLineAndCharacterOfPosition(savePreviousRange.end).line;
|
|
621
|
+
indentToken = lastTriviaWasNewLine && tokenStart.line !== prevEndLine;
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
indentToken = lineAction === 1 /* LineAction.LineAdded */;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (currentTokenInfo.trailingTrivia) {
|
|
629
|
+
processTrivia(currentTokenInfo.trailingTrivia, parent, childContextNode, dynamicIndentation);
|
|
630
|
+
}
|
|
631
|
+
if (indentToken) {
|
|
632
|
+
const tokenIndentation = (isTokenInRange && !rangeContainsError(currentTokenInfo.token)) ?
|
|
633
|
+
dynamicIndentation.getIndentationForToken(tokenStart.line, currentTokenInfo.token.kind, container, !!isListEndToken) :
|
|
634
|
+
-1 /* Constants.Unknown */;
|
|
635
|
+
let indentNextTokenOrTrivia = true;
|
|
636
|
+
if (currentTokenInfo.leadingTrivia) {
|
|
637
|
+
const commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
|
|
638
|
+
indentNextTokenOrTrivia = indentTriviaItems(currentTokenInfo.leadingTrivia, commentIndentation, indentNextTokenOrTrivia, item => insertIndentation(item.pos, commentIndentation, /*lineAdded*/ false));
|
|
639
|
+
}
|
|
640
|
+
// indent token only if is it is in target range and does not overlap with any error ranges
|
|
641
|
+
if (tokenIndentation !== -1 /* Constants.Unknown */ && indentNextTokenOrTrivia) {
|
|
642
|
+
insertIndentation(currentTokenInfo.token.pos, tokenIndentation, lineAction === 1 /* LineAction.LineAdded */);
|
|
643
|
+
lastIndentedLine = tokenStart.line;
|
|
644
|
+
indentationOnLastIndentedLine = tokenIndentation;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
formattingScanner.advance();
|
|
648
|
+
childContextNode = parent;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function indentTriviaItems(trivia, commentIndentation, indentNextTokenOrTrivia, indentSingleLine) {
|
|
652
|
+
for (const triviaItem of trivia) {
|
|
653
|
+
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
|
|
654
|
+
switch (triviaItem.kind) {
|
|
655
|
+
case SyntaxKind.MultiLineCommentTrivia:
|
|
656
|
+
if (triviaInRange) {
|
|
657
|
+
indentMultilineCommentOrJsxText(triviaItem, commentIndentation, /*firstLineIsIndented*/ !indentNextTokenOrTrivia);
|
|
658
|
+
}
|
|
659
|
+
indentNextTokenOrTrivia = false;
|
|
660
|
+
break;
|
|
661
|
+
case SyntaxKind.SingleLineCommentTrivia:
|
|
662
|
+
if (indentNextTokenOrTrivia && triviaInRange) {
|
|
663
|
+
indentSingleLine(triviaItem);
|
|
664
|
+
}
|
|
665
|
+
indentNextTokenOrTrivia = false;
|
|
666
|
+
break;
|
|
667
|
+
case SyntaxKind.NewLineTrivia:
|
|
668
|
+
indentNextTokenOrTrivia = true;
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return indentNextTokenOrTrivia;
|
|
673
|
+
}
|
|
674
|
+
function processTrivia(trivia, parent, contextNode, dynamicIndentation) {
|
|
675
|
+
for (const triviaItem of trivia) {
|
|
676
|
+
if (isComment(triviaItem.kind) && rangeContainsRange(originalRange, triviaItem)) {
|
|
677
|
+
const triviaItemStart = sourceFile.getLineAndCharacterOfPosition(triviaItem.pos);
|
|
678
|
+
processRange(triviaItem, triviaItemStart, parent, contextNode, dynamicIndentation);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function processRange(range, rangeStart, parent, contextNode, dynamicIndentation) {
|
|
683
|
+
const rangeHasError = rangeContainsError(range);
|
|
684
|
+
let lineAction = 0 /* LineAction.None */;
|
|
685
|
+
if (!rangeHasError) {
|
|
686
|
+
if (!previousRange) {
|
|
687
|
+
// trim whitespaces starting from the beginning of the span up to the current line
|
|
688
|
+
const originalStart = sourceFile.getLineAndCharacterOfPosition(originalRange.pos);
|
|
689
|
+
trimTrailingWhitespacesForLines(originalStart.line, rangeStart.line);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
lineAction =
|
|
693
|
+
processPair(range, rangeStart.line, parent, previousRange, previousRangeStartLine, previousParent, contextNode, dynamicIndentation);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
previousRange = range;
|
|
697
|
+
previousParent = parent;
|
|
698
|
+
previousRangeStartLine = rangeStart.line;
|
|
699
|
+
return lineAction;
|
|
700
|
+
}
|
|
701
|
+
function processPair(currentItem, currentStartLine, currentParent, previousItem, previousStartLine, previousParent, contextNode, dynamicIndentation) {
|
|
702
|
+
formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode);
|
|
703
|
+
const rules = getRules(formattingContext);
|
|
704
|
+
let trimTrailingWhitespaces = formattingContext.options.trimTrailingWhitespace !== false;
|
|
705
|
+
let lineAction = 0 /* LineAction.None */;
|
|
706
|
+
if (rules) {
|
|
707
|
+
// Apply rules in reverse order so that higher priority rules (which are first in the array)
|
|
708
|
+
// win in a conflict with lower priority rules.
|
|
709
|
+
forEachRight(rules, rule => {
|
|
710
|
+
lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine);
|
|
711
|
+
switch (lineAction) {
|
|
712
|
+
case 2 /* LineAction.LineRemoved */:
|
|
713
|
+
// Handle the case where the next line is moved to be the end of this line.
|
|
714
|
+
// In this case we don't indent the next line in the next pass.
|
|
715
|
+
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
|
716
|
+
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false, contextNode);
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
case 1 /* LineAction.LineAdded */:
|
|
720
|
+
// Handle the case where token2 is moved to the new line.
|
|
721
|
+
// In this case we indent token2 in the next pass but we set
|
|
722
|
+
// sameLineIndent flag to notify the indenter that the indentation is within the line.
|
|
723
|
+
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
|
724
|
+
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true, contextNode);
|
|
725
|
+
}
|
|
726
|
+
break;
|
|
727
|
+
default:
|
|
728
|
+
Debug.assert(lineAction === 0 /* LineAction.None */);
|
|
729
|
+
}
|
|
730
|
+
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
|
731
|
+
trimTrailingWhitespaces = trimTrailingWhitespaces && !(rule.action & 16 /* RuleAction.DeleteSpace */) && rule.flags !== 1 /* RuleFlags.CanDeleteNewLines */;
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
trimTrailingWhitespaces = trimTrailingWhitespaces && currentItem.kind !== SyntaxKind.EndOfFileToken;
|
|
736
|
+
}
|
|
737
|
+
if (currentStartLine !== previousStartLine && trimTrailingWhitespaces) {
|
|
738
|
+
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
|
|
739
|
+
trimTrailingWhitespacesForLines(previousStartLine, currentStartLine, previousItem);
|
|
740
|
+
}
|
|
741
|
+
return lineAction;
|
|
742
|
+
}
|
|
743
|
+
function insertIndentation(pos, indentation, lineAdded) {
|
|
744
|
+
const indentationString = getIndentationString(indentation, options);
|
|
745
|
+
if (lineAdded) {
|
|
746
|
+
// new line is added before the token by the formatting rules
|
|
747
|
+
// insert indentation string at the very beginning of the token
|
|
748
|
+
recordReplace(pos, 0, indentationString);
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
const tokenStart = sourceFile.getLineAndCharacterOfPosition(pos);
|
|
752
|
+
const startLinePosition = getStartPositionOfLine(tokenStart.line, sourceFile);
|
|
753
|
+
if (indentation !== characterToColumn(startLinePosition, tokenStart.character) || indentationIsDifferent(indentationString, startLinePosition)) {
|
|
754
|
+
recordReplace(startLinePosition, tokenStart.character, indentationString);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function characterToColumn(startLinePosition, characterInLine) {
|
|
759
|
+
let column = 0;
|
|
760
|
+
for (let i = 0; i < characterInLine; i++) {
|
|
761
|
+
if (sourceFile.text.charCodeAt(startLinePosition + i) === CharacterCodes.tab) {
|
|
762
|
+
column += options.tabSize - column % options.tabSize;
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
column++;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return column;
|
|
769
|
+
}
|
|
770
|
+
function indentationIsDifferent(indentationString, startLinePosition) {
|
|
771
|
+
return indentationString !== sourceFile.text.substr(startLinePosition, indentationString.length);
|
|
772
|
+
}
|
|
773
|
+
function indentMultilineCommentOrJsxText(commentRange, indentation, firstLineIsIndented, indentFinalLine = true, jsxTextStyleIndent) {
|
|
774
|
+
// split comment in lines
|
|
775
|
+
let startLine = sourceFile.getLineAndCharacterOfPosition(commentRange.pos).line;
|
|
776
|
+
const endLine = sourceFile.getLineAndCharacterOfPosition(commentRange.end).line;
|
|
777
|
+
if (startLine === endLine) {
|
|
778
|
+
if (!firstLineIsIndented) {
|
|
779
|
+
// treat as single line comment
|
|
780
|
+
insertIndentation(commentRange.pos, indentation, /*lineAdded*/ false);
|
|
781
|
+
}
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
const parts = [];
|
|
785
|
+
let startPos = commentRange.pos;
|
|
786
|
+
for (let line = startLine; line < endLine; line++) {
|
|
787
|
+
const endOfLine = getEndLinePosition(line, sourceFile);
|
|
788
|
+
parts.push({ pos: startPos, end: endOfLine });
|
|
789
|
+
startPos = getStartPositionOfLine(line + 1, sourceFile);
|
|
790
|
+
}
|
|
791
|
+
if (indentFinalLine) {
|
|
792
|
+
parts.push({ pos: startPos, end: commentRange.end });
|
|
793
|
+
}
|
|
794
|
+
if (parts.length === 0)
|
|
795
|
+
return;
|
|
796
|
+
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
|
|
797
|
+
const nonWhitespaceColumnInFirstPart = SmartIndenter.findFirstNonWhitespaceCharacterAndColumn(startLinePos, parts[0].pos, sourceFile, options);
|
|
798
|
+
if (indentation === nonWhitespaceColumnInFirstPart.column && !jsxTextStyleIndent) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
let startIndex = 0;
|
|
802
|
+
if (firstLineIsIndented) {
|
|
803
|
+
startIndex = 1;
|
|
804
|
+
startLine++;
|
|
805
|
+
}
|
|
806
|
+
// shift all parts on the delta size
|
|
807
|
+
let delta = indentation - nonWhitespaceColumnInFirstPart.column;
|
|
808
|
+
for (let i = startIndex; i < parts.length; i++, startLine++) {
|
|
809
|
+
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
|
|
810
|
+
const nonWhitespaceCharacterAndColumn = i === 0
|
|
811
|
+
? nonWhitespaceColumnInFirstPart
|
|
812
|
+
: SmartIndenter.findFirstNonWhitespaceCharacterAndColumn(parts[i].pos, parts[i].end, sourceFile, options);
|
|
813
|
+
if (jsxTextStyleIndent) {
|
|
814
|
+
// skip adding indentation to blank lines
|
|
815
|
+
if (isLineBreak(sourceFile.text.charCodeAt(getStartPositionOfLine(startLine, sourceFile))))
|
|
816
|
+
continue;
|
|
817
|
+
// reset delta on every line
|
|
818
|
+
delta = indentation - nonWhitespaceCharacterAndColumn.column;
|
|
819
|
+
}
|
|
820
|
+
const newIndentation = nonWhitespaceCharacterAndColumn.column + delta;
|
|
821
|
+
if (newIndentation > 0) {
|
|
822
|
+
const indentationString = getIndentationString(newIndentation, options);
|
|
823
|
+
recordReplace(startLinePos, nonWhitespaceCharacterAndColumn.character, indentationString);
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
recordDelete(startLinePos, nonWhitespaceCharacterAndColumn.character);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function trimTrailingWhitespacesForLines(line1, line2, range) {
|
|
831
|
+
for (let line = line1; line < line2; line++) {
|
|
832
|
+
const lineStartPosition = getStartPositionOfLine(line, sourceFile);
|
|
833
|
+
const lineEndPosition = getEndLinePosition(line, sourceFile);
|
|
834
|
+
// do not trim whitespaces in comments or template expression
|
|
835
|
+
if (range && (isComment(range.kind) || isStringOrRegularExpressionOrTemplateLiteral(range.kind)) && range.pos <= lineEndPosition && range.end > lineEndPosition) {
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
const whitespaceStart = getTrailingWhitespaceStartPosition(lineStartPosition, lineEndPosition);
|
|
839
|
+
if (whitespaceStart !== -1) {
|
|
840
|
+
Debug.assert(whitespaceStart === lineStartPosition || !isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(whitespaceStart - 1)));
|
|
841
|
+
recordDelete(whitespaceStart, lineEndPosition + 1 - whitespaceStart);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* @param start The position of the first character in range
|
|
847
|
+
* @param end The position of the last character in range
|
|
848
|
+
*/
|
|
849
|
+
function getTrailingWhitespaceStartPosition(start, end) {
|
|
850
|
+
let pos = end;
|
|
851
|
+
while (pos >= start && isWhiteSpaceSingleLine(sourceFile.text.charCodeAt(pos))) {
|
|
852
|
+
pos--;
|
|
853
|
+
}
|
|
854
|
+
if (pos !== end) {
|
|
855
|
+
return pos + 1;
|
|
856
|
+
}
|
|
857
|
+
return -1;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Trimming will be done for lines after the previous range
|
|
861
|
+
*/
|
|
862
|
+
function trimTrailingWhitespacesForRemainingRange() {
|
|
863
|
+
const startPosition = previousRange ? previousRange.end : originalRange.pos;
|
|
864
|
+
const startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
|
|
865
|
+
const endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
|
|
866
|
+
trimTrailingWhitespacesForLines(startLine, endLine + 1, previousRange);
|
|
867
|
+
}
|
|
868
|
+
function recordDelete(start, len) {
|
|
869
|
+
if (len) {
|
|
870
|
+
edits.push(createTextChangeFromStartLength(start, len, ""));
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function recordReplace(start, len, newText) {
|
|
874
|
+
if (len || newText) {
|
|
875
|
+
edits.push(createTextChangeFromStartLength(start, len, newText));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
function recordInsert(start, text) {
|
|
879
|
+
if (text) {
|
|
880
|
+
edits.push(createTextChangeFromStartLength(start, 0, text));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function applyRuleEdits(rule, previousRange, previousStartLine, currentRange, currentStartLine) {
|
|
884
|
+
const onLaterLine = currentStartLine !== previousStartLine;
|
|
885
|
+
switch (rule.action) {
|
|
886
|
+
case 1 /* RuleAction.StopProcessingSpaceActions */:
|
|
887
|
+
// no action required
|
|
888
|
+
return 0 /* LineAction.None */;
|
|
889
|
+
case 16 /* RuleAction.DeleteSpace */:
|
|
890
|
+
if (previousRange.end !== currentRange.pos) {
|
|
891
|
+
// delete characters starting from t1.end up to t2.pos exclusive
|
|
892
|
+
recordDelete(previousRange.end, currentRange.pos - previousRange.end);
|
|
893
|
+
return onLaterLine ? 2 /* LineAction.LineRemoved */ : 0 /* LineAction.None */;
|
|
894
|
+
}
|
|
895
|
+
break;
|
|
896
|
+
case 32 /* RuleAction.DeleteToken */:
|
|
897
|
+
recordDelete(previousRange.pos, previousRange.end - previousRange.pos);
|
|
898
|
+
break;
|
|
899
|
+
case 8 /* RuleAction.InsertNewLine */:
|
|
900
|
+
// exit early if we on different lines and rule cannot change number of newlines
|
|
901
|
+
// if line1 and line2 are on subsequent lines then no edits are required - ok to exit
|
|
902
|
+
// if line1 and line2 are separated with more than one newline - ok to exit since we cannot delete extra new lines
|
|
903
|
+
if (rule.flags !== 1 /* RuleFlags.CanDeleteNewLines */ && previousStartLine !== currentStartLine) {
|
|
904
|
+
return 0 /* LineAction.None */;
|
|
905
|
+
}
|
|
906
|
+
// edit should not be applied if we have one line feed between elements
|
|
907
|
+
const lineDelta = currentStartLine - previousStartLine;
|
|
908
|
+
if (lineDelta !== 1) {
|
|
909
|
+
recordReplace(previousRange.end, currentRange.pos - previousRange.end, getNewLineOrDefaultFromHost(host, options));
|
|
910
|
+
return onLaterLine ? 0 /* LineAction.None */ : 1 /* LineAction.LineAdded */;
|
|
911
|
+
}
|
|
912
|
+
break;
|
|
913
|
+
case 4 /* RuleAction.InsertSpace */:
|
|
914
|
+
// exit early if we on different lines and rule cannot change number of newlines
|
|
915
|
+
if (rule.flags !== 1 /* RuleFlags.CanDeleteNewLines */ && previousStartLine !== currentStartLine) {
|
|
916
|
+
return 0 /* LineAction.None */;
|
|
917
|
+
}
|
|
918
|
+
const posDelta = currentRange.pos - previousRange.end;
|
|
919
|
+
if (posDelta !== 1 || sourceFile.text.charCodeAt(previousRange.end) !== CharacterCodes.space) {
|
|
920
|
+
recordReplace(previousRange.end, currentRange.pos - previousRange.end, " ");
|
|
921
|
+
return onLaterLine ? 2 /* LineAction.LineRemoved */ : 0 /* LineAction.None */;
|
|
922
|
+
}
|
|
923
|
+
break;
|
|
924
|
+
case 64 /* RuleAction.InsertTrailingSemicolon */:
|
|
925
|
+
recordInsert(previousRange.end, ";");
|
|
926
|
+
}
|
|
927
|
+
return 0 /* LineAction.None */;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
var LineAction;
|
|
931
|
+
(function (LineAction) {
|
|
932
|
+
LineAction[LineAction["None"] = 0] = "None";
|
|
933
|
+
LineAction[LineAction["LineAdded"] = 1] = "LineAdded";
|
|
934
|
+
LineAction[LineAction["LineRemoved"] = 2] = "LineRemoved";
|
|
935
|
+
})(LineAction || (LineAction = {}));
|
|
936
|
+
// O `getRangeOfEnclosingComment` da tag morava aqui: ele só usa a árvore, e desceu para o `utilities.ts` da leitura, que o
|
|
937
|
+
// usa sem a edição (CK-67).
|
|
938
|
+
function getOpenTokenForList(node, list) {
|
|
939
|
+
switch (node.kind) {
|
|
940
|
+
// Na tag, também o construtor, a `function`, a function expression e o método (CK-02), e a method signature
|
|
941
|
+
// (CK-36).
|
|
942
|
+
case SyntaxKind.ArrowFunction:
|
|
943
|
+
if (node.typeParameters === list) {
|
|
944
|
+
return SyntaxKind.LessThanToken;
|
|
945
|
+
}
|
|
946
|
+
else if (node.parameters === list) {
|
|
947
|
+
return SyntaxKind.OpenParenToken;
|
|
948
|
+
}
|
|
949
|
+
break;
|
|
950
|
+
// Na tag, também o `new` (CK-02).
|
|
951
|
+
case SyntaxKind.CallExpression:
|
|
952
|
+
if (node.typeArguments === list) {
|
|
953
|
+
return SyntaxKind.LessThanToken;
|
|
954
|
+
}
|
|
955
|
+
else if (node.arguments === list) {
|
|
956
|
+
return SyntaxKind.OpenParenToken;
|
|
957
|
+
}
|
|
958
|
+
break;
|
|
959
|
+
case SyntaxKind.TypeReference:
|
|
960
|
+
if (node.typeArguments === list) {
|
|
961
|
+
return SyntaxKind.LessThanToken;
|
|
962
|
+
}
|
|
963
|
+
break;
|
|
964
|
+
case SyntaxKind.TypeLiteral:
|
|
965
|
+
return SyntaxKind.OpenBraceToken;
|
|
966
|
+
}
|
|
967
|
+
return SyntaxKind.Unknown;
|
|
968
|
+
}
|
|
969
|
+
function getCloseTokenForOpenToken(kind) {
|
|
970
|
+
switch (kind) {
|
|
971
|
+
case SyntaxKind.OpenParenToken:
|
|
972
|
+
return SyntaxKind.CloseParenToken;
|
|
973
|
+
case SyntaxKind.LessThanToken:
|
|
974
|
+
return SyntaxKind.GreaterThanToken;
|
|
975
|
+
case SyntaxKind.OpenBraceToken:
|
|
976
|
+
return SyntaxKind.CloseBraceToken;
|
|
977
|
+
}
|
|
978
|
+
return SyntaxKind.Unknown;
|
|
979
|
+
}
|
|
980
|
+
let internedSizes;
|
|
981
|
+
let internedTabsIndentation;
|
|
982
|
+
let internedSpacesIndentation;
|
|
983
|
+
export function getIndentationString(indentation, options) {
|
|
984
|
+
// reset interned strings if FormatCodeOptions were changed
|
|
985
|
+
const resetInternedStrings = !internedSizes || (internedSizes.tabSize !== options.tabSize || internedSizes.indentSize !== options.indentSize);
|
|
986
|
+
if (resetInternedStrings) {
|
|
987
|
+
internedSizes = { tabSize: options.tabSize, indentSize: options.indentSize };
|
|
988
|
+
internedTabsIndentation = internedSpacesIndentation = undefined;
|
|
989
|
+
}
|
|
990
|
+
if (!options.convertTabsToSpaces) {
|
|
991
|
+
const tabs = Math.floor(indentation / options.tabSize);
|
|
992
|
+
const spaces = indentation - tabs * options.tabSize;
|
|
993
|
+
let tabString;
|
|
994
|
+
if (!internedTabsIndentation) {
|
|
995
|
+
internedTabsIndentation = [];
|
|
996
|
+
}
|
|
997
|
+
if (internedTabsIndentation[tabs] === undefined) {
|
|
998
|
+
internedTabsIndentation[tabs] = tabString = repeatString("\t", tabs);
|
|
999
|
+
}
|
|
1000
|
+
else {
|
|
1001
|
+
tabString = internedTabsIndentation[tabs];
|
|
1002
|
+
}
|
|
1003
|
+
return spaces ? tabString + repeatString(" ", spaces) : tabString;
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
let spacesString;
|
|
1007
|
+
const quotient = Math.floor(indentation / options.indentSize);
|
|
1008
|
+
const remainder = indentation % options.indentSize;
|
|
1009
|
+
if (!internedSpacesIndentation) {
|
|
1010
|
+
internedSpacesIndentation = [];
|
|
1011
|
+
}
|
|
1012
|
+
if (internedSpacesIndentation[quotient] === undefined) {
|
|
1013
|
+
spacesString = repeatString(" ", options.indentSize * quotient);
|
|
1014
|
+
internedSpacesIndentation[quotient] = spacesString;
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
spacesString = internedSpacesIndentation[quotient];
|
|
1018
|
+
}
|
|
1019
|
+
return remainder ? spacesString + repeatString(" ", remainder) : spacesString;
|
|
1020
|
+
}
|
|
1021
|
+
}
|