mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add semicolon preference to formatter options (#33402)
* Add UserPreferences for semicolons
* Prototype formatter semicolon removal
* Implement semicolon insertion
* Fix existing tests
* Start adding tests
* Fix some edge cases of semicolon deletion
* Fix semicolon removal before comments
* Fix indentation
* Test on checker
* Replace semicolon-omitting writer with formatter preference
* Fix writing new nodes, update protocol
* Rename option
* Really fix formatting synthetic nodes
* Fix refactoring misses
* Un-update submodules gahhhh
* Update APIs
* Update for ESLint
* Revert accidental test change
* De-kludge deduplication of EOF processing
* Omit last element semicolon from single-line object-like types
* Revert "Omit last element semicolon from single-line object-like types"
This reverts commit 5625cb0237.
* Fix straggler test
* Add test for leading semicolon class members
* Rename a lot of stuff for clarity
* Invert some boolean logic
This commit is contained in:
+28
-2
@@ -329,6 +329,21 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `forEach`, but iterates in reverse order.
|
||||
*/
|
||||
export function forEachRight<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
|
||||
if (array) {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const result = callback(array[i], i);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). */
|
||||
export function firstDefined<T, U>(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined {
|
||||
if (array === undefined) {
|
||||
@@ -2159,8 +2174,19 @@ namespace ts {
|
||||
return (arg: T) => f(arg) && g(arg);
|
||||
}
|
||||
|
||||
export function or<T>(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean {
|
||||
return arg => f(arg) || g(arg);
|
||||
export function or<T extends unknown>(...fs: ((arg: T) => boolean)[]): (arg: T) => boolean {
|
||||
return arg => {
|
||||
for (const f of fs) {
|
||||
if (f(arg)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export function not<T extends unknown[]>(fn: (...args: T) => boolean): (...args: T) => boolean {
|
||||
return (...args) => !fn(...args);
|
||||
}
|
||||
|
||||
export function assertType<T>(_: T): void { }
|
||||
|
||||
@@ -3380,11 +3380,7 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
export interface TrailingSemicolonDeferringWriter extends EmitTextWriter {
|
||||
resetPendingTrailingSemicolon(): void;
|
||||
}
|
||||
|
||||
export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): TrailingSemicolonDeferringWriter {
|
||||
export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): EmitTextWriter {
|
||||
let pendingTrailingSemicolon = false;
|
||||
|
||||
function commitPendingTrailingSemicolon() {
|
||||
@@ -3451,20 +3447,6 @@ namespace ts {
|
||||
commitPendingTrailingSemicolon();
|
||||
writer.decreaseIndent();
|
||||
},
|
||||
resetPendingTrailingSemicolon() {
|
||||
pendingTrailingSemicolon = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter {
|
||||
const deferringWriter = getTrailingSemicolonDeferringWriter(writer);
|
||||
return {
|
||||
...deferringWriter,
|
||||
writeLine() {
|
||||
deferringWriter.resetPendingTrailingSemicolon();
|
||||
writer.writeLine();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+12
-22
@@ -1683,7 +1683,7 @@ namespace FourSlash {
|
||||
if (this.enableFormatting) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1756,7 +1756,7 @@ namespace FourSlash {
|
||||
if (this.enableFormatting) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
offset += this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1775,7 +1775,7 @@ namespace FourSlash {
|
||||
if (this.enableFormatting) {
|
||||
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings);
|
||||
if (edits.length) {
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1810,9 +1810,7 @@ namespace FourSlash {
|
||||
* @returns The number of characters added to the file as a result of the edits.
|
||||
* May be negative.
|
||||
*/
|
||||
private applyEdits(fileName: string, edits: readonly ts.TextChange[], isFormattingEdit: boolean): number {
|
||||
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
|
||||
const oldContent = this.getFileContent(fileName);
|
||||
private applyEdits(fileName: string, edits: readonly ts.TextChange[]): number {
|
||||
let runningOffset = 0;
|
||||
|
||||
forEachTextChange(edits, edit => {
|
||||
@@ -1833,14 +1831,6 @@ namespace FourSlash {
|
||||
runningOffset += editDelta;
|
||||
});
|
||||
|
||||
if (isFormattingEdit) {
|
||||
const newContent = this.getFileContent(fileName);
|
||||
|
||||
if (this.removeWhitespace(newContent) !== this.removeWhitespace(oldContent)) {
|
||||
this.raiseError("Formatting operation destroyed non-whitespace content");
|
||||
}
|
||||
}
|
||||
|
||||
return runningOffset;
|
||||
}
|
||||
|
||||
@@ -1856,17 +1846,17 @@ namespace FourSlash {
|
||||
|
||||
public formatDocument() {
|
||||
const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings);
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
|
||||
public formatSelection(start: number, end: number) {
|
||||
const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings);
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
|
||||
public formatOnType(pos: number, key: string) {
|
||||
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings);
|
||||
this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
|
||||
this.applyEdits(this.activeFile.fileName, edits);
|
||||
}
|
||||
|
||||
private editScriptAndUpdateMarkers(fileName: string, editStart: number, editEnd: number, newText: string) {
|
||||
@@ -2414,7 +2404,7 @@ namespace FourSlash {
|
||||
|
||||
if (options.applyChanges) {
|
||||
for (const change of action.changes) {
|
||||
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
|
||||
this.applyEdits(change.fileName, change.textChanges);
|
||||
}
|
||||
this.verifyNewContentAfterChange(options, action.changes.map(c => c.fileName));
|
||||
}
|
||||
@@ -2497,7 +2487,7 @@ namespace FourSlash {
|
||||
|
||||
private applyChanges(changes: readonly ts.FileTextChanges[]): void {
|
||||
for (const change of changes) {
|
||||
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
|
||||
this.applyEdits(change.fileName, change.textChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2525,7 +2515,7 @@ namespace FourSlash {
|
||||
ts.Debug.assert(codeFix.changes.length === 1);
|
||||
const change = ts.first(codeFix.changes);
|
||||
ts.Debug.assert(change.fileName === fileName);
|
||||
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
|
||||
this.applyEdits(change.fileName, change.textChanges);
|
||||
const text = range ? this.rangeText(range) : this.getFileContent(this.activeFile.fileName);
|
||||
actualTextArray.push(text);
|
||||
scriptInfo.updateContent(originalContent);
|
||||
@@ -2929,7 +2919,7 @@ namespace FourSlash {
|
||||
|
||||
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.emptyOptions)!;
|
||||
for (const edit of editInfo.edits) {
|
||||
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
|
||||
this.applyEdits(edit.fileName, edit.textChanges);
|
||||
}
|
||||
|
||||
let renameFilename: string | undefined;
|
||||
@@ -3045,7 +3035,7 @@ namespace FourSlash {
|
||||
const editInfo = this.languageService.getEditsForRefactor(marker.fileName, formattingOptions, marker.position, refactorNameToApply, actionName, ts.emptyOptions)!;
|
||||
|
||||
for (const edit of editInfo.edits) {
|
||||
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
|
||||
this.applyEdits(edit.fileName, edit.textChanges);
|
||||
}
|
||||
const actualContent = this.getFileContent(marker.fileName);
|
||||
|
||||
|
||||
@@ -2956,6 +2956,12 @@ namespace ts.server.protocol {
|
||||
Smart = "Smart",
|
||||
}
|
||||
|
||||
export enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove",
|
||||
}
|
||||
|
||||
export interface EditorSettings {
|
||||
baseIndentSize?: number;
|
||||
indentSize?: number;
|
||||
@@ -2982,6 +2988,7 @@ namespace ts.server.protocol {
|
||||
placeOpenBraceOnNewLineForFunctions?: boolean;
|
||||
placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
semicolons?: SemicolonPreference;
|
||||
}
|
||||
|
||||
export interface UserPreferences {
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace ts.codefix {
|
||||
// We sort the best codefixes first, so taking `first` is best for completions.
|
||||
const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, exportInfos, host, preferences)).moduleSpecifier;
|
||||
const fix = first(getFixForImport(exportInfos, symbolName, position, program, sourceFile, host, preferences));
|
||||
return { moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) };
|
||||
return { moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) };
|
||||
}
|
||||
|
||||
function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction {
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
namespace ts.formatting {
|
||||
export interface FormatContext {
|
||||
readonly options: FormatCodeSettings;
|
||||
readonly getRule: RulesMap;
|
||||
readonly getRules: RulesMap;
|
||||
}
|
||||
|
||||
export interface TextRangeWithKind extends TextRange {
|
||||
kind: SyntaxKind;
|
||||
export interface TextRangeWithKind<T extends SyntaxKind = SyntaxKind> extends TextRange {
|
||||
kind: T;
|
||||
}
|
||||
|
||||
export interface TextRangeWithTriviaKind extends TextRange {
|
||||
kind: TriviaKind;
|
||||
}
|
||||
export type TextRangeWithTriviaKind = TextRangeWithKind<TriviaKind>;
|
||||
|
||||
export interface TokenInfo {
|
||||
leadingTrivia: TextRangeWithTriviaKind[] | undefined;
|
||||
@@ -19,6 +17,16 @@ namespace ts.formatting {
|
||||
trailingTrivia: TextRangeWithTriviaKind[] | undefined;
|
||||
}
|
||||
|
||||
export function createTextRangeWithKind<T extends SyntaxKind>(pos: number, end: number, kind: T): TextRangeWithKind<T> {
|
||||
const textRangeWithKind: TextRangeWithKind<T> = { pos, end, kind };
|
||||
if (Debug.isDebugging) {
|
||||
Object.defineProperty(textRangeWithKind, "__debugKind", {
|
||||
get: () => Debug.formatSyntaxKind(kind),
|
||||
});
|
||||
}
|
||||
return textRangeWithKind;
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
Unknown = -1
|
||||
}
|
||||
@@ -386,7 +394,7 @@ namespace ts.formatting {
|
||||
initialIndentation: number,
|
||||
delta: number,
|
||||
formattingScanner: FormattingScanner,
|
||||
{ options, getRule }: FormatContext,
|
||||
{ options, getRules }: FormatContext,
|
||||
requestKind: FormattingRequestKind,
|
||||
rangeContainsError: (r: TextRange) => boolean,
|
||||
sourceFile: SourceFileLike): TextChange[] {
|
||||
@@ -469,7 +477,7 @@ namespace ts.formatting {
|
||||
parent: Node,
|
||||
parentDynamicIndentation: DynamicIndentation,
|
||||
effectiveParentStartLine: number
|
||||
): { indentation: number, delta: number } {
|
||||
): { indentation: number, delta: number; } {
|
||||
const delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize! : 0;
|
||||
|
||||
if (effectiveParentStartLine === startLine) {
|
||||
@@ -644,6 +652,21 @@ namespace ts.formatting {
|
||||
consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation, node);
|
||||
}
|
||||
|
||||
if (!node.parent && formattingScanner.isOnEOF()) {
|
||||
const token = formattingScanner.readEOFTokenRange();
|
||||
if (token.end <= node.end && previousRange) {
|
||||
processPair(
|
||||
token,
|
||||
sourceFile.getLineAndCharacterOfPosition(token.pos).line,
|
||||
node,
|
||||
previousRange,
|
||||
previousRangeStartLine,
|
||||
previousParent,
|
||||
contextNode,
|
||||
nodeDynamicIndentation);
|
||||
}
|
||||
}
|
||||
|
||||
function processChildNode(
|
||||
child: Node,
|
||||
inheritedIndentation: number,
|
||||
@@ -938,37 +961,41 @@ namespace ts.formatting {
|
||||
|
||||
formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode);
|
||||
|
||||
const rule = getRule(formattingContext);
|
||||
const rules = getRules(formattingContext);
|
||||
|
||||
let trimTrailingWhitespaces: boolean;
|
||||
let trimTrailingWhitespaces = false;
|
||||
let lineAction = LineAction.None;
|
||||
if (rule) {
|
||||
lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine);
|
||||
switch (lineAction) {
|
||||
case LineAction.LineRemoved:
|
||||
// Handle the case where the next line is moved to be the end of this line.
|
||||
// In this case we don't indent the next line in the next pass.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false);
|
||||
}
|
||||
break;
|
||||
case LineAction.LineAdded:
|
||||
// Handle the case where token2 is moved to the new line.
|
||||
// In this case we indent token2 in the next pass but we set
|
||||
// sameLineIndent flag to notify the indenter that the indentation is within the line.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.assert(lineAction === LineAction.None);
|
||||
}
|
||||
if (rules) {
|
||||
// Apply rules in reverse order so that higher priority rules (which are first in the array)
|
||||
// win in a conflict with lower priority rules.
|
||||
forEachRight(rules, rule => {
|
||||
lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine);
|
||||
switch (lineAction) {
|
||||
case LineAction.LineRemoved:
|
||||
// Handle the case where the next line is moved to be the end of this line.
|
||||
// In this case we don't indent the next line in the next pass.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false);
|
||||
}
|
||||
break;
|
||||
case LineAction.LineAdded:
|
||||
// Handle the case where token2 is moved to the new line.
|
||||
// In this case we indent token2 in the next pass but we set
|
||||
// sameLineIndent flag to notify the indenter that the indentation is within the line.
|
||||
if (currentParent.getStart(sourceFile) === currentItem.pos) {
|
||||
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Debug.assert(lineAction === LineAction.None);
|
||||
}
|
||||
|
||||
// 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
|
||||
trimTrailingWhitespaces = !(rule.action & RuleAction.Delete) && rule.flags !== RuleFlags.CanDeleteNewLines;
|
||||
// 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
|
||||
trimTrailingWhitespaces = !(rule.action & RuleAction.DeleteSpace) && rule.flags !== RuleFlags.CanDeleteNewLines;
|
||||
});
|
||||
}
|
||||
else {
|
||||
trimTrailingWhitespaces = true;
|
||||
trimTrailingWhitespaces = currentItem.kind !== SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
if (currentStartLine !== previousStartLine && trimTrailingWhitespaces) {
|
||||
@@ -1130,6 +1157,12 @@ namespace ts.formatting {
|
||||
}
|
||||
}
|
||||
|
||||
function recordInsert(start: number, text: string) {
|
||||
if (text) {
|
||||
edits.push(createTextChangeFromStartLength(start, 0, text));
|
||||
}
|
||||
}
|
||||
|
||||
function applyRuleEdits(rule: Rule,
|
||||
previousRange: TextRangeWithKind,
|
||||
previousStartLine: number,
|
||||
@@ -1138,17 +1171,20 @@ namespace ts.formatting {
|
||||
): LineAction {
|
||||
const onLaterLine = currentStartLine !== previousStartLine;
|
||||
switch (rule.action) {
|
||||
case RuleAction.Ignore:
|
||||
case RuleAction.StopProcessingSpaceActions:
|
||||
// no action required
|
||||
return LineAction.None;
|
||||
case RuleAction.Delete:
|
||||
case RuleAction.DeleteSpace:
|
||||
if (previousRange.end !== currentRange.pos) {
|
||||
// delete characters starting from t1.end up to t2.pos exclusive
|
||||
recordDelete(previousRange.end, currentRange.pos - previousRange.end);
|
||||
return onLaterLine ? LineAction.LineRemoved : LineAction.None;
|
||||
}
|
||||
break;
|
||||
case RuleAction.NewLine:
|
||||
case RuleAction.DeleteToken:
|
||||
recordDelete(previousRange.pos, previousRange.end - previousRange.pos);
|
||||
break;
|
||||
case RuleAction.InsertNewLine:
|
||||
// exit early if we on different lines and rule cannot change number of newlines
|
||||
// if line1 and line2 are on subsequent lines then no edits are required - ok to exit
|
||||
// if line1 and line2 are separated with more than one newline - ok to exit since we cannot delete extra new lines
|
||||
@@ -1163,7 +1199,7 @@ namespace ts.formatting {
|
||||
return onLaterLine ? LineAction.None : LineAction.LineAdded;
|
||||
}
|
||||
break;
|
||||
case RuleAction.Space:
|
||||
case RuleAction.InsertSpace:
|
||||
// exit early if we on different lines and rule cannot change number of newlines
|
||||
if (rule.flags !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
|
||||
return LineAction.None;
|
||||
@@ -1174,6 +1210,9 @@ namespace ts.formatting {
|
||||
recordReplace(previousRange.end, currentRange.pos - previousRange.end, " ");
|
||||
return onLaterLine ? LineAction.LineRemoved : LineAction.None;
|
||||
}
|
||||
break;
|
||||
case RuleAction.InsertTrailingSemicolon:
|
||||
recordInsert(previousRange.end, ";");
|
||||
}
|
||||
return LineAction.None;
|
||||
}
|
||||
@@ -1271,7 +1310,7 @@ namespace ts.formatting {
|
||||
return SyntaxKind.Unknown;
|
||||
}
|
||||
|
||||
let internedSizes: { tabSize: number; indentSize: number };
|
||||
let internedSizes: { tabSize: number; indentSize: number; };
|
||||
let internedTabsIndentation: string[] | undefined;
|
||||
let internedSpacesIndentation: string[] | undefined;
|
||||
|
||||
|
||||
@@ -99,4 +99,4 @@ namespace ts.formatting {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ namespace ts.formatting {
|
||||
export interface FormattingScanner {
|
||||
advance(): void;
|
||||
isOnToken(): boolean;
|
||||
isOnEOF(): boolean;
|
||||
readTokenInfo(n: Node): TokenInfo;
|
||||
readEOFTokenRange(): TextRangeWithKind;
|
||||
getCurrentLeadingTrivia(): TextRangeWithKind[] | undefined;
|
||||
lastTrailingTriviaWasNewLine(): boolean;
|
||||
skipToEndOf(node: Node): void;
|
||||
@@ -38,7 +40,9 @@ namespace ts.formatting {
|
||||
const res = cb({
|
||||
advance,
|
||||
readTokenInfo,
|
||||
readEOFTokenRange,
|
||||
isOnToken,
|
||||
isOnEOF,
|
||||
getCurrentLeadingTrivia: () => leadingTrivia,
|
||||
lastTrailingTriviaWasNewLine: () => wasNewLine,
|
||||
skipToEndOf,
|
||||
@@ -164,11 +168,11 @@ namespace ts.formatting {
|
||||
|
||||
let currentToken = getNextToken(n, expectedScanAction);
|
||||
|
||||
const token: TextRangeWithKind = {
|
||||
pos: scanner.getStartPos(),
|
||||
end: scanner.getTextPos(),
|
||||
kind: currentToken
|
||||
};
|
||||
const token = createTextRangeWithKind(
|
||||
scanner.getStartPos(),
|
||||
scanner.getTextPos(),
|
||||
currentToken,
|
||||
);
|
||||
|
||||
// consume trailing trivia
|
||||
if (trailingTrivia) {
|
||||
@@ -179,11 +183,11 @@ namespace ts.formatting {
|
||||
if (!isTrivia(currentToken)) {
|
||||
break;
|
||||
}
|
||||
const trivia: TextRangeWithTriviaKind = {
|
||||
pos: scanner.getStartPos(),
|
||||
end: scanner.getTextPos(),
|
||||
kind: currentToken
|
||||
};
|
||||
const trivia = createTextRangeWithKind(
|
||||
scanner.getStartPos(),
|
||||
scanner.getTextPos(),
|
||||
currentToken,
|
||||
);
|
||||
|
||||
if (!trailingTrivia) {
|
||||
trailingTrivia = [];
|
||||
@@ -243,12 +247,22 @@ namespace ts.formatting {
|
||||
return token;
|
||||
}
|
||||
|
||||
function readEOFTokenRange(): TextRangeWithKind<SyntaxKind.EndOfFileToken> {
|
||||
Debug.assert(isOnEOF());
|
||||
return createTextRangeWithKind(scanner.getStartPos(), scanner.getTextPos(), SyntaxKind.EndOfFileToken);
|
||||
}
|
||||
|
||||
function isOnToken(): boolean {
|
||||
const current = lastTokenInfo ? lastTokenInfo.token.kind : scanner.getToken();
|
||||
const startPos = lastTokenInfo ? lastTokenInfo.token.pos : scanner.getStartPos();
|
||||
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
|
||||
}
|
||||
|
||||
function isOnEOF(): boolean {
|
||||
const current = lastTokenInfo ? lastTokenInfo.token.kind : scanner.getToken();
|
||||
return current === SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
// when containing node in the tree is token
|
||||
// but its kind differs from the kind that was returned by the scanner,
|
||||
// then kind needs to be fixed. This might happen in cases
|
||||
|
||||
@@ -12,10 +12,17 @@ namespace ts.formatting {
|
||||
export const anyContext: readonly ContextPredicate[] = emptyArray;
|
||||
|
||||
export const enum RuleAction {
|
||||
Ignore = 1 << 0,
|
||||
Space = 1 << 1,
|
||||
NewLine = 1 << 2,
|
||||
Delete = 1 << 3,
|
||||
StopProcessingSpaceActions = 1 << 0,
|
||||
StopProcessingTokenActions = 1 << 1,
|
||||
InsertSpace = 1 << 2,
|
||||
InsertNewLine = 1 << 3,
|
||||
DeleteSpace = 1 << 4,
|
||||
DeleteToken = 1 << 5,
|
||||
InsertTrailingSemicolon = 1 << 6,
|
||||
|
||||
StopAction = StopProcessingSpaceActions | StopProcessingTokenActions,
|
||||
ModifySpaceAction = InsertSpace | InsertNewLine | DeleteSpace,
|
||||
ModifyTokenAction = DeleteToken | InsertTrailingSemicolon,
|
||||
}
|
||||
|
||||
export const enum RuleFlags {
|
||||
@@ -27,4 +34,4 @@ namespace ts.formatting {
|
||||
readonly tokens: readonly SyntaxKind[];
|
||||
readonly isSpecific: boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+213
-129
@@ -9,7 +9,9 @@ namespace ts.formatting {
|
||||
export function getAllRules(): RuleSpec[] {
|
||||
const allTokens: SyntaxKind[] = [];
|
||||
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
|
||||
allTokens.push(token);
|
||||
if (token !== SyntaxKind.EndOfFileToken) {
|
||||
allTokens.push(token);
|
||||
}
|
||||
}
|
||||
function anyTokenExcept(...tokens: SyntaxKind[]): TokenRange {
|
||||
return { tokens: allTokens.filter(t => !tokens.some(t2 => t2 === t)), isSpecific: false };
|
||||
@@ -17,6 +19,7 @@ namespace ts.formatting {
|
||||
|
||||
const anyToken: TokenRange = { tokens: allTokens, isSpecific: false };
|
||||
const anyTokenIncludingMultilineComments = tokenRangeFrom([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
|
||||
const anyTokenIncludingEOF = tokenRangeFrom([...allTokens, SyntaxKind.EndOfFileToken]);
|
||||
const keywords = tokenRangeFromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
|
||||
const binaryOperators = tokenRangeFromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
|
||||
const binaryKeywordOperators = [SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword];
|
||||
@@ -44,103 +47,103 @@ namespace ts.formatting {
|
||||
// These rules are higher in priority than user-configurable
|
||||
const highPriorityCommonRules = [
|
||||
// Leave comments alone
|
||||
rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.Ignore),
|
||||
rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.Ignore),
|
||||
rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.StopProcessingSpaceActions),
|
||||
rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.StopProcessingSpaceActions),
|
||||
|
||||
rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.Delete),
|
||||
rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete),
|
||||
rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteSpace),
|
||||
rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace),
|
||||
// insert space after '?' only when it is used in conditional operator
|
||||
rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.Space),
|
||||
rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.InsertSpace),
|
||||
|
||||
// in other cases there should be no space between '?' and next token
|
||||
rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.Delete),
|
||||
rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Special handling of unary operators.
|
||||
// Prefix operators generally shouldn't have a space between
|
||||
// them and their target unary expression.
|
||||
rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// More unary operator special-casing.
|
||||
// DevDiv 181814: Be careful when removing leading whitespace
|
||||
// around unary operators. Examples:
|
||||
// 1 - -2 --X--> 1--2
|
||||
// a + ++b --X--> a+++b
|
||||
rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterPostdecrementWhenFollowedBySubtract", SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterPostdecrementWhenFollowedBySubtract", SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
|
||||
rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
// For functions and control block place } on a new line [multi-line rule]
|
||||
rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.NewLine),
|
||||
rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.InsertNewLine),
|
||||
|
||||
// Space/new line after }.
|
||||
rule("SpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, anyTokenExcept(SyntaxKind.CloseParenToken), [isNonJsxSameLineTokenContext, isAfterCodeBlockContext], RuleAction.Space),
|
||||
rule("SpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, anyTokenExcept(SyntaxKind.CloseParenToken), [isNonJsxSameLineTokenContext, isAfterCodeBlockContext], RuleAction.InsertSpace),
|
||||
// Special case for (}, else) and (}, while) since else & while tokens are not part of the tree which makes SpaceAfterCloseBrace rule not applied
|
||||
// Also should not apply to })
|
||||
rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.Delete),
|
||||
rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Add a space after control dec context if the next character is an open bracket ex: 'if (false)[a, b] = [1, 2];' -> 'if (false) [a, b] = [1, 2];'
|
||||
rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.Space),
|
||||
rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.InsertSpace),
|
||||
|
||||
rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Delete),
|
||||
rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Space),
|
||||
rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.DeleteSpace),
|
||||
rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.InsertSpace),
|
||||
|
||||
rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.Space),
|
||||
rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.InsertSpace),
|
||||
// Insert new line after { and before } in multi-line contexts.
|
||||
rule("NewLineAfterOpenBraceInBlockContext", SyntaxKind.OpenBraceToken, anyToken, [isMultilineBlockContext], RuleAction.NewLine),
|
||||
rule("NewLineAfterOpenBraceInBlockContext", SyntaxKind.OpenBraceToken, anyToken, [isMultilineBlockContext], RuleAction.InsertNewLine),
|
||||
|
||||
// For get/set members, we check for (identifier,identifier) since get/set don't have tokens and they are represented as just an identifier token.
|
||||
// Though, we do extra check on the context to make sure we are dealing with get/set node. Example:
|
||||
// get x() {}
|
||||
// set x(val) {}
|
||||
rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.Space),
|
||||
rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.InsertSpace),
|
||||
|
||||
rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.Delete),
|
||||
rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.Space),
|
||||
rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteSpace),
|
||||
rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.InsertSpace),
|
||||
|
||||
rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.Space),
|
||||
rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.Delete),
|
||||
rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.DeleteSpace),
|
||||
|
||||
// Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options.
|
||||
rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterBinaryKeywordOperator", binaryKeywordOperators, anyToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterBinaryKeywordOperator", binaryKeywordOperators, anyToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
|
||||
rule("SpaceAfterVoidOperator", SyntaxKind.VoidKeyword, anyToken, [isNonJsxSameLineTokenContext, isVoidOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterVoidOperator", SyntaxKind.VoidKeyword, anyToken, [isNonJsxSameLineTokenContext, isVoidOpContext], RuleAction.InsertSpace),
|
||||
|
||||
// Async-await
|
||||
rule("SpaceBetweenAsyncAndOpenParen", SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken, [isArrowFunctionContext, isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBetweenAsyncAndOpenParen", SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken, [isArrowFunctionContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
|
||||
// Template string
|
||||
rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// JSX opening elements
|
||||
rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// TypeScript-specific rules
|
||||
// Use of module as a function call. e.g.: import m2 = module("m2");
|
||||
rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
// Add a space around certain TypeScript keywords
|
||||
rule(
|
||||
"SpaceAfterCertainTypeScriptKeywords",
|
||||
@@ -171,41 +174,41 @@ namespace ts.formatting {
|
||||
],
|
||||
anyToken,
|
||||
[isNonJsxSameLineTokenContext],
|
||||
RuleAction.Space),
|
||||
RuleAction.InsertSpace),
|
||||
rule(
|
||||
"SpaceBeforeCertainTypeScriptKeywords",
|
||||
anyToken,
|
||||
[SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword],
|
||||
[isNonJsxSameLineTokenContext],
|
||||
RuleAction.Space),
|
||||
RuleAction.InsertSpace),
|
||||
// Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" {
|
||||
rule("SpaceAfterModuleName", SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken, [isModuleDeclContext], RuleAction.Space),
|
||||
rule("SpaceAfterModuleName", SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken, [isModuleDeclContext], RuleAction.InsertSpace),
|
||||
|
||||
// Lambda expressions
|
||||
rule("SpaceBeforeArrow", anyToken, SyntaxKind.EqualsGreaterThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBeforeArrow", anyToken, SyntaxKind.EqualsGreaterThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
|
||||
// Optional parameters and let args
|
||||
rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Remove spaces in empty interface literals. e.g.: x: {}
|
||||
rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.Delete),
|
||||
rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.DeleteSpace),
|
||||
|
||||
// generics and type assertions
|
||||
rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete),
|
||||
rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterCloseAngularBracket",
|
||||
SyntaxKind.GreaterThanToken,
|
||||
[SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken],
|
||||
[isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext, isNotFunctionDeclContext /*To prevent an interference with the SpaceBeforeOpenParenInFuncDecl rule*/],
|
||||
RuleAction.Delete),
|
||||
RuleAction.DeleteSpace),
|
||||
|
||||
// decorators
|
||||
rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
// Insert space after @ in decorator
|
||||
rule("SpaceAfterDecorator",
|
||||
anyToken,
|
||||
@@ -225,111 +228,113 @@ namespace ts.formatting {
|
||||
SyntaxKind.AsteriskToken,
|
||||
],
|
||||
[isEndOfDecoratorContextOnSameLine],
|
||||
RuleAction.Space),
|
||||
RuleAction.InsertSpace),
|
||||
|
||||
rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.Delete),
|
||||
rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.DeleteSpace),
|
||||
rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
];
|
||||
|
||||
// These rules are applied after high priority
|
||||
const userConfigurableRules = [
|
||||
// Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses
|
||||
rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.Space),
|
||||
rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.Delete),
|
||||
rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after function keyword for anonymous functions
|
||||
rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Delete),
|
||||
rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after keywords in control flow statements
|
||||
rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.Delete),
|
||||
rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after opening and before closing nonempty parenthesis
|
||||
rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after opening and before closing nonempty brackets
|
||||
rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
|
||||
rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space),
|
||||
rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space),
|
||||
rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after opening and before closing template string braces
|
||||
rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// No space after { and before } in JSX expression
|
||||
rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space),
|
||||
rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Delete),
|
||||
rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace),
|
||||
rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space after semicolon in for statement
|
||||
rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.Delete),
|
||||
rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Insert space before and after binary operators
|
||||
rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Delete),
|
||||
rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.Delete),
|
||||
rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.DeleteSpace),
|
||||
|
||||
// Open Brace braces after control block
|
||||
rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines),
|
||||
rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
|
||||
|
||||
// Open Brace braces after function
|
||||
// TypeScript: Function can have return types, which can be made of tons of different token kinds
|
||||
rule("NewLineBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines),
|
||||
rule("NewLineBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
|
||||
// Open Brace braces after TypeScript module/class/interface
|
||||
rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines),
|
||||
rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
|
||||
|
||||
rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Space),
|
||||
rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Delete),
|
||||
rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Delete),
|
||||
rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.InsertSpace),
|
||||
rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteSpace),
|
||||
rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken),
|
||||
rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.InsertTrailingSemicolon),
|
||||
];
|
||||
|
||||
// These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list.
|
||||
const lowPriorityCommonRules = [
|
||||
// Space after keyword but not before ; or : or ?
|
||||
rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines),
|
||||
rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines),
|
||||
rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines),
|
||||
rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
|
||||
rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
|
||||
rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
|
||||
|
||||
rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
|
||||
// No space before and after indexer `x[]`
|
||||
rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete),
|
||||
rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete),
|
||||
rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
|
||||
rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.DeleteSpace),
|
||||
rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
|
||||
// Remove extra space between for and await
|
||||
rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
|
||||
// Add a space between statements. All keywords except (do,else,case) has open/close parens after them.
|
||||
// So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any]
|
||||
@@ -338,9 +343,9 @@ namespace ts.formatting {
|
||||
[SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword],
|
||||
anyToken,
|
||||
[isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNotForContext],
|
||||
RuleAction.Space),
|
||||
RuleAction.InsertSpace),
|
||||
// This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter.
|
||||
rule("SpaceAfterTryFinally", [SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.Space),
|
||||
rule("SpaceAfterTryFinally", [SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
|
||||
];
|
||||
|
||||
return [
|
||||
@@ -395,6 +400,10 @@ namespace ts.formatting {
|
||||
/// Contexts
|
||||
///
|
||||
|
||||
function optionEquals<K extends keyof FormatCodeSettings>(optionName: K, optionValue: FormatCodeSettings[K]): (context: FormattingContext) => boolean {
|
||||
return (context) => context.options && context.options[optionName] === optionValue;
|
||||
}
|
||||
|
||||
function isOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
|
||||
return (context) => context.options && context.options.hasOwnProperty(optionName) && !!context.options[optionName];
|
||||
}
|
||||
@@ -780,4 +789,79 @@ namespace ts.formatting {
|
||||
function isNonNullAssertionContext(context: FormattingContext): boolean {
|
||||
return context.contextNode.kind === SyntaxKind.NonNullExpression;
|
||||
}
|
||||
|
||||
function isSemicolonDeletionContext(context: FormattingContext): boolean {
|
||||
let nextTokenKind = context.nextTokenSpan.kind;
|
||||
let nextTokenStart = context.nextTokenSpan.pos;
|
||||
if (isTrivia(nextTokenKind)) {
|
||||
const nextRealToken = context.nextTokenParent === context.currentTokenParent
|
||||
? findNextToken(
|
||||
context.currentTokenParent,
|
||||
findAncestor(context.currentTokenParent, a => !a.parent)!,
|
||||
context.sourceFile)
|
||||
: context.nextTokenParent.getFirstToken(context.sourceFile);
|
||||
if (!nextRealToken) {
|
||||
return true;
|
||||
}
|
||||
nextTokenKind = nextRealToken.kind;
|
||||
nextTokenStart = nextRealToken.getStart(context.sourceFile);
|
||||
}
|
||||
|
||||
const startLine = context.sourceFile.getLineAndCharacterOfPosition(context.currentTokenSpan.pos).line;
|
||||
const endLine = context.sourceFile.getLineAndCharacterOfPosition(nextTokenStart).line;
|
||||
if (startLine === endLine) {
|
||||
return nextTokenKind === SyntaxKind.CloseBraceToken
|
||||
|| nextTokenKind === SyntaxKind.EndOfFileToken;
|
||||
}
|
||||
|
||||
if (nextTokenKind === SyntaxKind.SemicolonClassElement ||
|
||||
nextTokenKind === SyntaxKind.SemicolonToken
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (context.contextNode.kind === SyntaxKind.InterfaceDeclaration ||
|
||||
context.contextNode.kind === SyntaxKind.TypeAliasDeclaration
|
||||
) {
|
||||
// Can’t remove semicolon after `foo`; it would parse as a method declaration:
|
||||
//
|
||||
// interface I {
|
||||
// foo;
|
||||
// (): void
|
||||
// }
|
||||
return !isPropertySignature(context.currentTokenParent)
|
||||
|| !!context.currentTokenParent.type
|
||||
|| nextTokenKind !== SyntaxKind.OpenParenToken;
|
||||
}
|
||||
|
||||
if (isPropertyDeclaration(context.currentTokenParent)) {
|
||||
return !context.currentTokenParent.initializer;
|
||||
}
|
||||
|
||||
return context.currentTokenParent.kind !== SyntaxKind.ForStatement
|
||||
&& context.currentTokenParent.kind !== SyntaxKind.EmptyStatement
|
||||
&& context.currentTokenParent.kind !== SyntaxKind.SemicolonClassElement
|
||||
&& nextTokenKind !== SyntaxKind.OpenBracketToken
|
||||
&& nextTokenKind !== SyntaxKind.OpenParenToken
|
||||
&& nextTokenKind !== SyntaxKind.PlusToken
|
||||
&& nextTokenKind !== SyntaxKind.MinusToken
|
||||
&& nextTokenKind !== SyntaxKind.SlashToken
|
||||
&& nextTokenKind !== SyntaxKind.RegularExpressionLiteral
|
||||
&& nextTokenKind !== SyntaxKind.CommaToken
|
||||
&& nextTokenKind !== SyntaxKind.TemplateExpression
|
||||
&& nextTokenKind !== SyntaxKind.TemplateHead
|
||||
&& nextTokenKind !== SyntaxKind.NoSubstitutionTemplateLiteral
|
||||
&& nextTokenKind !== SyntaxKind.DotToken;
|
||||
}
|
||||
|
||||
function isSemicolonInsertionContext(context: FormattingContext): boolean {
|
||||
const contextAncestor = findAncestor(context.currentTokenParent, ancestor => {
|
||||
if (ancestor.end !== context.currentTokenSpan.end) {
|
||||
return "quit";
|
||||
}
|
||||
return syntaxMayBeASICandidate(ancestor.kind);
|
||||
});
|
||||
|
||||
return !!contextAncestor && isASICandidate(contextAncestor, context.sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @internal */
|
||||
namespace ts.formatting {
|
||||
export function getFormatContext(options: FormatCodeSettings): FormatContext {
|
||||
return { options, getRule: getRulesMap() };
|
||||
return { options, getRules: getRulesMap() };
|
||||
}
|
||||
|
||||
let rulesMapCache: RulesMap | undefined;
|
||||
@@ -13,12 +13,46 @@ namespace ts.formatting {
|
||||
return rulesMapCache;
|
||||
}
|
||||
|
||||
export type RulesMap = (context: FormattingContext) => Rule | undefined;
|
||||
/**
|
||||
* For a given rule action, gets a mask of other rule actions that
|
||||
* cannot be applied at the same position.
|
||||
*/
|
||||
function getRuleActionExclusion(ruleAction: RuleAction): RuleAction {
|
||||
let mask: RuleAction = 0;
|
||||
if (ruleAction & RuleAction.StopProcessingSpaceActions) {
|
||||
mask |= RuleAction.ModifySpaceAction;
|
||||
}
|
||||
if (ruleAction & RuleAction.StopProcessingTokenActions) {
|
||||
mask |= RuleAction.ModifyTokenAction;
|
||||
}
|
||||
if (ruleAction & RuleAction.ModifySpaceAction) {
|
||||
mask |= RuleAction.ModifySpaceAction;
|
||||
}
|
||||
if (ruleAction & RuleAction.ModifyTokenAction) {
|
||||
mask |= RuleAction.ModifyTokenAction;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
export type RulesMap = (context: FormattingContext) => readonly Rule[] | undefined;
|
||||
function createRulesMap(rules: readonly RuleSpec[]): RulesMap {
|
||||
const map = buildMap(rules);
|
||||
return context => {
|
||||
const bucket = map[getRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind)];
|
||||
return bucket && find(bucket, rule => every(rule.context, c => c(context)));
|
||||
if (bucket) {
|
||||
const rules: Rule[] = [];
|
||||
let ruleActionMask: RuleAction = 0;
|
||||
for (const rule of bucket) {
|
||||
const acceptRuleActions = ~getRuleActionExclusion(ruleActionMask);
|
||||
if (rule.action & acceptRuleActions && every(rule.context, c => c(context))) {
|
||||
rules.push(rule);
|
||||
ruleActionMask |= rule.action;
|
||||
}
|
||||
}
|
||||
if (rules.length) {
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,8 +88,8 @@ namespace ts.formatting {
|
||||
const mapRowLength = SyntaxKind.LastToken + 1;
|
||||
|
||||
enum RulesPosition {
|
||||
IgnoreRulesSpecific = 0,
|
||||
IgnoreRulesAny = maskBitSize * 1,
|
||||
StopRulesSpecific = 0,
|
||||
StopRulesAny = maskBitSize * 1,
|
||||
ContextRulesSpecific = maskBitSize * 2,
|
||||
ContextRulesAny = maskBitSize * 3,
|
||||
NoContextRulesSpecific = maskBitSize * 4,
|
||||
@@ -78,8 +112,8 @@ namespace ts.formatting {
|
||||
// In order to insert a rule to the end of sub-bucket (3), we get the index by adding
|
||||
// the values in the bitmap segments 3rd, 2nd, and 1st.
|
||||
function addRule(rules: Rule[], rule: Rule, specificTokens: boolean, constructionState: number[], rulesBucketIndex: number): void {
|
||||
const position = rule.action === RuleAction.Ignore ?
|
||||
specificTokens ? RulesPosition.IgnoreRulesSpecific : RulesPosition.IgnoreRulesAny :
|
||||
const position = rule.action & RuleAction.StopAction ?
|
||||
specificTokens ? RulesPosition.StopRulesSpecific : RulesPosition.StopRulesAny :
|
||||
rule.context !== anyContext ?
|
||||
specificTokens ? RulesPosition.ContextRulesSpecific : RulesPosition.ContextRulesAny :
|
||||
specificTokens ? RulesPosition.NoContextRulesSpecific : RulesPosition.NoContextRulesAny;
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace ts {
|
||||
newFileOrDirPath: string,
|
||||
host: LanguageServiceHost,
|
||||
formatContext: formatting.FormatContext,
|
||||
_preferences: UserPreferences,
|
||||
preferences: UserPreferences,
|
||||
sourceMapper: SourceMapper,
|
||||
): readonly FileTextChanges[] {
|
||||
const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host);
|
||||
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
const oldToNew = getPathUpdater(oldFileOrDirPath, newFileOrDirPath, getCanonicalFileName, sourceMapper);
|
||||
const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper);
|
||||
return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => {
|
||||
return textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => {
|
||||
updateTsconfigFiles(program, changeTracker, oldToNew, oldFileOrDirPath, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames);
|
||||
updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName);
|
||||
});
|
||||
|
||||
@@ -12,10 +12,10 @@ namespace ts.OrganizeImports {
|
||||
formatContext: formatting.FormatContext,
|
||||
host: LanguageServiceHost,
|
||||
program: Program,
|
||||
_preferences: UserPreferences,
|
||||
preferences: UserPreferences,
|
||||
) {
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext });
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext, preferences });
|
||||
|
||||
const coalesceAndOrganizeImports = (importGroup: readonly ImportDeclaration[]) => coalesceImports(removeUnusedImports(importGroup, sourceFile, program));
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace ts {
|
||||
return this._children || (this._children = createChildren(this, sourceFile));
|
||||
}
|
||||
|
||||
public getFirstToken(sourceFile?: SourceFile): Node | undefined {
|
||||
public getFirstToken(sourceFile?: SourceFileLike): Node | undefined {
|
||||
this.assertHasRealPosition();
|
||||
const children = this.getChildren(sourceFile);
|
||||
if (!children.length) {
|
||||
@@ -112,7 +112,7 @@ namespace ts {
|
||||
child.getFirstToken(sourceFile);
|
||||
}
|
||||
|
||||
public getLastToken(sourceFile?: SourceFile): Node | undefined {
|
||||
public getLastToken(sourceFile?: SourceFileLike): Node | undefined {
|
||||
this.assertHasRealPosition();
|
||||
const children = this.getChildren(sourceFile);
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ namespace ts.textChanges {
|
||||
export interface TextChangesContext {
|
||||
host: LanguageServiceHost;
|
||||
formatContext: formatting.FormatContext;
|
||||
preferences: UserPreferences;
|
||||
}
|
||||
|
||||
export type TypeAnnotatable = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature;
|
||||
@@ -831,28 +832,36 @@ namespace ts.textChanges {
|
||||
return (options.prefix || "") + noIndent + (options.suffix || "");
|
||||
}
|
||||
|
||||
function getFormatCodeSettingsForWriting({ options }: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings {
|
||||
const shouldAutoDetectSemicolonPreference = !options.semicolons || options.semicolons === SemicolonPreference.Ignore;
|
||||
const shouldRemoveSemicolons = options.semicolons === SemicolonPreference.Remove || shouldAutoDetectSemicolonPreference && !probablyUsesSemicolons(sourceFile);
|
||||
return {
|
||||
...options,
|
||||
semicolons: shouldRemoveSemicolons ? SemicolonPreference.Remove : SemicolonPreference.Ignore,
|
||||
};
|
||||
}
|
||||
|
||||
/** Note: this may mutate `nodeIn`. */
|
||||
function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string {
|
||||
const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter);
|
||||
if (validate) validate(node, text);
|
||||
const { options: formatOptions } = formatContext;
|
||||
const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile);
|
||||
const initialIndentation =
|
||||
indentation !== undefined
|
||||
? indentation
|
||||
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
|
||||
if (delta === undefined) {
|
||||
delta = formatting.SmartIndenter.shouldIndentChildNode(formatContext.options, nodeIn) ? (formatOptions.indentSize || 0) : 0;
|
||||
delta = formatting.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0;
|
||||
}
|
||||
|
||||
const file: SourceFileLike = { text, getLineAndCharacterOfPosition(pos) { return getLineAndCharacterOfPosition(this, pos); } };
|
||||
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, formatContext);
|
||||
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions });
|
||||
return applyChanges(text, changes);
|
||||
}
|
||||
|
||||
/** Note: output node may be mutated input node. */
|
||||
export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } {
|
||||
const omitTrailingSemicolon = !!sourceFile && !probablyUsesSemicolons(sourceFile);
|
||||
const writer = createWriter(newLineCharacter, omitTrailingSemicolon);
|
||||
const writer = createWriter(newLineCharacter);
|
||||
const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed;
|
||||
createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer);
|
||||
return { text: writer.getText(), node: assignPositionsToNode(node) };
|
||||
@@ -894,11 +903,11 @@ namespace ts.textChanges {
|
||||
|
||||
interface TextChangesWriter extends EmitTextWriter, PrintHandlers {}
|
||||
|
||||
function createWriter(newLine: string, omitTrailingSemicolon?: boolean): TextChangesWriter {
|
||||
function createWriter(newLine: string): TextChangesWriter {
|
||||
let lastNonTriviaPosition = 0;
|
||||
|
||||
|
||||
const writer = omitTrailingSemicolon ? getTrailingSemicolonOmittingWriter(createTextWriter(newLine)) : createTextWriter(newLine);
|
||||
const writer = createTextWriter(newLine);
|
||||
const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => {
|
||||
if (node) {
|
||||
setPos(node, lastNonTriviaPosition);
|
||||
|
||||
@@ -17,7 +17,11 @@ namespace ts {
|
||||
getFullText(sourceFile?: SourceFile): string;
|
||||
getText(sourceFile?: SourceFile): string;
|
||||
getFirstToken(sourceFile?: SourceFile): Node | undefined;
|
||||
/* @internal */
|
||||
getFirstToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
getLastToken(sourceFile?: SourceFile): Node | undefined;
|
||||
/* @internal */
|
||||
getLastToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures
|
||||
// See ts.forEachChild for documentation.
|
||||
forEachChild<T>(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray<Node>) => T | undefined): T | undefined;
|
||||
}
|
||||
@@ -680,6 +684,12 @@ namespace ts {
|
||||
Smart = 2,
|
||||
}
|
||||
|
||||
export enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove",
|
||||
}
|
||||
|
||||
/* @deprecated - consider using EditorSettings instead */
|
||||
export interface EditorOptions {
|
||||
BaseIndentSize?: number;
|
||||
@@ -738,6 +748,7 @@ namespace ts {
|
||||
readonly placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
readonly insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
|
||||
readonly semicolons?: SemicolonPreference;
|
||||
}
|
||||
|
||||
export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings {
|
||||
@@ -761,6 +772,7 @@ namespace ts {
|
||||
insertSpaceBeforeFunctionParenthesis: false,
|
||||
placeOpenBraceOnNewLineForFunctions: false,
|
||||
placeOpenBraceOnNewLineForControlBlocks: false,
|
||||
semicolons: SemicolonPreference.Ignore,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -749,7 +749,7 @@ namespace ts {
|
||||
return findPrecedingToken(position, file);
|
||||
}
|
||||
|
||||
export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFile): Node | undefined {
|
||||
export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFileLike): Node | undefined {
|
||||
return find(parent);
|
||||
|
||||
function find(n: Node): Node | undefined {
|
||||
@@ -757,7 +757,7 @@ namespace ts {
|
||||
// this is token that starts at the end of previous token - return it
|
||||
return n;
|
||||
}
|
||||
return firstDefined(n.getChildren(), child => {
|
||||
return firstDefined(n.getChildren(sourceFile), child => {
|
||||
const shouldDiveInChildNode =
|
||||
// previous token is enclosed somewhere in the child
|
||||
(child.pos <= previousToken.pos && child.end > previousToken.end) ||
|
||||
@@ -1989,7 +1989,27 @@ namespace ts {
|
||||
return typeIsAccessible ? res : undefined;
|
||||
}
|
||||
|
||||
export function syntaxUsuallyHasTrailingSemicolon(kind: SyntaxKind) {
|
||||
export function syntaxRequiresTrailingCommaOrSemicolonOrASI(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.CallSignature
|
||||
|| kind === SyntaxKind.ConstructSignature
|
||||
|| kind === SyntaxKind.IndexSignature
|
||||
|| kind === SyntaxKind.PropertySignature
|
||||
|| kind === SyntaxKind.MethodSignature;
|
||||
}
|
||||
|
||||
export function syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.FunctionDeclaration
|
||||
|| kind === SyntaxKind.Constructor
|
||||
|| kind === SyntaxKind.MethodDeclaration
|
||||
|| kind === SyntaxKind.GetAccessor
|
||||
|| kind === SyntaxKind.SetAccessor;
|
||||
}
|
||||
|
||||
export function syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.ModuleDeclaration;
|
||||
}
|
||||
|
||||
export function syntaxRequiresTrailingSemicolonOrASI(kind: SyntaxKind) {
|
||||
return kind === SyntaxKind.VariableStatement
|
||||
|| kind === SyntaxKind.ExpressionStatement
|
||||
|| kind === SyntaxKind.DoStatement
|
||||
@@ -2002,7 +2022,58 @@ namespace ts {
|
||||
|| kind === SyntaxKind.TypeAliasDeclaration
|
||||
|| kind === SyntaxKind.ImportDeclaration
|
||||
|| kind === SyntaxKind.ImportEqualsDeclaration
|
||||
|| kind === SyntaxKind.ExportDeclaration;
|
||||
|| kind === SyntaxKind.ExportDeclaration
|
||||
|| kind === SyntaxKind.NamespaceExportDeclaration
|
||||
|| kind === SyntaxKind.ExportAssignment;
|
||||
}
|
||||
|
||||
export const syntaxMayBeASICandidate = or(
|
||||
syntaxRequiresTrailingCommaOrSemicolonOrASI,
|
||||
syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI,
|
||||
syntaxRequiresTrailingModuleBlockOrSemicolonOrASI,
|
||||
syntaxRequiresTrailingSemicolonOrASI);
|
||||
|
||||
export function isASICandidate(node: Node, sourceFile: SourceFileLike): boolean {
|
||||
const lastToken = node.getLastToken(sourceFile);
|
||||
if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) {
|
||||
if (lastToken && lastToken.kind === SyntaxKind.CommaToken) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) {
|
||||
const lastChild = last(node.getChildren(sourceFile));
|
||||
if (lastChild && isModuleBlock(lastChild)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) {
|
||||
const lastChild = last(node.getChildren(sourceFile));
|
||||
if (lastChild && isFunctionBlock(lastChild)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!syntaxRequiresTrailingSemicolonOrASI(node.kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// See comment in parser’s `parseDoStatement`
|
||||
if (node.kind === SyntaxKind.DoStatement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const topNode = findAncestor(node, ancestor => !ancestor.parent)!;
|
||||
const nextToken = findNextToken(node, topNode, sourceFile);
|
||||
if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line;
|
||||
const endLine = sourceFile.getLineAndCharacterOfPosition(nextToken.getStart(sourceFile)).line;
|
||||
return startLine !== endLine;
|
||||
}
|
||||
|
||||
export function probablyUsesSemicolons(sourceFile: SourceFile): boolean {
|
||||
@@ -2010,7 +2081,7 @@ namespace ts {
|
||||
let withoutSemicolon = 0;
|
||||
const nStatementsToObserve = 5;
|
||||
forEachChild(sourceFile, function visit(node): boolean | undefined {
|
||||
if (syntaxUsuallyHasTrailingSemicolon(node.kind)) {
|
||||
if (syntaxRequiresTrailingSemicolonOrASI(node.kind)) {
|
||||
const lastToken = node.getLastToken(sourceFile);
|
||||
if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
|
||||
withSemicolon++;
|
||||
|
||||
@@ -5298,6 +5298,11 @@ declare namespace ts {
|
||||
Block = 1,
|
||||
Smart = 2
|
||||
}
|
||||
enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove"
|
||||
}
|
||||
interface EditorOptions {
|
||||
BaseIndentSize?: number;
|
||||
IndentSize: number;
|
||||
@@ -5350,6 +5355,7 @@ declare namespace ts {
|
||||
readonly placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
readonly insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
|
||||
readonly semicolons?: SemicolonPreference;
|
||||
}
|
||||
function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings;
|
||||
interface DefinitionInfo extends DocumentSpan {
|
||||
@@ -8197,6 +8203,11 @@ declare namespace ts.server.protocol {
|
||||
Block = "Block",
|
||||
Smart = "Smart"
|
||||
}
|
||||
enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove"
|
||||
}
|
||||
interface EditorSettings {
|
||||
baseIndentSize?: number;
|
||||
indentSize?: number;
|
||||
@@ -8222,6 +8233,7 @@ declare namespace ts.server.protocol {
|
||||
placeOpenBraceOnNewLineForFunctions?: boolean;
|
||||
placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
semicolons?: SemicolonPreference;
|
||||
}
|
||||
interface UserPreferences {
|
||||
readonly disableSuggestions?: boolean;
|
||||
|
||||
@@ -5298,6 +5298,11 @@ declare namespace ts {
|
||||
Block = 1,
|
||||
Smart = 2
|
||||
}
|
||||
enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove"
|
||||
}
|
||||
interface EditorOptions {
|
||||
BaseIndentSize?: number;
|
||||
IndentSize: number;
|
||||
@@ -5350,6 +5355,7 @@ declare namespace ts {
|
||||
readonly placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
readonly insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
|
||||
readonly semicolons?: SemicolonPreference;
|
||||
}
|
||||
function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings;
|
||||
interface DefinitionInfo extends DocumentSpan {
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
//// return result
|
||||
//// }
|
||||
|
||||
verify.rangeAfterCodeFix("app: { use: (arg0: string) => any; }");
|
||||
verify.rangeAfterCodeFix("app: { use: (arg0: string) => any }");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// @noImplicitAny: true
|
||||
////function wrap( [| arr |] ) {
|
||||
//// arr.other(function (a: number, b: number) { return a < b ? -1 : 1 })
|
||||
//// arr.other(function (a: number, b: number) { return a < b ? -1 : 1 });
|
||||
//// }
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/29330
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////*a*/console.log(0) //
|
||||
////console.log(0)/*b*/
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Insert });
|
||||
goTo.select("a", "b");
|
||||
edit.applyRefactor({
|
||||
refactorName: "Extract Symbol",
|
||||
actionName: "function_scope_0",
|
||||
actionDescription: "Extract to function in global scope",
|
||||
newContent:
|
||||
`/*RENAME*/newFunction();
|
||||
|
||||
function newFunction() {
|
||||
console.log(0); //
|
||||
console.log(0);
|
||||
}
|
||||
`
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////*a*/console.log(0); //
|
||||
////console.log(0);/*b*/
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove });
|
||||
goTo.select("a", "b");
|
||||
edit.applyRefactor({
|
||||
refactorName: "Extract Symbol",
|
||||
actionName: "function_scope_0",
|
||||
actionDescription: "Extract to function in global scope",
|
||||
newContent:
|
||||
`/*RENAME*/newFunction()
|
||||
|
||||
function newFunction() {
|
||||
console.log(0) //
|
||||
console.log(0)
|
||||
}
|
||||
`
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
/////*a*/console.log(0) //
|
||||
////console.log(0)/*b*/
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Ignore });
|
||||
goTo.select("a", "b");
|
||||
edit.applyRefactor({
|
||||
refactorName: "Extract Symbol",
|
||||
actionName: "function_scope_0",
|
||||
actionDescription: "Extract to function in global scope",
|
||||
newContent:
|
||||
`/*RENAME*/newFunction()
|
||||
|
||||
function newFunction() {
|
||||
console.log(0) //
|
||||
console.log(0)
|
||||
}
|
||||
`
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////console.log(1)
|
||||
////console.log(2)
|
||||
////const x = function() { }
|
||||
////for (let i = 0; i < 1; i++) {
|
||||
//// 1
|
||||
//// 2
|
||||
////}
|
||||
////do { } while (false) console.log(3)
|
||||
////function f() { }
|
||||
////class C {
|
||||
//// ["one"] = {}
|
||||
//// ["two"]
|
||||
//// three: string
|
||||
//// m() { }
|
||||
//// ;["three"] = {}
|
||||
//// ;["four"]
|
||||
////}
|
||||
////enum E {
|
||||
//// C
|
||||
////}
|
||||
////type M<T> = { [K in keyof T]: any }
|
||||
////declare module 'foo' { }
|
||||
////declare module 'bar'
|
||||
////type T = { x: string, y: number }
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Insert });
|
||||
format.document();
|
||||
verify.currentFileContentIs(`console.log(1);
|
||||
console.log(2);
|
||||
const x = function() { };
|
||||
for (let i = 0; i < 1; i++) {
|
||||
1;
|
||||
2;
|
||||
}
|
||||
do { } while (false); console.log(3);
|
||||
function f() { }
|
||||
class C {
|
||||
["one"] = {}
|
||||
["two"];
|
||||
three: string;
|
||||
m() { }
|
||||
;["three"] = {}
|
||||
;["four"];
|
||||
}
|
||||
enum E {
|
||||
C
|
||||
}
|
||||
type M<T> = { [K in keyof T]: any };
|
||||
declare module 'foo' { }
|
||||
declare module 'bar';
|
||||
type T = { x: string, y: number; };`);
|
||||
@@ -0,0 +1,77 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////; (function f() { })();
|
||||
////const a = 3;
|
||||
////+ 4;
|
||||
////const b = 3
|
||||
////+ 4;
|
||||
////const c = 3 +
|
||||
////4;
|
||||
////class C {
|
||||
//// prop;
|
||||
//// ["p"];
|
||||
//// zero: void;
|
||||
//// ["one"] = {};
|
||||
//// ["two"];
|
||||
//// ;
|
||||
////}
|
||||
////a;
|
||||
////`b`;
|
||||
////b;
|
||||
////(3);
|
||||
////4;
|
||||
//// / regex /;
|
||||
////;
|
||||
////[];
|
||||
/////** blah */[0];
|
||||
////interface I {
|
||||
//// new;
|
||||
//// ();
|
||||
//// foo;
|
||||
//// ();
|
||||
////}
|
||||
////type T = {
|
||||
//// new;
|
||||
//// ();
|
||||
//// foo;
|
||||
//// ();
|
||||
////}
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove });
|
||||
format.document();
|
||||
verify.currentFileContentIs(`; (function f() { })()
|
||||
const a = 3;
|
||||
+ 4
|
||||
const b = 3
|
||||
+ 4
|
||||
const c = 3 +
|
||||
4
|
||||
class C {
|
||||
prop
|
||||
["p"]
|
||||
zero: void
|
||||
["one"] = {};
|
||||
["two"];
|
||||
;
|
||||
}
|
||||
a;
|
||||
\`b\`
|
||||
b;
|
||||
(3)
|
||||
4;
|
||||
/ regex /;
|
||||
;
|
||||
[];
|
||||
/** blah */[0]
|
||||
interface I {
|
||||
new;
|
||||
()
|
||||
foo;
|
||||
()
|
||||
}
|
||||
type T = {
|
||||
new;
|
||||
()
|
||||
foo;
|
||||
()
|
||||
}`);
|
||||
@@ -0,0 +1,27 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////namespace ts {
|
||||
//// let x = 0;
|
||||
//// //
|
||||
//// interface I {
|
||||
//// a: string;
|
||||
//// /** @internal */
|
||||
//// b: string;
|
||||
//// }
|
||||
//// let y = 0; //
|
||||
////}
|
||||
////let z = 0; //
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove });
|
||||
format.document();
|
||||
verify.currentFileContentIs(`namespace ts {
|
||||
let x = 0
|
||||
//
|
||||
interface I {
|
||||
a: string
|
||||
/** @internal */
|
||||
b: string
|
||||
}
|
||||
let y = 0 //
|
||||
}
|
||||
let z = 0 //`);
|
||||
@@ -0,0 +1,11 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(members);
|
||||
////// Start with signatures at empty array in case of recursive types
|
||||
////(<InterfaceTypeWithDeclaredMembers>type).declaredCallSignatures = emptyArray;
|
||||
|
||||
format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove });
|
||||
format.document();
|
||||
verify.currentFileContentIs(`(<InterfaceTypeWithDeclaredMembers>type).declaredProperties = getNamedMembers(members);
|
||||
// Start with signatures at empty array in case of recursive types
|
||||
(<InterfaceTypeWithDeclaredMembers>type).declaredCallSignatures = emptyArray`);
|
||||
@@ -66,6 +66,12 @@ declare module ts {
|
||||
Smart = 2,
|
||||
}
|
||||
|
||||
enum SemicolonPreference {
|
||||
Ignore = "ignore",
|
||||
Insert = "insert",
|
||||
Remove = "remove",
|
||||
}
|
||||
|
||||
interface OutputFile {
|
||||
name: string;
|
||||
writeByteOrderMark: boolean;
|
||||
@@ -96,6 +102,11 @@ declare namespace FourSlashInterface {
|
||||
position: number;
|
||||
data?: any;
|
||||
}
|
||||
enum IndentStyle {
|
||||
None = 0,
|
||||
Block = 1,
|
||||
Smart = 2,
|
||||
}
|
||||
interface EditorOptions {
|
||||
BaseIndentSize?: number,
|
||||
IndentSize: number;
|
||||
@@ -103,6 +114,14 @@ declare namespace FourSlashInterface {
|
||||
NewLineCharacter: string;
|
||||
ConvertTabsToSpaces: boolean;
|
||||
}
|
||||
interface EditorSettings {
|
||||
baseIndentSize?: number;
|
||||
indentSize?: number;
|
||||
tabSize?: number;
|
||||
newLineCharacter?: string;
|
||||
convertTabsToSpaces?: boolean;
|
||||
indentStyle?: IndentStyle;
|
||||
}
|
||||
interface FormatCodeOptions extends EditorOptions {
|
||||
InsertSpaceAfterCommaDelimiter: boolean;
|
||||
InsertSpaceAfterSemicolonInForStatements: boolean;
|
||||
@@ -119,6 +138,26 @@ declare namespace FourSlashInterface {
|
||||
insertSpaceBeforeTypeAnnotation: boolean;
|
||||
[s: string]: boolean | number | string | undefined;
|
||||
}
|
||||
interface FormatCodeSettings extends EditorSettings {
|
||||
readonly insertSpaceAfterCommaDelimiter?: boolean;
|
||||
readonly insertSpaceAfterSemicolonInForStatements?: boolean;
|
||||
readonly insertSpaceBeforeAndAfterBinaryOperators?: boolean;
|
||||
readonly insertSpaceAfterConstructor?: boolean;
|
||||
readonly insertSpaceAfterKeywordsInControlFlowStatements?: boolean;
|
||||
readonly insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean;
|
||||
readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
|
||||
readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
|
||||
readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
|
||||
readonly insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
|
||||
readonly insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
|
||||
readonly insertSpaceAfterTypeAssertion?: boolean;
|
||||
readonly insertSpaceBeforeFunctionParenthesis?: boolean;
|
||||
readonly placeOpenBraceOnNewLineForFunctions?: boolean;
|
||||
readonly placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
readonly insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
|
||||
readonly semicolons?: ts.SemicolonPreference;
|
||||
}
|
||||
interface Range {
|
||||
fileName: string;
|
||||
pos: number;
|
||||
@@ -375,7 +414,7 @@ declare namespace FourSlashInterface {
|
||||
class format {
|
||||
document(): void;
|
||||
copyFormatOptions(): FormatCodeOptions;
|
||||
setFormatOptions(options: FormatCodeOptions): any;
|
||||
setFormatOptions(options: FormatCodeOptions | FormatCodeSettings): any;
|
||||
selection(startMarker: string, endMarker: string): void;
|
||||
onType(posMarker: string, key: string): void;
|
||||
setOption(name: keyof FormatCodeOptions, value: number | string | boolean): void;
|
||||
|
||||
Reference in New Issue
Block a user