@tslite/services 0.0.0-stage → 1.13.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,1163 @@
|
|
|
1
|
+
// Modificado de microsoft/TypeScript v3.9.10, src/services/textChanges.ts: o namespace virou módulo ES, com os imports explícitos.
|
|
2
|
+
import { CharacterCodes, Debug, EmitHint, NewLineKind, NodeSet, ScriptTarget, SemicolonPreference, SyntaxKind, addToSeen, contains, createMap, createNodeArray, createObjectLiteral, createParen, createPrinter, createRange, createSourceFile, createTextChange, createTextRangeFromSpan, createTextSpan, createTextSpanFromRange, createTextWriter, createToken, endsWith, find, findChildOfKind, findLastIndex, findNextToken, findPrecedingToken, first, firstOrUndefined, formatting, getAncestor, getFirstNonSpaceCharacterPosition, getLeadingCommentRanges, getLineAndCharacterOfPosition, getLineOfLocalPosition, getLineStartPositionForPosition, getNewLineOrDefaultFromHost, getNodeId, getScriptKindFromFileName, getShebang, getStartPositionOfLine, getTokenAtPosition, getTouchingToken, group, indexOfNode, isAnyImportSyntax, isArray, isArrowFunction, isCallExpression, isExpression, isFunctionLike, isImportClause, isImportDeclaration, isInComment, isInJSXText, isInString, isInTemplateString, isJsonSourceFile, isLineBreak, isNamedImports, isObjectLiteralExpression, isParameter, isPinnedComment, isPrologueDirective, isPropertySignature, isRecognizedTripleSlashComment, isStatement, isStatementButNotDeclaration, isStringLiteral, isTypeElement, isVariableDeclaration, isWhiteSpaceLike, isWhiteSpaceSingleLine, last, mapDefined, nodeIsSynthesized, nullTransformationContext, positionsAreOnSameLine, probablyUsesSemicolons, rangeContainsRangeExclusive, rangeOfNode, rangeOfTypeParameters, rangeStartPositionsAreOnSameLine, removeSuffix, skipTrivia, stableSort, stringContainsAt, textSpanEnd, tokenToString, visitEachChild, visitNodes, } from "./_namespaces/edit.js";
|
|
3
|
+
/**
|
|
4
|
+
* Currently for simplicity we store recovered positions on the node itself.
|
|
5
|
+
* It can be changed to side-table later if we decide that current design is too invasive.
|
|
6
|
+
*/
|
|
7
|
+
function getPos(n) {
|
|
8
|
+
const result = n.__pos;
|
|
9
|
+
Debug.assert(typeof result === "number");
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
function setPos(n, pos) {
|
|
13
|
+
Debug.assert(typeof pos === "number");
|
|
14
|
+
n.__pos = pos;
|
|
15
|
+
}
|
|
16
|
+
function getEnd(n) {
|
|
17
|
+
const result = n.__end;
|
|
18
|
+
Debug.assert(typeof result === "number");
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function setEnd(n, end) {
|
|
22
|
+
Debug.assert(typeof end === "number");
|
|
23
|
+
n.__end = end;
|
|
24
|
+
}
|
|
25
|
+
export var LeadingTriviaOption;
|
|
26
|
+
(function (LeadingTriviaOption) {
|
|
27
|
+
/** Exclude all leading trivia (use getStart()) */
|
|
28
|
+
LeadingTriviaOption[LeadingTriviaOption["Exclude"] = 0] = "Exclude";
|
|
29
|
+
/** Include leading trivia and,
|
|
30
|
+
* if there are no line breaks between the node and the previous token,
|
|
31
|
+
* include all trivia between the node and the previous token
|
|
32
|
+
*/
|
|
33
|
+
LeadingTriviaOption[LeadingTriviaOption["IncludeAll"] = 1] = "IncludeAll";
|
|
34
|
+
// Na tag, também o JSDoc preso ao nó; a cópia não tem o JSDoc, e o comentário `/** */` é um comentário como outro
|
|
35
|
+
// (CK-03).
|
|
36
|
+
/**
|
|
37
|
+
* Only delete trivia on the same line as getStart().
|
|
38
|
+
* Used to avoid deleting leading comments
|
|
39
|
+
*/
|
|
40
|
+
LeadingTriviaOption[LeadingTriviaOption["StartLine"] = 2] = "StartLine";
|
|
41
|
+
})(LeadingTriviaOption || (LeadingTriviaOption = {}));
|
|
42
|
+
export var TrailingTriviaOption;
|
|
43
|
+
(function (TrailingTriviaOption) {
|
|
44
|
+
/** Exclude all trailing trivia (use getEnd()) */
|
|
45
|
+
TrailingTriviaOption[TrailingTriviaOption["Exclude"] = 0] = "Exclude";
|
|
46
|
+
/** Include trailing trivia */
|
|
47
|
+
TrailingTriviaOption[TrailingTriviaOption["Include"] = 1] = "Include";
|
|
48
|
+
})(TrailingTriviaOption || (TrailingTriviaOption = {}));
|
|
49
|
+
function skipWhitespacesAndLineBreaks(text, start) {
|
|
50
|
+
return skipTrivia(text, start, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
|
51
|
+
}
|
|
52
|
+
function hasCommentsBeforeLineBreak(text, start) {
|
|
53
|
+
let i = start;
|
|
54
|
+
while (i < text.length) {
|
|
55
|
+
const ch = text.charCodeAt(i);
|
|
56
|
+
if (isWhiteSpaceSingleLine(ch)) {
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
return ch === CharacterCodes.slash;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const useNonAdjustedPositions = {
|
|
65
|
+
leadingTriviaOption: LeadingTriviaOption.Exclude,
|
|
66
|
+
trailingTriviaOption: TrailingTriviaOption.Exclude,
|
|
67
|
+
};
|
|
68
|
+
var ChangeKind;
|
|
69
|
+
(function (ChangeKind) {
|
|
70
|
+
ChangeKind[ChangeKind["Remove"] = 0] = "Remove";
|
|
71
|
+
ChangeKind[ChangeKind["ReplaceWithSingleNode"] = 1] = "ReplaceWithSingleNode";
|
|
72
|
+
ChangeKind[ChangeKind["ReplaceWithMultipleNodes"] = 2] = "ReplaceWithMultipleNodes";
|
|
73
|
+
ChangeKind[ChangeKind["Text"] = 3] = "Text";
|
|
74
|
+
})(ChangeKind || (ChangeKind = {}));
|
|
75
|
+
function getAdjustedRange(sourceFile, startNode, endNode, options) {
|
|
76
|
+
return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) };
|
|
77
|
+
}
|
|
78
|
+
function getAdjustedStartPosition(sourceFile, node, options) {
|
|
79
|
+
const { leadingTriviaOption } = options;
|
|
80
|
+
if (leadingTriviaOption === LeadingTriviaOption.Exclude) {
|
|
81
|
+
return node.getStart(sourceFile);
|
|
82
|
+
}
|
|
83
|
+
if (leadingTriviaOption === LeadingTriviaOption.StartLine) {
|
|
84
|
+
return getLineStartPositionForPosition(node.getStart(sourceFile), sourceFile);
|
|
85
|
+
}
|
|
86
|
+
// Na tag, também o começo do JSDoc preso ao nó (CK-03).
|
|
87
|
+
const fullStart = node.getFullStart();
|
|
88
|
+
const start = node.getStart(sourceFile);
|
|
89
|
+
if (fullStart === start) {
|
|
90
|
+
return start;
|
|
91
|
+
}
|
|
92
|
+
const fullStartLine = getLineStartPositionForPosition(fullStart, sourceFile);
|
|
93
|
+
const startLine = getLineStartPositionForPosition(start, sourceFile);
|
|
94
|
+
if (startLine === fullStartLine) {
|
|
95
|
+
// full start and start of the node are on the same line
|
|
96
|
+
// a, b;
|
|
97
|
+
// ^ ^
|
|
98
|
+
// | start
|
|
99
|
+
// fullstart
|
|
100
|
+
// when b is replaced - we usually want to keep the leading trvia
|
|
101
|
+
// when b is deleted - we delete it
|
|
102
|
+
return leadingTriviaOption === LeadingTriviaOption.IncludeAll ? fullStart : start;
|
|
103
|
+
}
|
|
104
|
+
// get start position of the line following the line that contains fullstart position
|
|
105
|
+
// (but only if the fullstart isn't the very beginning of the file)
|
|
106
|
+
const nextLineStart = fullStart > 0 ? 1 : 0;
|
|
107
|
+
let adjustedStartPosition = getStartPositionOfLine(getLineOfLocalPosition(sourceFile, fullStartLine) + nextLineStart, sourceFile);
|
|
108
|
+
// skip whitespaces/newlines
|
|
109
|
+
adjustedStartPosition = skipWhitespacesAndLineBreaks(sourceFile.text, adjustedStartPosition);
|
|
110
|
+
return getStartPositionOfLine(getLineOfLocalPosition(sourceFile, adjustedStartPosition), sourceFile);
|
|
111
|
+
}
|
|
112
|
+
function getAdjustedEndPosition(sourceFile, node, options) {
|
|
113
|
+
const { end } = node;
|
|
114
|
+
const { trailingTriviaOption } = options;
|
|
115
|
+
if (trailingTriviaOption === TrailingTriviaOption.Exclude || (isExpression(node) && trailingTriviaOption !== TrailingTriviaOption.Include)) {
|
|
116
|
+
return end;
|
|
117
|
+
}
|
|
118
|
+
const newEnd = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true);
|
|
119
|
+
return newEnd !== end && (trailingTriviaOption === TrailingTriviaOption.Include || isLineBreak(sourceFile.text.charCodeAt(newEnd - 1)))
|
|
120
|
+
? newEnd
|
|
121
|
+
: end;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Checks if 'candidate' argument is a legal separator in the list that contains 'node' as an element
|
|
125
|
+
*/
|
|
126
|
+
function isSeparator(node, candidate) {
|
|
127
|
+
return !!candidate && !!node.parent && (candidate.kind === SyntaxKind.CommaToken || (candidate.kind === SyntaxKind.SemicolonToken && node.parent.kind === SyntaxKind.ObjectLiteralExpression));
|
|
128
|
+
}
|
|
129
|
+
function spaces(count) {
|
|
130
|
+
let s = "";
|
|
131
|
+
for (let i = 0; i < count; i++) {
|
|
132
|
+
s += " ";
|
|
133
|
+
}
|
|
134
|
+
return s;
|
|
135
|
+
}
|
|
136
|
+
// Na tag, também o que recebe a anotação do `this` (a `function` e a function expression), com o
|
|
137
|
+
// `isThisTypeAnnotatable` (CK-02).
|
|
138
|
+
export class ChangeTracker {
|
|
139
|
+
static fromContext(context) {
|
|
140
|
+
return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext);
|
|
141
|
+
}
|
|
142
|
+
static with(context, cb) {
|
|
143
|
+
const tracker = ChangeTracker.fromContext(context);
|
|
144
|
+
cb(tracker);
|
|
145
|
+
return tracker.getChanges();
|
|
146
|
+
}
|
|
147
|
+
/** Public for tests only. Other callers should use `ChangeTracker.with`. */
|
|
148
|
+
constructor(newLineCharacter, formatContext) {
|
|
149
|
+
this.newLineCharacter = newLineCharacter;
|
|
150
|
+
this.formatContext = formatContext;
|
|
151
|
+
this.changes = [];
|
|
152
|
+
this.newFiles = [];
|
|
153
|
+
// Na tag, também a classe e a interface em que um membro se insere no começo (CK-02).
|
|
154
|
+
this.classesWithNodesInsertedAtStart = createMap(); // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
|
|
155
|
+
this.deletedNodes = [];
|
|
156
|
+
}
|
|
157
|
+
pushRaw(sourceFile, change) {
|
|
158
|
+
Debug.assertEqual(sourceFile.fileName, change.fileName);
|
|
159
|
+
for (const c of change.textChanges) {
|
|
160
|
+
this.changes.push({
|
|
161
|
+
kind: ChangeKind.Text,
|
|
162
|
+
sourceFile,
|
|
163
|
+
text: c.newText,
|
|
164
|
+
range: createTextRangeFromSpan(c.span),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
deleteRange(sourceFile, range) {
|
|
169
|
+
this.changes.push({ kind: ChangeKind.Remove, sourceFile, range });
|
|
170
|
+
}
|
|
171
|
+
delete(sourceFile, node) {
|
|
172
|
+
this.deletedNodes.push({ sourceFile, node });
|
|
173
|
+
}
|
|
174
|
+
deleteNode(sourceFile, node, options = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }) {
|
|
175
|
+
this.deleteRange(sourceFile, getAdjustedRange(sourceFile, node, node, options));
|
|
176
|
+
}
|
|
177
|
+
deleteModifier(sourceFile, modifier) {
|
|
178
|
+
this.deleteRange(sourceFile, { pos: modifier.getStart(sourceFile), end: skipTrivia(sourceFile.text, modifier.end, /*stopAfterLineBreak*/ true) });
|
|
179
|
+
}
|
|
180
|
+
deleteNodeRange(sourceFile, startNode, endNode, options = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }) {
|
|
181
|
+
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options);
|
|
182
|
+
const endPosition = getAdjustedEndPosition(sourceFile, endNode, options);
|
|
183
|
+
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
|
184
|
+
}
|
|
185
|
+
deleteNodeRangeExcludingEnd(sourceFile, startNode, afterEndNode, options = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }) {
|
|
186
|
+
const startPosition = getAdjustedStartPosition(sourceFile, startNode, options);
|
|
187
|
+
const endPosition = afterEndNode === undefined ? sourceFile.text.length : getAdjustedStartPosition(sourceFile, afterEndNode, options);
|
|
188
|
+
this.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
|
189
|
+
}
|
|
190
|
+
replaceRange(sourceFile, range, newNode, options = {}) {
|
|
191
|
+
this.changes.push({ kind: ChangeKind.ReplaceWithSingleNode, sourceFile, range, options, node: newNode });
|
|
192
|
+
}
|
|
193
|
+
replaceNode(sourceFile, oldNode, newNode, options = useNonAdjustedPositions) {
|
|
194
|
+
this.replaceRange(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNode, options);
|
|
195
|
+
}
|
|
196
|
+
replaceNodeRange(sourceFile, startNode, endNode, newNode, options = useNonAdjustedPositions) {
|
|
197
|
+
this.replaceRange(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNode, options);
|
|
198
|
+
}
|
|
199
|
+
replaceRangeWithNodes(sourceFile, range, newNodes, options = {}) {
|
|
200
|
+
this.changes.push({ kind: ChangeKind.ReplaceWithMultipleNodes, sourceFile, range, options, nodes: newNodes });
|
|
201
|
+
}
|
|
202
|
+
replaceNodeWithNodes(sourceFile, oldNode, newNodes, options = useNonAdjustedPositions) {
|
|
203
|
+
this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, options), newNodes, options);
|
|
204
|
+
}
|
|
205
|
+
replaceNodeWithText(sourceFile, oldNode, text) {
|
|
206
|
+
this.replaceRangeWithText(sourceFile, getAdjustedRange(sourceFile, oldNode, oldNode, useNonAdjustedPositions), text);
|
|
207
|
+
}
|
|
208
|
+
replaceNodeRangeWithNodes(sourceFile, startNode, endNode, newNodes, options = useNonAdjustedPositions) {
|
|
209
|
+
this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options);
|
|
210
|
+
}
|
|
211
|
+
nextCommaToken(sourceFile, node) {
|
|
212
|
+
const next = findNextToken(node, node.parent, sourceFile);
|
|
213
|
+
return next && next.kind === SyntaxKind.CommaToken ? next : undefined;
|
|
214
|
+
}
|
|
215
|
+
replacePropertyAssignment(sourceFile, oldNode, newNode) {
|
|
216
|
+
const suffix = this.nextCommaToken(sourceFile, oldNode) ? "" : ("," + this.newLineCharacter);
|
|
217
|
+
this.replaceNode(sourceFile, oldNode, newNode, { suffix });
|
|
218
|
+
}
|
|
219
|
+
insertNodeAt(sourceFile, pos, newNode, options = {}) {
|
|
220
|
+
this.replaceRange(sourceFile, createRange(pos), newNode, options);
|
|
221
|
+
}
|
|
222
|
+
insertNodesAt(sourceFile, pos, newNodes, options = {}) {
|
|
223
|
+
this.replaceRangeWithNodes(sourceFile, createRange(pos), newNodes, options);
|
|
224
|
+
}
|
|
225
|
+
insertNodeAtTopOfFile(sourceFile, newNode, blankLineBetween) {
|
|
226
|
+
this.insertAtTopOfFile(sourceFile, newNode, blankLineBetween);
|
|
227
|
+
}
|
|
228
|
+
insertNodesAtTopOfFile(sourceFile, newNodes, blankLineBetween) {
|
|
229
|
+
this.insertAtTopOfFile(sourceFile, newNodes, blankLineBetween);
|
|
230
|
+
}
|
|
231
|
+
insertAtTopOfFile(sourceFile, insert, blankLineBetween) {
|
|
232
|
+
const pos = getInsertionPositionAtSourceFileTop(sourceFile);
|
|
233
|
+
const options = {
|
|
234
|
+
prefix: pos === 0 ? undefined : this.newLineCharacter,
|
|
235
|
+
suffix: (isLineBreak(sourceFile.text.charCodeAt(pos)) ? "" : this.newLineCharacter) + (blankLineBetween ? this.newLineCharacter : ""),
|
|
236
|
+
};
|
|
237
|
+
if (isArray(insert)) {
|
|
238
|
+
this.insertNodesAt(sourceFile, pos, insert, options);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
this.insertNodeAt(sourceFile, pos, insert, options);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
insertFirstParameter(sourceFile, parameters, newParam) {
|
|
245
|
+
const p0 = firstOrUndefined(parameters);
|
|
246
|
+
if (p0) {
|
|
247
|
+
this.insertNodeBefore(sourceFile, p0, newParam);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
this.insertNodeAt(sourceFile, parameters.pos, newParam);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
insertNodeBefore(sourceFile, before, newNode, blankLineBetween = false) {
|
|
254
|
+
this.insertNodeAt(sourceFile, getAdjustedStartPosition(sourceFile, before, {}), newNode, this.getOptionsForInsertNodeBefore(before, newNode, blankLineBetween));
|
|
255
|
+
}
|
|
256
|
+
insertModifierBefore(sourceFile, modifier, before) {
|
|
257
|
+
const pos = before.getStart(sourceFile);
|
|
258
|
+
this.insertNodeAt(sourceFile, pos, createToken(modifier), { suffix: " " });
|
|
259
|
+
}
|
|
260
|
+
insertLastModifierBefore(sourceFile, modifier, before) {
|
|
261
|
+
if (!before.modifiers) {
|
|
262
|
+
this.insertModifierBefore(sourceFile, modifier, before);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const pos = before.modifiers.end;
|
|
266
|
+
this.insertNodeAt(sourceFile, pos, createToken(modifier), { prefix: " " });
|
|
267
|
+
}
|
|
268
|
+
insertCommentBeforeLine(sourceFile, lineNumber, position, commentText) {
|
|
269
|
+
const lineStartPosition = getStartPositionOfLine(lineNumber, sourceFile);
|
|
270
|
+
const startPosition = getFirstNonSpaceCharacterPosition(sourceFile.text, lineStartPosition);
|
|
271
|
+
// First try to see if we can put the comment on the previous line.
|
|
272
|
+
// We need to make sure that we are not in the middle of a string literal or a comment.
|
|
273
|
+
// If so, we do not want to separate the node from its comment if we can.
|
|
274
|
+
// Otherwise, add an extra new line immediately before the error span.
|
|
275
|
+
const insertAtLineStart = isValidLocationToAddComment(sourceFile, startPosition);
|
|
276
|
+
const token = getTouchingToken(sourceFile, insertAtLineStart ? startPosition : position);
|
|
277
|
+
const indent = sourceFile.text.slice(lineStartPosition, startPosition);
|
|
278
|
+
const text = `${insertAtLineStart ? "" : this.newLineCharacter}//${commentText}${this.newLineCharacter}${indent}`;
|
|
279
|
+
this.insertText(sourceFile, token.getStart(sourceFile), text);
|
|
280
|
+
}
|
|
281
|
+
// Na tag, também a inserção de um comentário JSDoc antes de um nó (o `insertJsdocCommentBefore`, CK-03).
|
|
282
|
+
replaceRangeWithText(sourceFile, range, text) {
|
|
283
|
+
this.changes.push({ kind: ChangeKind.Text, sourceFile, range, text });
|
|
284
|
+
}
|
|
285
|
+
insertText(sourceFile, pos, text) {
|
|
286
|
+
this.replaceRangeWithText(sourceFile, createRange(pos), text);
|
|
287
|
+
}
|
|
288
|
+
/** Prefer this over replacing a node with another that has a type annotation, as it avoids reformatting the other parts of the node. */
|
|
289
|
+
tryInsertTypeAnnotation(sourceFile, node, type) {
|
|
290
|
+
let endNode;
|
|
291
|
+
if (isFunctionLike(node)) {
|
|
292
|
+
endNode = findChildOfKind(node, SyntaxKind.CloseParenToken, sourceFile);
|
|
293
|
+
if (!endNode) {
|
|
294
|
+
if (!isArrowFunction(node))
|
|
295
|
+
return false; // Function missing parentheses, give up
|
|
296
|
+
// If no `)`, is an arrow function `x => x`, so use the end of the first parameter
|
|
297
|
+
endNode = first(node.parameters);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
endNode = (node.kind === SyntaxKind.VariableDeclaration ? node.exclamationToken : node.questionToken) ?? node.name;
|
|
302
|
+
}
|
|
303
|
+
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
// Na tag, também a anotação do parâmetro `this` (o `tryInsertThisTypeAnnotation`, CK-02).
|
|
307
|
+
insertTypeParameters(sourceFile, node, typeParameters) {
|
|
308
|
+
// If no `(`, is an arrow function `x => x`, so use the pos of the first parameter
|
|
309
|
+
const start = (findChildOfKind(node, SyntaxKind.OpenParenToken, sourceFile) || first(node.parameters)).getStart(sourceFile);
|
|
310
|
+
this.insertNodesAt(sourceFile, start, typeParameters, { prefix: "<", suffix: ">" });
|
|
311
|
+
}
|
|
312
|
+
getOptionsForInsertNodeBefore(before, inserted, doubleNewlines) {
|
|
313
|
+
// Na tag, também antes de um membro de classe (CK-02).
|
|
314
|
+
if (isStatement(before)) {
|
|
315
|
+
return { suffix: doubleNewlines ? this.newLineCharacter + this.newLineCharacter : this.newLineCharacter };
|
|
316
|
+
}
|
|
317
|
+
else if (isVariableDeclaration(before)) { // insert `x = 1, ` into `const x = 1, y = 2;
|
|
318
|
+
return { suffix: ", " };
|
|
319
|
+
}
|
|
320
|
+
else if (isParameter(before)) {
|
|
321
|
+
return isParameter(inserted) ? { suffix: ", " } : {};
|
|
322
|
+
}
|
|
323
|
+
else if (isStringLiteral(before) && isImportDeclaration(before.parent) || isNamedImports(before)) {
|
|
324
|
+
return { suffix: ", " };
|
|
325
|
+
}
|
|
326
|
+
return Debug.failBadSyntaxKind(before); // We haven't handled this kind of node yet -- add it
|
|
327
|
+
}
|
|
328
|
+
// Na tag, também a inserção no começo e no fim do corpo de um construtor (o `insertNodeAtConstructorStart` e o
|
|
329
|
+
// `insertNodeAtConstructorEnd`, CK-02).
|
|
330
|
+
insertNodeAtEndOfScope(sourceFile, scope, newNode) {
|
|
331
|
+
const pos = getAdjustedStartPosition(sourceFile, scope.getLastToken(), {});
|
|
332
|
+
this.insertNodeAt(sourceFile, pos, newNode, {
|
|
333
|
+
prefix: isLineBreak(sourceFile.text.charCodeAt(scope.getLastToken().pos)) ? this.newLineCharacter : this.newLineCharacter + this.newLineCharacter,
|
|
334
|
+
suffix: this.newLineCharacter
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
// Na tag, também a inserção de um membro no começo de uma classe ou de uma interface (o `insertNodeAtClassStart`,
|
|
338
|
+
// CK-02); o worker e os auxiliares dele ficam com o object literal.
|
|
339
|
+
insertNodeAtObjectStart(sourceFile, obj, newElement) {
|
|
340
|
+
this.insertNodeAtStartWorker(sourceFile, obj, newElement);
|
|
341
|
+
}
|
|
342
|
+
insertNodeAtStartWorker(sourceFile, cls, newElement) {
|
|
343
|
+
const indentation = this.guessIndentationFromExistingMembers(sourceFile, cls) ?? this.computeIndentationForNewMember(sourceFile, cls);
|
|
344
|
+
this.insertNodeAt(sourceFile, getMembersOrProperties(cls).pos, newElement, this.getInsertNodeAtStartInsertOptions(sourceFile, cls, indentation));
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Tries to guess the indentation from the existing members of a class/interface/object. All members must be on
|
|
348
|
+
* new lines and must share the same indentation.
|
|
349
|
+
*/
|
|
350
|
+
guessIndentationFromExistingMembers(sourceFile, cls) {
|
|
351
|
+
let indentation;
|
|
352
|
+
let lastRange = cls;
|
|
353
|
+
for (const member of getMembersOrProperties(cls)) {
|
|
354
|
+
if (rangeStartPositionsAreOnSameLine(lastRange, member, sourceFile)) {
|
|
355
|
+
// each indented member must be on a new line
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
const memberStart = member.getStart(sourceFile);
|
|
359
|
+
const memberIndentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(memberStart, sourceFile), memberStart, sourceFile, this.formatContext.options);
|
|
360
|
+
if (indentation === undefined) {
|
|
361
|
+
indentation = memberIndentation;
|
|
362
|
+
}
|
|
363
|
+
else if (memberIndentation !== indentation) {
|
|
364
|
+
// indentation of multiple members is not consistent
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
lastRange = member;
|
|
368
|
+
}
|
|
369
|
+
return indentation;
|
|
370
|
+
}
|
|
371
|
+
computeIndentationForNewMember(sourceFile, cls) {
|
|
372
|
+
const clsStart = cls.getStart(sourceFile);
|
|
373
|
+
return formatting.SmartIndenter.findFirstNonWhitespaceColumn(getLineStartPositionForPosition(clsStart, sourceFile), clsStart, sourceFile, this.formatContext.options)
|
|
374
|
+
+ (this.formatContext.options.indentSize ?? 4);
|
|
375
|
+
}
|
|
376
|
+
getInsertNodeAtStartInsertOptions(sourceFile, cls, indentation) {
|
|
377
|
+
// Rules:
|
|
378
|
+
// - Always insert leading newline.
|
|
379
|
+
// - For object literals:
|
|
380
|
+
// - Add a trailing comma if there are existing members in the node, or the source file is not a JSON file
|
|
381
|
+
// (because trailing commas are generally illegal in a JSON file).
|
|
382
|
+
// - Add a leading comma if the source file is not a JSON file, there are existing insertions,
|
|
383
|
+
// and the node is empty (because we didn't add a trailing comma per the previous rule).
|
|
384
|
+
// - Only insert a trailing newline if body is single-line and there are no other insertions for the node.
|
|
385
|
+
// NOTE: This is handled in `finishClassesWithNodesInsertedAtStart`.
|
|
386
|
+
const members = getMembersOrProperties(cls);
|
|
387
|
+
const isEmpty = members.length === 0;
|
|
388
|
+
const isFirstInsertion = addToSeen(this.classesWithNodesInsertedAtStart, getNodeId(cls), { node: cls, sourceFile });
|
|
389
|
+
const insertTrailingComma = isObjectLiteralExpression(cls) && (!isJsonSourceFile(sourceFile) || !isEmpty);
|
|
390
|
+
const insertLeadingComma = isObjectLiteralExpression(cls) && isJsonSourceFile(sourceFile) && isEmpty && !isFirstInsertion;
|
|
391
|
+
return {
|
|
392
|
+
indentation,
|
|
393
|
+
prefix: (insertLeadingComma ? "," : "") + this.newLineCharacter,
|
|
394
|
+
suffix: insertTrailingComma ? "," : ""
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
insertNodeAfterComma(sourceFile, after, newNode) {
|
|
398
|
+
const endPosition = this.insertNodeAfterWorker(sourceFile, this.nextCommaToken(sourceFile, after) || after, newNode);
|
|
399
|
+
this.insertNodeAt(sourceFile, endPosition, newNode, this.getInsertNodeAfterOptions(sourceFile, after));
|
|
400
|
+
}
|
|
401
|
+
insertNodeAfter(sourceFile, after, newNode) {
|
|
402
|
+
const endPosition = this.insertNodeAfterWorker(sourceFile, after, newNode);
|
|
403
|
+
this.insertNodeAt(sourceFile, endPosition, newNode, this.getInsertNodeAfterOptions(sourceFile, after));
|
|
404
|
+
}
|
|
405
|
+
insertNodeAtEndOfList(sourceFile, list, newNode) {
|
|
406
|
+
this.insertNodeAt(sourceFile, list.end, newNode, { prefix: ", " });
|
|
407
|
+
}
|
|
408
|
+
insertNodesAfter(sourceFile, after, newNodes) {
|
|
409
|
+
const endPosition = this.insertNodeAfterWorker(sourceFile, after, first(newNodes));
|
|
410
|
+
this.insertNodesAt(sourceFile, endPosition, newNodes, this.getInsertNodeAfterOptions(sourceFile, after));
|
|
411
|
+
}
|
|
412
|
+
insertNodeAfterWorker(sourceFile, after, newNode) {
|
|
413
|
+
if (needSemicolonBetween(after, newNode)) {
|
|
414
|
+
// check if previous statement ends with semicolon
|
|
415
|
+
// if not - insert semicolon to preserve the code from changing the meaning due to ASI
|
|
416
|
+
if (sourceFile.text.charCodeAt(after.end - 1) !== CharacterCodes.semicolon) {
|
|
417
|
+
this.replaceRange(sourceFile, createRange(after.end), createToken(SyntaxKind.SemicolonToken));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const endPosition = getAdjustedEndPosition(sourceFile, after, {});
|
|
421
|
+
return endPosition;
|
|
422
|
+
}
|
|
423
|
+
getInsertNodeAfterOptions(sourceFile, after) {
|
|
424
|
+
const options = this.getInsertNodeAfterOptionsWorker(after);
|
|
425
|
+
return {
|
|
426
|
+
...options,
|
|
427
|
+
prefix: after.end === sourceFile.end && isStatement(after) ? (options.prefix ? `\n${options.prefix}` : "\n") : options.prefix,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
getInsertNodeAfterOptionsWorker(node) {
|
|
431
|
+
switch (node.kind) {
|
|
432
|
+
// Na tag, também a classe (CK-02).
|
|
433
|
+
case SyntaxKind.ModuleDeclaration:
|
|
434
|
+
return { prefix: this.newLineCharacter, suffix: this.newLineCharacter };
|
|
435
|
+
case SyntaxKind.VariableDeclaration:
|
|
436
|
+
case SyntaxKind.StringLiteral:
|
|
437
|
+
case SyntaxKind.Identifier:
|
|
438
|
+
return { prefix: ", " };
|
|
439
|
+
case SyntaxKind.PropertyAssignment:
|
|
440
|
+
return { suffix: "," + this.newLineCharacter };
|
|
441
|
+
case SyntaxKind.ExportKeyword:
|
|
442
|
+
return { prefix: " " };
|
|
443
|
+
case SyntaxKind.Parameter:
|
|
444
|
+
return {};
|
|
445
|
+
default:
|
|
446
|
+
// Na tag, também o membro de classe (CK-02).
|
|
447
|
+
Debug.assert(isStatement(node) || isTypeElement(node)); // Else we haven't handled this kind of node yet -- add it
|
|
448
|
+
return { suffix: this.newLineCharacter };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// Na tag, também o nome dado a uma function expression, a uma class expression ou a uma arrow, que virava uma
|
|
452
|
+
// `function` (o `insertName`): só o `convertToEs6Module` o chamava, e ele sai com o CommonJS (CK-03).
|
|
453
|
+
insertExportModifier(sourceFile, node) {
|
|
454
|
+
this.insertText(sourceFile, node.getStart(sourceFile), "export ");
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* This function should be used to insert nodes in lists when nodes don't carry separators as the part of the node range,
|
|
458
|
+
* i.e. arguments in arguments lists, parameters in parameter lists etc.
|
|
459
|
+
* Note that separators are part of the node in statements and class elements.
|
|
460
|
+
*/
|
|
461
|
+
insertNodeInListAfter(sourceFile, after, newNode, containingList = formatting.SmartIndenter.getContainingList(after, sourceFile)) {
|
|
462
|
+
if (!containingList) {
|
|
463
|
+
Debug.fail("node is not a list element");
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const index = indexOfNode(containingList, after);
|
|
467
|
+
if (index < 0) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const end = after.getEnd();
|
|
471
|
+
if (index !== containingList.length - 1) {
|
|
472
|
+
// any element except the last one
|
|
473
|
+
// use next sibling as an anchor
|
|
474
|
+
const nextToken = getTokenAtPosition(sourceFile, after.end);
|
|
475
|
+
if (nextToken && isSeparator(after, nextToken)) {
|
|
476
|
+
// for list
|
|
477
|
+
// a, b, c
|
|
478
|
+
// create change for adding 'e' after 'a' as
|
|
479
|
+
// - find start of next element after a (it is b)
|
|
480
|
+
// - use this start as start and end position in final change
|
|
481
|
+
// - build text of change by formatting the text of node + separator + whitespace trivia of b
|
|
482
|
+
// in multiline case it will work as
|
|
483
|
+
// a,
|
|
484
|
+
// b,
|
|
485
|
+
// c,
|
|
486
|
+
// result - '*' denotes leading trivia that will be inserted after new text (displayed as '#')
|
|
487
|
+
// a,*
|
|
488
|
+
// ***insertedtext<separator>#
|
|
489
|
+
// ###b,
|
|
490
|
+
// c,
|
|
491
|
+
// find line and character of the next element
|
|
492
|
+
const lineAndCharOfNextElement = getLineAndCharacterOfPosition(sourceFile, skipWhitespacesAndLineBreaks(sourceFile.text, containingList[index + 1].getFullStart()));
|
|
493
|
+
// find line and character of the token that precedes next element (usually it is separator)
|
|
494
|
+
const lineAndCharOfNextToken = getLineAndCharacterOfPosition(sourceFile, nextToken.end);
|
|
495
|
+
let prefix;
|
|
496
|
+
let startPos;
|
|
497
|
+
if (lineAndCharOfNextToken.line === lineAndCharOfNextElement.line) {
|
|
498
|
+
// next element is located on the same line with separator:
|
|
499
|
+
// a,$$$$b
|
|
500
|
+
// ^ ^
|
|
501
|
+
// | |-next element
|
|
502
|
+
// |-separator
|
|
503
|
+
// where $$$ is some leading trivia
|
|
504
|
+
// for a newly inserted node we'll maintain the same relative position comparing to separator and replace leading trivia with spaces
|
|
505
|
+
// a, x,$$$$b
|
|
506
|
+
// ^ ^ ^
|
|
507
|
+
// | | |-next element
|
|
508
|
+
// | |-new inserted node padded with spaces
|
|
509
|
+
// |-separator
|
|
510
|
+
startPos = nextToken.end;
|
|
511
|
+
prefix = spaces(lineAndCharOfNextElement.character - lineAndCharOfNextToken.character);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
// next element is located on different line that separator
|
|
515
|
+
// let insert position be the beginning of the line that contains next element
|
|
516
|
+
startPos = getStartPositionOfLine(lineAndCharOfNextElement.line, sourceFile);
|
|
517
|
+
}
|
|
518
|
+
// write separator and leading trivia of the next element as suffix
|
|
519
|
+
const suffix = `${tokenToString(nextToken.kind)}${sourceFile.text.substring(nextToken.end, containingList[index + 1].getStart(sourceFile))}`;
|
|
520
|
+
this.replaceRange(sourceFile, createRange(startPos, containingList[index + 1].getStart(sourceFile)), newNode, { prefix, suffix });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
const afterStart = after.getStart(sourceFile);
|
|
525
|
+
const afterStartLinePosition = getLineStartPositionForPosition(afterStart, sourceFile);
|
|
526
|
+
let separator;
|
|
527
|
+
let multilineList = false;
|
|
528
|
+
// insert element after the last element in the list that has more than one item
|
|
529
|
+
// pick the element preceding the after element to:
|
|
530
|
+
// - pick the separator
|
|
531
|
+
// - determine if list is a multiline
|
|
532
|
+
if (containingList.length === 1) {
|
|
533
|
+
// if list has only one element then we'll format is as multiline if node has comment in trailing trivia, or as singleline otherwise
|
|
534
|
+
// i.e. var x = 1 // this is x
|
|
535
|
+
// | new element will be inserted at this position
|
|
536
|
+
separator = SyntaxKind.CommaToken;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
// element has more than one element, pick separator from the list
|
|
540
|
+
const tokenBeforeInsertPosition = findPrecedingToken(after.pos, sourceFile);
|
|
541
|
+
separator = isSeparator(after, tokenBeforeInsertPosition) ? tokenBeforeInsertPosition.kind : SyntaxKind.CommaToken;
|
|
542
|
+
// determine if list is multiline by checking lines of after element and element that precedes it.
|
|
543
|
+
const afterMinusOneStartLinePosition = getLineStartPositionForPosition(containingList[index - 1].getStart(sourceFile), sourceFile);
|
|
544
|
+
multilineList = afterMinusOneStartLinePosition !== afterStartLinePosition;
|
|
545
|
+
}
|
|
546
|
+
if (hasCommentsBeforeLineBreak(sourceFile.text, after.end)) {
|
|
547
|
+
// in this case we'll always treat containing list as multiline
|
|
548
|
+
multilineList = true;
|
|
549
|
+
}
|
|
550
|
+
if (multilineList) {
|
|
551
|
+
// insert separator immediately following the 'after' node to preserve comments in trailing trivia
|
|
552
|
+
this.replaceRange(sourceFile, createRange(end), createToken(separator));
|
|
553
|
+
// use the same indentation as 'after' item
|
|
554
|
+
const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.formatContext.options);
|
|
555
|
+
// insert element before the line break on the line that contains 'after' element
|
|
556
|
+
let insertPos = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true, /*stopAtComments*/ false);
|
|
557
|
+
if (insertPos !== end && isLineBreak(sourceFile.text.charCodeAt(insertPos - 1))) {
|
|
558
|
+
insertPos--;
|
|
559
|
+
}
|
|
560
|
+
this.replaceRange(sourceFile, createRange(insertPos), newNode, { indentation, prefix: this.newLineCharacter });
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
this.replaceRange(sourceFile, createRange(end), newNode, { prefix: `${tokenToString(separator)} ` });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
parenthesizeExpression(sourceFile, expression) {
|
|
568
|
+
this.replaceRange(sourceFile, rangeOfNode(expression), createParen(expression));
|
|
569
|
+
}
|
|
570
|
+
finishClassesWithNodesInsertedAtStart() {
|
|
571
|
+
this.classesWithNodesInsertedAtStart.forEach(({ node, sourceFile }) => {
|
|
572
|
+
const [openBraceEnd, closeBraceEnd] = getClassOrObjectBraceEnds(node, sourceFile);
|
|
573
|
+
if (openBraceEnd !== undefined && closeBraceEnd !== undefined) {
|
|
574
|
+
const isEmpty = getMembersOrProperties(node).length === 0;
|
|
575
|
+
const isSingleLine = positionsAreOnSameLine(openBraceEnd, closeBraceEnd, sourceFile);
|
|
576
|
+
if (isEmpty && isSingleLine && openBraceEnd !== closeBraceEnd - 1) {
|
|
577
|
+
// For `class C { }` remove the whitespace inside the braces.
|
|
578
|
+
this.deleteRange(sourceFile, createRange(openBraceEnd, closeBraceEnd - 1));
|
|
579
|
+
}
|
|
580
|
+
if (isSingleLine) {
|
|
581
|
+
this.insertText(sourceFile, closeBraceEnd - 1, this.newLineCharacter);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
finishDeleteDeclarations() {
|
|
587
|
+
const deletedNodesInLists = new NodeSet(); // Stores nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`.
|
|
588
|
+
for (const { sourceFile, node } of this.deletedNodes) {
|
|
589
|
+
if (!this.deletedNodes.some(d => d.sourceFile === sourceFile && rangeContainsRangeExclusive(d.node, node))) {
|
|
590
|
+
if (isArray(node)) {
|
|
591
|
+
this.deleteRange(sourceFile, rangeOfTypeParameters(node));
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
deleteDeclaration.deleteDeclaration(this, deletedNodesInLists, sourceFile, node);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
deletedNodesInLists.forEach(node => {
|
|
599
|
+
const sourceFile = node.getSourceFile();
|
|
600
|
+
const list = formatting.SmartIndenter.getContainingList(node, sourceFile);
|
|
601
|
+
if (node !== last(list))
|
|
602
|
+
return;
|
|
603
|
+
const lastNonDeletedIndex = findLastIndex(list, n => !deletedNodesInLists.has(n), list.length - 2);
|
|
604
|
+
if (lastNonDeletedIndex !== -1) {
|
|
605
|
+
this.deleteRange(sourceFile, { pos: list[lastNonDeletedIndex].end, end: startPositionToDeleteNodeInList(sourceFile, list[lastNonDeletedIndex + 1]) });
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Note: after calling this, the TextChanges object must be discarded!
|
|
611
|
+
* @param validate only for tests
|
|
612
|
+
* The reason we must validate as part of this method is that `getNonFormattedText` changes the node's positions,
|
|
613
|
+
* so we can only call this once and can't get the non-formatted text separately.
|
|
614
|
+
*/
|
|
615
|
+
getChanges(validate) {
|
|
616
|
+
this.finishDeleteDeclarations();
|
|
617
|
+
this.finishClassesWithNodesInsertedAtStart();
|
|
618
|
+
const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, validate);
|
|
619
|
+
for (const { oldFile, fileName, statements } of this.newFiles) {
|
|
620
|
+
changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext));
|
|
621
|
+
}
|
|
622
|
+
return changes;
|
|
623
|
+
}
|
|
624
|
+
createNewFile(oldFile, fileName, statements) {
|
|
625
|
+
this.newFiles.push({ oldFile, fileName, statements });
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// find first non-whitespace position in the leading trivia of the node
|
|
629
|
+
function startPositionToDeleteNodeInList(sourceFile, node) {
|
|
630
|
+
return skipTrivia(sourceFile.text, getAdjustedStartPosition(sourceFile, node, { leadingTriviaOption: LeadingTriviaOption.IncludeAll }), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
|
|
631
|
+
}
|
|
632
|
+
// Os dois auxiliares abaixo: na tag, também a classe e a interface (CK-02).
|
|
633
|
+
function getClassOrObjectBraceEnds(cls, sourceFile) {
|
|
634
|
+
const open = findChildOfKind(cls, SyntaxKind.OpenBraceToken, sourceFile);
|
|
635
|
+
const close = findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile);
|
|
636
|
+
return [open?.end, close?.end];
|
|
637
|
+
}
|
|
638
|
+
function getMembersOrProperties(cls) {
|
|
639
|
+
return cls.properties;
|
|
640
|
+
}
|
|
641
|
+
export function getNewFileText(statements, scriptKind, newLineCharacter, formatContext) {
|
|
642
|
+
return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext);
|
|
643
|
+
}
|
|
644
|
+
var changesToText;
|
|
645
|
+
(function (changesToText) {
|
|
646
|
+
function getTextChangesFromChanges(changes, newLineCharacter, formatContext, validate) {
|
|
647
|
+
return mapDefined(group(changes, c => c.sourceFile.path), changesInFile => {
|
|
648
|
+
const sourceFile = changesInFile[0].sourceFile;
|
|
649
|
+
// order changes by start position
|
|
650
|
+
// If the start position is the same, put the shorter range first, since an empty range (x, x) may precede (x, y) but not vice-versa.
|
|
651
|
+
const normalized = stableSort(changesInFile, (a, b) => (a.range.pos - b.range.pos) || (a.range.end - b.range.end));
|
|
652
|
+
// verify that change intervals do not overlap, except possibly at end points.
|
|
653
|
+
for (let i = 0; i < normalized.length - 1; i++) {
|
|
654
|
+
Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos, "Changes overlap", () => `${JSON.stringify(normalized[i].range)} and ${JSON.stringify(normalized[i + 1].range)}`);
|
|
655
|
+
}
|
|
656
|
+
const textChanges = mapDefined(normalized, c => {
|
|
657
|
+
const span = createTextSpanFromRange(c.range);
|
|
658
|
+
const newText = computeNewText(c, sourceFile, newLineCharacter, formatContext, validate);
|
|
659
|
+
// Filter out redundant changes.
|
|
660
|
+
if (span.length === newText.length && stringContainsAt(sourceFile.text, newText, span.start)) {
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
return createTextChange(span, newText);
|
|
664
|
+
});
|
|
665
|
+
return textChanges.length > 0 ? { fileName: sourceFile.fileName, textChanges } : undefined;
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
changesToText.getTextChangesFromChanges = getTextChangesFromChanges;
|
|
669
|
+
function newFileChanges(oldFile, fileName, statements, newLineCharacter, formatContext) {
|
|
670
|
+
const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext);
|
|
671
|
+
return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true };
|
|
672
|
+
}
|
|
673
|
+
changesToText.newFileChanges = newFileChanges;
|
|
674
|
+
function newFileChangesWorker(oldFile, scriptKind, statements, newLineCharacter, formatContext) {
|
|
675
|
+
// TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this
|
|
676
|
+
const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter).text).join(newLineCharacter);
|
|
677
|
+
const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind);
|
|
678
|
+
const changes = formatting.formatDocument(sourceFile, formatContext);
|
|
679
|
+
return applyChanges(nonFormattedText, changes) + newLineCharacter;
|
|
680
|
+
}
|
|
681
|
+
changesToText.newFileChangesWorker = newFileChangesWorker;
|
|
682
|
+
function computeNewText(change, sourceFile, newLineCharacter, formatContext, validate) {
|
|
683
|
+
if (change.kind === ChangeKind.Remove) {
|
|
684
|
+
return "";
|
|
685
|
+
}
|
|
686
|
+
if (change.kind === ChangeKind.Text) {
|
|
687
|
+
return change.text;
|
|
688
|
+
}
|
|
689
|
+
const { options = {}, range: { pos } } = change;
|
|
690
|
+
const format = (n) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, validate);
|
|
691
|
+
const text = change.kind === ChangeKind.ReplaceWithMultipleNodes
|
|
692
|
+
? change.nodes.map(n => removeSuffix(format(n), newLineCharacter)).join(change.options.joiner || newLineCharacter) // TODO: GH#18217
|
|
693
|
+
: format(change.node);
|
|
694
|
+
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
|
|
695
|
+
const noIndent = (options.preserveLeadingWhitespace || options.indentation !== undefined || getLineStartPositionForPosition(pos, sourceFile) === pos) ? text : text.replace(/^\s+/, "");
|
|
696
|
+
return (options.prefix || "") + noIndent
|
|
697
|
+
+ ((!options.suffix || endsWith(noIndent, options.suffix))
|
|
698
|
+
? "" : options.suffix);
|
|
699
|
+
}
|
|
700
|
+
function getFormatCodeSettingsForWriting({ options }, sourceFile) {
|
|
701
|
+
const shouldAutoDetectSemicolonPreference = !options.semicolons || options.semicolons === SemicolonPreference.Ignore;
|
|
702
|
+
const shouldRemoveSemicolons = options.semicolons === SemicolonPreference.Remove || shouldAutoDetectSemicolonPreference && !probablyUsesSemicolons(sourceFile);
|
|
703
|
+
return {
|
|
704
|
+
...options,
|
|
705
|
+
semicolons: shouldRemoveSemicolons ? SemicolonPreference.Remove : SemicolonPreference.Ignore,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
/** Note: this may mutate `nodeIn`. */
|
|
709
|
+
function getFormattedTextOfNode(nodeIn, sourceFile, pos, { indentation, prefix, delta }, newLineCharacter, formatContext, validate) {
|
|
710
|
+
const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter);
|
|
711
|
+
if (validate)
|
|
712
|
+
validate(node, text);
|
|
713
|
+
const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile);
|
|
714
|
+
const initialIndentation = indentation !== undefined
|
|
715
|
+
? indentation
|
|
716
|
+
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
|
|
717
|
+
if (delta === undefined) {
|
|
718
|
+
delta = formatting.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0;
|
|
719
|
+
}
|
|
720
|
+
const file = { text, getLineAndCharacterOfPosition(pos) { return getLineAndCharacterOfPosition(this, pos); } };
|
|
721
|
+
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions });
|
|
722
|
+
return applyChanges(text, changes);
|
|
723
|
+
}
|
|
724
|
+
/** Note: output node may be mutated input node. */
|
|
725
|
+
function getNonformattedText(node, sourceFile, newLineCharacter) {
|
|
726
|
+
const writer = createWriter(newLineCharacter);
|
|
727
|
+
const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
|
|
728
|
+
createPrinter({ newLine, neverAsciiEscape: true, preserveSourceNewlines: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
|
729
|
+
return { text: writer.getText(), node: assignPositionsToNode(node) };
|
|
730
|
+
}
|
|
731
|
+
changesToText.getNonformattedText = getNonformattedText;
|
|
732
|
+
})(changesToText || (changesToText = {}));
|
|
733
|
+
export function applyChanges(text, changes) {
|
|
734
|
+
for (let i = changes.length - 1; i >= 0; i--) {
|
|
735
|
+
const { span, newText } = changes[i];
|
|
736
|
+
text = `${text.substring(0, span.start)}${newText}${text.substring(textSpanEnd(span))}`;
|
|
737
|
+
}
|
|
738
|
+
return text;
|
|
739
|
+
}
|
|
740
|
+
function isTrivia(s) {
|
|
741
|
+
return skipTrivia(s, 0) === s.length;
|
|
742
|
+
}
|
|
743
|
+
function assignPositionsToNode(node) {
|
|
744
|
+
// O `as`: as sobrecargas do `visitNodes` não aceitam o callback sob o `strict` do repo (o §5 da arquitetura do checker).
|
|
745
|
+
const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); // TODO: GH#18217
|
|
746
|
+
// create proxy node for non synthesized nodes
|
|
747
|
+
const newNode = nodeIsSynthesized(visited) ? visited : Object.create(visited);
|
|
748
|
+
newNode.pos = getPos(node);
|
|
749
|
+
newNode.end = getEnd(node);
|
|
750
|
+
return newNode;
|
|
751
|
+
}
|
|
752
|
+
function assignPositionsToNodeArray(nodes, visitor, test, start, count) {
|
|
753
|
+
const visited = visitNodes(nodes, visitor, test, start, count);
|
|
754
|
+
if (!visited) {
|
|
755
|
+
return visited;
|
|
756
|
+
}
|
|
757
|
+
// clone nodearray if necessary
|
|
758
|
+
const nodeArray = visited === nodes ? createNodeArray(visited.slice(0)) : visited;
|
|
759
|
+
nodeArray.pos = getPos(nodes);
|
|
760
|
+
nodeArray.end = getEnd(nodes);
|
|
761
|
+
return nodeArray;
|
|
762
|
+
}
|
|
763
|
+
function createWriter(newLine) {
|
|
764
|
+
let lastNonTriviaPosition = 0;
|
|
765
|
+
const writer = createTextWriter(newLine);
|
|
766
|
+
const onEmitNode = (hint, node, printCallback) => {
|
|
767
|
+
if (node) {
|
|
768
|
+
setPos(node, lastNonTriviaPosition);
|
|
769
|
+
}
|
|
770
|
+
printCallback(hint, node);
|
|
771
|
+
if (node) {
|
|
772
|
+
setEnd(node, lastNonTriviaPosition);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
const onBeforeEmitNodeArray = nodes => {
|
|
776
|
+
if (nodes) {
|
|
777
|
+
setPos(nodes, lastNonTriviaPosition);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
const onAfterEmitNodeArray = nodes => {
|
|
781
|
+
if (nodes) {
|
|
782
|
+
setEnd(nodes, lastNonTriviaPosition);
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
const onBeforeEmitToken = node => {
|
|
786
|
+
if (node) {
|
|
787
|
+
setPos(node, lastNonTriviaPosition);
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
const onAfterEmitToken = node => {
|
|
791
|
+
if (node) {
|
|
792
|
+
setEnd(node, lastNonTriviaPosition);
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
function setLastNonTriviaPosition(s, force) {
|
|
796
|
+
if (force || !isTrivia(s)) {
|
|
797
|
+
lastNonTriviaPosition = writer.getTextPos();
|
|
798
|
+
let i = 0;
|
|
799
|
+
while (isWhiteSpaceLike(s.charCodeAt(s.length - i - 1))) {
|
|
800
|
+
i++;
|
|
801
|
+
}
|
|
802
|
+
// trim trailing whitespaces
|
|
803
|
+
lastNonTriviaPosition -= i;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
function write(s) {
|
|
807
|
+
writer.write(s);
|
|
808
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
809
|
+
}
|
|
810
|
+
function writeComment(s) {
|
|
811
|
+
writer.writeComment(s);
|
|
812
|
+
}
|
|
813
|
+
function writeKeyword(s) {
|
|
814
|
+
writer.writeKeyword(s);
|
|
815
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
816
|
+
}
|
|
817
|
+
function writeOperator(s) {
|
|
818
|
+
writer.writeOperator(s);
|
|
819
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
820
|
+
}
|
|
821
|
+
function writePunctuation(s) {
|
|
822
|
+
writer.writePunctuation(s);
|
|
823
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
824
|
+
}
|
|
825
|
+
function writeTrailingSemicolon(s) {
|
|
826
|
+
writer.writeTrailingSemicolon(s);
|
|
827
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
828
|
+
}
|
|
829
|
+
function writeParameter(s) {
|
|
830
|
+
writer.writeParameter(s);
|
|
831
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
832
|
+
}
|
|
833
|
+
function writeProperty(s) {
|
|
834
|
+
writer.writeProperty(s);
|
|
835
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
836
|
+
}
|
|
837
|
+
function writeSpace(s) {
|
|
838
|
+
writer.writeSpace(s);
|
|
839
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
840
|
+
}
|
|
841
|
+
function writeStringLiteral(s) {
|
|
842
|
+
writer.writeStringLiteral(s);
|
|
843
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
844
|
+
}
|
|
845
|
+
function writeSymbol(s, sym) {
|
|
846
|
+
writer.writeSymbol(s, sym);
|
|
847
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
848
|
+
}
|
|
849
|
+
function writeLine(force) {
|
|
850
|
+
writer.writeLine(force);
|
|
851
|
+
}
|
|
852
|
+
function increaseIndent() {
|
|
853
|
+
writer.increaseIndent();
|
|
854
|
+
}
|
|
855
|
+
function decreaseIndent() {
|
|
856
|
+
writer.decreaseIndent();
|
|
857
|
+
}
|
|
858
|
+
function getText() {
|
|
859
|
+
return writer.getText();
|
|
860
|
+
}
|
|
861
|
+
function rawWrite(s) {
|
|
862
|
+
writer.rawWrite(s);
|
|
863
|
+
setLastNonTriviaPosition(s, /*force*/ false);
|
|
864
|
+
}
|
|
865
|
+
function writeLiteral(s) {
|
|
866
|
+
writer.writeLiteral(s);
|
|
867
|
+
setLastNonTriviaPosition(s, /*force*/ true);
|
|
868
|
+
}
|
|
869
|
+
function getTextPos() {
|
|
870
|
+
return writer.getTextPos();
|
|
871
|
+
}
|
|
872
|
+
function getLine() {
|
|
873
|
+
return writer.getLine();
|
|
874
|
+
}
|
|
875
|
+
function getColumn() {
|
|
876
|
+
return writer.getColumn();
|
|
877
|
+
}
|
|
878
|
+
function getIndent() {
|
|
879
|
+
return writer.getIndent();
|
|
880
|
+
}
|
|
881
|
+
function isAtStartOfLine() {
|
|
882
|
+
return writer.isAtStartOfLine();
|
|
883
|
+
}
|
|
884
|
+
function clear() {
|
|
885
|
+
writer.clear();
|
|
886
|
+
lastNonTriviaPosition = 0;
|
|
887
|
+
}
|
|
888
|
+
return {
|
|
889
|
+
onEmitNode,
|
|
890
|
+
onBeforeEmitNodeArray,
|
|
891
|
+
onAfterEmitNodeArray,
|
|
892
|
+
onBeforeEmitToken,
|
|
893
|
+
onAfterEmitToken,
|
|
894
|
+
write,
|
|
895
|
+
writeComment,
|
|
896
|
+
writeKeyword,
|
|
897
|
+
writeOperator,
|
|
898
|
+
writePunctuation,
|
|
899
|
+
writeTrailingSemicolon,
|
|
900
|
+
writeParameter,
|
|
901
|
+
writeProperty,
|
|
902
|
+
writeSpace,
|
|
903
|
+
writeStringLiteral,
|
|
904
|
+
writeSymbol,
|
|
905
|
+
writeLine,
|
|
906
|
+
increaseIndent,
|
|
907
|
+
decreaseIndent,
|
|
908
|
+
getText,
|
|
909
|
+
rawWrite,
|
|
910
|
+
writeLiteral,
|
|
911
|
+
getTextPos,
|
|
912
|
+
getLine,
|
|
913
|
+
getColumn,
|
|
914
|
+
getIndent,
|
|
915
|
+
isAtStartOfLine,
|
|
916
|
+
hasTrailingComment: () => writer.hasTrailingComment(),
|
|
917
|
+
hasTrailingWhitespace: () => writer.hasTrailingWhitespace(),
|
|
918
|
+
clear
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function getInsertionPositionAtSourceFileTop(sourceFile) {
|
|
922
|
+
let lastPrologue;
|
|
923
|
+
for (const node of sourceFile.statements) {
|
|
924
|
+
if (isPrologueDirective(node)) {
|
|
925
|
+
lastPrologue = node;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
let position = 0;
|
|
932
|
+
const text = sourceFile.text;
|
|
933
|
+
if (lastPrologue) {
|
|
934
|
+
position = lastPrologue.end;
|
|
935
|
+
advancePastLineBreak();
|
|
936
|
+
return position;
|
|
937
|
+
}
|
|
938
|
+
const shebang = getShebang(text);
|
|
939
|
+
if (shebang !== undefined) {
|
|
940
|
+
position = shebang.length;
|
|
941
|
+
advancePastLineBreak();
|
|
942
|
+
}
|
|
943
|
+
const ranges = getLeadingCommentRanges(text, position);
|
|
944
|
+
if (!ranges)
|
|
945
|
+
return position;
|
|
946
|
+
// Find the first attached comment to the first node and add before it
|
|
947
|
+
let lastComment;
|
|
948
|
+
let firstNodeLine;
|
|
949
|
+
for (const range of ranges) {
|
|
950
|
+
if (range.kind === SyntaxKind.MultiLineCommentTrivia) {
|
|
951
|
+
if (isPinnedComment(text, range.pos)) {
|
|
952
|
+
lastComment = { range, pinnedOrTripleSlash: true };
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
else if (isRecognizedTripleSlashComment(text, range.pos, range.end)) {
|
|
957
|
+
lastComment = { range, pinnedOrTripleSlash: true };
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
if (lastComment) {
|
|
961
|
+
// Always insert after pinned or triple slash comments
|
|
962
|
+
if (lastComment.pinnedOrTripleSlash)
|
|
963
|
+
break;
|
|
964
|
+
// There was a blank line between the last comment and this comment.
|
|
965
|
+
// This comment is not part of the copyright comments
|
|
966
|
+
const commentLine = sourceFile.getLineAndCharacterOfPosition(range.pos).line;
|
|
967
|
+
const lastCommentEndLine = sourceFile.getLineAndCharacterOfPosition(lastComment.range.end).line;
|
|
968
|
+
if (commentLine >= lastCommentEndLine + 2)
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
if (sourceFile.statements.length) {
|
|
972
|
+
if (firstNodeLine === undefined)
|
|
973
|
+
firstNodeLine = sourceFile.getLineAndCharacterOfPosition(sourceFile.statements[0].getStart()).line;
|
|
974
|
+
const commentEndLine = sourceFile.getLineAndCharacterOfPosition(range.end).line;
|
|
975
|
+
if (firstNodeLine < commentEndLine + 2)
|
|
976
|
+
break;
|
|
977
|
+
}
|
|
978
|
+
lastComment = { range, pinnedOrTripleSlash: false };
|
|
979
|
+
}
|
|
980
|
+
if (lastComment) {
|
|
981
|
+
position = lastComment.range.end;
|
|
982
|
+
advancePastLineBreak();
|
|
983
|
+
}
|
|
984
|
+
return position;
|
|
985
|
+
function advancePastLineBreak() {
|
|
986
|
+
if (position < text.length) {
|
|
987
|
+
const charCode = text.charCodeAt(position);
|
|
988
|
+
if (isLineBreak(charCode)) {
|
|
989
|
+
position++;
|
|
990
|
+
if (position < text.length && charCode === CharacterCodes.carriageReturn && text.charCodeAt(position) === CharacterCodes.lineFeed) {
|
|
991
|
+
position++;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
export function isValidLocationToAddComment(sourceFile, position) {
|
|
998
|
+
return !isInComment(sourceFile, position) && !isInString(sourceFile, position) && !isInTemplateString(sourceFile, position) && !isInJSXText(sourceFile, position);
|
|
999
|
+
}
|
|
1000
|
+
function needSemicolonBetween(a, b) {
|
|
1001
|
+
// Na tag, também a propriedade e o membro de classe (CK-02).
|
|
1002
|
+
return isPropertySignature(a) && isTypeElement(b) && b.name.kind === SyntaxKind.ComputedPropertyName
|
|
1003
|
+
|| isStatementButNotDeclaration(a) && isStatementButNotDeclaration(b); // TODO: only if b would start with a `(` or `[`
|
|
1004
|
+
}
|
|
1005
|
+
var deleteDeclaration;
|
|
1006
|
+
(function (deleteDeclaration_1) {
|
|
1007
|
+
function deleteDeclaration(changes, deletedNodesInLists, sourceFile, node) {
|
|
1008
|
+
switch (node.kind) {
|
|
1009
|
+
case SyntaxKind.Parameter: {
|
|
1010
|
+
const oldFunction = node.parent;
|
|
1011
|
+
if (isArrowFunction(oldFunction) &&
|
|
1012
|
+
oldFunction.parameters.length === 1 &&
|
|
1013
|
+
!findChildOfKind(oldFunction, SyntaxKind.OpenParenToken, sourceFile)) {
|
|
1014
|
+
// Lambdas with exactly one parameter are special because, after removal, there
|
|
1015
|
+
// must be an empty parameter list (i.e. `()`) and this won't necessarily be the
|
|
1016
|
+
// case if the parameter is simply removed (e.g. in `x => 1`).
|
|
1017
|
+
changes.replaceNodeWithText(sourceFile, node, "()");
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1021
|
+
}
|
|
1022
|
+
break;
|
|
1023
|
+
}
|
|
1024
|
+
// Na tag, também o `import =` (CK-03).
|
|
1025
|
+
case SyntaxKind.ImportDeclaration:
|
|
1026
|
+
const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isAnyImportSyntax);
|
|
1027
|
+
// For first import, leave header comment in place, otherwise only delete JSDoc comments
|
|
1028
|
+
// Na tag, com o JSDoc preso ao import; a cópia não tem o JSDoc (CK-03).
|
|
1029
|
+
deleteNode(changes, sourceFile, node, { leadingTriviaOption: isFirstImport ? LeadingTriviaOption.Exclude : LeadingTriviaOption.StartLine });
|
|
1030
|
+
break;
|
|
1031
|
+
case SyntaxKind.BindingElement:
|
|
1032
|
+
const pattern = node.parent;
|
|
1033
|
+
const preserveComma = pattern.kind === SyntaxKind.ArrayBindingPattern && node !== last(pattern.elements);
|
|
1034
|
+
if (preserveComma) {
|
|
1035
|
+
deleteNode(changes, sourceFile, node);
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1039
|
+
}
|
|
1040
|
+
break;
|
|
1041
|
+
case SyntaxKind.VariableDeclaration:
|
|
1042
|
+
deleteVariableDeclaration(changes, deletedNodesInLists, sourceFile, node);
|
|
1043
|
+
break;
|
|
1044
|
+
case SyntaxKind.TypeParameter:
|
|
1045
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1046
|
+
break;
|
|
1047
|
+
case SyntaxKind.ImportSpecifier:
|
|
1048
|
+
const namedImports = node.parent;
|
|
1049
|
+
if (namedImports.elements.length === 1) {
|
|
1050
|
+
deleteImportBinding(changes, sourceFile, namedImports);
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1054
|
+
}
|
|
1055
|
+
break;
|
|
1056
|
+
case SyntaxKind.NamespaceImport:
|
|
1057
|
+
deleteImportBinding(changes, sourceFile, node);
|
|
1058
|
+
break;
|
|
1059
|
+
case SyntaxKind.SemicolonToken:
|
|
1060
|
+
deleteNode(changes, sourceFile, node, { trailingTriviaOption: TrailingTriviaOption.Exclude });
|
|
1061
|
+
break;
|
|
1062
|
+
// Na tag, também a palavra `function`, a classe e a `function` declarada (CK-02).
|
|
1063
|
+
default:
|
|
1064
|
+
if (isImportClause(node.parent) && node.parent.name === node) {
|
|
1065
|
+
deleteDefaultImport(changes, sourceFile, node.parent);
|
|
1066
|
+
}
|
|
1067
|
+
else if (isCallExpression(node.parent) && contains(node.parent.arguments, node)) {
|
|
1068
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1069
|
+
}
|
|
1070
|
+
else {
|
|
1071
|
+
deleteNode(changes, sourceFile, node);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
deleteDeclaration_1.deleteDeclaration = deleteDeclaration;
|
|
1076
|
+
function deleteDefaultImport(changes, sourceFile, importClause) {
|
|
1077
|
+
if (!importClause.namedBindings) {
|
|
1078
|
+
// Delete the whole import
|
|
1079
|
+
deleteNode(changes, sourceFile, importClause.parent);
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
// import |d,| * as ns from './file'
|
|
1083
|
+
const start = importClause.name.getStart(sourceFile);
|
|
1084
|
+
const nextToken = getTokenAtPosition(sourceFile, importClause.name.end);
|
|
1085
|
+
if (nextToken && nextToken.kind === SyntaxKind.CommaToken) {
|
|
1086
|
+
// shift first non-whitespace position after comma to the start position of the node
|
|
1087
|
+
const end = skipTrivia(sourceFile.text, nextToken.end, /*stopAfterLineBreaks*/ false, /*stopAtComments*/ true);
|
|
1088
|
+
changes.deleteRange(sourceFile, { pos: start, end });
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
deleteNode(changes, sourceFile, importClause.name);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
function deleteImportBinding(changes, sourceFile, node) {
|
|
1096
|
+
if (node.parent.name) {
|
|
1097
|
+
// Delete named imports while preserving the default import
|
|
1098
|
+
// import d|, * as ns| from './file'
|
|
1099
|
+
// import d|, { a }| from './file'
|
|
1100
|
+
const previousToken = Debug.checkDefined(getTokenAtPosition(sourceFile, node.pos - 1));
|
|
1101
|
+
changes.deleteRange(sourceFile, { pos: previousToken.getStart(sourceFile), end: node.end });
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
// Delete the entire import declaration
|
|
1105
|
+
// |import * as ns from './file'|
|
|
1106
|
+
// |import { a } from './file'|
|
|
1107
|
+
const importDecl = getAncestor(node, SyntaxKind.ImportDeclaration);
|
|
1108
|
+
deleteNode(changes, sourceFile, importDecl);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
function deleteVariableDeclaration(changes, deletedNodesInLists, sourceFile, node) {
|
|
1112
|
+
const { parent } = node;
|
|
1113
|
+
if (parent.kind === SyntaxKind.CatchClause) {
|
|
1114
|
+
// TODO: There's currently no unused diagnostic for this, could be a suggestion
|
|
1115
|
+
changes.deleteNodeRange(sourceFile, findChildOfKind(parent, SyntaxKind.OpenParenToken, sourceFile), findChildOfKind(parent, SyntaxKind.CloseParenToken, sourceFile));
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (parent.declarations.length !== 1) {
|
|
1119
|
+
deleteNodeInList(changes, deletedNodesInLists, sourceFile, node);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const gp = parent.parent;
|
|
1123
|
+
switch (gp.kind) {
|
|
1124
|
+
case SyntaxKind.ForOfStatement:
|
|
1125
|
+
case SyntaxKind.ForInStatement:
|
|
1126
|
+
changes.replaceNode(sourceFile, node, createObjectLiteral());
|
|
1127
|
+
break;
|
|
1128
|
+
case SyntaxKind.ForStatement:
|
|
1129
|
+
deleteNode(changes, sourceFile, parent);
|
|
1130
|
+
break;
|
|
1131
|
+
case SyntaxKind.VariableStatement:
|
|
1132
|
+
// Na tag, com o JSDoc preso à declaração; a cópia não tem o JSDoc (CK-03).
|
|
1133
|
+
deleteNode(changes, sourceFile, gp, { leadingTriviaOption: LeadingTriviaOption.StartLine });
|
|
1134
|
+
break;
|
|
1135
|
+
default:
|
|
1136
|
+
Debug.assertNever(gp);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
})(deleteDeclaration || (deleteDeclaration = {}));
|
|
1140
|
+
/** Warning: This deletes comments too. See `copyComments` in `convertFunctionToEs6Class`. */
|
|
1141
|
+
// Exported for tests only! (TODO: improve tests to not need this)
|
|
1142
|
+
export function deleteNode(changes, sourceFile, node, options = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }) {
|
|
1143
|
+
const startPosition = getAdjustedStartPosition(sourceFile, node, options);
|
|
1144
|
+
const endPosition = getAdjustedEndPosition(sourceFile, node, options);
|
|
1145
|
+
changes.deleteRange(sourceFile, { pos: startPosition, end: endPosition });
|
|
1146
|
+
}
|
|
1147
|
+
function deleteNodeInList(changes, deletedNodesInLists, sourceFile, node) {
|
|
1148
|
+
const containingList = Debug.checkDefined(formatting.SmartIndenter.getContainingList(node, sourceFile));
|
|
1149
|
+
const index = indexOfNode(containingList, node);
|
|
1150
|
+
Debug.assert(index !== -1);
|
|
1151
|
+
if (containingList.length === 1) {
|
|
1152
|
+
deleteNode(changes, sourceFile, node);
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
// Note: We will only delete a comma *after* a node. This will leave a trailing comma if we delete the last node.
|
|
1156
|
+
// That's handled in the end by `finishTrailingCommaAfterDeletingNodesInList`.
|
|
1157
|
+
Debug.assert(!deletedNodesInLists.has(node), "Deleting a node twice");
|
|
1158
|
+
deletedNodesInLists.add(node);
|
|
1159
|
+
changes.deleteRange(sourceFile, {
|
|
1160
|
+
pos: startPositionToDeleteNodeInList(sourceFile, node),
|
|
1161
|
+
end: index === containingList.length - 1 ? getAdjustedEndPosition(sourceFile, node, {}) : startPositionToDeleteNodeInList(sourceFile, containingList[index + 1]),
|
|
1162
|
+
});
|
|
1163
|
+
}
|