Remove unnecessary wrapper classes in ts.formatting.Rule (#19744)

* Remove unnecessary wrapper classes in ts.formatting.Rule

* RulesProvider -> immutable FormatContext

* Remove Rules class, just use a list of rules

* Remove Shared namespace, replace Shared.TokenRange with TokenRange

* Simplify TokenRange

* Separate Rule and RuleSpec

* Move FormattingRequestKind to formattingContext.ts

* Simplify references

* Fix lint

* Revert removal of trailing newlines
This commit is contained in:
Andy 2017-11-08 13:39:03 -08:00 committed by GitHub
parent 20e36dba53
commit 5ad7e9516b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 853 additions and 1456 deletions

View file

@ -60,20 +60,11 @@
"../services/jsTyping.ts",
"../services/formatting/formatting.ts",
"../services/formatting/formattingContext.ts",
"../services/formatting/formattingRequestKind.ts",
"../services/formatting/formattingScanner.ts",
"../services/formatting/references.ts",
"../services/formatting/rule.ts",
"../services/formatting/ruleAction.ts",
"../services/formatting/ruleDescriptor.ts",
"../services/formatting/ruleFlag.ts",
"../services/formatting/ruleOperation.ts",
"../services/formatting/ruleOperationContext.ts",
"../services/formatting/rules.ts",
"../services/formatting/rulesMap.ts",
"../services/formatting/rulesProvider.ts",
"../services/formatting/smartIndenter.ts",
"../services/formatting/tokenRange.ts",
"../services/codeFixProvider.ts",
"../services/codefixes/fixes.ts",
"../services/codefixes/helpers.ts",

View file

@ -67,33 +67,27 @@ namespace ts {
}
export const newLineCharacter = "\n";
export const getRuleProvider = memoize(getRuleProviderInternal);
function getRuleProviderInternal() {
const options = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
const rulesProvider = new formatting.RulesProvider();
rulesProvider.ensureUpToDate(options);
return rulesProvider;
}
export const testFormatOptions: ts.FormatCodeSettings = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
const notImplementedHost: LanguageServiceHost = {
getCompilationSettings: notImplemented,
@ -133,7 +127,7 @@ namespace ts {
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
rulesProvider: getRuleProvider()
formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);
@ -197,7 +191,7 @@ namespace ts {
startPosition: selectionRange.start,
endPosition: selectionRange.end,
host: notImplementedHost,
rulesProvider: getRuleProvider()
formatContext: formatting.getFormatContext(testFormatOptions),
};
const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText);

View file

@ -23,60 +23,8 @@ namespace ts {
const printerOptions = { newLine: NewLineKind.LineFeed };
const newLineCharacter = getNewLineCharacter(printerOptions);
const getRuleProviderDefault = memoize(() => {
const options = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
const rulesProvider = new formatting.RulesProvider();
rulesProvider.ensureUpToDate(options);
return rulesProvider;
});
const getRuleProviderNewlineBrace = memoize(() => {
const options = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: true,
placeOpenBraceOnNewLineForControlBlocks: false,
};
const rulesProvider = new formatting.RulesProvider();
rulesProvider.ensureUpToDate(options);
return rulesProvider;
});
function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean) {
return placeOpenBraceOnNewLineForFunctions ? getRuleProviderNewlineBrace() : getRuleProviderDefault();
function getRuleProvider(placeOpenBraceOnNewLineForFunctions: boolean): formatting.FormatContext {
return formatting.getFormatContext(placeOpenBraceOnNewLineForFunctions ? { ...testFormatOptions, placeOpenBraceOnNewLineForFunctions: true } : testFormatOptions);
}
// validate that positions that were recovered from the printed text actually match positions that will be created if the same text is parsed.

View file

@ -166,7 +166,7 @@ namespace ts.codefix {
return {
host: context.host,
newLineCharacter: context.newLineCharacter,
rulesProvider: context.rulesProvider,
formatContext: context.formatContext,
sourceFile: context.sourceFile,
checker,
compilerOptions: context.program.getCompilerOptions(),

View file

@ -415,7 +415,7 @@ namespace ts.Completions {
entryId: CompletionEntryIdentifier,
allSourceFiles: ReadonlyArray<SourceFile>,
host: LanguageServiceHost,
rulesProvider: formatting.RulesProvider,
formatContext: formatting.FormatContext,
): CompletionEntryDetails {
const { name, source } = entryId;
// Compute all the completion symbols again.
@ -436,7 +436,7 @@ namespace ts.Completions {
}
case "symbol": {
const { symbol, location, symbolToOriginInfoMap } = symbolCompletion;
const codeActions = getCompletionEntryCodeActions(symbolToOriginInfoMap, symbol, typeChecker, host, compilerOptions, sourceFile, rulesProvider);
const codeActions = getCompletionEntryCodeActions(symbolToOriginInfoMap, symbol, typeChecker, host, compilerOptions, sourceFile, formatContext);
const kindModifiers = SymbolDisplay.getSymbolModifiers(symbol);
const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All);
return { name, kindModifiers, kind: symbolKind, displayParts, documentation, tags, codeActions, source: source === undefined ? undefined : [textPart(source)] };
@ -467,7 +467,7 @@ namespace ts.Completions {
host: LanguageServiceHost,
compilerOptions: CompilerOptions,
sourceFile: SourceFile,
rulesProvider: formatting.RulesProvider,
formatContext: formatting.FormatContext,
): CodeAction[] | undefined {
const symbolOriginInfo = symbolToOriginInfoMap[getSymbolId(symbol)];
if (!symbolOriginInfo) {
@ -481,7 +481,7 @@ namespace ts.Completions {
newLineCharacter: host.getNewLine(),
compilerOptions,
sourceFile,
rulesProvider,
formatContext,
symbolName: symbol.name,
getCanonicalFileName: createGetCanonicalFileName(host.useCaseSensitiveFileNames ? host.useCaseSensitiveFileNames() : false),
symbolToken: undefined,

View file

@ -1,10 +1,14 @@
///<reference path='..\services.ts' />
///<reference path='formattingScanner.ts' />
///<reference path='rulesProvider.ts' />
///<reference path='references.ts' />
/// <reference path="formattingContext.ts" />
/// <reference path="formattingScanner.ts" />
/// <reference path="rule.ts" />
/// <reference path="rulesMap.ts" />
/* @internal */
namespace ts.formatting {
export interface FormatContext {
readonly options: ts.FormatCodeSettings;
readonly getRule: ts.formatting.RulesMap;
}
export interface TextRangeWithKind extends TextRange {
kind: SyntaxKind;
@ -67,7 +71,7 @@ namespace ts.formatting {
delta: number;
}
export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatOnEnter(position: number, sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
const line = sourceFile.getLineAndCharacterOfPosition(position).line;
if (line === 0) {
return [];
@ -93,15 +97,15 @@ namespace ts.formatting {
// end value is exclusive so add 1 to the result
end: endOfFormatSpan + 1
};
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter);
return formatSpan(span, sourceFile, formatContext, FormattingRequestKind.FormatOnEnter);
}
export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatOnSemicolon(position: number, sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
const semicolon = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.SemicolonToken, sourceFile);
return formatNodeLines(findOutermostNodeWithinListLevel(semicolon), sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon);
return formatNodeLines(findOutermostNodeWithinListLevel(semicolon), sourceFile, formatContext, FormattingRequestKind.FormatOnSemicolon);
}
export function formatOnOpeningCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatOnOpeningCurly(position: number, sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
const openingCurly = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.OpenBraceToken, sourceFile);
if (!openingCurly) {
return [];
@ -126,29 +130,29 @@ namespace ts.formatting {
end: position
};
return formatSpan(textRange, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnOpeningCurlyBrace);
return formatSpan(textRange, sourceFile, formatContext, FormattingRequestKind.FormatOnOpeningCurlyBrace);
}
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
const precedingToken = findImmediatelyPrecedingTokenOfKind(position, SyntaxKind.CloseBraceToken, sourceFile);
return formatNodeLines(findOutermostNodeWithinListLevel(precedingToken), sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace);
return formatNodeLines(findOutermostNodeWithinListLevel(precedingToken), sourceFile, formatContext, FormattingRequestKind.FormatOnClosingCurlyBrace);
}
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatDocument(sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
const span = {
pos: 0,
end: sourceFile.text.length
};
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatDocument);
return formatSpan(span, sourceFile, formatContext, FormattingRequestKind.FormatDocument);
}
export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
export function formatSelection(start: number, end: number, sourceFile: SourceFile, formatContext: FormatContext): TextChange[] {
// format from the beginning of the line
const span = {
pos: getLineStartPositionForPosition(start, sourceFile),
end,
};
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection);
return formatSpan(span, sourceFile, formatContext, FormattingRequestKind.FormatSelection);
}
/**
@ -337,7 +341,7 @@ namespace ts.formatting {
}
/* @internal */
export function formatNodeGivenIndentation(node: Node, sourceFileLike: SourceFileLike, languageVariant: LanguageVariant, initialIndentation: number, delta: number, rulesProvider: RulesProvider): TextChange[] {
export function formatNodeGivenIndentation(node: Node, sourceFileLike: SourceFileLike, languageVariant: LanguageVariant, initialIndentation: number, delta: number, formatContext: FormatContext): TextChange[] {
const range = { pos: 0, end: sourceFileLike.text.length };
return getFormattingScanner(sourceFileLike.text, languageVariant, range.pos, range.end, scanner => formatSpanWorker(
range,
@ -345,14 +349,13 @@ namespace ts.formatting {
initialIndentation,
delta,
scanner,
rulesProvider.getFormatOptions(),
rulesProvider,
formatContext,
FormattingRequestKind.FormatSelection,
_ => false, // assume that node does not have any errors
sourceFileLike));
}
function formatNodeLines(node: Node, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
function formatNodeLines(node: Node, sourceFile: SourceFile, formatContext: FormatContext, requestKind: FormattingRequestKind): TextChange[] {
if (!node) {
return [];
}
@ -362,24 +365,19 @@ namespace ts.formatting {
end: node.end
};
return formatSpan(span, sourceFile, options, rulesProvider, requestKind);
return formatSpan(span, sourceFile, formatContext, requestKind);
}
function formatSpan(originalRange: TextRange,
sourceFile: SourceFile,
options: FormatCodeSettings,
rulesProvider: RulesProvider,
requestKind: FormattingRequestKind): TextChange[] {
function formatSpan(originalRange: TextRange, sourceFile: SourceFile, formatContext: FormatContext, requestKind: FormattingRequestKind): TextChange[] {
// find the smallest node that fully wraps the range and compute the initial indentation for the node
const enclosingNode = findEnclosingNode(originalRange, sourceFile);
return getFormattingScanner(sourceFile.text, sourceFile.languageVariant, getScanStartPosition(enclosingNode, originalRange, sourceFile), originalRange.end, scanner => formatSpanWorker(
originalRange,
enclosingNode,
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, options),
getOwnOrInheritedDelta(enclosingNode, options, sourceFile),
SmartIndenter.getIndentationForNode(enclosingNode, originalRange, sourceFile, formatContext.options),
getOwnOrInheritedDelta(enclosingNode, formatContext.options, sourceFile),
scanner,
options,
rulesProvider,
formatContext,
requestKind,
prepareRangeContainsErrorFunction(sourceFile.parseDiagnostics, originalRange),
sourceFile));
@ -390,8 +388,7 @@ namespace ts.formatting {
initialIndentation: number,
delta: number,
formattingScanner: FormattingScanner,
options: FormatCodeSettings,
rulesProvider: RulesProvider,
{ options, getRule }: FormatContext,
requestKind: FormattingRequestKind,
rangeContainsError: (r: TextRange) => boolean,
sourceFile: SourceFileLike): TextChange[] {
@ -917,14 +914,14 @@ namespace ts.formatting {
formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode);
const rule = rulesProvider.getRulesMap().GetRule(formattingContext);
const rule = getRule(formattingContext);
let trimTrailingWhitespaces: boolean;
let lineAdded: boolean;
if (rule) {
applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine);
if (rule.operation.action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
if (rule.action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
lineAdded = false;
// 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.
@ -932,7 +929,7 @@ namespace ts.formatting {
dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false);
}
}
else if (rule.operation.action & RuleAction.NewLine && currentStartLine === previousStartLine) {
else if (rule.action & RuleAction.NewLine && currentStartLine === previousStartLine) {
lineAdded = true;
// Handle the case where token2 is moved to the new line.
// In this case we indent token2 in the next pass but we set
@ -943,7 +940,7 @@ namespace ts.formatting {
}
// 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.operation.action & RuleAction.Delete) && rule.flag !== RuleFlags.CanDeleteNewLines;
trimTrailingWhitespaces = !(rule.action & RuleAction.Delete) && rule.flags !== RuleFlags.CanDeleteNewLines;
}
else {
trimTrailingWhitespaces = true;
@ -1118,7 +1115,7 @@ namespace ts.formatting {
currentRange: TextRangeWithKind,
currentStartLine: number): void {
switch (rule.operation.action) {
switch (rule.action) {
case RuleAction.Ignore:
// no action required
return;
@ -1132,7 +1129,7 @@ namespace ts.formatting {
// 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
if (rule.flag !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
if (rule.flags !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
return;
}
@ -1144,7 +1141,7 @@ namespace ts.formatting {
break;
case RuleAction.Space:
// exit early if we on different lines and rule cannot change number of newlines
if (rule.flag !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
if (rule.flags !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) {
return;
}

View file

@ -1,7 +1,14 @@
/// <reference path="references.ts"/>
/* @internal */
namespace ts.formatting {
export const enum FormattingRequestKind {
FormatDocument,
FormatSelection,
FormatOnEnter,
FormatOnSemicolon,
FormatOnOpeningCurlyBrace,
FormatOnClosingCurlyBrace
}
export class FormattingContext {
public currentTokenSpan: TextRangeWithKind;
public nextTokenSpan: TextRangeWithKind;

View file

@ -1,13 +0,0 @@
/// <reference path="references.ts"/>
/* @internal */
namespace ts.formatting {
export const enum FormattingRequestKind {
FormatDocument,
FormatSelection,
FormatOnEnter,
FormatOnSemicolon,
FormatOnOpeningCurlyBrace,
FormatOnClosingCurlyBrace
}
}

View file

@ -1,12 +0,0 @@
///<reference path='..\services.ts' />
///<reference path='formattingContext.ts' />
///<reference path='formattingRequestKind.ts' />
///<reference path='rule.ts' />
///<reference path='ruleAction.ts' />
///<reference path='ruleDescriptor.ts' />
///<reference path='ruleFlag.ts' />
///<reference path='ruleOperation.ts' />
///<reference path='ruleOperationContext.ts' />
///<reference path='rules.ts' />
///<reference path='rulesMap.ts' />
///<reference path='tokenRange.ts' />

View file

@ -1,14 +1,30 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export class Rule {
export interface Rule {
// Used for debugging to identify each rule based on the property name it's assigned to.
public debugName?: string;
constructor(
readonly descriptor: RuleDescriptor,
readonly operation: RuleOperation,
readonly flag: RuleFlags = RuleFlags.None) {
}
readonly debugName: string;
readonly context: ReadonlyArray<ContextPredicate>;
readonly action: RuleAction;
readonly flags: RuleFlags;
}
export type ContextPredicate = (context: FormattingContext) => boolean;
export const anyContext: ReadonlyArray<ContextPredicate> = emptyArray;
export const enum RuleAction {
Ignore = 1 << 0,
Space = 1 << 1,
NewLine = 1 << 2,
Delete = 1 << 3,
}
export const enum RuleFlags {
None,
CanDeleteNewLines,
}
export interface TokenRange {
readonly tokens: ReadonlyArray<SyntaxKind>;
readonly isSpecific: boolean;
}
}

View file

@ -1,11 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export const enum RuleAction {
Ignore = 0x00000001,
Space = 0x00000002,
NewLine = 0x00000004,
Delete = 0x00000008
}
}

View file

@ -1,30 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export class RuleDescriptor {
constructor(public leftTokenRange: Shared.TokenRange, public rightTokenRange: Shared.TokenRange) {
}
public toString(): string {
return "[leftRange=" + this.leftTokenRange + "," +
"rightRange=" + this.rightTokenRange + "]";
}
static create1(left: SyntaxKind, right: SyntaxKind): RuleDescriptor {
return RuleDescriptor.create4(Shared.TokenRange.FromToken(left), Shared.TokenRange.FromToken(right));
}
static create2(left: Shared.TokenRange, right: SyntaxKind): RuleDescriptor {
return RuleDescriptor.create4(left, Shared.TokenRange.FromToken(right));
}
static create3(left: SyntaxKind, right: Shared.TokenRange): RuleDescriptor {
return RuleDescriptor.create4(Shared.TokenRange.FromToken(left), right);
}
static create4(left: Shared.TokenRange, right: Shared.TokenRange): RuleDescriptor {
return new RuleDescriptor(left, right);
}
}
}

View file

@ -1,10 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export const enum RuleFlags {
None,
CanDeleteNewLines
}
}

View file

@ -1,21 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export class RuleOperation {
constructor(readonly context: RuleOperationContext, readonly action: RuleAction) {}
public toString(): string {
return "[context=" + this.context + "," +
"action=" + this.action + "]";
}
static create1(action: RuleAction) {
return RuleOperation.create2(RuleOperationContext.any, action);
}
static create2(context: RuleOperationContext, action: RuleAction) {
return new RuleOperation(context, action);
}
}
}

View file

@ -1,32 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export class RuleOperationContext {
private readonly customContextChecks: ((context: FormattingContext) => boolean)[];
constructor(...funcs: ((context: FormattingContext) => boolean)[]) {
this.customContextChecks = funcs;
}
static readonly any: RuleOperationContext = new RuleOperationContext();
public IsAny(): boolean {
return this === RuleOperationContext.any;
}
public InContext(context: FormattingContext): boolean {
if (this.IsAny()) {
return true;
}
for (const check of this.customContextChecks) {
if (!check(context)) {
return false;
}
}
return true;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,60 +1,59 @@
///<reference path='references.ts' />
/// <reference path="rules.ts" />
/* @internal */
namespace ts.formatting {
export class RulesMap {
public map: RulesBucket[];
public mapRowLength: number;
export function getFormatContext(options: FormatCodeSettings): formatting.FormatContext {
return { options, getRule: getRulesMap() };
}
constructor(rules: ReadonlyArray<Rule>) {
this.mapRowLength = SyntaxKind.LastToken + 1;
this.map = new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
let rulesMapCache: RulesMap | undefined;
// This array is used only during construction of the rulesbucket in the map
const rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array<RulesBucketConstructionState>(this.map.length);
for (const rule of rules) {
this.FillRule(rule, rulesBucketConstructionStateList);
}
function getRulesMap(): RulesMap {
if (rulesMapCache === undefined) {
rulesMapCache = createRulesMap(getAllRules());
}
return rulesMapCache;
}
private GetRuleBucketIndex(row: number, column: number): number {
Debug.assert(row <= SyntaxKind.LastKeyword && column <= SyntaxKind.LastKeyword, "Must compute formatting context from tokens");
return (row * this.mapRowLength) + column;
}
export type RulesMap = (context: FormattingContext) => Rule | undefined;
function createRulesMap(rules: ReadonlyArray<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)));
};
}
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
const specificRule = rule.descriptor.leftTokenRange.isSpecific() && rule.descriptor.rightTokenRange.isSpecific();
function buildMap(rules: ReadonlyArray<RuleSpec>): ReadonlyArray<ReadonlyArray<Rule>> {
// Map from bucket index to array of rules
const map: Rule[][] = new Array(mapRowLength * mapRowLength);
// This array is used only during construction of the rulesbucket in the map
const rulesBucketConstructionStateList = new Array<number>(map.length);
for (const rule of rules) {
const specificRule = rule.leftTokenRange.isSpecific && rule.rightTokenRange.isSpecific;
rule.descriptor.leftTokenRange.GetTokens().forEach((left) => {
rule.descriptor.rightTokenRange.GetTokens().forEach((right) => {
const rulesBucketIndex = this.GetRuleBucketIndex(left, right);
let rulesBucket = this.map[rulesBucketIndex];
for (const left of rule.leftTokenRange.tokens) {
for (const right of rule.rightTokenRange.tokens) {
const index = getRuleBucketIndex(left, right);
let rulesBucket = map[index];
if (rulesBucket === undefined) {
rulesBucket = this.map[rulesBucketIndex] = new RulesBucket();
}
rulesBucket.AddRule(rule, specificRule, rulesBucketConstructionStateList, rulesBucketIndex);
});
});
}
public GetRule(context: FormattingContext): Rule | undefined {
const bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind);
const bucket = this.map[bucketIndex];
if (bucket) {
for (const rule of bucket.Rules()) {
if (rule.operation.context.InContext(context)) {
return rule;
rulesBucket = map[index] = [];
}
addRule(rulesBucket, rule.rule, specificRule, rulesBucketConstructionStateList, index);
}
}
return undefined;
}
return map;
}
function getRuleBucketIndex(row: number, column: number): number {
Debug.assert(row <= SyntaxKind.LastKeyword && column <= SyntaxKind.LastKeyword, "Must compute formatting context from tokens");
return (row * mapRowLength) + column;
}
const maskBitSize = 5;
const mask = 0x1f;
const mask = 0b11111; // MaskBitSize bits
const mapRowLength = SyntaxKind.LastToken + 1;
enum RulesPosition {
IgnoreRulesSpecific = 0,
@ -65,92 +64,44 @@ namespace ts.formatting {
NoContextRulesAny = maskBitSize * 5
}
export class RulesBucketConstructionState {
private rulesInsertionIndexBitmap: number;
constructor() {
//// The Rules list contains all the inserted rules into a rulebucket in the following order:
//// 1- Ignore rules with specific token combination
//// 2- Ignore rules with any token combination
//// 3- Context rules with specific token combination
//// 4- Context rules with any token combination
//// 5- Non-context rules with specific token combination
//// 6- Non-context rules with any token combination
////
//// The member rulesInsertionIndexBitmap is used to describe the number of rules
//// in each sub-bucket (above) hence can be used to know the index of where to insert
//// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits.
////
//// Example:
//// 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.
this.rulesInsertionIndexBitmap = 0;
}
public GetInsertionIndex(maskPosition: RulesPosition): number {
let index = 0;
let pos = 0;
let indexBitmap = this.rulesInsertionIndexBitmap;
while (pos <= maskPosition) {
index += (indexBitmap & mask);
indexBitmap >>= maskBitSize;
pos += maskBitSize;
}
return index;
}
public IncreaseInsertionIndex(maskPosition: RulesPosition): void {
let value = (this.rulesInsertionIndexBitmap >> maskPosition) & mask;
value++;
Debug.assert((value & mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
let temp = this.rulesInsertionIndexBitmap & ~(mask << maskPosition);
temp |= value << maskPosition;
this.rulesInsertionIndexBitmap = temp;
}
// The Rules list contains all the inserted rules into a rulebucket in the following order:
// 1- Ignore rules with specific token combination
// 2- Ignore rules with any token combination
// 3- Context rules with specific token combination
// 4- Context rules with any token combination
// 5- Non-context rules with specific token combination
// 6- Non-context rules with any token combination
//
// The member rulesInsertionIndexBitmap is used to describe the number of rules
// in each sub-bucket (above) hence can be used to know the index of where to insert
// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits.
//
// Example:
// 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
: rule.context !== anyContext
? specificTokens ? RulesPosition.ContextRulesSpecific : RulesPosition.ContextRulesAny
: specificTokens ? RulesPosition.NoContextRulesSpecific : RulesPosition.NoContextRulesAny;
const state = constructionState[rulesBucketIndex] || 0;
rules.splice(getInsertionIndex(state, position), 0, rule);
constructionState[rulesBucketIndex] = increaseInsertionIndex(state, position);
}
export class RulesBucket {
private rules: Rule[];
constructor() {
this.rules = [];
function getInsertionIndex(indexBitmap: number, maskPosition: RulesPosition) {
let index = 0;
for (let pos = 0; pos <= maskPosition; pos += maskBitSize) {
index += indexBitmap & mask;
indexBitmap >>= maskBitSize;
}
return index;
}
public Rules(): Rule[] {
return this.rules;
}
public AddRule(rule: Rule, specificTokens: boolean, constructionState: RulesBucketConstructionState[], rulesBucketIndex: number): void {
let position: RulesPosition;
if (rule.operation.action === RuleAction.Ignore) {
position = specificTokens ?
RulesPosition.IgnoreRulesSpecific :
RulesPosition.IgnoreRulesAny;
}
else if (!rule.operation.context.IsAny()) {
position = specificTokens ?
RulesPosition.ContextRulesSpecific :
RulesPosition.ContextRulesAny;
}
else {
position = specificTokens ?
RulesPosition.NoContextRulesSpecific :
RulesPosition.NoContextRulesAny;
}
let state = constructionState[rulesBucketIndex];
if (state === undefined) {
state = constructionState[rulesBucketIndex] = new RulesBucketConstructionState();
}
const index = state.GetInsertionIndex(position);
this.rules.splice(index, 0, rule);
state.IncreaseInsertionIndex(position);
}
function increaseInsertionIndex(indexBitmap: number, maskPosition: RulesPosition): number {
const value = ((indexBitmap >> maskPosition) & mask) + 1;
Debug.assert((value & mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
return (indexBitmap & ~(mask << maskPosition)) | (value << maskPosition);
}
}

View file

@ -1,30 +0,0 @@
/// <reference path="references.ts"/>
/* @internal */
namespace ts.formatting {
export class RulesProvider {
private globalRules: Rules;
private options: ts.FormatCodeSettings;
private rulesMap: RulesMap;
constructor() {
this.globalRules = new Rules();
const activeRules = this.globalRules.HighPriorityCommonRules.concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
this.rulesMap = new RulesMap(activeRules);
}
public getRulesMap() {
return this.rulesMap;
}
public getFormatOptions(): Readonly<ts.FormatCodeSettings> {
return this.options;
}
public ensureUpToDate(options: ts.FormatCodeSettings) {
if (!this.options || !ts.compareDataObjects(this.options, options)) {
this.options = ts.clone(options);
}
}
}
}

View file

@ -1,5 +1,3 @@
///<reference path='..\services.ts' />
/* @internal */
namespace ts.formatting {
export namespace SmartIndenter {

View file

@ -1,124 +0,0 @@
///<reference path='references.ts' />
/* @internal */
namespace ts.formatting {
export namespace Shared {
const allTokens: SyntaxKind[] = [];
for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
allTokens.push(token);
}
class TokenValuesAccess implements TokenRange {
constructor(private readonly tokens: SyntaxKind[] = []) { }
public GetTokens(): SyntaxKind[] {
return this.tokens;
}
public Contains(token: SyntaxKind): boolean {
return this.tokens.indexOf(token) >= 0;
}
public isSpecific() { return true; }
}
class TokenSingleValueAccess implements TokenRange {
constructor(private readonly token: SyntaxKind) {}
public GetTokens(): SyntaxKind[] {
return [this.token];
}
public Contains(tokenValue: SyntaxKind): boolean {
return tokenValue === this.token;
}
public isSpecific() { return true; }
}
class TokenAllAccess implements TokenRange {
public GetTokens(): SyntaxKind[] {
return allTokens;
}
public Contains(): boolean {
return true;
}
public toString(): string {
return "[allTokens]";
}
public isSpecific() { return false; }
}
class TokenAllExceptAccess implements TokenRange {
constructor(private readonly except: SyntaxKind) {}
public GetTokens(): SyntaxKind[] {
return allTokens.filter(t => t !== this.except);
}
public Contains(token: SyntaxKind): boolean {
return token !== this.except;
}
public isSpecific() { return false; }
}
export interface TokenRange {
GetTokens(): SyntaxKind[];
Contains(token: SyntaxKind): boolean;
isSpecific(): boolean;
}
export namespace TokenRange {
export function FromToken(token: SyntaxKind): TokenRange {
return new TokenSingleValueAccess(token);
}
export function FromTokens(tokens: SyntaxKind[]): TokenRange {
return new TokenValuesAccess(tokens);
}
export function FromRange(from: SyntaxKind, to: SyntaxKind, except: SyntaxKind[] = []): TokenRange {
const tokens: SyntaxKind[] = [];
for (let token = from; token <= to; token++) {
if (ts.indexOf(except, token) < 0) {
tokens.push(token);
}
}
return new TokenValuesAccess(tokens);
}
export function AnyExcept(token: SyntaxKind): TokenRange {
return new TokenAllExceptAccess(token);
}
// tslint:disable variable-name (TODO)
export const Any: TokenRange = new TokenAllAccess();
export const AnyIncludingMultilineComments = TokenRange.FromTokens([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
export const Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
export const BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
export const BinaryKeywordOperators = TokenRange.FromTokens([
SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]);
export const UnaryPrefixOperators = TokenRange.FromTokens([
SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken]);
export const UnaryPrefixExpressions = TokenRange.FromTokens([
SyntaxKind.NumericLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken,
SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPreincrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPostincrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
export const UnaryPredecrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword]);
export const UnaryPostdecrementExpressions = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword]);
export const Comments = TokenRange.FromTokens([SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia]);
export const TypeNames = TokenRange.FromTokens([
SyntaxKind.Identifier, SyntaxKind.NumberKeyword, SyntaxKind.StringKeyword, SyntaxKind.BooleanKeyword,
SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, SyntaxKind.AnyKeyword]);
}
}
}

View file

@ -50,7 +50,7 @@ namespace ts.refactor.convertFunctionToES6Class {
const { file: sourceFile } = context;
const ctorSymbol = getConstructorSymbol(context);
const newLine = context.rulesProvider.getFormatOptions().newLineCharacter;
const newLine = context.formatContext.options.newLineCharacter;
const deletedNodes: Node[] = [];
const deletes: (() => any)[] = [];

View file

@ -33,9 +33,6 @@ namespace ts {
/** The version of the language service API */
export const servicesVersion = "0.7";
/* @internal */
let ruleProvider: formatting.RulesProvider;
function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject<TKind> | IdentifierObject {
const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) :
kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
@ -1157,7 +1154,6 @@ namespace ts {
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
ruleProvider = ruleProvider || new formatting.RulesProvider();
let program: Program;
let lastProjectVersion: string;
let lastTypesRootVersion = 0;
@ -1187,11 +1183,6 @@ namespace ts {
return sourceFile;
}
function getRuleProvider(options: FormatCodeSettings) {
ruleProvider.ensureUpToDate(options);
return ruleProvider;
}
function synchronizeHostData(): void {
// perform fast check if host supports it
if (host.getProjectVersion) {
@ -1429,7 +1420,6 @@ namespace ts {
function getCompletionEntryDetails(fileName: string, position: number, name: string, formattingOptions?: FormatCodeSettings, source?: string): CompletionEntryDetails {
synchronizeHostData();
const ruleProvider = formattingOptions ? getRuleProvider(formattingOptions) : undefined;
return Completions.getCompletionEntryDetails(
program.getTypeChecker(),
log,
@ -1439,7 +1429,7 @@ namespace ts {
{ name, source },
program.getSourceFiles(),
host,
ruleProvider);
formattingOptions && formatting.getFormatContext(formattingOptions));
}
function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol {
@ -1838,32 +1828,27 @@ namespace ts {
function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const settings = toEditorSettings(options);
return formatting.formatSelection(start, end, sourceFile, getRuleProvider(settings), settings);
return formatting.formatSelection(start, end, sourceFile, formatting.getFormatContext(toEditorSettings(options)));
}
function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const settings = toEditorSettings(options);
return formatting.formatDocument(sourceFile, getRuleProvider(settings), settings);
return formatting.formatDocument(syntaxTreeCache.getCurrentSourceFile(fileName), formatting.getFormatContext(toEditorSettings(options)));
}
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const settings = toEditorSettings(options);
const formatContext = formatting.getFormatContext(toEditorSettings(options));
if (!isInComment(sourceFile, position)) {
if (key === "{") {
return formatting.formatOnOpeningCurly(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === "}") {
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === ";") {
return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === "\n") {
return formatting.formatOnEnter(position, sourceFile, getRuleProvider(settings), settings);
switch (key) {
case "{":
return formatting.formatOnOpeningCurly(position, sourceFile, formatContext);
case "}":
return formatting.formatOnClosingCurly(position, sourceFile, formatContext);
case ";":
return formatting.formatOnSemicolon(position, sourceFile, formatContext);
case "\n":
return formatting.formatOnEnter(position, sourceFile, formatContext);
}
}
@ -1875,11 +1860,11 @@ namespace ts {
const sourceFile = getValidSourceFile(fileName);
const span = createTextSpanFromBounds(start, end);
const newLineCharacter = getNewLineOrDefaultFromHost(host);
const rulesProvider = getRuleProvider(formatOptions);
const formatContext = formatting.getFormatContext(formatOptions);
return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => {
cancellationToken.throwIfCancellationRequested();
return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider });
return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, formatContext });
});
}
@ -2104,7 +2089,7 @@ namespace ts {
program: getProgram(),
newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(),
host,
rulesProvider: getRuleProvider(formatOptions),
formatContext: formatting.getFormatContext(formatOptions),
cancellationToken,
};
}

View file

@ -188,7 +188,7 @@ namespace ts.textChanges {
export interface TextChangesContext {
newLineCharacter: string;
rulesProvider: formatting.RulesProvider;
formatContext: ts.formatting.FormatContext;
}
export class ChangeTracker {
@ -196,7 +196,7 @@ namespace ts.textChanges {
private readonly newLineCharacter: string;
public static fromContext(context: TextChangesContext): ChangeTracker {
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.rulesProvider);
return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext);
}
public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] {
@ -207,7 +207,7 @@ namespace ts.textChanges {
constructor(
private readonly newLine: NewLineKind,
private readonly rulesProvider: formatting.RulesProvider,
private readonly formatContext: ts.formatting.FormatContext,
private readonly validator?: (text: NonFormattedText) => void) {
this.newLineCharacter = getNewLineCharacter({ newLine });
}
@ -475,7 +475,7 @@ namespace ts.textChanges {
options: {}
});
// use the same indentation as 'after' item
const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.rulesProvider.getFormatOptions());
const indentation = formatting.SmartIndenter.findFirstNonWhitespaceColumn(afterStartLinePosition, afterStart, sourceFile, this.formatContext.options);
// insert element before the line break on the line that contains 'after' element
let insertPos = skipTrivia(sourceFile.text, end, /*stopAfterLineBreak*/ true, /*stopAtComments*/ false);
if (insertPos !== end && isLineBreak(sourceFile.text.charCodeAt(insertPos - 1))) {
@ -562,7 +562,7 @@ namespace ts.textChanges {
this.validator(nonformattedText);
}
const formatOptions = this.rulesProvider.getFormatOptions();
const { options: formatOptions } = this.formatContext;
const posStartsLine = getLineStartPositionForPosition(pos, sourceFile) === pos;
const initialIndentation =
@ -578,7 +578,7 @@ namespace ts.textChanges {
? (formatOptions.indentSize || 0)
: 0;
return applyFormatting(nonformattedText, sourceFile, initialIndentation, delta, this.rulesProvider);
return applyFormatting(nonformattedText, sourceFile, initialIndentation, delta, this.formatContext);
}
private static normalize(changes: Change[]): Change[] {
@ -605,14 +605,14 @@ namespace ts.textChanges {
return { text: writer.getText(), node: assignPositionsToNode(node) };
}
function applyFormatting(nonFormattedText: NonFormattedText, sourceFile: SourceFile, initialIndentation: number, delta: number, rulesProvider: formatting.RulesProvider) {
function applyFormatting(nonFormattedText: NonFormattedText, sourceFile: SourceFile, initialIndentation: number, delta: number, formatContext: ts.formatting.FormatContext) {
const lineMap = computeLineStarts(nonFormattedText.text);
const file: SourceFileLike = {
text: nonFormattedText.text,
lineMap,
getLineAndCharacterOfPosition: pos => computeLineAndCharacterOfPosition(lineMap, pos)
};
const changes = formatting.formatNodeGivenIndentation(nonFormattedText.node, file, sourceFile.languageVariant, initialIndentation, delta, rulesProvider);
const changes = formatting.formatNodeGivenIndentation(nonFormattedText.node, file, sourceFile.languageVariant, initialIndentation, delta, formatContext);
return applyChanges(nonFormattedText.text, changes);
}

View file

@ -1068,20 +1068,19 @@ namespace ts {
return createTextSpanFromBounds(range.pos, range.end);
}
export const typeKeywords: ReadonlyArray<SyntaxKind> = [
SyntaxKind.AnyKeyword,
SyntaxKind.BooleanKeyword,
SyntaxKind.NeverKeyword,
SyntaxKind.NumberKeyword,
SyntaxKind.ObjectKeyword,
SyntaxKind.StringKeyword,
SyntaxKind.SymbolKeyword,
SyntaxKind.VoidKeyword,
];
export function isTypeKeyword(kind: SyntaxKind): boolean {
switch (kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.VoidKeyword:
return true;
default:
return false;
}
return contains(typeKeywords, kind);
}
/** True if the symbol is for an external module, as opposed to a namespace. */