Git - move "Open Commit" command from core to the git extension (#202042)

* Initial implementation

* Move the command to the git extension

* Add missing enablement property
This commit is contained in:
Ladislau Szomoru 2024-01-08 21:39:07 +01:00 committed by GitHub
parent 88a6ed6d9b
commit 891a17ab57
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 137 additions and 75 deletions

View file

@ -24,7 +24,8 @@
"tabInputTextMerge", "tabInputTextMerge",
"timeline", "timeline",
"contribMergeEditorMenus", "contribMergeEditorMenus",
"contribSourceControlInputBoxMenu" "contribSourceControlInputBoxMenu",
"contribSourceControlHistoryItemMenu"
], ],
"categories": [ "categories": [
"Other" "Other"
@ -708,7 +709,7 @@
}, },
{ {
"command": "git.timeline.openCommit", "command": "git.timeline.openCommit",
"title": "%command.timelineOpenCommit%", "title": "%command.openCommit%",
"icon": "$(diff-multiple)", "icon": "$(diff-multiple)",
"category": "Git" "category": "Git"
}, },
@ -776,13 +777,22 @@
"command": "git.viewChanges", "command": "git.viewChanges",
"title": "%command.viewChanges%", "title": "%command.viewChanges%",
"icon": "$(diff-multiple)", "icon": "$(diff-multiple)",
"category": "Git" "category": "Git",
"enablement": "!operationInProgress"
}, },
{ {
"command": "git.viewStagedChanges", "command": "git.viewStagedChanges",
"title": "%command.viewStagedChanges%", "title": "%command.viewStagedChanges%",
"icon": "$(diff-multiple)", "icon": "$(diff-multiple)",
"category": "Git" "category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.openCommit",
"title": "%command.openCommit%",
"icon": "$(diff-multiple)",
"category": "Git",
"enablement": "!operationInProgress"
} }
], ],
"continueEditSession": [ "continueEditSession": [
@ -1274,6 +1284,10 @@
{ {
"command": "git.viewStagedChanges", "command": "git.viewStagedChanges",
"when": "false" "when": "false"
},
{
"command": "git.openCommit",
"when": "false"
} }
], ],
"scm/title": [ "scm/title": [
@ -1751,6 +1765,20 @@
"group": "1_modification@3" "group": "1_modification@3"
} }
], ],
"scm/incoming/historyItem/context": [
{
"command": "git.openCommit",
"when": "scmProvider == git && config.multiDiffEditor.experimental.enabled",
"group": "inline@1"
}
],
"scm/outgoing/historyItem/context": [
{
"command": "git.openCommit",
"when": "scmProvider == git && config.multiDiffEditor.experimental.enabled",
"group": "inline@1"
}
],
"editor/title": [ "editor/title": [
{ {
"command": "git.openFile", "command": "git.openFile",

View file

@ -106,7 +106,6 @@
"command.stashDropAll": "Drop All Stashes...", "command.stashDropAll": "Drop All Stashes...",
"command.stashPreview": "Preview Stash...", "command.stashPreview": "Preview Stash...",
"command.timelineOpenDiff": "Open Changes", "command.timelineOpenDiff": "Open Changes",
"command.timelineOpenCommit": "Open Commit",
"command.timelineCopyCommitId": "Copy Commit ID", "command.timelineCopyCommitId": "Copy Commit ID",
"command.timelineCopyCommitMessage": "Copy Commit Message", "command.timelineCopyCommitMessage": "Copy Commit Message",
"command.timelineSelectForCompare": "Select for Compare", "command.timelineSelectForCompare": "Select for Compare",
@ -115,6 +114,7 @@
"command.openRepositoriesInParentFolders": "Open Repositories In Parent Folders", "command.openRepositoriesInParentFolders": "Open Repositories In Parent Folders",
"command.viewChanges": "View Changes", "command.viewChanges": "View Changes",
"command.viewStagedChanges": "View Staged Changes", "command.viewStagedChanges": "View Staged Changes",
"command.openCommit": "Open Commit",
"command.api.getRepositories": "Get Repositories", "command.api.getRepositories": "Get Repositories",
"command.api.getRepositoryState": "Get Repository State", "command.api.getRepositoryState": "Get Repository State",
"command.api.getRemoteSources": "Get Remote Sources", "command.api.getRemoteSources": "Get Remote Sources",

View file

@ -5,7 +5,7 @@
import * as os from 'os'; import * as os from 'os';
import * as path from 'path'; import * as path from 'path';
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon } from 'vscode'; import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry'; import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git'; import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git';
@ -3617,7 +3617,6 @@ export class CommandCenter {
@command('git.timeline.openCommit', { repository: false }) @command('git.timeline.openCommit', { repository: false })
async timelineOpenCommit(item: TimelineItem, uri: Uri | undefined, _source: string) { async timelineOpenCommit(item: TimelineItem, uri: Uri | undefined, _source: string) {
console.log('timelineOpenCommit', item);
if (!GitTimelineItem.is(item)) { if (!GitTimelineItem.is(item)) {
return; return;
} }
@ -3840,6 +3839,29 @@ export class CommandCenter {
commands.executeCommand('vscode.changes', title, args); commands.executeCommand('vscode.changes', title, args);
} }
@command('git.openCommit', { repository: true })
async openCommit(repository: Repository, historyItem: SourceControlHistoryItem): Promise<void> {
if (!repository || !historyItem) {
return;
}
const historyProvider = repository.historyProvider;
const historyItemParentId = historyItem.parentIds.length > 0 ? historyItem.parentIds[0] : undefined;
const historyItemChanges = await historyProvider.provideHistoryItemChanges(historyItem.id, historyItemParentId);
if (historyItemChanges.length === 0) {
return;
}
const modifiedShortRef = historyItem.id.substring(0, 8);
const originalShortRef = historyItem.parentIds.length > 0 ? historyItem.parentIds[0].substring(0, 8) : `${modifiedShortRef}^`;
const title = l10n.t('Changes ({0} ↔ {1})', originalShortRef, modifiedShortRef);
const args = historyItemChanges.map(change => [change.uri, change.originalUri, change.modifiedUri]);
commands.executeCommand('vscode.changes', title, args);
}
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any { private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => { const result = (...args: any[]) => {
let result: Promise<any>; let result: Promise<any>;

View file

@ -796,6 +796,9 @@ export class Repository implements Disposable {
return this.repository.dotGit; return this.repository.dotGit;
} }
private _historyProvider: GitHistoryProvider;
get historyProvider(): GitHistoryProvider { return this._historyProvider; }
private isRepositoryHuge: false | { limit: number } = false; private isRepositoryHuge: false | { limit: number } = false;
private didWarnAboutLimit = false; private didWarnAboutLimit = false;
@ -850,9 +853,9 @@ export class Repository implements Disposable {
this._sourceControl.quickDiffProvider = this; this._sourceControl.quickDiffProvider = this;
const historyProvider = new GitHistoryProvider(this, logger); this._historyProvider = new GitHistoryProvider(this, logger);
this._sourceControl.historyProvider = historyProvider; this._sourceControl.historyProvider = this._historyProvider;
this.disposables.push(historyProvider); this.disposables.push(this._historyProvider);
this._sourceControl.acceptInputCommand = { command: 'git.commit', title: l10n.t('Commit'), arguments: [this._sourceControl] }; this._sourceControl.acceptInputCommand = { command: 'git.commit', title: l10n.t('Commit'), arguments: [this._sourceControl] };
this._sourceControl.inputBox.validateInput = this.validateInput.bind(this); this._sourceControl.inputBox.validateInput = this.validateInput.bind(this);

View file

@ -107,7 +107,8 @@ export class MenuId {
static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare'); static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare');
static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext'); static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext');
static readonly SCMInputBox = new MenuId('SCMInputBox'); static readonly SCMInputBox = new MenuId('SCMInputBox');
static readonly SCMHistoryItem = new MenuId('SCMHistoryItem'); static readonly SCMIncomingHistoryItemContext = new MenuId('SCMIncomingHistoryItemContext');
static readonly SCMOutgoingHistoryItemContext = new MenuId('SCMOutgoingHistoryItemContext');
static readonly SCMChangeContext = new MenuId('SCMChangeContext'); static readonly SCMChangeContext = new MenuId('SCMChangeContext');
static readonly SCMResourceContext = new MenuId('SCMResourceContext'); static readonly SCMResourceContext = new MenuId('SCMResourceContext');
static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare'); static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare');

View file

@ -158,6 +158,7 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider {
id: historyItemGroupBase.id, id: historyItemGroupBase.id,
label: historyItemGroupBase.label, label: historyItemGroupBase.label,
icon: Codicon.arrowCircleDown, icon: Codicon.arrowCircleDown,
direction: 'incoming',
ancestor: ancestor.id, ancestor: ancestor.id,
count: ancestor.behind, count: ancestor.behind,
}; };
@ -167,6 +168,7 @@ class MainThreadSCMHistoryProvider implements ISCMHistoryProvider {
id: historyItemGroup.id, id: historyItemGroup.id,
label: historyItemGroup.label, label: historyItemGroup.label,
icon: Codicon.arrowCircleUp, icon: Codicon.arrowCircleUp,
direction: 'outgoing',
ancestor: ancestor.id, ancestor: ancestor.id,
count: ancestor.ahead, count: ancestor.ahead,
}; };

View file

@ -15,7 +15,7 @@ import { equals } from 'vs/base/common/arrays';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { ISCMHistoryItem, ISCMHistoryProviderMenus } from 'vs/workbench/contrib/scm/common/history'; import { ISCMHistoryItem, ISCMHistoryItemGroupEntry, ISCMHistoryProviderMenus } from 'vs/workbench/contrib/scm/common/history';
function actionEquals(a: IAction, b: IAction): boolean { function actionEquals(a: IAction, b: IAction): boolean {
return a.id === b.id; return a.id === b.id;
@ -177,7 +177,7 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable {
private _historyProviderMenu: SCMHistoryProviderMenus | undefined; private _historyProviderMenu: SCMHistoryProviderMenus | undefined;
get historyProviderMenu(): SCMHistoryProviderMenus | undefined { get historyProviderMenu(): SCMHistoryProviderMenus | undefined {
if (this.provider.historyProvider && !this._historyProviderMenu) { if (this.provider.historyProvider && !this._historyProviderMenu) {
this._historyProviderMenu = this.instantiationService.createInstance(SCMHistoryProviderMenus); this._historyProviderMenu = new SCMHistoryProviderMenus(this.contextKeyService, this.menuService);
this.disposables.add(this._historyProviderMenu); this.disposables.add(this._historyProviderMenu);
} }
@ -189,7 +189,7 @@ export class SCMRepositoryMenus implements ISCMRepositoryMenus, IDisposable {
constructor( constructor(
private readonly provider: ISCMProvider, private readonly provider: ISCMProvider,
@IContextKeyService contextKeyService: IContextKeyService, @IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService private readonly instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@IMenuService private readonly menuService: IMenuService @IMenuService private readonly menuService: IMenuService
) { ) {
this.contextKeyService = contextKeyService.createOverlay([ this.contextKeyService = contextKeyService.createOverlay([
@ -261,11 +261,11 @@ export class SCMHistoryProviderMenus implements ISCMHistoryProviderMenus, IDispo
@IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextKeyService private readonly contextKeyService: IContextKeyService,
@IMenuService private readonly menuService: IMenuService) { } @IMenuService private readonly menuService: IMenuService) { }
getHistoryItemMenu(historyItem: ISCMHistoryItem): IMenu { getHistoryItemMenu(historyItemGroup: ISCMHistoryItemGroupEntry, historyItem: ISCMHistoryItem): IMenu {
return this.getOrCreateHistoryItemMenu(historyItem); return this.getOrCreateHistoryItemMenu(historyItemGroup, historyItem);
} }
private getOrCreateHistoryItemMenu(historyItem: ISCMHistoryItem): IMenu { private getOrCreateHistoryItemMenu(historyItemGroup: ISCMHistoryItemGroupEntry, historyItem: ISCMHistoryItem): IMenu {
let result = this.historyItemMenus.get(historyItem); let result = this.historyItemMenus.get(historyItem);
if (!result) { if (!result) {
@ -273,7 +273,10 @@ export class SCMHistoryProviderMenus implements ISCMHistoryProviderMenus, IDispo
['scmHistoryItem', historyItem.id], ['scmHistoryItem', historyItem.id],
]); ]);
result = this.menuService.createMenu(MenuId.SCMHistoryItem, contextKeyService); const menuId = historyItemGroup.direction === 'incoming' ?
MenuId.SCMIncomingHistoryItemContext : MenuId.SCMOutgoingHistoryItemContext;
result = this.menuService.createMenu(menuId, contextKeyService);
this.historyItemMenus.set(historyItem, result); this.historyItemMenus.set(historyItem, result);
} }

View file

@ -11,7 +11,7 @@ import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/par
import { append, $, Dimension, asCSSUrl, trackFocus, clearNode, prepend } from 'vs/base/browser/dom'; import { append, $, Dimension, asCSSUrl, trackFocus, clearNode, prepend } from 'vs/base/browser/dom';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryProviderCacheEntry, SCMHistoryItemChangeTreeElement, SCMHistoryItemGroupTreeElement, SCMHistoryItemTreeElement, SCMViewSeparatorElement } from 'vs/workbench/contrib/scm/common/history'; import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryProviderCacheEntry, SCMHistoryItemChangeTreeElement, SCMHistoryItemGroupTreeElement, SCMHistoryItemTreeElement, SCMViewSeparatorElement } from 'vs/workbench/contrib/scm/common/history';
import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, REPOSITORIES_VIEW_PANE_ID, ISCMInputValueProviderContext } from 'vs/workbench/contrib/scm/common/scm'; import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, REPOSITORIES_VIEW_PANE_ID, ISCMInputValueProviderContext, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm';
import { ResourceLabels, IResourceLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { ResourceLabels, IResourceLabel, IFileLabelOptions } from 'vs/workbench/browser/labels';
import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@ -21,7 +21,7 @@ import { IContextKeyService, IContextKey, ContextKeyExpr, RawContextKey } from '
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, MenuRegistry, Action2, IMenu } from 'vs/platform/actions/common/actions'; import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, MenuRegistry, Action2, IMenu } from 'vs/platform/actions/common/actions';
import { IAction, ActionRunner, Action, Separator } from 'vs/base/common/actions'; import { IAction, ActionRunner, Action, Separator, IActionRunner } from 'vs/base/common/actions';
import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService';
import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator } from './util'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator } from './util';
@ -808,6 +808,29 @@ class HistoryItemGroupRenderer implements ICompressibleTreeRenderer<SCMHistoryIt
} }
} }
class HistoryItemActionRunner extends ActionRunner {
protected override async runAction(action: IAction, context: SCMHistoryItemTreeElement): Promise<any> {
if (!(action instanceof MenuItemAction)) {
return super.runAction(action, context);
}
const args: (ISCMProvider | ISCMHistoryItem)[] = [];
args.push(context.historyItemGroup.repository.provider);
args.push({
id: context.id,
parentIds: context.parentIds,
label: context.label,
description: context.description,
icon: context.icon,
timestamp: context.timestamp,
statistics: context.statistics,
} satisfies ISCMHistoryItem);
await action.run(...args);
}
}
interface HistoryItemTemplate { interface HistoryItemTemplate {
readonly iconContainer: HTMLElement; readonly iconContainer: HTMLElement;
readonly label: IconLabel; readonly label: IconLabel;
@ -826,6 +849,7 @@ class HistoryItemRenderer implements ICompressibleTreeRenderer<SCMHistoryItemTre
get templateId(): string { return HistoryItemRenderer.TEMPLATE_ID; } get templateId(): string { return HistoryItemRenderer.TEMPLATE_ID; }
constructor( constructor(
private actionRunner: IActionRunner,
private actionViewItemProvider: IActionViewItemProvider, private actionViewItemProvider: IActionViewItemProvider,
@ISCMViewService private scmViewService: ISCMViewService) { } @ISCMViewService private scmViewService: ISCMViewService) { }
@ -840,7 +864,7 @@ class HistoryItemRenderer implements ICompressibleTreeRenderer<SCMHistoryItemTre
const disposables = new DisposableStore(); const disposables = new DisposableStore();
const actionsContainer = append(element, $('.actions')); const actionsContainer = append(element, $('.actions'));
const actionBar = new ActionBar(actionsContainer, { actionViewItemProvider: this.actionViewItemProvider }); const actionBar = new ActionBar(actionsContainer, { actionRunner: this.actionRunner, actionViewItemProvider: this.actionViewItemProvider });
disposables.add(actionBar); disposables.add(actionBar);
const statsContainer = append(element, $('.stats-container')); const statsContainer = append(element, $('.stats-container'));
@ -866,7 +890,8 @@ class HistoryItemRenderer implements ICompressibleTreeRenderer<SCMHistoryItemTre
const menus = this.scmViewService.menus.getRepositoryMenus(historyItem.historyItemGroup.repository.provider); const menus = this.scmViewService.menus.getRepositoryMenus(historyItem.historyItemGroup.repository.provider);
if (menus.historyProviderMenu) { if (menus.historyProviderMenu) {
templateData.elementDisposables.add(connectPrimaryMenuToInlineActionBar(menus.historyProviderMenu.getHistoryItemMenu(historyItem), templateData.actionBar)); const historyItemMenu = menus.historyProviderMenu.getHistoryItemMenu(historyItem.historyItemGroup, historyItem);
templateData.elementDisposables.add(connectPrimaryMenuToInlineActionBar(historyItemMenu, templateData.actionBar));
} }
this.renderStatistics(node, index, templateData, height); this.renderStatistics(node, index, templateData, height);
@ -1698,53 +1723,6 @@ class ExpandAllRepositoriesAction extends ViewAction<SCMViewPane> {
registerAction2(CollapseAllRepositoriesAction); registerAction2(CollapseAllRepositoriesAction);
registerAction2(ExpandAllRepositoriesAction); registerAction2(ExpandAllRepositoriesAction);
class HistoryItemViewChangesAction extends Action2 {
constructor() {
super({
id: `workbench.scm.action.historyItemViewChanges`,
title: localize('historyItemViewChanges', "View Changes"),
icon: Codicon.diffMultiple,
f1: false,
menu: {
id: MenuId.SCMHistoryItem,
group: 'inline',
when: ContextKeyExpr.has('config.multiDiffEditor.experimental.enabled'),
}
});
}
async run(accessor: ServicesAccessor, historyItem: SCMHistoryItemTreeElement): Promise<void> {
const commandService = accessor.get(ICommandService);
const historyProvider = historyItem.historyItemGroup.repository.provider.historyProvider;
if (!historyProvider) {
return;
}
const historyItemParentId = historyItem.parentIds.length > 0 ? historyItem.parentIds[0] : undefined;
const historyItemChanges = await historyProvider.provideHistoryItemChanges(historyItem.id, historyItemParentId);
if (!historyItemChanges || historyItemChanges.length === 0) {
return;
}
let [originalRef, modifiedRef] = historyItem.id.includes('..')
? historyItem.id.split('..').map(id => id.substring(0, 8)) : [undefined, historyItem.id.substring(0, 8)];
if (!originalRef) {
originalRef = historyItem.parentIds.length > 0 ? historyItem.parentIds[0].substring(0, 8) : `${modifiedRef}^`;
}
const title = localize('historyItemChangesTitle', "Changes ({0} ↔ {1})", originalRef, modifiedRef);
const args = historyItemChanges.map(change => [change.uri, change.originalUri, change.modifiedUri]);
return commandService.executeCommand('_workbench.changes', title, args);
}
}
registerAction2(HistoryItemViewChangesAction);
const enum SCMInputWidgetCommandId { const enum SCMInputWidgetCommandId {
CancelAction = 'scm.input.cancelAction' CancelAction = 'scm.input.cancelAction'
} }
@ -2742,9 +2720,13 @@ export class SCMViewPane extends ViewPane {
this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility });
this.disposables.add(this.listLabels); this.disposables.add(this.listLabels);
const actionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources()); const resourceActionRunner = new RepositoryPaneActionRunner(() => this.getSelectedResources());
actionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables); resourceActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables);
this.disposables.add(actionRunner); this.disposables.add(resourceActionRunner);
const historyItemActionRunner = new HistoryItemActionRunner();
historyItemActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables);
this.disposables.add(historyItemActionRunner);
const treeDataSource = this.instantiationService.createInstance(SCMTreeDataSource, () => this.viewMode, () => this.alwaysShowRepositories, () => this.showActionButton, () => this.showIncomingChanges, () => this.showOutgoingChanges); const treeDataSource = this.instantiationService.createInstance(SCMTreeDataSource, () => this.viewMode, () => this.alwaysShowRepositories, () => this.showActionButton, () => this.showIncomingChanges, () => this.showOutgoingChanges);
this.disposables.add(treeDataSource); this.disposables.add(treeDataSource);
@ -2760,9 +2742,9 @@ export class SCMViewPane extends ViewPane {
this.actionButtonRenderer, this.actionButtonRenderer,
this.instantiationService.createInstance(RepositoryRenderer, MenuId.SCMTitle, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(RepositoryRenderer, MenuId.SCMTitle, getActionViewItemProvider(this.instantiationService)),
this.instantiationService.createInstance(ResourceGroupRenderer, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(ResourceGroupRenderer, getActionViewItemProvider(this.instantiationService)),
this.instantiationService.createInstance(ResourceRenderer, () => this.viewMode, this.listLabels, getActionViewItemProvider(this.instantiationService), actionRunner), this.instantiationService.createInstance(ResourceRenderer, () => this.viewMode, this.listLabels, getActionViewItemProvider(this.instantiationService), resourceActionRunner),
this.instantiationService.createInstance(HistoryItemGroupRenderer), this.instantiationService.createInstance(HistoryItemGroupRenderer),
this.instantiationService.createInstance(HistoryItemRenderer, getActionViewItemProvider(this.instantiationService)), this.instantiationService.createInstance(HistoryItemRenderer, historyItemActionRunner, getActionViewItemProvider(this.instantiationService)),
this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewMode, this.listLabels), this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewMode, this.listLabels),
this.instantiationService.createInstance(SeparatorRenderer) this.instantiationService.createInstance(SeparatorRenderer)
], ],

View file

@ -10,7 +10,7 @@ import { IMenu } from 'vs/platform/actions/common/actions';
import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; import { ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
export interface ISCMHistoryProviderMenus { export interface ISCMHistoryProviderMenus {
getHistoryItemMenu(historyItem: ISCMHistoryItem): IMenu; getHistoryItemMenu(historyItemGroup: ISCMHistoryItemGroupEntry, historyItem: ISCMHistoryItem): IMenu;
} }
export interface ISCMHistoryProvider { export interface ISCMHistoryProvider {
@ -57,6 +57,7 @@ export interface ISCMHistoryItemGroupDetails {
export interface ISCMHistoryItemGroupEntry { export interface ISCMHistoryItemGroupEntry {
readonly id: string; readonly id: string;
readonly label: string; readonly label: string;
readonly direction: 'incoming' | 'outgoing';
readonly icon?: URI | { light: URI; dark: URI } | ThemeIcon; readonly icon?: URI | { light: URI; dark: URI } | ThemeIcon;
readonly description?: string; readonly description?: string;
readonly ancestor?: string; readonly ancestor?: string;

View file

@ -150,6 +150,18 @@ const apiMenus: IAPIMenu[] = [
description: localize('menus.input', "The Source Control input box menu"), description: localize('menus.input', "The Source Control input box menu"),
proposed: 'contribSourceControlInputBoxMenu' proposed: 'contribSourceControlInputBoxMenu'
}, },
{
key: 'scm/incoming/historyItem/context',
id: MenuId.SCMIncomingHistoryItemContext,
description: localize('menus.incomingHistoryItemContext', "The Source Control incoming history item context menu"),
proposed: 'contribSourceControlHistoryItemMenu'
},
{
key: 'scm/outgoing/historyItem/context',
id: MenuId.SCMOutgoingHistoryItemContext,
description: localize('menus.outgoingHistoryItemContext', "The Source Control outgoing history item context menu"),
proposed: 'contribSourceControlHistoryItemMenu'
},
{ {
key: 'statusBar/remoteIndicator', key: 'statusBar/remoteIndicator',
id: MenuId.StatusBarRemoteIndicatorMenu, id: MenuId.StatusBarRemoteIndicatorMenu,

View file

@ -32,6 +32,7 @@ export const allApiProposals = Object.freeze({
contribNotebookStaticPreloads: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts', contribNotebookStaticPreloads: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribNotebookStaticPreloads.d.ts',
contribRemoteHelp: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribRemoteHelp.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', contribShareMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts',
contribSourceControlHistoryItemMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemMenu.d.ts',
contribSourceControlInputBoxMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlInputBoxMenu.d.ts', contribSourceControlInputBoxMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlInputBoxMenu.d.ts',
contribStatusBarItems: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribStatusBarItems.d.ts', contribStatusBarItems: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribStatusBarItems.d.ts',
contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts',

View file

@ -0,0 +1,7 @@
/*---------------------------------------------------------------------------------------------
* 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 the `scm/historyItem/context`-menu contribution point
// https://github.com/microsoft/vscode/issues/201997