diff --git a/src/DocumentWatcher.ts b/src/DocumentWatcher.ts index fb0bbc6..3bb7907 100644 --- a/src/DocumentWatcher.ts +++ b/src/DocumentWatcher.ts @@ -8,6 +8,7 @@ import { TextEditorOptions, window, workspace, + WorkspaceEdit, } from 'vscode' import { InsertFinalNewline, @@ -75,18 +76,20 @@ export default class DocumentWatcher { const activeEditor = window.activeTextEditor const activeDoc = activeEditor?.document if (activeDoc && activeDoc === e.document && activeEditor) { - selections = activeEditor.selections + selections = [...activeEditor.selections] } const transformations = this.calculatePreSaveTransformations( e.document, e.reason, ) - e.waitUntil(transformations) - if (selections.length) { - const edits = await transformations - if (activeEditor && edits.length) { - activeEditor.selections = selections - } + + const workspaceEdit = new WorkspaceEdit() + const textEdits = await transformations + workspaceEdit.set(e.document.uri, textEdits) + await workspace.applyEdit(workspaceEdit) + + if (activeEditor && textEdits.length && selections.length) { + activeEditor.selections = selections } }), ) diff --git a/src/test/suite/index.test.ts b/src/test/suite/index.test.ts index fa390d8..635cb4a 100644 --- a/src/test/suite/index.test.ts +++ b/src/test/suite/index.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert' import * as os from 'os' -import { Position, window, workspace, WorkspaceEdit } from 'vscode' +import { Position, window, workspace, WorkspaceEdit, Uri } from 'vscode' import { getFixturePath, getOptionsForFixture, wait } from '../testUtils' import * as utils from 'vscode-test-utils' @@ -284,6 +284,25 @@ suite('EditorConfig extension', function () { `editor has insertSpaces: ${options.insertSpaces}`, ) }) + + test('keep selection on format', async () => { + await withSetting('insert_final_newline', 'true', { + fileName: 'test-selection', + }).saveText('foobar') + assert(window.activeTextEditor, 'no active editor') + + // Before saving, the selection is on line 0. This should remain unchanged. + assert.strictEqual( + window.activeTextEditor.selection.start.line, + 0, + 'editor selection start line changed', + ) + assert.strictEqual( + window.activeTextEditor.selection.end.line, + 0, + 'editor selection end line changed', + ) + }) }) function withSetting( @@ -291,15 +310,16 @@ function withSetting( value: string, options: { contents?: string + fileName?: string } = {}, ) { return { async getText() { - return (await createDoc(options.contents)).getText() + return (await createDoc(options.contents, options.fileName)).getText() }, saveText(text: string) { return new Promise(async resolve => { - const doc = await createDoc(options.contents) + const doc = await createDoc(options.contents, options.fileName) workspace.onDidChangeTextDocument(doc.save) workspace.onDidSaveTextDocument(savedDoc => { assert.strictEqual(savedDoc.isDirty, false, 'dirty saved doc') @@ -315,12 +335,16 @@ function withSetting( }) }, } + async function createDoc(contents = '', name = 'test') { + const fixturePath = getFixturePath([rule, value, name]) - async function createDoc(contents = '') { - const uri = await utils.createFile( - contents, - getFixturePath([rule, value, 'test']), - ) + try { + await workspace.fs.delete(Uri.file(fixturePath)) + } catch { + // ignore + } + + const uri = await utils.createFile(contents, fixturePath) const doc = await workspace.openTextDocument(uri) await window.showTextDocument(doc) await wait(50) // wait for EditorConfig to apply new settings