diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 1711beeab58..f32740e89cc 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -103,11 +103,11 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { const visibleStartLineNumber = ctx.visibleRange.startLineNumber; const visibleEndLineNumber = ctx.visibleRange.endLineNumber; - const tabSize = this._context.model.getTabSize(); - const tabWidth = tabSize * this._spaceWidth; + const indentSize = this._context.model.getIndentSize(); + const indentWidth = indentSize * this._spaceWidth; const scrollWidth = ctx.scrollWidth; const lineHeight = this._lineHeight; - const indentGuideWidth = tabWidth; + const indentGuideWidth = indentWidth; const indents = this._context.model.getLinesIndentGuides(visibleStartLineNumber, visibleEndLineNumber); @@ -133,7 +133,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { for (let i = 1; i <= indent; i++) { let className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); result += `
`; - left += tabWidth; + left += indentWidth; if (left > scrollWidth) { break; } diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index fdc0a21e052..45dccb004cb 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -14,31 +14,31 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo export interface IShiftCommandOpts { isUnshift: boolean; - tabSize: number; + indentSize: number; oneIndent: string; useTabStops: boolean; } export class ShiftCommand implements ICommand { - public static unshiftIndentCount(line: string, column: number, tabSize: number): number { + public static unshiftIndentCount(line: string, column: number, indentSize: number): number { // Determine the visible column where the content starts - let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize); + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, indentSize); - let desiredTabStop = CursorColumns.prevTabStop(contentStartVisibleColumn, tabSize); + let desiredTabStop = CursorColumns.prevTabStop(contentStartVisibleColumn, indentSize); - // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents - return desiredTabStop / tabSize; + // The `desiredTabStop` is a multiple of `indentSize` => determine the number of indents + return desiredTabStop / indentSize; } - public static shiftIndentCount(line: string, column: number, tabSize: number): number { + public static shiftIndentCount(line: string, column: number, indentSize: number): number { // Determine the visible column where the content starts - let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize); + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, indentSize); - let desiredTabStop = CursorColumns.nextTabStop(contentStartVisibleColumn, tabSize); + let desiredTabStop = CursorColumns.nextTabStop(contentStartVisibleColumn, indentSize); - // The `desiredTabStop` is a multiple of `tabSize` => determine the number of indents - return desiredTabStop / tabSize; + // The `desiredTabStop` is a multiple of `indentSize` => determine the number of indents + return desiredTabStop / indentSize; } private _opts: IShiftCommandOpts; @@ -70,7 +70,7 @@ export class ShiftCommand implements ICommand { endLine = endLine - 1; } - const tabSize = this._opts.tabSize; + const indentSize = this._opts.indentSize; const oneIndent = this._opts.oneIndent; const shouldIndentEmptyLines = (startLine === endLine); @@ -108,8 +108,8 @@ export class ShiftCommand implements ICommand { } if (lineNumber > 1) { - let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(lineText, indentationEndIndex + 1, tabSize); - if (contentStartVisibleColumn % tabSize !== 0) { + let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(lineText, indentationEndIndex + 1, indentSize); + if (contentStartVisibleColumn % indentSize !== 0) { // The current line is "miss-aligned", so let's see if this is expected... // This can only happen when it has trailing commas in the indent if (model.isCheapToTokenize(lineNumber - 1)) { @@ -117,7 +117,7 @@ export class ShiftCommand implements ICommand { if (enterAction) { extraSpaces = previousLineExtraSpaces; if (enterAction.appendText) { - for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) { + for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < indentSize; j++) { if (enterAction.appendText.charCodeAt(j) === CharCode.Space) { extraSpaces++; } else { @@ -149,9 +149,9 @@ export class ShiftCommand implements ICommand { let desiredIndentCount: number; if (this._opts.isUnshift) { - desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, tabSize); + desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, indentSize); } else { - desiredIndentCount = ShiftCommand.shiftIndentCount(lineText, indentationEndIndex + 1, tabSize); + desiredIndentCount = ShiftCommand.shiftIndentCount(lineText, indentationEndIndex + 1, indentSize); } // Fill `indents`, as needed @@ -193,7 +193,7 @@ export class ShiftCommand implements ICommand { if (this._opts.isUnshift) { - indentationEndIndex = Math.min(indentationEndIndex, tabSize); + indentationEndIndex = Math.min(indentationEndIndex, indentSize); for (let i = 0; i < indentationEndIndex; i++) { const chr = lineText.charCodeAt(i); if (chr === CharCode.Tab) { diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 1455e28e711..5ee01a35306 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -288,6 +288,20 @@ const editorConfiguration: IConfigurationNode = { 'minimum': 1, 'markdownDescription': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") }, + 'editor.indentSize': { + 'anyOf': [ + { + 'type': 'string', + 'enum': ['tab'] + }, + { + 'type': 'number', + 'minimum': 1 + } + ], + 'default': 'tab', + 'markdownDescription': nls.localize('indentSize', "The number of spaces used for indentation or 'tab' to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") + }, 'editor.insertSpaces': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 935e403f14c..f4f23181cd1 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2541,6 +2541,7 @@ export const EDITOR_FONT_DEFAULTS = { */ export const EDITOR_MODEL_DEFAULTS = { tabSize: 4, + indentSize: 4, insertSpaces: true, detectIndentation: true, trimAutoWhitespace: true, diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 99deb5a4635..3e052efc25e 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -73,7 +73,7 @@ export class CursorConfiguration { _cursorMoveConfigurationBrand: void; public readonly readOnly: boolean; - public readonly tabSize: number; + public readonly indentSize: number; public readonly insertSpaces: boolean; public readonly oneIndent: string; public readonly pageSize: number; @@ -121,7 +121,7 @@ export class CursorConfiguration { let c = configuration.editor; this.readOnly = c.readOnly; - this.tabSize = modelOptions.tabSize; + this.indentSize = modelOptions.indentSize; this.insertSpaces = modelOptions.insertSpaces; this.oneIndent = oneIndent; this.pageSize = Math.max(1, Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2); @@ -176,7 +176,7 @@ export class CursorConfiguration { } public normalizeIndentation(str: string): string { - return TextModel.normalizeIndentation(str, this.tabSize, this.insertSpaces); + return TextModel.normalizeIndentation(str, this.indentSize, this.insertSpaces); } private static _getElectricCharacters(languageIdentifier: LanguageIdentifier): string[] | null { @@ -508,7 +508,7 @@ export class CursorColumns { return this.isHighSurrogate(model, lineNumber, column - 2); } - public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number { + public static visibleColumnFromColumn(lineContent: string, column: number, indentSize: number): number { let endOffset = lineContent.length; if (endOffset > column - 1) { endOffset = column - 1; @@ -518,7 +518,7 @@ export class CursorColumns { for (let i = 0; i < endOffset; i++) { let charCode = lineContent.charCodeAt(i); if (charCode === CharCode.Tab) { - result = this.nextTabStop(result, tabSize); + result = this.nextTabStop(result, indentSize); } else if (strings.isFullWidthCharacter(charCode)) { result = result + 2; } else { @@ -529,10 +529,10 @@ export class CursorColumns { } public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number { - return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize); + return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.indentSize); } - public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number { + public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, indentSize: number): number { if (visibleColumn <= 0) { return 1; } @@ -545,7 +545,7 @@ export class CursorColumns { let afterVisibleColumn: number; if (charCode === CharCode.Tab) { - afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, tabSize); + afterVisibleColumn = this.nextTabStop(beforeVisibleColumn, indentSize); } else if (strings.isFullWidthCharacter(charCode)) { afterVisibleColumn = beforeVisibleColumn + 2; } else { @@ -570,7 +570,7 @@ export class CursorColumns { } public static columnFromVisibleColumn2(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, visibleColumn: number): number { - let result = this.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.tabSize); + let result = this.columnFromVisibleColumn(model.getLineContent(lineNumber), visibleColumn, config.indentSize); let minColumn = model.getLineMinColumn(lineNumber); if (result < minColumn) { @@ -588,15 +588,15 @@ export class CursorColumns { /** * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) */ - public static nextTabStop(visibleColumn: number, tabSize: number): number { - return visibleColumn + tabSize - visibleColumn % tabSize; + public static nextTabStop(visibleColumn: number, indentSize: number): number { + return visibleColumn + indentSize - visibleColumn % indentSize; } /** * ATTENTION: This works with 0-based columns (as oposed to the regular 1-based columns) */ - public static prevTabStop(column: number, tabSize: number): number { - return column - 1 - (column - 1) % tabSize; + public static prevTabStop(column: number, indentSize: number): number { + return column - 1 - (column - 1) % indentSize; } } diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 799a719c485..45f83879e26 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -131,7 +131,7 @@ export class DeleteOperations { if (position.column <= lastIndentationColumn) { let fromVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, position); - let toVisibleColumn = CursorColumns.prevTabStop(fromVisibleColumn, config.tabSize); + let toVisibleColumn = CursorColumns.prevTabStop(fromVisibleColumn, config.indentSize); let toColumn = CursorColumns.columnFromVisibleColumn2(config, model, position.lineNumber, toVisibleColumn); deleteSelection = new Range(position.lineNumber, toColumn, position.lineNumber, position.column); } else { diff --git a/src/vs/editor/common/controller/cursorMoveOperations.ts b/src/vs/editor/common/controller/cursorMoveOperations.ts index 65ad93791b8..bcab46d7757 100644 --- a/src/vs/editor/common/controller/cursorMoveOperations.ts +++ b/src/vs/editor/common/controller/cursorMoveOperations.ts @@ -92,7 +92,7 @@ export class MoveOperations { } public static down(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnLastLine: boolean): CursorPosition { - const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.indentSize) + leftoverVisibleColumns; lineNumber = lineNumber + count; let lineCount = model.getLineCount(); @@ -113,7 +113,7 @@ export class MoveOperations { } } - leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.indentSize); return new CursorPosition(lineNumber, column, leftoverVisibleColumns); } @@ -151,7 +151,7 @@ export class MoveOperations { } public static up(config: CursorConfiguration, model: ICursorSimpleModel, lineNumber: number, column: number, leftoverVisibleColumns: number, count: number, allowMoveOnFirstLine: boolean): CursorPosition { - const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize) + leftoverVisibleColumns; + const currentVisibleColumn = CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.indentSize) + leftoverVisibleColumns; lineNumber = lineNumber - count; if (lineNumber < 1) { @@ -171,7 +171,7 @@ export class MoveOperations { } } - leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.tabSize); + leftoverVisibleColumns = currentVisibleColumn - CursorColumns.visibleColumnFromColumn(model.getLineContent(lineNumber), column, config.indentSize); return new CursorPosition(lineNumber, column, leftoverVisibleColumns); } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index ac585844d86..366825b3a73 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -30,7 +30,7 @@ export class TypeOperations { for (let i = 0, len = selections.length; i < len; i++) { commands[i] = new ShiftCommand(selections[i], { isUnshift: false, - tabSize: config.tabSize, + indentSize: config.indentSize, oneIndent: config.oneIndent, useTabStops: config.useTabStops }); @@ -43,7 +43,7 @@ export class TypeOperations { for (let i = 0, len = selections.length; i < len; i++) { commands[i] = new ShiftCommand(selections[i], { isUnshift: true, - tabSize: config.tabSize, + indentSize: config.indentSize, oneIndent: config.oneIndent, useTabStops: config.useTabStops }); @@ -53,7 +53,7 @@ export class TypeOperations { public static shiftIndent(config: CursorConfiguration, indentation: string, count?: number): string { count = count || 1; - let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, config.tabSize); + let desiredIndentCount = ShiftCommand.shiftIndentCount(indentation, indentation.length + count, config.indentSize); let newIndentation = ''; for (let i = 0; i < desiredIndentCount; i++) { newIndentation += '\t'; @@ -64,7 +64,7 @@ export class TypeOperations { public static unshiftIndent(config: CursorConfiguration, indentation: string, count?: number): string { count = count || 1; - let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, config.tabSize); + let desiredIndentCount = ShiftCommand.unshiftIndentCount(indentation, indentation.length + count, config.indentSize); let newIndentation = ''; for (let i = 0; i < desiredIndentCount; i++) { newIndentation += '\t'; @@ -209,8 +209,8 @@ export class TypeOperations { let position = selection.getStartPosition(); if (config.insertSpaces) { let visibleColumnFromColumn = CursorColumns.visibleColumnFromColumn2(config, model, position); - let tabSize = config.tabSize; - let spacesCnt = tabSize - (visibleColumnFromColumn % tabSize); + let indentSize = config.indentSize; + let spacesCnt = indentSize - (visibleColumnFromColumn % indentSize); for (let i = 0; i < spacesCnt; i++) { typeText += ' '; } @@ -253,7 +253,7 @@ export class TypeOperations { commands[i] = new ShiftCommand(selection, { isUnshift: false, - tabSize: config.tabSize, + indentSize: config.indentSize, oneIndent: config.oneIndent, useTabStops: config.useTabStops }); @@ -377,7 +377,7 @@ export class TypeOperations { let offset = 0; if (oldEndColumn <= firstNonWhitespace + 1) { if (!config.insertSpaces) { - oldEndViewColumn = Math.ceil(oldEndViewColumn / config.tabSize); + oldEndViewColumn = Math.ceil(oldEndViewColumn / config.indentSize); } offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0); } diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 9d9a1a7d8c4..69ac87557ab 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -346,6 +346,7 @@ export class TextModelResolvedOptions { _textModelResolvedOptionsBrand: void; readonly tabSize: number; + readonly indentSize: number; readonly insertSpaces: boolean; readonly defaultEOL: DefaultEndOfLine; readonly trimAutoWhitespace: boolean; @@ -355,11 +356,13 @@ export class TextModelResolvedOptions { */ constructor(src: { tabSize: number; + indentSize: number; insertSpaces: boolean; defaultEOL: DefaultEndOfLine; trimAutoWhitespace: boolean; }) { this.tabSize = src.tabSize | 0; + this.indentSize = src.indentSize | 0; this.insertSpaces = Boolean(src.insertSpaces); this.defaultEOL = src.defaultEOL | 0; this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); @@ -383,6 +386,7 @@ export class TextModelResolvedOptions { public createChangeEvent(newOpts: TextModelResolvedOptions): IModelOptionsChangedEvent { return { tabSize: this.tabSize !== newOpts.tabSize, + indentSize: this.indentSize !== newOpts.indentSize, insertSpaces: this.insertSpaces !== newOpts.insertSpaces, trimAutoWhitespace: this.trimAutoWhitespace !== newOpts.trimAutoWhitespace, }; @@ -394,6 +398,7 @@ export class TextModelResolvedOptions { */ export interface ITextModelCreationOptions { tabSize: number; + indentSize: number; insertSpaces: boolean; detectIndentation: boolean; trimAutoWhitespace: boolean; @@ -404,6 +409,7 @@ export interface ITextModelCreationOptions { export interface ITextModelUpdateOptions { tabSize?: number; + indentSize?: number; insertSpaces?: boolean; trimAutoWhitespace?: boolean; } diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 3a0d722fdea..99acfd8b156 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -163,6 +163,7 @@ export class TextModel extends Disposable implements model.ITextModel { public static DEFAULT_CREATION_OPTIONS: model.ITextModelCreationOptions = { isForSimpleWidget: false, tabSize: EDITOR_MODEL_DEFAULTS.tabSize, + indentSize: EDITOR_MODEL_DEFAULTS.indentSize, insertSpaces: EDITOR_MODEL_DEFAULTS.insertSpaces, detectIndentation: false, defaultEOL: model.DefaultEndOfLine.LF, @@ -179,6 +180,7 @@ export class TextModel extends Disposable implements model.ITextModel { const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces); return new model.TextModelResolvedOptions({ tabSize: guessedIndentation.tabSize, + indentSize: guessedIndentation.tabSize, // TODO: guess indentSize independent of tabSize insertSpaces: guessedIndentation.insertSpaces, trimAutoWhitespace: options.trimAutoWhitespace, defaultEOL: options.defaultEOL @@ -187,6 +189,7 @@ export class TextModel extends Disposable implements model.ITextModel { return new model.TextModelResolvedOptions({ tabSize: options.tabSize, + indentSize: options.indentSize, insertSpaces: options.insertSpaces, trimAutoWhitespace: options.trimAutoWhitespace, defaultEOL: options.defaultEOL @@ -590,11 +593,13 @@ export class TextModel extends Disposable implements model.ITextModel { public updateOptions(_newOpts: model.ITextModelUpdateOptions): void { this._assertNotDisposed(); let tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize; + let indentSize = (typeof _newOpts.indentSize !== 'undefined') ? _newOpts.indentSize : this._options.indentSize; let insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces; let trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace; let newOpts = new model.TextModelResolvedOptions({ tabSize: tabSize, + indentSize: indentSize, insertSpaces: insertSpaces, defaultEOL: this._options.defaultEOL, trimAutoWhitespace: trimAutoWhitespace @@ -660,12 +665,12 @@ export class TextModel extends Disposable implements model.ITextModel { public getOneIndent(): string { this._assertNotDisposed(); - let tabSize = this._options.tabSize; + let indentSize = this._options.indentSize; let insertSpaces = this._options.insertSpaces; if (insertSpaces) { let result = ''; - for (let i = 0; i < tabSize; i++) { + for (let i = 0; i < indentSize; i++) { result += ' '; } return result; @@ -2574,7 +2579,7 @@ export class TextModel extends Disposable implements model.ITextModel { // Use the line's indent up_belowContentLineIndex = upLineNumber - 1; up_belowContentLineIndent = currentIndent; - upLineIndentLevel = Math.ceil(currentIndent / this._options.tabSize); + upLineIndentLevel = Math.ceil(currentIndent / this._options.indentSize); } else { up_resolveIndents(upLineNumber); upLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, up_aboveContentLineIndent, up_belowContentLineIndent); @@ -2609,7 +2614,7 @@ export class TextModel extends Disposable implements model.ITextModel { // Use the line's indent down_aboveContentLineIndex = downLineNumber - 1; down_aboveContentLineIndent = currentIndent; - downLineIndentLevel = Math.ceil(currentIndent / this._options.tabSize); + downLineIndentLevel = Math.ceil(currentIndent / this._options.indentSize); } else { down_resolveIndents(downLineNumber); downLineIndentLevel = this._getIndentLevelForWhitespaceLine(offSide, down_aboveContentLineIndent, down_belowContentLineIndent); @@ -2657,7 +2662,7 @@ export class TextModel extends Disposable implements model.ITextModel { // Use the line's indent aboveContentLineIndex = lineNumber - 1; aboveContentLineIndent = currentIndent; - result[resultIndex] = Math.ceil(currentIndent / this._options.tabSize); + result[resultIndex] = Math.ceil(currentIndent / this._options.indentSize); continue; } @@ -2704,20 +2709,20 @@ export class TextModel extends Disposable implements model.ITextModel { } else if (aboveContentLineIndent < belowContentLineIndent) { // we are inside the region above - return (1 + Math.floor(aboveContentLineIndent / this._options.tabSize)); + return (1 + Math.floor(aboveContentLineIndent / this._options.indentSize)); } else if (aboveContentLineIndent === belowContentLineIndent) { // we are in between two regions - return Math.ceil(belowContentLineIndent / this._options.tabSize); + return Math.ceil(belowContentLineIndent / this._options.indentSize); } else { if (offSide) { // same level as region below - return Math.ceil(belowContentLineIndent / this._options.tabSize); + return Math.ceil(belowContentLineIndent / this._options.indentSize); } else { // we are inside the region that ends below - return (1 + Math.floor(belowContentLineIndent / this._options.tabSize)); + return (1 + Math.floor(belowContentLineIndent / this._options.indentSize)); } } diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index 458566e1014..fc84cb84f93 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -97,6 +97,7 @@ export interface IModelTokensChangedEvent { export interface IModelOptionsChangedEvent { readonly tabSize: boolean; + readonly indentSize: boolean; readonly insertSpaces: boolean; readonly trimAutoWhitespace: boolean; } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 183f659fc29..3c546e759d1 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -73,6 +73,7 @@ class ModelData implements IDisposable { interface IRawEditorConfig { tabSize?: any; + indentSize?: any; insertSpaces?: any; detectIndentation?: any; trimAutoWhitespace?: any; @@ -138,6 +139,17 @@ export class ModelServiceImpl extends Disposable implements IModelService { } } + let indentSize = tabSize; + if (config.editor && typeof config.editor.indentSize !== 'undefined' && config.editor.indentSize !== 'tab') { + let parsedIndentSize = parseInt(config.editor.indentSize, 10); + if (!isNaN(parsedIndentSize)) { + indentSize = parsedIndentSize; + } + if (indentSize < 1) { + indentSize = 1; + } + } + let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces; if (config.editor && typeof config.editor.insertSpaces !== 'undefined') { insertSpaces = (config.editor.insertSpaces === 'false' ? false : Boolean(config.editor.insertSpaces)); @@ -169,6 +181,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { return { isForSimpleWidget: isForSimpleWidget, tabSize: tabSize, + indentSize: indentSize, insertSpaces: insertSpaces, detectIndentation: detectIndentation, defaultEOL: newDefaultEOL, @@ -209,6 +222,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { if (currentOptions && (currentOptions.detectIndentation === newOptions.detectIndentation) && (currentOptions.insertSpaces === newOptions.insertSpaces) + && (currentOptions.indentSize === newOptions.indentSize) && (currentOptions.tabSize === newOptions.tabSize) && (currentOptions.trimAutoWhitespace === newOptions.trimAutoWhitespace) ) { @@ -224,6 +238,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { } else { model.updateOptions({ insertSpaces: newOptions.insertSpaces, + indentSize: newOptions.indentSize, tabSize: newOptions.tabSize, trimAutoWhitespace: newOptions.trimAutoWhitespace }); diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 3c3dffa41d5..82840e91b88 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -130,6 +130,7 @@ export interface IViewModel { getCompletelyVisibleViewRangeAtScrollTop(scrollTop: number): Range; getTabSize(): number; + getIndentSize(): number; getLineCount(): number; getLineContent(lineNumber: number): string; getLineLength(lineNumber: number): number; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 9681d2164b7..99fb8ab7d6f 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -452,6 +452,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.model.getOptions().tabSize; } + public getIndentSize(): number { + return this.model.getOptions().indentSize; + } + public getLineCount(): number { return this.lines.getViewLineCount(); } diff --git a/src/vs/editor/test/browser/commands/shiftCommand.test.ts b/src/vs/editor/test/browser/commands/shiftCommand.test.ts index 4c187481918..1de29be22fc 100644 --- a/src/vs/editor/test/browser/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/browser/commands/shiftCommand.test.ts @@ -46,7 +46,7 @@ class DocBlockCommentMode extends MockMode { function testShiftCommand(lines: string[], languageIdentifier: LanguageIdentifier | null, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void { testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, { isUnshift: false, - tabSize: 4, + indentSize: 4, oneIndent: '\t', useTabStops: useTabStops, }), expectedLines, expectedSelection); @@ -55,7 +55,7 @@ function testShiftCommand(lines: string[], languageIdentifier: LanguageIdentifie function testUnshiftCommand(lines: string[], languageIdentifier: LanguageIdentifier | null, useTabStops: boolean, selection: Selection, expectedLines: string[], expectedSelection: Selection): void { testCommand(lines, languageIdentifier, selection, (sel) => new ShiftCommand(sel, { isUnshift: true, - tabSize: 4, + indentSize: 4, oneIndent: '\t', useTabStops: useTabStops, }), expectedLines, expectedSelection); @@ -667,7 +667,7 @@ suite('Editor Commands - ShiftCommand', () => { new Selection(1, 1, 13, 1), (sel) => new ShiftCommand(sel, { isUnshift: false, - tabSize: 4, + indentSize: 4, oneIndent: ' ', useTabStops: false }), @@ -711,7 +711,7 @@ suite('Editor Commands - ShiftCommand', () => { new Selection(1, 1, 13, 1), (sel) => new ShiftCommand(sel, { isUnshift: true, - tabSize: 4, + indentSize: 4, oneIndent: ' ', useTabStops: false }), @@ -755,7 +755,7 @@ suite('Editor Commands - ShiftCommand', () => { new Selection(1, 1, 13, 1), (sel) => new ShiftCommand(sel, { isUnshift: true, - tabSize: 4, + indentSize: 4, oneIndent: '\t', useTabStops: false }), @@ -799,7 +799,7 @@ suite('Editor Commands - ShiftCommand', () => { new Selection(1, 1, 13, 1), (sel) => new ShiftCommand(sel, { isUnshift: true, - tabSize: 4, + indentSize: 4, oneIndent: ' ', useTabStops: false }), @@ -832,7 +832,7 @@ suite('Editor Commands - ShiftCommand', () => { new Selection(1, 1, 1, 13), (sel) => new ShiftCommand(sel, { isUnshift: false, - tabSize: 4, + indentSize: 4, oneIndent: '\t', useTabStops: true }), @@ -854,31 +854,31 @@ suite('Editor Commands - ShiftCommand', () => { return r; }; - let testOutdent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { + let testOutdent = (indentSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { let expectedIndent = repeatStr(oneIndent, expectedIndents); if (lineText.length > 0) { - _assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); + _assertUnshiftCommand(indentSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); } else { - _assertUnshiftCommand(tabSize, oneIndent, [lineText + 'aaa'], []); + _assertUnshiftCommand(indentSize, oneIndent, [lineText + 'aaa'], []); } }; - let testIndent = (tabSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { + let testIndent = (indentSize: number, oneIndent: string, lineText: string, expectedIndents: number) => { let expectedIndent = repeatStr(oneIndent, expectedIndents); - _assertShiftCommand(tabSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); + _assertShiftCommand(indentSize, oneIndent, [lineText + 'aaa'], [createSingleEditOp(expectedIndent, 1, 1, 1, lineText.length + 1)]); }; - let testIndentation = (tabSize: number, lineText: string, expectedOnOutdent: number, expectedOnIndent: number) => { + let testIndentation = (indentSize: number, lineText: string, expectedOnOutdent: number, expectedOnIndent: number) => { let spaceIndent = ''; - for (let i = 0; i < tabSize; i++) { + for (let i = 0; i < indentSize; i++) { spaceIndent += ' '; } - testOutdent(tabSize, spaceIndent, lineText, expectedOnOutdent); - testOutdent(tabSize, '\t', lineText, expectedOnOutdent); + testOutdent(indentSize, spaceIndent, lineText, expectedOnOutdent); + testOutdent(indentSize, '\t', lineText, expectedOnOutdent); - testIndent(tabSize, spaceIndent, lineText, expectedOnIndent); - testIndent(tabSize, '\t', lineText, expectedOnIndent); + testIndent(indentSize, spaceIndent, lineText, expectedOnIndent); + testIndent(indentSize, '\t', lineText, expectedOnIndent); }; // insertSpaces: true @@ -940,11 +940,11 @@ suite('Editor Commands - ShiftCommand', () => { // 3 => 2 testIndentation(4, ' ', 2, 3); - function _assertUnshiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { + function _assertUnshiftCommand(indentSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { return withEditorModel(text, (model) => { let op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), { isUnshift: true, - tabSize: tabSize, + indentSize: indentSize, oneIndent: oneIndent, useTabStops: true }); @@ -953,11 +953,11 @@ suite('Editor Commands - ShiftCommand', () => { }); } - function _assertShiftCommand(tabSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { + function _assertShiftCommand(indentSize: number, oneIndent: string, text: string[], expected: IIdentifiedSingleEditOperation[]): void { return withEditorModel(text, (model) => { let op = new ShiftCommand(new Selection(1, 1, text.length + 1, 1), { isUnshift: false, - tabSize: tabSize, + indentSize: indentSize, oneIndent: oneIndent, useTabStops: true }); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 5276a15c3bd..d34c9ff1b1f 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -2212,6 +2212,7 @@ suite('Editor Controller - Cursor Configuration', () => { ].join('\n'), { tabSize: 13, + indentSize: 13, } ); @@ -3122,7 +3123,10 @@ suite('Editor Controller - Indentation Rules', () => { '}a}' ], languageIdentifier: mode.getLanguageIdentifier(), - modelOpts: { tabSize: 2 } + modelOpts: { + tabSize: 2, + indentSize: 2 + } }, (model, cursor) => { moveTo(cursor, 3, 3, false); assertCursor(cursor, new Selection(3, 3, 3, 3)); @@ -3594,7 +3598,10 @@ suite('Editor Controller - Indentation Rules', () => { '', ')', ].join('\n'), - { tabSize: 2 }, + { + tabSize: 2, + indentSize: 2 + }, mode.getLanguageIdentifier() ); diff --git a/src/vs/editor/test/common/editorTestUtils.ts b/src/vs/editor/test/common/editorTestUtils.ts index 6fae62d5463..3116fa31171 100644 --- a/src/vs/editor/test/common/editorTestUtils.ts +++ b/src/vs/editor/test/common/editorTestUtils.ts @@ -16,6 +16,7 @@ export function withEditorModel(text: string[], callback: (model: TextModel) => export interface IRelaxedTextModelCreationOptions { tabSize?: number; + indentSize?: number; insertSpaces?: boolean; detectIndentation?: boolean; trimAutoWhitespace?: boolean; @@ -27,6 +28,7 @@ export interface IRelaxedTextModelCreationOptions { export function createTextModel(text: string, _options: IRelaxedTextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel { const options: ITextModelCreationOptions = { tabSize: (typeof _options.tabSize === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.tabSize : _options.tabSize), + indentSize: (typeof _options.indentSize === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.indentSize : _options.indentSize), insertSpaces: (typeof _options.insertSpaces === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.insertSpaces : _options.insertSpaces), detectIndentation: (typeof _options.detectIndentation === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.detectIndentation : _options.detectIndentation), trimAutoWhitespace: (typeof _options.trimAutoWhitespace === 'undefined' ? TextModel.DEFAULT_CREATION_OPTIONS.trimAutoWhitespace : _options.trimAutoWhitespace), diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 22e1c73ae83..4a3d33f9b27 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1421,6 +1421,7 @@ declare namespace monaco.editor { export class TextModelResolvedOptions { _textModelResolvedOptionsBrand: void; readonly tabSize: number; + readonly indentSize: number; readonly insertSpaces: boolean; readonly defaultEOL: DefaultEndOfLine; readonly trimAutoWhitespace: boolean; @@ -1428,6 +1429,7 @@ declare namespace monaco.editor { export interface ITextModelUpdateOptions { tabSize?: number; + indentSize?: number; insertSpaces?: boolean; trimAutoWhitespace?: boolean; } @@ -2278,6 +2280,7 @@ declare namespace monaco.editor { export interface IModelOptionsChangedEvent { readonly tabSize: boolean; + readonly indentSize: boolean; readonly insertSpaces: boolean; readonly trimAutoWhitespace: boolean; } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index fe6540c6291..b5a1007a599 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -95,6 +95,7 @@ const configurationValueWhitelist = [ 'editor.rulers', 'editor.wordSeparators', 'editor.tabSize', + 'editor.indentSize', 'editor.insertSpaces', 'editor.detectIndentation', 'editor.roundedSelection', diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 978c31dd58c..b066cd53ace 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -641,13 +641,22 @@ declare module 'vscode' { /** * The size in spaces a tab takes. This is used for two purposes: * - the rendering width of a tab character; - * - the number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true. + * - the number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true + * and `indentSize` is set to `"tab"`. * * When getting a text editor's options, this property will always be a number (resolved). * When setting a text editor's options, this property is optional and it can be a number or `"auto"`. */ tabSize?: number | string; + /** + * The number of spaces to insert when [insertSpaces](#TextEditorOptions.insertSpaces) is true. + * + * When getting a text editor's options, this property will always be a number (resolved). + * When setting a text editor's options, this property is optional and it can be a number or `"tab"`. + */ + indentSize?: number | string; + /** * When pressing Tab insert [n](#TextEditorOptions.tabSize) spaces. * When getting a text editor's options, this property will always be a boolean (resolved). diff --git a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts index 672b53ed6b9..d7995a87095 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadEditor.ts @@ -83,6 +83,7 @@ export class MainThreadTextEditorProperties { const modelOptions = model.getOptions(); return { insertSpaces: modelOptions.insertSpaces, + indentSize: modelOptions.indentSize, tabSize: modelOptions.tabSize, cursorStyle: cursorStyle, lineNumbers: lineNumbers @@ -166,6 +167,7 @@ export class MainThreadTextEditorProperties { } return ( a.tabSize === b.tabSize + && a.indentSize === b.indentSize && a.insertSpaces === b.insertSpaces && a.cursorStyle === b.cursorStyle && a.lineNumbers === b.lineNumbers @@ -321,10 +323,10 @@ export class MainThreadTextEditor { } private _setIndentConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void { + let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget); + if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') { // one of the options was set to 'auto' => detect indentation - - let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget); let insertSpaces = creationOpts.insertSpaces; let tabSize = creationOpts.tabSize; @@ -347,6 +349,13 @@ export class MainThreadTextEditor { if (typeof newConfiguration.tabSize !== 'undefined') { newOpts.tabSize = newConfiguration.tabSize; } + if (typeof newConfiguration.indentSize !== 'undefined') { + if (newConfiguration.indentSize === 'tab') { + newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize; + } else { + newOpts.indentSize = newConfiguration.indentSize; + } + } this._model.updateOptions(newOpts); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index cfafeed72ac..e0cb988acdb 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -176,6 +176,7 @@ export interface MainThreadDocumentsShape extends IDisposable { export interface ITextEditorConfigurationUpdate { tabSize?: number | 'auto'; + indentSize?: number | 'tab'; insertSpaces?: boolean | 'auto'; cursorStyle?: TextEditorCursorStyle; lineNumbers?: TextEditorLineNumbersStyle; @@ -183,6 +184,7 @@ export interface ITextEditorConfigurationUpdate { export interface IResolvedTextEditorConfiguration { tabSize: number; + indentSize: number; insertSpaces: boolean; cursorStyle: TextEditorCursorStyle; lineNumbers: TextEditorLineNumbersStyle; diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 4c26b539193..18a10252183 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -142,6 +142,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { private _id: string; private _tabSize: number; + private _indentSize: number; private _insertSpaces: boolean; private _cursorStyle: TextEditorCursorStyle; private _lineNumbers: TextEditorLineNumbersStyle; @@ -154,6 +155,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { public _accept(source: IResolvedTextEditorConfiguration): void { this._tabSize = source.tabSize; + this._indentSize = source.indentSize; this._insertSpaces = source.insertSpaces; this._cursorStyle = source.cursorStyle; this._lineNumbers = source.lineNumbers; @@ -200,6 +202,47 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { })); } + public get indentSize(): number | string { + return this._indentSize; + } + + private _validateIndentSize(value: number | string): number | 'tab' | null { + if (value === 'tab') { + return 'tab'; + } + if (typeof value === 'number') { + let r = Math.floor(value); + return (r > 0 ? r : null); + } + if (typeof value === 'string') { + let r = parseInt(value, 10); + if (isNaN(r)) { + return null; + } + return (r > 0 ? r : null); + } + return null; + } + + public set indentSize(value: number | string) { + let indentSize = this._validateIndentSize(value); + if (indentSize === null) { + // ignore invalid call + return; + } + if (typeof indentSize === 'number') { + if (this._indentSize === indentSize) { + // nothing to do + return; + } + // reflect the new indentSize value immediately + this._indentSize = indentSize; + } + warnOnError(this._proxy.$trySetOptions(this._id, { + indentSize: indentSize + })); + } + public get insertSpaces(): boolean | string { return this._insertSpaces; } @@ -273,6 +316,19 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } } + if (typeof newOptions.indentSize !== 'undefined') { + let indentSize = this._validateIndentSize(newOptions.indentSize); + if (indentSize === 'tab') { + hasUpdate = true; + bulkConfigurationUpdate.indentSize = indentSize; + } else if (typeof indentSize === 'number' && this._indentSize !== indentSize) { + // reflect the new indentSize value immediately + this._indentSize = indentSize; + hasUpdate = true; + bulkConfigurationUpdate.indentSize = indentSize; + } + } + if (typeof newOptions.insertSpaces !== 'undefined') { let insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces); if (insertSpaces === 'auto') { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 6d36ad6d9e7..8e27e3a23fd 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -655,7 +655,7 @@ export class EditorStatus implements IStatusbarItem { const modelOpts = model.getOptions(); update.indentation = ( modelOpts.insertSpaces - ? nls.localize('spacesSize', "Spaces: {0}", modelOpts.tabSize) + ? nls.localize('spacesSize', "Spaces: {0}", modelOpts.indentSize) : nls.localize({ key: 'tabSize', comment: ['Tab corresponds to the tab key'] }, "Tab Size: {0}", modelOpts.tabSize) ); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index 87f42a87e02..0ee055ccc23 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -19,7 +19,7 @@ suite('ExtHostTextEditor', () => { ], '\n', 'text', 1, false); setup(() => { - editor = new ExtHostTextEditor(null!, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); + editor = new ExtHostTextEditor(null!, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); }); test('disposed editor', () => { @@ -45,7 +45,7 @@ suite('ExtHostTextEditor', () => { applyCount += 1; return Promise.resolve(true); } - }, 'edt1', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); + }, 'edt1', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); await editor.edit(edit => { }); assert.equal(applyCount, 0); @@ -88,6 +88,7 @@ suite('ExtHostTextEditorOptions', () => { }; opts = new ExtHostTextEditorOptions(mockProxy, '1', { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -102,6 +103,7 @@ suite('ExtHostTextEditorOptions', () => { function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void { let actual = { tabSize: opts.tabSize, + indentSize: opts.indentSize, insertSpaces: opts.insertSpaces, cursorStyle: opts.cursorStyle, lineNumbers: opts.lineNumbers @@ -113,6 +115,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = 4; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -124,6 +127,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = 1; assertState(opts, { tabSize: 1, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -135,6 +139,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = 2.3; assertState(opts, { tabSize: 2, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -146,6 +151,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = '2'; assertState(opts, { tabSize: 2, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -157,6 +163,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = 'auto'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -168,6 +175,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = null!; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -179,6 +187,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = -5; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -190,6 +199,7 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = 'hello'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -201,6 +211,127 @@ suite('ExtHostTextEditorOptions', () => { opts.tabSize = '-17'; assertState(opts, { tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can set indentSize to the same value', () => { + opts.indentSize = 4; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('can change indentSize to positive integer', () => { + opts.indentSize = 1; + assertState(opts, { + tabSize: 4, + indentSize: 1, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ indentSize: 1 }]); + }); + + test('can change indentSize to positive float', () => { + opts.indentSize = 2.3; + assertState(opts, { + tabSize: 4, + indentSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ indentSize: 2 }]); + }); + + test('can change indentSize to a string number', () => { + opts.indentSize = '2'; + assertState(opts, { + tabSize: 4, + indentSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ indentSize: 2 }]); + }); + + test('indentSize can request to use tabSize', () => { + opts.indentSize = 'tab'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, [{ indentSize: 'tab' }]); + }); + + test('indentSize cannot request indentation detection', () => { + opts.indentSize = 'auto'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid indentSize 1', () => { + opts.indentSize = null; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid indentSize 2', () => { + opts.indentSize = -5; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid indentSize 3', () => { + opts.indentSize = 'hello'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: TextEditorLineNumbersStyle.On + }); + assert.deepEqual(calls, []); + }); + + test('ignores invalid indentSize 4', () => { + opts.indentSize = '-17'; + assertState(opts, { + tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -212,6 +343,7 @@ suite('ExtHostTextEditorOptions', () => { opts.insertSpaces = false; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -223,6 +355,7 @@ suite('ExtHostTextEditorOptions', () => { opts.insertSpaces = true; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -234,6 +367,7 @@ suite('ExtHostTextEditorOptions', () => { opts.insertSpaces = 'false'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -245,6 +379,7 @@ suite('ExtHostTextEditorOptions', () => { opts.insertSpaces = 'hello'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -256,6 +391,7 @@ suite('ExtHostTextEditorOptions', () => { opts.insertSpaces = 'auto'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -267,6 +403,7 @@ suite('ExtHostTextEditorOptions', () => { opts.cursorStyle = TextEditorCursorStyle.Line; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -278,6 +415,7 @@ suite('ExtHostTextEditorOptions', () => { opts.cursorStyle = TextEditorCursorStyle.Block; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: TextEditorLineNumbersStyle.On @@ -289,6 +427,7 @@ suite('ExtHostTextEditorOptions', () => { opts.lineNumbers = TextEditorLineNumbersStyle.On; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -300,6 +439,7 @@ suite('ExtHostTextEditorOptions', () => { opts.lineNumbers = TextEditorLineNumbersStyle.Off; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.Off @@ -316,6 +456,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -330,6 +471,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -344,6 +486,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 3, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On @@ -358,6 +501,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: TextEditorLineNumbersStyle.Relative