@@ -592,9 +592,75 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
592
592
} ) ;
593
593
} ) ) ;
594
594
595
+ // Revert change when an arrow is clicked.
596
+ this . _register ( editor . onMouseDown ( event => {
597
+ if ( ! event . event . rightButton && event . target . position && event . target . element ?. className . includes ( 'arrow-revert-change' ) ) {
598
+ const lineNumber = event . target . position . lineNumber ;
599
+ const change = this . _diffComputationResult ?. changes . find ( c => c . modifiedStartLineNumber === lineNumber - 1 || c . modifiedStartLineNumber === lineNumber ) ;
600
+ if ( change ) {
601
+ this . revertChange ( change ) ;
602
+ }
603
+ event . event . stopPropagation ( ) ;
604
+ this . _updateDecorations ( ) ;
605
+ return ;
606
+ }
607
+ } ) ) ;
608
+
595
609
return editor ;
596
610
}
597
611
612
+ /**
613
+ * Reverts a change in the modified editor.
614
+ */
615
+ revertChange ( change : IChange ) {
616
+ const editor = this . _modifiedEditor ;
617
+ const original = this . _originalEditor . getModel ( ) ;
618
+ const modified = this . _modifiedEditor . getModel ( ) ;
619
+ if ( ! original || ! modified || ! editor ) {
620
+ return ;
621
+ }
622
+
623
+ const originalRange = change . originalEndLineNumber > 0 ? new Range ( change . originalStartLineNumber , 1 , change . originalEndLineNumber , original . getLineMaxColumn ( change . originalEndLineNumber ) ) : null ;
624
+ const originalContent = originalRange ? original . getValueInRange ( originalRange ) : null ;
625
+
626
+ const newRange = change . modifiedEndLineNumber > 0 ? new Range ( change . modifiedStartLineNumber , 1 , change . modifiedEndLineNumber , modified . getLineMaxColumn ( change . modifiedEndLineNumber ) ) : null ;
627
+
628
+ const eol = modified . getEOL ( ) ;
629
+
630
+ if ( change . originalEndLineNumber === 0 && newRange ) {
631
+ // Insert change.
632
+ // To revert: delete the new content and a linebreak (if possible)
633
+
634
+ let range = newRange ;
635
+ if ( change . modifiedStartLineNumber > 1 ) {
636
+ // Try to include a linebreak from before.
637
+ range = newRange . setStartPosition ( change . modifiedStartLineNumber - 1 , modified . getLineMaxColumn ( change . modifiedStartLineNumber - 1 ) ) ;
638
+ } else if ( change . modifiedEndLineNumber < modified . getLineCount ( ) ) {
639
+ // Try to include the linebreak from after.
640
+ range = newRange . setEndPosition ( change . modifiedEndLineNumber + 1 , 1 ) ;
641
+ }
642
+ editor . executeEdits ( 'diffEditor' , [ {
643
+ range,
644
+ text : '' ,
645
+ } ] ) ;
646
+ } else if ( change . modifiedEndLineNumber === 0 && originalContent ) {
647
+ // Delete change.
648
+ // To revert: insert the old content and a linebreak.
649
+
650
+ const insertAt = change . modifiedStartLineNumber < modified . getLineCount ( ) ? new Position ( change . modifiedStartLineNumber + 1 , 1 ) : new Position ( change . modifiedStartLineNumber , modified . getLineMaxColumn ( change . modifiedStartLineNumber ) ) ;
651
+ editor . executeEdits ( 'diffEditor' , [ {
652
+ range : Range . fromPositions ( insertAt , insertAt ) ,
653
+ text : change . modifiedStartLineNumber < modified . getLineCount ( ) ? originalContent + eol : eol + originalContent ,
654
+ } ] ) ;
655
+ } else if ( newRange && originalContent !== null ) {
656
+ // Modified change.
657
+ editor . executeEdits ( 'diffEditor' , [ {
658
+ range : newRange ,
659
+ text : originalContent ,
660
+ } ] ) ;
661
+ }
662
+ }
663
+
598
664
protected _createInnerEditor ( instantiationService : IInstantiationService , container : HTMLElement , options : Readonly < IEditorConstructionOptions > , editorWidgetOptions : ICodeEditorWidgetOptions ) : CodeEditorWidget {
599
665
return instantiationService . createInstance ( CodeEditorWidget , container , { enableDropIntoEditor : true , ...options } , editorWidgetOptions ) ;
600
666
}
@@ -1733,6 +1799,11 @@ function createDecoration(startLineNumber: number, startColumn: number, endLineN
1733
1799
1734
1800
const DECORATIONS = {
1735
1801
1802
+ arrowRevertChange : ModelDecorationOptions . register ( {
1803
+ description : 'diff-editor-arrow-revert-change' ,
1804
+ glyphMarginClassName : 'arrow-revert-change ' + ThemeIcon . asClassName ( Codicon . arrowRight ) ,
1805
+ } ) ,
1806
+
1736
1807
charDelete : ModelDecorationOptions . register ( {
1737
1808
description : 'diff-editor-char-delete' ,
1738
1809
className : 'char-delete'
@@ -1968,6 +2039,19 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti
1968
2039
1969
2040
for ( const lineChange of lineChanges ) {
1970
2041
2042
+ // Arrows for reverting changes.
2043
+ if ( lineChange . modifiedEndLineNumber > 0 ) {
2044
+ result . decorations . push ( {
2045
+ range : new Range ( lineChange . modifiedStartLineNumber , 1 , lineChange . modifiedStartLineNumber , 1 ) ,
2046
+ options : DECORATIONS . arrowRevertChange
2047
+ } ) ;
2048
+ } else {
2049
+ const viewZone = zones . modified . find ( z => z . afterLineNumber === lineChange . modifiedStartLineNumber ) ;
2050
+ if ( viewZone ) {
2051
+ viewZone . marginDomNode = createViewZoneMarginArrow ( ) ;
2052
+ }
2053
+ }
2054
+
1971
2055
if ( isChangeOrInsert ( lineChange ) ) {
1972
2056
1973
2057
result . decorations . push ( {
@@ -2517,6 +2601,12 @@ function createFakeLinesDiv(): HTMLElement {
2517
2601
return r ;
2518
2602
}
2519
2603
2604
+ function createViewZoneMarginArrow ( ) : HTMLElement {
2605
+ const arrow = document . createElement ( 'div' ) ;
2606
+ arrow . className = 'arrow-revert-change ' + ThemeIcon . asClassName ( Codicon . arrowRight ) ;
2607
+ return dom . $ ( 'div' , { } , arrow ) ;
2608
+ }
2609
+
2520
2610
function getViewRange ( model : ITextModel , viewModel : IViewModel , startLineNumber : number , endLineNumber : number ) : Range {
2521
2611
const lineCount = model . getLineCount ( ) ;
2522
2612
startLineNumber = Math . min ( lineCount , Math . max ( 1 , startLineNumber ) ) ;
0 commit comments