@@ -125,10 +125,11 @@ struct ActiveSearch {
125125 /// created. When the user manually selects text, we need to
126126 /// refresh the [`ActiveSearch::pattern`] with it.
127127 selection_generation : u32 ,
128- /// Stores the text buffer offset in between searches.
129- next_search_offset : usize ,
130- /// If we know there were no hits, we can skip searching.
131- no_matches : bool ,
128+
129+ /// All found matches in the buffer.
130+ matches : Vec < Range < usize > > ,
131+ /// The index of the currently selected match in `matches`.
132+ current_match_index : Option < usize > ,
132133}
133134
134135/// Options for a search operation.
@@ -1080,7 +1081,7 @@ impl TextBuffer {
10801081 }
10811082
10821083 /// Find the next occurrence of the given `pattern` and select it.
1083- pub fn find_and_select ( & mut self , pattern : & str , options : SearchOptions ) -> apperr:: Result < ( ) > {
1084+ pub fn find_and_select ( & mut self , pattern : & str , options : SearchOptions ) -> apperr:: Result < Option < ( usize , usize ) > > {
10841085 if let Some ( search) = & mut self . search {
10851086 let search = search. get_mut ( ) ;
10861087 // When the search input changes we must reset the search.
@@ -1097,7 +1098,7 @@ impl TextBuffer {
10971098 }
10981099
10991100 if pattern. is_empty ( ) {
1100- return Ok ( ( ) ) ;
1101+ return Ok ( None ) ;
11011102 }
11021103
11031104 let search = match & self . search {
@@ -1109,18 +1110,19 @@ impl TextBuffer {
11091110 }
11101111 } ;
11111112
1112- // If we previously searched through the entire document and found 0 matches,
1113- // then we can avoid searching again.
1114- if search. no_matches {
1115- return Ok ( ( ) ) ;
1113+ if search. matches . is_empty ( ) {
1114+ return Ok ( Some ( ( 0 , 0 ) ) ) ;
11161115 }
11171116
11181117 // If the user moved the cursor since the last search, but the needle remained the same,
11191118 // we still need to move the start of the search to the new cursor position.
11201119 let next_search_offset = match self . selection {
11211120 Some ( TextBufferSelection { beg, end } ) => {
11221121 if self . selection_generation == search. selection_generation {
1123- search. next_search_offset
1122+ // search.next_search_offset was removed.
1123+ // If we have a valid selection from previous search, start after it.
1124+ let p = beg. max ( end) ;
1125+ self . cursor_move_to_logical_internal ( self . cursor , p) . offset
11241126 } else {
11251127 self . cursor_move_to_logical_internal ( self . cursor , beg. min ( end) ) . offset
11261128 }
@@ -1129,7 +1131,12 @@ impl TextBuffer {
11291131 } ;
11301132
11311133 self . find_select_next ( search, next_search_offset, true ) ;
1132- Ok ( ( ) )
1134+
1135+ if let Some ( idx) = search. current_match_index {
1136+ Ok ( Some ( ( idx + 1 , search. matches . len ( ) ) ) )
1137+ } else {
1138+ Ok ( Some ( ( 0 , search. matches . len ( ) ) ) )
1139+ }
11331140 }
11341141
11351142 /// Find the next occurrence of the given `pattern` and replace it with `replacement`.
@@ -1138,7 +1145,7 @@ impl TextBuffer {
11381145 pattern : & str ,
11391146 options : SearchOptions ,
11401147 replacement : & [ u8 ] ,
1141- ) -> apperr:: Result < ( ) > {
1148+ ) -> apperr:: Result < Option < ( usize , usize ) > > {
11421149 // Editors traditionally replace the previous search hit, not the next possible one.
11431150 if let ( Some ( search) , Some ( ..) ) = ( & self . search , & self . selection ) {
11441151 let search = unsafe { & mut * search. get ( ) } ;
@@ -1161,7 +1168,7 @@ impl TextBuffer {
11611168 pattern : & str ,
11621169 options : SearchOptions ,
11631170 replacement : & [ u8 ] ,
1164- ) -> apperr:: Result < ( ) > {
1171+ ) -> apperr:: Result < Option < ( usize , usize ) > > {
11651172 let scratch = scratch_arena ( None ) ;
11661173 let mut search = self . find_construct_search ( pattern, options) ?;
11671174 let mut offset = 0 ;
@@ -1179,7 +1186,7 @@ impl TextBuffer {
11791186 offset = self . cursor . offset ;
11801187 }
11811188
1182- Ok ( ( ) )
1189+ Ok ( None )
11831190 }
11841191
11851192 fn find_construct_search (
@@ -1228,7 +1235,13 @@ impl TextBuffer {
12281235 // or otherwise to the current cursor position.
12291236
12301237 let text = unsafe { icu:: Text :: new ( self ) ? } ;
1231- let regex = unsafe { icu:: Regex :: new ( & sanitized_pattern, flags, & text) ? } ;
1238+ let mut regex = unsafe { icu:: Regex :: new ( & sanitized_pattern, flags, & text) ? } ;
1239+
1240+ let mut matches = Vec :: new ( ) ;
1241+ regex. reset ( 0 ) ;
1242+ while let Some ( range) = regex. next ( ) {
1243+ matches. push ( range) ;
1244+ }
12321245
12331246 Ok ( ActiveSearch {
12341247 pattern : pattern. to_string ( ) ,
@@ -1237,50 +1250,62 @@ impl TextBuffer {
12371250 regex,
12381251 buffer_generation : self . buffer . generation ( ) ,
12391252 selection_generation : 0 ,
1240- next_search_offset : 0 ,
1241- no_matches : false ,
1253+ matches ,
1254+ current_match_index : None ,
12421255 } )
12431256 }
12441257
12451258 fn find_select_next ( & mut self , search : & mut ActiveSearch , offset : usize , wrap : bool ) {
12461259 if search. buffer_generation != self . buffer . generation ( ) {
12471260 unsafe { search. regex . set_text ( & mut search. text , offset) } ;
12481261 search. buffer_generation = self . buffer . generation ( ) ;
1249- search. next_search_offset = offset;
1250- } else if search. next_search_offset != offset {
1251- search. next_search_offset = offset;
1252- search. regex . reset ( offset) ;
1262+
1263+ search. matches . clear ( ) ;
1264+ search. regex . reset ( 0 ) ;
1265+ while let Some ( range) = search. regex . next ( ) {
1266+ search. matches . push ( range) ;
1267+ }
1268+ search. current_match_index = None ;
1269+ }
1270+
1271+ if search. matches . is_empty ( ) {
1272+ search. current_match_index = None ;
1273+ self . set_selection ( None ) ;
1274+ return ;
12531275 }
12541276
1255- let mut hit = search. regex . next ( ) ;
1256-
1257- // If we hit the end of the buffer, and we know that there's something to find,
1258- // start the search again from the beginning (= wrap around).
1259- if wrap && hit. is_none ( ) && search. next_search_offset != 0 {
1260- search. next_search_offset = 0 ;
1261- search. regex . reset ( 0 ) ;
1262- hit = search. regex . next ( ) ;
1277+ // Find match >= offset
1278+ let idx_res = search. matches . binary_search_by_key ( & offset, |r| r. start ) ;
1279+ let start_idx = match idx_res {
1280+ Ok ( i) => i,
1281+ Err ( i) => i,
1282+ } ;
1283+
1284+ let mut next_idx = None ;
1285+ if start_idx < search. matches . len ( ) {
1286+ next_idx = Some ( start_idx) ;
1287+ } else if wrap {
1288+ next_idx = Some ( 0 ) ;
12631289 }
12641290
1265- search . selection_generation = if let Some ( range ) = hit {
1266- // Now the search offset is no more at the start of the buffer.
1267- search . next_search_offset = range . end ;
1291+ if let Some ( idx ) = next_idx {
1292+ search. current_match_index = Some ( idx ) ;
1293+ let range = search . matches [ idx ] . clone ( ) ;
12681294
12691295 let beg = self . cursor_move_to_offset_internal ( self . cursor , range. start ) ;
12701296 let end = self . cursor_move_to_offset_internal ( beg, range. end ) ;
12711297
12721298 unsafe { self . set_cursor ( end) } ;
12731299 self . make_cursor_visible ( ) ;
12741300
1275- self . set_selection ( Some ( TextBufferSelection {
1301+ search . selection_generation = self . set_selection ( Some ( TextBufferSelection {
12761302 beg : beg. logical_pos ,
12771303 end : end. logical_pos ,
1278- } ) )
1304+ } ) ) ;
12791305 } else {
1280- // Avoid searching through the entire document again if we know there's nothing to find.
1281- search. no_matches = true ;
1282- self . set_selection ( None )
1283- } ;
1306+ search. current_match_index = None ;
1307+ self . set_selection ( None ) ;
1308+ }
12841309 }
12851310
12861311 fn find_parse_replacement < ' a > (
0 commit comments