2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * All content managed in edlib is stored in documents.
6 * There can be multiple document handlers which export the
7 * doc_operations interface to provide access to a particular
8 * style of document storage.
9 * A document has a list of marks and points (managed in core-mark.c)
10 * and some attributes (managed in attr.c).
11 * It has a list of 'views' which are notified when the document changes.
12 * Those are managed here.
14 * Finally all documents are kept in a single list which itself is
15 * used as the basis for a document: the document-list. The list is
16 * kept in most-recently-used order. Each document has a unique name
20 #define _GNU_SOURCE for strchrnul
29 #define PRIVATE_DOC_REF
35 #define PANE_DATA_TYPE struct doc_data
40 /* this is ->data for a document reference pane.
43 struct pane *doc safe;
44 struct mark *point safe;
45 struct mark *old_point; /* location at last refresh */
46 struct mark *marks[4];
48 #include "core-pane.h"
50 static struct pane *doc_attach_assign(struct pane *parent safe, struct pane *doc safe);
53 static void doc_init(struct doc *d safe)
56 INIT_HLIST_HEAD(&d->marks);
57 INIT_TLIST_HEAD(&d->points, 0);
61 memset(d->recent_points, 0, sizeof(d->recent_points));
67 struct pane *do_doc_register(struct pane *parent safe,
68 struct command *handle safe,
70 unsigned short data_size)
74 if (doc == NULL && data_size < sizeof(*doc))
75 /* Not enough room for the doc ! */
78 /* Documents are always registered against the root */
79 parent = pane_root(parent);
80 p = do_pane_register(parent, 0, handle, doc, data_size);
89 /* For these 'default commands', home->data is struct doc */
92 struct pane *f = ci->focus;
93 struct doc_data *dd = ci->home->data;
94 struct mark *m = ci->mark;
95 int rpt = RPT_NUM(ci);
101 if (doc_next(f,m) == WEOF)
106 if (doc_prev(f,m) == WEOF)
116 /* Step to the Nth word boundary in appropriate
117 * direction. If N is 0, don't move.
118 * Return 1 if succeeded before EOF, else Efalse.
120 struct pane *f = ci->focus;
121 struct mark *m = ci->mark;
122 int rpt = RPT_NUM(ci);
127 /* doc:word should finish at a word boundary, which usually
128 * means an alphanum (possibly including '_' depending on doc
129 * attributes?). However it should never cross two different
130 * sets of spaces or punctuation. So if we cross space and
131 * punct and don't find alphanum, then we treat end of punct as
132 * a word boundary. We never stop immediately after a space.
133 * So skip spaces, then punct, then alphanum.
134 * Same in either direction.
137 while (rpt > 0 && wi != WEOF) {
138 while ((wi = doc_following(f, m)) != WEOF &&
141 while ((wi = doc_following(f, m)) != WEOF &&
142 !iswspace(wi) && !iswalnum(wi))
144 while ((wi = doc_following(f, m)) != WEOF &&
149 while (rpt < 0 && wi != WEOF) {
150 while ((wi = doc_prior(f, m)) != WEOF &&
153 while ((wi = doc_prior(f, m)) != WEOF &&
154 !iswspace(wi) && !iswalnum(wi))
156 while ((wi = doc_prior(f, m)) != WEOF &&
161 return rpt == 0 ? 1 : Efalse;
164 static bool check_slosh(struct pane *p safe, struct mark *m safe)
167 /* Check is preceded by exactly 1 '\' */
168 if (doc_prior(p, m) != '\\')
171 ch = doc_prior(p, m);
178 /* doc_expr skips an 'expression' which is the same as a word
179 * unless we see open '({[' or close ')}]' or quote (\'\").
180 * We ignore quotes when preceeded by a single '\'
181 * If we see close going forward, or open going backward, we stop.
182 * If we see open going forward or close going backward, or quote,
183 * we skip to matching close/open/quote, allowing for nested
184 * open/close etc. Inside quotes, we stop at EOL.
185 * If num2 is 1, then if we reach a true 'open' we continue
186 * one more character to enter (going forward) or leave (backward)
188 * 'str' can be set to extra chars that should be included in words.
190 struct pane *f = ci->focus;
191 struct mark *m = ci->mark;
192 int rpt = RPT_NUM(ci);
193 int enter_leave = ci->num2;
194 int dir = rpt > 0 ? 1 : -1;
197 const char *wordchars = ci->str ?: "";
198 const char *special safe = "[](){}'\"";
203 open = "([{"; close = ")]}";
205 open = ")]}"; close = "([{";
210 while ((wi = doc_pending(f, m, dir)) != WEOF
214 while ((wi = doc_pending(f, m, dir)) != WEOF &&
215 !iswspace(wi) && !iswalnum(wi) &&
216 (wi > 255 || (strchr(special, wi) == NULL &&
217 strchr(wordchars, wi) == NULL)))
220 if (strchr(close, wi)) {
221 if (dir < 0 && enter_leave) {
227 } else if (strchr(open, wi)) {
228 /* skip bracketed expression */
233 if (enter_leave && dir > 0)
234 /* Just entered the expression */
236 else while (depth > 0 &&
237 (wi = doc_move(f, m, dir)) != WEOF) {
241 if ((!check_slosh(f, m) && wi == q) ||
246 } else if (strchr(open, wi))
248 else if (strchr(close, wi))
250 else if (wi == '"' || wi == '\'') {
253 if (!check_slosh(f, m))
259 } else if (wi == '"' || wi == '\'') {
260 /* skip quoted or to EOL */
264 slosh = check_slosh(f, m);
268 slosh = check_slosh(f, m);
271 while (((wi = doc_pending(f, m, dir))
275 slosh = check_slosh(f, m);
279 slosh = check_slosh(f, m);
281 if (wi == q && !slosh)
285 } else while (((wi=doc_pending(f, m, dir)) != WEOF && iswalnum(wi)) ||
286 (wi > 0 && wi <= 255 &&
287 strchr(wordchars, wi) != NULL))
300 /* Step to the Nth word boundary in appropriate
301 * direction. For this function, puctuation is treated the
302 * same as alphanum. Only space separates words.
303 * If N is 0, don't move.
304 * Return 1 if succeeded before EOF, else Efalse.
306 struct pane *f = ci->focus;
307 struct mark *m = ci->mark;
308 int rpt = RPT_NUM(ci);
313 /* We skip spaces, then non-spaces */
317 while ((wi = doc_following(f, m)) != WEOF &&
321 while ((wi = doc_following(f, m)) != WEOF &&
329 while ((wi = doc_prior(f, m)) != WEOF &&
332 while ((wi = doc_prior(f, m)) != WEOF &&
338 return rpt == 0 ? 1 : Efalse;
343 struct pane *f = ci->focus;
344 struct mark *m = ci->mark;
346 int rpt = RPT_NUM(ci);
347 bool one_more = ci->num2 > 0;
352 while (rpt > 0 && ch != WEOF) {
353 while ((ch = doc_next(f, m)) != WEOF &&
359 while (rpt < 0 && ch != WEOF) {
360 while ((ch = doc_prev(f, m)) != WEOF &&
370 else if (RPT_NUM(ci) < 0)
376 else if (RPT_NUM(ci) < 0)
380 return rpt == 0 ? 1 : Efalse;
385 int rpt = RPT_NUM(ci);
386 struct mark *m = ci->mark;
391 call("doc:set-ref", ci->focus, (rpt < 0), m);
398 struct pane *p = ci->focus;
399 struct doc_data *dd = ci->home->data;
400 struct mark *m = ci->mark;
402 int rpt = RPT_NUM(ci);
407 while (rpt > 0 && ch != WEOF) {
408 while ((ch = doc_next(p, m)) != WEOF &&
413 while (rpt < 0 && ch != WEOF) {
414 while ((ch = doc_prev(p, m)) != WEOF &&
424 /* Default paragraph move - find blank line - two or more
426 * If moving forward, skip over all those chars.
427 * If moving backward, stop before the first one.
429 struct pane *p = ci->focus;
430 struct mark *m = ci->mark;
431 int rpt = RPT_NUM(ci);
434 int dir = rpt > 0 ? 1 : -1;
439 while (dir < 0 && is_eol(doc_prior(p, m)))
442 while (rpt && ch != WEOF) {
445 ch = doc_move(p, m, dir);
451 doc_move(p, m, -dir);
458 while (dir < 0 && nlcnt-- > 0)
465 struct pane *p = ci->focus;
466 struct doc_data *dd = ci->home->data;
467 struct mark *m = ci->mark, *old;
469 int rpt = RPT_NUM(ci);
476 /* repeat count is in 1000th of the pane */
478 while (rpt > 0 && ch != WEOF) {
479 while ((ch = doc_next(p, m)) != WEOF &&
484 while (rpt < 0 && ch != WEOF) {
485 while ((ch = doc_prev(p, m)) != WEOF &&
490 if (mark_same(m, old)) {
500 struct doc *d = ci->home->_data;
501 const char *val = ksuffix(ci, "doc:set:");
508 if (strcmp(val, "autoclose") == 0) {
509 d->autoclose = ci->num;
512 if (strcmp(val, "readonly") == 0) {
513 d->readonly = ci->num;
514 call("doc:notify:doc:status-changed", ci->home);
518 attr_set_str(&ci->home->attrs, val, ci->str);
525 struct pane *p = ci->home;
526 const char *attr = ksuffix(ci, "doc:append:");
527 const char *val = ci->str;
537 /* Append the string to the attr. It attr doesn't
538 * exists, strip first char of val and use that.
540 old = attr_find(p->attrs, attr);
542 attr_set_str(&p->attrs, attr, val+1);
544 const char *pos = strstr(old, val+1);
545 int len = strlen(val+1);
547 (pos == old || pos[-1] == val[0]) &&
548 (pos[len] == 0 || pos[len] == val[0]))
549 ; /* val already present */
551 attr_set_str(&p->attrs, attr, strconcat(p, old, val));
556 DEF_CMD(doc_get_attr)
558 struct doc *d = ci->home->_data;
559 char pathbuf[PATH_MAX];
565 if ((a = attr_find(ci->home->attrs, ci->str)) != NULL)
567 else if (strcmp(ci->str, "doc-name") == 0)
569 else if (strcmp(ci->str, "doc-modified") == 0)
571 else if (strcmp(ci->str, "doc-readonly") == 0) {
572 a = d->readonly ? "yes":"no";
573 } else if (strcmp(ci->str, "dirname") == 0) {
575 a = pane_attr_get(ci->home, "filename");
577 a = realpath(".", pathbuf);
578 if (a != pathbuf && a)
581 strcat(pathbuf, "/");
582 a = strsave(ci->focus, pathbuf);
584 sl = strrchr(a, '/');
587 a = strnsave(ci->focus, a, (sl-a)+1);
589 attr_set_str(&ci->home->attrs, "dirname", a);
590 } else if (strcmp(ci->str, "realdir") == 0) {
591 a = pane_attr_get(ci->home, "dirname");
594 a = realpath(a, pathbuf);
595 if (a && a != pathbuf)
598 strcat(pathbuf, "/");
601 attr_set_str(&ci->home->attrs, "realdir", a);
604 return comm_call(ci->comm2, "callback:get_attr", ci->focus, 0,
609 DEF_CMD(doc_doc_get_attr)
611 /* If the document doesn't provide the attribute for
612 * this location, see if there is a pane-attribute for
619 a = pane_attr_get(ci->home, ci->str);
621 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, a);
625 DEF_CMD(doc_set_name)
627 struct doc *d = ci->home->_data;
632 d->name = strdup(ci->str);
633 return call("doc:notify:doc:revisit", ci->home, ci->num) ?: 1;
636 DEF_CMD(doc_request_notify)
638 pane_add_notify(ci->focus, ci->home, ksuffix(ci, "doc:request:"));
644 /* Key is "doc:notify:..." */
645 int ret = pane_notify(ksuffix(ci, "doc:notify:"),
647 ci->num, ci->mark, ci->str,
648 ci->num2, ci->mark2, ci->str2, ci->comm2);
652 static int do_del_view(struct doc *d safe, int v,
656 /* This view should only have points on the list, not typed
657 * marks. Just delete everything and clear the 'notify' pointer
659 if (v < 0 || v >= d->nviews || d->views == NULL ||
660 !owner || d->views[v].owner != owner)
663 d->views[v].owner = NULL;
664 while (!tlist_empty(&d->views[v].head)) {
666 struct tlist_head *tl = d->views[v].head.next;
668 switch (TLIST_TYPE(tl)) {
669 case GRP_LIST: /* A point */
672 case GRP_MARK: /* a vmark */
673 m = container_of(tl, struct mark, view);
675 pane_call(owner, "Close:mark", owner, 0, m);
676 if (tl == d->views[v].head.next) {
677 /* It hasn't been freed */
678 if (m->mdata && !warned) {
679 call("editor:notify:Message:broadcast",
681 "WARNING mark not freed by Close:mark");
682 LOG("WARNING Mark not freed by Close:mark");
689 default: /* impossible */
698 struct doc *d = ci->home->_data;
700 return do_del_view(d, ci->num, ci->focus);
705 struct doc *d = ci->home->_data;
710 for (ret = 0; d->views && ret < d->nviews; ret++)
711 if (d->views[ret].owner == NULL)
713 if (!d->views || ret == d->nviews) {
714 /* Resize the view list */
716 g = alloc_buf(sizeof(*g) * d->nviews, pane);
717 for (i = 0; d->views && i < ret; i++) {
718 tlist_add(&g[i].head, GRP_HEAD, &d->views[i].head);
719 tlist_del(&d->views[i].head);
720 g[i].owner = d->views[i].owner;
722 for (; i < d->nviews; i++) {
723 INIT_TLIST_HEAD(&g[i].head, GRP_HEAD);
726 unalloc_buf(d->views, sizeof(*g)*(d->nviews - 4), pane);
728 /* now resize all the points */
731 if (d->views /* FIXME always true */) {
732 points_attach(d, ret);
733 d->views[ret].owner = ci->focus;
734 pane_add_notify(ci->home, ci->focus, "Notify:Close");
739 DEF_CMD(doc_close_doc)
741 struct doc *d = ci->home->_data;
742 doc_free(d, ci->home);
746 DEF_CMD(doc_view_close)
748 /* A pane which once held a view is closing. We must discard
749 * that view if it still exists.
751 struct doc *d = ci->home->_data;
754 for (v = 0 ; d->views && v < d->nviews; v++)
755 do_del_view(d, v, ci->focus);
759 DEF_CMD(doc_vmarkget)
762 m = do_vmark_first(ci->home->_data, ci->num, ci->focus);
763 m2 = do_vmark_last(ci->home->_data, ci->num, ci->focus);
764 return comm_call(ci->comm2, "callback:vmark", ci->focus,
765 0, m, NULL, 0, m2) ?: 1;
768 DEF_CMD(doc_vmarkprev)
770 struct mark *m = NULL;
772 m = do_vmark_at_or_before(ci->home->_data, ci->mark,
774 comm_call(ci->comm2, "callback:vmark", ci->focus, 0, m);
778 DEF_CMD(doc_vmarknew)
782 m = doc_new_mark(ci->home, ci->num, ci->focus);
783 comm_call(ci->comm2, "callback:vmark", ci->focus, 0, m);
787 DEF_CMD(doc_drop_cache)
789 struct pane *p = ci->home;
790 struct doc *d = p->_data;
797 DEF_CMD(doc_delayed_close)
799 struct pane *p = ci->home;
802 /* If there are any doc-displays open, then will return '1' and
803 * we will know not to destroy document yet.
805 ret = pane_notify("doc:notify-viewers", p);
807 call("doc:drop-cache", p);
811 DEF_CMD(doc_do_closed)
813 struct pane *p = ci->home;
816 /* Close the path of filters from doc to focus */
817 child = pane_my_child(p, ci->focus);
821 call_comm("event:on-idle", p, &doc_delayed_close, 1);
825 DEF_CMD(doc_do_destroy)
827 pane_close(ci->home);
831 DEF_CMD(doc_get_point)
833 struct doc_data *dd = ci->home->data;
836 if (ci->num >= 1 && ci->num <= 4)
839 comm_call(ci->comm2, "callback", ci->focus, 0, dd->point, NULL,
844 DEF_CMD(doc_default_content)
846 /* doc:content delivers one char at a time to a callback.
847 * This is used for 'search' and 'copy'.
848 * This default version calls doc:char which is simple, but might
851 * If called as doc:content-bytes: return bytes, not chars
853 * .mark is 'location': to start. This is not moved.
854 * .mark2, if set, is location to stop.
855 * .comm2 is 'consume': pass char mark and report if finished.
858 * .mark - the mark that was passed in and gets moved
859 * .num - char character just before .mark
860 * .str - num utf8 text after mark. It may not be present
861 * and if it is, at most .num2 bytes can be used
862 * .num2 - usable length of .str
864 * comm2 it typically embedded in another struct that can
865 * be accessed in the callback (using container_of in C code).
866 * If the caller need to know where the callback aborted, the
867 * callback need to record that somehow.
869 * comm2 should return 1 if the main char was consumed,
870 * 1+n if n bytes (not chars) from str were consumed
873 * If the callback processes some of 'str', the mark will no longer
874 * be accurate. If it needs an accurate mark, it can walk a copy
875 * forward, or return a suitable count and be called again with an
878 struct mark *m = ci->mark;
880 char *cmd = "doc:char";
882 if (!m || !ci->comm2)
885 if (strcmp(ci->key, "doc:content-bytes") == 0)
888 nxt = call(cmd, ci->home, 1, m);
889 while (nxt > 0 && nxt != CHAR_RET(WEOF) &&
890 (!ci->mark2 || mark_ordered_or_same(m, ci->mark2)) &&
891 comm_call(ci->comm2, "consume", ci->home, (nxt & 0x1FFFF), m) > 0)
892 nxt = call(cmd, ci->home, 1, m);
895 return nxt < 0 ? nxt : 1;
898 DEF_CMD(doc_insert_char)
900 const char *str = ksuffix(ci, "doc:char-");
902 return call("doc:replace", ci->focus, 1, NULL, str, ci->num2,
913 DEF_CB(get_str_callback)
915 /* First char will be in ci->num and ->mark will be *after* that char.
916 * Some more chars might be in ->str (for ->num2 bytes).
917 * If ->x, then expect that many bytes (approximately).
918 * Return Efalse to stop, 1 if char was consumed,
919 * 1+N (N <= ->num2) if N bytes from ->str were consumed.
921 wint_t wch = ci->num & 0x1FFFFF;
922 struct getstr *g = container_of(ci->comm, struct getstr, c);
927 buf_resize(&g->b, ci->x);
929 buf_append_byte(&g->b, ci->num & 0xff);
931 buf_append(&g->b, wch);
932 if (g->end && (ci->mark->seq >= g->end->seq ||
933 mark_same(ci->mark, g->end)))
935 if (!ci->str || ci->num2 <= 0)
938 /* This could over-run ->end, but we assume it doesn't */
939 buf_concat_len(&g->b, ci->str, ci->num2);
946 * uses doc:content to collect the content
948 * If mark and mark2 are both set, they are end points.
949 * If only mark is set we ignore it. It is likely
950 * 'point' provided by default.
952 int bytes = strcmp(ci->key, "doc:get-bytes") == 0;
954 struct mark *from = NULL, *to = NULL;
956 if (ci->mark && ci->mark2) {
957 if (ci->mark2->seq < ci->mark->seq) {
966 g.c = get_str_callback;
971 from = mark_new(ci->focus);
973 call("doc:set-ref", ci->focus, 1, from);
978 to = mark_new(ci->focus);
980 call("doc:set-ref", ci->focus, 0, to);
982 call_comm(bytes ? "doc:content-bytes" : "doc:content",
983 ci->focus, &g.c, 0, from, NULL, 0, to);
984 if (from != ci->mark && from != ci->mark2)
986 if (to != ci->mark && to != ci->mark2)
988 comm_call(ci->comm2, "callback:get-str", ci->focus, g.b.len, NULL,
994 DEF_CMD(doc_notify_viewers)
996 /* The autoclose document wants to know if it should close,
997 * or a new view wants to find an active point.
998 * If a mark was provided, move it to point, then
999 * report that there are active viewers by returning 1
1001 struct doc_data *dd = ci->home->data;
1004 mark_to_mark(ci->mark, dd->point);
1008 DEF_CMD(doc_notify_moving)
1010 struct doc_data *dd = ci->home->data;
1012 if (ci->mark == dd->point)
1013 pane_damaged(ci->home, DAMAGED_VIEW);
1014 return Efallthrough;
1017 DEF_CMD(doc_refresh_view)
1019 struct doc_data *dd = ci->home->data;
1020 int active = attr_find_int(dd->point->attrs, "selection:active");
1023 call("view:changed", ci->focus, 0, dd->point, NULL,
1026 call("view:changed", ci->focus, 0, dd->point);
1028 call("view:changed", ci->focus, 0, dd->old_point);
1031 dd->old_point = mark_dup(dd->point);
1033 mark_to_mark(dd->old_point, dd->point);
1034 mark_watch(dd->point);
1038 DEF_CMD(doc_notify_close)
1040 /* This pane has to go away */
1041 struct doc_data *dd = ci->home->data;
1043 mark_free(dd->point);
1044 dd->point = safe_cast NULL;
1045 pane_close(ci->home);
1051 struct doc_data *dd = ci->home->data;
1052 struct pane *p = doc_attach_assign(ci->focus, dd->doc);
1056 call("Move-to", p, 0, dd->point);
1057 pane_clone_children(ci->home, p);
1063 struct doc_data *dd = ci->home->data;
1065 call("doc:push-point", dd->doc, 0, dd->point);
1066 mark_free(dd->point);
1067 mark_free(dd->old_point);
1068 for (i = 0; i < 4; i++)
1069 mark_free(dd->marks[i]);
1070 call("doc:closed", dd->doc);
1074 DEF_CMD(doc_dup_point)
1076 struct doc_data *dd = ci->home->data;
1077 struct mark *pt = dd->point;
1079 if (ci->mark && ci->mark->viewnum == MARK_POINT)
1082 if (!pt || !ci->comm2)
1085 if (ci->num2 == MARK_POINT)
1087 else if (ci->num2 == MARK_UNGROUPED)
1090 m = do_mark_at_point(pt, ci->num2);
1092 comm_call(ci->comm2, "callback:dup-point", ci->focus,
1097 DEF_CMD(doc_replace)
1099 struct doc_data *dd = ci->home->data;
1100 return call("doc:replace", ci->focus,
1101 1, ci->mark, ci->str,
1102 ci->num2, dd->point, ci->str2);
1105 DEF_CMD(doc_handle_get_attr)
1107 struct doc_data *dd = ci->home->data;
1111 a = pane_attr_get(dd->doc, ci->str);
1113 return Efallthrough;
1114 return comm_call(ci->comm2, "callback", ci->focus, 0, NULL, a) ?: 1;
1117 DEF_CMD(doc_move_to)
1119 struct doc_data *dd = ci->home->data;
1124 mark_to_mark(dd->point, ci->mark);
1125 } else if (ci->num > 0 && ci->num <= 4) {
1126 int mnum = ci->num - 1;
1128 if (!dd->marks[mnum]) {
1129 dd->marks[mnum] = mark_dup(dd->point);
1130 if (!dd->marks[mnum])
1133 attr_set_str(&dd->marks[mnum]->attrs,
1134 "render:interactive-mark", "yes");
1136 m = ci->mark ?: dd->point;
1137 mark_to_mark(dd->marks[mnum], m);
1138 /* Make sure mark is *before* point so insertion
1139 * leaves mark alone */
1140 mark_step(dd->marks[mnum], 0);
1141 } else if (ci->num < 0 && ci->num >= -4) {
1142 int mnum = -1 - ci->num;
1144 mark_free(dd->marks[mnum]);
1145 dd->marks[mnum] = NULL;
1153 struct doc_data *dd = ci->home->data;
1156 mark_clip(dd->point, ci->mark, ci->mark2, !!ci->num);
1158 mark_clip(dd->old_point, ci->mark, ci->mark2, !!ci->num);
1159 for (mnum = 0; mnum < 4; mnum++)
1160 if (dd->marks[mnum])
1161 mark_clip(dd->marks[mnum], ci->mark, ci->mark2, !!ci->num);
1165 DEF_CMD(doc_pass_on)
1167 struct doc_data *dd = ci->home->data;
1168 int ret = home_call(dd->doc, ci->key, ci->focus, ci->num,
1169 ci->mark ?: dd->point, ci->str,
1170 ci->num2, ci->mark2, ci->str2,
1171 ci->x, ci->y, ci->comm2);
1175 DEF_CMD(doc_push_point)
1177 struct doc *d = ci->home->_data;
1178 int n = ARRAY_SIZE(d->recent_points);
1182 mark_free(d->recent_points[n-1]);
1183 memmove(&d->recent_points[1],
1184 &d->recent_points[0],
1185 (n-1)*sizeof(d->recent_points[0]));
1186 m = mark_dup(ci->mark);
1187 m->attrs = attr_copy(ci->mark->attrs);
1188 d->recent_points[0] = m;
1192 DEF_CMD(doc_pop_point)
1194 struct doc *d = ci->home->_data;
1195 int n = ARRAY_SIZE(d->recent_points);
1199 if (!d->recent_points[0])
1201 mark_to_mark(ci->mark, d->recent_points[0]);
1202 if (!ci->mark->attrs) {
1203 ci->mark->attrs = d->recent_points[0]->attrs;
1204 d->recent_points[0]->attrs = NULL;
1206 mark_free(d->recent_points[0]);
1207 memmove(&d->recent_points[0],
1208 &d->recent_points[1],
1209 (n-1) * sizeof(d->recent_points[0]));
1210 d->recent_points[n-1] = NULL;
1214 DEF_CMD(doc_attach_view)
1216 struct pane *focus = ci->focus;
1217 struct pane *doc = ci->home;
1218 struct pane *p, *p2;
1220 const char *type = ci->str ?: "default";
1222 if (strcmp(type, "invisible") != 0) {
1224 /* caller is confused. */
1226 /* Double check the focus can display things */
1227 if (call("Draw:text", focus) == Efallthrough)
1231 p = doc_attach_assign(focus, doc);
1235 call("doc:notify:doc:revisit", p, ci->num);
1236 if (strcmp(type, "invisible") != 0) {
1237 /* Attach renderer */
1238 p2 = call_ret(pane, "attach-view", p);
1243 s = strconcat(p, "render-", type);
1245 s = pane_attr_get(doc, s);
1247 s = pane_attr_get(doc, "render-default");
1250 s = strconcat(p, "attach-render-", s);
1251 p2 = call_ret(pane, s, p);
1256 s = strconcat(p, "view-", type);
1258 s = pane_attr_get(doc, s);
1260 s = pane_attr_get(doc, "view-default");
1263 while ((s2 = strchr(s, ',')) != NULL) {
1264 char *s3 = strndup(s, s2-s);
1265 p2 = call_ret(pane, strconcat(p, "attach-", s3)
1272 s = strconcat(p, "attach-", s);
1273 p2 = call_ret(pane, s, p);
1279 comm_call(ci->comm2, "callback:doc", p);
1283 DEF_CMD(doc_get_doc)
1288 comm_call(ci->comm2, "attach", ci->home, ci->num, NULL, ci->str,
1289 ci->num2, NULL, ci->str2);
1295 struct doc_data *dd = ci->home->data;
1297 call("doc:notify:Abort", dd->doc);
1298 return Efallthrough;
1301 struct map *doc_default_cmd safe;
1302 static struct map *doc_handle_cmd safe;
1304 DEF_LOOKUP_CMD(doc_handle, doc_handle_cmd);
1306 static void init_doc_cmds(void)
1308 doc_default_cmd = key_alloc();
1309 doc_handle_cmd = key_alloc();
1311 key_add_prefix(doc_handle_cmd, "doc:", &doc_pass_on);
1313 key_add(doc_handle_cmd, "Move-Char", &doc_char);
1314 key_add(doc_handle_cmd, "Move-Line", &doc_line);
1315 key_add(doc_handle_cmd, "Move-View", &doc_page);
1316 key_add(doc_handle_cmd, "doc:point", &doc_get_point);
1318 key_add(doc_handle_cmd, "doc:notify-viewers", &doc_notify_viewers);
1319 key_add(doc_handle_cmd, "Notify:Close", &doc_notify_close);
1320 key_add(doc_handle_cmd, "mark:moving", &doc_notify_moving);
1321 key_add(doc_handle_cmd, "Refresh:view", &doc_refresh_view);
1322 key_add(doc_handle_cmd, "Clone", &doc_clone);
1323 key_add(doc_handle_cmd, "Close", &doc_close);
1324 key_add(doc_handle_cmd, "doc:dup-point", &doc_dup_point);
1325 key_add(doc_handle_cmd, "Replace", &doc_replace);
1326 key_add(doc_handle_cmd, "get-attr", &doc_handle_get_attr);
1327 key_add(doc_handle_cmd, "Move-to", &doc_move_to);
1328 key_add(doc_handle_cmd, "Notify:clip", &doc_clip);
1329 key_add(doc_handle_cmd, "Abort", &doc_abort);
1331 key_add(doc_default_cmd, "doc:add-view", &doc_addview);
1332 key_add(doc_default_cmd, "doc:del-view", &doc_delview);
1333 key_add(doc_default_cmd, "Notify:Close", &doc_view_close);
1334 key_add(doc_default_cmd, "doc:vmark-get", &doc_vmarkget);
1335 key_add(doc_default_cmd, "doc:vmark-prev", &doc_vmarkprev);
1336 key_add(doc_default_cmd, "doc:vmark-new", &doc_vmarknew);
1337 key_add(doc_default_cmd, "get-attr", &doc_get_attr);
1338 key_add(doc_default_cmd, "doc:get-attr", &doc_doc_get_attr);
1339 key_add(doc_default_cmd, "doc:set-name", &doc_set_name);
1340 key_add(doc_default_cmd, "doc:destroy", &doc_do_destroy);
1341 key_add(doc_default_cmd, "doc:drop-cache", &doc_drop_cache);
1342 key_add(doc_default_cmd, "doc:closed", &doc_do_closed);
1343 key_add(doc_default_cmd, "doc:get-str", &doc_get_str);
1344 key_add(doc_default_cmd, "doc:get-bytes", &doc_get_str);
1345 key_add(doc_default_cmd, "doc:content", &doc_default_content);
1346 key_add(doc_default_cmd, "doc:content-bytes", &doc_default_content);
1347 key_add(doc_default_cmd, "doc:push-point", &doc_push_point);
1348 key_add(doc_default_cmd, "doc:pop-point", &doc_pop_point);
1349 key_add(doc_default_cmd, "doc:attach-view", &doc_attach_view);
1350 key_add(doc_default_cmd, "doc:get-doc", &doc_get_doc);
1351 key_add(doc_default_cmd, "Close", &doc_close_doc);
1353 key_add(doc_default_cmd, "doc:word", &doc_word);
1354 key_add(doc_default_cmd, "doc:WORD", &doc_WORD);
1355 key_add(doc_default_cmd, "doc:EOL", &doc_eol);
1356 key_add(doc_default_cmd, "doc:file", &doc_file);
1357 key_add(doc_default_cmd, "doc:expr", &doc_expr);
1358 key_add(doc_default_cmd, "doc:paragraph", &doc_para);
1360 key_add_prefix(doc_default_cmd, "doc:char-", &doc_insert_char);
1361 key_add_prefix(doc_default_cmd, "doc:request:",
1362 &doc_request_notify);
1363 key_add_prefix(doc_default_cmd, "doc:notify:", &doc_notify);
1364 key_add_prefix(doc_default_cmd, "doc:set:", &doc_set);
1365 key_add_prefix(doc_default_cmd, "doc:append:", &doc_append);
1368 static struct pane *doc_attach_assign(struct pane *parent safe, struct pane *doc safe)
1371 struct doc_data *dd;
1374 p = pane_register(parent, 0, &doc_handle.c);
1378 pane_damaged(p, DAMAGED_VIEW);
1385 if (call("doc:pop-point", doc, 0, m) <= 0)
1386 pane_notify("doc:notify-viewers", doc, 0, m);
1389 attr_set_str(&m->attrs, "render:interactive-point", "yes");
1391 pane_add_notify(p, doc, "Notify:Close");
1392 pane_add_notify(p, doc, "doc:notify-viewers");
1393 pane_add_notify(p, doc, "mark:moving");
1394 call("doc:notify:doc:revisit", doc, 0);
1399 static void simplify_path(const char *path safe, char *buf safe)
1401 /* Like readpath, but doesn't process symlinks,
1402 * so only "..", "." and extra '/' are handled
1403 * Assumes that 'path' starts with a '/'.
1409 for (p = path; *p; p = end) {
1411 end = strchrnul(p+1, '/');
1415 /* Extra '/' at end or in the middle, ignore */
1417 if (len == 2 && strstarts(p, "/.") )
1418 /* Ignore the dot */
1420 if (len == 3 && strstarts(p, "/..")) {
1421 /* strip last component of buf */
1422 while (b > buf && b[-1] != '/')
1428 /* Append component to buf */
1433 /* This is the only case where we allow a trailing '/' */
1440 struct pane *ed = ci->home;
1443 char *realname = NULL;
1446 int autoclose = ci->num2 & 1;
1447 int create_ok = ci->num2 & 4;
1448 int reload = ci->num2 & 8;
1449 int force_reload = ci->num2 & 16;
1450 int quiet = ci->num2 & 32;
1451 int only_existing = ci->num2 & 64;
1452 char pathbuf[PATH_MAX];
1458 /* fd < -1 mean a non-filesystem name */
1460 char *sl = NULL, *rp, *restore = NULL;
1462 /* First, make sure we have an absolute path, and
1465 if (ci->str[0] != '/') {
1466 char *c = getcwd(pathbuf, sizeof(pathbuf));
1468 name = strconcat(ed, c, "/", ci->str);
1469 simplify_path(name, pathbuf);
1470 name = strsave(ed, pathbuf);
1472 name = strsave(ed, ci->str);
1474 simplify_path(ci->str, pathbuf);
1475 name = strsave(ed, pathbuf);
1479 /* Now try to canonicalize directory part of name as realname */
1482 sl = strrchr(dir, '/');
1483 if (sl && sl[1] && strcmp(sl, "/.") != 0 && strcmp(sl, "/..") != 0) {
1484 /* Found a real basename */
1488 /* Cannot preserve basename in relative path */
1494 rp = realpath(dir, pathbuf);
1496 realname = strconcat(ed, rp, "/", sl);
1504 name = (char*)ci->str;
1508 fd = open(name, O_RDONLY);
1513 stb.st_mode = S_IFREG;
1517 p = call_ret(pane, "docs:byfd", ed, 0, NULL, name, fd);
1520 if (only_existing) {
1525 p = call_ret(pane, "global-multicall-open-doc-", ed,
1527 stb.st_mode & S_IFMT);
1535 call("doc:set:autoclose", p, 1);
1536 call("doc:load-file", p, 0, NULL, name, fd);
1537 call("global-multicall-doc:appeared-", p);
1540 n = pane_attr_get(p, "filename");
1541 if (n && strlen(n) > 1 && n[strlen(n)-1] == '/') {
1542 /* Make sure both end in '/' if either do */
1544 realname = strconcat(ed, realname, "/");
1545 name = strconcat(ed, name, "/");
1547 if (!quiet && n && realname && strcmp(n, realname) != 0)
1548 call("Message", ci->focus, 0, NULL,
1549 strconcat(ci->focus, "File ", realname, " and ", n,
1551 else if (!quiet && n && strcmp(n, name) != 0)
1552 call("Message", ci->focus, 0, NULL,
1553 strconcat(ci->focus, "File ", name, " and ", n,
1556 if (reload || force_reload)
1557 call("doc:load-file", p, force_reload?0:1, NULL, name,
1562 return comm_call(ci->comm2, "callback", p) ?: 1;
1565 DEF_CMD(doc_from_text)
1567 const char *name = ci->str;
1568 const char *text = ci->str2;
1571 p = call_ret(pane, "attach-doc-text", ci->focus);
1575 call("doc:set-name", p, 0, NULL, name);
1576 call("global-multicall-doc:appeared-", p);
1578 call("doc:replace", p, 1, NULL, text);
1579 return comm_call(ci->comm2, "callback", p) ?: 1;
1582 void doc_free(struct doc *d safe, struct pane *root safe)
1584 /* NOTE: this must be idempotent as both it can be called
1585 * twice, once from doc_default_cmd, once deliberately by the
1589 bool warned = False;
1591 for (i = 0; i < ARRAY_SIZE(d->recent_points); i++) {
1592 mark_free(d->recent_points[i]);
1593 d->recent_points[i] = NULL;
1595 for (i = 0; i < (unsigned int)d->nviews; i++)
1597 do_del_view(d, i, d->views[i].owner);
1598 unalloc_buf(d->views, sizeof(d->views[0]) * d->nviews, pane);
1601 while (!hlist_empty(&d->marks)) {
1602 struct mark *m = hlist_first_entry(&d->marks, struct mark, all);
1603 if (m->viewnum == MARK_UNGROUPED && m->mdata) {
1604 /* we cannot free this, so warn and discard */
1606 call("editor:notify:Message:broadcast",
1608 "WARNING mark with data not freed");
1609 LOG("WARNING Mark with data no freed");
1614 if (m->viewnum == MARK_POINT || m->viewnum == MARK_UNGROUPED)
1617 /* vmarks should have gone already */
1622 void doc_setup(struct pane *ed safe)
1624 call_comm("global-set-command", ed, &doc_open, 0, NULL, "doc:open");
1625 call_comm("global-set-command", ed, &doc_from_text, 0, NULL,
1627 if (!(void*)doc_default_cmd)