diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts index ef78607a7d59..5a869f83bd2f 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/model'; -import { IMultiDiffEditorViewState, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { IMultiDiffEditorViewState, IMultiDiffResource, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import './colors'; @@ -19,6 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { Range } from 'vs/editor/common/core/range'; export class MultiDiffEditorWidget extends Disposable { private readonly _dimension = observableValue(this, undefined); @@ -45,6 +46,10 @@ export class MultiDiffEditorWidget extends Disposable { this._register(recomputeInitiallyAndOnChange(this._widgetImpl)); } + public reveal(resource: IMultiDiffResource, range: Range): void { + this._widgetImpl.get().reveal(resource, range); + } + public createViewModel(model: IMultiDiffEditorModel): MultiDiffEditorViewModel { return new MultiDiffEditorViewModel(model, this._instantiationService); } diff --git a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts index 129d91151b1a..ca4e240c1dac 100644 --- a/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl.ts @@ -25,6 +25,9 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; export class MultiDiffEditorWidgetImpl extends Disposable { private readonly _elements = h('div.monaco-component.multiDiffEditor', [ @@ -104,6 +107,7 @@ export class MultiDiffEditorWidgetImpl extends Disposable { private readonly _workbenchUIElementFactory: IWorkbenchUIElementFactory, @IContextKeyService private readonly _parentContextKeyService: IContextKeyService, @IInstantiationService private readonly _parentInstantiationService: IInstantiationService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); @@ -186,6 +190,23 @@ export class MultiDiffEditorWidgetImpl extends Disposable { this._scrollableElement.setScrollPosition({ scrollLeft: scrollState.left, scrollTop: scrollState.top }); } + // todo@aiday-mar need to reveal the range instead of just the start line number + public reveal(resource: IMultiDiffResource, range: Range): void { + const viewItems = this._viewItems.get(); + let searchCallback: (item: VirtualizedViewItem) => boolean; + if ('original' in resource) { + searchCallback = (item) => item.viewModel.originalUri?.toString() === resource.original.toString(); + } else { + searchCallback = (item) => item.viewModel.modifiedUri?.toString() === resource.modified.toString(); + } + const index = viewItems.findIndex(searchCallback); + let scrollTop = (range.startLineNumber - 1) * this._configurationService.getValue('editor.lineHeight'); + for (let i = 0; i < index; i++) { + scrollTop += viewItems[i].contentHeight.get() + this._spaceBetweenPx; + } + this._scrollableElement.setScrollPosition({ scrollTop }); + } + public getViewState(): IMultiDiffEditorViewState { return { scrollState: { @@ -279,6 +300,19 @@ interface IMultiDiffDocState { selections?: ISelection[]; } +export interface IMultiDiffEditorOptions extends ITextEditorOptions { + viewState?: IMultiDiffEditorOptionsViewState; +} + +export interface IMultiDiffEditorOptionsViewState { + revealData?: { + resource: IMultiDiffResource; + range: Range; + }; +} + +export type IMultiDiffResource = { original: URI } | { modified: URI }; + class VirtualizedViewItem extends Disposable { private readonly _templateRef = this._register(disposableObservableValue | undefined>(this, undefined)); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 4a18e62f2563..8739a79bbbb8 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; +import { BulkEditPreviewProvider, BulkFileOperation, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { URI } from 'vs/base/common/uri'; @@ -24,11 +24,9 @@ import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/cont import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { basename, dirname } from 'vs/base/common/resources'; import { MenuId } from 'vs/platform/actions/common/actions'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IViewDescriptorService } from 'vs/workbench/common/views'; @@ -38,6 +36,9 @@ import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Mutable } from 'vs/base/common/types'; +import { IResourceDiffEditorInput } from 'vs/workbench/common/editor'; +import { Range } from 'vs/editor/common/core/range'; +import { IMultiDiffEditorOptions } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; const enum State { Data = 'data', @@ -68,7 +69,8 @@ export class BulkEditPane extends ViewPane { private _currentResolve?: (edit?: ResourceEdit[]) => void; private _currentInput?: BulkFileOperations; private _currentProvider?: BulkEditPreviewProvider; - + private _fileOperations?: BulkFileOperation[]; + private _resources?: IResourceDiffEditorInput[]; constructor( options: IViewletViewOptions, @@ -143,7 +145,7 @@ export class BulkEditPane extends ViewPane { ); this._disposables.add(this._tree.onContextMenu(this._onContextMenu, this)); - this._disposables.add(this._tree.onDidOpen(e => this._openElementAsEditor(e))); + this._disposables.add(this._tree.onDidOpen(e => this._openElementInMultiDiffEditor(e))); // buttons const buttonsContainer = document.createElement('div'); @@ -316,65 +318,76 @@ export class BulkEditPane extends ViewPane { } } - private async _openElementAsEditor(e: IOpenEvent): Promise { + private async _openElementInMultiDiffEditor(e: IOpenEvent): Promise { - const options: Mutable = { ...e.editorOptions }; + const fileOperations = this._currentInput?.fileOperations; + if (!fileOperations) { + return; + } let fileElement: FileElement; if (e.element instanceof TextEditElement) { fileElement = e.element.parent; - options.selection = e.element.edit.textEdit.textEdit.range; - } else if (e.element instanceof FileElement) { fileElement = e.element; - options.selection = e.element.edit.textEdits[0]?.textEdit.textEdit.range; - } else { // invalid event return; } - const previewUri = this._currentProvider!.asPreviewUri(fileElement.edit.uri); - - if (fileElement.edit.type & BulkFileOperationType.Delete) { - // delete -> show single editor - this._editorService.openEditor({ - label: localize('edt.title.del', "{0} (delete, refactor preview)", basename(fileElement.edit.uri)), - resource: previewUri, - options - }); - - } else { - // rename, create, edits -> show diff editr - let leftResource: URI | undefined; - try { - (await this._textModelService.createModelReference(fileElement.edit.uri)).dispose(); - leftResource = fileElement.edit.uri; - } catch { - leftResource = BulkEditPreviewProvider.emptyPreview; + const resources = await this._resolveResources(fileOperations); + const options: Mutable = { + ...e.editorOptions, + viewState: { + revealData: { + resource: { original: fileElement.edit.uri }, + range: new Range(1, 1, 1, 1) + } } + }; + const multiDiffSource = URI.from({ scheme: 'refactor-preview' }); + const label = 'Refactor Preview'; + this._editorService.openEditor({ + multiDiffSource, + resources, + label, + options, + description: label + }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + } - let typeLabel: string | undefined; - if (fileElement.edit.type & BulkFileOperationType.Rename) { - typeLabel = localize('rename', "rename"); - } else if (fileElement.edit.type & BulkFileOperationType.Create) { - typeLabel = localize('create', "create"); - } - - let label: string; - if (typeLabel) { - label = localize('edt.title.2', "{0} ({1}, refactor preview)", basename(fileElement.edit.uri), typeLabel); - } else { - label = localize('edt.title.1', "{0} (refactor preview)", basename(fileElement.edit.uri)); - } - - this._editorService.openEditor({ - original: { resource: leftResource }, - modified: { resource: previewUri }, - label, - description: this._labelService.getUriLabel(dirname(leftResource), { relative: true }), - options - }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + private async _resolveResources(fileOperations: BulkFileOperation[]): Promise { + if (this._fileOperations === fileOperations && this._resources) { + return this._resources; } + const resources: IResourceDiffEditorInput[] = []; + for (const operation of fileOperations) { + const operationUri = operation.uri; + const previewUri = this._currentProvider!.asPreviewUri(operationUri); + // delete -> show single editor + if (operation.type & BulkFileOperationType.Delete) { + resources.push({ + original: { resource: undefined }, + modified: { resource: URI.revive(previewUri) } + }); + + } else { + // rename, create, edits -> show diff editr + let leftResource: URI | undefined; + try { + (await this._textModelService.createModelReference(operationUri)).dispose(); + leftResource = operationUri; + } catch { + leftResource = BulkEditPreviewProvider.emptyPreview; + } + resources.push({ + original: { resource: URI.revive(leftResource) }, + modified: { resource: URI.revive(previewUri) } + }); + } + } + this._fileOperations = fileOperations; + this._resources = resources; + return resources; } private _onContextMenu(e: ITreeContextMenuEvent): void { diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index a879f7b85c48..bb8bdc7c16e5 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -8,7 +8,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { MultiDiffEditorWidget } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidget'; import { IResourceLabel, IWorkbenchUIElementFactory } from 'vs/editor/browser/widget/multiDiffEditorWidget/workbenchUIElementFactory'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -24,7 +23,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { URI } from 'vs/base/common/uri'; import { MultiDiffEditorViewModel } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorViewModel'; -import { IMultiDiffEditorViewState } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; +import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from 'vs/editor/browser/widget/multiDiffEditorWidget/multiDiffEditorWidgetImpl'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; @@ -72,7 +71,7 @@ export class MultiDiffEditor extends AbstractEditorWithViewState { + override async setInput(input: MultiDiffEditorInput, options: IMultiDiffEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { await super.setInput(input, options, context, token); this._viewModel = await input.getViewModel(); this._multiDiffEditorWidget!.setViewModel(this._viewModel); @@ -81,6 +80,19 @@ export class MultiDiffEditor extends AbstractEditorWithViewState {