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
5332 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 * as nls from '../../../../nls.js';
9
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
10
import { ICodeEditor } from '../../../browser/editorBrowser.js';
11
import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js';
12
import { ShiftCommand } from '../../../common/commands/shiftCommand.js';
13
import { EditorAutoIndentStrategy, EditorOption } from '../../../common/config/editorOptions.js';
14
import { ISingleEditOperation } from '../../../common/core/editOperation.js';
15
import { Position } from '../../../common/core/position.js';
16
import { IRange, Range } from '../../../common/core/range.js';
17
import { Selection } from '../../../common/core/selection.js';
18
import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from '../../../common/editorCommon.js';
19
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
20
import { StandardTokenType } from '../../../common/encodedTokenAttributes.js';
21
import { TextEdit } from '../../../common/languages.js';
22
import { getGoodIndentForLine, getIndentMetadata } from '../../../common/languages/autoIndent.js';
23
import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
24
import { IndentConsts } from '../../../common/languages/supports/indentRules.js';
25
import { EndOfLineSequence, ITextModel } from '../../../common/model.js';
26
import { IModelService } from '../../../common/services/model.js';
27
import { getStandardTokenTypeAtPosition } from '../../../common/tokens/lineTokens.js';
28
import { getReindentEditOperations } from '../common/indentation.js';
29
import * as indentUtils from '../common/indentUtils.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
canTriggerInlineEdits: true,
247
});
248
}
249
250
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
251
const languageConfigurationService = accessor.get(ILanguageConfigurationService);
252
253
const model = editor.getModel();
254
if (!model) {
255
return;
256
}
257
const edits = getReindentEditOperations(model, languageConfigurationService, 1, model.getLineCount());
258
if (edits.length > 0) {
259
editor.pushUndoStop();
260
editor.executeEdits(this.id, edits);
261
editor.pushUndoStop();
262
}
263
}
264
}
265
266
export class ReindentSelectedLinesAction extends EditorAction {
267
constructor() {
268
super({
269
id: 'editor.action.reindentselectedlines',
270
label: nls.localize2('editor.reindentselectedlines', "Reindent Selected Lines"),
271
precondition: EditorContextKeys.writable,
272
metadata: {
273
description: nls.localize2('editor.reindentselectedlinesDescription', "Reindent the selected lines of the editor."),
274
},
275
canTriggerInlineEdits: true,
276
});
277
}
278
279
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
280
const languageConfigurationService = accessor.get(ILanguageConfigurationService);
281
282
const model = editor.getModel();
283
if (!model) {
284
return;
285
}
286
287
const selections = editor.getSelections();
288
if (selections === null) {
289
return;
290
}
291
292
const edits: ISingleEditOperation[] = [];
293
294
for (const selection of selections) {
295
let startLineNumber = selection.startLineNumber;
296
let endLineNumber = selection.endLineNumber;
297
298
if (startLineNumber !== endLineNumber && selection.endColumn === 1) {
299
endLineNumber--;
300
}
301
302
if (startLineNumber === 1) {
303
if (startLineNumber === endLineNumber) {
304
continue;
305
}
306
} else {
307
startLineNumber--;
308
}
309
310
const editOperations = getReindentEditOperations(model, languageConfigurationService, startLineNumber, endLineNumber);
311
edits.push(...editOperations);
312
}
313
314
if (edits.length > 0) {
315
editor.pushUndoStop();
316
editor.executeEdits(this.id, edits);
317
editor.pushUndoStop();
318
}
319
}
320
}
321
322
export class AutoIndentOnPasteCommand implements ICommand {
323
324
private readonly _edits: { range: IRange; text: string; eol?: EndOfLineSequence }[];
325
326
private readonly _initialSelection: Selection;
327
private _selectionId: string | null;
328
329
constructor(edits: TextEdit[], initialSelection: Selection) {
330
this._initialSelection = initialSelection;
331
this._edits = [];
332
this._selectionId = null;
333
334
for (const edit of edits) {
335
if (edit.range && typeof edit.text === 'string') {
336
this._edits.push(edit as { range: IRange; text: string; eol?: EndOfLineSequence });
337
}
338
}
339
}
340
341
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
342
for (const edit of this._edits) {
343
builder.addEditOperation(Range.lift(edit.range), edit.text);
344
}
345
346
let selectionIsSet = false;
347
if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
348
if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
349
this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
350
selectionIsSet = true;
351
this._selectionId = builder.trackSelection(this._initialSelection, true);
352
} else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
353
this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
354
selectionIsSet = true;
355
this._selectionId = builder.trackSelection(this._initialSelection, false);
356
}
357
}
358
359
if (!selectionIsSet) {
360
this._selectionId = builder.trackSelection(this._initialSelection);
361
}
362
}
363
364
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
365
return helper.getTrackedSelection(this._selectionId!);
366
}
367
}
368
369
export class AutoIndentOnPaste implements IEditorContribution {
370
public static readonly ID = 'editor.contrib.autoIndentOnPaste';
371
372
private readonly callOnDispose = new DisposableStore();
373
private readonly callOnModel = new DisposableStore();
374
375
constructor(
376
private readonly editor: ICodeEditor,
377
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService
378
) {
379
380
this.callOnDispose.add(editor.onDidChangeConfiguration(() => this.update()));
381
this.callOnDispose.add(editor.onDidChangeModel(() => this.update()));
382
this.callOnDispose.add(editor.onDidChangeModelLanguage(() => this.update()));
383
}
384
385
private update(): void {
386
387
// clean up
388
this.callOnModel.clear();
389
390
// we are disabled
391
if (!this.editor.getOption(EditorOption.autoIndentOnPaste) || this.editor.getOption(EditorOption.autoIndent) < EditorAutoIndentStrategy.Full) {
392
return;
393
}
394
395
// no model
396
if (!this.editor.hasModel()) {
397
return;
398
}
399
400
this.callOnModel.add(this.editor.onDidPaste(({ range }) => {
401
this.trigger(range);
402
}));
403
}
404
405
public trigger(range: Range): void {
406
const selections = this.editor.getSelections();
407
if (selections === null || selections.length > 1) {
408
return;
409
}
410
411
const model = this.editor.getModel();
412
if (!model) {
413
return;
414
}
415
const containsOnlyWhitespace = this.rangeContainsOnlyWhitespaceCharacters(model, range);
416
if (containsOnlyWhitespace) {
417
return;
418
}
419
if (!this.editor.getOption(EditorOption.autoIndentOnPasteWithinString) && isStartOrEndInString(model, range)) {
420
return;
421
}
422
if (!model.tokenization.isCheapToTokenize(range.getStartPosition().lineNumber)) {
423
return;
424
}
425
const autoIndent = this.editor.getOption(EditorOption.autoIndent);
426
const { tabSize, indentSize, insertSpaces } = model.getOptions();
427
const textEdits: TextEdit[] = [];
428
429
const indentConverter = {
430
shiftIndent: (indentation: string) => {
431
return ShiftCommand.shiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
432
},
433
unshiftIndent: (indentation: string) => {
434
return ShiftCommand.unshiftIndent(indentation, indentation.length + 1, tabSize, indentSize, insertSpaces);
435
}
436
};
437
438
let startLineNumber = range.startLineNumber;
439
440
let firstLineText = model.getLineContent(startLineNumber);
441
if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) {
442
const indentOfFirstLine = getGoodIndentForLine(autoIndent, model, model.getLanguageId(), startLineNumber, indentConverter, this._languageConfigurationService);
443
444
if (indentOfFirstLine !== null) {
445
const oldIndentation = strings.getLeadingWhitespace(firstLineText);
446
const newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
447
const oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
448
449
if (newSpaceCnt !== oldSpaceCnt) {
450
const newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
451
textEdits.push({
452
range: new Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
453
text: newIndent
454
});
455
firstLineText = newIndent + firstLineText.substring(oldIndentation.length);
456
} else {
457
const indentMetadata = getIndentMetadata(model, startLineNumber, this._languageConfigurationService);
458
459
if (indentMetadata === 0 || indentMetadata === IndentConsts.UNINDENT_MASK) {
460
// we paste content into a line where only contains whitespaces
461
// after pasting, the indentation of the first line is already correct
462
// the first line doesn't match any indentation rule
463
// then no-op.
464
return;
465
}
466
}
467
}
468
}
469
470
const firstLineNumber = startLineNumber;
471
472
// ignore empty or ignored lines
473
while (startLineNumber < range.endLineNumber) {
474
if (!/\S/.test(model.getLineContent(startLineNumber + 1))) {
475
startLineNumber++;
476
continue;
477
}
478
break;
479
}
480
481
if (startLineNumber !== range.endLineNumber) {
482
const virtualModel = {
483
tokenization: {
484
getLineTokens: (lineNumber: number) => {
485
return model.tokenization.getLineTokens(lineNumber);
486
},
487
getLanguageId: () => {
488
return model.getLanguageId();
489
},
490
getLanguageIdAtPosition: (lineNumber: number, column: number) => {
491
return model.getLanguageIdAtPosition(lineNumber, column);
492
},
493
},
494
getLineContent: (lineNumber: number) => {
495
if (lineNumber === firstLineNumber) {
496
return firstLineText;
497
} else {
498
return model.getLineContent(lineNumber);
499
}
500
}
501
};
502
const indentOfSecondLine = getGoodIndentForLine(autoIndent, virtualModel, model.getLanguageId(), startLineNumber + 1, indentConverter, this._languageConfigurationService);
503
if (indentOfSecondLine !== null) {
504
const newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize);
505
const oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize);
506
507
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
508
const spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
509
for (let i = startLineNumber + 1; i <= range.endLineNumber; i++) {
510
const lineContent = model.getLineContent(i);
511
const originalIndent = strings.getLeadingWhitespace(lineContent);
512
const originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
513
const newSpacesCnt = originalSpacesCnt + spaceCntOffset;
514
const newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
515
516
if (newIndent !== originalIndent) {
517
textEdits.push({
518
range: new Range(i, 1, i, originalIndent.length + 1),
519
text: newIndent
520
});
521
}
522
}
523
}
524
}
525
}
526
527
if (textEdits.length > 0) {
528
this.editor.pushUndoStop();
529
const cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!);
530
this.editor.executeCommand('autoIndentOnPaste', cmd);
531
this.editor.pushUndoStop();
532
}
533
}
534
535
private rangeContainsOnlyWhitespaceCharacters(model: ITextModel, range: Range): boolean {
536
const lineContainsOnlyWhitespace = (content: string): boolean => {
537
return content.trim().length === 0;
538
};
539
let containsOnlyWhitespace: boolean = true;
540
if (range.startLineNumber === range.endLineNumber) {
541
const lineContent = model.getLineContent(range.startLineNumber);
542
const linePart = lineContent.substring(range.startColumn - 1, range.endColumn - 1);
543
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
544
} else {
545
for (let i = range.startLineNumber; i <= range.endLineNumber; i++) {
546
const lineContent = model.getLineContent(i);
547
if (i === range.startLineNumber) {
548
const linePart = lineContent.substring(range.startColumn - 1);
549
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
550
} else if (i === range.endLineNumber) {
551
const linePart = lineContent.substring(0, range.endColumn - 1);
552
containsOnlyWhitespace = lineContainsOnlyWhitespace(linePart);
553
} else {
554
containsOnlyWhitespace = model.getLineFirstNonWhitespaceColumn(i) === 0;
555
}
556
if (!containsOnlyWhitespace) {
557
break;
558
}
559
}
560
}
561
return containsOnlyWhitespace;
562
}
563
564
public dispose(): void {
565
this.callOnDispose.dispose();
566
this.callOnModel.dispose();
567
}
568
}
569
570
function isStartOrEndInString(model: ITextModel, range: Range): boolean {
571
const isPositionInString = (position: Position): boolean => {
572
const tokenType = getStandardTokenTypeAtPosition(model, position);
573
return tokenType === StandardTokenType.String;
574
};
575
return isPositionInString(range.getStartPosition()) || isPositionInString(range.getEndPosition());
576
}
577
578
function getIndentationEditOperations(model: ITextModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
579
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
580
// Model is empty
581
return;
582
}
583
584
let spaces = '';
585
for (let i = 0; i < tabSize; i++) {
586
spaces += ' ';
587
}
588
589
const spacesRegExp = new RegExp(spaces, 'gi');
590
591
for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber <= lineCount; lineNumber++) {
592
let lastIndentationColumn = model.getLineFirstNonWhitespaceColumn(lineNumber);
593
if (lastIndentationColumn === 0) {
594
lastIndentationColumn = model.getLineMaxColumn(lineNumber);
595
}
596
597
if (lastIndentationColumn === 1) {
598
continue;
599
}
600
601
const originalIndentationRange = new Range(lineNumber, 1, lineNumber, lastIndentationColumn);
602
const originalIndentation = model.getValueInRange(originalIndentationRange);
603
const newIndentation = (
604
tabsToSpaces
605
? originalIndentation.replace(/\t/ig, spaces)
606
: originalIndentation.replace(spacesRegExp, '\t')
607
);
608
609
builder.addEditOperation(originalIndentationRange, newIndentation);
610
}
611
}
612
613
export class IndentationToSpacesCommand implements ICommand {
614
615
private selectionId: string | null = null;
616
617
constructor(private readonly selection: Selection, private tabSize: number) { }
618
619
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
620
this.selectionId = builder.trackSelection(this.selection);
621
getIndentationEditOperations(model, builder, this.tabSize, true);
622
}
623
624
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
625
return helper.getTrackedSelection(this.selectionId!);
626
}
627
}
628
629
export class IndentationToTabsCommand implements ICommand {
630
631
private selectionId: string | null = null;
632
633
constructor(private readonly selection: Selection, private tabSize: number) { }
634
635
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
636
this.selectionId = builder.trackSelection(this.selection);
637
getIndentationEditOperations(model, builder, this.tabSize, false);
638
}
639
640
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
641
return helper.getTrackedSelection(this.selectionId!);
642
}
643
}
644
645
registerEditorContribution(AutoIndentOnPaste.ID, AutoIndentOnPaste, EditorContributionInstantiation.BeforeFirstInteraction);
646
registerEditorAction(IndentationToSpacesAction);
647
registerEditorAction(IndentationToTabsAction);
648
registerEditorAction(IndentUsingTabs);
649
registerEditorAction(IndentUsingSpaces);
650
registerEditorAction(ChangeTabDisplaySize);
651
registerEditorAction(DetectIndentation);
652
registerEditorAction(ReindentLinesAction);
653
registerEditorAction(ReindentSelectedLinesAction);
654
655