mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Implement semicolon insertion
This commit is contained in:
+13
-2
@@ -2158,8 +2158,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 { } // tslint:disable-line no-empty
|
||||
|
||||
@@ -1186,6 +1186,9 @@ namespace ts.formatting {
|
||||
recordReplace(previousRange.end, currentRange.pos - previousRange.end, " ");
|
||||
return onLaterLine ? LineAction.LineRemoved : LineAction.None;
|
||||
}
|
||||
break;
|
||||
case RuleAction.TrailingSemicolon:
|
||||
recordReplace(previousRange.end, 0, ";");
|
||||
}
|
||||
return LineAction.None;
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@ namespace ts.formatting {
|
||||
export const anyContext: ReadonlyArray<ContextPredicate> = emptyArray;
|
||||
|
||||
export const enum RuleAction {
|
||||
Ignore = 1 << 0,
|
||||
Space = 1 << 1,
|
||||
NewLine = 1 << 2,
|
||||
DeleteTrivia = 1 << 3,
|
||||
DeleteToken = 1 << 4,
|
||||
Ignore = 1 << 0,
|
||||
Space = 1 << 1,
|
||||
NewLine = 1 << 2,
|
||||
DeleteTrivia = 1 << 3,
|
||||
DeleteToken = 1 << 4,
|
||||
TrailingSemicolon = 1 << 5,
|
||||
|
||||
TriviaAction = Space | NewLine | DeleteTrivia,
|
||||
TokenAction = DeleteToken
|
||||
TokenAction = DeleteToken | TrailingSemicolon
|
||||
}
|
||||
|
||||
export const enum RuleFlags {
|
||||
|
||||
@@ -310,7 +310,8 @@ namespace ts.formatting {
|
||||
|
||||
rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space),
|
||||
rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia),
|
||||
rule("NoDiscretionarySemicolon", SyntaxKind.SemicolonToken, anyToken, [isSemicolonDeletionContext], RuleAction.DeleteToken),
|
||||
rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabled("insertTrailingSemicolon"), isSemicolonDeletionContext], RuleAction.DeleteToken),
|
||||
rule("OptionalSemicolon", anyToken, anyToken, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon),
|
||||
];
|
||||
|
||||
function isSemicolonDeletionContext(context: FormattingContext): boolean {
|
||||
@@ -329,6 +330,17 @@ namespace ts.formatting {
|
||||
&& context.nextTokenSpan.kind !== 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);
|
||||
}
|
||||
|
||||
// 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 ?
|
||||
|
||||
@@ -734,6 +734,7 @@ namespace ts {
|
||||
readonly placeOpenBraceOnNewLineForControlBlocks?: boolean;
|
||||
readonly insertSpaceBeforeTypeAnnotation?: boolean;
|
||||
readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
|
||||
readonly insertTrailingSemicolon?: boolean;
|
||||
}
|
||||
|
||||
export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings {
|
||||
@@ -757,6 +758,7 @@ namespace ts {
|
||||
insertSpaceBeforeFunctionParenthesis: false,
|
||||
placeOpenBraceOnNewLineForFunctions: false,
|
||||
placeOpenBraceOnNewLineForControlBlocks: false,
|
||||
insertTrailingSemicolon: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -743,7 +743,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 {
|
||||
@@ -1981,7 +1981,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
|
||||
@@ -1994,7 +2014,57 @@ 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): boolean {
|
||||
const lastToken = node.getLastToken();
|
||||
if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) {
|
||||
if (lastToken && lastToken.kind === SyntaxKind.CommaToken) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) {
|
||||
const children = node.parent.getChildren();
|
||||
const nextChild = children[children.indexOf(node) + 1];
|
||||
if (nextChild && isModuleBlock(nextChild)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) {
|
||||
const children = node.parent.getChildren();
|
||||
const nextChild = children[children.indexOf(node) + 1];
|
||||
if (nextChild && isFunctionBlock(nextChild)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!syntaxRequiresTrailingSemicolonOrASI(node.kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sourceFile = node.getSourceFile();
|
||||
const nextToken = findNextToken(node, sourceFile, sourceFile);
|
||||
if (!nextToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nextToken.kind === SyntaxKind.CloseBraceToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !positionsAreOnSameLine(node.getEnd(), nextToken.getStart(sourceFile), sourceFile);
|
||||
}
|
||||
|
||||
export function probablyUsesSemicolons(sourceFile: SourceFile): boolean {
|
||||
@@ -2002,7 +2072,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++;
|
||||
|
||||
Reference in New Issue
Block a user