提交 727774d6 编写于 作者: J Johannes Rieken

debt - make bulkEdit a service so that we can add create/delete/rename file to it

上级 8d139ed5
......@@ -150,6 +150,10 @@
"name": "vs/workbench/services/actions",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/bulkEdit",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/services/configuration",
"project": "vscode-workbench"
......@@ -219,4 +223,4 @@
"project": "vscode-preferences"
}
]
}
\ No newline at end of file
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { WorkspaceEdit } from 'vs/editor/common/modes';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from '../editorBrowser';
import { Selection } from 'vs/editor/common/core/selection';
import { IProgressRunner } from 'vs/platform/progress/common/progress';
export const IBulkEditService = createDecorator<IBulkEditService>('IWorkspaceEditService');
export interface IBulkEditOptions {
editor?: ICodeEditor;
progress?: IProgressRunner;
}
export interface IBulkEditResult {
selection: Selection;
ariaSummary: string;
}
export interface IBulkEditService {
_serviceBrand: any;
apply(edit: WorkspaceEdit, options: IBulkEditOptions): TPromise<IBulkEditResult>;
}
......@@ -8,18 +8,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CodeAction } from 'vs/editor/common/modes';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IFileService } from 'vs/platform/files/common/files';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { CodeActionModel, CodeActionsComputeEvent, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
......@@ -27,6 +23,7 @@ import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeAct
import { CodeActionContextMenu } from './codeActionWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { IBulkEditService } from '../../browser/services/bulkEditService';
function contextKeyForSupportedActions(kind: CodeActionKind) {
return ContextKeyExpr.regex(
......@@ -54,8 +51,7 @@ export class QuickFixController implements IEditorContribution {
@ICommandService private readonly _commandService: ICommandService,
@IContextMenuService contextMenuService: IContextMenuService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@ITextModelService private readonly _textModelService: ITextModelService,
@optional(IFileService) private _fileService: IFileService
@IBulkEditService private readonly _bulkEditService: IBulkEditService
) {
this._editor = editor;
this._model = new CodeActionModel(this._editor, markerService, contextKeyService);
......@@ -131,19 +127,18 @@ export class QuickFixController implements IEditorContribution {
}
private async _onApplyCodeAction(action: CodeAction): TPromise<void> {
await applyCodeAction(action, this._textModelService, this._fileService, this._commandService, this._editor);
await applyCodeAction(action, this._bulkEditService, this._commandService, this._editor);
}
}
export async function applyCodeAction(
action: CodeAction,
textModelService: ITextModelService,
fileService: IFileService,
bulkEditService: IBulkEditService,
commandService: ICommandService,
editor: ICodeEditor,
) {
if (action.edit) {
await BulkEdit.perform(action.edit.edits, textModelService, fileService, editor);
await bulkEditService.apply(action.edit, { editor });
}
if (action.command) {
await commandService.executeCommand(action.command.id, ...action.command.arguments);
......@@ -332,4 +327,4 @@ export class OrganizeImportsAction extends EditorAction {
{ kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true },
CodeActionAutoApply.IfSingle);
}
}
\ No newline at end of file
}
......@@ -9,18 +9,14 @@ import * as nls from 'vs/nls';
import { illegalArgument } from 'vs/base/common/errors';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { TPromise } from 'vs/base/common/winjs.base';
import { IFileService } from 'vs/platform/files/common/files';
import { RawContextKey, IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import RenameInputField from './renameInputField';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { asWinJsPromise } from 'vs/base/common/async';
import { WorkspaceEdit, RenameProviderRegistry, RenameProvider, RenameLocation } from 'vs/editor/common/modes';
......@@ -31,6 +27,7 @@ import { MessageController } from 'vs/editor/contrib/message/messageController';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IBulkEditService } from '../../browser/services/bulkEditService';
class RenameSkeleton {
......@@ -111,11 +108,10 @@ class RenameController implements IEditorContribution {
constructor(
private editor: ICodeEditor,
@INotificationService private readonly _notificationService: INotificationService,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IProgressService private readonly _progressService: IProgressService,
@IContextKeyService contextKeyService: IContextKeyService,
@IThemeService themeService: IThemeService,
@optional(IFileService) private _fileService: IFileService
) {
this._renameInputField = new RenameInputField(editor, themeService);
this._renameInputVisible = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService);
......@@ -168,7 +164,6 @@ class RenameController implements IEditorContribution {
this.editor.focus();
const edit = new BulkEdit(this.editor, null, this._textModelResolverService, this._fileService);
const state = new EditorState(this.editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection | CodeEditorStateFlag.Scroll);
const renameOperation = skeleton.provideRenameEdits(newNameOrFocusFlag, 0, [], Range.lift(loc.range).getStartPosition()).then(result => {
......@@ -180,14 +175,15 @@ class RenameController implements IEditorContribution {
}
return undefined;
}
edit.add(result.edits);
return edit.perform().then(selection => {
if (selection) {
this.editor.setSelection(selection);
return this._bulkEditService.apply(result, { editor: this.editor }).then(result => {
if (result.selection) {
this.editor.setSelection(result.selection);
}
// alert
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc.text, newNameOrFocusFlag, edit.ariaMessage()));
if (result.ariaSummary) {
alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc.text, newNameOrFocusFlag, result.ariaSummary));
}
});
}, err => {
......
......@@ -36,12 +36,17 @@ import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/
import { ResolvedKeybinding, Keybinding, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
import { OS } from 'vs/base/common/platform';
import { IRange } from 'vs/editor/common/core/range';
import { IRange, Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { INotificationService, INotification, INotificationHandle, NoOpNotification, IPromptChoice } from 'vs/platform/notification/common/notification';
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions } from 'vs/platform/dialogs/common/dialogs';
import { IPosition, Position as Pos } from 'vs/editor/common/core/position';
import { isEditorConfigurationKey, isDiffEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig';
import { IBulkEditService, IBulkEditOptions, IBulkEditResult } from 'vs/editor/browser/services/bulkEditService';
import { WorkspaceEdit, isResourceTextEdit, TextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { localize } from 'vs/nls';
export class SimpleEditor implements IEditor {
......@@ -643,3 +648,44 @@ export function applyConfigurationValues(configurationService: IConfigurationSer
}
});
}
export class SimpleBulkEditService implements IBulkEditService {
_serviceBrand: any;
constructor(private readonly _modelService: IModelService) {
//
}
apply(workspaceEdit: WorkspaceEdit, options: IBulkEditOptions): TPromise<IBulkEditResult> {
let edits = new Map<ITextModel, TextEdit[]>();
for (let edit of workspaceEdit.edits) {
if (!isResourceTextEdit(edit)) {
return TPromise.wrapError(new Error('bad edit - only text edits are supported'));
}
let model = this._modelService.getModel(edit.resource);
if (!model) {
return TPromise.wrapError(new Error('bad edit - model not found'));
}
let array = edits.get(model);
if (!array) {
array = [];
}
edits.set(model, array.concat(edit.edits));
}
let totalEdits = 0;
let totalFiles = 0;
edits.forEach((edits, model) => {
model.applyEdits(edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)));
totalFiles += 1;
totalEdits += edits.length;
});
return TPromise.as({
selection: undefined,
ariaSummary: localize('summary', 'Made {0} edits in {1} files', totalEdits, totalFiles)
});
}
}
......@@ -33,7 +33,7 @@ import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServ
import {
SimpleConfigurationService, SimpleResourceConfigurationService, SimpleMenuService,
SimpleProgressService, StandaloneCommandService, StandaloneKeybindingService, SimpleNotificationService,
StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService
StandaloneTelemetryService, SimpleWorkspaceContextService, SimpleDialogService, SimpleBulkEditService
} from 'vs/editor/standalone/browser/simpleServices';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IMenuService } from 'vs/platform/actions/common/actions';
......@@ -43,6 +43,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IListService, ListService } from 'vs/platform/list/browser/listService';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
export interface IEditorContextViewService extends IContextViewService {
dispose(): void;
......@@ -192,6 +193,8 @@ export class DynamicStandaloneServices extends Disposable {
ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, notificationService, contextViewService)));
ensure(IMenuService, () => new SimpleMenuService(commandService));
ensure(IBulkEditService, () => new SimpleBulkEditService(StaticServices.modelService.get(IModelService)));
}
public get<T>(serviceId: ServiceIdentifier<T>): T {
......
......@@ -24,6 +24,7 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { isCodeEditor, isDiffEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import URI from 'vs/base/common/uri';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
namespace mapset {
......@@ -310,14 +311,16 @@ export class MainThreadDocumentsAndEditors {
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService
@IEditorGroupService editorGroupService: IEditorGroupService,
@IBulkEditService bulkEditService: IBulkEditService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService);
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadTextEditors = new MainThreadTextEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, textModelResolverService, fileService, this._modelService);
const mainThreadTextEditors = new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._workbenchEditorService, editorGroupService);
extHostContext.set(MainContext.MainThreadTextEditors, mainThreadTextEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.
......
......@@ -4,29 +4,24 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { disposed } from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { equals as objectEquals } from 'vs/base/common/objects';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDecorationRenderOptions, IDecorationOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { IDecorationOptions, IDecorationRenderOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ITextEditorOptions, Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IApplyEditsOptions, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostContext, ExtHostEditorsShape, IExtHostContext, ITextDocumentShowOptions, ITextEditorPositionData, MainThreadTextEditorsShape } from '../node/extHost.protocol';
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import { equals as objectEquals } from 'vs/base/common/objects';
import { ExtHostContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { IModelService } from 'vs/editor/common/services/modelService';
import { isCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { isResourceFileEdit } from 'vs/editor/common/modes';
import { MainThreadTextEditor } from './mainThreadEditor';
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
......@@ -42,11 +37,9 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
documentsAndEditors: MainThreadDocumentsAndEditors,
extHostContext: IExtHostContext,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IFileService private readonly _fileService: IFileService,
@IModelService private readonly _modelService: IModelService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
this._documentsAndEditors = documentsAndEditors;
......@@ -209,31 +202,8 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
}
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto): TPromise<boolean> {
const { edits } = reviveWorkspaceEditDto(dto);
// First check if loaded models were not changed in the meantime
for (let i = 0, len = edits.length; i < len; i++) {
const edit = edits[i];
if (!isResourceFileEdit(edit) && edit.modelVersionId) {
let model = this._modelService.getModel(edit.resource);
if (model && model.getVersionId() !== edit.modelVersionId) {
// model changed in the meantime
return TPromise.as(false);
}
}
}
let codeEditor: ICodeEditor;
let editor = this._workbenchEditorService.getActiveEditor();
if (editor) {
let candidate = editor.getControl();
if (isCodeEditor(candidate)) {
codeEditor = candidate;
}
}
return BulkEdit.perform(edits, this._textModelResolverService, this._fileService, codeEditor).then(() => true);
return this._bulkEditService.apply({ edits }, undefined).then(() => true, err => false);
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
......
......@@ -30,14 +30,13 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IFileService } from 'vs/platform/files/common/files';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { CodeAction } from 'vs/editor/common/modes';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { ICodeActionsOnSaveOptions } from 'vs/editor/common/config/editorOptions';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
export interface ISaveParticipantParticipant extends ISaveParticipant {
// progressMessage: string;
......@@ -270,8 +269,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
class CodeActionOnParticipant implements ISaveParticipant {
constructor(
@ITextModelService private readonly _textModelService: ITextModelService,
@IFileService private readonly _fileService: IFileService,
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@ICommandService private readonly _commandService: ICommandService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IConfigurationService private readonly _configurationService: IConfigurationService
......@@ -309,7 +307,7 @@ class CodeActionOnParticipant implements ISaveParticipant {
private async applyCodeActions(actionsToRun: CodeAction[], editor: ICodeEditor) {
for (const action of actionsToRun) {
await applyCodeAction(action, this._textModelService, this._fileService, this._commandService, editor);
await applyCodeAction(action, this._bulkEditService, this._commandService, editor);
}
}
......
......@@ -15,7 +15,6 @@ 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 } from 'vs/editor/browser/services/bulkEdit';
import { IProgressRunner } from 'vs/platform/progress/common/progress';
import { IDiffEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
......@@ -23,10 +22,10 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IFileService } from 'vs/platform/files/common/files';
import { ResourceTextEdit } from 'vs/editor/common/modes';
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
const REPLACE_PREVIEW = 'replacePreview';
......@@ -95,10 +94,10 @@ export class ReplaceService implements IReplaceService {
public _serviceBrand: any;
constructor(
@IFileService private fileService: IFileService,
@ITextFileService private textFileService: ITextFileService,
@IEditorService private editorService: IWorkbenchEditorService,
@ITextModelService private textModelResolverService: ITextModelService
@ITextModelService private textModelResolverService: ITextModelService,
@IBulkEditService private bulkEditorService: IBulkEditService
) { }
public replace(match: Match): TPromise<any>;
......@@ -108,8 +107,6 @@ export class ReplaceService implements IReplaceService {
const edits: ResourceTextEdit[] = [];
let bulkEdit = new BulkEdit(null, progress, this.textModelResolverService, this.fileService);
if (arg instanceof Match) {
let match = <Match>arg;
edits.push(this.createEdit(match, match.replaceString, resource));
......@@ -128,8 +125,8 @@ export class ReplaceService implements IReplaceService {
});
}
bulkEdit.add(edits);
return bulkEdit.perform().then(() => this.textFileService.saveAll(edits.map(e => e.resource)));
return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource)));
}
public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise<any> {
......
......@@ -4,40 +4,40 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
import { getPathLabel } from 'vs/base/common/labels';
import { IDisposable, IReference, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
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, ITextModel, EndOfLineSequence } from 'vs/editor/common/model';
import { IProgressRunner, emptyProgressRunner, IProgress } from 'vs/platform/progress/common/progress';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { ResourceTextEdit, ResourceFileEdit, isResourceFileEdit, isResourceTextEdit } from 'vs/editor/common/modes';
import { getPathLabel } from 'vs/base/common/labels';
import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { ResourceFileEdit, ResourceTextEdit, WorkspaceEdit, isResourceFileEdit, isResourceTextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
import { localize } from 'vs/nls';
import { FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IProgress, IProgressRunner, emptyProgressRunner } from 'vs/platform/progress/common/progress';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
abstract class Recording {
abstract class IRecording {
static start(fileService: IFileService): Recording {
static start(fileService: IFileService): IRecording {
const _changes = new Set<string>();
let _changes = new Set<string>();
let stop: IDisposable;
if (fileService) {
// watch only when there is a fileservice available
stop = fileService.onFileChanges(event => {
for (const change of event.changes) {
if (change.type === FileChangeType.UPDATED) {
_changes.add(change.resource.toString());
}
stop = fileService.onFileChanges(event => {
for (const change of event.changes) {
if (change.type === FileChangeType.UPDATED) {
_changes.add(change.resource.toString());
}
});
}
}
});
return {
stop() { return dispose(stop); },
......@@ -258,12 +258,6 @@ export type Edit = ResourceFileEdit | ResourceTextEdit;
export class BulkEdit {
static perform(edits: Edit[], textModelService: ITextModelService, fileService: IFileService, editor: ICodeEditor): TPromise<any> {
const edit = new BulkEdit(editor, null, textModelService, fileService);
edit.add(edits);
return edit.perform();
}
private _edits: Edit[] = [];
private _editor: ICodeEditor;
private _progress: IProgressRunner;
......@@ -272,7 +266,7 @@ export class BulkEdit {
editor: ICodeEditor,
progress: IProgressRunner,
@ITextModelService private readonly _textModelService: ITextModelService,
@optional(IFileService) private _fileService: IFileService
@IFileService private readonly _fileService: IFileService
) {
this._editor = editor;
this._progress = progress || emptyProgressRunner;
......@@ -290,11 +284,11 @@ export class BulkEdit {
const editCount = this._edits.reduce((prev, cur) => isResourceFileEdit(cur) ? prev : prev + cur.edits.length, 0);
const resourceCount = this._edits.length;
if (editCount === 0) {
return nls.localize('summary.0', "Made no edits");
return localize('summary.0', "Made no edits");
} else if (editCount > 1 && resourceCount > 1) {
return nls.localize('summary.nm', "Made {0} text edits in {1} files", editCount, resourceCount);
return localize('summary.nm', "Made {0} text edits in {1} files", editCount, resourceCount);
} else {
return nls.localize('summary.n0', "Made {0} text edits in one file", editCount, resourceCount);
return localize('summary.n0', "Made {0} text edits in one file", editCount, resourceCount);
}
}
......@@ -358,7 +352,7 @@ export class BulkEdit {
private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress<void>): TPromise<Selection> {
const recording = IRecording.start(this._fileService);
const recording = Recording.start(this._fileService);
const model = new BulkEditModel(this._textModelService, this._editor, edits, progress);
await model.prepare();
......@@ -371,7 +365,7 @@ export class BulkEdit {
if (conflicts.length > 0) {
model.dispose();
throw new Error(nls.localize('conflict', "These files have changed in the meantime: {0}", conflicts.join(', ')));
throw new Error(localize('conflict', "These files have changed in the meantime: {0}", conflicts.join(', ')));
}
const selection = await model.apply();
......@@ -379,3 +373,59 @@ export class BulkEdit {
return selection;
}
}
export class BulkEditService implements IBulkEditService {
_serviceBrand: any;
constructor(
@IModelService private readonly _modelService: IModelService,
@IWorkbenchEditorService private readonly _workbenchEditorService: IWorkbenchEditorService,
@ITextModelService private readonly _textModelService: ITextModelService,
@IFileService private readonly _fileService: IFileService
) {
}
apply(edit: WorkspaceEdit, options: IBulkEditOptions = {}): TPromise<IBulkEditResult> {
let { edits } = edit;
let codeEditor = options.editor;
// First check if loaded models were not changed in the meantime
for (let i = 0, len = edits.length; i < len; i++) {
const edit = edits[i];
if (!isResourceFileEdit(edit) && typeof edit.modelVersionId === 'number') {
let model = this._modelService.getModel(edit.resource);
if (model && model.getVersionId() !== edit.modelVersionId) {
// model changed in the meantime
return TPromise.wrapError(new Error(`${model.uri.toString()} has changed in the meantime`));
}
}
}
// try to find code editor
// todo@joh, prefer edit that gets edited
if (!codeEditor) {
let editor = this._workbenchEditorService.getActiveEditor();
if (editor) {
let candidate = editor.getControl();
if (isCodeEditor(candidate)) {
codeEditor = candidate;
}
}
}
const bulkEdit = new BulkEdit(options.editor, options.progress, this._textModelService, this._fileService);
bulkEdit.add(edits);
return bulkEdit.perform().then(selection => {
return {
selection,
ariaSummary: bulkEdit.ariaMessage()
};
});
}
}
registerSingleton(IBulkEditService, BulkEditService);
......@@ -27,6 +27,7 @@ import { TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { TPromise } from 'vs/base/common/winjs.base';
import { IFileStat } from 'vs/platform/files/common/files';
import { ResourceTextEdit } from 'vs/editor/common/modes';
import { BulkEditService } from '../../../services/bulkEdit/electron-browser/bulkEditService';
suite('MainThreadEditors', () => {
......@@ -80,6 +81,8 @@ suite('MainThreadEditors', () => {
onEditorGroupMoved = Event.None;
};
const bulkEditService = new BulkEditService(modelService, workbenchEditorService, null, fileService);
const rpcProtocol = new TestRPCProtocol();
rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock<ExtHostDocumentsShape>() {
$acceptModelChanged(): void {
......@@ -101,17 +104,16 @@ suite('MainThreadEditors', () => {
null,
null,
editorGroupService,
bulkEditService,
);
editors = new MainThreadTextEditors(
documentAndEditor,
SingleProxyRPCProtocol(null),
codeEditorService,
bulkEditService,
workbenchEditorService,
editorGroupService,
null,
fileService,
modelService
);
});
......
......@@ -131,3 +131,6 @@ import 'vs/workbench/parts/themes/test/electron-browser/themes.test.contribution
import 'vs/workbench/parts/watermark/electron-browser/watermark';
import 'vs/workbench/parts/welcome/overlay/browser/welcomeOverlay';
// services
import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService';
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册