diff --git a/src/vs/editor/common/languages/autoIndent.ts b/src/vs/editor/common/languages/autoIndent.ts index c25ca14744815..5bb2317247fd1 100644 --- a/src/vs/editor/common/languages/autoIndent.ts +++ b/src/vs/editor/common/languages/autoIndent.ts @@ -75,7 +75,8 @@ export function getInheritIndentForLine( model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true, - languageConfigurationService: ILanguageConfigurationService + languageConfigurationService: ILanguageConfigurationService, + indentConverter: IIndentConverter | undefined = undefined ): { indentation: string; action: IndentAction | null; line?: number } | null { if (autoIndent < EditorAutoIndentStrategy.Full) { return null; @@ -152,8 +153,26 @@ export function getInheritIndentForLine( } if (honorIntentialIndent) { + let indentation = strings.getLeadingWhitespace(model.getLineContent(precedingUnIgnoredLine)); + // Check for onEnter rules that should decrease the indent + if (indentConverter) { + const richEditSupport = languageConfigurationService.getLanguageConfiguration(model.tokenization.getLanguageId()); + if (richEditSupport) { + const previousLineText = precedingUnIgnoredLine < 1 ? '' : model.getLineContent(precedingUnIgnoredLine - 1); + const afterEnterText = model.getLineContent(lineNumber); + const enterResult = richEditSupport.onEnter(autoIndent, previousLineText, precedingUnIgnoredLineContent, afterEnterText); + if (enterResult) { + if (enterResult.indentAction === IndentAction.Outdent) { + indentation = indentConverter.unshiftIndent(indentation); + } else if (enterResult.removeText && indentation.length >= enterResult.removeText) { + indentation = indentation.substring(0, indentation.length - enterResult.removeText - 1); + } + } + } + } + return { - indentation: strings.getLeadingWhitespace(model.getLineContent(precedingUnIgnoredLine)), + indentation: indentation, action: null, line: precedingUnIgnoredLine }; @@ -222,7 +241,7 @@ export function getGoodIndentForLine( return null; } - const indent = getInheritIndentForLine(autoIndent, virtualModel, lineNumber, undefined, languageConfigurationService); + const indent = getInheritIndentForLine(autoIndent, virtualModel, lineNumber, undefined, languageConfigurationService, indentConverter); const lineContent = virtualModel.getLineContent(lineNumber); if (indent) { @@ -348,7 +367,7 @@ export function getIndentForEnter( }; const currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent()); - const afterEnterAction = getInheritIndentForLine(autoIndent, virtualModel, range.startLineNumber + 1, undefined, languageConfigurationService); + const afterEnterAction = getInheritIndentForLine(autoIndent, virtualModel, range.startLineNumber + 1, undefined, languageConfigurationService, indentConverter); if (!afterEnterAction) { const beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent; return { @@ -417,7 +436,7 @@ export function getIndentActionForType( if (!indentRulesSupport.shouldDecrease(beforeTypeText + afterTypeText) && indentRulesSupport.shouldDecrease(beforeTypeText + ch + afterTypeText)) { // after typing `ch`, the content matches decreaseIndentPattern, we should adjust the indent to a good manner. // 1. Get inherited indent action - const r = getInheritIndentForLine(autoIndent, model, range.startLineNumber, false, languageConfigurationService); + const r = getInheritIndentForLine(autoIndent, model, range.startLineNumber, false, languageConfigurationService, indentConverter); if (!r) { return null; } diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 3615f378627a6..a2c18024af33e 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -4749,6 +4749,53 @@ suite('Editor Controller', () => { }); }); + test('Issue #136592: onEnterRules should be considered for new line indentation', () => { + const languageId = 'onEnterRules'; + disposables.add(languageService.registerLanguage({ id: languageId })); + disposables.add(languageConfigurationService.register(languageId, { + indentationRules: { + increaseIndentPattern: /if/, + decreaseIndentPattern: /never/ + }, + onEnterRules: [{ + beforeText: /outdent/, + action: { + indentAction: IndentAction.Outdent + } + }] + })); + usingCursor({ + text: [ + 'if (1)', + ' outdent', + '', + 'if (1) {', + ' keep indent', + '', + '}' + ], + languageId: languageId, + }, (editor, model, viewModel) => { + + // Use indent + moveTo(editor, viewModel, 6, 1); + viewModel.type('\n', 'keyboard'); + assert.strictEqual(model.getLineContent(5), ' keep indent'); + assert.strictEqual(model.getLineContent(6), ''); + assert.strictEqual(model.getLineContent(7), ' '); + assertCursor(viewModel, new Position(7, 5)); + + // No indent + moveTo(editor, viewModel, 3, 1); + viewModel.type('\n', 'keyboard'); + assert.strictEqual(model.getLineContent(1), 'if (1)'); + assert.strictEqual(model.getLineContent(2), ' outdent'); + assert.strictEqual(model.getLineContent(3), ''); + assert.strictEqual(model.getLineContent(4), ''); + assertCursor(viewModel, new Position(4, 1)); + }); + }); + test('ElectricCharacter - does nothing if no electric char', () => { usingCursor({ text: [