@@ -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 , options , editorWidgetOptions ) ;
600
666
}
@@ -1734,6 +1800,11 @@ function createDecoration(startLineNumber: number, startColumn: number, endLineN
1734
1800
1735
1801
const DECORATIONS = {
1736
1802
1803
+ arrowRevertChange : ModelDecorationOptions . register ( {
1804
+ description : 'diff-editor-arrow-revert-change' ,
1805
+ glyphMarginClassName : 'arrow-revert-change ' + ThemeIcon . asClassName ( Codicon . arrowRight ) ,
1806
+ } ) ,
1807
+
1737
1808
charDelete : ModelDecorationOptions . register ( {
1738
1809
description : 'diff-editor-char-delete' ,
1739
1810
className : 'char-delete'
@@ -1969,6 +2040,19 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerti
1969
2040
1970
2041
for ( const lineChange of lineChanges ) {
1971
2042
2043
+ // Arrows for reverting changes.
2044
+ if ( lineChange . modifiedEndLineNumber > 0 ) {
2045
+ result . decorations . push ( {
2046
+ range : new Range ( lineChange . modifiedStartLineNumber , 1 , lineChange . modifiedStartLineNumber , 1 ) ,
2047
+ options : DECORATIONS . arrowRevertChange
2048
+ } ) ;
2049
+ } else {
2050
+ const viewZone = zones . modified . find ( z => z . afterLineNumber === lineChange . modifiedStartLineNumber ) ;
2051
+ if ( viewZone ) {
2052
+ viewZone . marginDomNode = createViewZoneMarginArrow ( ) ;
2053
+ }
2054
+ }
2055
+
1972
2056
if ( isChangeOrInsert ( lineChange ) ) {
1973
2057
1974
2058
result . decorations . push ( {
@@ -2532,6 +2616,12 @@ function createFakeLinesDiv(): HTMLElement {
2532
2616
return r ;
2533
2617
}
2534
2618
2619
+ function createViewZoneMarginArrow ( ) : HTMLElement {
2620
+ const arrow = document . createElement ( 'div' ) ;
2621
+ arrow . className = 'arrow-revert-change ' + ThemeIcon . asClassName ( Codicon . arrowRight ) ;
2622
+ return dom . $ ( 'div' , { } , arrow ) ;
2623
+ }
2624
+
2535
2625
function getViewRange ( model : ITextModel , viewModel : IViewModel , startLineNumber : number , endLineNumber : number ) : Range {
2536
2626
const lineCount = model . getLineCount ( ) ;
2537
2627
startLineNumber = Math . min ( lineCount , Math . max ( 1 , startLineNumber ) ) ;
0 commit comments