@@ -45,7 +45,7 @@ use helix_view::{
45
45
document:: { FormatterError , Mode , SCRATCH_BUFFER_NAME } ,
46
46
editor:: Action ,
47
47
info:: Info ,
48
- input:: KeyEvent ,
48
+ input:: { Event , KeyEvent , KeyModifiers } ,
49
49
keyboard:: KeyCode ,
50
50
theme:: Style ,
51
51
tree,
@@ -58,10 +58,13 @@ use insert::*;
58
58
use movement:: Movement ;
59
59
60
60
use crate :: {
61
- compositor:: { self , Component , Compositor } ,
61
+ compositor:: { self , Component , Compositor , EventResult } ,
62
62
filter_picker_entry,
63
63
job:: Callback ,
64
- ui:: { self , overlay:: overlaid, Picker , PickerColumn , Popup , Prompt , PromptEvent } ,
64
+ ui:: {
65
+ self , overlay:: overlaid, picker:: PickerSideEffect , Picker , PickerColumn , Popup , Prompt ,
66
+ PromptEvent ,
67
+ } ,
65
68
} ;
66
69
67
70
use crate :: job:: { self , Jobs } ;
@@ -78,6 +81,7 @@ use std::{
78
81
79
82
use std:: {
80
83
borrow:: Cow ,
84
+ iter,
81
85
path:: { Path , PathBuf } ,
82
86
} ;
83
87
@@ -402,6 +406,7 @@ impl MappableCommand {
402
406
file_explorer_in_current_buffer_directory, "Open file explorer at current buffer's directory" ,
403
407
file_explorer_in_current_directory, "Open file explorer at current working directory" ,
404
408
code_action, "Perform code action" ,
409
+ labelled_buffer_picker, "Open labelled buffer picker" ,
405
410
buffer_picker, "Open buffer picker" ,
406
411
jumplist_picker, "Open jumplist picker" ,
407
412
symbol_picker, "Open symbol picker" ,
@@ -3113,36 +3118,78 @@ fn file_explorer_in_current_directory(cx: &mut Context) {
3113
3118
}
3114
3119
}
3115
3120
3116
- fn buffer_picker ( cx : & mut Context ) {
3121
+ fn iter_newbase ( n : u32 , base : u32 ) -> impl Iterator < Item = u32 > {
3122
+ let mut num = n;
3123
+ let mut divisor = 1 ;
3124
+ while divisor * base <= n {
3125
+ divisor *= base;
3126
+ }
3127
+ iter:: from_fn ( move || {
3128
+ if divisor <= 0 {
3129
+ return None ;
3130
+ }
3131
+ let digit = num / divisor;
3132
+ num %= divisor;
3133
+ divisor /= base;
3134
+ Some ( digit)
3135
+ } )
3136
+ }
3137
+
3138
+ fn ord_label_nopad ( ord : u32 , labels : & [ char ] ) -> impl Iterator < Item = char > + use < ' _ > {
3139
+ iter_newbase ( ord, labels. len ( ) as u32 )
3140
+ . map ( |i| labels. get ( i as usize ) )
3141
+ . filter_map ( |o| o)
3142
+ . map ( |r| * r)
3143
+ }
3144
+
3145
+ fn ord_label ( ord : u32 , max : u32 , labels : & [ char ] ) -> Vec < char > {
3146
+ let max_len = ord_label_nopad ( max, labels) . count ( ) ;
3147
+ let label_nopad: Vec < char > = ord_label_nopad ( ord, labels) . collect ( ) ;
3148
+ iter:: repeat_n ( labels[ 0 ] , max_len - label_nopad. len ( ) )
3149
+ . chain ( label_nopad. into_iter ( ) )
3150
+ . collect ( )
3151
+ }
3152
+
3153
+ #[ derive( Clone ) ]
3154
+ struct BufferMeta {
3155
+ id : DocumentId ,
3156
+ label : Vec < char > ,
3157
+ path : Option < PathBuf > ,
3158
+ is_modified : bool ,
3159
+ is_current : bool ,
3160
+ focused_at : std:: time:: Instant ,
3161
+ }
3162
+
3163
+ fn get_buffers ( cx : & mut Context ) -> Vec < BufferMeta > {
3117
3164
let current = view ! ( cx. editor) . doc ;
3118
3165
3119
- struct BufferMeta {
3120
- id : DocumentId ,
3121
- path : Option < PathBuf > ,
3122
- is_modified : bool ,
3123
- is_current : bool ,
3124
- focused_at : std:: time:: Instant ,
3125
- }
3166
+ let labels = & cx. editor . config ( ) . buffer_picker . label_alphabet ;
3126
3167
3127
- let new_meta = |doc : & Document | BufferMeta {
3168
+ let new_meta = |( i , doc) : ( usize , & Document ) | BufferMeta {
3128
3169
id : doc. id ( ) ,
3129
3170
path : doc. path ( ) . cloned ( ) ,
3130
3171
is_modified : doc. is_modified ( ) ,
3131
3172
is_current : doc. id ( ) == current,
3132
3173
focused_at : doc. focused_at ,
3174
+ label : ord_label ( i as u32 , cx. editor . documents . len ( ) as u32 , labels) ,
3133
3175
} ;
3134
3176
3135
- let mut items = cx
3136
- . editor
3177
+ cx. editor
3137
3178
. documents
3138
3179
. values ( )
3180
+ . enumerate ( )
3139
3181
. map ( new_meta)
3140
- . collect :: < Vec < BufferMeta > > ( ) ;
3182
+ . collect :: < Vec < BufferMeta > > ( )
3183
+ }
3141
3184
3142
- // mru
3143
- items. sort_unstable_by_key ( |item| std:: cmp:: Reverse ( item. focused_at ) ) ;
3185
+ fn get_buffers_mru ( cx : & mut Context ) -> Vec < BufferMeta > {
3186
+ let mut buffers = get_buffers ( cx) ;
3187
+ buffers. sort_unstable_by_key ( |item| std:: cmp:: Reverse ( item. focused_at ) ) ;
3188
+ buffers
3189
+ }
3144
3190
3145
- let columns = [
3191
+ fn get_buffer_picker_columns < T > ( ) -> impl IntoIterator < Item = PickerColumn < BufferMeta , T > > {
3192
+ [
3146
3193
PickerColumn :: new ( "id" , |meta : & BufferMeta , _| meta. id . to_string ( ) . into ( ) ) ,
3147
3194
PickerColumn :: new ( "flags" , |meta : & BufferMeta , _| {
3148
3195
let mut flags = String :: new ( ) ;
@@ -3165,10 +3212,97 @@ fn buffer_picker(cx: &mut Context) {
3165
3212
. to_string ( )
3166
3213
. into ( )
3167
3214
} ) ,
3168
- ] ;
3169
- let picker = Picker :: new ( columns, 2 , items, ( ) , |cx, meta, action| {
3170
- cx. editor . switch ( meta. id , action) ;
3171
- } )
3215
+ ]
3216
+ }
3217
+
3218
+ fn get_labelled_buffer_picker_columns < T > ( ) -> impl IntoIterator < Item = PickerColumn < BufferMeta , T > >
3219
+ {
3220
+ iter:: once ( PickerColumn :: new ( "label" , |meta : & BufferMeta , _| {
3221
+ meta. label . iter ( ) . collect :: < String > ( ) . into ( )
3222
+ } ) )
3223
+ . chain ( get_buffer_picker_columns ( ) )
3224
+ }
3225
+
3226
+ fn labelled_buffer_picker ( cx : & mut Context ) {
3227
+ let items = get_buffers ( cx) ;
3228
+
3229
+ let labels = & cx. editor . config ( ) . buffer_picker . label_alphabet ;
3230
+ let max_label = ord_label_nopad ( cx. editor . documents . len ( ) as u32 , labels) . count ( ) ;
3231
+
3232
+ let mut chars_read = 0 ;
3233
+ let mut matching: Vec < bool > = iter:: repeat_n ( true , items. len ( ) ) . collect ( ) ;
3234
+
3235
+ let picker = Picker :: new (
3236
+ get_labelled_buffer_picker_columns ( ) ,
3237
+ 2 ,
3238
+ items. clone ( ) ,
3239
+ ( ) ,
3240
+ |cx, meta, action| {
3241
+ cx. editor . switch ( meta. id , action) ;
3242
+ } ,
3243
+ )
3244
+ . with_text_typing_handler (
3245
+ move |event : & Event , cx : & mut compositor:: Context | -> ( PickerSideEffect , EventResult ) {
3246
+ if let Event :: Key ( KeyEvent {
3247
+ code : KeyCode :: Char ( c) ,
3248
+ modifiers : KeyModifiers :: NONE ,
3249
+ } ) = event
3250
+ {
3251
+ chars_read += 1 ;
3252
+ if chars_read > max_label {
3253
+ // TODO: raise message that match failed (invalid key sequence)
3254
+ chars_read = 0 ;
3255
+ matching. iter_mut ( ) . for_each ( |v| * v = true ) ;
3256
+ return ( PickerSideEffect :: None , EventResult :: Consumed ( None ) ) ;
3257
+ }
3258
+ let idx = chars_read - 1 ;
3259
+ items. iter ( ) . enumerate ( ) . for_each ( |( i, item) | {
3260
+ if * c != item. label [ idx] {
3261
+ matching[ i] = false ;
3262
+ }
3263
+ } ) ;
3264
+ let nmatches = matching. iter ( ) . fold ( 0 , |acc, & c| acc + c as i32 ) ;
3265
+ if nmatches == 0 {
3266
+ // TODO: raise message that match failed (invalid key sequence)
3267
+ chars_read = 0 ;
3268
+ matching. iter_mut ( ) . for_each ( |v| * v = true ) ;
3269
+ } else if nmatches == 1 {
3270
+ // unique match found
3271
+ let match_idx = matching
3272
+ . iter ( )
3273
+ . enumerate ( )
3274
+ . find ( |( _, c) | * * c)
3275
+ . map ( |( i, _) | i)
3276
+ . unwrap ( ) ;
3277
+ cx. editor . switch ( items[ match_idx] . id , Action :: Replace ) ;
3278
+ return ( PickerSideEffect :: Close , EventResult :: Consumed ( None ) ) ;
3279
+ }
3280
+ }
3281
+ ( PickerSideEffect :: None , EventResult :: Consumed ( None ) )
3282
+ } ,
3283
+ )
3284
+ . with_preview ( |editor, meta| {
3285
+ let doc = & editor. documents . get ( & meta. id ) ?;
3286
+ let lines = doc. selections ( ) . values ( ) . next ( ) . map ( |selection| {
3287
+ let cursor_line = selection. primary ( ) . cursor_line ( doc. text ( ) . slice ( ..) ) ;
3288
+ ( cursor_line, cursor_line)
3289
+ } ) ;
3290
+ Some ( ( meta. id . into ( ) , lines) )
3291
+ } ) ;
3292
+ cx. push_layer ( Box :: new ( overlaid ( picker) ) ) ;
3293
+ }
3294
+
3295
+ fn buffer_picker ( cx : & mut Context ) {
3296
+ let items = get_buffers_mru ( cx) ;
3297
+ let picker = Picker :: new (
3298
+ get_buffer_picker_columns ( ) ,
3299
+ 2 ,
3300
+ items,
3301
+ ( ) ,
3302
+ |cx, meta, action| {
3303
+ cx. editor . switch ( meta. id , action) ;
3304
+ } ,
3305
+ )
3172
3306
. with_preview ( |editor, meta| {
3173
3307
let doc = & editor. documents . get ( & meta. id ) ?;
3174
3308
let lines = doc. selections ( ) . values ( ) . next ( ) . map ( |selection| {
0 commit comments