Path: blob/main/src/vs/editor/browser/widget/diffEditor/commands.ts
5236 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { getActiveElement } from '../../../../base/browser/dom.js';6import { Codicon } from '../../../../base/common/codicons.js';7import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';8import { ICodeEditor, IDiffEditor } from '../../editorBrowser.js';9import { EditorAction2, ServicesAccessor } from '../../editorExtensions.js';10import { ICodeEditorService } from '../../services/codeEditorService.js';11import { DiffEditorWidget } from './diffEditorWidget.js';12import { EditorContextKeys } from '../../../common/editorContextKeys.js';13import { localize2 } from '../../../../nls.js';14import { ILocalizedString } from '../../../../platform/action/common/action.js';15import { Action2, MenuId } from '../../../../platform/actions/common/actions.js';16import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';17import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';18import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';19import './registrations.contribution.js';20import { DiffEditorSelectionHunkToolbarContext } from './features/gutterFeature.js';21import { URI } from '../../../../base/common/uri.js';22import { EditorOption } from '../../../common/config/editorOptions.js';2324export class ToggleCollapseUnchangedRegions extends Action2 {25constructor() {26super({27id: 'diffEditor.toggleCollapseUnchangedRegions',28title: localize2('toggleCollapseUnchangedRegions', 'Toggle Collapse Unchanged Regions'),29icon: Codicon.map,30toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'),31precondition: ContextKeyExpr.has('isInDiffEditor'),32menu: {33when: ContextKeyExpr.has('isInDiffEditor'),34id: MenuId.EditorTitle,35order: 22,36group: 'navigation',37},38});39}4041run(accessor: ServicesAccessor, ...args: unknown[]): void {42const configurationService = accessor.get(IConfigurationService);43const newValue = !configurationService.getValue<boolean>('diffEditor.hideUnchangedRegions.enabled');44configurationService.updateValue('diffEditor.hideUnchangedRegions.enabled', newValue);45}46}4748export class ToggleShowMovedCodeBlocks extends Action2 {49constructor() {50super({51id: 'diffEditor.toggleShowMovedCodeBlocks',52title: localize2('toggleShowMovedCodeBlocks', 'Toggle Show Moved Code Blocks'),53precondition: ContextKeyExpr.has('isInDiffEditor'),54});55}5657run(accessor: ServicesAccessor, ...args: unknown[]): void {58const configurationService = accessor.get(IConfigurationService);59const newValue = !configurationService.getValue<boolean>('diffEditor.experimental.showMoves');60configurationService.updateValue('diffEditor.experimental.showMoves', newValue);61}62}6364export class ToggleUseInlineViewWhenSpaceIsLimited extends Action2 {65constructor() {66super({67id: 'diffEditor.toggleUseInlineViewWhenSpaceIsLimited',68title: localize2('toggleUseInlineViewWhenSpaceIsLimited', 'Toggle Use Inline View When Space Is Limited'),69precondition: ContextKeyExpr.has('isInDiffEditor'),70});71}7273run(accessor: ServicesAccessor, ...args: unknown[]): void {74const configurationService = accessor.get(IConfigurationService);75const newValue = !configurationService.getValue<boolean>('diffEditor.useInlineViewWhenSpaceIsLimited');76configurationService.updateValue('diffEditor.useInlineViewWhenSpaceIsLimited', newValue);77}78}7980const diffEditorCategory: ILocalizedString = localize2('diffEditor', "Diff Editor");8182export class SwitchSide extends EditorAction2 {83constructor() {84super({85id: 'diffEditor.switchSide',86title: localize2('switchSide', 'Switch Side'),87icon: Codicon.arrowSwap,88precondition: ContextKeyExpr.has('isInDiffEditor'),89f1: true,90category: diffEditorCategory,91});92}9394runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: { dryRun: boolean }): unknown {95const diffEditor = findFocusedDiffEditor(accessor);96if (diffEditor instanceof DiffEditorWidget) {97if (arg && arg.dryRun) {98return { destinationSelection: diffEditor.mapToOtherSide().destinationSelection };99} else {100diffEditor.switchSide();101}102}103return undefined;104}105}106export class ExitCompareMove extends EditorAction2 {107constructor() {108super({109id: 'diffEditor.exitCompareMove',110title: localize2('exitCompareMove', 'Exit Compare Move'),111icon: Codicon.close,112precondition: EditorContextKeys.comparingMovedCode,113f1: false,114category: diffEditorCategory,115keybinding: {116weight: 10000,117primary: KeyCode.Escape,118}119});120}121122runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void {123const diffEditor = findFocusedDiffEditor(accessor);124if (diffEditor instanceof DiffEditorWidget) {125diffEditor.exitCompareMove();126}127}128}129130export class CollapseAllUnchangedRegions extends EditorAction2 {131constructor() {132super({133id: 'diffEditor.collapseAllUnchangedRegions',134title: localize2('collapseAllUnchangedRegions', 'Collapse All Unchanged Regions'),135icon: Codicon.fold,136precondition: ContextKeyExpr.has('isInDiffEditor'),137f1: true,138category: diffEditorCategory,139});140}141142runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void {143const diffEditor = findFocusedDiffEditor(accessor);144if (diffEditor instanceof DiffEditorWidget) {145diffEditor.collapseAllUnchangedRegions();146}147}148}149150export class ShowAllUnchangedRegions extends EditorAction2 {151constructor() {152super({153id: 'diffEditor.showAllUnchangedRegions',154title: localize2('showAllUnchangedRegions', 'Show All Unchanged Regions'),155icon: Codicon.unfold,156precondition: ContextKeyExpr.has('isInDiffEditor'),157f1: true,158category: diffEditorCategory,159});160}161162runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void {163const diffEditor = findFocusedDiffEditor(accessor);164if (diffEditor instanceof DiffEditorWidget) {165diffEditor.showAllUnchangedRegions();166}167}168}169170export class RevertHunkOrSelection extends Action2 {171constructor() {172super({173id: 'diffEditor.revert',174title: localize2('revert', 'Revert'),175f1: true,176category: diffEditorCategory,177precondition: ContextKeyExpr.has('isInDiffEditor'),178});179}180181run(accessor: ServicesAccessor, arg?: DiffEditorSelectionHunkToolbarContext): unknown {182return arg ? this.runViaToolbarContext(accessor, arg) : this.runViaCursorOrSelection(accessor);183}184185runViaCursorOrSelection(accessor: ServicesAccessor): unknown {186const diffEditor = findFocusedDiffEditor(accessor);187if (diffEditor instanceof DiffEditorWidget) {188diffEditor.revertFocusedRangeMappings();189}190return undefined;191}192193runViaToolbarContext(accessor: ServicesAccessor, arg: DiffEditorSelectionHunkToolbarContext): unknown {194const diffEditor = findDiffEditor(accessor, arg.originalUri, arg.modifiedUri);195if (diffEditor instanceof DiffEditorWidget) {196diffEditor.revertRangeMappings(arg.mapping.innerChanges ?? []);197}198return undefined;199}200}201202const accessibleDiffViewerCategory: ILocalizedString = localize2('accessibleDiffViewer', "Accessible Diff Viewer");203204export class AccessibleDiffViewerNext extends Action2 {205public static id = 'editor.action.accessibleDiffViewer.next';206207constructor() {208super({209id: AccessibleDiffViewerNext.id,210title: localize2('editor.action.accessibleDiffViewer.next', 'Go to Next Difference'),211category: accessibleDiffViewerCategory,212precondition: ContextKeyExpr.has('isInDiffEditor'),213keybinding: {214primary: KeyCode.F7,215weight: KeybindingWeight.EditorContrib216},217f1: true,218});219}220221public override run(accessor: ServicesAccessor): void {222const diffEditor = findFocusedDiffEditor(accessor);223diffEditor?.accessibleDiffViewerNext();224}225}226227export class AccessibleDiffViewerPrev extends Action2 {228public static id = 'editor.action.accessibleDiffViewer.prev';229230constructor() {231super({232id: AccessibleDiffViewerPrev.id,233title: localize2('editor.action.accessibleDiffViewer.prev', 'Go to Previous Difference'),234category: accessibleDiffViewerCategory,235precondition: ContextKeyExpr.has('isInDiffEditor'),236keybinding: {237primary: KeyMod.Shift | KeyCode.F7,238weight: KeybindingWeight.EditorContrib239},240f1: true,241});242}243244public override run(accessor: ServicesAccessor): void {245const diffEditor = findFocusedDiffEditor(accessor);246diffEditor?.accessibleDiffViewerPrev();247}248}249250export function findDiffEditor(accessor: ServicesAccessor, originalUri: URI, modifiedUri: URI): IDiffEditor | null {251const codeEditorService = accessor.get(ICodeEditorService);252const diffEditors = codeEditorService.listDiffEditors();253254return diffEditors.find(diffEditor => {255const modified = diffEditor.getModifiedEditor();256const original = diffEditor.getOriginalEditor();257258return modified && modified.getModel()?.uri.toString() === modifiedUri.toString()259&& original && original.getModel()?.uri.toString() === originalUri.toString();260}) || null;261}262263export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null {264const codeEditorService = accessor.get(ICodeEditorService);265const diffEditors = codeEditorService.listDiffEditors();266267const activeElement = getActiveElement();268if (activeElement) {269for (const d of diffEditors) {270const container = d.getContainerDomNode();271if (container.contains(activeElement)) {272return d;273}274}275}276277return null;278}279280281/**282* If `editor` is the original or modified editor of a diff editor, it returns it.283* It returns null otherwise.284*/285export function findDiffEditorContainingCodeEditor(accessor: ServicesAccessor, editor: ICodeEditor): IDiffEditor | null {286if (!editor.getOption(EditorOption.inDiffEditor)) {287return null;288}289290const codeEditorService = accessor.get(ICodeEditorService);291292for (const diffEditor of codeEditorService.listDiffEditors()) {293const originalEditor = diffEditor.getOriginalEditor();294const modifiedEditor = diffEditor.getModifiedEditor();295if (originalEditor === editor || modifiedEditor === editor) {296return diffEditor;297}298}299return null;300}301302303