diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 7843b0a164044..b6f8d6ad7fac0 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -26,20 +26,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 {0} is on.", '`#editor.detectIndentation#`') }, - // 'editor.indentSize': { - // 'anyOf': [ - // { - // type: 'string', - // enum: ['tabSize'] - // }, - // { - // type: 'number', - // minimum: 1 - // } - // ], - // default: 'tabSize', - // markdownDescription: nls.localize('indentSize', "The number of spaces used for indentation or 'tabSize' to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.") - // }, + 'editor.indentSize': { + 'anyOf': [ + { + type: 'string', + enum: ['tabSize'] + }, + { + type: 'number', + minimum: 1 + } + ], + default: 'tabSize', + markdownDescription: nls.localize('indentSize', "The number of spaces used for indentation or 'tabSize' 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/model.ts b/src/vs/editor/common/model.ts index 2aa74d591f477..0d2d8f3576fc0 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -440,7 +440,7 @@ export class TextModelResolvedOptions { bracketPairColorizationOptions: BracketPairColorizationOptions; }) { this.tabSize = Math.max(1, src.tabSize | 0); - this.indentSize = src.tabSize | 0; + this.indentSize = Math.max(1, src.indentSize | 0); this.insertSpaces = Boolean(src.insertSpaces); this.defaultEOL = src.defaultEOL | 0; this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); diff --git a/src/vs/editor/contrib/indentation/browser/indentation.ts b/src/vs/editor/contrib/indentation/browser/indentation.ts index fcb7848bf4e63..8e737485cf626 100644 --- a/src/vs/editor/contrib/indentation/browser/indentation.ts +++ b/src/vs/editor/contrib/indentation/browser/indentation.ts @@ -208,7 +208,7 @@ export class IndentationToTabsAction extends EditorAction { export class ChangeIndentationSizeAction extends EditorAction { - constructor(private readonly insertSpaces: boolean, opts: IActionOptions) { + constructor(private readonly insertSpaces: boolean, private readonly displaySizeOnly: boolean, opts: IActionOptions) { super(opts); } @@ -222,11 +222,20 @@ export class ChangeIndentationSizeAction extends EditorAction { } const creationOpts = modelService.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget); + const modelOpts = model.getOptions(); const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({ id: n.toString(), label: n.toString(), // add description for tabSize value set in the configuration - description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : undefined + description: ( + n === creationOpts.tabSize && n === modelOpts.tabSize + ? nls.localize('configuredTabSize', "Configured Tab Size") + : n === creationOpts.tabSize + ? nls.localize('defaultTabSize', "Default Tab Size") + : n === modelOpts.tabSize + ? nls.localize('currentTabSize', "Current Tab Size") + : undefined + ) })); // auto focus the tabSize set for the current editor @@ -236,10 +245,18 @@ export class ChangeIndentationSizeAction extends EditorAction { quickInputService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then(pick => { if (pick) { if (model && !model.isDisposed()) { - model.updateOptions({ - tabSize: parseInt(pick.label, 10), - insertSpaces: this.insertSpaces - }); + const pickedVal = parseInt(pick.label, 10); + if (this.displaySizeOnly) { + model.updateOptions({ + tabSize: pickedVal + }); + } else { + model.updateOptions({ + tabSize: this.insertSpaces ? undefined : pickedVal, + indentSize: pickedVal, + insertSpaces: this.insertSpaces + }); + } } } }); @@ -252,7 +269,7 @@ export class IndentUsingTabs extends ChangeIndentationSizeAction { public static readonly ID = 'editor.action.indentUsingTabs'; constructor() { - super(false, { + super(false, false, { id: IndentUsingTabs.ID, label: nls.localize('indentUsingTabs', "Indent Using Tabs"), alias: 'Indent Using Tabs', @@ -266,7 +283,7 @@ export class IndentUsingSpaces extends ChangeIndentationSizeAction { public static readonly ID = 'editor.action.indentUsingSpaces'; constructor() { - super(true, { + super(true, false, { id: IndentUsingSpaces.ID, label: nls.localize('indentUsingSpaces', "Indent Using Spaces"), alias: 'Indent Using Spaces', @@ -275,6 +292,20 @@ export class IndentUsingSpaces extends ChangeIndentationSizeAction { } } +export class ChangeTabDisplaySize extends ChangeIndentationSizeAction { + + public static readonly ID = 'editor.action.changeTabDisplaySize'; + + constructor() { + super(true, true, { + id: ChangeTabDisplaySize.ID, + label: nls.localize('changeTabDisplaySize', "Change Tab Display Size"), + alias: 'Change Tab Display Size', + precondition: undefined + }); + } +} + export class DetectIndentation extends EditorAction { public static readonly ID = 'editor.action.detectIndentation'; @@ -694,6 +725,7 @@ registerEditorAction(IndentationToSpacesAction); registerEditorAction(IndentationToTabsAction); registerEditorAction(IndentUsingTabs); registerEditorAction(IndentUsingSpaces); +registerEditorAction(ChangeTabDisplaySize); registerEditorAction(DetectIndentation); registerEditorAction(ReindentLinesAction); registerEditorAction(ReindentSelectedLinesAction); diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts index 137dfc2bb9f03..aab47e06c9a71 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -706,7 +706,7 @@ suite('SnippetSession', function () { test('Tabs don\'t get replaced with spaces in snippet transformations #103818', function () { const model = editor.getModel()!; model.setValue('\n{\n \n}'); - model.updateOptions({ insertSpaces: true, tabSize: 2 }); + model.updateOptions({ insertSpaces: true, indentSize: 2 }); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(3, 6, 3, 6)]); const session = new SnippetSession(editor, [ 'function animate () {', diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 4d4bfcafcc498..39add3ef2877c 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -699,7 +699,7 @@ suite('TextModelWithTokens regression tests', () => { }); suite('TextModel.getLineIndentGuide', () => { - function assertIndentGuides(lines: [number, number, number, number, string][], tabSize: number): void { + function assertIndentGuides(lines: [number, number, number, number, string][], indentSize: number): void { const languageId = 'testLang'; const disposables = new DisposableStore(); const instantiationService = createModelServices(disposables); @@ -708,7 +708,7 @@ suite('TextModel.getLineIndentGuide', () => { const text = lines.map(l => l[4]).join('\n'); const model = disposables.add(instantiateTextModel(instantiationService, text, languageId)); - model.updateOptions({ tabSize: tabSize }); + model.updateOptions({ indentSize: indentSize }); const actualIndents = model.guides.getLinesIndentGuides(1, model.getLineCount()); diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 638fe8aa4747b..cbc28f4d953f8 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -80,6 +80,7 @@ export class MainThreadTextEditorProperties { return { insertSpaces: modelOptions.insertSpaces, tabSize: modelOptions.tabSize, + indentSize: modelOptions.indentSize, cursorStyle: cursorStyle, lineNumbers: lineNumbers }; @@ -146,6 +147,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 @@ -376,6 +378,13 @@ export class MainThreadTextEditor { if (typeof newConfiguration.tabSize !== 'undefined') { newOpts.tabSize = newConfiguration.tabSize; } + if (typeof newConfiguration.indentSize !== 'undefined') { + if (newConfiguration.indentSize === 'tabSize') { + newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize; + } else { + newOpts.indentSize = newConfiguration.indentSize; + } + } this._model.updateOptions(newOpts); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 34ba252e13f7e..b43131cb6d925 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -203,6 +203,7 @@ export interface MainThreadDocumentsShape extends IDisposable { export interface ITextEditorConfigurationUpdate { tabSize?: number | 'auto'; + indentSize?: number | 'tabSize'; insertSpaces?: boolean | 'auto'; cursorStyle?: TextEditorCursorStyle; lineNumbers?: RenderLineNumbersType; @@ -210,6 +211,7 @@ export interface ITextEditorConfigurationUpdate { export interface IResolvedTextEditorConfiguration { tabSize: number; + indentSize: number; insertSpaces: boolean; cursorStyle: TextEditorCursorStyle; lineNumbers: RenderLineNumbersType; diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index f025cc805c474..316e0fe076acc 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -143,6 +143,7 @@ export class ExtHostTextEditorOptions { private _logService: ILogService; private _tabSize!: number; + private _indentSize!: number; private _insertSpaces!: boolean; private _cursorStyle!: TextEditorCursorStyle; private _lineNumbers!: TextEditorLineNumbersStyle; @@ -164,6 +165,12 @@ export class ExtHostTextEditorOptions { set tabSize(value: number | string) { that._setTabSize(value); }, + get indentSize(): number | 'tabSize' { + return that._indentSize; + }, + set indentSize(value: number | 'tabSize') { + that._setIndentSize(value); + }, get insertSpaces(): boolean | string { return that._insertSpaces; }, @@ -187,6 +194,7 @@ export class ExtHostTextEditorOptions { public _accept(source: IResolvedTextEditorConfiguration): void { this._tabSize = source.tabSize; + this._indentSize = source.indentSize; this._insertSpaces = source.insertSpaces; this._cursorStyle = source.cursorStyle; this._lineNumbers = TypeConverters.TextEditorLineNumbersStyle.to(source.lineNumbers); @@ -231,6 +239,45 @@ export class ExtHostTextEditorOptions { })); } + // --- internal: indentSize + + private _validateIndentSize(value: number | string): number | 'tabSize' | null { + if (value === 'tabSize') { + return 'tabSize'; + } + if (typeof value === 'number') { + const r = Math.floor(value); + return (r > 0 ? r : null); + } + if (typeof value === 'string') { + const r = parseInt(value, 10); + if (isNaN(r)) { + return null; + } + return (r > 0 ? r : null); + } + return null; + } + + private _setIndentSize(value: number | string) { + const 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; + } + this._warnOnError('setIndentSize', this._proxy.$trySetOptions(this._id, { + indentSize: indentSize + })); + } + // --- internal: insert spaces private _validateInsertSpaces(value: boolean | string): boolean | 'auto' { @@ -298,18 +345,18 @@ export class ExtHostTextEditorOptions { } } - // if (typeof newOptions.indentSize !== 'undefined') { - // const indentSize = this._validateIndentSize(newOptions.indentSize); - // if (indentSize === 'tabSize') { - // 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.indentSize !== 'undefined') { + const indentSize = this._validateIndentSize(newOptions.indentSize); + if (indentSize === 'tabSize') { + 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') { const insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces); diff --git a/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts b/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts index accded46c5d62..0793a57fff6fa 100644 --- a/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTextEditor.test.ts @@ -21,7 +21,7 @@ suite('ExtHostTextEditor', () => { ], '\n', 1, 'text', false); setup(() => { - editor = new ExtHostTextEditor('fake', null!, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); + editor = new ExtHostTextEditor('fake', null!, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); }); test('disposed editor', () => { @@ -48,7 +48,7 @@ suite('ExtHostTextEditor', () => { applyCount += 1; return Promise.resolve(true); } - }, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); + }, new NullLogService(), new Lazy(() => doc.document), [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); await editor.value.edit(edit => { }); assert.strictEqual(applyCount, 0); @@ -90,6 +90,7 @@ suite('ExtHostTextEditorOptions', () => { }; opts = new ExtHostTextEditorOptions(mockProxy, '1', { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -104,6 +105,7 @@ suite('ExtHostTextEditorOptions', () => { function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void { const actual = { tabSize: opts.value.tabSize, + indentSize: opts.value.indentSize, insertSpaces: opts.value.insertSpaces, cursorStyle: opts.value.cursorStyle, lineNumbers: opts.value.lineNumbers @@ -115,6 +117,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = 4; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -126,6 +129,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = 1; assertState(opts, { tabSize: 1, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -137,6 +141,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = 2.3; assertState(opts, { tabSize: 2, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -148,6 +153,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = '2'; assertState(opts, { tabSize: 2, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -159,6 +165,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = 'auto'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -170,6 +177,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = null!; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -181,6 +189,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = -5; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -192,6 +201,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = 'hello'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -203,6 +213,127 @@ suite('ExtHostTextEditorOptions', () => { opts.value.tabSize = '-17'; assertState(opts, { tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('can set indentSize to the same value', () => { + opts.value.indentSize = 4; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('can change indentSize to positive integer', () => { + opts.value.indentSize = 1; + assertState(opts, { + tabSize: 4, + indentSize: 1, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, [{ indentSize: 1 }]); + }); + + test('can change indentSize to positive float', () => { + opts.value.indentSize = 2.3; + assertState(opts, { + tabSize: 4, + indentSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, [{ indentSize: 2 }]); + }); + + test('can change indentSize to a string number', () => { + opts.value.indentSize = '2'; + assertState(opts, { + tabSize: 4, + indentSize: 2, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, [{ indentSize: 2 }]); + }); + + test('indentSize can request to use tabSize', () => { + opts.value.indentSize = 'tabSize'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, [{ indentSize: 'tabSize' }]); + }); + + test('indentSize cannot request indentation detection', () => { + opts.value.indentSize = 'auto'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('ignores invalid indentSize 1', () => { + opts.value.indentSize = null!; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('ignores invalid indentSize 2', () => { + opts.value.indentSize = -5; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('ignores invalid indentSize 3', () => { + opts.value.indentSize = 'hello'; + assertState(opts, { + tabSize: 4, + indentSize: 4, + insertSpaces: false, + cursorStyle: TextEditorCursorStyle.Line, + lineNumbers: RenderLineNumbersType.On + }); + assert.deepStrictEqual(calls, []); + }); + + test('ignores invalid indentSize 4', () => { + opts.value.indentSize = '-17'; + assertState(opts, { + tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -214,6 +345,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.insertSpaces = false; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -225,6 +357,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.insertSpaces = true; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -236,6 +369,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.insertSpaces = 'false'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -247,6 +381,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.insertSpaces = 'hello'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -258,6 +393,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.insertSpaces = 'auto'; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -269,6 +405,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.cursorStyle = TextEditorCursorStyle.Line; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -280,6 +417,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.cursorStyle = TextEditorCursorStyle.Block; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.On @@ -291,6 +429,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.lineNumbers = TextEditorLineNumbersStyle.On; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -302,6 +441,7 @@ suite('ExtHostTextEditorOptions', () => { opts.value.lineNumbers = TextEditorLineNumbersStyle.Off; assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.Off @@ -312,12 +452,14 @@ suite('ExtHostTextEditorOptions', () => { test('can do bulk updates 0', () => { opts.assign({ tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: TextEditorLineNumbersStyle.On }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -332,6 +474,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: true, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -346,6 +489,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 3, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On @@ -360,6 +504,7 @@ suite('ExtHostTextEditorOptions', () => { }); assertState(opts, { tabSize: 4, + indentSize: 4, insertSpaces: false, cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.Relative diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 1f54e4ec3e773..8cc75d0c09d08 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -19,7 +19,7 @@ import { Disposable, MutableDisposable, DisposableStore } from 'vs/base/common/l import { IEditorAction } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence } from 'vs/editor/common/model'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/browser/linesOperations'; -import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/browser/indentation'; +import { IndentUsingSpaces, IndentUsingTabs, ChangeTabDisplaySize, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/browser/indentation'; import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -383,6 +383,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const picks: QuickPickInput[] = [ assertIsDefined(activeTextEditorControl.getAction(IndentUsingSpaces.ID)), assertIsDefined(activeTextEditorControl.getAction(IndentUsingTabs.ID)), + assertIsDefined(activeTextEditorControl.getAction(ChangeTabDisplaySize.ID)), assertIsDefined(activeTextEditorControl.getAction(DetectIndentation.ID)), assertIsDefined(activeTextEditorControl.getAction(IndentationToSpacesAction.ID)), assertIsDefined(activeTextEditorControl.getAction(IndentationToTabsAction.ID)), @@ -760,7 +761,9 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { const modelOpts = model.getOptions(); update.indentation = ( modelOpts.insertSpaces - ? localize('spacesSize', "Spaces: {0}", modelOpts.indentSize) + ? modelOpts.tabSize === modelOpts.indentSize + ? localize('spacesSize', "Spaces: {0}", modelOpts.indentSize) + : localize('spacesAndTabsSize', "Spaces: {0} (Tab Size: {1})", modelOpts.indentSize, modelOpts.tabSize) : localize({ key: 'tabSize', comment: ['Tab corresponds to the tab key'] }, "Tab Size: {0}", modelOpts.tabSize) ); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 37133b8e76b04..d9336a794c482 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -36,6 +36,7 @@ export const allApiProposals = Object.freeze({ findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', fsChunks: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fsChunks.d.ts', idToken: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.idToken.d.ts', + indentSize: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.indentSize.d.ts', inlineCompletionsAdditions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsAdditions.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', diff --git a/src/vscode-dts/vscode.proposed.indentSize.d.ts b/src/vscode-dts/vscode.proposed.indentSize.d.ts new file mode 100644 index 0000000000000..0ce01fa218aca --- /dev/null +++ b/src/vscode-dts/vscode.proposed.indentSize.d.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + /** + * Represents a {@link TextEditor text editor}'s {@link TextEditor.options options}. + */ + export interface TextEditorOptions { + /** + * 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 {@link TextEditorOptions.insertSpaces insertSpaces} is true + * and `indentSize` is set to `"tabSize"`. + * + * 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 `"tabSize"`. + */ + indentSize?: number | 'tabSize'; + } +}