Skip to content

Commit f47650c

Browse files
committed
Merge pull request #4631 from Microsoft/formattingFixesFromMaster
Port formatting related issues from master
2 parents fafbec6 + ff640e4 commit f47650c

File tree

10 files changed

+115
-36
lines changed

10 files changed

+115
-36
lines changed

src/services/formatting/rules.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,13 @@ namespace ts.formatting {
227227
public SpaceBetweenTagAndTemplateString: Rule;
228228
public NoSpaceBetweenTagAndTemplateString: Rule;
229229

230-
// Union type
230+
// Type operation
231231
public SpaceBeforeBar: Rule;
232232
public NoSpaceBeforeBar: Rule;
233233
public SpaceAfterBar: Rule;
234234
public NoSpaceAfterBar: Rule;
235+
public SpaceBeforeAmpersand: Rule;
236+
public SpaceAfterAmpersand: Rule;
235237

236238
constructor() {
237239
///
@@ -272,7 +274,7 @@ namespace ts.formatting {
272274
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);
273275

274276
// Place a space before open brace in a TypeScript declaration that has braces as children (class, module, enum, etc)
275-
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia]);
277+
this.TypeScriptOpenBraceLeftTokenRange = Shared.TokenRange.FromTokens([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia, SyntaxKind.ClassKeyword]);
276278
this.SpaceBeforeOpenBraceInTypeScriptDeclWithBlock = new Rule(RuleDescriptor.create2(this.TypeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsTypeScriptDeclWithBlockContext, Rules.IsNotFormatOnEnter, Rules.IsSameLineTokenOrBeforeMultilineBlockContext), RuleAction.Space), RuleFlags.CanDeleteNewLines);
277279

278280
// Place a space before open brace in a control flow construct
@@ -394,12 +396,13 @@ namespace ts.formatting {
394396
this.SpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
395397
this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
396398

397-
// union type
399+
// type operation
398400
this.SpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
399401
this.NoSpaceBeforeBar = new Rule(RuleDescriptor.create3(SyntaxKind.BarToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
400402
this.SpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
401403
this.NoSpaceAfterBar = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.BarToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete));
402-
404+
this.SpaceBeforeAmpersand = new Rule(RuleDescriptor.create3(SyntaxKind.AmpersandToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
405+
this.SpaceAfterAmpersand = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.AmpersandToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space));
403406

404407
// These rules are higher in priority than user-configurable rules.
405408
this.HighPriorityCommonRules =
@@ -432,6 +435,7 @@ namespace ts.formatting {
432435
this.SpaceAfterTypeKeyword, this.NoSpaceAfterTypeKeyword,
433436
this.SpaceBetweenTagAndTemplateString, this.NoSpaceBetweenTagAndTemplateString,
434437
this.SpaceBeforeBar, this.NoSpaceBeforeBar, this.SpaceAfterBar, this.NoSpaceAfterBar,
438+
this.SpaceBeforeAmpersand, this.SpaceAfterAmpersand,
435439

436440
// TypeScript-specific rules
437441
this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport,
@@ -663,6 +667,7 @@ namespace ts.formatting {
663667
static NodeIsTypeScriptDeclWithBlockContext(node: Node): boolean {
664668
switch (node.kind) {
665669
case SyntaxKind.ClassDeclaration:
670+
case SyntaxKind.ClassExpression:
666671
case SyntaxKind.InterfaceDeclaration:
667672
case SyntaxKind.EnumDeclaration:
668673
case SyntaxKind.TypeLiteral:

src/services/formatting/smartIndenter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ namespace ts.formatting {
406406
function nodeContentIsAlwaysIndented(kind: SyntaxKind): boolean {
407407
switch (kind) {
408408
case SyntaxKind.ClassDeclaration:
409+
case SyntaxKind.ClassExpression:
409410
case SyntaxKind.InterfaceDeclaration:
410411
case SyntaxKind.EnumDeclaration:
411412
case SyntaxKind.TypeAliasDeclaration:
@@ -436,7 +437,6 @@ namespace ts.formatting {
436437
case SyntaxKind.Parameter:
437438
case SyntaxKind.FunctionType:
438439
case SyntaxKind.ConstructorType:
439-
case SyntaxKind.UnionType:
440440
case SyntaxKind.ParenthesizedType:
441441
case SyntaxKind.TaggedTemplateExpression:
442442
case SyntaxKind.AwaitExpression:

src/services/services.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,7 +1866,7 @@ namespace ts {
18661866
let sourceMapText: string;
18671867
// Create a compilerHost object to allow the compiler to read and write files
18681868
let compilerHost: CompilerHost = {
1869-
getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined,
1869+
getSourceFile: (fileName, target) => fileName === normalizeSlashes(inputFileName) ? sourceFile : undefined,
18701870
writeFile: (name, text, writeByteOrderMark) => {
18711871
if (fileExtensionIs(name, ".map")) {
18721872
Debug.assert(sourceMapText === undefined, `Unexpected multiple source map outputs for the file '${name}'`);

src/services/utilities.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ namespace ts {
360360
return find(startNode || sourceFile);
361361

362362
function findRightmostToken(n: Node): Node {
363-
if (isToken(n)) {
363+
if (isToken(n) || n.kind === SyntaxKind.JsxText) {
364364
return n;
365365
}
366366

@@ -371,24 +371,35 @@ namespace ts {
371371
}
372372

373373
function find(n: Node): Node {
374-
if (isToken(n)) {
374+
if (isToken(n) || n.kind === SyntaxKind.JsxText) {
375375
return n;
376376
}
377377

378-
let children = n.getChildren();
378+
const children = n.getChildren();
379379
for (let i = 0, len = children.length; i < len; i++) {
380380
let child = children[i];
381-
if (nodeHasTokens(child)) {
382-
if (position <= child.end) {
383-
if (child.getStart(sourceFile) >= position) {
384-
// actual start of the node is past the position - previous token should be at the end of previous child
385-
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
386-
return candidate && findRightmostToken(candidate)
387-
}
388-
else {
389-
// candidate should be in this node
390-
return find(child);
391-
}
381+
// condition 'position < child.end' checks if child node end after the position
382+
// in the example below this condition will be false for 'aaaa' and 'bbbb' and true for 'ccc'
383+
// aaaa___bbbb___$__ccc
384+
// after we found child node with end after the position we check if start of the node is after the position.
385+
// if yes - then position is in the trivia and we need to look into the previous child to find the token in question.
386+
// if no - position is in the node itself so we should recurse in it.
387+
// NOTE: JsxText is a weird kind of node that can contain only whitespaces (since they are not counted as trivia).
388+
// if this is the case - then we should assume that token in question is located in previous child.
389+
if (position < child.end && (nodeHasTokens(child) || child.kind === SyntaxKind.JsxText)) {
390+
const start = child.getStart(sourceFile);
391+
const lookInPreviousChild =
392+
(start >= position) || // cursor in the leading trivia
393+
(child.kind === SyntaxKind.JsxText && start === child.end); // whitespace only JsxText
394+
395+
if (lookInPreviousChild) {
396+
// actual start of the node is past the position - previous token should be at the end of previous child
397+
let candidate = findRightmostChildNodeWithTokens(children, /*exclusiveStartPosition*/ i);
398+
return candidate && findRightmostToken(candidate)
399+
}
400+
else {
401+
// candidate should be in this node
402+
return find(child);
392403
}
393404
}
394405
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
////class Thing extends (
4+
//// class/*classOpenBrace*/
5+
//// {
6+
/////*classIndent*/
7+
//// protected doThing() {/*methodAutoformat*/
8+
/////*methodIndent*/
9+
//// }
10+
//// }
11+
////) {
12+
////}
13+
14+
format.document();
15+
16+
goTo.marker("classOpenBrace");
17+
verify.currentLineContentIs(" class {");
18+
goTo.marker("classIndent");
19+
verify.indentationIs(8);
20+
goTo.marker("methodAutoformat");
21+
verify.currentLineContentIs(" protected doThing() {");
22+
goTo.marker("methodIndent");
23+
verify.indentationIs(12);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
////type Union = number | {}/*formatBarOperator*/
4+
/////*indent*/
5+
////|string/*autoformat*/
6+
////type Intersection = Foo & Bar;/*formatAmpersandOperator*/
7+
////type Complexed =
8+
//// Foo&
9+
//// Bar|/*unionTypeNoIndent*/
10+
//// Baz;/*intersectionTypeNoIndent*/
11+
12+
format.document();
13+
14+
goTo.marker("formatBarOperator");
15+
verify.currentLineContentIs("type Union = number | {}");
16+
goTo.marker("indent");
17+
verify.indentationIs(4);
18+
goTo.marker("autoformat");
19+
verify.currentLineContentIs(" | string");
20+
goTo.marker("formatAmpersandOperator");
21+
verify.currentLineContentIs("type Intersection = Foo & Bar;");
22+
23+
goTo.marker("unionTypeNoIndent");
24+
verify.currentLineContentIs(" Bar |");
25+
goTo.marker("intersectionTypeNoIndent");
26+
verify.currentLineContentIs(" Baz;");

tests/cases/fourslash/formatTypeUnion.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//@Filename: file.tsx
4+
////(function () {
5+
//// return (
6+
//// <div>
7+
//// <div>
8+
//// </div>
9+
//// /*indent2*/
10+
//// </div>
11+
//// )
12+
////})
13+
14+
15+
format.document();
16+
goTo.marker("indent2");
17+
verify.indentationIs(12);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
//@Filename: file.tsx
4+
////<div>/*1*/
5+
goTo.marker("1");
6+
edit.insert("\n");
7+
verify.indentationIs(0);

tests/cases/unittests/transpile.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ module ts {
6464
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
6565
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
6666

67-
let expectedSourceMapFileName = removeFileExtension(transpileOptions.fileName) + ".js.map";
68-
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
67+
let expectedSourceMapFileName = removeFileExtension(getBaseFileName(normalizeSlashes(transpileOptions.fileName))) + ".js.map";
68+
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
6969

7070
if (testSettings.expectedOutput !== undefined) {
7171
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
@@ -270,5 +270,9 @@ var x = 0;`,
270270
expectedOutput: output
271271
});
272272
});
273+
274+
it("Supports backslashes in file name", () => {
275+
test("var x", { expectedOutput: "var x;\r\n", options: { fileName: "a\\b.ts" }});
276+
});
273277
});
274278
}

0 commit comments

Comments
 (0)