From: NeilBrown Date: Sun, 2 Jul 2023 11:42:57 +0000 (+1000) Subject: linecount: allow attachment to a viewer stack. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=4de7a969302f5bd9d79a5bdd0f11ec73344543c8;p=edlib.git linecount: allow attachment to a viewer stack. Sometimes we want linecount to count lines in a view, not in a document. Instead of having the view pretend to be a document for linecount, change it to explicitly support attachment. Signed-off-by: NeilBrown --- diff --git a/DOC/TODO.md b/DOC/TODO.md index 7603af24..cb0572d7 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -27,7 +27,7 @@ the file. off top of screen - [X] emacs: :C-q to recognise names of Unicode chars: e.g. WASTEBASKET Possibly matches a list which continued :C-q cycles through -- [ ] linecount 'view' mode improvements +- [X] linecount 'view' mode improvements - [ ] allocate pane->data together with pane. A single allocation so that we can avoid the cost of a dereference. @@ -35,7 +35,7 @@ the file. - [X] lib-mergeview improvements - [X] lib-diff slowness with large diff -- [ ] linecount :when used in 'view' mode, stack the counting pane with all the +- [X] linecount :when used in 'view' mode, stack the counting pane with all the others so it can easily catch view-changed. - [X] C config module that reads an ini-style file to set attributes based on path @@ -73,6 +73,10 @@ Requirements for a v1.0 release Core features ------------- +- [ ] Add optional unit-test interface for modules. This should be + implemented at least by lib-search, doc-text and probably + many others. It is particularly for things that are awkward + to test with the ncurses/replay test approach. - [ ] Send global notify before/after refresh. LOG must suspend logging (or notifications at least) during refresh if is visible anywhere @@ -172,10 +176,13 @@ Module features ### lib-linecount -- [ ] when used in 'view' mode, stack the counting pane with all the +- [X] when used in 'view' mode, stack the counting pane with all the others so it can easily catch view-changed. In general, make it easier to use this way. e.g. easier than catching doc:request:doc:CountLines. +- [ ] 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. ### lib-server @@ -194,6 +201,9 @@ Module features ### lib-textfill +- [ ] auto-wrap on a line like this one doesn't recognize all the + punctuation a the start of the line ... should it? + ### render-format @@ -277,6 +287,8 @@ Module features ### emacs +- [ ] 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. - [X] Num-C-l doesn't work if it would require part of a wrapped line off top of screen - [X] :C-q to recognise names of Unicode chars: e.g. WASTEBASKET diff --git a/doc-email.c b/doc-email.c index 30b9abed..14ce1106 100644 --- a/doc-email.c +++ b/doc-email.c @@ -1171,8 +1171,6 @@ DEF_CMD(email_view_set_attr) 1, m2); call("view:changed", ci->focus, 0, m1, NULL, 0, m2); call("Notify:clip", ci->focus, 0, m1, NULL, 0, m2); - /* for CountLines */ - pane_notify("doc:replaced", ci->home, 0, m1, NULL, 0, m2); mark_free(m1); mark_free(m2); @@ -1192,45 +1190,9 @@ DEF_CMD(email_view_set_attr) return Efallthrough; } -DEF_CMD(email_request) -{ - /* Someone wants to count lines - attach the line count here, - * rather than let it fall through to document. - */ - pane_add_notify(ci->focus, ci->home, ksuffix(ci, "doc:request:")); - if (strcmp(ci->key, "doc:request:doc:replaced") == 0) - /* Though for doc:replaced, we want the doc to respond too */ - return Efallthrough; - return 1; -} - -DEF_CMD(email_countlines) -{ - return pane_notify(ksuffix(ci, "doc:notify:"), - ci->home, - ci->num, ci->mark, ci->str, - ci->num2, ci->mark2, ci->str2, ci->comm2); -} - -DEF_CMD(status_changed) -{ - /* line-count sent a status-change message */ - call("doc:status-changed", ci->focus->parent); - return 1; -} - -DEF_CMD(doc_replaced) -{ - /* doc sent a doc;replaced message */ - pane_notify("doc:replaced", ci->home, - ci->num, ci->mark, ci->str, - ci->num2, ci->mark2, ci->str2, ci->comm2); - return 1; -} - DEF_CMD(attach_email_view) { - struct pane *p; + struct pane *p, *p2; struct email_view *evi; struct mark *m; int n, i; @@ -1263,9 +1225,9 @@ DEF_CMD(attach_email_view) free(evi); return Efail; } - /* get doc:replaced from the email so I can pass it to linecount */ - home_call(ci->focus, "doc:request:doc:replaced", p); - pane_add_notify(p, p, "doc:status-changed"); + p2 = call_ret(pane, "attach-line-count", p); + if (p2) + p = p2; attr_set_str(&p->attrs, "render-hide-CR", "yes"); return comm_call(ci->comm2, "callback:attach", p); } @@ -1287,11 +1249,6 @@ static void email_init_map(void) key_add(email_view_map, "email:select:hide", &email_select_hide); key_add(email_view_map, "email:select:full", &email_select_full); key_add(email_view_map, "email:select:extras", &email_select_extras); - key_add(email_view_map, "doc:request:doc:CountLines", &email_request); - key_add(email_view_map, "doc:request:doc:replaced", &email_request); - key_add(email_view_map, "doc:notify:doc:CountLines", &email_countlines); - key_add(email_view_map, "doc:status-changed", &status_changed); - key_add(email_view_map, "doc:replaced", &doc_replaced); } void edlib_init(struct pane *ed safe) diff --git a/lib-linecount.c b/lib-linecount.c index 07a7f9e7..99135268 100644 --- a/lib-linecount.c +++ b/lib-linecount.c @@ -25,6 +25,13 @@ * When it is called on a mark in the pane, attributes are set on the * mark to indicate the line, work and char where the mark is. * These are always at least 1. + * + * Alternately, the pane can be attaching in the view stack so that it + * applies to the view rather than the document. This is useful when + * There are views imposed that dramatically alter the number of + * lines/words, or that hide parts of the document that really shouldn't + * be counted. The view on an RFC2822 email or the results of a notmuch + * search are good and current examples. */ #include @@ -160,7 +167,7 @@ static void do_count(struct pane *p safe, struct pane *owner safe, DEF_CMD(linecount_restart) { - pane_call(ci->home, "doc:CountLines", ci->focus, 1); + pane_call(ci->home, "CountLinesAsync", pane_leaf(ci->focus), 1); return Efalse; } @@ -308,6 +315,10 @@ DEF_CMD(linecount_notify_replace) struct count_info *cli = ci->home->data; struct mark *m, *m2; + if (ci->mark && !ci->mark2) + /* I might not care about this one... */ + return Efallthrough; + attr_del(&d->attrs, "lines"); attr_del(&d->attrs, "words"); attr_del(&d->attrs, "chars"); @@ -317,7 +328,7 @@ DEF_CMD(linecount_notify_replace) else m = vmark_first(d, cli->view_num, ci->home); if (!m) - return 1; + return Efallthrough; attr_del(mark_attr(m), "lines"); attr_del(mark_attr(m), "words"); @@ -328,17 +339,39 @@ DEF_CMD(linecount_notify_replace) mark_free(m2); call_comm("event:free", ci->home, &linecount_restart); - return 1; + return Efallthrough; } DEF_CMD(linecount_notify_count) { struct pane *d = ci->focus; struct count_info *cli = ci->home->data; - /* Option mark is "mark2" as "mark" gets the "point" */ + /* Option mark is "mark2" as "mark" gets the "point" so is never NULL */ /* num==1 means we don't want to wait for precision */ + bool sync = ci->mark2 && ci->num != 1; + + if (strcmp(ci->key, "CountLinesAsync") == 0) + sync = False; count_calculate(d, ci->mark2, ci->home, cli->view_num, - ci->mark2 && ci->num != 1); + sync); + return 1; +} + +DEF_CMD(linecount_view_count) +{ + struct pane *d = ci->focus; + struct count_info *cli = ci->home->data; + bool sync = strcmp(ci->key, "CountLines") == 0; + + if (strcmp(ci->key, "CountLinesAsync") == 0) + sync = False; + + if (ci->mark && ci->str && strcmp(ci->str, "goto:line") == 0 && + ci->num != NO_NUMERIC) { + pane_call(ci->home, "doc:GotoLine", d, ci->num, ci->mark); + } + count_calculate(d, ci->mark, ci->home, cli->view_num, + sync); return 1; } @@ -416,10 +449,40 @@ DEF_CMD(count_lines) return 1; } +DEF_CMD(linecount_attach) +{ + struct count_info *cli; + struct pane *p; + + alloc(cli, pane); + p = pane_register(ci->focus, 0, + &handle_count_lines.c, cli); + if (!p) + return Efail; + cli->view_num = home_call(p, "doc:add-view", p) - 1; + call("doc:request:doc:replaced", p); + call("doc:request:Notify:Close", p); + call_comm("event:on-idle", p, &linecount_restart, 1); + + comm_call(ci->comm2, "cb", p); + return 1; +} + +DEF_CMD(linecount_clone) +{ + struct pane *p; + + p = comm_call_ret(pane, &linecount_attach, "attach", ci->focus); + pane_clone_children(ci->home, p); + return 1; +} + void edlib_init(struct pane *ed safe) { call_comm("global-set-command", ed, &count_lines, 0, NULL, "CountLines"); call_comm("global-set-command", ed, &count_lines, 0, NULL, "CountLinesAsync"); + call_comm("global-set-command", ed, &linecount_attach, 0, NULL, + "attach-line-count"); if (linecount_map) return; @@ -430,4 +493,10 @@ void edlib_init(struct pane *ed safe) key_add(linecount_map, "doc:CountLines", &linecount_notify_count); key_add(linecount_map, "doc:GotoLine", &linecount_notify_goto); key_add(linecount_map, "Free", &edlib_do_free); + + /* For view-attached version */ + key_add(linecount_map, "CountLines", &linecount_view_count); + key_add(linecount_map, "CountLinesAsync", &linecount_view_count); + //key_add(linecount_map, "view:changed", &linecount_notify_replace); + key_add(linecount_map, "Clone", &linecount_clone); } diff --git a/modules.ini b/modules.ini index bf41b517..eee2beca 100644 --- a/modules.ini +++ b/modules.ini @@ -8,6 +8,7 @@ lib-popup = PopupTile lib-linecount = Countlines CountLinesAsync + attach-line-count lib-search = text-search text-match diff --git a/render-format.c b/render-format.c index f445844d..c2028af9 100644 --- a/render-format.c +++ b/render-format.c @@ -38,7 +38,6 @@ struct rf_data { char *attr_cache; void *cache_pos; int cache_field; - bool no_linecount; }; static inline short FIELD_NUM(int i) { return i >> 16; } @@ -930,46 +929,6 @@ DEF_CMD(render_line_prev2) return 1; } -DEF_CMD(format_request) -{ - /* Someone wants to count lines - attach the line count here, - * rather than let it fall through to document. - */ - struct rf_data *rd = ci->home->data; - - if (rd->no_linecount) - return Efallthrough; - pane_add_notify(ci->focus, ci->home, ksuffix(ci, "doc:request:")); - if (strcmp(ci->key, "doc:request:doc:replaced") == 0) - /* Though for doc:replaced, we want the doc to respond too */ - return Efallthrough; - return 1; -} - -DEF_CMD(format_countlines) -{ - return pane_notify(ksuffix(ci, "doc:notify:"), - ci->home, - ci->num, ci->mark, ci->str, - ci->num2, ci->mark2, ci->str2, ci->comm2); -} - -DEF_CMD(status_changed) -{ - /* line-count sent a status-change message */ - call("doc:status-changed", ci->focus->parent); - return 1; -} - -DEF_CMD(doc_replaced) -{ - /* doc sent a doc;replaced message */ - pane_notify("doc:replaced", ci->home, - ci->num, ci->mark, ci->str, - ci->num2, ci->mark2, ci->str2, ci->comm2); - return 1; -} - static struct pane *do_render_format_attach(struct pane *parent safe); DEF_CMD(format_clone) { @@ -1006,11 +965,6 @@ static void render_format_register_map(void) key_add(rf2_map, "doc:content", &format_content2); key_add(rf2_map, "Free", &format_free); key_add(rf2_map, "doc:shares-ref", &format_noshare_ref); - key_add(rf2_map, "doc:request:doc:CountLines", &format_request); - key_add(rf2_map, "doc:request:doc:replaced", &format_request); - key_add(rf2_map, "doc:notify:doc:CountLines", &format_countlines); - key_add(rf2_map, "doc:status-changed", &status_changed); - key_add(rf2_map, "doc:replaced", &doc_replaced); } DEF_LOOKUP_CMD(render_format_handle, rf_map); @@ -1031,12 +985,11 @@ static struct pane *do_render_format_attach(struct pane *parent safe) render_format_register_map(); alloc(rf, pane); - if (pane_attr_get(parent, "format:no-linecount")) - rf->no_linecount = True; p = pane_register(parent, 0, &render_format2_handle.c, rf); - if (p) { - pane_add_notify(p, p, "doc:status-changed"); - home_call(parent, "doc:request:doc:replaced", p); + if (p && !pane_attr_get(parent, "format:no-linecount")) { + struct pane *p2 = call_ret(pane, "attach-line-count", p); + if (p2) + p = p2; } } if (!p)