From dba0fb83719d4c706b4eb41d99757ff445fcd145 Mon Sep 17 00:00:00 2001 From: Grigory Date: Wed, 19 Feb 2025 08:23:57 +0500 Subject: [PATCH 1/4] feat(commands): add `insertFinalNewLine` --- .../commands/insertFinalNewLineCommand.ts | 54 +++++++++++++++++++ .../browser/insertFinalNewLine.ts | 45 ++++++++++++++++ src/vs/editor/editor.all.ts | 1 + 3 files changed, 100 insertions(+) create mode 100644 src/vs/editor/common/commands/insertFinalNewLineCommand.ts create mode 100644 src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts diff --git a/src/vs/editor/common/commands/insertFinalNewLineCommand.ts b/src/vs/editor/common/commands/insertFinalNewLineCommand.ts new file mode 100644 index 0000000000000..4d4785135261d --- /dev/null +++ b/src/vs/editor/common/commands/insertFinalNewLineCommand.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as strings from '../../../base/common/strings.js'; +import { EditOperation, ISingleEditOperation } from '../core/editOperation.js'; +import { Position } from '../core/position.js'; +import { Selection } from '../core/selection.js'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; +import { ITextModel } from '../model.js'; + +export class InsertFinalNewLineCommand implements ICommand { + + private readonly _selection: Selection; + private _selectionId: string | null; + + + constructor(selection: Selection) { + this._selection = selection; + this._selectionId = null; + } + + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { + const op = insertFinalNewLine(model); + if (op) { + builder.addEditOperation(op.range, op.text); + } + this._selectionId = builder.trackSelection(this._selection); + } + + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { + return helper.getTrackedSelection(this._selectionId!); + } +} + +/** + * Generate edit operations for inserting a final new line if needed. + * Returns undefined if no edit is needed. + */ +export function insertFinalNewLine(model: ITextModel): ISingleEditOperation | undefined { + const lineCount = model.getLineCount(); + const lastLine = model.getLineContent(lineCount); + const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1; + + if (!lineCount || lastLineIsEmptyOrWhitespace) { + return undefined; + } + + return EditOperation.insert( + new Position(lineCount, model.getLineMaxColumn(lineCount)), + model.getEOL() + ); +} diff --git a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts new file mode 100644 index 0000000000000..ce8ffbf5d6b13 --- /dev/null +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ICodeEditor } from '../../../browser/editorBrowser.js'; +import { EditorAction, registerEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; +import { InsertFinalNewLineCommand } from '../../../common/commands/insertFinalNewLineCommand.js'; +import { EditorContextKeys } from '../../../common/editorContextKeys.js'; +import * as nls from '../../../../nls.js'; + +export class InsertFinalNewLineAction extends EditorAction { + + public static readonly ID = 'editor.action.insertFinalNewLine'; + + constructor() { + super({ + id: InsertFinalNewLineAction.ID, + label: nls.localize2('insertFinalNewLine', "Insert Final New Line"), + precondition: EditorContextKeys.writable + }); + } + + public run(_accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { + const selection = editor.getSelection(); + if (selection === null) { + return; + } + + const command = new InsertFinalNewLineCommand(selection); + + editor.pushUndoStop(); + editor.executeCommands(this.id, [command]); + editor.pushUndoStop(); + + if (args.reason === 'auto-save') { + // See https://github.com/editorconfig/editorconfig-vscode/issues/330 + // It is very convenient for the editor config extension to invoke this action. + // So, if we get a reason:'auto-save' passed in, let's set cursor back to initial position after inserting final new line. + editor.setSelection(selection); + } + } +} + +registerEditorAction(InsertFinalNewLineAction); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 3af59c96be09f..9edba1675a3fe 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -35,6 +35,7 @@ import './contrib/hover/browser/hoverContribution.js'; import './contrib/indentation/browser/indentation.js'; import './contrib/inlayHints/browser/inlayHintsContribution.js'; import './contrib/inPlaceReplace/browser/inPlaceReplace.js'; +import './contrib/insertFinalNewLine/browser/insertFinalNewLine.js'; import './contrib/lineSelection/browser/lineSelection.js'; import './contrib/linesOperations/browser/linesOperations.js'; import './contrib/linkedEditing/browser/linkedEditing.js'; From 3b9710c5fcb1bb28317f28f17f5c74e087f0ec38 Mon Sep 17 00:00:00 2001 From: Grigory Date: Wed, 19 Feb 2025 09:55:57 +0500 Subject: [PATCH 2/4] refactor: move `insertFinalNewLine` command from `common` folder --- .../insertFinalNewLine/browser/insertFinalNewLine.ts | 2 +- .../browser}/insertFinalNewLineCommand.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/vs/editor/{common/commands => contrib/insertFinalNewLine/browser}/insertFinalNewLineCommand.ts (81%) diff --git a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts index ce8ffbf5d6b13..5762034417a61 100644 --- a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts @@ -5,7 +5,7 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, registerEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; -import { InsertFinalNewLineCommand } from '../../../common/commands/insertFinalNewLineCommand.js'; +import { InsertFinalNewLineCommand } from './insertFinalNewLineCommand.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/common/commands/insertFinalNewLineCommand.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts similarity index 81% rename from src/vs/editor/common/commands/insertFinalNewLineCommand.ts rename to src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts index 4d4785135261d..476419f0baee1 100644 --- a/src/vs/editor/common/commands/insertFinalNewLineCommand.ts +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from '../../../base/common/strings.js'; -import { EditOperation, ISingleEditOperation } from '../core/editOperation.js'; -import { Position } from '../core/position.js'; -import { Selection } from '../core/selection.js'; -import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import * as strings from '../../../../base/common/strings.js'; +import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; +import { Position } from '../../../common/core/position.js'; +import { Selection } from '../../../common/core/selection.js'; +import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; +import { ITextModel } from '../../../common/model.js'; export class InsertFinalNewLineCommand implements ICommand { From 457892a14843fe32261848704db5b1e4447ca88b Mon Sep 17 00:00:00 2001 From: Grigory Date: Wed, 19 Feb 2025 10:50:29 +0500 Subject: [PATCH 3/4] refactor: remove redunant code --- .../insertFinalNewLine/browser/insertFinalNewLine.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts index 5762034417a61..091ccf9788cf5 100644 --- a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLine.ts @@ -32,13 +32,6 @@ export class InsertFinalNewLineAction extends EditorAction { editor.pushUndoStop(); editor.executeCommands(this.id, [command]); editor.pushUndoStop(); - - if (args.reason === 'auto-save') { - // See https://github.com/editorconfig/editorconfig-vscode/issues/330 - // It is very convenient for the editor config extension to invoke this action. - // So, if we get a reason:'auto-save' passed in, let's set cursor back to initial position after inserting final new line. - editor.setSelection(selection); - } } } From 6a0356fa9d906205eb0fbf17dab9283579458089 Mon Sep 17 00:00:00 2001 From: Grigory Date: Wed, 19 Feb 2025 12:51:55 +0500 Subject: [PATCH 4/4] very minor change --- .../insertFinalNewLine/browser/insertFinalNewLineCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts index 476419f0baee1..0b1493d16b710 100644 --- a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts @@ -44,7 +44,7 @@ export function insertFinalNewLine(model: ITextModel): ISingleEditOperation | un const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1; if (!lineCount || lastLineIsEmptyOrWhitespace) { - return undefined; + return; } return EditOperation.insert(