diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d8b7c358a55..95a320ac052 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1621,8 +1621,9 @@ module FourSlash { this.taoInvalidReason = 'verifyIndentationAtCurrentPosition NYI'; var actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition); - if (actual != numberOfSpaces) { - this.raiseError('verifyIndentationAtCurrentPosition failed - expected: ' + numberOfSpaces + ', actual: ' + actual); + var lineCol = this.getLineColStringAtPosition(this.currentCaretPosition); + if (actual !== numberOfSpaces) { + this.raiseError('verifyIndentationAtCurrentPosition failed at ' + lineCol + ' - expected: ' + numberOfSpaces + ', actual: ' + actual); } } @@ -1630,8 +1631,9 @@ module FourSlash { this.taoInvalidReason = 'verifyIndentationAtPosition NYI'; var actual = this.getIndentation(fileName, position); + var lineCol = this.getLineColStringAtPosition(position); if (actual !== numberOfSpaces) { - this.raiseError('verifyIndentationAtPosition failed - expected: ' + numberOfSpaces + ', actual: ' + actual); + this.raiseError('verifyIndentationAtPosition failed at ' + lineCol + ' - expected: ' + numberOfSpaces + ', actual: ' + actual); } } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index e7beebf6866..1a52c72c9d8 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -359,6 +359,7 @@ module ts.formatting { case SyntaxKind.ModuleBlock: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeLiteral: + case SyntaxKind.TupleType: case SyntaxKind.CaseBlock: case SyntaxKind.DefaultClause: case SyntaxKind.CaseClause: @@ -370,6 +371,8 @@ module ts.formatting { case SyntaxKind.ExportAssignment: case SyntaxKind.ReturnStatement: case SyntaxKind.ConditionalExpression: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ObjectBindingPattern: return true; } return false; @@ -390,6 +393,7 @@ module ts.formatting { case SyntaxKind.FunctionExpression: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: + case SyntaxKind.CallSignature: case SyntaxKind.ArrowFunction: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: @@ -431,46 +435,85 @@ module ts.formatting { case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.TypeLiteral: case SyntaxKind.Block: case SyntaxKind.ModuleBlock: case SyntaxKind.CaseBlock: return nodeEndsWith(n, SyntaxKind.CloseBraceToken, sourceFile); case SyntaxKind.CatchClause: return isCompletedNode((n).block, sourceFile); - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.CallSignature: + case SyntaxKind.NewExpression: + if (!(n).arguments) { + return true; + } + // fall through case SyntaxKind.CallExpression: - case SyntaxKind.ConstructSignature: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ParenthesizedType: return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile); + + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return isCompletedNode((n).type, sourceFile); + + case SyntaxKind.Constructor: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.CallSignature: case SyntaxKind.ArrowFunction: - return !(n).body || isCompletedNode((n).body, sourceFile); + if ((n).body) { + return isCompletedNode((n).body, sourceFile); + } + + if ((n).type) { + return isCompletedNode((n).type, sourceFile); + } + + // Even though type parameters can be unclosed, we can get away with + // having at least a closing paren. + return hasChildOfKind(n, SyntaxKind.CloseParenToken, sourceFile); + case SyntaxKind.ModuleDeclaration: return (n).body && isCompletedNode((n).body, sourceFile); + case SyntaxKind.IfStatement: if ((n).elseStatement) { return isCompletedNode((n).elseStatement, sourceFile); } return isCompletedNode((n).thenStatement, sourceFile); + case SyntaxKind.ExpressionStatement: return isCompletedNode((n).expression, sourceFile); + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ComputedPropertyName: + case SyntaxKind.TupleType: return nodeEndsWith(n, SyntaxKind.CloseBracketToken, sourceFile); + + case SyntaxKind.IndexSignature: + if ((n).type) { + return isCompletedNode((n).type, sourceFile); + } + + return hasChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile); + case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: - // there is no such thing as terminator token for CaseClause\DefaultClause so for simplicitly always consider them non-completed + // there is no such thing as terminator token for CaseClause/DefaultClause so for simplicitly always consider them non-completed return false; + case SyntaxKind.ForStatement: - return isCompletedNode((n).statement, sourceFile); case SyntaxKind.ForInStatement: - return isCompletedNode((n).statement, sourceFile); case SyntaxKind.ForOfStatement: - return isCompletedNode((n).statement, sourceFile); case SyntaxKind.WhileStatement: - return isCompletedNode((n).statement, sourceFile); + return isCompletedNode((n).statement, sourceFile); case SyntaxKind.DoStatement: // rough approximation: if DoStatement has While keyword - then if node is completed is checking the presence of ')'; let hasWhileKeyword = findChildOfKind(n, SyntaxKind.WhileKeyword, sourceFile); @@ -478,6 +521,7 @@ module ts.formatting { return nodeEndsWith(n, SyntaxKind.CloseParenToken, sourceFile); } return isCompletedNode((n).statement, sourceFile); + default: return true; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 452395f454a..7671ee3f88d 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -79,6 +79,10 @@ module ts { }; } + export function hasChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFile): boolean { + return !!findChildOfKind(n, kind, sourceFile); + } + export function findChildOfKind(n: Node, kind: SyntaxKind, sourceFile?: SourceFile): Node { return forEach(n.getChildren(sourceFile), c => c.kind === kind && c); } diff --git a/tests/cases/fourslash/indentation.ts b/tests/cases/fourslash/indentation.ts index 2a2090c1d87..356d076b9f2 100644 --- a/tests/cases/fourslash/indentation.ts +++ b/tests/cases/fourslash/indentation.ts @@ -176,8 +176,8 @@ ////// the purpose of this test is to verity smart indent ////// works for unterminated function arguments at the end of a file. ////function unterminatedListIndentation(a, -////{| "indent": 0 |} +////{| "indent": 4 |} -test.markers().forEach((marker) => { - verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); - }); +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentAfterNewExpression.ts b/tests/cases/fourslash/smartIndentAfterNewExpression.ts new file mode 100644 index 00000000000..f2d699f988b --- /dev/null +++ b/tests/cases/fourslash/smartIndentAfterNewExpression.ts @@ -0,0 +1,17 @@ +/// + +//// +////new Array +////{| "indent": 0 |} +////new Array; +////{| "indent": 0 |} +////new Array(0); +////{| "indent": 0 |} +////new Array(; +////{| "indent": 0 |} +////new Array( +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts b/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts new file mode 100644 index 00000000000..980f2383299 --- /dev/null +++ b/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts @@ -0,0 +1,14 @@ +/// + +////var /*1*/[/*2*/a,/*3*/b,/*4*/ + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 8); +verifyIndentationAfterNewLine("3", 8); +verifyIndentationAfterNewLine("4", 8); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts b/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts new file mode 100644 index 00000000000..b9a1da7796a --- /dev/null +++ b/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts @@ -0,0 +1,15 @@ +/// + +////var /*1*/[/*2*/a,/*3*/b/*4*/]/*5*/ + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 8); +verifyIndentationAfterNewLine("3", 8); +verifyIndentationAfterNewLine("4", 8); +verifyIndentationAfterNewLine("5", 0); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts b/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts new file mode 100644 index 00000000000..253f58071d9 --- /dev/null +++ b/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts @@ -0,0 +1,13 @@ +/// + +////var x = (/*1*/1/*2*/)/*3*/ + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 4); +verifyIndentationAfterNewLine("3", 0); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentInParenthesizedExpression02.ts b/tests/cases/fourslash/smartIndentInParenthesizedExpression02.ts new file mode 100644 index 00000000000..ac169d0b17e --- /dev/null +++ b/tests/cases/fourslash/smartIndentInParenthesizedExpression02.ts @@ -0,0 +1,8 @@ +/// + +////var y = ( +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts b/tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts index b2b1f746a32..a533eca1d67 100644 --- a/tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts +++ b/tests/cases/fourslash/smartIndentNonterminatedArgumentListAtEOF.ts @@ -4,4 +4,4 @@ /////**/ goTo.marker(); -verify.indentationIs(0); +verify.indentationIs(4); diff --git a/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts b/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts new file mode 100644 index 00000000000..8bfcbe83b72 --- /dev/null +++ b/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts @@ -0,0 +1,15 @@ +/// + +////var /*1*/{/*2*/a,/*3*/b:/*4*/k,/*5*/ + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 8); +verifyIndentationAfterNewLine("3", 8); +verifyIndentationAfterNewLine("4", 8); +verifyIndentationAfterNewLine("5", 8); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts b/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts new file mode 100644 index 00000000000..e1612dffbb4 --- /dev/null +++ b/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts @@ -0,0 +1,16 @@ +/// + +////var /*1*/{/*2*/a,/*3*/b:/*4*/k,/*5*/}/*6*/ + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 8); +verifyIndentationAfterNewLine("3", 8); +verifyIndentationAfterNewLine("4", 8); +verifyIndentationAfterNewLine("5", 8); +verifyIndentationAfterNewLine("6", 0); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnAccessors.ts b/tests/cases/fourslash/smartIndentOnAccessors.ts new file mode 100644 index 00000000000..a7972b1f48b --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnAccessors.ts @@ -0,0 +1,36 @@ +/// + +////class Foo { +//// get foo(a, +//// /*1*/b,/*0*/ +//// //comment/*2*/ +//// /*3*/c +//// ) { +//// } +//// set foo(a, +//// /*5*/b,/*4*/ +//// //comment/*6*/ +//// /*7*/c +//// ) { +//// } +////} + + +goTo.marker("0"); +edit.insert("\r\n"); +verify.indentationIs(8); +goTo.marker("1"); +verify.currentLineContentIs(" b,"); +goTo.marker("2"); +verify.currentLineContentIs(" //comment"); +goTo.marker("3"); +verify.currentLineContentIs(" c"); +goTo.marker("4"); +edit.insert("\r\n"); +verify.indentationIs(8); +goTo.marker("5"); +verify.currentLineContentIs(" b,"); +goTo.marker("6"); +verify.currentLineContentIs(" //comment"); +goTo.marker("7"); +verify.currentLineContentIs(" c"); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnAccessors01.ts b/tests/cases/fourslash/smartIndentOnAccessors01.ts new file mode 100644 index 00000000000..a7972b1f48b --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnAccessors01.ts @@ -0,0 +1,36 @@ +/// + +////class Foo { +//// get foo(a, +//// /*1*/b,/*0*/ +//// //comment/*2*/ +//// /*3*/c +//// ) { +//// } +//// set foo(a, +//// /*5*/b,/*4*/ +//// //comment/*6*/ +//// /*7*/c +//// ) { +//// } +////} + + +goTo.marker("0"); +edit.insert("\r\n"); +verify.indentationIs(8); +goTo.marker("1"); +verify.currentLineContentIs(" b,"); +goTo.marker("2"); +verify.currentLineContentIs(" //comment"); +goTo.marker("3"); +verify.currentLineContentIs(" c"); +goTo.marker("4"); +edit.insert("\r\n"); +verify.indentationIs(8); +goTo.marker("5"); +verify.currentLineContentIs(" b,"); +goTo.marker("6"); +verify.currentLineContentIs(" //comment"); +goTo.marker("7"); +verify.currentLineContentIs(" c"); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnAccessors02.ts b/tests/cases/fourslash/smartIndentOnAccessors02.ts new file mode 100644 index 00000000000..08f0083f2f2 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnAccessors02.ts @@ -0,0 +1,9 @@ +/// + +////class Foo { +//// get foo() { +////{| "indent": 8 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedArrowType01.ts b/tests/cases/fourslash/smartIndentOnUnclosedArrowType01.ts new file mode 100644 index 00000000000..44a47d984ab --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedArrowType01.ts @@ -0,0 +1,8 @@ +/// + +////var x: () => { +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedComputedProperty01.ts b/tests/cases/fourslash/smartIndentOnUnclosedComputedProperty01.ts new file mode 100644 index 00000000000..0b57a2cb466 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedComputedProperty01.ts @@ -0,0 +1,11 @@ +/// + +////var x = { +//// [1123123123132 +////{| "indent": 4 |} +////} + +// Note that we currently do NOT indent further in a computed property. +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedConstructorType01.ts b/tests/cases/fourslash/smartIndentOnUnclosedConstructorType01.ts new file mode 100644 index 00000000000..ae76cbf06c9 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedConstructorType01.ts @@ -0,0 +1,8 @@ +/// + +////var x: new () => { +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts new file mode 100644 index 00000000000..3e08fe5975e --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts @@ -0,0 +1,13 @@ +/// + +////function /*1*/f/*2*/ + + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 4); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts new file mode 100644 index 00000000000..3de8b5ec6c3 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts @@ -0,0 +1,14 @@ +/// + +////function f + +////function f/*1*/ + + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts new file mode 100644 index 00000000000..3931433e51f --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts @@ -0,0 +1,17 @@ +/// + +////function f/*1*/(/*2*/a: A, /*3*/b:/*4*/B, c/*5*/, d: C/*6*/ + + +function verifyIndentationAfterNewLine(marker: string, indentation: number): void { + goTo.marker(marker); + edit.insert("\r\n"); + verify.indentationIs(indentation); +} + +verifyIndentationAfterNewLine("1", 4); +verifyIndentationAfterNewLine("2", 4); +verifyIndentationAfterNewLine("3", 4); +verifyIndentationAfterNewLine("4", 4); +verifyIndentationAfterNewLine("5", 4); +verifyIndentationAfterNewLine("6", 4); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration05.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration05.ts new file mode 100644 index 00000000000..24c2bca72f7 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration05.ts @@ -0,0 +1,9 @@ +/// + +////function f(a: A, b:B, c, d: C): { +////{| "indent": 4 |} +//// + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration06.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration06.ts new file mode 100644 index 00000000000..782a85c908a --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration06.ts @@ -0,0 +1,10 @@ +/// + +////function f(a: A, b:B, c, d: C): { +////{| "indent": 4 |} +////} { +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedIndexSignature01.ts b/tests/cases/fourslash/smartIndentOnUnclosedIndexSignature01.ts new file mode 100644 index 00000000000..da88bc9d0c1 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedIndexSignature01.ts @@ -0,0 +1,11 @@ +/// + +////class C { +////[x: string +////{| "indent": 4 |} +//// + +// Note that we currently do NOT indent further in an index signature. +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedObjectTypeLiteral01.ts b/tests/cases/fourslash/smartIndentOnUnclosedObjectTypeLiteral01.ts new file mode 100644 index 00000000000..f2f900ce5d8 --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedObjectTypeLiteral01.ts @@ -0,0 +1,8 @@ +/// + +////var x: { +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentOnUnclosedTupleTypeLiteral01.ts b/tests/cases/fourslash/smartIndentOnUnclosedTupleTypeLiteral01.ts new file mode 100644 index 00000000000..f2346e6bddc --- /dev/null +++ b/tests/cases/fourslash/smartIndentOnUnclosedTupleTypeLiteral01.ts @@ -0,0 +1,8 @@ +/// + +////var x: [string, number, +////{| "indent": 4 |} + +test.markers().forEach(marker => { + verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent); +}); \ No newline at end of file