Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/indentation/browser/indentation.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { DisposableStore } from '../../../../base/common/lifecycle.js';
7
import * as strings from '../../../../base/common/strings.js';
8
import { ICodeEditor } from '../../../browser/editorBrowser.js';
9
import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js';
10
import { ShiftCommand } from '../../../common/commands/shiftCommand.js';
11
import { EditorAutoIndentStrategy, EditorOption } from '../../../common/config/editorOptions.js';
12
import { ISingleEditOperation } from '../../../common/core/editOperation.js';
13
import { IRange, Range } from '../../../common/core/range.js';
14
import { Selection } from '../../../common/core/selection.js';
15
import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from '../../../common/editorCommon.js';
16
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
17
import { EndOfLineSequence, ITextModel } from '../../../common/model.js';
18
import { TextEdit } from '../../../common/languages.js';
19
import { StandardTokenType } from '../../../common/encodedTokenAttributes.js';
20
import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
21
import { IndentConsts } from '../../../common/languages/supports/indentRules.js';
22
import { IModelService } from '../../../common/services/model.js';
23
import * as indentUtils from '../common/indentUtils.js';
24
import * as nls from '../../../../nls.js';
25
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
26
import { getGoodIndentForLine, getIndentMetadata } from '../../../common/languages/autoIndent.js';
27
import { getReindentEditOperations } from '../common/indentation.js';
28
import { getStandardTokenTypeAtPosition } from '../../../common/tokens/lineTokens.js';
29
import { Position } from '../../../common/core/position.js';
30
31
export class IndentationToSpacesAction extends EditorAction {
32
public static readonly ID = 'editor.action.indentationToSpaces';
33
34
constructor() {
35
super({
36
id: IndentationToSpacesAction.ID,
37
label: nls.localize2('indentationToSpaces', "Convert Indentation to Spaces"),
38
precondition: EditorContextKeys.writable,
39
metadata: {
40
description: nls.localize2('indentationToSpacesDescription', "Convert the tab indentation to spaces."),
41
}
42
});
43
}
44
45
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
46
const model = editor.getModel();
47
if (!model) {
48
return;
49
}
50
const modelOpts = model.getOptions();
51
const selection = editor.getSelection();
52
if (!selection) {
53
return;
54
}
55
const command = new IndentationToSpacesCommand(selection, modelOpts.tabSize);
56
57
editor.pushUndoStop();
58
editor.executeCommands(this.id, [command]);
59
editor.pushUndoStop();
60
61
model.updateOptions({
62
insertSpaces: true
63
});
64
}
65
}
66
67
export class IndentationToTabsAction extends EditorAction {
68
public static readonly ID = 'editor.action.indentationToTabs';
69
70
constructor() {
71
super({
72
id: IndentationToTabsAction.ID,
73
label: nls.localize2('indentationToTabs', "Convert Indentation to Tabs"),
74
precondition: EditorContextKeys.writable,
75
metadata: {
76
description: nls.localize2('indentationToTabsDescription', "Convert the spaces indentation to tabs."),
77
}
78
});
79
}
80
81
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
82
const model = editor.getModel();
83
if (!model) {
84
return;
85
}
86
const modelOpts = model.getOptions();
87
const selection = editor.getSelection();
88
if (!selection) {
89
return;
90
}
91
const command = new IndentationToTabsCommand(selection, modelOpts.tabSize);
92
93
editor.pushUndoStop();
94
editor.executeCommands(this.id, [command]);
95
editor.pushUndoStop();
96
97
model.updateOptions({
98
insertSpaces: false
99
});
100
}
101
}
102
103
export class ChangeIndentationSizeAction extends EditorAction {
104
105
constructor(private readonly insertSpaces: boolean, private readonly displaySizeOnly: boolean, opts: IActionOptions) {
106
super(opts);
107
}
108
109
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
110
const quickInputService = accessor.get(IQuickInputService);
111
const modelService = accessor.get(IModelService);
112
113
const model = editor.getModel();
114
if (!model) {
115
return;
116
}
117
118
const creationOpts = modelService.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
119
const modelOpts = model.getOptions();
120
const picks = [1, 2, 3, 4, 5, 6, 7, 8].map(n => ({
121
id: n.toString(),
122
label: n.toString(),
123
// add description for tabSize value set in the configuration
124
description: (
125
n === creationOpts.tabSize && n === modelOpts.tabSize
126
? nls.localize('configuredTabSize', "Configured Tab Size")
127
: n === creationOpts.tabSize
128
? nls.localize('defaultTabSize', "Default Tab Size")
129
: n === modelOpts.tabSize
130
? nls.localize('currentTabSize', "Current Tab Size")
131
: undefined
132
)
133
}));
134
135
// auto focus the tabSize set for the current editor
136
const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7);
137
138
setTimeout(() => {
139
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 => {
140
if (pick) {
141
if (model && !model.isDisposed()) {
142
const pickedVal = parseInt(pick.label, 10);
143
if (this.displaySizeOnly) {
144
model.updateOptions({
145
tabSize: pickedVal
146
});
147
} else {
148
model.updateOptions({
149
tabSize: pickedVal,
150
indentSize: pickedVal,
151
insertSpaces: this.insertSpaces
152
});
153
}
154
}
155
}
156
});
157
}, 50/* quick input is sensitive to being opened so soon after another */);
158
}
159
}
160
161
export class IndentUsingTabs extends ChangeIndentationSizeAction {
162
163
public static readonly ID = 'editor.action.indentUsingTabs';
164
165
constructor() {
166
super(false, false, {
167
id: IndentUsingTabs.ID,
168
label: nls.localize2('indentUsingTabs', "Indent Using Tabs"),
169
precondition: undefined,
170
metadata: {
171
description: nls.localize2('indentUsingTabsDescription', "Use indentation with tabs."),
172
}
173
});
174
}
175
}
176
177
export class IndentUsingSpaces extends ChangeIndentationSizeAction {
178
179
public static readonly ID = 'editor.action.indentUsingSpaces';
180
181
constructor() {
182
super(true, false, {
183
id: IndentUsingSpaces.ID,
184
label: nls.localize2('indentUsingSpaces', "Indent Using Spaces"),
185
precondition: undefined,
186
metadata: {
187
description: nls.localize2('indentUsingSpacesDescription', "Use indentation with spaces."),
188
}
189
});
190
}
191
}
192
193
export class ChangeTabDisplaySize extends ChangeIndentationSizeAction {
194
195
public static readonly ID = 'editor.action.changeTabDisplaySize';
196
197
constructor() {
198
super(true, true, {
199
id: ChangeTabDisplaySize.ID,
200
label: nls.localize2('changeTabDisplaySize', "Change Tab Display Size"),
201
precondition: undefined,
202
metadata: {
203
description: nls.localize2('changeTabDisplaySizeDescription', "Change the space size equivalent of the tab."),
204
}
205
});
206
}
207
}
208
209
export class DetectIndentation extends EditorAction {
210
211
public static readonly ID = 'editor.action.detectIndentation';
212
213
constructor() {
214
super({
215
id: DetectIndentation.ID,
216
label: nls.localize2('detectIndentation', "Detect Indentation from Content"),
217
precondition: undefined,
218
metadata: {
219
description: nls.localize2('detectIndentationDescription', "Detect the indentation from content."),
220
}
221
});
222
}
223
224
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
225
const modelService = accessor.get(IModelService);
226
227
const model = editor.getModel();
228
if (!model) {
229
return;
230
}
231
232
const creationOpts = modelService.getCreationOptions(model.getLanguageId(), model.uri, model.isForSimpleWidget);
233
model.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
234
}
235
}
236
237
export class ReindentLinesAction extends EditorAction {
238
constructor() {
239
super({
240
id: 'editor.action.reindentlines',
241
label: nls.localize2('editor.reindentlines', "Reindent Lines"),
242
precondition: EditorContextKeys.writable,
243
metadata: {
244
description: nls.localize2('editor.reindentlinesDescription', "Reindent the lines of the editor."),
245
}
246
});
247
}
248
249
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
250
const languageConfigurationService = accessor.get(ILanguageConfigurationService);
251
252
const model = editor.getModel();
253
if (!model) {
254
return;
255
}
256
const edits = getReindentEditOperations(model, languageConfigurationService, 1, model.getLineCount());
257
if (edits.length > 0) {
258
editor.pushUndoStop();
259
editor.executeEdits(this.id, edits);
260
editor.pushUndoStop();
261
}
262
}
263
}
264
265
export class ReindentSelectedLinesAction extends EditorAction {
266
constructor() {
267
super({
268
id: 'editor.action.reindentselectedlines',
269
label: nls.localize2('editor.reindentselectedlines', "Reindent Selected Lines"),
270
precondition: EditorContextKeys.writable,
271
metadata: {
272
description: nls.localize2('editor.reindentselectedlinesDescription', "Reindent the selected lines of the editor."),
273
}
274
});
275
}
276
277
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
278
const languageConfigurationService = accessor.get(ILanguageConfigurationService);
279
280
const model = editor.getModel();
281
if (!model) {
282
return;
283
}
284
285
const selections = editor.getSelections();
286
if (selections === null) {
287
return;
288
}
289
290
const edits: ISingleEditOperation[] = [];
291
292
for (const selection of selections) {
293
let startLineNumber = selection.startLineNumber;
294
let endLineNumber = selection.endLineNumber;
295
296
if (startLineNumber !== endLineNumber && selection.endColumn === 1) {
297
endLineNumber--;
298
}
299
300
if (startLineNumber === 1) {
301
if (startLineNumber === endLineNumber) {
302
continue;
303
}
304
} else {
305
startLineNumber--;
306
}
307
308
const editOperations = getReindentEditOperations(model, languageConfigurationService, startLineNumber, endLineNumber);
309
edits.push(...editOperations);
310
}
311
312
if (edits.length > 0) {
313
editor.pushUndoStop();
314
editor.executeEdits(this.id, edits);
315
editor.pushUndoStop();
316
}
317
}
318
}
319
320
export class AutoIndentOnPasteCommand implements ICommand {
321
322
private readonly _edits: { range: IRange; text: string; eol?: EndOfLineSequence }[];
323
324
private readonly _initialSelection: Selection;
325
private _selectionId: string | null;
326
327
constructor(edits: TextEdit[], initialSelection: Selection) {
328
this._initialSelection = initialSelection;
329
this._edits = [];
330
this._selectionId = null;
331
332
for (const edit of edits) {
333
if (edit.range && typeof edit.text === 'string') {
334
this._edits.push(edit as { range: IRange; text: string; eol?: EndOfLineSequence });
335
}
336
}
337
}
338
339
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
340
for (const edit of this._edits) {
341
builder.addEditOperation(Range.lift(edit.range), edit.text);
342
}
343
344
let selectionIsSet = false;
345
if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
346
if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
347
this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
348
selectionIsSet = true;
349
this._selectionId = builder.trackSelection(this._initialSelection, true);
350
} else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
351
this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
352
selectionIsSet = true;
353
this._selectionId = builder.trackSelection(this._initialSelection, false);
354
}
355
}
356
357
if (!selectionIsSet) {
358
this._selectionId = builder.trackSelection(this._initialSelection);
359
}
360
}
361
362
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
363
return helper.getTrackedSelection(this._selectionId!);
364
}
365
}
366
367
export class AutoIndentOnPaste implements IEditorContribution {
368
public static readonly ID = 'editor.contrib.autoIndentOnPaste';
369
370
private readonly callOnDispose = new DisposableStore();
371
private readonly callOnModel = new DisposableStore();
372
373
constructor(
374
private readonly editor: ICodeEditor,
375
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService
376
) {
377
378
this.callOnDispose.add(editor.onDidChangeConfiguration(() => this.update()));
379
this.callOnDispose.add(editor.onDidChangeModel(() => this.update()));
380
this.callOnDispose.add(editor.onDidChangeModelLanguage(() => this.update()));
381
}
382
383
private update(): void {
384
385
// clean up
386
this.callOnModel.clear();
387
388
// we are disabled
389
if (!this.editor.getOption(EditorOption.autoIndentOnPaste) || this.editor.getOption(EditorOption.autoIndent) < EditorAutoIndentStrategy.Full) {
390
return;
391
}
392
393
// no model
394
if (!this.editor.hasModel()) {
395
return;
396
}
397
398
this.callOnModel.add(this.editor.onDidPaste(({ range }) => {
399
this.trigger(range);
400
}));
401
}
402
403
public trigger(range: Range): void {
404
const selections = this.editor.getSelections();
405
if (selections === null || selections.length > 1) {
406
return;
407
}
408
409
const model = this.editor.getModel();
410
if (!model) {
411
return;
412
}
413
const containsOnlyWhitespace = this.rangeContainsOnlyWhitespaceCharacters(model, range);
414
if (containsOnlyWhitespace) {
415
return;
416
}
417
if (!this.editor.getOption(EditorOption.autoIndentOnPasteWithinString) && isStartOrEndInString(model, range)) {
418
return;
419
}
420
if (!model.tokenization.isCheapToTokenize(range.getStartPosition().lineNumber)) {
421
return;
422
}
423
const autoIndent = this.editor.getOption(EditorOption.autoIndent);
424
const { tabSize, indentSize, insertSpaces } = model.getOptions();
425
const textEdits: TextEdit[] = [];
426
427
const indentConverter = {
428
shiftIndent: (indentation: string) => {
429
return ShiftCommand.shiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
430
},
431
unshiftIndent: (indentation: string) => {
432
return ShiftCommand.unshiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
433
}
434
};
435
436
let startLineNumber = range.startLineNumber;
437
438
let firstLineText = model.getLineContent(startLineNumber);
439
if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) {
440
const indentOfFirstLine = getGoodIndentForLine(autoIndent, model, model.getLanguageId(), startLineNumber, indentConverter, this._languageConfigurationService);
441
442
if (indentOfFirstLine !== null) {
443
const oldIndentation = strings.getLeadingWhitespace(firstLineText);
444
const newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
445
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
446
447
if (newSpaceCnt !== oldSpaceCnt) {
448
const newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
449
textEdits.push({
450
range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
451
text: newIndent
452
});
453
firstLineText = newIndent + firstLineText.substring(oldIndentation.length);
454
} else {
455
const indentMetadata = getIndentMetadata(model, startLineNumber, this._languageConfigurationService);
456
457
if (indentMetadata === 0 || indentMetadata === IndentConsts.UNINDENT_MASK) {
458
// we paste content into a line where only contains whitespaces
459
// after pasting, the indentation of the first line is already correct
460
// the first line doesn't match any indentation rule
461
// then no-op.
462
return;
463
}
464
}
465
}
466
}
467
468
const firstLineNumber = startLineNumber;
469
470
// ignore empty or ignored lines
471
while (startLineNumber < range.endLineNumber) {
472
if (!/\S/.test(model.getLineContent(startLineNumber + 1))) {
473
startLineNumber++;
474
continue;
475
}
476
break;
477
}
478
479
if (startLineNumber !== range.endLineNumber) {
480
const virtualModel = {
481
tokenization: {
482
getLineTokens: (lineNumber: number) => {
483
return model.tokenization.getLineTokens(lineNumber);
484
},
485
getLanguageId: () => {
486
return model.getLanguageId();
487
},
488
getLanguageIdAtPosition: (lineNumber: number, column: number) => {
489
return model.getLanguageIdAtPosition(lineNumber, column);
490
},
491
},
492
getLineContent: (lineNumber: number) => {
493
if (lineNumber === firstLineNumber) {
494
return firstLineText;
495
} else {
496
return model.getLineContent(lineNumber);
497
}
498
}
499
};
500
const indentOfSecondLine = getGoodIndentForLine(autoIndent, virtualModel, model.getLanguageId(), startLineNumber + 1, indentConverter, this._languageConfigurationService);
501
if (indentOfSecondLine !== null) {
502
const newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize);
503
const oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
504
505
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
506
const spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
507
for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) {
508
const lineContent = model.getLineContent(i);
509
const originalIndent = strings.getLeadingWhitespace(lineContent);
510
const originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
511
const newSpacesCnt = originalSpacesCnt + spaceCntOffset;
512
const newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
513
514
if (newIndent !== originalIndent) {
515
textEdits.push({
516
range: new Range(i, 1, i, originalIndent.length + 1),
517
text: newIndent
518
});
519
}
520
}
521
}
522
}
523
}
524
525
if (textEdits.length > 0) {
526
this.editor.pushUndoStop();
527
const cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!);
528
this.editor.executeCommand('autoIndentOnPaste', cmd);
529
this.editor.pushUndoStop();
530
}
531
}
532
533
private rangeContainsOnlyWhitespaceCharacters(model: ITextModel, range: Range): boolean {
534
const lineContainsOnlyWhitespace = (content: string): boolean => {
535
return content.trim().length === 0;
536
};
537
let containsOnlyWhitespace: boolean = true;
538
if (range.startLineNumber === range.endLineNumber) {
539
const lineContent = model.getLineContent(range.startLineNumber);
540
const linePart = lineContent.substring(range.startColumn - 1, range.endColumn - 1);
541
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
542
} else {
543
for (let i = range.startLineNumber; i <= range.endLineNumber; i++) {
544
const lineContent = model.getLineContent(i);
545
if (i === range.startLineNumber) {
546
const linePart = lineContent.substring(range.startColumn - 1);
547
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
548
} else if (i === range.endLineNumber) {
549
const linePart = lineContent.substring(0, range.endColumn - 1);
550
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
551
} else {
552
containsOnlyWhitespace = model.getLineFirstNonWhitespaceColumn(i) === 0;
553
}
554
if (!containsOnlyWhitespace) {
555
break;
556
}
557
}
558
}
559
return containsOnlyWhitespace;
560
}
561
562
public dispose(): void {
563
this.callOnDispose.dispose();
564
this.callOnModel.dispose();
565
}
566
}
567
568
function isStartOrEndInString(model: ITextModel, range: Range): boolean {
569
const isPositionInString = (position: Position): boolean => {
570
const tokenType = getStandardTokenTypeAtPosition(model, position);
571
return tokenType === StandardTokenType.String;
572
};
573
return isPositionInString(range.getStartPosition()) || isPositionInString(range.getEndPosition());
574
}
575
576
function getIndentationEditOperations(model: ITextModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
577
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
578
// Model is empty
579
return;
580
}
581
582
let spaces = '';
583
for (let i = 0; i < tabSize; i++) {
584
spaces += ' ';
585
}
586
587
const spacesRegExp = new RegExp(spaces, 'gi');
588
589
for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) {
590
let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
591
if (lastIndentationColumn === 0) {
592
lastIndentationColumn = model.getLineMaxColumn(lineNumber);
593
}
594
595
if (lastIndentationColumn === 1) {
596
continue;
597
}
598
599
const originalIndentationRange = new Range(lineNumber, 1, lineNumber, lastIndentationColumn);
600
const originalIndentation = model.getValueInRange(originalIndentationRange);
601
const newIndentation = (
602
tabsToSpaces
603
? originalIndentation.replace(/\t/ig, spaces)
604
: originalIndentation.replace(spacesRegExp, '\t')
605
);
606
607
builder.addEditOperation(originalIndentationRange, newIndentation);
608
}
609
}
610
611
export class IndentationToSpacesCommand implements ICommand {
612
613
private selectionId: string | null = null;
614
615
constructor(private readonly selection: Selection, private tabSize: number) { }
616
617
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
618
this.selectionId = builder.trackSelection(this.selection);
619
getIndentationEditOperations(model, builder, this.tabSize, true);
620
}
621
622
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
623
return helper.getTrackedSelection(this.selectionId!);
624
}
625
}
626
627
export class IndentationToTabsCommand implements ICommand {
628
629
private selectionId: string | null = null;
630
631
constructor(private readonly selection: Selection, private tabSize: number) { }
632
633
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
634
this.selectionId = builder.trackSelection(this.selection);
635
getIndentationEditOperations(model, builder, this.tabSize, false);
636
}
637
638
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
639
return helper.getTrackedSelection(this.selectionId!);
640
}
641
}
642
643
registerEditorContribution(AutoIndentOnPaste.ID, AutoIndentOnPaste, EditorContributionInstantiation.BeforeFirstInteraction);
644
registerEditorAction(IndentationToSpacesAction);
645
registerEditorAction(IndentationToTabsAction);
646
registerEditorAction(IndentUsingTabs);
647
registerEditorAction(IndentUsingSpaces);
648
registerEditorAction(ChangeTabDisplaySize);
649
registerEditorAction(DetectIndentation);
650
registerEditorAction(ReindentLinesAction);
651
registerEditorAction(ReindentSelectedLinesAction);
652
653