]> git.neil.brown.name Git - edlib.git/commitdiff
Add lib-rangetrack
authorNeilBrown <neil@brown.name>
Tue, 19 Sep 2023 10:49:52 +0000 (20:49 +1000)
committerNeilBrown <neil@brown.name>
Tue, 19 Sep 2023 11:55:45 +0000 (21:55 +1000)
Split the rangetracking code out of lib-autospell.py and place it
in lib-rangetrack.c.  This will allow it to be used by other modules
that want on-demand parsing of content.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/TODO.md
Makefile
data/modules.ini
lib-rangetrack.c [new file with mode: 0644]
python/lib-autospell.py

index fd5352933da907059276838ebbcc41d8a19d0b58..5fd1d6819c3fa62759be8d45058fb426faf6c8ce 100644 (file)
@@ -9,6 +9,9 @@ the file.
 
 ### Triage
 
+- [ ] unknown keysequence should be reported so e.g. if keyboard
+      is is Greek mode, then I will be told that Cx-b doesn't work
+- [ ] menubar doesn't redraw background when resized wider.
 - [X] open second x11 window, use selections.  Close it.  command
       in x11selection_Xcb gets freed???
 - [X] adding new lines at end of doc in x11 leaves phantom underline
@@ -45,7 +48,7 @@ the file.
 
 ### Medium
 
-- [ ] split range management out of autospell so it can be used by other
+- [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
 - [ ] Make a start on CUA mode with mouse/menu/selection support.
@@ -824,7 +827,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
-- [ ] split range management out of autospell so it can be used by other
+- [X] split range management out of autospell so it can be used by other
   modules.
 
 ### calculator
index 2b676f181323a9a1eb47a273495dd38d44a11cff..bda901a72a285b50424138eb07e793a50aefff7b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -87,7 +87,7 @@ SHOBJ = O/doc-text.o O/doc-dir.o O/doc-docs.o \
        O/lib-x11selection-xcb.o O/display-x11-xcb.o \
        O/lib-linefilter.o O/lib-wiggle.o O/lib-aspell.o O/lib-calc.o \
        O/lib-menu.o O/lib-unicode-names.o O/lib-askpass.o \
-       O/lib-test-markup.o O/lib-menubar.o \
+       O/lib-test-markup.o O/lib-menubar.o O/lib-rangetrack.o \
        O/lang-python.o \
        O/mode-emacs.o O/emacs-search.o \
        O/display-ncurses.o
index 6f7d73465c3b34dcd75ee7f4000851e9305c4e72..3cb81a369d27d941ed44a92cc5854b7b619e5b3c 100644 (file)
@@ -165,3 +165,5 @@ lib-test-markup =
 
 lib-menubar =
        attach-menubar
+
+lib-rangetrack = rangetrack:new
diff --git a/lib-rangetrack.c b/lib-rangetrack.c
new file mode 100644 (file)
index 0000000..bd47b00
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Copyright Neil Brown ©2023 <neil@brown.name>
+ * May be distributed under terms of GPLv2 - see file:COPYING
+ *
+ * rangetrack: track ranges of a document which have been processed
+ * in some why, such a spell-check or syntax-highlight or other
+ * parsing.
+ *
+ * rangetrack will attach a pane to the target document to store
+ * marks and other state.  It can track an arbitrary set of different
+ * range types.
+ *
+ * rangetrack:new : start tracking ranges on 'focus' document.
+ *             str is the name of the range set.
+ * rangetrack:add     : record that mark to mark2 are a valid range
+ * rangetrack:remove  : record that from mark to mark2 are no longer valid
+ * rangetrack:choose  : report a subrange for mark..mark2 which is not
+ *                     currently valid.
+ *
+ */
+
+#define PANE_DATA_TYPE struct rangetrack_data
+
+#include "core.h"
+struct rangetrack_data {
+       struct rci {
+               const char *set safe;
+               struct pane *owner safe;
+               int view;
+               struct rci *next;
+       } *info;
+};
+#include "core-pane.h"
+
+static struct rci *find_set(const struct cmd_info *ci safe)
+{
+       struct rangetrack_data *rtd = ci->home->data;
+       struct rci *i;
+
+       for (i = rtd->info; i; i = i->next) {
+               if (ci->str && strcmp(ci->str, i->set) == 0)
+                       return i;
+       }
+       return NULL;
+}
+
+static void add_set(struct pane *home safe, const char *set safe,
+                   struct pane *focus safe)
+{
+       struct rangetrack_data *rtd = home->data;
+       struct rci *i;
+
+       alloc(i, pane);
+       i->set = strdup(set);
+       i->owner = focus;
+       pane_add_notify(home, focus, "Notify:Close");
+       i->view = call("doc:add-view", home) - 1;
+       i->next = rtd->info;
+       rtd->info = i;
+}
+
+DEF_CMD(rangetrack_notify_close)
+{
+       struct rangetrack_data *rtd = ci->home->data;
+       struct rci **ip;
+
+       for (ip = &rtd->info; *ip; ip = &(*ip)->next) {
+               struct rci *i;
+               if ((*ip)->owner != ci->focus)
+                       continue;
+               i = *ip;
+               *ip = i->next;
+               free((void*)i->set);
+               unalloc(i, pane);
+               break;
+       }
+       return Efallthrough;
+}
+
+DEF_CMD_CLOSED(rangetrack_close)
+{
+       struct rangetrack_data *rtd = ci->home->data;
+       struct rci *i;
+
+       while ((i = rtd->info) != NULL) {
+               rtd->info = i->next;
+               free((void*)i->set);
+               unalloc(i, pane);
+       }
+       return 1;
+}
+
+DEF_CMD(rangetrack_new)
+{
+       struct rci *i = find_set(ci);
+
+       if (!ci->str)
+               return Enoarg;
+       if (i)
+               return Efalse;
+       add_set(ci->home, ci->str, ci->focus);
+       return 1;
+}
+
+DEF_CMD(rangetrack_add)
+{
+       struct rci *i = find_set(ci);
+       struct mark *start = ci->mark;
+       struct mark *end = ci->mark2;
+       struct mark *m, *m1, *m2;
+
+       if (!i)
+               return Efail;
+       if (!start || !end)
+               /* Testing if configured already */
+               return 1;
+       m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
+       if (m1 && attr_find(m1->attrs, "start"))
+               m1 = vmark_next(m1);
+       else if (m1 && mark_same(m1, start))
+               /* m1 is an end-of-range. Can move m1 down to cover range */
+               ;
+       else
+               /* Must create a new mark, or move a later mark up */
+               m1 = NULL;
+       m2 = vmark_at_or_before(ci->home, end, i->view, ci->home);
+       if (m2 && attr_find(m2->attrs, "start") == NULL) {
+               if (mark_same(m2, end))
+                       /* Can move the start of this range earlier */
+                       m2 = vmark_prev(m2);
+               else
+                       /* end not in rnage, must create mark or move
+                        * earlier mark down
+                        */
+                       m2 = NULL;
+       }
+       /* If m2, then move it backwards - no need to create */
+       if (!m1 && !m2) {
+               /* no overlaps, create a new region */
+               m1 = vmark_new(ci->home, i->view, ci->home);
+               if (!m1)
+                       return Efail;
+               mark_to_mark(m1, start);
+               m2 = vmark_new(ci->home, i->view, ci->home);
+               if (!m2)
+                       return Efail;
+               mark_to_mark(m2, end);
+               attr_set_str(&m1->attrs, "start", "yes");
+       } else if (m1 && !m2) {
+               /* Can move m1 dow n to end, removing anything in the way */
+               m = vmark_next(m1);
+               while (m && mark_ordered_or_same(m, end)) {
+                       mark_free(m);
+                       m = vmark_next(m1);
+               }
+               mark_to_mark(m1, end);
+       } else if (!m1 && m2) {
+               /* Can move m2 up to start, removing things */
+               m = vmark_prev(m2);
+               while (m && mark_ordered_or_same(start, m)) {
+                       mark_free(m);
+                       m = vmark_prev(m2);
+               }
+               mark_to_mark(m2, start);
+       } else if (m1 && m2) {
+               /* Can remove all from m1 to m2 inclusive */
+               while (m1 && mark_ordered_not_same(m1, m2)) {
+                       m = vmark_next(m1);
+                       mark_free(m1);
+                       m1 = m;
+               }
+               mark_free(m2);
+       }
+       return 1;
+}
+
+DEF_CMD(rangetrack_clear)
+{
+       struct rci *i = find_set(ci);
+       struct mark *start = ci->mark;
+       struct mark *end = ci->mark2;
+       struct mark *m1, *m2;
+
+       if (!i)
+               return Efail;
+       if (!start || !end) {
+               start = vmark_first(ci->home, i->view, ci->home);
+               end = vmark_last(ci->home, i->view, ci->home);
+       }
+       if (!start || !end)
+               return 1;
+
+       m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
+
+       if (!m1 || attr_find(m1->attrs, "start") == NULL) {
+               /* Immediately after start is not active, so the
+                * earlierst we might need to remove is the next
+                * mark, or possibly the very first mark.
+                */
+               if (m1)
+                       m1 = vmark_next(m1);
+               else
+                       m1 = vmark_first(ci->home, i->view, ci->home);
+               if (!m1 || mark_ordered_or_same(end, m1))
+                       /* Nothing to remove */
+                       return 1;
+       } else {
+               /* From m1 to start are in a range and should stay
+                * there.  Split the range from 'm1' at 'start'
+                */
+               m1 = vmark_new(ci->home, i->view, ci->home);
+               if (!m1)
+                       return Efail;
+               mark_to_mark(m1, start);
+               m1 = mark_dup_view(m1);
+               /* Ensure this m1 is after the previous one */
+               mark_step(m1, 1);
+               attr_set_str(&m1->attrs, "start", "yes");
+       }
+       /* m is now the start of an active section that is within
+        * start-end and should be removed */
+       m2 = vmark_at_or_before(ci->home, end, i->view, ci->home);
+       if (m2 && mark_same(m2, end) && attr_find(m2->attrs, "start"))
+               /* This section is entirely after end, so not interesting */
+               m2 = vmark_prev(m2);
+       if (m2 && attr_find(m2->attrs, "start")) {
+               /* end is within an active secion that needs to be split */
+               m2 = vmark_new(ci->home, i->view, ci->home);
+               if (!m2)
+                       return Efail;
+               mark_to_mark(m2, end);
+               attr_set_str(&m2->attrs, "start", "yes");
+               m2 = mark_dup_view(m2);
+               mark_step(m2, 0);
+       }
+       if (!m2)
+               return Efail;
+       /* m2 is now the end of an active section that needs to bie discarded */
+       while (m1 && mark_ordered_not_same(m1, m2)) {
+               struct mark *m = m1;
+               m1 = vmark_next(m1);
+               mark_free(m);
+       }
+       mark_free(m2);
+       call(strconcat(ci->home, "doc:notify:rangetrack:recheck-", i->set),
+            ci->home);
+       return 1;
+}
+
+DEF_CMD(rangetrack_choose)
+{
+       struct rci *i = find_set(ci);
+       struct mark *start = ci->mark;
+       struct mark *end = ci->mark2;
+       struct mark *m1, *m2;
+
+       if (!i)
+               return Efail;
+       if (!start || !end)
+               return Enoarg;
+       /* Contract start-end so that none of it is in-range */
+       m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
+       if (m1 && attr_find(m1->attrs, "start") == NULL)
+               /* Start is not in-range, end must not exceed m1 */
+               m2 = vmark_next(m1);
+       else if (m1) {
+               /* m1 is in-range, move it forward */
+               m1 = vmark_next(m1);
+               if (m1) {
+                       mark_to_mark(start, m1);
+                       m2 = vmark_next(m1);
+               } else {
+                       /* Should be impossible */
+                       m2 = start;
+               }
+       } else {
+               /* Start is before all ranges */
+               m2 = vmark_first(ci->home, i->view, ci->home);
+       }
+       if (m2 && mark_ordered_not_same(m2, end))
+               mark_to_mark(end, m2);
+       return 1;
+}
+
+static struct map *rangetrack_map safe;
+DEF_LOOKUP_CMD(rangetrack_handle, rangetrack_map);
+
+DEF_CMD(rangetrack_attach)
+{
+       struct pane *doc = call_ret(pane, "doc:get-doc", ci->focus);
+       const char *set = ci->str;
+       struct pane *p;
+
+       if (!set)
+               return Enoarg;
+       if (!doc)
+               return Efail;
+       if (call("doc:notify:rangetrack:new", ci->focus, 0, NULL, set) > 0)
+               return 1;
+       p = pane_register(doc, 0, &rangetrack_handle.c);
+       if (!p)
+               return Efail;
+       pane_add_notify(p, doc, "rangetrack:new");
+       pane_add_notify(p, doc, "rangetrack:add");
+       pane_add_notify(p, doc, "rangetrack:clear");
+       pane_add_notify(p, doc, "rangetrack:choose");
+       add_set(p, set, ci->focus);
+       return 1;
+}
+
+void edlib_init(struct pane *ed safe)
+{
+       call_comm("global-set-command", ed, &rangetrack_attach,
+                 0, NULL, "rangetrack:new");
+       rangetrack_map = key_alloc();
+       key_add(rangetrack_map, "Close", &rangetrack_close);
+       key_add(rangetrack_map, "Notify:Close", &rangetrack_notify_close);
+       key_add(rangetrack_map, "rangetrack:new", &rangetrack_new);
+       key_add(rangetrack_map, "rangetrack:add", &rangetrack_add);
+       key_add(rangetrack_map, "rangetrack:clear", &rangetrack_clear);
+       key_add(rangetrack_map, "rangetrack:choose", &rangetrack_choose);
+}
index f68d4d36bd645c42e7dec3e8bacc3cf01827e47b..79c6c3d8fe0e5511fa9f31b02c9e6d3d01085299 100644 (file)
 # We add ranges when a word is checked.  This might merge with existing
 # ranges.
 # We need to find an unchecked section in some range to start checking.
-# This is all managed by:
-#   remove_range(focus, viewnum, attr, start, end)
-#   add_range(focus, viewnum, attr, start, end)
-#   choose_range(focus, viewnum, attr, start, end) - changes start and end to be
-#    a contiguous unchecked section in the range
+# This is all managed with the help of rangetrack
 
 import edlib
 import os
 
-def show_range(action, focus, viewnum, attr):
-    edlib.LOG("range:", attr, action)
-    f,l = focus.vmarks(viewnum)
-    while f:
-        edlib.LOG("  ", f, f[attr])
-        f = f.next()
-    edlib.LOG("done", action)
-
-def remove_range(focus, viewnum, attr, start, end):
-    m = focus.vmark_at_or_before(viewnum, start)
-    if not m or not m[attr]:
-        # Immediately after start is not active, so the earliest we might need
-        # to remove is the next mark, or possibly the very first
-        if m:
-            m = m.next()
-        else:
-            m, l = focus.vmarks(viewnum)
-        if not m or m >= end:
-            # Nothing to remove
-            return
-    elif start < m:
-        # From m to start are in a range and should stay there.
-        # Split the range from 'm' at 'start'
-        m = edlib.Mark(focus, view = viewnum)
-        m.to_mark(start)
-        m = edlib.Mark(orig=m, owner = focus)
-        # ensure the m is after the previous one
-        m.step(1)
-        m[attr] = 'yes'
-    # m is now the start of an active section that is within start-end
-    # and should be removed
-    m2 = focus.vmark_at_or_before(viewnum, end)
-    if m2 and m2 == end and m2[attr]:
-        # this section is entirely after end, so not interesting
-        m2 = m2.prev()
-    if m2 and m2[attr]:
-        # end is within an active section that needs to be split
-        m2 = edlib.Mark(focus, view = viewnum)
-        m2.to_mark(end)
-        m2[attr] = 'yes'
-        m2 = edlib.Mark(orig=m2, owner=focus)
-        m2.step(0)
-    # m2 is now the end of an active section that needs to be
-    # discarded
-    while m < m2:
-        old = m
-        m = m.next()
-        old.release()
-    m2.release()
-    return
-
-def add_range(focus, viewnum, attr, start, end):
-    m1 = focus.vmark_at_or_before(viewnum, start)
-    if m1 and m1[attr]:
-        m1 = m1.next()
-        # Range ending at m1 can be extended to cover start->end
-    elif m1 and m1 == start:
-        # can move m1 down to cover range
-        pass
-    else:
-        m1 = None
-        # must create new mark, or move a later mark up
-    m2 = focus.vmark_at_or_before(viewnum, end)
-    if m2 and not m2[attr]:
-        if m2 == end:
-             m2 = m2.prev()
-             # can move m2 earlier
-        else:
-            # end not in range, must create mark or move earlier down
-            m2 = None
-    # if m2, then can move it backwards.  No need to create
-    if not m1 and not m2:
-        # no overlaps, create new region
-        m1 = edlib.Mark(focus, viewnum)
-        m1.to_mark(start)
-        m2 = edlib.Mark(orig=m1, owner=focus)
-        m2.to_mark(end)
-        m1[attr] = 'yes'
-    elif m1 and not m2:
-        # can move m1 down to end, removing anything in the way
-        m = m1.next()
-        while m and m <= end:
-            m.release()
-            m = m1.next()
-        m1.to_mark(end)
-    elif not m1 and m2:
-        # can move m2 up to start, removing things
-        m = m2.prev()
-        while m and m >= start:
-            m.release()
-            m = m2.prev()
-        m2.to_mark(start)
-    else:
-        # can remove all from m1 to m2 inclusive
-        while m1 < m2:
-            m = m1.next()
-            m1.release()
-            m1 = m
-        m2.release()
-
-def choose_range(focus, viewnum, attr, start, end):
-    # contract start-end so that none of it is in-range
-    m1 = focus.vmark_at_or_before(viewnum, start)
-    if m1 and not m1[attr]:
-        m2 = m1.next()
-        # start not in-range, end must not exceed m1
-    elif m1 and m1[attr]:
-        # start is in range - move it forward
-        m1 = m1.next()
-        if m1:
-            start.to_mark(m1)
-            m2 = m1.next()
-        else:
-            # error
-            m2 = start
-    else:
-        m2, l = focus.vmarks(viewnum)
-    if m2 and m2 < end:
-        end.to_mark(m2)
-
-class autospell_monitor(edlib.Pane):
-    # autospell_monitor attaches to a document and tracks ranges
-    # that have been spell-checked.  Sends notifications when there
-    # is a change, so viewers can do the checking.  Only views know
-    # mode-specific details
-    def __init__(self, focus):
-        edlib.Pane.__init__(self, focus)
-        self.view = self.call("doc:add-view") - 1
-        self.call("doc:request:doc:replaced")
-        self.call("doc:request:spell:mark-checked")
-        self.call("doc:request:spell:choose-range")
-        self.call("doc:request:spell:dict-changed")
-
-    def doc_replace(self, key, focus, mark, mark2, num2, **a):
-        "handle-list/doc:replaced/spell:dict-changed"
-        if not mark:
-            mark = edlib.Mark(focus)
-            focus.call("doc:set-ref", mark, 1);
-        if not mark2:
-            mark2 = edlib.Mark(focus)
-            focus.call("doc:set-ref", mark2, 0);
-
-        # mark2 might have been the start-of-word, but not any longer
-        # So any spell-incorrect must be cleared as normal checking
-        # only affects first char of a word.
-        focus.call("doc:set-attr", mark2, "render:spell-incorrect",
-                   None);
-        # Need to capture adjacent words, and avoid zero-size gap
-        mark = mark.dup()
-        focus.prev(mark)
-        mark2 = mark2.dup()
-        focus.next(mark2)
-
-        remove_range(self, self.view, "spell:start", mark, mark2)
-
-        self.call("doc:notify:spell:recheck")
-        return 1
-
-    def handle_checked(self, key, mark, mark2, **a):
-        "handle:spell:mark-checked"
-        if mark and mark2:
-            add_range(self, self.view, 'spell:start', mark, mark2)
-        return 1
-
-    def handle_choose(self, key, mark, mark2, **a):
-        "handle:spell:choose-range"
-        if mark and mark2:
-            choose_range(self, self.view, 'spell:start', mark, mark2)
-        return 1
-
 class autospell_view(edlib.Pane):
     def __init__(self, focus):
         edlib.Pane.__init__(self, focus)
@@ -203,8 +29,9 @@ class autospell_view(edlib.Pane):
         self.vstart = None
         self.vend = None
         self.menu = None
-        self.call("doc:request:spell:recheck")
+        self.call("doc:request:rangetrack:recheck-autospell")
         self.call("doc:request:doc:replaced")
+        self.call("doc:request:spell:dict-changed")
         # trigger render-lines refresh notification
         pt = focus.call("doc:point", ret='mark')
         focus.call("render:request:reposition", pt)
@@ -228,7 +55,8 @@ class autospell_view(edlib.Pane):
         if not str1 or not mark or not comm2:
             return edlib.Efallthrough
         if str1 == "render:spell-incorrect":
-            comm2("cb", focus, int(str2), mark, "fg:red-80,underline,action-menu:autospell-menu", 120)
+            comm2("cb", focus, int(str2), mark,
+                  "fg:red-80,underline,action-menu:autospell-menu", 120)
         return edlib.Efallthrough
 
     def handle_click(self, key, focus, mark, xy, str1, **a):
@@ -242,7 +70,8 @@ class autospell_view(edlib.Pane):
         self.thisword = w
         mp.call("menu-add", "[Insert in dict]", "+")
         mp.call("menu-add", "[Accept for now]", "!")
-        focus.call("Spell:Suggest", w, lambda key, str1, **a: mp.call("menu-add", str1))
+        focus.call("Spell:Suggest", w,
+                   lambda key, str1, **a: mp.call("menu-add", str1))
         mp.call("doc:file", -1)
         self.menu = mp
         self.add_notify(mp, "Notify:Close")
@@ -276,20 +105,45 @@ class autospell_view(edlib.Pane):
         return 1
 
     def handle_recheck(self, key, **a):
-        "handle:spell:recheck"
+        "handle:rangetrack:recheck-autospell"
         self.sched()
+        return 1
+
+    def handle_dict_changed(self, key, focus, mark, mark2, num2, **a):
+        "handle:spell:dict-changed"
+        # clear everything
+        self.call("doc:notify:rangetrack:clear", "autospell")
+        return 1
 
     def handle_replace(self, key, focus, mark, mark2, num2, **a):
         "handle:doc:replaced"
         if not mark or not mark2:
+            # clear everything
+            self.call("doc:notify:rangetrack:clear", "autospell")
             return 1
+
+        # mark2 might have been the start-of-word, but not any longer
+        # So any spell-incorrect must be cleared as normal checking
+        # only affects first char of a word.
+        focus.call("doc:set-attr", mark2, "render:spell-incorrect",
+                   None);
+
         # if change at either end of view, extend view until reposition message
+        # If we don't get a render:resposition message then probably the
+        # rendered didn't see the mark move, but we did, because the change
+        # was between the marks?  Should I test more precisely for that FIXME
         if mark < self.vstart and mark2 >= self.vstart:
             self.vstart.to_mark(mark)
-            self.sched()
         if mark2 > self.vend and mark <= self.vend:
             self.vend.to_mark(mark2)
-            self.sched()
+
+        # Need to capture adjacent words, and avoid zero-size gap
+        mark = mark.dup()
+        focus.prev(mark)
+        mark2 = mark2.dup()
+        focus.next(mark2)
+        self.call("doc:notify:rangetrack:clear", "autospell", mark, mark2)
+
         return 1
 
     def reposition(self, key, mark, mark2, **a):
@@ -298,9 +152,12 @@ class autospell_view(edlib.Pane):
             self.vstart = mark.dup()
             self.vend = mark2.dup()
             if (not self.helper_attached and
-                not self.call("doc:notify:spell:mark-checked")):
-                self.call("doc:get-doc", autospell_attach_helper)
-                self.helper_attached = True
+                not self.call("doc:notify:rangetrack:add",
+                              "autospell")):
+                if self.call("rangetrack:new", "autospell") > 0:
+                    self.helper_attached = True
+                else:
+                    pass # FIXME
             self.sched()
         return edlib.Efallthrough
 
@@ -315,7 +172,8 @@ class autospell_view(edlib.Pane):
             return edlib.Efalse
         start = self.vstart.dup()
         end = self.vend.dup()
-        self.call("doc:notify:spell:choose-range", start, end)
+        self.call("doc:notify:rangetrack:choose", "autospell",
+                  start, end)
         if start >= end:
             # nothing to do
             return edlib.Efalse
@@ -334,7 +192,8 @@ class autospell_view(edlib.Pane):
             focus.call("Spell:NextWord", ed)
             st = ed.dup()
             word = focus.call("Spell:ThisWord", ed, st, ret='str')
-            self.call("doc:notify:spell:mark-checked", start, ed)
+            self.call("doc:notify:rangetrack:add", "autospell",
+                      start, ed)
             start = ed
             if word:
                 ret = focus.call("Spell:Check", word)
@@ -357,10 +216,6 @@ def autospell_attach(key, focus, comm2, **a):
         comm2("callback", p)
     return 1
 
-def autospell_attach_helper(key, focus, **a):
-    p = autospell_monitor(focus)
-    return 1
-
 def autospell_activate(key, focus, comm2, **a):
     autospell_view(focus)
 
@@ -370,4 +225,4 @@ def autospell_activate(key, focus, comm2, **a):
 
 edlib.editor.call("global-set-command", "attach-autospell", autospell_attach)
 edlib.editor.call("global-set-command", "interactive-cmd-autospell",
-            autospell_activate)
+                  autospell_activate)