@@ -7,6 +7,7 @@ use crate::models::application::Event;
77use crate :: util:: SelectableVec ;
88use bloodhound:: ExclusionPattern ;
99pub use bloodhound:: Index ;
10+ use std:: collections:: HashSet ;
1011use std:: fmt;
1112use std:: path:: PathBuf ;
1213use std:: slice:: Iter ;
@@ -25,6 +26,7 @@ pub struct OpenMode {
2526 pinned_input : String ,
2627 index : OpenModeIndex ,
2728 pub results : SelectableVec < DisplayablePath > ,
29+ marked_results : HashSet < usize > ,
2830 config : SearchSelectConfig ,
2931}
3032
@@ -36,6 +38,7 @@ impl OpenMode {
3638 pinned_input : String :: new ( ) ,
3739 index : OpenModeIndex :: Indexing ( path) ,
3840 results : SelectableVec :: new ( Vec :: new ( ) ) ,
41+ marked_results : HashSet :: new ( ) ,
3942 config,
4043 }
4144 }
@@ -56,6 +59,7 @@ impl OpenMode {
5659 self . config = config;
5760 self . index = OpenModeIndex :: Indexing ( path. clone ( ) ) ;
5861 self . results = SelectableVec :: new ( Vec :: new ( ) ) ;
62+ self . marked_results = HashSet :: new ( ) ;
5963
6064 // Build and populate the index in a separate thread.
6165 thread:: spawn ( move || {
@@ -107,6 +111,30 @@ impl OpenMode {
107111 PopSearchToken :: pop_search_token ( self ) ;
108112 }
109113 }
114+
115+ pub fn toggle_selection ( & mut self ) {
116+ if let None = self . marked_results . take ( & self . selected_index ( ) ) {
117+ self . marked_results . insert ( self . selected_index ( ) ) ;
118+ }
119+ }
120+
121+ pub fn selections ( & self ) -> Vec < & DisplayablePath > {
122+ let mut selections: Vec < & DisplayablePath > = self
123+ . marked_results
124+ . iter ( )
125+ . map ( |i| self . results . get ( * i) . unwrap ( ) )
126+ . collect ( ) ;
127+ selections. push ( self . selection ( ) . unwrap ( ) ) ;
128+
129+ selections
130+ }
131+
132+ pub fn selected_indices ( & self ) -> Vec < usize > {
133+ let mut selected_indices: Vec < usize > = self . marked_results . iter ( ) . copied ( ) . collect ( ) ;
134+ selected_indices. push ( self . selected_index ( ) ) ;
135+
136+ selected_indices
137+ }
110138}
111139
112140impl fmt:: Display for OpenMode {
@@ -137,6 +165,7 @@ impl SearchSelectMode for OpenMode {
137165 } ;
138166
139167 self . results = SelectableVec :: new ( results) ;
168+ self . marked_results = HashSet :: new ( ) ;
140169 }
141170
142171 fn query ( & mut self ) -> & mut String {
@@ -191,6 +220,7 @@ impl SearchSelectMode for OpenMode {
191220#[ cfg( test) ]
192221mod tests {
193222 use super :: OpenMode ;
223+ use crate :: models:: application:: modes:: open:: DisplayablePath ;
194224 use crate :: models:: application:: modes:: { SearchSelectConfig , SearchSelectMode } ;
195225 use crate :: models:: application:: Event ;
196226 use std:: env;
@@ -306,4 +336,193 @@ mod tests {
306336 mode. pop_search_token ( ) ;
307337 assert_eq ! ( mode. pinned_query( ) , "" ) ;
308338 }
339+
340+ #[ test]
341+ fn selections_returns_current_selection ( ) {
342+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
343+ let config = SearchSelectConfig :: default ( ) ;
344+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
345+ let ( sender, receiver) = channel ( ) ;
346+
347+ // Populate the index
348+ mode. reset ( path, None , sender, config) ;
349+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
350+ mode. set_index ( index) ;
351+ }
352+
353+ mode. query ( ) . push_str ( "Cargo.toml" ) ;
354+ mode. search ( ) ;
355+
356+ let selections: Vec < & DisplayablePath > = mode. selections ( ) . iter ( ) . copied ( ) . collect ( ) ;
357+ assert_eq ! ( selections, vec![ mode. results( ) . next( ) . unwrap( ) ] ) ;
358+ }
359+
360+ #[ test]
361+ fn selections_includes_marked_selections ( ) {
362+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
363+ let config = SearchSelectConfig :: default ( ) ;
364+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
365+ let ( sender, receiver) = channel ( ) ;
366+
367+ // Populate the index
368+ mode. reset ( path, None , sender, config) ;
369+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
370+ mode. set_index ( index) ;
371+ }
372+
373+ mode. query ( ) . push_str ( "Cargo" ) ;
374+ mode. search ( ) ;
375+ mode. toggle_selection ( ) ;
376+ mode. select_next ( ) ;
377+
378+ let selections: Vec < & DisplayablePath > = mode. selections ( ) . iter ( ) . copied ( ) . collect ( ) ;
379+ assert_eq ! ( selections, mode. results( ) . take( 2 ) . collect:: <Vec <_>>( ) ) ;
380+ }
381+
382+ #[ test]
383+ fn selections_does_not_include_unmarked_indices ( ) {
384+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
385+ let config = SearchSelectConfig :: default ( ) ;
386+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
387+ let ( sender, receiver) = channel ( ) ;
388+
389+ // Populate the index
390+ mode. reset ( path, None , sender, config) ;
391+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
392+ mode. set_index ( index) ;
393+ }
394+
395+ mode. query ( ) . push_str ( "Cargo" ) ;
396+ mode. search ( ) ;
397+ mode. toggle_selection ( ) ;
398+ mode. toggle_selection ( ) ;
399+ mode. select_next ( ) ;
400+
401+ let selections: Vec < & DisplayablePath > = mode. selections ( ) . iter ( ) . copied ( ) . collect ( ) ;
402+ assert_eq ! ( selections, vec![ mode. results( ) . nth( 1 ) . unwrap( ) ] ) ;
403+ }
404+
405+ #[ test]
406+ fn selected_indices_returns_current_index ( ) {
407+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
408+ let config = SearchSelectConfig :: default ( ) ;
409+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
410+ let ( sender, receiver) = channel ( ) ;
411+
412+ // Populate the index
413+ mode. reset ( path, None , sender, config) ;
414+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
415+ mode. set_index ( index) ;
416+ }
417+
418+ mode. query ( ) . push_str ( "Cargo.toml" ) ;
419+ mode. search ( ) ;
420+
421+ assert_eq ! ( mode. selected_indices( ) , vec![ 0 ] ) ;
422+ }
423+
424+ #[ test]
425+ fn selected_indices_includes_marked_indices ( ) {
426+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
427+ let config = SearchSelectConfig :: default ( ) ;
428+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
429+ let ( sender, receiver) = channel ( ) ;
430+
431+ // Populate the index
432+ mode. reset ( path, None , sender, config) ;
433+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
434+ mode. set_index ( index) ;
435+ }
436+
437+ mode. query ( ) . push_str ( "Cargo" ) ;
438+ mode. search ( ) ;
439+ mode. toggle_selection ( ) ;
440+ mode. select_next ( ) ;
441+
442+ assert_eq ! ( mode. selected_indices( ) , vec![ 0 , 1 ] ) ;
443+ }
444+
445+ #[ test]
446+ fn selected_indices_does_not_include_unmarked_indices ( ) {
447+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
448+ let config = SearchSelectConfig :: default ( ) ;
449+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
450+ let ( sender, receiver) = channel ( ) ;
451+
452+ // Populate the index
453+ mode. reset ( path, None , sender, config) ;
454+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
455+ mode. set_index ( index) ;
456+ }
457+
458+ mode. query ( ) . push_str ( "Cargo" ) ;
459+ mode. search ( ) ;
460+ mode. toggle_selection ( ) ;
461+ mode. toggle_selection ( ) ;
462+ mode. select_next ( ) ;
463+
464+ assert_eq ! ( mode. selected_indices( ) , vec![ 1 ] ) ;
465+ }
466+
467+ #[ test]
468+ fn search_clears_marked_indices ( ) {
469+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
470+ let config = SearchSelectConfig :: default ( ) ;
471+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
472+ let ( sender, receiver) = channel ( ) ;
473+
474+ // Populate the index
475+ mode. reset ( path. clone ( ) , None , sender. clone ( ) , config. clone ( ) ) ;
476+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
477+ mode. set_index ( index) ;
478+ }
479+
480+ // Produce results and mark one of them
481+ mode. query ( ) . push_str ( "Cargo" ) ;
482+ mode. search ( ) ;
483+ mode. toggle_selection ( ) ;
484+
485+ // Change the search results
486+ mode. query ( ) . push_str ( "." ) ;
487+ mode. search ( ) ;
488+
489+ // Ensure the previously-marked result isn't currently selected
490+ mode. select_next ( ) ;
491+
492+ // Verify that the marked result isn't included
493+ assert_eq ! ( mode. selected_indices( ) , vec![ 1 ] ) ;
494+ }
495+
496+ #[ test]
497+ fn reset_clears_marked_indices ( ) {
498+ let path = env:: current_dir ( ) . expect ( "can't get current directory/path" ) ;
499+ let config = SearchSelectConfig :: default ( ) ;
500+ let mut mode = OpenMode :: new ( path. clone ( ) , config. clone ( ) ) ;
501+ let ( sender, receiver) = channel ( ) ;
502+
503+ // Populate the index
504+ mode. reset ( path. clone ( ) , None , sender. clone ( ) , config. clone ( ) ) ;
505+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
506+ mode. set_index ( index) ;
507+ }
508+
509+ // Produce results and mark one of them
510+ mode. query ( ) . push_str ( "Cargo" ) ;
511+ mode. search ( ) ;
512+ mode. toggle_selection ( ) ;
513+
514+ // Reset the mode and repopulate the index
515+ mode. reset ( path, None , sender, config) ;
516+ if let Ok ( Event :: OpenModeIndexComplete ( index) ) = receiver. recv ( ) {
517+ mode. set_index ( index) ;
518+ }
519+ mode. query ( ) . push_str ( "Cargo" ) ;
520+ mode. search ( ) ;
521+
522+ // Ensure the previously-marked result isn't currently selected
523+ mode. select_next ( ) ;
524+
525+ // Verify that the marked result isn't included
526+ assert_eq ! ( mode. selected_indices( ) , vec![ 1 ] ) ;
527+ }
309528}
0 commit comments