From 525aea3cf9e233a06425e1a95848cb43e9055a87 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Fri, 28 Aug 2015 23:00:58 +0900 Subject: [PATCH 01/17] add rules and indents to named export/import --- src/services/formatting/formatting.ts | 1 + src/services/formatting/rules.ts | 43 ++++++------------- src/services/formatting/smartIndenter.ts | 6 +++ .../fourslash/formatNamedExportImport.ts | 28 ++++++++++++ 4 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 tests/cases/fourslash/formatNamedExportImport.ts diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 66cdbd53750..f1388eb8995 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -484,6 +484,7 @@ namespace ts.formatting { case SyntaxKind.CloseParenToken: case SyntaxKind.ElseKeyword: case SyntaxKind.WhileKeyword: + case SyntaxKind.FromKeyword: case SyntaxKind.AtToken: return indentation; default: diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index f609edb0501..99090dd0a0e 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -213,25 +213,15 @@ namespace ts.formatting { public NoSpaceBetweenYieldKeywordAndStar: Rule; public SpaceBetweenYieldOrYieldStarAndOperand: Rule; - // Async-await + // Async functions public SpaceBetweenAsyncAndFunctionKeyword: Rule; - public NoSpaceBetweenAsyncAndFunctionKeyword: Rule; - public SpaceAfterAwaitKeyword: Rule; - public NoSpaceAfterAwaitKeyword: Rule; - - // Type alias declaration - public SpaceAfterTypeKeyword: Rule; - public NoSpaceAfterTypeKeyword: Rule; // Tagged template string public SpaceBetweenTagAndTemplateString: Rule; - public NoSpaceBetweenTagAndTemplateString: Rule; // Union type public SpaceBeforeBar: Rule; - public NoSpaceBeforeBar: Rule; public SpaceAfterBar: Rule; - public NoSpaceAfterBar: Rule; constructor() { /// @@ -313,7 +303,7 @@ namespace ts.formatting { this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space)); this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete)); this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); @@ -346,8 +336,13 @@ namespace ts.formatting { this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // Add a space around certain TypeScript keywords - this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([ + SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, + SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, + SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, + SyntaxKind.TypeKeyword, SyntaxKind.AsKeyword, SyntaxKind.FromKeyword + ]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.AsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { this.SpaceAfterModuleName = new Rule(RuleDescriptor.create1(SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsModuleDeclContext), RuleAction.Space)); @@ -382,23 +377,13 @@ namespace ts.formatting { // Async-await this.SpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - - // Type alias declaration - this.SpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.NoSpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // template string 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 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)); // These rules are higher in priority than user-configurable rules. @@ -427,11 +412,9 @@ namespace ts.formatting { this.NoSpaceBeforeOpenParenInFuncCall, this.SpaceBeforeBinaryKeywordOperator, this.SpaceAfterBinaryKeywordOperator, this.SpaceAfterVoidOperator, - this.SpaceBetweenAsyncAndFunctionKeyword, this.NoSpaceBetweenAsyncAndFunctionKeyword, - this.SpaceAfterAwaitKeyword, this.NoSpaceAfterAwaitKeyword, - this.SpaceAfterTypeKeyword, this.NoSpaceAfterTypeKeyword, - this.SpaceBetweenTagAndTemplateString, this.NoSpaceBetweenTagAndTemplateString, - this.SpaceBeforeBar, this.NoSpaceBeforeBar, this.SpaceAfterBar, this.NoSpaceAfterBar, + this.SpaceBetweenAsyncAndFunctionKeyword, + this.SpaceBetweenTagAndTemplateString, + this.SpaceBeforeBar, this.SpaceAfterBar, // TypeScript-specific rules this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport, @@ -623,6 +606,8 @@ namespace ts.formatting { case SyntaxKind.CaseBlock: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.ModuleBlock: + case SyntaxKind.NamedExports: + case SyntaxKind.NamedImports: return true; } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index ebbf09e9feb..3049e66fd00 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -440,6 +440,12 @@ namespace ts.formatting { case SyntaxKind.ParenthesizedType: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.AwaitExpression: + case SyntaxKind.NamedExports: + case SyntaxKind.ExportDeclaration: + case SyntaxKind.NamedImports: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportSpecifier: + //case SyntaxKind.ImportSpecifier: return true; } return false; diff --git a/tests/cases/fourslash/formatNamedExportImport.ts b/tests/cases/fourslash/formatNamedExportImport.ts new file mode 100644 index 00000000000..29e58160cc1 --- /dev/null +++ b/tests/cases/fourslash/formatNamedExportImport.ts @@ -0,0 +1,28 @@ +/// +////var x = `sadasdasdasdasfegsfd +/////*1*/rasdesgeryt35t35y35 e4 ergt er 35t 3535 `; +////var y = `1${2}/*2*/3`; +////let z= `foo`/*3*/ +////let w= `bar${3}`/*4*/ +////String.raw +//// `template`/*5*/ + + +goTo.marker("1"); +edit.insert("\r\n"); // edit will trigger formatting - should succeeed + +goTo.marker("2"); +edit.insert("\r\n"); +verify.indentationIs(0); +verify.currentLineContentIs("3`;") + +goTo.marker("3"); +edit.insert(";"); +verify.currentLineContentIs("let z = `foo`;"); +goTo.marker("4"); +edit.insert(";"); +verify.currentLineContentIs("let w = `bar${3}`;"); + +goTo.marker("5"); +edit.insert(";"); +verify.currentLineContentIs(" `template`;"); \ No newline at end of file From 9d867b70e3e09d54c073933ce54c0309d32e7ec9 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Sun, 30 Aug 2015 15:02:15 +0900 Subject: [PATCH 02/17] pseudo-block indentation --- src/services/formatting/formatting.ts | 14 ++++++++++++++ src/services/formatting/smartIndenter.ts | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index f1388eb8995..adaaed40d79 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -403,6 +403,20 @@ namespace ts.formatting { indentation = parentDynamicIndentation.getIndentation(); } } + else if (node.kind === SyntaxKind.NamedExports || node.kind === SyntaxKind.ImportClause) { + let blockParent = node.kind === SyntaxKind.ImportClause ? + (node).namedBindings : node; + + let children = (blockParent).getChildren(sourceFile); + // Detect named export/import pseudo-block + if (children[0] && children[0].kind === SyntaxKind.OpenBraceToken && + children[2] && children[2].kind === SyntaxKind.CloseBraceToken) { + indentation = parentDynamicIndentation.getIndentation(); + } + else { + indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(); + } + } else { if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { indentation = parentDynamicIndentation.getIndentation(); diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 3049e66fd00..57d527d943f 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -440,12 +440,11 @@ namespace ts.formatting { case SyntaxKind.ParenthesizedType: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.AwaitExpression: - case SyntaxKind.NamedExports: - case SyntaxKind.ExportDeclaration: - case SyntaxKind.NamedImports: case SyntaxKind.ImportDeclaration: + case SyntaxKind.NamedExports: + case SyntaxKind.NamedImports: case SyntaxKind.ExportSpecifier: - //case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportSpecifier: return true; } return false; @@ -470,6 +469,8 @@ namespace ts.formatting { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return child !== SyntaxKind.Block; + case SyntaxKind.ExportDeclaration: + return child !== SyntaxKind.NamedExports; default: return false; } From 66924a371842f13bcfd54f0313352313886b89dd Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 3 Sep 2015 20:01:39 +0900 Subject: [PATCH 03/17] format open brace in export/import decl --- src/services/formatting/rules.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 99090dd0a0e..62a91f3e3c8 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -262,7 +262,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.ExportKeyword, SyntaxKind.ImportKeyword]); 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 @@ -606,8 +606,6 @@ namespace ts.formatting { case SyntaxKind.CaseBlock: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.ModuleBlock: - case SyntaxKind.NamedExports: - case SyntaxKind.NamedImports: return true; } @@ -652,6 +650,10 @@ namespace ts.formatting { case SyntaxKind.EnumDeclaration: case SyntaxKind.TypeLiteral: case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ExportDeclaration: + case SyntaxKind.NamedExports: + case SyntaxKind.ImportDeclaration: + case SyntaxKind.NamedImports: return true; } From f0ed672eb1d2058605a2ca732cf6e790471081ef Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 3 Sep 2015 20:02:48 +0900 Subject: [PATCH 04/17] disable indentation when in brace shell This pattern can also be used to format JSX end tag. --- src/services/formatting/formatting.ts | 15 ++------------- src/services/formatting/smartIndenter.ts | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index adaaed40d79..3773412bace 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -403,19 +403,8 @@ namespace ts.formatting { indentation = parentDynamicIndentation.getIndentation(); } } - else if (node.kind === SyntaxKind.NamedExports || node.kind === SyntaxKind.ImportClause) { - let blockParent = node.kind === SyntaxKind.ImportClause ? - (node).namedBindings : node; - - let children = (blockParent).getChildren(sourceFile); - // Detect named export/import pseudo-block - if (children[0] && children[0].kind === SyntaxKind.OpenBraceToken && - children[2] && children[2].kind === SyntaxKind.CloseBraceToken) { - indentation = parentDynamicIndentation.getIndentation(); - } - else { - indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(); - } + else if (SmartIndenter.isIndentationPrevented(node)) { + indentation = parentDynamicIndentation.getIndentation(); } else { if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 57d527d943f..ebcbfbb1394 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -133,7 +133,9 @@ namespace ts.formatting { } // increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line - if (shouldIndentChildNode(parent.kind, current.kind) && !parentAndChildShareLine) { + if (shouldIndentChildNode(parent.kind, current.kind) && + !isIndentationPrevented(current) && !parentAndChildShareLine) { + indentationDelta += options.IndentSize; } @@ -475,5 +477,23 @@ namespace ts.formatting { return false; } } + + function hasBraceShell(node: Node) { + let children = node.getChildren(); + let last = children.length - 1; + // Check if node looks like a block + return children[0] && children[0].kind === SyntaxKind.OpenBraceToken && + children[last] && children[last].kind === SyntaxKind.CloseBraceToken; + } + + export function isIndentationPrevented(node: TextRangeWithKind) { + switch (node.kind) { + case SyntaxKind.NamedExports: + return hasBraceShell(node); + case SyntaxKind.ImportClause: + return hasBraceShell((node).namedBindings); + } + return false; + } } } \ No newline at end of file From 80089f7e3aad1e3a107b08f32aa602d7a29fd0ec Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 3 Sep 2015 20:03:05 +0900 Subject: [PATCH 05/17] add tests --- .../fourslash/formatNamedExportImport.ts | 87 ++++++++++++++----- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/tests/cases/fourslash/formatNamedExportImport.ts b/tests/cases/fourslash/formatNamedExportImport.ts index 29e58160cc1..2e22c54ae3b 100644 --- a/tests/cases/fourslash/formatNamedExportImport.ts +++ b/tests/cases/fourslash/formatNamedExportImport.ts @@ -1,28 +1,71 @@ /// -////var x = `sadasdasdasdasfegsfd -/////*1*/rasdesgeryt35t35y35 e4 ergt er 35t 3535 `; -////var y = `1${2}/*2*/3`; -////let z= `foo`/*3*/ -////let w= `bar${3}`/*4*/ -////String.raw -//// `template`/*5*/ +/////*selectionStart*/ +////export { x, y as yy, z } from "foo"/*export1*/ +////export{x, y as yy, z}from"bar"/*export2*/ +//// +////export +/////*exportOpenBrace*/{x,/*exportSpecifier1*/ +////y as yy, z/*exportSpecifier2*/ }/*exportCloseBrace*/ +//// from/*fromKeywordAutoformat*/ +/////*fromKeywordIndent*/ +////"foo"/*exportDir*/ +//// +////import {x, y as yy, z}from "baz"/*import1*/ +//// +////import/*importOpenBrace*/{x,/*importSpecifier1*/ +////y +////as yy,/*importSpecifier2*/ +////z}/*importCloseBrace*/ +////from "wow"/*importDir*/ +/////*selectionEnd*/ +//// +////export/*formatOnEnter*/{/*formatOnEnterOpenBrace*/ +/////*differentLineIndent*/x/*differentLineAutoformat*/ +////} from "abc" -goTo.marker("1"); -edit.insert("\r\n"); // edit will trigger formatting - should succeeed +format.selection("selectionStart", "selectionEnd"); -goTo.marker("2"); -edit.insert("\r\n"); -verify.indentationIs(0); -verify.currentLineContentIs("3`;") +goTo.marker("export1"); +verify.currentLineContentIs('export { x, y as yy, z } from "foo"'); +goTo.marker("export2"); +verify.currentLineContentIs('export { x, y as yy, z } from "bar"'); -goTo.marker("3"); -edit.insert(";"); -verify.currentLineContentIs("let z = `foo`;"); -goTo.marker("4"); -edit.insert(";"); -verify.currentLineContentIs("let w = `bar${3}`;"); +goTo.marker("exportOpenBrace"); +verify.currentLineContentIs("export {"); +goTo.marker("exportSpecifier1"); +verify.currentLineContentIs(" x,"); +goTo.marker("exportSpecifier2"); +verify.currentLineContentIs(" y as yy, z"); +goTo.marker("exportCloseBrace"); +verify.currentLineContentIs("}"); +goTo.marker("fromKeywordAutoformat"); +verify.currentLineContentIs("from"); +goTo.marker("fromKeywordIndent"); +verify.indentationIs(4); +goTo.marker("exportDir"); +verify.currentLineContentIs(' "foo"'); -goTo.marker("5"); -edit.insert(";"); -verify.currentLineContentIs(" `template`;"); \ No newline at end of file +goTo.marker("import1"); +verify.currentLineContentIs('import { x, y as yy, z } from "baz"'); + +goTo.marker("importOpenBrace"); +verify.currentLineContentIs("import {"); +goTo.marker("importSpecifier1"); +verify.currentLineContentIs(" x,"); +goTo.marker("importSpecifier2"); +verify.currentLineContentIs(" as yy,"); +goTo.marker("importCloseBrace"); +verify.currentLineContentIs("}"); +goTo.marker("importDir"); +verify.currentLineContentIs('from "wow"'); + +goTo.marker("formatOnEnter"); +edit.insertLine(''); +goTo.marker("formatOnEnterOpenBrace"); +verify.currentLineContentIs("{"); +goTo.marker("differentLineIndent"); +verify.indentationIs(4); +edit.insertLine(''); +goTo.marker("differentLineAutoformat"); +verify.currentLineContentIs(" x"); \ No newline at end of file From 40f3fcf575d808bd7b006c5f9a58e8520997a1dd Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Sat, 5 Sep 2015 11:43:08 +0900 Subject: [PATCH 06/17] remove hasBraceShell and add namedBindings check --- src/services/formatting/smartIndenter.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index d8bc8fc3b98..a95d756f356 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -478,20 +478,13 @@ namespace ts.formatting { } } - function hasBraceShell(node: Node) { - let children = node.getChildren(); - let last = children.length - 1; - // Check if node looks like a block - return children[0] && children[0].kind === SyntaxKind.OpenBraceToken && - children[last] && children[last].kind === SyntaxKind.CloseBraceToken; - } - export function isIndentationPrevented(node: TextRangeWithKind) { switch (node.kind) { case SyntaxKind.NamedExports: - return hasBraceShell(node); + return true; + // Allow indentation only when namedBindings is NamespaceImport. case SyntaxKind.ImportClause: - return hasBraceShell((node).namedBindings); + return (node).namedBindings.kind === SyntaxKind.NamedImports; } return false; } From 0fc9e7e313412e1a77c260a0823a03e4609360a2 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Sun, 6 Sep 2015 16:02:38 +0900 Subject: [PATCH 07/17] simplified code flow --- src/services/formatting/formatting.ts | 11 ++++------- src/services/formatting/smartIndenter.ts | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 3773412bace..d8ca7f03d96 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -403,16 +403,13 @@ namespace ts.formatting { indentation = parentDynamicIndentation.getIndentation(); } } - else if (SmartIndenter.isIndentationPrevented(node)) { + else if (SmartIndenter.isIndentationPrevented(node) || + SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { + indentation = parentDynamicIndentation.getIndentation(); } else { - if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { - indentation = parentDynamicIndentation.getIndentation(); - } - else { - indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(); - } + indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(); } } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index a95d756f356..86ee0f5f363 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -482,7 +482,7 @@ namespace ts.formatting { switch (node.kind) { case SyntaxKind.NamedExports: return true; - // Allow indentation only when namedBindings is NamespaceImport. + // NamedImports has its own braces as Block does case SyntaxKind.ImportClause: return (node).namedBindings.kind === SyntaxKind.NamedImports; } From 9ca46b8e0de4f691deac7952dfe7b1f6f4c8698f Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Sep 2015 01:58:42 +0900 Subject: [PATCH 08/17] Move housekeeping part to new PR #4711 --- src/services/formatting/rules.ts | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 5ba1fd8d9c7..273a104a6b8 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -213,15 +213,25 @@ namespace ts.formatting { public NoSpaceBetweenYieldKeywordAndStar: Rule; public SpaceBetweenYieldOrYieldStarAndOperand: Rule; - // Async functions + // Async-await public SpaceBetweenAsyncAndFunctionKeyword: Rule; + public NoSpaceBetweenAsyncAndFunctionKeyword: Rule; + public SpaceAfterAwaitKeyword: Rule; + public NoSpaceAfterAwaitKeyword: Rule; + + // Type alias declaration + public SpaceAfterTypeKeyword: Rule; + public NoSpaceAfterTypeKeyword: Rule; // Tagged template string public SpaceBetweenTagAndTemplateString: Rule; + public NoSpaceBetweenTagAndTemplateString: Rule; // Type operation public SpaceBeforeBar: Rule; + public NoSpaceBeforeBar: Rule; public SpaceAfterBar: Rule; + public NoSpaceAfterBar: Rule; public SpaceBeforeAmpersand: Rule; public SpaceAfterAmpersand: Rule; @@ -305,7 +315,7 @@ namespace ts.formatting { this.NoSpaceBeforeComma = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CommaToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceAfterLetConstInVariableDeclaration = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsStartOfVariableDeclarationList), RuleAction.Space)); this.NoSpaceBeforeOpenParenInFuncCall = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext, Rules.IsFunctionCallOrNewContext, Rules.IsPreviousTokenNotComma), RuleAction.Delete)); this.SpaceAfterFunctionInFuncDecl = new Rule(RuleDescriptor.create3(SyntaxKind.FunctionKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space)); @@ -342,7 +352,7 @@ namespace ts.formatting { SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, - SyntaxKind.TypeKeyword, SyntaxKind.AsKeyword, SyntaxKind.FromKeyword + SyntaxKind.AsKeyword, SyntaxKind.FromKeyword ]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.AsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); @@ -379,13 +389,23 @@ namespace ts.formatting { // Async-await this.SpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceBetweenAsyncAndFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + this.SpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceAfterAwaitKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.AwaitKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); + + // Type alias declaration + this.SpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.NoSpaceAfterTypeKeyword = new Rule(RuleDescriptor.create3(SyntaxKind.TypeKeyword, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // template string 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)); // 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)); @@ -415,9 +435,11 @@ namespace ts.formatting { this.NoSpaceBeforeOpenParenInFuncCall, this.SpaceBeforeBinaryKeywordOperator, this.SpaceAfterBinaryKeywordOperator, this.SpaceAfterVoidOperator, - this.SpaceBetweenAsyncAndFunctionKeyword, - this.SpaceBetweenTagAndTemplateString, - this.SpaceBeforeBar, this.SpaceAfterBar, + this.SpaceBetweenAsyncAndFunctionKeyword, this.NoSpaceBetweenAsyncAndFunctionKeyword, + this.SpaceAfterAwaitKeyword, this.NoSpaceAfterAwaitKeyword, + this.SpaceAfterTypeKeyword, this.NoSpaceAfterTypeKeyword, + this.SpaceBetweenTagAndTemplateString, this.NoSpaceBetweenTagAndTemplateString, + this.SpaceBeforeBar, this.NoSpaceBeforeBar, this.SpaceAfterBar, this.NoSpaceAfterBar, this.SpaceBeforeAmpersand, this.SpaceAfterAmpersand, // TypeScript-specific rules From feae0f711ab45203a2fc1822b2dec3bbb39cdc1c Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Sep 2015 02:18:26 +0900 Subject: [PATCH 09/17] Consider export/import specifier as binary op context --- src/services/formatting/rules.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 273a104a6b8..c4c72f3e6f0 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -348,13 +348,8 @@ namespace ts.formatting { this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); // Add a space around certain TypeScript keywords - this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([ - SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, - SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, - SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, - SyntaxKind.AsKeyword, SyntaxKind.FromKeyword - ]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.AsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, SyntaxKind.FromKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { this.SpaceAfterModuleName = new Rule(RuleDescriptor.create1(SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsModuleDeclContext), RuleAction.Space)); @@ -543,6 +538,8 @@ namespace ts.formatting { case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: case SyntaxKind.AsExpression: + case SyntaxKind.ExportSpecifier: + case SyntaxKind.ImportSpecifier: case SyntaxKind.TypePredicate: return true; From ad010ea8ee240054cf2beedf5e50e760eab43b21 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Sep 2015 02:31:27 +0900 Subject: [PATCH 10/17] Add some comments to isIndentationPrevented --- src/services/formatting/smartIndenter.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 86ee0f5f363..05aa867113d 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -478,12 +478,15 @@ namespace ts.formatting { } } + /** + * Function returns true if a node should not get additional indentation in its parent node. + */ export function isIndentationPrevented(node: TextRangeWithKind) { switch (node.kind) { case SyntaxKind.NamedExports: return true; - // NamedImports has its own braces as Block does case SyntaxKind.ImportClause: + // NamedImports has its own braces as Block does return (node).namedBindings.kind === SyntaxKind.NamedImports; } return false; From b65cfe13da06a3073168fd9d90100b6564145b6a Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Sep 2015 06:32:43 +0900 Subject: [PATCH 11/17] update isCompletedNode --- src/services/utilities.ts | 6 ++++++ tests/cases/fourslash/formatNamedExportImport.ts | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 8b77dcb953b..c413cedd5d1 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -79,6 +79,8 @@ namespace ts { case SyntaxKind.Block: case SyntaxKind.ModuleBlock: case SyntaxKind.CaseBlock: + case SyntaxKind.NamedImports: + case SyntaxKind.NamedExports: return nodeEndsWith(n, SyntaxKind.CloseBraceToken, sourceFile); case SyntaxKind.CatchClause: return isCompletedNode((n).block, sourceFile); @@ -181,6 +183,10 @@ namespace ts { case SyntaxKind.TemplateSpan: return nodeIsPresent((n).literal); + case SyntaxKind.ExportDeclaration: + case SyntaxKind.ImportDeclaration: + return nodeIsPresent((n).moduleSpecifier); + case SyntaxKind.PrefixUnaryExpression: return isCompletedNode((n).operand, sourceFile); case SyntaxKind.BinaryExpression: diff --git a/tests/cases/fourslash/formatNamedExportImport.ts b/tests/cases/fourslash/formatNamedExportImport.ts index 2e22c54ae3b..18f2f19577a 100644 --- a/tests/cases/fourslash/formatNamedExportImport.ts +++ b/tests/cases/fourslash/formatNamedExportImport.ts @@ -23,6 +23,10 @@ ////export/*formatOnEnter*/{/*formatOnEnterOpenBrace*/ /////*differentLineIndent*/x/*differentLineAutoformat*/ ////} from "abc" +//// +////export { +/////*incompleteExportDeclIndent*/ +/////*incompleteExportDeclIndent2*/ format.selection("selectionStart", "selectionEnd"); @@ -68,4 +72,10 @@ goTo.marker("differentLineIndent"); verify.indentationIs(4); edit.insertLine(''); goTo.marker("differentLineAutoformat"); -verify.currentLineContentIs(" x"); \ No newline at end of file +verify.currentLineContentIs(" x"); + +goTo.marker("incompleteExportDeclIndent") +verify.indentationIs(4); +edit.insert("} from"); +goTo.marker("incompleteExportDeclIndent2"); +verify.indentationIs(4); \ No newline at end of file From 886e4d22593d6cead584997f33a02d99d8bae88b Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Sep 2015 14:36:13 +0900 Subject: [PATCH 12/17] rename isIndentationPrevented --- src/services/formatting/formatting.ts | 2 +- src/services/formatting/smartIndenter.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index d8ca7f03d96..4f21f3ae2a7 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -403,7 +403,7 @@ namespace ts.formatting { indentation = parentDynamicIndentation.getIndentation(); } } - else if (SmartIndenter.isIndentationPrevented(node) || + else if (SmartIndenter.shouldInheritParentIndentation(node) || SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { indentation = parentDynamicIndentation.getIndentation(); diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 05aa867113d..aabd9af2544 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -134,7 +134,7 @@ namespace ts.formatting { // increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line if (shouldIndentChildNode(parent.kind, current.kind) && - !isIndentationPrevented(current) && !parentAndChildShareLine) { + !shouldInheritParentIndentation(current) && !parentAndChildShareLine) { indentationDelta += options.IndentSize; } @@ -481,7 +481,7 @@ namespace ts.formatting { /** * Function returns true if a node should not get additional indentation in its parent node. */ - export function isIndentationPrevented(node: TextRangeWithKind) { + export function shouldInheritParentIndentation(node: TextRangeWithKind) { switch (node.kind) { case SyntaxKind.NamedExports: return true; From 1110b902dc88cf93cac92e3a0aa4dffb3e72c311 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Fri, 11 Sep 2015 13:43:35 +0900 Subject: [PATCH 13/17] nodeWillIndentChild --- src/services/formatting/formatting.ts | 31 +++++----------- src/services/formatting/smartIndenter.ts | 46 ++++++++++++------------ 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 4f21f3ae2a7..a47d5c85712 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -282,19 +282,19 @@ namespace ts.formatting { */ function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number { let previousLine = Constants.Unknown; - let childKind = SyntaxKind.Unknown; + let child: Node = null; while (n) { let line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line; if (previousLine !== Constants.Unknown && line !== previousLine) { break; } - if (SmartIndenter.shouldIndentChildNode(n.kind, childKind)) { + if (SmartIndenter.shouldIndentChildNode(n, child)) { return options.IndentSize; } previousLine = line; - childKind = n.kind; + child = n; n = n.parent; } return 0; @@ -386,24 +386,9 @@ namespace ts.formatting { effectiveParentStartLine: number): Indentation { let indentation = inheritedIndentation; - if (indentation === Constants.Unknown) { - if (isSomeBlock(node.kind)) { - // blocks should be indented in - // - other blocks - // - source file - // - switch\default clauses - if (isSomeBlock(parent.kind) || - parent.kind === SyntaxKind.SourceFile || - parent.kind === SyntaxKind.CaseClause || - parent.kind === SyntaxKind.DefaultClause) { - indentation = parentDynamicIndentation.getIndentation() + parentDynamicIndentation.getDelta(); - } - else { - indentation = parentDynamicIndentation.getIndentation(); - } - } - else if (SmartIndenter.shouldInheritParentIndentation(node) || + if (indentation === Constants.Unknown) { + if (SmartIndenter.shouldInheritParentIndentation(parent, node) || SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { indentation = parentDynamicIndentation.getIndentation(); @@ -413,7 +398,7 @@ namespace ts.formatting { } } - var delta = SmartIndenter.shouldIndentChildNode(node.kind, SyntaxKind.Unknown) ? options.IndentSize : 0; + var delta = SmartIndenter.shouldIndentChildNode(node, null) ? options.IndentSize : 0; if (effectiveParentStartLine === startLine) { // if node is located on the same line with the parent @@ -495,7 +480,7 @@ namespace ts.formatting { getIndentation: () => indentation, getDelta: () => delta, recomputeIndentation: lineAdded => { - if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent.kind, node.kind)) { + if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent, node)) { if (lineAdded) { indentation += options.IndentSize; } @@ -503,7 +488,7 @@ namespace ts.formatting { indentation -= options.IndentSize; } - if (SmartIndenter.shouldIndentChildNode(node.kind, SyntaxKind.Unknown)) { + if (SmartIndenter.shouldIndentChildNode(node, null)) { delta = options.IndentSize; } else { diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index aabd9af2544..e30c83151a7 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -48,7 +48,7 @@ namespace ts.formatting { let indentationDelta: number; while (current) { - if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(current.kind, previous ? previous.kind : SyntaxKind.Unknown)) { + if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(current, previous)) { currentStart = getStartLineAndCharacterForNode(current, sourceFile); if (nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken, current, lineAtPosition, sourceFile)) { @@ -133,9 +133,7 @@ namespace ts.formatting { } // increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line - if (shouldIndentChildNode(parent.kind, current.kind) && - !shouldInheritParentIndentation(current) && !parentAndChildShareLine) { - + if (shouldIndentChildNode(parent, current) && !parentAndChildShareLine) { indentationDelta += options.IndentSize; } @@ -442,7 +440,6 @@ namespace ts.formatting { case SyntaxKind.ParenthesizedType: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.AwaitExpression: - case SyntaxKind.ImportDeclaration: case SyntaxKind.NamedExports: case SyntaxKind.NamedImports: case SyntaxKind.ExportSpecifier: @@ -451,12 +448,10 @@ namespace ts.formatting { } return false; } - - export function shouldIndentChildNode(parent: SyntaxKind, child: SyntaxKind): boolean { - if (nodeContentIsAlwaysIndented(parent)) { - return true; - } - switch (parent) { + + function nodeWillIndentChild(parent: TextRangeWithKind, child: TextRangeWithKind, indentByDefault: boolean) { + let childKind = child ? child.kind : SyntaxKind.Unknown; + switch (parent.kind) { case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.ForInStatement: @@ -470,26 +465,29 @@ namespace ts.formatting { case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: - return child !== SyntaxKind.Block; + return childKind !== SyntaxKind.Block; case SyntaxKind.ExportDeclaration: - return child !== SyntaxKind.NamedExports; - default: - return false; + return childKind !== SyntaxKind.NamedExports; + case SyntaxKind.ImportDeclaration: + return childKind !== SyntaxKind.ImportClause || + (child).namedBindings.kind !== SyntaxKind.NamedImports; } + // No explicit rule for selected nodes, so result will follow the default value argument. + return indentByDefault; } + export function shouldIndentChildNode(parent: TextRangeWithKind, child: TextRangeWithKind): boolean { + if (nodeContentIsAlwaysIndented(parent.kind)) { + return true; + } + return nodeWillIndentChild(parent, child, false); + } + /** * Function returns true if a node should not get additional indentation in its parent node. */ - export function shouldInheritParentIndentation(node: TextRangeWithKind) { - switch (node.kind) { - case SyntaxKind.NamedExports: - return true; - case SyntaxKind.ImportClause: - // NamedImports has its own braces as Block does - return (node).namedBindings.kind === SyntaxKind.NamedImports; - } - return false; + export function shouldInheritParentIndentation(parent: TextRangeWithKind, child: TextRangeWithKind) { + return !nodeWillIndentChild(parent, child, true); } } } \ No newline at end of file From 70cb7582047bb7a73e4d9c4e81f90e6c093730bf Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Fri, 11 Sep 2015 14:26:10 +0900 Subject: [PATCH 14/17] making comment more specific --- src/services/formatting/smartIndenter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 982a6dc5394..2720ae0b6d3 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -485,7 +485,7 @@ namespace ts.formatting { } /** - * Function returns true if a node should not get additional indentation in its parent node. + * Function returns true if existing node content indentation should be suppressed for a specific child */ export function shouldInheritParentIndentation(parent: TextRangeWithKind, child: TextRangeWithKind) { return !nodeWillIndentChild(parent, child, true); From 76d799cf0da6303910d284793c25a02c3e761482 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Dec 2015 00:34:48 +0900 Subject: [PATCH 15/17] crlf --- src/services/formatting/rules.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index ca7f633545a..191868039ac 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -335,9 +335,9 @@ namespace ts.formatting { // Use of module as a function call. e.g.: import m2 = module("m2"); this.NoSpaceAfterModuleImport = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword]), SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Delete)); - // Add a space around certain TypeScript keywords - this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, SyntaxKind.TypeKeyword, SyntaxKind.FromKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); - this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + // Add a space around certain TypeScript keywords + this.SpaceAfterCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.AbstractKeyword, SyntaxKind.ClassKeyword, SyntaxKind.DeclareKeyword, SyntaxKind.DefaultKeyword, SyntaxKind.EnumKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ExtendsKeyword, SyntaxKind.GetKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.ImportKeyword, SyntaxKind.InterfaceKeyword, SyntaxKind.ModuleKeyword, SyntaxKind.NamespaceKeyword, SyntaxKind.PrivateKeyword, SyntaxKind.PublicKeyword, SyntaxKind.ProtectedKeyword, SyntaxKind.SetKeyword, SyntaxKind.StaticKeyword, SyntaxKind.TypeKeyword, SyntaxKind.FromKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); + this.SpaceBeforeCertainTypeScriptKeywords = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword])), RuleOperation.create2(new RuleOperationContext(Rules.IsSameLineTokenContext), RuleAction.Space)); // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { this.SpaceAfterModuleName = new Rule(RuleDescriptor.create1(SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsModuleDeclContext), RuleAction.Space)); From d90a66ca2338b502f5be6ee72b3f663118a1ee76 Mon Sep 17 00:00:00 2001 From: SaschaNaz Date: Thu, 10 Dec 2015 01:32:07 +0900 Subject: [PATCH 16/17] crlf 2 --- src/services/formatting/formatting.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 4453bd299fc..ab0867bdd69 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -281,8 +281,8 @@ namespace ts.formatting { * to the initial indentation. */ function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number { - let previousLine = Constants.Unknown; - let child: Node; + let previousLine = Constants.Unknown; + let child: Node; while (n) { let line = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)).line; if (previousLine !== Constants.Unknown && line !== previousLine) { @@ -385,8 +385,8 @@ namespace ts.formatting { parentDynamicIndentation: DynamicIndentation, effectiveParentStartLine: number): Indentation { - let indentation = inheritedIndentation; - var delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0; + let indentation = inheritedIndentation; + var delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0; if (effectiveParentStartLine === startLine) { // if node is located on the same line with the parent @@ -485,8 +485,8 @@ namespace ts.formatting { else { indentation -= options.IndentSize; } - - if (SmartIndenter.shouldIndentChildNode(node)) { + + if (SmartIndenter.shouldIndentChildNode(node)) { delta = options.IndentSize; } else { From 294862c15e66e7533d78fe83342d99d1b4d3d63f Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Fri, 19 Feb 2016 15:58:00 +0900 Subject: [PATCH 17/17] indent 'from' again --- src/services/formatting/formatting.ts | 1 - tests/cases/fourslash/formatNamedExportImport.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 08390788245..53a8c2b4307 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -477,7 +477,6 @@ namespace ts.formatting { case SyntaxKind.CloseParenToken: case SyntaxKind.ElseKeyword: case SyntaxKind.WhileKeyword: - case SyntaxKind.FromKeyword: case SyntaxKind.AtToken: return indentation; default: diff --git a/tests/cases/fourslash/formatNamedExportImport.ts b/tests/cases/fourslash/formatNamedExportImport.ts index 18f2f19577a..efc91111b2d 100644 --- a/tests/cases/fourslash/formatNamedExportImport.ts +++ b/tests/cases/fourslash/formatNamedExportImport.ts @@ -44,7 +44,7 @@ verify.currentLineContentIs(" y as yy, z"); goTo.marker("exportCloseBrace"); verify.currentLineContentIs("}"); goTo.marker("fromKeywordAutoformat"); -verify.currentLineContentIs("from"); +verify.currentLineContentIs(" from"); goTo.marker("fromKeywordIndent"); verify.indentationIs(4); goTo.marker("exportDir"); @@ -62,7 +62,7 @@ verify.currentLineContentIs(" as yy,"); goTo.marker("importCloseBrace"); verify.currentLineContentIs("}"); goTo.marker("importDir"); -verify.currentLineContentIs('from "wow"'); +verify.currentLineContentIs(' from "wow"'); goTo.marker("formatOnEnter"); edit.insertLine('');