2 * Copyright Neil Brown ©2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * rangetrack: track ranges of a document which have been processed
6 * in some why, such a spell-check or syntax-highlight or other
9 * rangetrack will attach a pane to the target document to store
10 * marks and other state. It can track an arbitrary set of different
13 * rangetrack:new : start tracking ranges on 'focus' document.
14 * str is the name of the range set.
15 * rangetrack:add : record that mark to mark2 are a valid range
16 * rangetrack:remove : record that from mark to mark2 are no longer valid
17 * rangetrack:choose : report a subrange for mark..mark2 which is not
22 #define PANE_DATA_TYPE struct rangetrack_data
25 struct rangetrack_data {
28 struct pane *owner safe;
33 #include "core-pane.h"
35 static struct rci *find_set(const struct cmd_info *ci safe)
37 struct rangetrack_data *rtd = ci->home->data;
40 for (i = rtd->info; i; i = i->next) {
41 if (ci->str && strcmp(ci->str, i->set) == 0)
47 static void add_set(struct pane *home safe, const char *set safe,
48 struct pane *focus safe)
50 struct rangetrack_data *rtd = home->data;
56 pane_add_notify(home, focus, "Notify:Close");
57 i->view = call("doc:add-view", home) - 1;
62 DEF_CMD(rangetrack_notify_close)
64 struct rangetrack_data *rtd = ci->home->data;
67 for (ip = &rtd->info; *ip; ip = &(*ip)->next) {
69 if ((*ip)->owner != ci->focus)
80 DEF_CMD_CLOSED(rangetrack_close)
82 struct rangetrack_data *rtd = ci->home->data;
85 while ((i = rtd->info) != NULL) {
93 DEF_CMD(rangetrack_new)
95 struct rci *i = find_set(ci);
101 add_set(ci->home, ci->str, ci->focus);
105 DEF_CMD(rangetrack_add)
107 struct rci *i = find_set(ci);
108 struct mark *start = ci->mark;
109 struct mark *end = ci->mark2;
110 struct mark *m, *m1, *m2;
115 /* Testing if configured already */
117 m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
118 if (m1 && attr_find(m1->attrs, "start"))
120 else if (m1 && mark_same(m1, start))
121 /* m1 is an end-of-range. Can move m1 down to cover range */
124 /* Must create a new mark, or move a later mark up */
126 m2 = vmark_at_or_before(ci->home, end, i->view, ci->home);
127 if (m2 && attr_find(m2->attrs, "start") == NULL) {
128 if (mark_same(m2, end))
129 /* Can move the start of this range earlier */
132 /* end not in rnage, must create mark or move
137 /* If m2, then move it backwards - no need to create */
139 /* no overlaps, create a new region */
140 m1 = vmark_new(ci->home, i->view, ci->home);
143 mark_to_mark(m1, start);
144 m2 = vmark_new(ci->home, i->view, ci->home);
147 mark_to_mark(m2, end);
148 attr_set_str(&m1->attrs, "start", "yes");
149 } else if (m1 && !m2) {
150 /* Can move m1 dow n to end, removing anything in the way */
152 while (m && mark_ordered_or_same(m, end)) {
156 mark_to_mark(m1, end);
157 } else if (!m1 && m2) {
158 /* Can move m2 up to start, removing things */
160 while (m && mark_ordered_or_same(start, m)) {
164 mark_to_mark(m2, start);
165 } else if (m1 && m2) {
166 /* Can remove all from m1 to m2 inclusive */
167 while (m1 && mark_ordered_not_same(m1, m2)) {
177 DEF_CMD(rangetrack_clear)
179 struct rci *i = find_set(ci);
180 struct mark *start = ci->mark;
181 struct mark *end = ci->mark2;
182 struct mark *m1, *m2;
186 if (!start || !end) {
187 start = vmark_first(ci->home, i->view, ci->home);
188 end = vmark_last(ci->home, i->view, ci->home);
193 m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
195 if (!m1 || attr_find(m1->attrs, "start") == NULL) {
196 /* Immediately after start is not active, so the
197 * earlierst we might need to remove is the next
198 * mark, or possibly the very first mark.
203 m1 = vmark_first(ci->home, i->view, ci->home);
204 if (!m1 || mark_ordered_or_same(end, m1))
205 /* Nothing to remove */
208 /* From m1 to start are in a range and should stay
209 * there. Split the range from 'm1' at 'start'
211 m1 = vmark_new(ci->home, i->view, ci->home);
214 mark_to_mark(m1, start);
215 m1 = mark_dup_view(m1);
216 /* Ensure this m1 is after the previous one */
218 attr_set_str(&m1->attrs, "start", "yes");
220 /* m is now the start of an active section that is within
221 * start-end and should be removed */
222 m2 = vmark_at_or_before(ci->home, end, i->view, ci->home);
223 if (m2 && mark_same(m2, end) && attr_find(m2->attrs, "start"))
224 /* This section is entirely after end, so not interesting */
226 if (m2 && attr_find(m2->attrs, "start")) {
227 /* end is within an active secion that needs to be split */
228 m2 = vmark_new(ci->home, i->view, ci->home);
231 mark_to_mark(m2, end);
232 attr_set_str(&m2->attrs, "start", "yes");
233 m2 = mark_dup_view(m2);
238 /* m2 is now the end of an active section that needs to bie discarded */
239 while (m1 && mark_ordered_not_same(m1, m2)) {
245 call(strconcat(ci->home, "doc:notify:rangetrack:recheck-", i->set),
250 DEF_CMD(rangetrack_choose)
252 struct rci *i = find_set(ci);
253 struct mark *start = ci->mark;
254 struct mark *end = ci->mark2;
255 struct mark *m1, *m2;
261 /* Contract start-end so that none of it is in-range */
262 m1 = vmark_at_or_before(ci->home, start, i->view, ci->home);
263 if (m1 && attr_find(m1->attrs, "start") == NULL)
264 /* Start is not in-range, end must not exceed m1 */
267 /* m1 is in-range, move it forward */
270 mark_to_mark(start, m1);
273 /* Should be impossible */
277 /* Start is before all ranges */
278 m2 = vmark_first(ci->home, i->view, ci->home);
280 if (m2 && mark_ordered_not_same(m2, end))
281 mark_to_mark(end, m2);
285 static struct map *rangetrack_map safe;
286 DEF_LOOKUP_CMD(rangetrack_handle, rangetrack_map);
288 DEF_CMD(rangetrack_attach)
290 struct pane *doc = call_ret(pane, "doc:get-doc", ci->focus);
291 const char *set = ci->str;
298 if (call("doc:notify:rangetrack:new", ci->focus, 0, NULL, set) > 0)
300 p = pane_register(doc, 0, &rangetrack_handle.c);
303 pane_add_notify(p, doc, "rangetrack:new");
304 pane_add_notify(p, doc, "rangetrack:add");
305 pane_add_notify(p, doc, "rangetrack:clear");
306 pane_add_notify(p, doc, "rangetrack:choose");
307 add_set(p, set, ci->focus);
311 void edlib_init(struct pane *ed safe)
313 call_comm("global-set-command", ed, &rangetrack_attach,
314 0, NULL, "rangetrack:new");
315 rangetrack_map = key_alloc();
316 key_add(rangetrack_map, "Close", &rangetrack_close);
317 key_add(rangetrack_map, "Notify:Close", &rangetrack_notify_close);
318 key_add(rangetrack_map, "rangetrack:new", &rangetrack_new);
319 key_add(rangetrack_map, "rangetrack:add", &rangetrack_add);
320 key_add(rangetrack_map, "rangetrack:clear", &rangetrack_clear);
321 key_add(rangetrack_map, "rangetrack:choose", &rangetrack_choose);