From 16536c25a16ad610cdf4b54e9f0fb6e8fcee24a6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 25 Aug 2022 17:30:42 +0200 Subject: [PATCH 1/7] Implements result menu --- src/vs/platform/actions/common/actions.ts | 1 + .../mergeEditor/browser/commands/commands.ts | 48 +++++++++++++++++++ .../browser/mergeEditor.contribution.ts | 4 +- .../mergeEditor/browser/model/editing.ts | 1 - .../browser/model/mergeEditorModel.ts | 30 +++++++++--- .../view/editors/resultCodeEditorView.ts | 24 +++++++++- 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e9fca5f288b..d05df6a8889 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -168,6 +168,7 @@ export class MenuId { static readonly NewFile = new MenuId('NewFile'); static readonly MergeInput1Toolbar = new MenuId('MergeToolbar1Toolbar'); static readonly MergeInput2Toolbar = new MenuId('MergeToolbar2Toolbar'); + static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar'); /** * Create or reuse a `MenuId` with the given identifier diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts index 284e76467a7..ac6e8aa9c9f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts @@ -454,3 +454,51 @@ export class AcceptAllInput2 extends MergeEditorAction { viewModel.acceptAll(2); } } + +export class ResetToBaseAndAutoMergeCommand extends MergeEditorAction { + constructor() { + super({ + id: 'mergeEditor.resetResultToBaseAndAutoMerge', + category: mergeEditorCategory, + title: { + value: localize( + 'mergeEditor.resetResultToBaseAndAutoMerge', + 'Reset Result' + ), + original: 'Reset Result', + }, + shortTitle: localize('mergeEditor.resetResultToBaseAndAutoMerge.short', 'Reset'), + f1: true, + precondition: ctxIsMergeEditor, + menu: { id: MenuId.MergeInputResultToolbar } + }); + } + + override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { + viewModel.model.resetResultToBaseAndAutoMerge(); + } +} + +export class ResetDirtyConflictsToBaseCommand extends MergeEditorAction { + constructor() { + super({ + id: 'mergeEditor.resetDirtyConflictsToBase', + category: mergeEditorCategory, + title: { + value: localize( + 'mergeEditor.resetDirtyConflictsToBase', + 'Reset Dirty Conflicts In Result To Base' + ), + original: 'Reset Dirty Conflicts In Result To Base', + }, + shortTitle: localize('mergeEditor.resetDirtyConflictsToBase.short', 'Reset Dirty Conflicts To Base'), + f1: true, + precondition: ctxIsMergeEditor, + menu: { id: MenuId.MergeInputResultToolbar } + }); + } + + override runWithViewModel(viewModel: MergeEditorViewModel, accessor: ServicesAccessor): void { + viewModel.model.resetDirtyConflictsToBase(); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index b370ddcad5f..6b88657f8ee 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -11,7 +11,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { AcceptAllInput1, AcceptAllInput2, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { AcceptAllInput1, AcceptAllInput2, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, ResetDirtyConflictsToBaseCommand, ResetToBaseAndAutoMergeCommand, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditor, MergeEditorResolverContribution, MergeEditorOpenHandlerContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -65,6 +65,8 @@ registerAction2(CompareInput2WithBaseCommand); registerAction2(AcceptAllInput1); registerAction2(AcceptAllInput2); +registerAction2(ResetToBaseAndAutoMergeCommand); +registerAction2(ResetDirtyConflictsToBaseCommand); Registry .as(WorkbenchExtensions.Workbench) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts index 9ff067339ea..de2e9e2c6df 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -42,7 +42,6 @@ export class LineEdits { constructor(public readonly edits: readonly LineRangeEdit[]) { } public apply(model: ITextModel): void { - model.pushStackElement(); model.pushEditOperations( null, this.edits.map((e) => { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index c165e579157..8b7304cbafc 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -187,7 +187,7 @@ export class MergeEditorModel extends EditorModel { if (options.resetUnknownOnInitialization) { this.onInitialized.then(() => { - this.resetUnknown(); + this.resetDirtyConflictsToBase(); }); } } @@ -217,20 +217,23 @@ export class MergeEditorModel extends EditorModel { } } - public resetUnknown(): void { + public resetDirtyConflictsToBase(): void { transaction(tx => { /** @description Reset Unknown Base Range States */ + this.resultTextModel.pushStackElement(); for (const range of this.modifiedBaseRanges.get()) { if (this.getState(range).get().conflicting) { - this.setState(range, ModifiedBaseRangeState.default, false, tx); + this.setState(range, ModifiedBaseRangeState.default, false, tx, false); } } + this.resultTextModel.pushStackElement(); }); } - public mergeNonConflictingDiffs(): void { + public acceptNonConflictingDiffs(): void { transaction((tx) => { /** @description Merge None Conflicting Diffs */ + this.resultTextModel.pushStackElement(); for (const m of this.modifiedBaseRanges.get()) { if (m.isConflicting) { continue; @@ -241,9 +244,11 @@ export class MergeEditorModel extends EditorModel { ? ModifiedBaseRangeState.default.withInput1(true) : ModifiedBaseRangeState.default.withInput2(true), true, - tx + tx, + false ); } + this.resultTextModel.pushStackElement(); }); } @@ -259,7 +264,8 @@ export class MergeEditorModel extends EditorModel { baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState, markHandled: boolean, - transaction: ITransaction + transaction: ITransaction, + pushStackElement: boolean = false ): void { if (!this.isUpToDate.get()) { throw new BugIndicatingError('Cannot set state while updating'); @@ -282,7 +288,13 @@ export class MergeEditorModel extends EditorModel { existingState.set(effectiveState, transaction); if (edit) { + if (pushStackElement) { + this.resultTextModel.pushStackElement(); + } this.resultTextModelDiffs.applyEditRelativeToOriginal(edit, transaction); + if (pushStackElement) { + this.resultTextModel.pushStackElement(); + } } if (markHandled) { @@ -353,6 +365,12 @@ export class MergeEditorModel extends EditorModel { this.modelService.setMode(this.input2.textModel, language); this.modelService.setMode(this.resultTextModel, language); } + + public async resetResultToBaseAndAutoMerge() { + this.resultTextModel.setValue(this.base.getValue()); + await waitForState(this.state, state => state === MergeEditorModelState.upToDate); + this.acceptNonConflictingDiffs(); + } } export interface InputData { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index 0632fc504f2..0f5e131f571 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -3,13 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IAction } from 'vs/base/common/actions'; import { CompareResult } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { toDisposable } from 'vs/base/common/lifecycle'; import { autorun, derived } from 'vs/base/common/observable'; import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MergeMarkersController } from 'vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController'; import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; @@ -112,7 +117,10 @@ export class ResultCodeEditorView extends CodeEditorView { }); constructor( - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IContextMenuService contextMenuService: IContextMenuService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, ) { super(instantiationService); @@ -160,5 +168,19 @@ export class ResultCodeEditorView extends CodeEditorView { ); })); + + + // title menu + const titleMenu = menuService.createMenu(MenuId.MergeInputResultToolbar, contextKeyService); + const toolBar = new ToolBar(this.htmlElements.toolbar, contextMenuService); + const toolBarUpdate = () => { + const secondary: IAction[] = []; + createAndFillInActionBarActions(titleMenu, { renderShortTitle: true }, secondary); + toolBar.setActions([], secondary); + }; + this._store.add(toolBar); + this._store.add(titleMenu); + this._store.add(titleMenu.onDidChange(toolBarUpdate)); + toolBarUpdate(); } } From 7688a4024963ccb39556ae223eb4ec8dfe9d879f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 26 Aug 2022 12:01:47 +0200 Subject: [PATCH 2/7] Implements commands to recompute merge with git merge-file --- extensions/git/package.json | 22 ++++++++++ extensions/git/package.nls.json | 2 + extensions/git/src/commands.ts | 42 +++++++++++++++++++ extensions/git/src/git.ts | 21 ++++++++++ .../actions/common/menusExtensionPoint.ts | 6 ++- 5 files changed, 92 insertions(+), 1 deletion(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index ecca4d76e17..34789ea5213 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -609,6 +609,18 @@ "command": "git.openMergeEditor", "title": "%command.git.openMergeEditor%", "category": "Git" + }, + { + "command": "git.runGitMerge", + "title": "%command.git.runGitMerge%", + "category": "Git", + "enablement": "isMergeEditor" + }, + { + "command": "git.runGitMergeDiff3", + "title": "%command.git.runGitMergeDiff3%", + "category": "Git", + "enablement": "isMergeEditor" } ], "keybindings": [ @@ -1568,6 +1580,16 @@ "when": "config.git.enabled && !git.missing && !isInDiffEditor && !isMergeEditor && resource in git.mergeChanges" } ], + "mergeEditor/result/context": [ + { + "command": "git.runGitMerge", + "when": "isMergeEditor" + }, + { + "command": "git.runGitMergeDiff3", + "when": "isMergeEditor" + } + ], "scm/change/title": [ { "command": "git.stageChange", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index eaf627c3731..1aba43cb86a 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -104,6 +104,8 @@ "command.api.getRemoteSources": "Get Remote Sources", "command.git.acceptMerge": "Accept Merge", "command.git.openMergeEditor": "Open in Merge Editor", + "command.git.runGitMerge": "Compute Conflicts With Git", + "command.git.runGitMergeDiff3": "Compute Conflicts With Git (Diff3)", "config.enabled": "Whether git is enabled.", "config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.", "config.autoRepositoryDetection": "Configures when repositories should be automatically detected.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 00827778a0e..65a9ba9ec98 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1134,6 +1134,48 @@ export class CommandCenter { } } + @command('git.runGitMerge') + async runGitMergeNoDiff3(): Promise { + await this.runGitMerge(false); + } + + @command('git.runGitMergeDiff3') + async runGitMergeDiff3(): Promise { + await this.runGitMerge(true); + } + + private async runGitMerge(diff3: boolean): Promise { + const { activeTab } = window.tabGroups.activeTabGroup; + if (!activeTab) { + return; + } + + const input = activeTab.input as { base: Uri; input1: Uri; input2: Uri; result: Uri }; + + const result = await this.git.mergeFile({ + basePath: input.base.fsPath, + input1Path: input.input1.fsPath, + input2Path: input.input2.fsPath, + diff3, + }); + + const doc = workspace.textDocuments.find(doc => doc.uri.toString() === input.result.toString()); + if (!doc) { + return; + } + const e = new WorkspaceEdit(); + + e.replace( + input.result, + new Range( + new Position(0, 0), + new Position(doc.lineCount, 0), + ), + result + ); + await workspace.applyEdit(e); + } + private async _stageChanges(textEditor: TextEditor, changes: LineChange[]): Promise { const modifiedDocument = textEditor.document; const modifiedUri = modifiedDocument.uri; diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 392ec661728..55c55bcdf7b 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -656,6 +656,27 @@ export class Git { private log(output: string): void { this._onOutput.emit('log', output); } + + async mergeFile(options: { input1Path: string; input2Path: string; basePath: string; diff3?: boolean }): Promise { + const args = ['merge-file', '-p', options.input1Path, options.basePath, options.input2Path]; + if (options.diff3) { + args.push('--diff3'); + } else { + args.push('--no-diff3'); + } + + try { + const result = await this.exec('', args); + return result.stdout; + } catch (err) { + if (typeof err.stdout === 'string') { + // The merge had conflicts, stdout still contains the merged result (with conflict markers) + return err.stdout; + } else { + throw err; + } + } + } } export interface Commit { diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 40eea48969a..0cb561ff822 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -291,7 +291,11 @@ const apiMenus: IAPIMenu[] = [ description: localize('webview.context', "The webview context menu"), proposed: 'contribWebviewContext' }, - + { + key: 'mergeEditor/result/context', + id: MenuId.MergeInputResultToolbar, + description: localize('menus.mergeEditorResult', "The result toolbar of the merge editor"), + }, ]; namespace schema { From 2b53e15c8108952611d0fe4eca299f4784ab0a41 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sun, 28 Aug 2022 15:53:17 +0200 Subject: [PATCH 3/7] Makes some merge editor dev commands available in browser --- .../browser/commands/devCommands.ts | 216 ++++++++++++++++++ .../browser/mergeEditor.contribution.ts | 6 + .../contrib/mergeEditor/common/mergeEditor.ts | 9 + .../electron-sandbox/devCommands.ts | 201 +--------------- .../mergeEditor.contribution.ts | 5 +- 5 files changed, 233 insertions(+), 204 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts new file mode 100644 index 00000000000..1c28237d1bf --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts @@ -0,0 +1,216 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { Codicon } from 'vs/base/common/codicons'; +import { URI } from 'vs/base/common/uri'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { localize } from 'vs/nls'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IResourceMergeEditorInput } from 'vs/workbench/common/editor'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; +import { ctxIsMergeEditor, MergeEditorContents } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class MergeEditorCopyContentsToJSON extends Action2 { + constructor() { + super({ + id: 'merge.dev.copyContentsJson', + category: 'Merge Editor (Dev)', + title: { + value: localize( + 'merge.dev.copyState', + 'Copy Merge Editor State as JSON' + ), + original: 'Copy Merge Editor State as JSON', + }, + icon: Codicon.layoutCentered, + f1: true, + precondition: ctxIsMergeEditor, + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + const clipboardService = accessor.get(IClipboardService); + const notificationService = accessor.get(INotificationService); + + if (!(activeEditorPane instanceof MergeEditor)) { + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") + }); + return; + } + const model = activeEditorPane.model; + if (!model) { + return; + } + const contents: MergeEditorContents = { + languageId: model.resultTextModel.getLanguageId(), + base: model.base.getValue(), + input1: model.input1.textModel.getValue(), + input2: model.input2.textModel.getValue(), + result: model.resultTextModel.getValue(), + initialResult: model.getInitialResultValue(), + }; + const jsonStr = JSON.stringify(contents, undefined, 4); + clipboardService.writeText(jsonStr); + + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor state"), + }); + } +} + +export class MergeEditorSaveContentsToFolder extends Action2 { + constructor() { + super({ + id: 'merge.dev.saveContentsToFolder', + category: 'Merge Editor (Dev)', + title: { + value: localize( + 'merge.dev.saveContentsToFolder', + 'Save Merge Editor State to Folder' + ), + original: 'Save Merge Editor State to Folder', + }, + icon: Codicon.layoutCentered, + f1: true, + precondition: ctxIsMergeEditor, + }); + } + + async run(accessor: ServicesAccessor) { + const { activeEditorPane } = accessor.get(IEditorService); + const notificationService = accessor.get(INotificationService); + const dialogService = accessor.get(IFileDialogService); + const fileService = accessor.get(IFileService); + const languageService = accessor.get(ILanguageService); + + if (!(activeEditorPane instanceof MergeEditor)) { + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") + }); + return; + } + const model = activeEditorPane.model; + if (!model) { + return; + } + + const result = await dialogService.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + title: localize('mergeEditor.selectFolderToSaveTo', 'Select folder to save to') + }); + if (!result) { + return; + } + const targetDir = result[0]; + + const extension = languageService.getExtensions(model.resultTextModel.getLanguageId())[0] || ''; + + async function write(fileName: string, source: string) { + await fileService.writeFile(URI.joinPath(targetDir, fileName + extension), VSBuffer.fromString(source), {}); + } + + await Promise.all([ + write('base', model.base.getValue()), + write('input1', model.input1.textModel.getValue()), + write('input2', model.input2.textModel.getValue()), + write('result', model.resultTextModel.getValue()), + write('initialResult', model.getInitialResultValue()), + ]); + + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.successfullySavedMergeEditorContentsToFolder', "Successfully saved merge editor state to folder"), + }); + } +} + +export class MergeEditorLoadContentsFromFolder extends Action2 { + constructor() { + super({ + id: 'merge.dev.loadContentsFromFolder', + category: 'Merge Editor (Dev)', + title: { + value: localize( + 'merge.dev.loadContentsFromFolder', + 'Load Merge Editor State from Folder' + ), + original: 'Load Merge Editor State from Folder', + }, + icon: Codicon.layoutCentered, + f1: true + }); + } + + async run(accessor: ServicesAccessor, args?: { folderUri?: URI; resultState?: 'initial' | 'current' }) { + const dialogService = accessor.get(IFileDialogService); + const editorService = accessor.get(IEditorService); + const fileService = accessor.get(IFileService); + const quickInputService = accessor.get(IQuickInputService); + + if (!args) { + args = {}; + } + + let targetDir: URI; + if (!args.folderUri) { + const result = await dialogService.showOpenDialog({ + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + title: localize('mergeEditor.selectFolderToSaveTo', 'Select folder to save to') + }); + if (!result) { + return; + } + targetDir = result[0]; + } else { + targetDir = args.folderUri; + } + + const targetDirInfo = await fileService.resolve(targetDir); + + function findFile(name: string) { + return targetDirInfo.children!.find(c => c.name.startsWith(name))?.resource!; + } + + const shouldOpenInitial = await promptOpenInitial(quickInputService, args.resultState); + + const baseUri = findFile('base'); + const input1Uri = findFile('input1'); + const input2Uri = findFile('input2'); + const resultUri = findFile(shouldOpenInitial ? 'initialResult' : 'result'); + + const input: IResourceMergeEditorInput = { + base: { resource: baseUri }, + input1: { resource: input1Uri, label: 'Input 1', description: 'Input 1', detail: '(from file)' }, + input2: { resource: input2Uri, label: 'Input 2', description: 'Input 2', detail: '(from file)' }, + result: { resource: resultUri }, + }; + editorService.openEditor(input); + } +} + +async function promptOpenInitial(quickInputService: IQuickInputService, resultStateOverride?: 'initial' | 'current') { + if (resultStateOverride) { + return resultStateOverride === 'initial'; + } + const result = await quickInputService.pick([{ label: 'result', result: false }, { label: 'initial result', result: true }], { canPickMany: false }); + return result?.result; +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 6b88657f8ee..e595e4c4537 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -12,6 +12,7 @@ import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/ import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; import { AcceptAllInput1, AcceptAllInput2, CompareInput1WithBaseCommand, CompareInput2WithBaseCommand, GoToNextUnhandledConflict, GoToPreviousUnhandledConflict, OpenBaseFile, OpenMergeEditor, OpenResultResource, ResetDirtyConflictsToBaseCommand, ResetToBaseAndAutoMergeCommand, SetColumnLayout, SetMixedLayout, ToggleActiveConflictInput1, ToggleActiveConflictInput2 } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { MergeEditorCopyContentsToJSON, MergeEditorSaveContentsToFolder, MergeEditorLoadContentsFromFolder } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditor, MergeEditorResolverContribution, MergeEditorOpenHandlerContribution } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -68,6 +69,11 @@ registerAction2(AcceptAllInput2); registerAction2(ResetToBaseAndAutoMergeCommand); registerAction2(ResetDirtyConflictsToBaseCommand); +// Dev Commands +registerAction2(MergeEditorCopyContentsToJSON); +registerAction2(MergeEditorSaveContentsToFolder); +registerAction2(MergeEditorLoadContentsFromFolder); + Registry .as(WorkbenchExtensions.Workbench) .registerWorkbenchContribution(MergeEditorOpenHandlerContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts index 56da17d7333..a88895826d2 100644 --- a/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/common/mergeEditor.ts @@ -13,3 +13,12 @@ export const ctxIsMergeResultEditor = new RawContextKey('isMergeResultE export const ctxMergeEditorLayout = new RawContextKey('mergeEditorLayout', 'mixed', { type: 'string', description: localize('editorLayout', 'The layout mode of a merge editor') }); export const ctxMergeBaseUri = new RawContextKey('mergeEditorBaseUri', '', { type: 'string', description: localize('baseUri', 'The uri of the baser of a merge editor') }); export const ctxMergeResultUri = new RawContextKey('mergeEditorResultUri', '', { type: 'string', description: localize('resultUri', 'The uri of the result of a merge editor') }); + +export interface MergeEditorContents { + languageId: string; + base: string; + input1: string; + input2: string; + result: string; + initialResult?: string; +} diff --git a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts index 5d66e435319..7f89c306c20 100644 --- a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts +++ b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts @@ -11,78 +11,14 @@ import { ILanguageService } from 'vs/editor/common/languages/language'; import { localize } from 'vs/nls'; import { Action2 } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IResourceMergeEditorInput } from 'vs/workbench/common/editor'; -import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; -import { ctxIsMergeEditor } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; +import { MergeEditorContents } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -interface MergeEditorContents { - languageId: string; - base: string; - input1: string; - input2: string; - result: string; - initialResult?: string; -} - -export class MergeEditorCopyContentsToJSON extends Action2 { - constructor() { - super({ - id: 'merge.dev.copyContentsJson', - category: 'Merge Editor (Dev)', - title: { - value: localize( - 'merge.dev.copyState', - 'Copy Merge Editor State as JSON' - ), - original: 'Copy Merge Editor State as JSON', - }, - icon: Codicon.layoutCentered, - f1: true, - precondition: ctxIsMergeEditor, - }); - } - - run(accessor: ServicesAccessor): void { - const { activeEditorPane } = accessor.get(IEditorService); - const clipboardService = accessor.get(IClipboardService); - const notificationService = accessor.get(INotificationService); - - if (!(activeEditorPane instanceof MergeEditor)) { - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") - }); - return; - } - const model = activeEditorPane.model; - if (!model) { - return; - } - const contents: MergeEditorContents = { - languageId: model.resultTextModel.getLanguageId(), - base: model.base.getValue(), - input1: model.input1.textModel.getValue(), - input2: model.input2.textModel.getValue(), - result: model.resultTextModel.getValue(), - initialResult: model.getInitialResultValue(), - }; - const jsonStr = JSON.stringify(contents, undefined, 4); - clipboardService.writeText(jsonStr); - - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor state"), - }); - } -} - export class MergeEditorOpenContentsFromJSON extends Action2 { constructor() { super({ @@ -163,141 +99,6 @@ export class MergeEditorOpenContentsFromJSON extends Action2 { } } -export class MergeEditorSaveContentsToFolder extends Action2 { - constructor() { - super({ - id: 'merge.dev.saveContentsToFolder', - category: 'Merge Editor (Dev)', - title: { - value: localize( - 'merge.dev.saveContentsToFolder', - 'Save Merge Editor State to Folder' - ), - original: 'Save Merge Editor State to Folder', - }, - icon: Codicon.layoutCentered, - f1: true, - precondition: ctxIsMergeEditor, - }); - } - - async run(accessor: ServicesAccessor) { - const { activeEditorPane } = accessor.get(IEditorService); - const notificationService = accessor.get(INotificationService); - const dialogService = accessor.get(IFileDialogService); - const fileService = accessor.get(IFileService); - const languageService = accessor.get(ILanguageService); - - if (!(activeEditorPane instanceof MergeEditor)) { - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") - }); - return; - } - const model = activeEditorPane.model; - if (!model) { - return; - } - - const result = await dialogService.showOpenDialog({ - canSelectFiles: false, - canSelectFolders: true, - canSelectMany: false, - title: localize('mergeEditor.selectFolderToSaveTo', 'Select folder to save to') - }); - if (!result) { - return; - } - const targetDir = result[0]; - - const extension = languageService.getExtensions(model.resultTextModel.getLanguageId())[0] || ''; - - async function write(fileName: string, source: string) { - await fileService.writeFile(URI.joinPath(targetDir, fileName + extension), VSBuffer.fromString(source), {}); - } - - await Promise.all([ - write('base', model.base.getValue()), - write('input1', model.input1.textModel.getValue()), - write('input2', model.input2.textModel.getValue()), - write('result', model.resultTextModel.getValue()), - write('initialResult', model.getInitialResultValue()), - ]); - - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.successfullySavedMergeEditorContentsToFolder', "Successfully saved merge editor state to folder"), - }); - } -} - -export class MergeEditorLoadContentsFromFolder extends Action2 { - constructor() { - super({ - id: 'merge.dev.loadContentsFromFolder', - category: 'Merge Editor (Dev)', - title: { - value: localize( - 'merge.dev.loadContentsFromFolder', - 'Load Merge Editor State from Folder' - ), - original: 'Load Merge Editor State from Folder', - }, - icon: Codicon.layoutCentered, - f1: true - }); - } - - async run(accessor: ServicesAccessor, args?: { folderUri?: URI; resultState?: 'initial' | 'current' }) { - const dialogService = accessor.get(IFileDialogService); - const editorService = accessor.get(IEditorService); - const fileService = accessor.get(IFileService); - const quickInputService = accessor.get(IQuickInputService); - - if (!args) { - args = {}; - } - - let targetDir: URI; - if (!args.folderUri) { - const result = await dialogService.showOpenDialog({ - canSelectFiles: false, - canSelectFolders: true, - canSelectMany: false, - title: localize('mergeEditor.selectFolderToSaveTo', 'Select folder to save to') - }); - if (!result) { - return; - } - targetDir = result[0]; - } else { - targetDir = args.folderUri; - } - - const targetDirInfo = await fileService.resolve(targetDir); - - function findFile(name: string) { - return targetDirInfo.children!.find(c => c.name.startsWith(name))?.resource!; - } - - const shouldOpenInitial = await promptOpenInitial(quickInputService, args.resultState); - - const baseUri = findFile('base'); - const input1Uri = findFile('input1'); - const input2Uri = findFile('input2'); - const resultUri = findFile(shouldOpenInitial ? 'initialResult' : 'result'); - - const input: IResourceMergeEditorInput = { - base: { resource: baseUri }, - input1: { resource: input1Uri, label: 'Input 1', description: 'Input 1', detail: '(from file)' }, - input2: { resource: input2Uri, label: 'Input 2', description: 'Input 2', detail: '(from file)' }, - result: { resource: resultUri }, - }; - editorService.openEditor(input); - } -} - async function promptOpenInitial(quickInputService: IQuickInputService, resultStateOverride?: 'initial' | 'current') { if (resultStateOverride) { return resultStateOverride === 'initial'; diff --git a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contribution.ts index dca92757f29..1ee975754b1 100644 --- a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contribution.ts @@ -4,10 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { registerAction2 } from 'vs/platform/actions/common/actions'; -import { MergeEditorCopyContentsToJSON, MergeEditorLoadContentsFromFolder, MergeEditorOpenContentsFromJSON, MergeEditorSaveContentsToFolder } from 'vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands'; +import { MergeEditorOpenContentsFromJSON } from 'vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands'; // Dev Commands -registerAction2(MergeEditorCopyContentsToJSON); registerAction2(MergeEditorOpenContentsFromJSON); -registerAction2(MergeEditorSaveContentsToFolder); -registerAction2(MergeEditorLoadContentsFromFolder); From 6a853ed3f33e63d0ff738568591d6723ce994749 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 2 Sep 2022 09:06:24 +0200 Subject: [PATCH 4/7] Introduces mergeEditorMenus proposal & fixes issues from PR. --- extensions/git/package.json | 5 +++-- extensions/git/src/commands.ts | 5 ++++- .../services/actions/common/menusExtensionPoint.ts | 3 ++- .../services/extensions/common/extensionsApiProposals.ts | 1 + src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts | 6 ++++++ 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index 34789ea5213..15065c4a343 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -18,7 +18,8 @@ "scmSelectedProvider", "scmValidation", "tabInputTextMerge", - "timeline" + "timeline", + "mergeEditorMenus" ], "categories": [ "Other" @@ -1580,7 +1581,7 @@ "when": "config.git.enabled && !git.missing && !isInDiffEditor && !isMergeEditor && resource in git.mergeChanges" } ], - "mergeEditor/result/context": [ + "mergeEditor/result/title": [ { "command": "git.runGitMerge", "when": "isMergeEditor" diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 65a9ba9ec98..5ad7a7ad028 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1150,7 +1150,10 @@ export class CommandCenter { return; } - const input = activeTab.input as { base: Uri; input1: Uri; input2: Uri; result: Uri }; + const input = activeTab.input; + if (!(input instanceof TabInputTextMerge)) { + return; + } const result = await this.git.mergeFile({ basePath: input.base.fsPath, diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 0cb561ff822..cb11cd936c2 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -292,9 +292,10 @@ const apiMenus: IAPIMenu[] = [ proposed: 'contribWebviewContext' }, { - key: 'mergeEditor/result/context', + key: 'mergeEditor/result/title', id: MenuId.MergeInputResultToolbar, description: localize('menus.mergeEditorResult', "The result toolbar of the merge editor"), + proposed: 'mergeEditorMenus' }, ]; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 85ccd21e919..71c9dbb022e 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -37,6 +37,7 @@ export const allApiProposals = Object.freeze({ inlineCompletionsNew: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', + mergeEditorMenus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', diff --git a/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts b/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts new file mode 100644 index 00000000000..6a6e5c22d33 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for `mergeEditor/*` menus From e19f431845cbf5451a6450de6ebb391cd3661316 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 2 Sep 2022 09:15:00 +0200 Subject: [PATCH 5/7] Extracts menu toolbar logic. --- .../browser/view/editors/codeEditorView.ts | 30 ++++++++++++++++++ .../view/editors/inputCodeEditorView.ts | 22 +++++-------- .../view/editors/resultCodeEditorView.ts | 31 ++++++------------- .../mergeEditor/browser/view/mergeEditor.ts | 4 +-- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts index f38adcbc9b2..560d26d9e0c 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts @@ -6,12 +6,18 @@ import { h, reset } from 'vs/base/browser/dom'; import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IAction } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, observableFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuId, IMenuService } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { InputData } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; @@ -110,3 +116,27 @@ export abstract class CodeEditorView extends Disposable { }); } } + +export class TitleMenu extends Disposable { + constructor( + menuId: MenuId, + targetHtmlElement: HTMLElement, + @IContextMenuService contextMenuService: IContextMenuService, + @IMenuService menuService: IMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { + super(); + + const titleMenu = menuService.createMenu(menuId, contextKeyService); + const toolBar = new ToolBar(targetHtmlElement, contextMenuService); + const toolBarUpdate = () => { + const secondary: IAction[] = []; + createAndFillInActionBarActions(titleMenu, { renderShortTitle: true }, secondary); + toolBar.setActions([], secondary); + }; + this._store.add(toolBar); + this._store.add(titleMenu); + this._store.add(titleMenu.onDidChange(toolBarUpdate)); + toolBarUpdate(); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 0b5ef74d836..1d5321d8ca6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -28,7 +28,7 @@ import { InputState, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEd import { applyObservableDecorations, setFields } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from 'vs/workbench/contrib/mergeEditor/browser/view/colors'; import { EditorGutter, IGutterItemInfo, IGutterItemView } from '../editorGutter'; -import { CodeEditorView } from './codeEditorView'; +import { CodeEditorView, TitleMenu } from './codeEditorView'; export class InputCodeEditorView extends CodeEditorView { private readonly decorations = derived(`input${this.inputNumber}.decorations`, reader => { @@ -256,7 +256,6 @@ export class InputCodeEditorView extends CodeEditorView { constructor( public readonly inputNumber: 1 | 2, - titleMenuId: MenuId, @IInstantiationService instantiationService: IInstantiationService, @IContextMenuService contextMenuService: IContextMenuService, @IThemeService themeService: IThemeService, @@ -276,18 +275,13 @@ export class InputCodeEditorView extends CodeEditorView { }) ); - // title menu - const titleMenu = menuService.createMenu(titleMenuId, contextKeyService); - const toolBar = new ToolBar(this.htmlElements.toolbar, contextMenuService); - const toolBarUpdate = () => { - const secondary: IAction[] = []; - createAndFillInActionBarActions(titleMenu, { renderShortTitle: true }, secondary); - toolBar.setActions([], secondary); - }; - this._store.add(toolBar); - this._store.add(titleMenu); - this._store.add(titleMenu.onDidChange(toolBarUpdate)); - toolBarUpdate(); + this._register( + instantiationService.createInstance( + TitleMenu, + inputNumber === 1 ? MenuId.MergeInput1Toolbar : MenuId.MergeInput2Toolbar, + this.htmlElements.title + ) + ); } protected override getEditorContributions(): IEditorContributionDescription[] | undefined { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index 0f5e131f571..094e4c783e2 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -3,18 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IAction } from 'vs/base/common/actions'; import { CompareResult } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { toDisposable } from 'vs/base/common/lifecycle'; import { autorun, derived } from 'vs/base/common/observable'; import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { MergeMarkersController } from 'vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController'; import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; @@ -22,7 +18,7 @@ import { applyObservableDecorations, join } from 'vs/workbench/contrib/mergeEdit import { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from 'vs/workbench/contrib/mergeEditor/browser/view/colors'; import { EditorGutter } from 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter'; import { ctxIsMergeResultEditor } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; -import { CodeEditorView } from './codeEditorView'; +import { CodeEditorView, TitleMenu } from './codeEditorView'; export class ResultCodeEditorView extends CodeEditorView { private readonly decorations = derived('result.decorations', reader => { @@ -118,9 +114,6 @@ export class ResultCodeEditorView extends CodeEditorView { constructor( @IInstantiationService instantiationService: IInstantiationService, - @IContextMenuService contextMenuService: IContextMenuService, - @IMenuService menuService: IMenuService, - @IContextKeyService contextKeyService: IContextKeyService, ) { super(instantiationService); @@ -169,18 +162,12 @@ export class ResultCodeEditorView extends CodeEditorView { })); - - // title menu - const titleMenu = menuService.createMenu(MenuId.MergeInputResultToolbar, contextKeyService); - const toolBar = new ToolBar(this.htmlElements.toolbar, contextMenuService); - const toolBarUpdate = () => { - const secondary: IAction[] = []; - createAndFillInActionBarActions(titleMenu, { renderShortTitle: true }, secondary); - toolBar.setActions([], secondary); - }; - this._store.add(toolBar); - this._store.add(titleMenu); - this._store.add(titleMenu.onDidChange(toolBarUpdate)); - toolBarUpdate(); + this._register( + instantiationService.createInstance( + TitleMenu, + MenuId.MergeInputResultToolbar, + this.htmlElements.title + ) + ); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 758d2966ae1..fb160871d25 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -85,8 +85,8 @@ export class MergeEditor extends AbstractTextEditor { private readonly _sessionDisposables = new DisposableStore(); private _grid!: Grid; - private readonly input1View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 1, MenuId.MergeInput1Toolbar)); - private readonly input2View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 2, MenuId.MergeInput2Toolbar)); + private readonly input1View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 1)); + private readonly input2View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 2)); private readonly inputResultView = this._register(this.instantiationService.createInstance(ResultCodeEditorView)); private readonly _layoutMode: MergeEditorLayout; From d9ffee469d31f8962379d214b9393e7c0b452621 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 2 Sep 2022 17:19:49 +0200 Subject: [PATCH 6/7] Fixes CI --- .../mergeEditor/browser/view/editors/inputCodeEditorView.ts | 6 ++---- .../browser/view/editors/resultCodeEditorView.ts | 2 +- .../contrib/mergeEditor/browser/view/mergeEditor.ts | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 1d5321d8ca6..586cbfbec7f 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -5,19 +5,17 @@ import * as dom from 'vs/base/browser/dom'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { Action, IAction, Separator } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; -import { autorun, derived, IObservable, ISettableObservable, ITransaction, transaction, observableValue } from 'vs/base/common/observable'; +import { autorun, derived, IObservable, ISettableObservable, ITransaction, observableValue, transaction } from 'vs/base/common/observable'; import { noBreakWhitespace } from 'vs/base/common/strings'; import { isDefined } from 'vs/base/common/types'; import { EditorExtensionsRegistry, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model'; import { CodeLensContribution } from 'vs/editor/contrib/codelens/browser/codelensController'; import { localize } from 'vs/nls'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -279,7 +277,7 @@ export class InputCodeEditorView extends CodeEditorView { instantiationService.createInstance( TitleMenu, inputNumber === 1 ? MenuId.MergeInput1Toolbar : MenuId.MergeInput2Toolbar, - this.htmlElements.title + this.htmlElements.toolbar ) ); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index 094e4c783e2..36c5a093c4d 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -166,7 +166,7 @@ export class ResultCodeEditorView extends CodeEditorView { instantiationService.createInstance( TitleMenu, MenuId.MergeInputResultToolbar, - this.htmlElements.title + this.htmlElements.toolbar ) ); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index fb160871d25..b8a01681d4c 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -22,7 +22,6 @@ import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/ed import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; @@ -42,7 +41,7 @@ import { DocumentMapping, getOppositeDirection, MappingDirection } from 'vs/work import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; import { deepMerge, ReentrancyBarrier, thenIfNotDisposed } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { MergeEditorViewModel } from 'vs/workbench/contrib/mergeEditor/browser/view/viewModel'; -import { ctxMergeBaseUri, ctxIsMergeEditor, ctxMergeEditorLayout, ctxMergeResultUri, MergeEditorLayoutTypes } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; +import { ctxIsMergeEditor, ctxMergeBaseUri, ctxMergeEditorLayout, ctxMergeResultUri, MergeEditorLayoutTypes } from 'vs/workbench/contrib/mergeEditor/common/mergeEditor'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorResolverService, MergeEditorInputFactoryFunction, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; From 226be699891d28916e2b0fd9c88b3e012942f364 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 5 Sep 2022 09:59:56 +0200 Subject: [PATCH 7/7] Adresses feedback from PR --- extensions/git/package.json | 2 +- src/vs/workbench/services/actions/common/menusExtensionPoint.ts | 2 +- .../services/extensions/common/extensionsApiProposals.ts | 2 +- ...rMenus.d.ts => vscode.proposed.contribMergeEditorMenus.d.ts} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename src/vscode-dts/{vscode.proposed.mergeEditorMenus.d.ts => vscode.proposed.contribMergeEditorMenus.d.ts} (100%) diff --git a/extensions/git/package.json b/extensions/git/package.json index 15065c4a343..1b67c450a15 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -19,7 +19,7 @@ "scmValidation", "tabInputTextMerge", "timeline", - "mergeEditorMenus" + "contribMergeEditorMenus" ], "categories": [ "Other" diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index cb11cd936c2..93afdc08ace 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -295,7 +295,7 @@ const apiMenus: IAPIMenu[] = [ key: 'mergeEditor/result/title', id: MenuId.MergeInputResultToolbar, description: localize('menus.mergeEditorResult', "The result toolbar of the merge editor"), - proposed: 'mergeEditorMenus' + proposed: 'contribMergeEditorMenus' }, ]; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 71c9dbb022e..6dfba82ba91 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -14,6 +14,7 @@ export const allApiProposals = Object.freeze({ contribEditorContentMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribEditorContentMenu.d.ts', contribLabelFormatterWorkspaceTooltip: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribLabelFormatterWorkspaceTooltip.d.ts', contribMenuBarHome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMenuBarHome.d.ts', + contribMergeEditorMenus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribMergeEditorMenus.d.ts', contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.d.ts', contribShareMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts', contribViewSize: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewSize.d.ts', @@ -37,7 +38,6 @@ export const allApiProposals = Object.freeze({ inlineCompletionsNew: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', - mergeEditorMenus: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', diff --git a/src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts b/src/vscode-dts/vscode.proposed.contribMergeEditorMenus.d.ts similarity index 100% rename from src/vscode-dts/vscode.proposed.mergeEditorMenus.d.ts rename to src/vscode-dts/vscode.proposed.contribMergeEditorMenus.d.ts