From ac0e3c615bb3c4f5ebc2f35bea6e0ebcd78138cb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 9 Mar 2017 16:00:56 +0100 Subject: [PATCH] allow bulk edit to change EOL sequences --- src/vs/editor/common/services/bulkEdit.ts | 46 +++++++++++++------ src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../node/extHostDocumentSaveParticipant.ts | 10 ++-- src/vs/workbench/api/node/extHostWorkspace.ts | 4 +- .../workbench/api/node/mainThreadWorkspace.ts | 4 +- .../parts/search/browser/replaceService.ts | 8 ++-- .../extHostDocumentSaveParticipant.test.ts | 8 ++-- 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/common/services/bulkEdit.ts b/src/vs/editor/common/services/bulkEdit.ts index 1bd22951d33..5292fac6f08 100644 --- a/src/vs/editor/common/services/bulkEdit.ts +++ b/src/vs/editor/common/services/bulkEdit.ts @@ -15,15 +15,26 @@ import { IFileService, IFileChange } from 'vs/platform/files/common/files'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IIdentifiedSingleEditOperation, IModel, IRange, ISelection, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; +import { IIdentifiedSingleEditOperation, IModel, IRange, ISelection, EndOfLineSequence, ICommonCodeEditor } from 'vs/editor/common/editorCommon'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; -export interface IResourceEdit { +export interface IResourceTextEdit { resource: URI; range?: IRange; newText: string; } +export interface IResourceEOLEdit { + resource: URI; + eol: EndOfLineSequence; +} + +export type IResourceEdit = IResourceTextEdit | IResourceEOLEdit; + +function isIEndOfLineSequenceEdit(thing: any): thing is IResourceEOLEdit { + return thing && URI.isUri((thing).resource) && typeof (thing).eol === 'number'; +} + interface IRecording { stop(): void; hasChanged(resource: URI): boolean; @@ -74,6 +85,7 @@ class EditTask implements IDisposable { private get _model(): IModel { return this._modelReference.object.textEditorModel; } private _modelReference: IReference; private _edits: IIdentifiedSingleEditOperation[]; + private _newEol: EndOfLineSequence; constructor(modelReference: IReference) { this._endCursorSelection = null; @@ -82,23 +94,31 @@ class EditTask implements IDisposable { } public addEdit(edit: IResourceEdit): void { - let range: IRange; - if (!edit.range) { - range = this._model.getFullModelRange(); + if (isIEndOfLineSequenceEdit(edit)) { + // store new EOL-sequence, last wins + this._newEol = edit.eol; + } else { - range = edit.range; + // create edit operation + let range: IRange; + if (!edit.range) { + range = this._model.getFullModelRange(); + } else { + range = edit.range; + } + this._edits.push(EditOperation.replaceMove(Range.lift(range), edit.newText)); } - this._edits.push(EditOperation.replaceMove(Range.lift(range), edit.newText)); } public apply(): void { - if (this._edits.length === 0) { - return; + if (this._edits.length > 0) { + this._edits.sort(EditTask._editCompare); + this._initialSelections = this._getInitialSelections(); + this._model.pushEditOperations(this._initialSelections, this._edits, (edits) => this._getEndCursorSelections(edits)); + } + if (this._newEol !== undefined) { + this._model.setEOL(this._newEol); } - this._edits.sort(EditTask._editCompare); - - this._initialSelections = this._getInitialSelections(); - this._model.pushEditOperations(this._initialSelections, this._edits, (edits) => this._getEndCursorSelections(edits)); } protected _getInitialSelections(): Selection[] { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 51d64d7dc8e..c58fbe0c4f2 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -26,7 +26,7 @@ import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; -import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { IResourceTextEdit } from 'vs/editor/common/services/bulkEdit'; import { ITextSource } from 'vs/editor/common/model/textSource'; import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; @@ -234,7 +234,7 @@ export abstract class MainThreadWorkspaceShape { $startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable { throw ni(); } $cancelSearch(requestId: number): Thenable { throw ni(); } $saveAll(includeUntitled?: boolean): Thenable { throw ni(); } - $applyWorkspaceEdit(edits: IResourceEdit[]): TPromise { throw ni(); } + $applyWorkspaceEdit(edits: IResourceTextEdit[]): TPromise { throw ni(); } } export abstract class MainProcessExtensionServiceShape { diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 038b7d42ea8..cd92d74a952 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -13,7 +13,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { MainThreadWorkspaceShape, ExtHostDocumentSaveParticipantShape } from 'vs/workbench/api/node/extHost.protocol'; import { TextEdit } from 'vs/workbench/api/node/extHostTypes'; import { fromRange, TextDocumentSaveReason } from 'vs/workbench/api/node/extHostTypeConverters'; -import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { IResourceTextEdit } from 'vs/editor/common/services/bulkEdit'; import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; @@ -103,8 +103,8 @@ export class ExtHostDocumentSaveParticipant extends ExtHostDocumentSaveParticipa const promises: TPromise[] = []; - const {document, reason} = stubEvent; - const {version} = document; + const { document, reason } = stubEvent; + const { version } = document; const event = Object.freeze({ document, @@ -134,10 +134,10 @@ export class ExtHostDocumentSaveParticipant extends ExtHostDocumentSaveParticipa }).then(values => { - const edits: IResourceEdit[] = []; + const edits: IResourceTextEdit[] = []; for (const value of values) { if (Array.isArray(value) && (value).every(e => e instanceof TextEdit)) { - for (const {newText, range} of value) { + for (const { newText, range } of value) { edits.push({ resource: document.uri, range: fromRange(range), diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index d7d52f02c37..b63e432e583 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri'; import { normalize } from 'vs/base/common/paths'; import { relative } from 'path'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; -import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { IResourceTextEdit } from 'vs/editor/common/services/bulkEdit'; import { TPromise } from 'vs/base/common/winjs.base'; import { fromRange } from 'vs/workbench/api/node/extHostTypeConverters'; import { MainContext, MainThreadWorkspaceShape } from './extHost.protocol'; @@ -70,7 +70,7 @@ export class ExtHostWorkspace { appyEdit(edit: vscode.WorkspaceEdit): TPromise { - let resourceEdits: IResourceEdit[] = []; + let resourceEdits: IResourceTextEdit[] = []; let entries = edit.entries(); for (let entry of entries) { diff --git a/src/vs/workbench/api/node/mainThreadWorkspace.ts b/src/vs/workbench/api/node/mainThreadWorkspace.ts index 78c536ee88f..8e39ee69b05 100644 --- a/src/vs/workbench/api/node/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/node/mainThreadWorkspace.ts @@ -10,7 +10,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ICommonCodeEditor } from 'vs/editor/common/editorCommon'; -import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { bulkEdit, IResourceTextEdit } from 'vs/editor/common/services/bulkEdit'; import { TPromise } from 'vs/base/common/winjs.base'; import { Uri } from 'vscode'; import { MainThreadWorkspaceShape } from './extHost.protocol'; @@ -89,7 +89,7 @@ export class MainThreadWorkspace extends MainThreadWorkspaceShape { }); } - $applyWorkspaceEdit(edits: IResourceEdit[]): TPromise { + $applyWorkspaceEdit(edits: IResourceTextEdit[]): TPromise { let codeEditor: ICommonCodeEditor; let editor = this._editorService.getActiveEditor(); diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 33161e86911..98ed6f1ad1a 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -15,7 +15,7 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/parts/search/common/searchModel'; -import { BulkEdit, IResourceEdit, createBulkEdit } from 'vs/editor/common/services/bulkEdit'; +import { BulkEdit, IResourceTextEdit, createBulkEdit } from 'vs/editor/common/services/bulkEdit'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -182,13 +182,13 @@ export class ReplaceService implements IReplaceService { }); } - private createEdit(match: Match, text: string, resource: URI = null): IResourceEdit { + private createEdit(match: Match, text: string, resource: URI = null): IResourceTextEdit { let fileMatch: FileMatch = match.parent(); - let resourceEdit: IResourceEdit = { + let resourceEdit: IResourceTextEdit = { resource: resource !== null ? resource : fileMatch.resource(), range: match.range(), newText: text }; return resourceEdit; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts index 81aef369d84..57599fb66fb 100644 --- a/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/node/api/extHostDocumentSaveParticipant.test.ts @@ -13,7 +13,7 @@ import { TextDocumentSaveReason, TextEdit, Position } from 'vs/workbench/api/nod import { MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant'; import { OneGetThreadService } from './testThreadService'; -import { IResourceEdit } from 'vs/editor/common/services/bulkEdit'; +import { IResourceTextEdit } from 'vs/editor/common/services/bulkEdit'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; @@ -252,7 +252,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, pushEdits sync', () => { - let edits: IResourceEdit[]; + let edits: IResourceTextEdit[]; const participant = new ExtHostDocumentSaveParticipant(documents, new class extends MainThreadWorkspaceShape { $applyWorkspaceEdit(_edits) { edits = _edits; @@ -273,7 +273,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, concurrent change', () => { - let edits: IResourceEdit[]; + let edits: IResourceTextEdit[]; const participant = new ExtHostDocumentSaveParticipant(documents, new class extends MainThreadWorkspaceShape { $applyWorkspaceEdit(_edits) { edits = _edits; @@ -306,7 +306,7 @@ suite('ExtHostDocumentSaveParticipant', () => { test('event delivery, two listeners -> two document states', () => { const participant = new ExtHostDocumentSaveParticipant(documents, new class extends MainThreadWorkspaceShape { - $applyWorkspaceEdit(_edits: IResourceEdit[]) { + $applyWorkspaceEdit(_edits: IResourceTextEdit[]) { for (const { resource, newText, range } of _edits) { documents.$acceptModelChanged(resource.toString(), [{ -- GitLab