55
66import { $ , addDisposableListener , h , reset } from 'vs/base/browser/dom' ;
77import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels' ;
8+ import { Codicon } from 'vs/base/common/codicons' ;
9+ import { MarkdownString } from 'vs/base/common/htmlContent' ;
810import { Disposable } from 'vs/base/common/lifecycle' ;
911import { IObservable , observableFromEvent , transaction } from 'vs/base/common/observable' ;
1012import { autorun , autorunWithStore2 } from 'vs/base/common/observableImpl/autorun' ;
1113import { derived , derivedWithStore } from 'vs/base/common/observableImpl/derived' ;
14+ import { ThemeIcon } from 'vs/base/common/themables' ;
1215import { isDefined } from 'vs/base/common/types' ;
1316import { ICodeEditor , IViewZone } from 'vs/editor/browser/editorBrowser' ;
1417import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors' ;
1518import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions' ;
1619import { DiffEditorViewModel , UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel' ;
1720import { PlaceholderViewZone , ViewZoneOverlayWidget , applyObservableDecorations , applyStyle , applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils' ;
1821import { EditorOption } from 'vs/editor/common/config/editorOptions' ;
22+ import { LineRange } from 'vs/editor/common/core/lineRange' ;
23+ import { Position } from 'vs/editor/common/core/position' ;
24+ import { Range } from 'vs/editor/common/core/range' ;
1925import { CursorChangeReason } from 'vs/editor/common/cursorEvents' ;
2026import { IModelDecorationOptions , IModelDeltaDecoration } from 'vs/editor/common/model' ;
27+ import { localize } from 'vs/nls' ;
2128
2229export class UnchangedRangesFeature extends Disposable {
2330 private _isUpdatingViewZones = false ;
@@ -90,21 +97,45 @@ export class UnchangedRangesFeature extends Disposable {
9097 className : 'diff-unchanged-lines' ,
9198 isWholeLine : true ,
9299 } ;
100+ const unchangedLinesDecorationShow : IModelDecorationOptions = {
101+ description : 'Fold Unchanged' ,
102+ glyphMarginHoverMessage : new MarkdownString ( undefined , { isTrusted : true , supportThemeIcons : true } ) . appendMarkdown ( localize ( 'foldUnchanged' , 'Fold Unchanged Region' ) ) ,
103+ glyphMarginClassName : 'fold-unchanged ' + ThemeIcon . asClassName ( Codicon . fold ) ,
104+ zIndex : 10001 ,
105+ } ;
93106
94107 this . _register ( applyObservableDecorations ( this . _editors . original , derived ( 'decorations' , ( reader ) => {
95108 const curUnchangedRegions = unchangedRegions . read ( reader ) ;
96- return curUnchangedRegions . map < IModelDeltaDecoration > ( r => ( {
109+ const result = curUnchangedRegions . map < IModelDeltaDecoration > ( r => ( {
97110 range : r . originalRange . toInclusiveRange ( ) ! ,
98111 options : unchangedLinesDecoration ,
99112 } ) ) ;
113+ for ( const r of curUnchangedRegions ) {
114+ if ( r . shouldHideControls ( reader ) ) {
115+ result . push ( {
116+ range : Range . fromPositions ( new Position ( r . originalLineNumber , 1 ) ) ,
117+ options : unchangedLinesDecorationShow
118+ } ) ;
119+ }
120+ }
121+ return result ;
100122 } ) ) ) ;
101123
102124 this . _register ( applyObservableDecorations ( this . _editors . modified , derived ( 'decorations' , ( reader ) => {
103125 const curUnchangedRegions = unchangedRegions . read ( reader ) ;
104- return curUnchangedRegions . map < IModelDeltaDecoration > ( r => ( {
126+ const result = curUnchangedRegions . map < IModelDeltaDecoration > ( r => ( {
105127 range : r . modifiedRange . toInclusiveRange ( ) ! ,
106128 options : unchangedLinesDecoration ,
107129 } ) ) ;
130+ for ( const r of curUnchangedRegions ) {
131+ if ( r . shouldHideControls ( reader ) ) {
132+ result . push ( {
133+ range : LineRange . ofLength ( r . modifiedLineNumber , 1 ) . toInclusiveRange ( ) ! ,
134+ options : unchangedLinesDecorationShow
135+ } ) ;
136+ }
137+ }
138+ return result ;
108139 } ) ) ) ;
109140
110141 this . _register ( applyViewZones ( this . _editors . original , viewZones . map ( v => v . origViewZones ) , v => this . _isUpdatingViewZones = v ) ) ;
@@ -115,6 +146,32 @@ export class UnchangedRangesFeature extends Disposable {
115146 this . _editors . original . setHiddenAreas ( curUnchangedRegions . map ( r => r . getHiddenOriginalRange ( reader ) . toInclusiveRange ( ) ) . filter ( isDefined ) ) ;
116147 this . _editors . modified . setHiddenAreas ( curUnchangedRegions . map ( r => r . getHiddenModifiedRange ( reader ) . toInclusiveRange ( ) ) . filter ( isDefined ) ) ;
117148 } ) ) ;
149+
150+ this . _register ( this . _editors . modified . onMouseUp ( event => {
151+ if ( ! event . event . rightButton && event . target . position && event . target . element ?. className . includes ( 'fold-unchanged' ) ) {
152+ const lineNumber = event . target . position . lineNumber ;
153+ const model = this . _diffModel . get ( ) ;
154+ if ( ! model ) { return ; }
155+ const region = model . unchangedRegions . get ( ) . find ( r => r . modifiedRange . includes ( lineNumber ) ) ;
156+ if ( ! region ) { return ; }
157+ region . setState ( 0 , 0 , undefined ) ;
158+ event . event . stopPropagation ( ) ;
159+ event . event . preventDefault ( ) ;
160+ }
161+ } ) ) ;
162+
163+ this . _register ( this . _editors . original . onMouseUp ( event => {
164+ if ( ! event . event . rightButton && event . target . position && event . target . element ?. className . includes ( 'fold-unchanged' ) ) {
165+ const lineNumber = event . target . position . lineNumber ;
166+ const model = this . _diffModel . get ( ) ;
167+ if ( ! model ) { return ; }
168+ const region = model . unchangedRegions . get ( ) . find ( r => r . originalRange . includes ( lineNumber ) ) ;
169+ if ( ! region ) { return ; }
170+ region . setState ( 0 , 0 , undefined ) ;
171+ event . event . stopPropagation ( ) ;
172+ event . event . preventDefault ( ) ;
173+ }
174+ } ) ) ;
118175 }
119176}
120177
@@ -165,7 +222,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
165222 this . _unchangedRegion . isDragged . set ( true , undefined ) ;
166223
167224
168- const mouseMoveListener = addDisposableListener ( document . body , 'mousemove' , e => {
225+ const mouseMoveListener = addDisposableListener ( window , 'mousemove' , e => {
169226 const currentTop = e . clientY ;
170227 const delta = currentTop - startTop ;
171228 didMove = didMove || Math . abs ( delta ) > 2 ;
@@ -174,7 +231,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
174231 this . _unchangedRegion . visibleLineCountTop . set ( newVal , undefined ) ;
175232 } ) ;
176233
177- const mouseUpListener = addDisposableListener ( document . body , 'mouseup' , e => {
234+ const mouseUpListener = addDisposableListener ( window , 'mouseup' , e => {
178235 if ( ! didMove ) {
179236 this . _unchangedRegion . showMoreAbove ( 20 , undefined ) ;
180237 }
@@ -198,26 +255,26 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
198255 const cur = this . _unchangedRegion . visibleLineCountBottom . get ( ) ;
199256 this . _unchangedRegion . isDragged . set ( true , undefined ) ;
200257
201- const mouseMoveListener = addDisposableListener ( document . body , 'mousemove' , e => {
258+ const mouseMoveListener = addDisposableListener ( window , 'mousemove' , e => {
202259 const currentTop = e . clientY ;
203260 const delta = currentTop - startTop ;
204261 didMove = didMove || Math . abs ( delta ) > 2 ;
205262 const lineDelta = Math . round ( delta / editor . getOption ( EditorOption . lineHeight ) ) ;
206263 const newVal = Math . max ( 0 , Math . min ( cur - lineDelta , this . _unchangedRegion . getMaxVisibleLineCountBottom ( ) ) ) ;
207- const top = editor . getTopForLineNumber ( this . _unchangedRegion . modifiedRange . endLineNumberExclusive ) ;
264+ const top = editor . getTopForLineNumber ( this . _unchangedRegion . originalRange . endLineNumberExclusive ) ;
208265 this . _unchangedRegion . visibleLineCountBottom . set ( newVal , undefined ) ;
209- const top2 = editor . getTopForLineNumber ( this . _unchangedRegion . modifiedRange . endLineNumberExclusive ) ;
266+ const top2 = editor . getTopForLineNumber ( this . _unchangedRegion . originalRange . endLineNumberExclusive ) ;
210267 editor . setScrollTop ( editor . getScrollTop ( ) + ( top2 - top ) ) ;
211268 } ) ;
212269
213- const mouseUpListener = addDisposableListener ( document . body , 'mouseup' , e => {
270+ const mouseUpListener = addDisposableListener ( window , 'mouseup' , e => {
214271 this . _unchangedRegion . isDragged . set ( false , undefined ) ;
215272
216273 if ( ! didMove ) {
217- const top = editor . getTopForLineNumber ( this . _unchangedRegion . modifiedRange . endLineNumberExclusive ) ;
274+ const top = editor . getTopForLineNumber ( this . _unchangedRegion . originalRange . endLineNumberExclusive ) ;
218275
219276 this . _unchangedRegion . showMoreBelow ( 20 , undefined ) ;
220- const top2 = editor . getTopForLineNumber ( this . _unchangedRegion . modifiedRange . endLineNumberExclusive ) ;
277+ const top2 = editor . getTopForLineNumber ( this . _unchangedRegion . originalRange . endLineNumberExclusive ) ;
221278 editor . setScrollTop ( editor . getScrollTop ( ) + ( top2 - top ) ) ;
222279 }
223280 this . _nodes . bottom . classList . toggle ( 'dragging' , false ) ;
0 commit comments