Merge pull request #5197 from Microsoft/supportIndentStyle

Support different indentation styles
This commit is contained in:
Paul van Brenk 2015-10-09 16:46:31 -07:00 committed by Paul van Brenk
parent 12b436bb2c
commit 573652160c
8 changed files with 426 additions and 12 deletions

View File

@ -100,6 +100,8 @@ namespace FourSlash {
end: number;
}
export import IndentStyle = ts.IndentStyle;
let entityMap: ts.Map<string> = {
"&": "&amp;",
"\"": "&quot;",
@ -309,6 +311,7 @@ namespace FourSlash {
TabSize: 4,
NewLineCharacter: Harness.IO.newLine(),
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Smart,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
@ -1695,24 +1698,28 @@ namespace FourSlash {
}
}
private getIndentation(fileName: string, position: number): number {
return this.languageService.getIndentationAtPosition(fileName, position, this.formatCodeOptions);
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle): number {
let formatOptions = ts.clone(this.formatCodeOptions);
formatOptions.IndentStyle = indentStyle;
return this.languageService.getIndentationAtPosition(fileName, position, formatOptions);
}
public verifyIndentationAtCurrentPosition(numberOfSpaces: number) {
public verifyIndentationAtCurrentPosition(numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
this.taoInvalidReason = "verifyIndentationAtCurrentPosition NYI";
let actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition);
let actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition, indentStyle);
let lineCol = this.getLineColStringAtPosition(this.currentCaretPosition);
if (actual !== numberOfSpaces) {
this.raiseError(`verifyIndentationAtCurrentPosition failed at ${lineCol} - expected: ${numberOfSpaces}, actual: ${actual}`);
}
}
public verifyIndentationAtPosition(fileName: string, position: number, numberOfSpaces: number) {
public verifyIndentationAtPosition(fileName: string, position: number, numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
this.taoInvalidReason = "verifyIndentationAtPosition NYI";
let actual = this.getIndentation(fileName, position);
let actual = this.getIndentation(fileName, position, indentStyle);
let lineCol = this.getLineColStringAtPosition(position);
if (actual !== numberOfSpaces) {
this.raiseError(`verifyIndentationAtPosition failed at ${lineCol} - expected: ${numberOfSpaces}, actual: ${actual}`);

View File

@ -1177,6 +1177,7 @@ namespace ts.server {
TabSize: 4,
NewLineCharacter: ts.sys ? ts.sys.newLine : '\n',
ConvertTabsToSpaces: true,
IndentStyle: ts.IndentStyle.Smart,
InsertSpaceAfterCommaDelimiter: true,
InsertSpaceAfterSemicolonInForStatements: true,
InsertSpaceBeforeAndAfterBinaryOperators: true,
@ -1187,7 +1188,6 @@ namespace ts.server {
PlaceOpenBraceOnNewLineForFunctions: false,
PlaceOpenBraceOnNewLineForControlBlocks: false,
}
}
export interface LineCollection {

View File

@ -606,6 +606,7 @@ namespace ts.server {
TabSize: formatOptions.TabSize,
NewLineCharacter: "\n",
ConvertTabsToSpaces: formatOptions.ConvertTabsToSpaces,
IndentStyle: ts.IndentStyle.Smart,
};
var indentPosition =
compilerService.languageService.getIndentationAtPosition(file, position, editorOptions);

View File

@ -13,6 +13,12 @@ namespace ts.formatting {
return 0; // past EOF
}
// no indentation when the indent style is set to none,
// so we can return fast
if (options.IndentStyle === IndentStyle.None) {
return 0;
}
let precedingToken = findPrecedingToken(position, sourceFile);
if (!precedingToken) {
return 0;
@ -26,6 +32,26 @@ namespace ts.formatting {
let lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
// indentation is first non-whitespace character in a previous line
// for block indentation, we should look for a line which contains something that's not
// whitespace.
if (options.IndentStyle === IndentStyle.Block) {
// move backwards until we find a line with a non-whitespace character,
// then find the first non-whitespace character for that line.
let current = position;
while (current > 0){
let char = sourceFile.text.charCodeAt(current);
if (!isWhiteSpace(char) && !isLineBreak(char)) {
break;
}
current--;
}
let lineStart = ts.getLineStartPositionForPosition(current, sourceFile);
return SmartIndenter.findFirstNonWhitespaceColumn(lineStart, current, sourceFile, options);
}
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
let actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
@ -218,7 +244,7 @@ namespace ts.formatting {
function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFile): LineAndCharacter {
return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
}
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean {
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
let elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
@ -319,7 +345,7 @@ namespace ts.formatting {
}
return Value.Unknown;
function getStartingExpression(node: PropertyAccessExpression | CallExpression | ElementAccessExpression) {
while (true) {
switch (node.kind) {
@ -465,4 +491,4 @@ namespace ts.formatting {
}
}
}
}
}

View File

@ -1189,6 +1189,13 @@ namespace ts {
TabSize: number;
NewLineCharacter: string;
ConvertTabsToSpaces: boolean;
IndentStyle: IndentStyle;
}
export enum IndentStyle {
None = 0,
Block = 1,
Smart = 2,
}
export interface FormatCodeOptions extends EditorOptions {

View File

@ -68,6 +68,13 @@ enum EmitReturnStatus {
EmitErrorsEncountered = 4 // Emitter errors occurred during emitting process
}
// This is a duplicate of the indentstyle in services.ts to expose it to testcases in fourslash
enum IndentStyle {
None,
Block,
Smart,
}
module FourSlashInterface {
export interface Marker {
@ -278,8 +285,8 @@ module FourSlashInterface {
FourSlash.currentTestState.verifyIndentationAtCurrentPosition(numberOfSpaces);
}
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number) {
FourSlash.currentTestState.verifyIndentationAtPosition(fileName, position, numberOfSpaces);
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle = IndentStyle.Smart) {
FourSlash.currentTestState.verifyIndentationAtPosition(fileName, position, numberOfSpaces, indentStyle);
}
public textAtCaretIs(text: string) {

View File

@ -0,0 +1,183 @@
/// <reference path="fourslash.ts"/>
////
////module classes {
////{| "indent": 0 |}
//// class Bar {
////{| "indent": 4 |}
////
//// constructor() {
////{| "indent": 8 |}
//// }
////
//// private foo: string = "";
////{| "indent": 8 |}
////
//// private f() {
//// var a: any[] = [[1, 2], [3, 4], 5];
////{| "indent": 12 |}
//// return ((1 + 1));
//// }
////
////{| "indent": 8 |}
//// private f2() {
//// if (true) { } { };
//// }
//// }
////}
////
////
////module interfaces {
////{| "indent": 0 |}
//// interface Foo {
////{| "indent": 4 |}
////
//// x: number;
////{| "indent": 8 |}
////
//// foo(): number;
////{| "indent": 8 |}
//// }
////}
////
////
////module nestedModules {
//// module Foo2 {
////{| "indent": 4 |}
//// function f() {
//// }
////{| "indent": 8 |}
//// var x: number;
////{| "indent": 8 |}
//// }
////}
////
////
////module Enums {
//// enum Foo3 {
////{| "indent": 4 |}
//// val1,
////{| "indent": 8 |}
//// val2,
////{| "indent": 8 |}
//// }
////{| "indent": 4 |}
////}
////
////
////function controlStatements() {
//// for (var i = 0; i < 10; i++) {
////{| "indent": 4 |}
//// }
////
//// for (var e in foo.bar) {
////{| "indent": 4 |}
//// }
////
//// with (foo.bar) {
////{| "indent": 4 |}
//// }
////
//// while (false) {
////{| "indent": 4 |}
//// }
////
//// do {
////{| "indent": 4 |}
//// } while (false);
////
//// switch (foo.bar) {
////{| "indent": 4 |}
//// }
////
//// switch (foo.bar) {
////{| "indent": 4 |}
//// case 1:
////{| "indent": 8 |}
//// break;
//// default:
////{| "indent": 8 |}
//// break;
//// }
////}
////
////
////function tryCatch() {
////{| "indent": 0 |}
//// try {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
//// catch (err) {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
////}
////
////
////function tryFinally() {
////{| "indent": 0 |}
//// try {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
//// finally {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
////}
////
////
////function tryCatchFinally() {
////{| "indent": 0 |}
//// try {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
//// catch (err) {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
//// finally {
////{| "indent": 4 |}
//// }
////{| "indent": 4 |}
////}
////
////
////class indentBeforeCurly
////{| "indent": 0 |}
////{| "indent": 0 |}{
////{| "indent": 0 |}
////}
////
////
////function argumentsListIndentation(bar,
//// blah,
//// {| "indent": 13 |}
////);
////
////
////function blockIndentAfterIndentedParameter1(bar,
//// blah) {
////{| "indent": 13 |}
////}
////
////
////function blockIndentAfterIndentedParameter2(bar,
//// blah) {
//// if (foo) {
////{| "indent": 4 |}
//// }
////}
////
////
////// Note: Do not add more tests at the end of this file, as
////// 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 |}
test.markers().forEach(marker => {
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent, IndentStyle.Block);
});

View File

@ -0,0 +1,183 @@
/// <reference path="fourslash.ts"/>
////
////module classes {
////{| "indent": 0 |}
//// class Bar {
////{| "indent": 0 |}
////
//// constructor() {
////{| "indent": 0 |}
//// }
////
//// private foo: string = "";
////{| "indent": 0 |}
////
//// private f() {
//// var a: any[] = [[1, 2], [3, 4], 5];
////{| "indent": 0 |}
//// return ((1 + 1));
//// }
////
////{| "indent": 0 |}
//// private f2() {
//// if (true) { } { };
//// }
//// }
////}
////
////
////module interfaces {
////{| "indent": 0 |}
//// interface Foo {
////{| "indent": 0 |}
////
//// x: number;
////{| "indent": 0 |}
////
//// foo(): number;
////{| "indent": 0 |}
//// }
////}
////
////
////module nestedModules {
//// module Foo2 {
////{| "indent": 0 |}
//// function f() {
//// }
////{| "indent": 0 |}
//// var x: number;
////{| "indent": 0 |}
//// }
////}
////
////
////module Enums {
//// enum Foo3 {
////{| "indent": 0 |}
//// val1,
////{| "indent": 0 |}
//// val2,
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
////}
////
////
////function controlStatements() {
//// for (var i = 0; i < 10; i++) {
////{| "indent": 0 |}
//// }
////
//// for (var e in foo.bar) {
////{| "indent": 0 |}
//// }
////
//// with (foo.bar) {
////{| "indent": 0 |}
//// }
////
//// while (false) {
////{| "indent": 0 |}
//// }
////
//// do {
////{| "indent": 0 |}
//// } while (false);
////
//// switch (foo.bar) {
////{| "indent": 0 |}
//// }
////
//// switch (foo.bar) {
////{| "indent": 0 |}
//// case 1:
////{| "indent": 0 |}
//// break;
//// default:
////{| "indent": 0 |}
//// break;
//// }
////}
////
////
////function tryCatch() {
////{| "indent": 0 |}
//// try {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
//// catch (err) {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
////}
////
////
////function tryFinally() {
////{| "indent": 0 |}
//// try {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
//// finally {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
////}
////
////
////function tryCatchFinally() {
////{| "indent": 0 |}
//// try {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
//// catch (err) {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
//// finally {
////{| "indent": 0 |}
//// }
////{| "indent": 0 |}
////}
////
////
////class indentBeforeCurly
////{| "indent": 0 |}
////{| "indent": 0 |}{
////{| "indent": 0 |}
////}
////
////
////function argumentsListIndentation(bar,
//// blah,
//// {| "indent": 0 |}
////);
////
////
////function blockIndentAfterIndentedParameter1(bar,
//// blah) {
////{| "indent": 0 |}
////}
////
////
////function blockIndentAfterIndentedParameter2(bar,
//// blah) {
//// if (foo) {
////{| "indent": 0 |}
//// }
////}
////
////
////// Note: Do not add more tests at the end of this file, as
////// 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 |}
test.markers().forEach(marker => {
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent, IndentStyle.None);
});