Skip to content

Commit

Permalink
Implements microsoft#206808
Browse files Browse the repository at this point in the history
  • Loading branch information
hediet authored and chen-ky committed Mar 18, 2024
1 parent c881848 commit 96d7c32
Show file tree
Hide file tree
Showing 26 changed files with 800 additions and 52 deletions.
27 changes: 27 additions & 0 deletions extensions/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"contribEditorContentMenu",
"contribMergeEditorMenus",
"contribMultiDiffEditorMenus",
"contribDiffEditorGutterToolBarMenus",
"contribSourceControlHistoryItemGroupMenu",
"contribSourceControlHistoryItemMenu",
"contribSourceControlInputBoxMenu",
Expand Down Expand Up @@ -188,6 +189,18 @@
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.diff.stageHunk",
"title": "%command.stageBlock%",
"category": "Git",
"icon": "$(plus)"
},
{
"command": "git.diff.stageSelection",
"title": "%command.stageSelection%",
"category": "Git",
"icon": "$(plus)"
},
{
"command": "git.revertSelectedRanges",
"title": "%command.revertSelectedRanges%",
Expand Down Expand Up @@ -2047,6 +2060,20 @@
"when": "scmProvider == git && scmResourceGroup == index"
}
],
"diffEditor/gutter/hunk": [
{
"command": "git.diff.stageHunk",
"group": "primary@10",
"when": "diffEditorOriginalUri =~ /^git\\:.*%22ref%22%3A%22~%22%7D$/"
}
],
"diffEditor/gutter/selection": [
{
"command": "git.diff.stageSelection",
"group": "primary@10",
"when": "diffEditorOriginalUri =~ /^git\\:.*%22ref%22%3A%22~%22%7D$/"
}
],
"scm/change/title": [
{
"command": "git.stageChange",
Expand Down
2 changes: 2 additions & 0 deletions extensions/git/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change",
"command.stageSelection": "Stage Selection",
"command.stageBlock": "Stage Block",
"command.revertChange": "Revert Change",
"command.unstage": "Unstage Changes",
"command.unstageAll": "Unstage All Changes",
Expand Down
26 changes: 25 additions & 1 deletion extensions/git/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, Remo
import { Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri';
import { dispose, grep, isDefined, isDescendant, pathEquals, relativePath } from './util';
import { GitTimelineItem } from './timelineProvider';
Expand Down Expand Up @@ -1511,6 +1511,30 @@ export class CommandCenter {
textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)];
}

@command('git.diff.stageHunk')
async diffStageHunk(changes: DiffEditorSelectionHunkToolbarContext): Promise<void> {
this.diffStageHunkOrSelection(changes);
}

@command('git.diff.stageSelection')
async diffStageSelection(changes: DiffEditorSelectionHunkToolbarContext): Promise<void> {
this.diffStageHunkOrSelection(changes);
}

async diffStageHunkOrSelection(changes: DiffEditorSelectionHunkToolbarContext): Promise<void> {
const textEditor = window.activeTextEditor;
if (!textEditor) {
return;
}
const modifiedDocument = textEditor.document;
const modifiedUri = modifiedDocument.uri;
if (modifiedUri.scheme !== 'file') {
return;
}
const result = changes.originalWithModifiedChanges;
await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result));
}

@command('git.stageSelectedRanges', { diff: true })
async stageSelectedChanges(changes: LineChange[]): Promise<void> {
const textEditor = window.activeTextEditor;
Expand Down
8 changes: 8 additions & 0 deletions extensions/git/src/staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,11 @@ export function invertLineChange(diff: LineChange): LineChange {
originalEndLineNumber: diff.modifiedEndLineNumber
};
}

export interface DiffEditorSelectionHunkToolbarContext {
mapping: unknown;
/**
* The original text with the selected modified changes applied.
*/
originalWithModifiedChanges: string;
}
21 changes: 21 additions & 0 deletions src/vs/editor/browser/widget/diffEditor/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import './registrations.contribution';
import { DiffEditorSelectionHunkToolbarContext } from 'vs/editor/browser/widget/diffEditor/features/gutterFeature';

export class ToggleCollapseUnchangedRegions extends Action2 {
constructor() {
Expand Down Expand Up @@ -165,6 +166,26 @@ export class ShowAllUnchangedRegions extends EditorAction2 {
}
}

export class RevertHunkOrSelection extends EditorAction2 {
constructor() {
super({
id: 'diffEditor.revert',
title: localize2('revert', 'Revert'),
precondition: ContextKeyExpr.has('isInDiffEditor'),
f1: false,
category: diffEditorCategory,
});
}

runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg: DiffEditorSelectionHunkToolbarContext): unknown {
const diffEditor = findFocusedDiffEditor(accessor);
if (diffEditor instanceof DiffEditorWidget) {
diffEditor.revertRangeMappings(arg.mapping.innerChanges ?? []);
}
return undefined;
}
}

const accessibleDiffViewerCategory: ILocalizedString = localize2('accessibleDiffViewer', "Accessible Diff Viewer");

export class AccessibleDiffViewerNext extends Action2 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,56 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IObservable, IReader, autorunHandleChanges, derivedOpts, observableFromEvent } from 'vs/base/common/observable';
import { IReader, autorunHandleChanges, derived, derivedOpts, observableFromEvent } from 'vs/base/common/observable';
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
import { OverviewRulerFeature } from 'vs/editor/browser/widget/diffEditor/features/overviewRulerFeature';
import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { Position } from 'vs/editor/common/core/position';
import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DiffEditorOptions } from '../diffEditorOptions';
import { ITextModel } from 'vs/editor/common/model';
import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';

export class DiffEditorEditors extends Disposable {
public readonly modified: CodeEditorWidget;
public readonly original: CodeEditorWidget;
public readonly original = this._register(this._createLeftHandSideEditor(this._options.editorOptions.get(), this._argCodeEditorWidgetOptions.originalEditor || {}));
public readonly modified = this._register(this._createRightHandSideEditor(this._options.editorOptions.get(), this._argCodeEditorWidgetOptions.modifiedEditor || {}));

private readonly _onDidContentSizeChange = this._register(new Emitter<IContentSizeChangedEvent>());
public get onDidContentSizeChange() { return this._onDidContentSizeChange.event; }

public readonly modifiedScrollTop: IObservable<number>;
public readonly modifiedScrollHeight: IObservable<number>;
public readonly modifiedScrollTop = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollTop */ this.modified.getScrollTop());
public readonly modifiedScrollHeight = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollHeight */ this.modified.getScrollHeight());

public readonly modifiedModel = observableFromEvent(this.modified.onDidChangeModel, () => /** @description modified.model */ this.modified.getModel());

public readonly modifiedModel: IObservable<ITextModel | null>;
public readonly modifiedSelections = observableFromEvent(this.modified.onDidChangeCursorSelection, () => this.modified.getSelections() ?? []);
public readonly modifiedCursor = derivedOpts({ owner: this, equalityComparer: Position.equals }, reader => this.modifiedSelections.read(reader)[0]?.getPosition() ?? new Position(1, 1));

public readonly modifiedSelections: IObservable<Selection[]>;
public readonly modifiedCursor: IObservable<Position>;
public readonly originalCursor = observableFromEvent(this.original.onDidChangeCursorPosition, () => this.original.getPosition() ?? new Position(1, 1));

public readonly originalCursor: IObservable<Position>;
public readonly isOriginalFocused = observableFromEvent(Event.any(this.original.onDidFocusEditorWidget, this.original.onDidBlurEditorWidget), () => this.original.hasWidgetFocus());
public readonly isModifiedFocused = observableFromEvent(Event.any(this.modified.onDidFocusEditorWidget, this.modified.onDidBlurEditorWidget), () => this.modified.hasWidgetFocus());

public readonly isFocused = derived(this, reader => this.isOriginalFocused.read(reader) || this.isModifiedFocused.read(reader));

constructor(
private readonly originalEditorElement: HTMLElement,
private readonly modifiedEditorElement: HTMLElement,
private readonly _options: DiffEditorOptions,
codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions,
private _argCodeEditorWidgetOptions: IDiffCodeEditorWidgetOptions,
private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly<IEditorOptions>, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IKeybindingService private readonly _keybindingService: IKeybindingService
) {
super();

this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}));
this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}));

this.modifiedModel = observableFromEvent(this.modified.onDidChangeModel, () => /** @description modified.model */ this.modified.getModel());

this.modifiedScrollTop = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollTop */ this.modified.getScrollTop());
this.modifiedScrollHeight = observableFromEvent(this.modified.onDidScrollChange, () => /** @description modified.getScrollHeight */ this.modified.getScrollHeight());

this.modifiedSelections = observableFromEvent(this.modified.onDidChangeCursorSelection, () => this.modified.getSelections() ?? []);
this.modifiedCursor = derivedOpts({ owner: this, equalityComparer: Position.equals }, reader => this.modifiedSelections.read(reader)[0]?.getPosition() ?? new Position(1, 1));

this.originalCursor = observableFromEvent(this.original.onDidChangeCursorPosition, () => this.original.getPosition() ?? new Position(1, 1));
this._argCodeEditorWidgetOptions = null as any;

this._register(autorunHandleChanges({
createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ export class DiffEditorViewZones extends Disposable {
}

let marginDomNode: HTMLElement | undefined = undefined;
if (a.diff && a.diff.modified.isEmpty && this._options.shouldRenderRevertArrows.read(reader)) {
if (a.diff && a.diff.modified.isEmpty && this._options.shouldRenderOldRevertArrows.read(reader)) {
marginDomNode = createViewZoneMarginArrow();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { Codicon } from 'vs/base/common/codicons';
import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev, CollapseAllUnchangedRegions, ExitCompareMove, ShowAllUnchangedRegions, SwitchSide, ToggleCollapseUnchangedRegions, ToggleShowMovedCodeBlocks, ToggleUseInlineViewWhenSpaceIsLimited } from 'vs/editor/browser/widget/diffEditor/commands';
import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev, CollapseAllUnchangedRegions, ExitCompareMove, RevertHunkOrSelection, ShowAllUnchangedRegions, SwitchSide, ToggleCollapseUnchangedRegions, ToggleShowMovedCodeBlocks, ToggleUseInlineViewWhenSpaceIsLimited } from 'vs/editor/browser/widget/diffEditor/commands';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { localize } from 'vs/nls';
import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
Expand Down Expand Up @@ -44,6 +44,35 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
when: ContextKeyExpr.has('isInDiffEditor'),
});

registerAction2(RevertHunkOrSelection);

for (const ctx of [
{ icon: Codicon.arrowRight, key: EditorContextKeys.diffEditorInlineMode.toNegated() },
{ icon: Codicon.discard, key: EditorContextKeys.diffEditorInlineMode }
]) {
MenuRegistry.appendMenuItem(MenuId.DiffEditorHunkToolbar, {
command: {
id: new RevertHunkOrSelection().desc.id,
title: localize('revertHunk', "Revert Block"),
icon: ctx.icon,
},
when: ContextKeyExpr.and(EditorContextKeys.diffEditorModifiedWritable, ctx.key),
order: 5,
group: 'primary',
});

MenuRegistry.appendMenuItem(MenuId.DiffEditorSelectionToolbar, {
command: {
id: new RevertHunkOrSelection().desc.id,
title: localize('revertSelection', "Revert Selection"),
icon: ctx.icon,
},
when: ContextKeyExpr.and(EditorContextKeys.diffEditorModifiedWritable, ctx.key),
order: 5,
group: 'primary',
});

}

registerAction2(SwitchSide);
registerAction2(ExitCompareMove);
Expand Down
6 changes: 5 additions & 1 deletion src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ export class DiffEditorOptions {
);
public readonly readOnly = derived(this, reader => this._options.read(reader).readOnly);

public readonly shouldRenderRevertArrows = derived(this, reader => {
public readonly shouldRenderOldRevertArrows = derived(this, reader => {
if (!this._options.read(reader).renderMarginRevertIcon) { return false; }
if (!this.renderSideBySide.read(reader)) { return false; }
if (this.readOnly.read(reader)) { return false; }
if (this.shouldRenderGutterMenu.read(reader)) { return false; }
return true;
});

public readonly shouldRenderGutterMenu = derived(this, reader => this._options.read(reader).renderGutterMenu);
public readonly renderIndicators = derived(this, reader => this._options.read(reader).renderIndicators);
public readonly enableSplitViewResizing = derived(this, reader => this._options.read(reader).enableSplitViewResizing);
public readonly splitViewDefaultRatio = derived(this, reader => this._options.read(reader).splitViewDefaultRatio);
Expand Down Expand Up @@ -99,5 +102,6 @@ function validateDiffEditorOptions(options: Readonly<IDiffEditorOptions>, defaul
onlyShowAccessibleDiffViewer: validateBooleanOption(options.onlyShowAccessibleDiffViewer, defaults.onlyShowAccessibleDiffViewer),
renderSideBySideInlineBreakpoint: clampedInt(options.renderSideBySideInlineBreakpoint, defaults.renderSideBySideInlineBreakpoint, 0, Constants.MAX_SAFE_SMALL_INTEGER),
useInlineViewWhenSpaceIsLimited: validateBooleanOption(options.useInlineViewWhenSpaceIsLimited, defaults.useInlineViewWhenSpaceIsLimited),
renderGutterMenu: validateBooleanOption(options.renderGutterMenu, defaults.renderGutterMenu),
};
}
Loading

0 comments on commit 96d7c32

Please sign in to comment.