Path: blob/main/src/vs/editor/contrib/indentation/common/indentation.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as strings from '../../../../base/common/strings.js';6import { ShiftCommand } from '../../../common/commands/shiftCommand.js';7import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js';8import { normalizeIndentation } from '../../../common/core/misc/indentation.js';9import { Selection } from '../../../common/core/selection.js';10import { StandardTokenType } from '../../../common/encodedTokenAttributes.js';11import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';12import { ProcessedIndentRulesSupport } from '../../../common/languages/supports/indentationLineProcessor.js';13import { ITextModel } from '../../../common/model.js';1415export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number): ISingleEditOperation[] {16if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {17// Model is empty18return [];19}2021const indentationRulesSupport = languageConfigurationService.getLanguageConfiguration(model.getLanguageId()).indentRulesSupport;22if (!indentationRulesSupport) {23return [];24}2526const processedIndentRulesSupport = new ProcessedIndentRulesSupport(model, indentationRulesSupport, languageConfigurationService);27endLineNumber = Math.min(endLineNumber, model.getLineCount());2829// Skip `unIndentedLinePattern` lines30while (startLineNumber <= endLineNumber) {31if (!processedIndentRulesSupport.shouldIgnore(startLineNumber)) {32break;33}3435startLineNumber++;36}3738if (startLineNumber > endLineNumber - 1) {39return [];40}4142const { tabSize, indentSize, insertSpaces } = model.getOptions();43const shiftIndent = (indentation: string, count?: number) => {44count = count || 1;45return ShiftCommand.shiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);46};47const unshiftIndent = (indentation: string, count?: number) => {48count = count || 1;49return ShiftCommand.unshiftIndent(indentation, indentation.length + count, tabSize, indentSize, insertSpaces);50};51const indentEdits: ISingleEditOperation[] = [];5253// indentation being passed to lines below5455// Calculate indentation for the first line56// If there is no passed-in indentation, we use the indentation of the first line as base.57const currentLineText = model.getLineContent(startLineNumber);58let globalIndent = strings.getLeadingWhitespace(currentLineText);59// idealIndentForNextLine doesn't equal globalIndent when there is a line matching `indentNextLinePattern`.60let idealIndentForNextLine: string = globalIndent;6162if (processedIndentRulesSupport.shouldIncrease(startLineNumber)) {63idealIndentForNextLine = shiftIndent(idealIndentForNextLine);64globalIndent = shiftIndent(globalIndent);65}66else if (processedIndentRulesSupport.shouldIndentNextLine(startLineNumber)) {67idealIndentForNextLine = shiftIndent(idealIndentForNextLine);68}6970startLineNumber++;7172// Calculate indentation adjustment for all following lines73for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {74if (doesLineStartWithString(model, lineNumber)) {75continue;76}77const text = model.getLineContent(lineNumber);78const oldIndentation = strings.getLeadingWhitespace(text);79const currentIdealIndent = idealIndentForNextLine;8081if (processedIndentRulesSupport.shouldDecrease(lineNumber, currentIdealIndent)) {82idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);83globalIndent = unshiftIndent(globalIndent);84}8586if (oldIndentation !== idealIndentForNextLine) {87indentEdits.push(EditOperation.replaceMove(new Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces)));88}8990// calculate idealIndentForNextLine91if (processedIndentRulesSupport.shouldIgnore(lineNumber)) {92// In reindent phase, if the line matches `unIndentedLinePattern` we inherit indentation from above lines93// but don't change globalIndent and idealIndentForNextLine.94continue;95} else if (processedIndentRulesSupport.shouldIncrease(lineNumber, currentIdealIndent)) {96globalIndent = shiftIndent(globalIndent);97idealIndentForNextLine = globalIndent;98} else if (processedIndentRulesSupport.shouldIndentNextLine(lineNumber, currentIdealIndent)) {99idealIndentForNextLine = shiftIndent(idealIndentForNextLine);100} else {101idealIndentForNextLine = globalIndent;102}103}104105return indentEdits;106}107108function doesLineStartWithString(model: ITextModel, lineNumber: number): boolean {109if (!model.tokenization.isCheapToTokenize(lineNumber)) {110return false;111}112const lineTokens = model.tokenization.getLineTokens(lineNumber);113return lineTokens.getStandardTokenType(0) === StandardTokenType.String;114}115116117