@@ -15,29 +15,70 @@ pub enum LogTableKind {
1515 Search ,
1616}
1717
18- /// Narrows the global row selection to the rows one table's actions operate on.
18+ /// Identifies the log table whose logical contents an action operates on.
19+ ///
20+ /// The row selection is global, so actions started from one table must narrow it to the rows
21+ /// that table actually shows.
1922#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
2023pub enum SelectionScope {
21- /// Every selected stream row.
22- AllSelected ,
23- /// Selected rows that belong to the search table's logical contents .
24- SearchRows ,
24+ /// The main logs table, which contains every stream row.
25+ MainTable ,
26+ /// The search results table, which contains search matches and bookmarked rows .
27+ SearchResults ,
2528}
2629
2730impl SelectionScope {
2831 /// Iterates over the selected stream rows inside this scope.
2932 pub fn rows ( self , shared : & SessionShared ) -> impl Iterator < Item = u64 > + ' _ {
3033 shared. logs . selected_rows ( ) . filter ( move |& row| match self {
31- Self :: AllSelected => true ,
32- Self :: SearchRows => shared. search . has_match ( row) || shared. logs . is_bookmarked ( row) ,
34+ Self :: MainTable => true ,
35+ Self :: SearchResults => shared. search . has_match ( row) || shared. logs . is_bookmarked ( row) ,
3336 } )
3437 }
3538
3639 /// Returns how many selected rows fall inside this scope.
3740 pub fn count ( self , shared : & SessionShared ) -> usize {
3841 match self {
39- Self :: AllSelected => shared. logs . selected_count ( ) ,
40- Self :: SearchRows => self . rows ( shared) . count ( ) ,
42+ Self :: MainTable => shared. logs . selected_count ( ) ,
43+ Self :: SearchResults => self . rows ( shared) . count ( ) ,
44+ }
45+ }
46+
47+ /// Returns how many rows this table contains in total.
48+ pub fn total_count ( self , shared : & SessionShared ) -> usize {
49+ match self {
50+ Self :: MainTable => shared. logs . logs_count ( ) as usize ,
51+ // Bookmarks are user-created and few, so correcting the overlap per frame is cheap.
52+ Self :: SearchResults => {
53+ let extra_bookmarks = shared
54+ . logs
55+ . bookmarked_rows
56+ . iter ( )
57+ . filter ( |row| !shared. search . has_match ( * * row) )
58+ . count ( ) ;
59+
60+ shared. search . matches_count ( ) + extra_bookmarks
61+ }
62+ }
63+ }
64+
65+ /// Replaces the selection with every row this table contains.
66+ ///
67+ /// This reports no [`SelectionChange`](crate::session::ui::shared::SelectionChange): selecting
68+ /// a whole table must not load details or realign the peer table.
69+ pub fn select_all ( self , shared : & mut SessionShared ) {
70+ match self {
71+ Self :: MainTable => {
72+ let logs_count = shared. logs . logs_count ( ) ;
73+ shared. logs . select_rows ( 0 ..logs_count) ;
74+ }
75+ Self :: SearchResults => {
76+ // Bookmarks live in `LogsState`, which is borrowed mutably below.
77+ let bookmarks: Vec < u64 > = shared. logs . bookmarked_rows . iter ( ) . copied ( ) . collect ( ) ;
78+ shared
79+ . logs
80+ . select_rows ( shared. search . match_positions ( ) . chain ( bookmarks) ) ;
81+ }
4182 }
4283 }
4384}
@@ -95,27 +136,59 @@ mod tests {
95136 shared
96137 }
97138
139+ fn sorted_selection ( shared : & SessionShared ) -> Vec < u64 > {
140+ let mut rows = shared. logs . selected_rows ( ) . collect :: < Vec < _ > > ( ) ;
141+ rows. sort_unstable ( ) ;
142+
143+ rows
144+ }
145+
98146 #[ test]
99147 fn search_scope_includes_matches_and_bookmarks_only ( ) {
100148 let shared = shared_with_selection ( ) ;
101149
102- let mut rows = SelectionScope :: SearchRows . rows ( & shared) . collect :: < Vec < _ > > ( ) ;
150+ let mut rows = SelectionScope :: SearchResults
151+ . rows ( & shared)
152+ . collect :: < Vec < _ > > ( ) ;
103153 rows. sort_unstable ( ) ;
104154
105155 assert_eq ! ( rows, vec![ 10 , 20 , 30 ] ) ;
106- assert_eq ! ( SelectionScope :: SearchRows . count( & shared) , 3 ) ;
156+ assert_eq ! ( SelectionScope :: SearchResults . count( & shared) , 3 ) ;
107157 }
108158
109159 #[ test]
110- fn all_selected_scope_keeps_whole_selection ( ) {
160+ fn main_table_scope_keeps_whole_selection ( ) {
111161 let shared = shared_with_selection ( ) ;
112162
113- let mut rows = SelectionScope :: AllSelected
114- . rows ( & shared)
115- . collect :: < Vec < _ > > ( ) ;
163+ let mut rows = SelectionScope :: MainTable . rows ( & shared) . collect :: < Vec < _ > > ( ) ;
116164 rows. sort_unstable ( ) ;
117165
118166 assert_eq ! ( rows, vec![ 10 , 20 , 30 , 40 ] ) ;
119- assert_eq ! ( SelectionScope :: AllSelected . count( & shared) , 4 ) ;
167+ assert_eq ! ( SelectionScope :: MainTable . count( & shared) , 4 ) ;
168+ }
169+
170+ #[ test]
171+ fn main_table_select_all_replaces_selection_with_every_stream_row ( ) {
172+ let mut shared = shared_with_selection ( ) ;
173+ shared. logs . set_logs_count ( 5 ) ;
174+
175+ assert_eq ! ( SelectionScope :: MainTable . total_count( & shared) , 5 ) ;
176+
177+ SelectionScope :: MainTable . select_all ( & mut shared) ;
178+
179+ assert_eq ! ( sorted_selection( & shared) , vec![ 0 , 1 , 2 , 3 , 4 ] ) ;
180+ }
181+
182+ #[ test]
183+ fn search_results_select_all_takes_matches_and_bookmarks_only ( ) {
184+ let mut shared = shared_with_selection ( ) ;
185+ shared. logs . set_logs_count ( 50 ) ;
186+
187+ // Row 30 is both a match and a bookmark, so the overlap must not be counted twice.
188+ assert_eq ! ( SelectionScope :: SearchResults . total_count( & shared) , 3 ) ;
189+
190+ SelectionScope :: SearchResults . select_all ( & mut shared) ;
191+
192+ assert_eq ! ( sorted_selection( & shared) , vec![ 10 , 20 , 30 ] ) ;
120193 }
121194}
0 commit comments