Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/indentation/common/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 * as strings from '../../../../base/common/strings.js';
7
import { ShiftCommand } from '../../../common/commands/shiftCommand.js';
8
import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js';
9
import { normalizeIndentation } from '../../../common/core/misc/indentation.js';
10
import { Selection } from '../../../common/core/selection.js';
11
import { StandardTokenType } from '../../../common/encodedTokenAttributes.js';
12
import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
13
import { ProcessedIndentRulesSupport } from '../../../common/languages/supports/indentationLineProcessor.js';
14
import { ITextModel } from '../../../common/model.js';
15
16
export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number): ISingleEditOperation[] {
17
if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
18
// Model is empty
19
return [];
20
}
21
22
const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).indentRulesSupport;
23
if (!indentationRulesSupport) {
24
return [];
25
}
26
27
const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);
28
endLineNumber = Math.min(endLineNumber, model.getLineCount());
29
30
// Skip `unIndentedLinePattern` lines
31
while (startLineNumber <= endLineNumber) {
32
if (!processedIndentRulesSupport.shouldIgnore(startLineNumber)) {
33
break;
34
}
35
36
startLineNumber++;
37
}
38
39
if (startLineNumber > endLineNumber - 1) {
40
return [];
41
}
42
43
const { tabSize, indentSize, insertSpaces } = model.getOptions();
44
const shiftIndent = (indentation: string, count?: number) => {
45
count = count || 1;
46
return ShiftCommand.shiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
47
};
48
const unshiftIndent = (indentation: string, count?: number) => {
49
count = count || 1;
50
return ShiftCommand.unshiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);
51
};
52
const indentEdits: ISingleEditOperation[] = [];
53
54
// indentation being passed to lines below
55
56
// Calculate indentation for the first line
57
// If there is no passed-in indentation, we use the indentation of the first line as base.
58
const currentLineText = model.getLineContent(startLineNumber);
59
let globalIndent = strings.getLeadingWhitespace(currentLineText);
60
// idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`.
61
let idealIndentForNextLine: string = globalIndent;
62
63
if (processedIndentRulesSupport.shouldIncrease(startLineNumber)) {
64
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
65
globalIndent = shiftIndent(globalIndent);
66
}
67
else if (processedIndentRulesSupport.shouldIndentNextLine(startLineNumber)) {
68
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
69
}
70
71
startLineNumber++;
72
73
// Calculate indentation adjustment for all following lines
74
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
75
if (doesLineStartWithString(model, lineNumber)) {
76
continue;
77
}
78
const text = model.getLineContent(lineNumber);
79
const oldIndentation = strings.getLeadingWhitespace(text);
80
const currentIdealIndent = idealIndentForNextLine;
81
82
if (processedIndentRulesSupport.shouldDecrease(lineNumber, currentIdealIndent)) {
83
idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);
84
globalIndent = unshiftIndent(globalIndent);
85
}
86
87
if (oldIndentation !== idealIndentForNextLine) {
88
indentEdits.push(EditOperation.replaceMove(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces)));
89
}
90
91
// calculate idealIndentForNextLine
92
if (processedIndentRulesSupport.shouldIgnore(lineNumber)) {
93
// In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines
94
// but don't change globalIndent and idealIndentForNextLine.
95
continue;
96
} else if (processedIndentRulesSupport.shouldIncrease(lineNumber, currentIdealIndent)) {
97
globalIndent = shiftIndent(globalIndent);
98
idealIndentForNextLine = globalIndent;
99
} else if (processedIndentRulesSupport.shouldIndentNextLine(lineNumber, currentIdealIndent)) {
100
idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
101
} else {
102
idealIndentForNextLine = globalIndent;
103
}
104
}
105
106
return indentEdits;
107
}
108
109
function doesLineStartWithString(model: ITextModel, lineNumber: number): boolean {
110
if (!model.tokenization.isCheapToTokenize(lineNumber)) {
111
return false;
112
}
113
const lineTokens = model.tokenization.getLineTokens(lineNumber);
114
return lineTokens.getStandardTokenType(0) === StandardTokenType.String;
115
}
116
117