From: NeilBrown Date: Wed, 1 Nov 2023 06:23:07 +0000 (+1100) Subject: lib-search: add "reverse" command for search commands. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=80b413452e180644cb6ed3969073e4e2ce442db3;p=edlib.git lib-search: add "reverse" command for search commands. A "search" command created with make-search now responds to "reverse" and does a backwards search. Signed-off-by: NeilBrown --- diff --git a/DOC/TODO.md b/DOC/TODO.md index 8eb73263..77ec877c 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -34,18 +34,7 @@ the file. - [X] search hangs when seeking "^( *)" - [X] selection-menu item to show git-commit from list of known git trees -- [ ] selection-menu item for word-count - [X] selection-menu item for QR-code -- [ ] selection-menu sub-menu for UPPER lower Caps ?? -- [ ] selection-menu item for text-fill -- [ ] selection-menu item for spell-check ?? -- [ ] selection-menu item to view a named file - in Popup:Tile -- [ ] notmuch - capture errors about multiple Subject lines and - display them better. -- [ ] switch display-pygtk to use Draw:scale-image -- [ ] when add address to a query in notmuch, reassess the current - query. Maybe don't reload, but make sure that next reload will - used updated query. - [X] get too many Failed:: C-N C-P Up C-R history:C-R A-! Maybe these should return Efalse, not Efail @@ -64,7 +53,7 @@ the file. - [X] split range management out of autospell so it can be used by other modules. -- [ ] make it easy for a make-search command to search backwards +- [X] make it easy for a make-search command to search backwards - [X] Make a start on CUA mode with mouse/menu/selection support. Also Function keys: help, close, refresh @@ -265,6 +254,7 @@ Module features ### lib-linecount +- [ ] selection-menu item for word-count - [ ] handle view:changed properly, and make sure total count changes appropriately in notmuch-query-view. Maybe view:changed should report if content changed, or just attributes. @@ -295,6 +285,7 @@ Module features - [X] auto-wrap on a line like this one doesn't recognize all the punctuation a the start of the line ... should it? - [X] fill mode to handle all punctuation at start of this line +- [ ] selection-menu item for text-fill ### render-format @@ -303,7 +294,7 @@ Module features ### lib-search -- [ ] make it easy for a make-search command to search backwards +- [X] make it easy for a make-search command to search backwards ### autosave @@ -356,6 +347,8 @@ Module features ### emacs +- [ ] selection-menu sub-menu for UPPER lower Caps ?? +- [ ] selection-menu item to view a named file - in Popup:Tile - [ ] multi-file search-replace. Find files with 'git-grep' - [ ] there is no way to count characters in a range, or find how many characters into the document I am. Maybe bytes would be good too. @@ -400,6 +393,7 @@ Module features ### pygtk +- [ ] switch display-pygtk to use Draw:scale-image - [ ] can we capture the substates of character composition, and give feed-back? @@ -629,6 +623,9 @@ Module features ### Notmuch message display +- [ ] when add address to a query in notmuch, reassess the current + query. Maybe don't reload, but make sure that next reload will + used updated query. - [X] notmuch addresses in From: list to have menu to add address to any from-* query - [ ] "%d quoted lines" still not quite right. Moving 'down' past it @@ -683,6 +680,8 @@ Module features ### Notmuch composition +- [ ] notmuch - capture errors about multiple Subject lines and + display them better. - [ ] when aborting email composition, unlink the file if it is empty. Probably dispose of autosave too. - [ ] should I look for Delivered-to headers. Even; @@ -849,6 +848,7 @@ Module features - [ ] Some way for 'c-mode' to report where comments are so they can be spell-checked - [ ] drop-down with options - [ ] unify UI with dynamic-completion +- [ ] selection-menu item for spell-check ?? - [X] split range management out of autospell so it can be used by other modules. diff --git a/lib-search.c b/lib-search.c index feda3da8..81ab9f0d 100644 --- a/lib-search.c +++ b/lib-search.c @@ -217,6 +217,101 @@ DEF_CB(search_test) free(ret); return 1; } + if (strcmp(ci->key, "reverse") == 0) { + /* Search backward from @mark in @focus for a match, or + * until we hit @mark2. Leave @mark at the start of the + * match. Return length of match, or negative. + * + * rexel only lets us search forwards, and stepping back one + * char at a time to match the pattern is too slow. + * So we step back a steadily growing number of chars and search + * forward as far as the previous location. Once we + * find any match, we check if there is a later one that + * still satisfies. + */ + int step_size = 65536; + int maxlen; + int ret = -1; + struct mark *m, *start, *end, *endmark; + struct mark *m2 = ci->mark2; + struct pane *p = ci->focus; + + if (!ci->mark) + return Enoarg; + m = mark_dup(ci->mark); /* search cursor */ + start = mark_dup(ci->mark); /* start of the range being searched */ + end = mark_dup(ci->mark); /* end of the range being searched */ + + ss->end = end; + if (!ss->endmark) + ss->endmark = ci->mark; + endmark = ss->endmark; + ss->anchor_at_end = True; + + pane_set_time(p); + + while (!m2 || m2->seq < start->seq) { + mark_to_mark(end, start); + call("doc:char", p, -step_size, start, NULL, 0, m2); + if (mark_same(start, end)) + /* We have hit the start(m2), don't continue */ + break; + step_size *= 2; + ss->prev_ch = doc_prior(p, start); + ss->st = rxl_prepare(ss->rxl, 0); + ss->prev_point = ss->point ? mark_same(ss->point, m) : False; + + mark_to_mark(m, start); + call_comm("doc:content", p, &ss->c, 0, m); + rxl_info(ss->st, &maxlen, NULL, NULL, NULL); + rxl_free_state(ss->st); + if (maxlen >= 0) { + /* found a match */ + ret = maxlen; + break; + } + + if (pane_too_long(p, 2000)) { + /* FIXME returning success is wrong if + * we timed out But I want to move the + * point, and this is easiest. What do + * I really want here? Do I just need + * to make reverse search faster? + */ + mark_to_mark(endmark, start); + ret = 0; + break; + } + } + while (maxlen >= 0) { + /* There is a match starting at 'endmark'. + * The might be a later match - check for it. + */ + call("doc:char", p, -maxlen, ss->endmark); + if (mark_ordered_not_same(end, ss->endmark)) + break; + ret = maxlen; + if (endmark != ss->endmark && + mark_ordered_or_same(ss->endmark, endmark)) + /* Didn't move forward!! Presumably + * buggy doc:step implementation. + */ + break; + + mark_to_mark(endmark, ss->endmark); + ss->endmark = m; + mark_to_mark(start, endmark); + ss->prev_ch = doc_next(p, start); + ss->st = rxl_prepare(ss->rxl, 0); + call_comm("doc:content", p, &ss->c, 0, start); + rxl_info(ss->st, &maxlen, NULL, NULL, NULL); + rxl_free_state(ss->st); + } + mark_free(start); + mark_free(end); + mark_free(m); + return ret; + } return Efail; } @@ -265,85 +360,24 @@ static int search_backward(struct pane *p safe, * rexel only lets us search forwards, and stepping back * one char at a time to match the pattern is too slow. * So we step back a steadily growing number of - * chars, and search forward as far as the previous location. + * chars, and search forward as pfar as the previous location. * Once we find any match, we check if there is a later one * that still satisfies. */ struct search_state ss; - int step_size = 65536; - int maxlen; - int ret = -1; - struct mark *start = mark_dup(m); /* Start of the range to search */ - struct mark *end = mark_dup(m); - ss.end = end; + if (m2 && m->seq <= m2->seq) + return -1; + + ss.rxl = rxl; + ss.st = rxl_prepare(rxl, 0); + ss.prefix_len = rxl_prefix(rxl, ss.prefix, sizeof(ss.prefix)); + ss.end = m2; ss.endmark = endmark; ss.point = point; + ss.prev_point = point ? mark_same(point, m) : False; ss.c = search_test; - ss.prefix_len = rxl_prefix(rxl, ss.prefix, sizeof(ss.prefix)); - ss.anchor_at_end = True; - - pane_set_time(p); - - while (!m2 || m2->seq < start->seq) { - mark_to_mark(end, start); - call("doc:char", p, -step_size, start, NULL, 0, m2); - if (mark_same(start, end)) - /* We have hit the start, don't continue */ - break; - step_size *= 2; - ss.prev_ch = doc_prior(p, start); - ss.st = rxl_prepare(rxl, 0); - ss.prev_point = point ? mark_same(point, m) : False; - - mark_to_mark(m, start); - call_comm("doc:content", p, &ss.c, 0, m); - rxl_info(ss.st, &maxlen, NULL, NULL, NULL); - rxl_free_state(ss.st); - if (maxlen >= 0) { - /* found a match */ - ret = maxlen; - break; - } - - if (pane_too_long(p, 2000)) { - /* FIXME returning success is wrong if we timed out - * But I want to move the point, and this is easiest. - * What do I really want here? - * Do I just need to make reverse search faster? - */ - mark_to_mark(endmark, start); - ret = 0; - break; - } - } - while (maxlen >= 0) { - /* There is a match starting at 'endmark'. - * The might be a later match - check for it. - */ - call("doc:char", p, -maxlen, ss.endmark); - if (mark_ordered_not_same(end, ss.endmark)) - break; - ret = maxlen; - if (endmark != ss.endmark && - mark_ordered_or_same(ss.endmark, endmark)) - /* Didn't move forward!! Presumably - * buggy doc:step implementation. - */ - break; - - mark_to_mark(endmark, ss.endmark); - ss.endmark = m; - mark_to_mark(start, endmark); - ss.prev_ch = doc_next(p, start); - ss.st = rxl_prepare(rxl, 0); - call_comm("doc:content", p, &ss.c, 0, start); - rxl_info(ss.st, &maxlen, NULL, NULL, NULL); - rxl_free_state(ss.st); - } - mark_free(start); - mark_free(end); - return ret; + return comm_call(&ss.c, "reverse", p, 0, m, NULL, 0, m2); } DEF_CMD(text_search)