Merge pull request #4631 from Microsoft/formattingFixesFromMaster
Port formatting related issues from master
This commit is contained in:
commit
f47650c3b3
|
@ -227,11 +227,13 @@ namespace ts.formatting {
|
|||
public SpaceBetweenTagAndTemplateString: Rule;
|
||||
public NoSpaceBetweenTagAndTemplateString: Rule;
|
||||
|
||||
// Union type
|
||||
// Type operation
|
||||
public SpaceBeforeBar: Rule;
|
||||
public NoSpaceBeforeBar: Rule;
|
||||
public SpaceAfterBar: Rule;
|
||||
public NoSpaceAfterBar: Rule;
|
||||
public SpaceBeforeAmpersand: Rule;
|
||||
public SpaceAfterAmpersand: Rule;
|
||||
|
||||
constructor() {
|
||||
///
|
||||
|
@ -272,7 +274,7 @@ namespace ts.formatting {
|
|||
this.SpaceBeforeOpenBraceInFunction = new Rule(RuleDescriptor.create2(this.FunctionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext, Rules.IsBeforeBlockContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Place a space before open brace in a TypeScript declaration that has braces as children (class, module, enum, etc)
|
||||
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia]);
|
||||
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia, SyntaxKind.ClassKeyword]);
|
||||
this.SpaceBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
|
||||
|
||||
// Place a space before open brace in a control flow construct
|
||||
|
@ -394,12 +396,13 @@ namespace ts.formatting {
|
|||
this.SpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
// union type
|
||||
// type operation
|
||||
this.SpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
this.SpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.NoSpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
|
||||
|
||||
this.SpaceBeforeAmpersand = new Rule(RuleDescriptor.create3(SyntaxKind.AmpersandToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
this.SpaceAfterAmpersand = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.AmpersandToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
|
||||
|
||||
// These rules are higher in priority than user-configurable rules.
|
||||
this.HighPriorityCommonRules =
|
||||
|
@ -432,6 +435,7 @@ namespace ts.formatting {
|
|||
this.SpaceAfterTypeKeyword, this.NoSpaceAfterTypeKeyword,
|
||||
this.SpaceBetweenTagAndTemplateString, this.NoSpaceBetweenTagAndTemplateString,
|
||||
this.SpaceBeforeBar, this.NoSpaceBeforeBar, this.SpaceAfterBar, this.NoSpaceAfterBar,
|
||||
this.SpaceBeforeAmpersand, this.SpaceAfterAmpersand,
|
||||
|
||||
// TypeScript-specific rules
|
||||
this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport,
|
||||
|
@ -663,6 +667,7 @@ namespace ts.formatting {
|
|||
static NodeIsTypeScriptDeclWithBlockContext(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
|
|
|
@ -406,6 +406,7 @@ namespace ts.formatting {
|
|||
function nodeContentIsAlwaysIndented(kind: SyntaxKind): boolean {
|
||||
switch (kind) {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.TypeAliasDeclaration:
|
||||
|
@ -436,7 +437,6 @@ namespace ts.formatting {
|
|||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.UnionType:
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.AwaitExpression:
|
||||
|
|
|
@ -1866,7 +1866,7 @@ namespace ts {
|
|||
let sourceMapText: string;
|
||||
// Create a compilerHost object to allow the compiler to read and write files
|
||||
let compilerHost: CompilerHost = {
|
||||
getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined,
|
||||
getSourceFile: (fileName, target) => fileName === normalizeSlashes(inputFileName) ? sourceFile : undefined,
|
||||
writeFile: (name, text, writeByteOrderMark) => {
|
||||
if (fileExtensionIs(name, ".map")) {
|
||||
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);
|
||||
|
|
|
@ -360,7 +360,7 @@ namespace ts {
|
|||
return find(startNode || sourceFile);
|
||||
|
||||
function findRightmostToken(n: Node): Node {
|
||||
if (isToken(n)) {
|
||||
if (isToken(n) || n.kind === SyntaxKind.JsxText) {
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -371,24 +371,35 @@ namespace ts {
|
|||
}
|
||||
|
||||
function find(n: Node): Node {
|
||||
if (isToken(n)) {
|
||||
if (isToken(n) || n.kind === SyntaxKind.JsxText) {
|
||||
return n;
|
||||
}
|
||||
|
||||
let children = n.getChildren();
|
||||
const children = n.getChildren();
|
||||
for (let i = 0, len = children.length; i < len; i++) {
|
||||
let child = children[i];
|
||||
if (nodeHasTokens(child)) {
|
||||
if (position <= child.end) {
|
||||
if (child.getStart(sourceFile) >= position) {
|
||||
// actual start of the node is past the position - previous token should be at the end of previous child
|
||||
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
|
||||
return candidate && findRightmostToken(candidate)
|
||||
}
|
||||
else {
|
||||
// candidate should be in this node
|
||||
return find(child);
|
||||
}
|
||||
// condition 'position < child.end' checks if child node end after the position
|
||||
// in the example below this condition will be false for 'aaaa' and 'bbbb' and true for 'ccc'
|
||||
// aaaa___bbbb___$__ccc
|
||||
// after we found child node with end after the position we check if start of the node is after the position.
|
||||
// if yes - then position is in the trivia and we need to look into the previous child to find the token in question.
|
||||
// if no - position is in the node itself so we should recurse in it.
|
||||
// NOTE: JsxText is a weird kind of node that can contain only whitespaces (since they are not counted as trivia).
|
||||
// if this is the case - then we should assume that token in question is located in previous child.
|
||||
if (position < child.end && (nodeHasTokens(child) || child.kind === SyntaxKind.JsxText)) {
|
||||
const start = child.getStart(sourceFile);
|
||||
const lookInPreviousChild =
|
||||
(start >= position) || // cursor in the leading trivia
|
||||
(child.kind === SyntaxKind.JsxText && start === child.end); // whitespace only JsxText
|
||||
|
||||
if (lookInPreviousChild) {
|
||||
// actual start of the node is past the position - previous token should be at the end of previous child
|
||||
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
|
||||
return candidate && findRightmostToken(candidate)
|
||||
}
|
||||
else {
|
||||
// candidate should be in this node
|
||||
return find(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
23
tests/cases/fourslash/formatClassExpression.ts
Normal file
23
tests/cases/fourslash/formatClassExpression.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
////class Thing extends (
|
||||
//// class/*classOpenBrace*/
|
||||
//// {
|
||||
/////*classIndent*/
|
||||
//// protected doThing() {/*methodAutoformat*/
|
||||
/////*methodIndent*/
|
||||
//// }
|
||||
//// }
|
||||
////) {
|
||||
////}
|
||||
|
||||
format.document();
|
||||
|
||||
goTo.marker("classOpenBrace");
|
||||
verify.currentLineContentIs(" class {");
|
||||
goTo.marker("classIndent");
|
||||
verify.indentationIs(8);
|
||||
goTo.marker("methodAutoformat");
|
||||
verify.currentLineContentIs(" protected doThing() {");
|
||||
goTo.marker("methodIndent");
|
||||
verify.indentationIs(12);
|
26
tests/cases/fourslash/formatTypeOperation.ts
Normal file
26
tests/cases/fourslash/formatTypeOperation.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
////type Union = number | {}/*formatBarOperator*/
|
||||
/////*indent*/
|
||||
////|string/*autoformat*/
|
||||
////type Intersection = Foo & Bar;/*formatAmpersandOperator*/
|
||||
////type Complexed =
|
||||
//// Foo&
|
||||
//// Bar|/*unionTypeNoIndent*/
|
||||
//// Baz;/*intersectionTypeNoIndent*/
|
||||
|
||||
format.document();
|
||||
|
||||
goTo.marker("formatBarOperator");
|
||||
verify.currentLineContentIs("type Union = number | {}");
|
||||
goTo.marker("indent");
|
||||
verify.indentationIs(4);
|
||||
goTo.marker("autoformat");
|
||||
verify.currentLineContentIs(" | string");
|
||||
goTo.marker("formatAmpersandOperator");
|
||||
verify.currentLineContentIs("type Intersection = Foo & Bar;");
|
||||
|
||||
goTo.marker("unionTypeNoIndent");
|
||||
verify.currentLineContentIs(" Bar |");
|
||||
goTo.marker("intersectionTypeNoIndent");
|
||||
verify.currentLineContentIs(" Baz;");
|
|
@ -1,14 +0,0 @@
|
|||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
////type Union = number | {}/*formatOperator*/
|
||||
/////*indent*/
|
||||
////|string/*autoformat*/
|
||||
|
||||
format.document();
|
||||
|
||||
goTo.marker("formatOperator");
|
||||
verify.currentLineContentIs("type Union = number | {}");
|
||||
goTo.marker("indent");
|
||||
verify.indentationIs(4);
|
||||
goTo.marker("autoformat");
|
||||
verify.currentLineContentIs(" | string");
|
17
tests/cases/fourslash/indentationInJsx1.ts
Normal file
17
tests/cases/fourslash/indentationInJsx1.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//@Filename: file.tsx
|
||||
////(function () {
|
||||
//// return (
|
||||
//// <div>
|
||||
//// <div>
|
||||
//// </div>
|
||||
//// /*indent2*/
|
||||
//// </div>
|
||||
//// )
|
||||
////})
|
||||
|
||||
|
||||
format.document();
|
||||
goTo.marker("indent2");
|
||||
verify.indentationIs(12);
|
7
tests/cases/fourslash/indentationInJsx2.ts
Normal file
7
tests/cases/fourslash/indentationInJsx2.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
/// <reference path="fourslash.ts"/>
|
||||
|
||||
//@Filename: file.tsx
|
||||
////<div>/*1*/
|
||||
goTo.marker("1");
|
||||
edit.insert("\n");
|
||||
verify.indentationIs(0);
|
|
@ -64,8 +64,8 @@ module ts {
|
|||
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
|
||||
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
|
||||
|
||||
let expectedSourceMapFileName = removeFileExtension(transpileOptions.fileName) + ".js.map";
|
||||
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
|
||||
let expectedSourceMapFileName = removeFileExtension(getBaseFileName(normalizeSlashes(transpileOptions.fileName))) + ".js.map";
|
||||
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
|
||||
|
||||
if (testSettings.expectedOutput !== undefined) {
|
||||
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
|
||||
|
@ -270,5 +270,9 @@ var x = 0;`,
|
|||
expectedOutput: output
|
||||
});
|
||||
});
|
||||
|
||||
it("Supports backslashes in file name", () => {
|
||||
test("var x", { expectedOutput: "var x;\r\n", options: { fileName: "a\\b.ts" }});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue