@@ -63,6 +63,7 @@ type action =
63
63
| Accept
64
64
| Clear_screen
65
65
| Prev_search
66
+ | Next_search
66
67
| Cancel_search
67
68
| Break
68
69
| Suspend
@@ -81,6 +82,7 @@ let doc_of_action = function
81
82
| Accept -> " accept the current input."
82
83
| Clear_screen -> " clear the screen."
83
84
| Prev_search -> " search backward in the history."
85
+ | Next_search -> " search forward in the history."
84
86
| Cancel_search -> " cancel search mode."
85
87
| Break -> " cancel edition."
86
88
| Suspend -> " suspend edition."
@@ -98,6 +100,7 @@ let actions = [
98
100
Accept , " accept" ;
99
101
Clear_screen , " clear-screen" ;
100
102
Prev_search , " prev-search" ;
103
+ Next_search , " next-search" ;
101
104
Cancel_search , " cancel-search" ;
102
105
Break , " break" ;
103
106
Suspend , " suspend" ;
@@ -164,6 +167,7 @@ let () =
164
167
bind [{ control = true ; meta = false ; shift = false ; code = Char (UChar. of_char 'm' ) }] [Accept ];
165
168
bind [{ control = true ; meta = false ; shift = false ; code = Char (UChar. of_char 'l' ) }] [Clear_screen ];
166
169
bind [{ control = true ; meta = false ; shift = false ; code = Char (UChar. of_char 'r' ) }] [Prev_search ];
170
+ bind [{ control = true ; meta = false ; shift = false ; code = Char (UChar. of_char 's' ) }] [Next_search ];
167
171
bind [{ control = true ; meta = false ; shift = false ; code = Char (UChar. of_char 'd' ) }] [Interrupt_or_delete_next_char ];
168
172
bind [{ control = false ; meta = true ; shift = false ; code = Char (UChar. of_char 'p' ) }] [History_prev ];
169
173
bind [{ control = false ; meta = true ; shift = false ; code = Char (UChar. of_char 'n' ) }] [History_next ];
@@ -217,6 +221,14 @@ let no_completion = {
217
221
count = 0 ;
218
222
}
219
223
224
+ type direction = Forward | Backward
225
+
226
+ type search_status =
227
+ { before : Zed_utf8 .t list
228
+ ; after : Zed_utf8 .t list
229
+ ; match_ : (Zed_utf8 .t * int ) option
230
+ }
231
+
220
232
class virtual ['a ] engine ?(history = [] ) ?(clipboard = LTerm_edit. clipboard) ?(macro = macro) () =
221
233
let edit : unit Zed_edit.t = Zed_edit. create ~clipboard () in
222
234
let context = Zed_edit. context edit (Zed_edit. new_cursor edit) in
@@ -300,41 +312,86 @@ object(self)
300
312
(* The event which search for the string in the history. *)
301
313
val mutable search_event = E. never
302
314
303
- (* The result of the search. If the search was successful it
304
- contains the matched history entry, the position of the substring
305
- in this entry and the rest of the history. *)
306
- val mutable search_result = None
315
+ val mutable search_status = None
307
316
308
317
initializer
309
- search_event < - E. map (fun _ -> search_result < - None ; self#search) (E. when_ (S. map (fun mode -> mode = Search ) mode) (Zed_edit. changes edit))
310
-
311
- method private search =
312
- let input = Zed_rope. to_string (Zed_edit. text edit) in
313
- let rec loop = function
314
- | [] ->
315
- search_result < - None ;
318
+ let reset_search _ =
319
+ search_status < - None ;
320
+ self#search Backward
321
+ in
322
+ search_event < -
323
+ E. map reset_search
324
+ (E. when_ (S. map (fun mode -> mode = Search ) mode)
325
+ (Zed_edit. changes edit))
326
+
327
+ method private search direction =
328
+ let do_search direction =
329
+ let set_status other_entries entries match_ =
330
+ let before, after =
331
+ match direction with
332
+ | Backward -> (other_entries, entries)
333
+ | Forward -> (entries, other_entries)
334
+ in
335
+ search_status < - Some { before; after; match_ }
336
+ in
337
+ let input = Zed_rope. to_string (Zed_edit. text edit) in
338
+ let rec loop other_entries entries =
339
+ match entries with
340
+ | [] ->
341
+ set_status other_entries entries None ;
316
342
set_message (Some (LTerm_text. of_string " Reverse search: not found" ))
317
- | entry :: rest ->
343
+ | entry :: rest ->
318
344
match search_string entry input with
319
- | Some pos -> begin
320
- match search_result with
321
- | Some (entry' , _ , _ ) when entry = entry' ->
322
- loop rest
323
- | _ ->
324
- search_result < - Some (entry, pos, rest);
325
- let txt = LTerm_text. of_string entry in
326
- for i = pos to pos + Zed_rope. length (Zed_edit. text edit) - 1 do
327
- let ch, style = txt.(i) in
328
- txt.(i) < - (ch, { style with underline = Some true })
329
- done ;
330
- set_message (Some (Array. append (LTerm_text. of_string " Reverse search: " ) txt))
331
- end
332
- | None ->
333
- loop rest
345
+ | Some pos -> begin
346
+ match search_status with
347
+ | Some { match_ = Some (entry' , _ ) } when entry = entry' ->
348
+ loop (entry :: other_entries) rest
349
+ | _ ->
350
+ set_status other_entries rest (Some (entry, pos));
351
+ let txt = LTerm_text. of_string entry in
352
+ for i = pos to pos + Zed_rope. length (Zed_edit. text edit) - 1 do
353
+ let ch, style = txt.(i) in
354
+ txt.(i) < - (ch, { style with underline = Some true })
355
+ done ;
356
+ set_message
357
+ (Some (Array. append (LTerm_text. of_string " Reverse search: " ) txt))
358
+ end
359
+ | None ->
360
+ loop (entry :: other_entries) rest
361
+ in
362
+ match search_status with
363
+ | None ->
364
+ let hist = fst (S. value history) in
365
+ loop []
366
+ (match direction with
367
+ | Backward -> hist
368
+ | Forward -> List. rev hist)
369
+ | Some { before; after; match_ } ->
370
+ let other_entries, entries =
371
+ match direction with
372
+ | Backward -> (before, after)
373
+ | Forward -> (after, before)
374
+ in
375
+ let other_entries =
376
+ match match_ with
377
+ | None -> other_entries
378
+ | Some (entry , _ ) -> entry :: other_entries
379
+ in
380
+ loop other_entries entries
334
381
in
335
- match search_result with
336
- | Some (entry , pos , rest ) -> loop rest
337
- | None -> loop (fst (S. value history))
382
+ match S. value mode with
383
+ | Search -> do_search direction
384
+ | Edition ->
385
+ let text = Zed_edit. text edit in
386
+ Zed_edit. goto context 0 ;
387
+ Zed_edit. remove context (Zed_rope. length text);
388
+ let prev, next = S. value history in
389
+ set_history (Zed_rope. to_string text :: (List. rev_append next prev), [] );
390
+ search_status < - None ;
391
+ set_mode Search ;
392
+ do_search direction
393
+ | _ ->
394
+ ()
338
395
339
396
method insert ch =
340
397
Zed_edit. insert context (Zed_rope. singleton ch)
@@ -345,13 +402,13 @@ object(self)
345
402
| (Complete | Complete_bar | Accept ) when S. value mode = Search -> begin
346
403
set_mode Edition ;
347
404
set_message None ;
348
- match search_result with
349
- | Some (entry , pos , rest ) ->
350
- search_result < - None ;
405
+ match search_status with
406
+ | Some { match_ = Some (entry , pos ) } ->
407
+ search_status < - None ;
351
408
Zed_edit. goto context 0 ;
352
409
Zed_edit. remove context (Zed_rope. length (Zed_edit. text edit));
353
410
Zed_edit. insert context (Zed_rope. of_string entry)
354
- | None ->
411
+ | Some { match_ = None } | None ->
355
412
()
356
413
end
357
414
@@ -422,22 +479,8 @@ object(self)
422
479
Zed_edit. insert context (Zed_rope. of_string line)
423
480
end
424
481
425
- | Prev_search -> begin
426
- match S. value mode with
427
- | Search ->
428
- self#search
429
- | Edition ->
430
- let text = Zed_edit. text edit in
431
- Zed_edit. goto context 0 ;
432
- Zed_edit. remove context (Zed_rope. length text);
433
- let prev, next = S. value history in
434
- set_history (Zed_rope. to_string text :: (List. rev_append next prev), [] );
435
- search_result < - None ;
436
- set_mode Search ;
437
- self#search
438
- | _ ->
439
- ()
440
- end
482
+ | Prev_search -> self#search Backward
483
+ | Next_search -> self#search Forward
441
484
442
485
| Cancel_search ->
443
486
if S. value mode = Search then begin
@@ -580,7 +623,7 @@ class read_password () = object(self)
580
623
method show_box = false
581
624
582
625
method send_action = function
583
- | Prev_search -> ()
626
+ | Prev_search | Next_search -> ()
584
627
| action -> super#send_action action
585
628
end
586
629
0 commit comments