@@ -417,6 +417,7 @@ impl MappableCommand {
417417 jumplist_picker, "Open jumplist picker" ,
418418 symbol_picker, "Open symbol picker" ,
419419 changed_file_picker, "Open changed file picker" ,
420+ document_change_picker, "Open a picker of VCS changes in the current document" ,
420421 select_references_to_symbol_under_cursor, "Select symbol references" ,
421422 workspace_symbol_picker, "Open workspace symbol picker" ,
422423 diagnostics_picker, "Open diagnostic picker" ,
@@ -3261,6 +3262,78 @@ pub fn command_palette(cx: &mut Context) {
32613262 ) ) ;
32623263}
32633264
3265+ fn document_change_picker ( cx : & mut Context ) {
3266+ struct Data {
3267+ location : String ,
3268+ style_added : Style ,
3269+ style_modified : Style ,
3270+ style_removed : Style ,
3271+ }
3272+
3273+ let doc = doc ! ( cx. editor) ;
3274+ let Some ( diff_handle) = doc. diff_handle ( ) else {
3275+ cx. editor . set_status ( "Diff is not available in current buffer" ) ;
3276+ return ;
3277+ } ;
3278+ let doc_id = doc. id ( ) ;
3279+ let diff = diff_handle. load ( ) ;
3280+ let location = doc
3281+ . relative_path ( )
3282+ . map ( |path| path. to_string_lossy ( ) . into_owned ( ) )
3283+ . unwrap_or_else ( || SCRATCH_BUFFER_NAME . to_string ( ) ) ;
3284+
3285+ let data = Data {
3286+ location,
3287+ style_added : cx. editor . theme . get ( "diff.plus" ) ,
3288+ style_modified : cx. editor . theme . get ( "diff.delta" ) ,
3289+ style_removed : cx. editor . theme . get ( "diff.minus" ) ,
3290+ } ;
3291+
3292+ let columns = vec ! [
3293+ PickerColumn :: new( "change" , |hunk: & Hunk , data: & Data | {
3294+ use tui:: text:: Span ;
3295+
3296+ if hunk. is_pure_insertion( ) {
3297+ Span :: styled( "+ added" , data. style_added)
3298+ } else if hunk. is_pure_removal( ) {
3299+ Span :: styled( "- removed" , data. style_removed)
3300+ } else {
3301+ Span :: styled( "~ modified" , data. style_modified)
3302+ }
3303+ . into( )
3304+ } ) ,
3305+ PickerColumn :: new( "location" , |hunk: & Hunk , data: & Data | {
3306+ format!( "{}:{}" , data. location, hunk. after. start) . into( )
3307+ } ) ,
3308+ ] ;
3309+
3310+ let hunks = diff. hunks ( ) . cloned ( ) . collect ( ) ;
3311+
3312+ let picker = Picker :: new ( columns, 0 , hunks, data, move |cx, hunk : & Hunk , action| {
3313+ cx. editor . switch ( doc_id, action) ;
3314+ let config = cx. editor . config ( ) ;
3315+ let ( view, doc) = ( view_mut ! ( cx. editor) , doc_mut ! ( cx. editor, & doc_id) ) ;
3316+ let text = doc. text ( ) . slice ( ..) ;
3317+ let range = hunk_range ( hunk, text) ;
3318+ doc. set_selection ( view. id , range. into ( ) ) ;
3319+ if action. align_view ( view, doc. id ( ) ) {
3320+ view. ensure_cursor_in_view_center ( doc, config. scrolloff )
3321+ }
3322+ } )
3323+ . with_preview ( move |_editor, hunk| {
3324+ let start_line = hunk. after . start as usize ;
3325+ let end_line = if hunk. after . is_empty ( ) {
3326+ start_line + 1
3327+ } else {
3328+ hunk. after . end as usize
3329+ } ;
3330+ Some ( ( doc_id. into ( ) , Some ( ( start_line, end_line) ) ) )
3331+ } ) ;
3332+
3333+ drop ( diff) ;
3334+ cx. push_layer ( Box :: new ( overlaid ( picker) ) ) ;
3335+ }
3336+
32643337fn last_picker ( cx : & mut Context ) {
32653338 // TODO: last picker does not seem to work well with buffer_picker
32663339 cx. callback . push ( Box :: new ( |compositor, cx| {
@@ -3723,7 +3796,7 @@ fn goto_first_change_impl(cx: &mut Context, reverse: bool) {
37233796 diff. nth_hunk ( idx)
37243797 } ;
37253798 if hunk != Hunk :: NONE {
3726- let range = hunk_range ( hunk, doc. text ( ) . slice ( ..) ) ;
3799+ let range = hunk_range ( & hunk, doc. text ( ) . slice ( ..) ) ;
37273800 doc. set_selection ( view. id , Selection :: single ( range. anchor , range. head ) ) ;
37283801 }
37293802 }
@@ -3765,7 +3838,7 @@ fn goto_next_change_impl(cx: &mut Context, direction: Direction) {
37653838 return range;
37663839 } ;
37673840 let hunk = diff. nth_hunk ( hunk_idx) ;
3768- let new_range = hunk_range ( hunk, doc_text) ;
3841+ let new_range = hunk_range ( & hunk, doc_text) ;
37693842 if editor. mode == Mode :: Select {
37703843 let head = if new_range. head < range. anchor {
37713844 new_range. anchor
@@ -3787,7 +3860,7 @@ fn goto_next_change_impl(cx: &mut Context, direction: Direction) {
37873860/// Returns the [Range] for a [Hunk] in the given text.
37883861/// Additions and modifications cover the added and modified ranges.
37893862/// Deletions are represented as the point at the start of the deletion hunk.
3790- fn hunk_range ( hunk : Hunk , text : RopeSlice ) -> Range {
3863+ fn hunk_range ( hunk : & Hunk , text : RopeSlice ) -> Range {
37913864 let anchor = text. line_to_char ( hunk. after . start as usize ) ;
37923865 let head = if hunk. after . is_empty ( ) {
37933866 anchor + 1
0 commit comments