Path: blob/main/src/vs/editor/browser/widget/diffEditor/commands.ts
3296 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: false,176category: diffEditorCategory,177});178}179180run(accessor: ServicesAccessor, arg: DiffEditorSelectionHunkToolbarContext): unknown {181const diffEditor = findDiffEditor(accessor, arg.originalUri, arg.modifiedUri);182if (diffEditor instanceof DiffEditorWidget) {183diffEditor.revertRangeMappings(arg.mapping.innerChanges ?? []);184}185return undefined;186}187}188189const accessibleDiffViewerCategory: ILocalizedString = localize2('accessibleDiffViewer', "Accessible Diff Viewer");190191export class AccessibleDiffViewerNext extends Action2 {192public static id = 'editor.action.accessibleDiffViewer.next';193194constructor() {195super({196id: AccessibleDiffViewerNext.id,197title: localize2('editor.action.accessibleDiffViewer.next', 'Go to Next Difference'),198category: accessibleDiffViewerCategory,199precondition: ContextKeyExpr.has('isInDiffEditor'),200keybinding: {201primary: KeyCode.F7,202weight: KeybindingWeight.EditorContrib203},204f1: true,205});206}207208public override run(accessor: ServicesAccessor): void {209const diffEditor = findFocusedDiffEditor(accessor);210diffEditor?.accessibleDiffViewerNext();211}212}213214export class AccessibleDiffViewerPrev extends Action2 {215public static id = 'editor.action.accessibleDiffViewer.prev';216217constructor() {218super({219id: AccessibleDiffViewerPrev.id,220title: localize2('editor.action.accessibleDiffViewer.prev', 'Go to Previous Difference'),221category: accessibleDiffViewerCategory,222precondition: ContextKeyExpr.has('isInDiffEditor'),223keybinding: {224primary: KeyMod.Shift | KeyCode.F7,225weight: KeybindingWeight.EditorContrib226},227f1: true,228});229}230231public override run(accessor: ServicesAccessor): void {232const diffEditor = findFocusedDiffEditor(accessor);233diffEditor?.accessibleDiffViewerPrev();234}235}236237export function findDiffEditor(accessor: ServicesAccessor, originalUri: URI, modifiedUri: URI): IDiffEditor | null {238const codeEditorService = accessor.get(ICodeEditorService);239const diffEditors = codeEditorService.listDiffEditors();240241return diffEditors.find(diffEditor => {242const modified = diffEditor.getModifiedEditor();243const original = diffEditor.getOriginalEditor();244245return modified && modified.getModel()?.uri.toString() === modifiedUri.toString()246&& original && original.getModel()?.uri.toString() === originalUri.toString();247}) || null;248}249250export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null {251const codeEditorService = accessor.get(ICodeEditorService);252const diffEditors = codeEditorService.listDiffEditors();253254const activeElement = getActiveElement();255if (activeElement) {256for (const d of diffEditors) {257const container = d.getContainerDomNode();258if (container.contains(activeElement)) {259return d;260}261}262}263264return null;265}266267268/**269* If `editor` is the original or modified editor of a diff editor, it returns it.270* It returns null otherwise.271*/272export function findDiffEditorContainingCodeEditor(accessor: ServicesAccessor, editor: ICodeEditor): IDiffEditor | null {273if (!editor.getOption(EditorOption.inDiffEditor)) {274return null;275}276277const codeEditorService = accessor.get(ICodeEditorService);278279for (const diffEditor of codeEditorService.listDiffEditors()) {280const originalEditor = diffEditor.getOriginalEditor();281const modifiedEditor = diffEditor.getModifiedEditor();282if (originalEditor === editor || modifiedEditor === editor) {283return diffEditor;284}285}286return null;287}288289290