Merge pull request #9236 from Microsoft/BaseIndentationSupport

[Salsa] Adding base indentation for script block formatting and smart indent
This commit is contained in:
jramsay
2016-06-24 14:22:58 -07:00
committed by GitHub
10 changed files with 508 additions and 17 deletions

View File

@@ -310,6 +310,7 @@ namespace FourSlash {
}
this.formatCodeOptions = {
BaseIndentSize: 0,
IndentSize: 4,
TabSize: 4,
NewLineCharacter: Harness.IO.newLine(),
@@ -1659,24 +1660,25 @@ namespace FourSlash {
}
}
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle): number {
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle, baseIndentSize: number): number {
const formatOptions = ts.clone(this.formatCodeOptions);
formatOptions.IndentStyle = indentStyle;
formatOptions.BaseIndentSize = baseIndentSize;
return this.languageService.getIndentationAtPosition(fileName, position, formatOptions);
}
public verifyIndentationAtCurrentPosition(numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
const actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition, indentStyle);
public verifyIndentationAtCurrentPosition(numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart, baseIndentSize = 0) {
const actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition, indentStyle, baseIndentSize);
const 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, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
const actual = this.getIndentation(fileName, position, indentStyle);
public verifyIndentationAtPosition(fileName: string, position: number, numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart, baseIndentSize = 0) {
const actual = this.getIndentation(fileName, position, indentStyle, baseIndentSize);
const lineCol = this.getLineColStringAtPosition(position);
if (actual !== numberOfSpaces) {
this.raiseError(`verifyIndentationAtPosition failed at ${lineCol} - expected: ${numberOfSpaces}, actual: ${actual}`);
@@ -2938,8 +2940,8 @@ namespace FourSlashInterface {
this.state.verifyIndentationAtCurrentPosition(numberOfSpaces);
}
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle = ts.IndentStyle.Smart) {
this.state.verifyIndentationAtPosition(fileName, position, numberOfSpaces, indentStyle);
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle = ts.IndentStyle.Smart, baseIndentSize = 0) {
this.state.verifyIndentationAtPosition(fileName, position, numberOfSpaces, indentStyle, baseIndentSize);
}
public textAtCaretIs(text: string) {

View File

@@ -1566,6 +1566,7 @@ namespace ts.server {
static getDefaultFormatCodeOptions(host: ServerHost): ts.FormatCodeOptions {
return ts.clone({
BaseIndentSize: 0,
IndentSize: 4,
TabSize: 4,
NewLineCharacter: host.newLine || "\n",

View File

@@ -438,6 +438,9 @@ declare namespace ts.server.protocol {
/** Number of spaces to indent during formatting. Default value is 4. */
indentSize?: number;
/** Number of additional spaces to indent during formatting to preserve base indentation (ex. script block indentation). Default value is 0. */
baseIndentSize?: number;
/** The new line character to be used. Default value is the OS line delimiter. */
newLineCharacter?: string;
@@ -478,7 +481,7 @@ declare namespace ts.server.protocol {
placeOpenBraceOnNewLineForControlBlocks?: boolean;
/** Index operator */
[key: string]: string | number | boolean;
[key: string]: string | number | boolean | undefined;
}
/**

View File

@@ -703,6 +703,7 @@ namespace ts.server {
if (lineText.search("\\S") < 0) {
// TODO: get these options from host
const editorOptions: ts.EditorOptions = {
BaseIndentSize: formatOptions.BaseIndentSize,
IndentSize: formatOptions.IndentSize,
TabSize: formatOptions.TabSize,
NewLineCharacter: formatOptions.NewLineCharacter,

View File

@@ -394,7 +394,10 @@ namespace ts.formatting {
const startLinePosition = getLineStartPositionForPosition(startPos, sourceFile);
const column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, startPos, sourceFile, options);
if (startLine !== parentStartLine || startPos === column) {
return column;
// Use the base indent size if it is greater than
// the indentation of the inherited predecessor.
const baseIndentSize = SmartIndenter.getBaseIndentation(options);
return baseIndentSize > column ? baseIndentSize : column;
}
}

View File

@@ -10,7 +10,7 @@ namespace ts.formatting {
export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number {
if (position > sourceFile.text.length) {
return 0; // past EOF
return getBaseIndentation(options); // past EOF
}
// no indentation when the indent style is set to none,
@@ -21,7 +21,7 @@ namespace ts.formatting {
const precedingToken = findPrecedingToken(position, sourceFile);
if (!precedingToken) {
return 0;
return getBaseIndentation(options);
}
// no indentation in string \regex\template literals
@@ -96,13 +96,17 @@ namespace ts.formatting {
}
if (!current) {
// no parent was found - return 0 to be indented on the level of SourceFile
return 0;
// no parent was found - return the base indentation of the SourceFile
return getBaseIndentation(options);
}
return getIndentationForNodeWorker(current, currentStart, /*ignoreActualIndentationRange*/ undefined, indentationDelta, sourceFile, options);
}
export function getBaseIndentation(options: EditorOptions) {
return options.BaseIndentSize || 0;
}
export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number {
const start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options);
@@ -162,7 +166,7 @@ namespace ts.formatting {
parent = current.parent;
}
return indentationDelta;
return indentationDelta + getBaseIndentation(options);
}

View File

@@ -1244,6 +1244,7 @@ namespace ts {
}
export interface EditorOptions {
BaseIndentSize?: number;
IndentSize: number;
TabSize: number;
NewLineCharacter: string;
@@ -1268,7 +1269,7 @@ namespace ts {
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
PlaceOpenBraceOnNewLineForFunctions: boolean;
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
[s: string]: boolean | number | string;
[s: string]: boolean | number | string | undefined;
}
export interface DefinitionInfo {

View File

@@ -0,0 +1,264 @@
/// <reference path='fourslash.ts'/>
////
/////*1*/ module classes {
/////*2*/ class Bar {
////
/////*3*/ constructor() {
/////*4*/ }
////
/////*5*/private foo: string = "";
////
/////*6*/ private f() {
/////*7*/ var a: any[] = [[1, 2], [3, 4], 5];
/////*8*/ return ((1 + 1));
/////*9*/ }
////
/////*10*/ private f2() {
/////*11*/ if (true) { } { };
/////*12*/ }
/////*13*/ }
/////*14*/ }
////
////
/////*15*/ module interfaces {
/////*16*/ interface Foo {
////
/////*17*/ x: number;
////
/////*18*/ foo(): number;
/////*19*/ }
/////*20*/ }
////
////
/////*21*/ module nestedModules {
/////*22*/ module Foo2 {
/////*23*/ function f() {
/////*24*/ }
/////*25*/ var x: number;
/////*26*/}
/////*27*/ }
////
////
/////*28*/ module Enums {
/////*29*/ enum Foo3 {
/////*30*/ val1 ,
/////*31*/ val2,
/////*32*/ }
/////*33*/ }
////
////
/////*34*/ function controlStatements() {
/////*35*/ for (var i = 0; i < 10; i++) {
/////*36*/ }
////
/////*37*/ for (var e in foo.bar) {
/////*38*/ }
////
/////*39*/with (foo.bar) {
/////*40*/ }
////
/////*41*/ while (false) {
/////*42*/ }
////
/////*43*/ do {
/////*44*/ } while (false);
////
/////*45*/ switch (foo.bar) {
/////*46*/ }
////
/////*47*/ switch (foo.bar) {
/////*48*/ case 1:
/////*49*/ break;
/////*50*/ default:
/////*51*/ break;
/////*52*/ }
/////*53*/ }
////
////
/////*54*/ function tryCatch() {
/////*55*/try {
/////*56*/}
/////*57*/catch (err) {
/////*58*/ }
/////*59*/ }
////
////
/////*60*/ function tryFinally() {
/////*61*/ try {
/////*62*/ }
/////*63*/ finally {
/////*64*/ }
/////*65*/ }
////
////
/////*66*/ function tryCatchFinally() {
/////*67*/ try {
/////*68*/ }
/////*69*/ catch (err) {
/////*70*/ }
/////*71*/ finally {
/////*72*/ }
/////*73*/ }
////
////
/////*74*/ class indentBeforeCurly
/////*75*/ {
/////*76*/ }
////
////
/////*77*/ function argumentsListIndentation(bar,
/////*78*/ blah,
/////*79*/ );
////
////
/////*80*/ function blockIndentAfterIndentedParameter1(bar,
/////*81*/ blah) {
/////*82*/ }
////
////
/////*83*/ function blockIndentAfterIndentedParameter2(bar,
/////*84*/ blah) {
/////*85*/ if (foo) {
/////*86*/ }
/////*87*/}
////
/////*88*/ var templateLiterals = `abcdefghi
/////*89*/jklmnop
/////*90*/qrstuvwxyz`;
var originalOptions = format.copyFormatOptions();
var copy = format.copyFormatOptions();
copy.BaseIndentSize = 10;
copy.IndentSize = 4;
format.setFormatOptions(copy);
format.document();
verify.currentFileContentIs(`
module classes {
class Bar {
constructor() {
}
private foo: string = "";
private f() {
var a: any[] = [[1, 2], [3, 4], 5];
return ((1 + 1));
}
private f2() {
if (true) { } { };
}
}
}
module interfaces {
interface Foo {
x: number;
foo(): number;
}
}
module nestedModules {
module Foo2 {
function f() {
}
var x: number;
}
}
module Enums {
enum Foo3 {
val1,
val2,
}
}
function controlStatements() {
for (var i = 0; i < 10; i++) {
}
for (var e in foo.bar) {
}
with (foo.bar) {
}
while (false) {
}
do {
} while (false);
switch (foo.bar) {
}
switch (foo.bar) {
case 1:
break;
default:
break;
}
}
function tryCatch() {
try {
}
catch (err) {
}
}
function tryFinally() {
try {
}
finally {
}
}
function tryCatchFinally() {
try {
}
catch (err) {
}
finally {
}
}
class indentBeforeCurly {
}
function argumentsListIndentation(bar,
blah,
);
function blockIndentAfterIndentedParameter1(bar,
blah) {
}
function blockIndentAfterIndentedParameter2(bar,
blah) {
if (foo) {
}
}
var templateLiterals = \`abcdefghi
jklmnop
qrstuvwxyz\`;`);
format.setFormatOptions(originalOptions);

View File

@@ -68,6 +68,7 @@ declare namespace FourSlashInterface {
data?: any;
}
interface EditorOptions {
BaseIndentSize?: number,
IndentSize: number;
TabSize: number;
NewLineCharacter: string;
@@ -84,7 +85,7 @@ declare namespace FourSlashInterface {
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
PlaceOpenBraceOnNewLineForFunctions: boolean;
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
[s: string]: boolean | number | string;
[s: string]: boolean | number | string | undefined;
}
interface Range {
fileName: string;
@@ -141,7 +142,7 @@ declare namespace FourSlashInterface {
assertHasRanges(ranges: Range[]): void;
caretAtMarker(markerName?: string): void;
indentationIs(numberOfSpaces: number): void;
indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle): void;
indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle, baseIndentSize?: number): void;
textAtCaretIs(text: string): void;
/**
* Compiles the current file and evaluates 'expr' in a context containing

View File

@@ -0,0 +1,211 @@
/// <reference path="fourslash.ts"/>
////
////{| "indent": 10 , "baseIndentSize": 10 |}
//// module classes {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// class Bar {
////{| "indent": 18 , "baseIndentSize": 10 |}
////
//// constructor() {
////{| "indent": 22, "baseIndentSize": 10 |}
//// }
////
//// private foo: string = "";
////{| "indent": 18, "baseIndentSize": 10 |}
////
//// private f() {
//// var a: any[] = [[1, 2], [3, 4], 5];
////{| "indent": 22, "baseIndentSize": 10 |}
//// return ((1 + 1));
//// }
////
////{| "indent": 18, "baseIndentSize": 10 |}
//// private f2() {
//// if (true) { } { };
//// }
//// }
//// }
////
////
//// module interfaces {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// interface Foo {
////{| "indent": 18 , "baseIndentSize": 10 |}
////
//// x: number;
////{| "indent": 18 , "baseIndentSize": 10 |}
////
//// foo(): number;
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
//// }
////
////
//// module nestedModules {
//// module Foo2 {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// function f() {
//// }
////{| "indent": 18 , "baseIndentSize": 10 |}
//// var x: number;
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
//// }
////
////
//// module Enums {
//// enum Foo3 {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// val1,
////{| "indent": 18 , "baseIndentSize": 10 |}
//// val2,
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// }
////
////
//// function controlStatements() {
//// for (var i = 0; i < 10; i++) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////
//// for (var e in foo.bar) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////
//// with (foo.bar) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////
//// while (false) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////
//// do {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// } while (false);
////
//// switch (foo.bar) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////
//// switch (foo.bar) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// case 1:
////{| "indent": 22 , "baseIndentSize": 10 |}
//// break;
//// default:
////{| "indent": 22 , "baseIndentSize": 10 |}
//// break;
//// }
//// }
////
////
//// function tryCatch() {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// try {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// catch (err) {
////{| "indent": 18, "baseIndentSize": 10 |}
//// }
////{| "indent": 14, "baseIndentSize": 10 |}
//// }
////
////
//// function tryFinally() {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// try {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// finally {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// }
////
////
//// function tryCatchFinally() {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// try {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// catch (err) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// finally {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////{| "indent": 14 , "baseIndentSize": 10 |}
//// }
////
////
//// class indentBeforeCurly
////{| "indent": 10 , "baseIndentSize": 10 |}
////{| "indent": 10 , "baseIndentSize": 10 |}
//// {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// }
////{| "indent": 10 , "baseIndentSize": 10 |}
////
////
//// function argumentsListIndentation(bar,
//// blah,
//// {| "indent": 14 , "baseIndentSize": 10 |}
//// );
////
////
//// function blockIndentAfterIndentedParameter1(bar,
//// blah) {
////{| "indent": 14 , "baseIndentSize": 10 |}
//// }
////
////
//// function blockIndentAfterIndentedParameter2(bar,
//// blah) {
//// if (foo) {
////{| "indent": 18 , "baseIndentSize": 10 |}
//// }
////}
////{| "indent": 10 , "baseIndentSize": 10 |}
////
//// var templateLiterals = `abcdefghi
////{| "indent": 0 , "baseIndentSize": 10 |}
////jklmnop
////{| "indent": 0 , "baseIndentSize": 10 |}
////qrstuvwxyz`;
////{| "indent": 10 , "baseIndentSize": 10 |}
////
////
//// module changeBaseIndentSizeInSameFile {
////{| "indent": 21 , "baseIndentSize": 17 |}
//// interface Foo {
////{| "indent": 25 , "baseIndentSize": 17 |}
////
//// x: number;
////{| "indent": 25 , "baseIndentSize": 10 |}
////
//// foo(): number;
////{| "indent": 25 , "baseIndentSize": 10 |}
//// }
////{| "indent": 21 , "baseIndentSize": 10 |}
//// }
////{| "indent": 17 , "baseIndentSize": 17 |}
////
////
////// Note: Do not add more tests at the end of this file, as
////// the purpose of this test is to verify smart indent
////// works for unterminated function arguments at the end of a file.
//// function unterminatedListIndentation(a,
////{| "indent": 14 , "baseIndentSize": 10 |}
debugger;
test.markers().forEach(marker => {
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent, ts.IndentStyle.Smart, marker.data.baseIndentSize);
});