2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Generic text document.
7 * This allows for a file to be read in, and edited by creating
8 * a linked list of chunks of text - the original isn't changed.
9 * This means that pointers outside of the edit region are mostly
12 * Indefinite undo is kept as a record of changes to the list of chunks.
13 * New text is added to the end of a list of allocations.
17 * The text of a document is stored in a collection of allocations
18 * which are immutable once created. They are attached to the 'document'
19 * and freed only when the document is discard.
20 * The current state of the document is represented by a linked list of
21 * 'chunks' which each point to part of some allocation.
23 * Each chunk can have 'attributes' which add arbitrary annotations to
24 * ranges of text. Unlike the text itself, these are not immutable. Only
25 * the 'current' attributes are stored. It is assumed that following
26 * 'undo', the appropriate attributes can be re-computed. i.e. they are
27 * a cache. The owner can get notified of changes which imply that
28 * attributes may have been lost.
30 * When text is removed from a document, the 'chunk' is modified to
31 * reference less text. If the chunk becomes empty, it is removed
32 * from the list, but not freed - as it will be in the undo list.
33 * When text is added to a document a new chunk is created which
34 * points to the next free space in the latest allocation, and text is
35 * added there. If the text is being added to the end of a chunk and
36 * it already points to the end of the latest allocation, then no new
39 * Text is assumed to be UTF-8 encoded. This becomes relevant when adding
40 * a string to the document and it won't all fit in the current allocation.
41 * In that case we ensure the first byte that goes in the next allocation
42 * matches 0xxxxxxx or 11xxxxxx., not 10xxxxxx.
44 * Undo/redo information is stored as a list of edits. Each edit
45 * changes either the start or the end of a 'chunk'. When a chunk becomes
46 * empty it is removed from the chunk list. The 'prev' pointer is preserved
47 * so when an undo makes it non-empty, it knows where to be added back.
49 * A text always has a least one allocation which is created with the text.
50 * If the text is empty, there will not be any chunks though, so all text refs
51 * will point to NULL. The NULL chunk is at the end of the text.
52 * The ->txt pointer of a chunk never changes. It is set when the chunk
53 * is created and then only start and end are changed.
56 #define _GNU_SOURCE /* for asprintf */
68 /* A doc_ref is treated as a pointer to a chunk, and an offset
69 * from the start of 'txt'. So 'o' must be between c->start and
71 * A 'c' of NULL means end of file.
72 * The normalized form requires that 'o' does
73 * not point to the end of the chunk.
75 #define PRIVATE_DOC_REF
82 #define DOC_DATA_TYPE struct text
83 #define DOC_NEXT(d,m,r,b) text_next(d,r,b)
84 #define DOC_PREV(d,m,r,b) text_prev(d,r,b)
88 /* text is allocated is large blocks - possibly a whole
89 * file or some other large unit being added to a document.
90 * For small additions (normal typing) the default allocation
92 * When more is allocated than needed, extra can be added on to
93 * the end - 'free' is the next index with free space.
96 struct text_alloc *prev;
102 #define DEFAULT_SIZE ((int)(4096 - sizeof(struct text_alloc)))
103 #define MAX_SIZE ((int)((1<<20) - sizeof(struct text_alloc)))
105 /* The text document is a list of text_chunk.
106 * The 'txt' pointer is within the text[] of a text_alloc.
107 * 'start' and 'end' narrow that.
108 * Each alloc potentially is divided into multiple
109 * separate chunks which are never merged. The only
110 * chunk that can change size is the last one allocated,
111 * which may grow into the free space.
117 struct list_head lst;
118 struct attrset *attrs;
121 /* An 'edit' consists of one or more text_edit structs linked together.
122 * The 'first' text_edit in a group has 'first' set. So when popping
123 * off the 'undo' list, we pop until we find the 'first' one. When
124 * popping off the 'redo' list, we pop a first, then any following
126 * Each entry identifies a chunk. If 'at_start' is set, the 'len' is
127 * added to the 'start' pointer (subtracted for undo). Otherwise
128 * the len added to the end. If the resulting length is zero, the
129 * chunk is removed from the list.
131 * Each edit can have an altnext.
132 * For the undo list, this is an alternate redo to reflect a branching
134 * For the redo list, this is a second change that happened from the
135 * same starting point. If there is a third change, we insert a no-op
136 * edit so as to get an extra altnext.
137 * In the undo list, altnext is an alternate forward path.
138 * if alt_is_second, then we are currently on the second path, and after
139 * undoing it, will go up the first.
140 * if !alt_is_second, we are currently on the first path, and
141 * don't want to go back up the second (until we undo all the way to the
142 * start and try again).
145 struct text_chunk *target safe;
146 struct text_edit *next, *altnext;
149 bool alt_is_second:1;
150 signed int len:29; // bytes add, -ve for removed.
153 /* A text document is a document with allocations, a list
154 * of chunks, and some undo info.
159 struct text_alloc *alloc safe;
160 struct list_head text;
161 struct text_edit *undo, *redo;
162 /* If prev_edit is Redo then next edit is ->redo or ->undo->altnext
164 * If prev_edit is Undo, then next edit is ->undo->altnext or ->undo
165 * If prev_edit is AltUndo, then next edit is ->undo
167 enum { Redo, Undo, AltUndo } prev_edit;
170 char file_changed; /* '2' means it has changed, but
171 * we are editing anyway
173 char newfile; /* file doesn't exist yet */
174 bool autosave_exists;
177 const char *autosave_name;
178 struct text_edit *saved;
186 #include "core-pane.h"
188 static int text_advance_towards(struct text *t safe, struct doc_ref *ref safe,
189 struct doc_ref *target safe);
190 static int text_retreat_towards(struct text *t safe, struct doc_ref *ref safe,
191 struct doc_ref *target safe);
192 static bool text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
193 struct doc_ref *r2 safe);
194 static bool _text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
195 struct doc_ref *r2 safe);
196 static int text_locate(struct text *t safe, struct doc_ref *r safe,
197 struct doc_ref *dest safe);
198 static void text_check_consistent(struct text *t safe);
199 static void text_normalize(struct text *t safe, struct doc_ref *r safe);
200 static void text_cleanout(struct text *t safe);
201 static void text_add_str(struct text *t safe, struct mark *pm safe,
202 const char *str safe, off_t size,
203 bool *first_edit safe);
204 static void text_check_autosave(struct pane *p safe);
205 static bool check_readonly(const struct cmd_info *ci safe);
207 static MEMPOOL(text);
208 static MEMPOOL(undo);
209 static struct map *text_map;
211 static struct text_alloc *safe
212 text_new_alloc(struct text *t safe, int size)
214 struct text_alloc *new;
217 size += sizeof(struct text_alloc);
218 size = ((size-1) | 255) + 1;
219 new = alloc_buf(size, text);
220 new->prev = t->alloc;
222 new->size = size - sizeof(struct text_alloc);
227 static bool check_file_changed(struct pane *p safe)
229 struct text *t = p->doc_data;
233 /* '1' means it has changed, '2' means "but we don't care" */
234 return t->file_changed == 1;
237 if (stat(t->fname, &st) != 0) {
238 memset(&st, 0, sizeof(st));
242 if (st.st_ino != t->stat.st_ino ||
243 st.st_dev != t->stat.st_dev ||
244 st.st_mtime != t->stat.st_mtime ||
245 st.st_mtim.tv_nsec != t->stat.st_mtim.tv_nsec) {
247 call("doc:notify:doc:status-changed", p);
253 DEF_CMD(text_readonly)
255 struct text *t = ci->home->doc_data;
257 if (t->file_changed && !t->doc.readonly && ci->num)
259 /* Use default handling */
263 static const char *safe autosave_name(const char *name safe)
265 char *tempname = malloc(strlen(name) + 3 + 10);
269 strcpy(tempname, name);
270 base = strrchr(name, '/');
275 tbase = tempname + (base - name);
276 sprintf(tbase, "#%s#", base);
280 DEF_CMD(text_load_file)
283 const char *name = ci->str;
285 struct text_alloc *a;
286 struct text_chunk *c = NULL;
288 struct text *t = ci->home->doc_data;
290 if (t->saved != t->undo)
292 if (fd < 0 && (ci->num & 6) && t->fname) {
293 /* re-open existing file name */
295 fd = open(t->autosave_name, O_RDONLY);
297 fd = open(t->fname, O_RDONLY);
304 size = lseek(fd, 0, SEEK_END);
305 lseek(fd, 0, SEEK_SET);
309 if ((ci->num & 1) && t->fname && fd >= 0) {
313 if (stb.st_ino == t->stat.st_ino &&
314 stb.st_dev == t->stat.st_dev &&
315 stb.st_size == t->stat.st_size &&
316 stb.st_mtime == t->stat.st_mtime) {
327 a = text_new_alloc(t, size);
330 while (a->free < size &&
331 (len = read(fd, a->text + a->free, size - a->free)) > 0)
338 list_add(&c->lst, &t->text);
339 hlist_for_each_entry(m, &t->doc.marks, all) {
347 if (fstat(fd, &t->stat) < 0) {
349 memset(&t->stat, 0, sizeof(t->stat));
351 if (name != t->fname) {
353 free((void*)t->fname);
354 t->fname = strdup(name);
355 dname = strrchr(name, '/');
360 call("doc:set-name", ci->home, 0, NULL, dname);
362 if (!t->autosave_name)
363 t->autosave_name = autosave_name(name);
364 if (stat(t->autosave_name, &stb) == 0 &&
365 stb.st_mtime > t->stat.st_mtime)
366 t->autosave_exists = True;
369 /* restored from autoload, so nothing matches saved version */
373 /* Current state is 'saved' */
377 call("doc:notify:doc:status-changed", ci->home);
378 pane_notify("doc:replaced", ci->home);
389 DEF_CMD(text_insert_file)
391 struct text *t = ci->home->doc_data;
392 struct mark *pm = ci->mark, *early;
393 struct text_alloc *a;
398 bool status_changes = (t->undo == t->saved);
400 if (check_readonly(ci))
402 if (!pm || fd < 0 || fd == NO_NUMERIC)
404 size = lseek(fd, 0, SEEK_END);
405 lseek(fd, 0, SEEK_SET);
409 if (a->size - a->free < size)
410 a = text_new_alloc(t, size);
414 early = mark_dup(pm);
418 while (a->free < start + size &&
419 (len = read(fd, a->text + a->free, start + size - a->free)) > 0)
421 text_add_str(t, pm, a->text + start, size, &first);
423 text_check_consistent(t);
424 text_check_autosave(ci->home);
426 call("doc:notify:doc:status-changed", ci->home);
427 pane_notify("doc:replaced", ci->home, 0, early, NULL,
434 static bool do_text_output_file(struct pane *p safe, struct doc_ref *start,
435 struct doc_ref *end, int fd)
437 struct text *t = p->doc_data;
438 struct text_chunk *c;
445 c = list_first_entry_or_null(&t->text, struct text_chunk, lst);
447 list_for_each_entry_from(c, &t->text, lst) {
448 char *s = c->txt + c->start;
449 int ln = c->end - c->start;
450 if (end && end->c == c)
452 if (write(fd, s + offset, ln - offset) != ln - offset)
455 if (end && end->c == c)
463 static bool do_text_write_file(struct pane *p safe, struct doc_ref *start,
465 const char *fname safe)
467 /* Don't worry about links for now
468 * Create a temp file with #basename#~, write to that,
469 * copy mode across, fsync and then rename
471 struct text *t = p->doc_data;
472 char *tempname = malloc(strlen(fname) + 3 + 10);
479 strcpy(tempname, fname);
480 base = strrchr(fname, '/');
485 tbase = tempname + (base - fname);
486 while (cnt < 20 && fd == -1) {
488 sprintf(tbase, "#%s#~%d", base, cnt);
490 sprintf(tbase, "#%s#~", base);
491 fd = open(tempname, O_WRONLY|O_CREAT|O_EXCL, 0666);
492 if (fd < 0 && errno != EEXIST)
499 if (!do_text_output_file(p, start, end, fd))
501 if (stat(fname, &stb) == 0 &&
502 S_ISREG(stb.st_mode))
503 /* Preserve modes, but not setuid */
504 fchmod(fd, stb.st_mode & 0777);
505 if (fname == t->fname && check_file_changed(p)) {
506 /* We are saving to a file which changed since we read it,
507 * so let's move that changed file to a backup
511 for (i = 1 ; i < 1000; i++) {
513 if (asprintf(&new, "%s~%d~", fname, i) < 0)
515 if (link(fname, new) == 0) {
524 if (rename(tempname, fname) < 0)
538 static void autosaves_record(struct pane *p safe, const char *path safe,
542 char *home = getenv("HOME");
543 char *dirname = getenv("EDLIB_AUTOSAVE");
545 bool changed = False;
550 dirname = strconcat(p, home, "/.edlib_autosave");
551 d = opendir(dirname);
555 if (mkdir(dirname, 0770) < 0)
557 d = opendir(dirname);
565 while ((de = readdir(d)) != NULL) {
569 char current[PATH_MAX];
571 if (de->d_name[0] == '.')
573 n = strtoul(de->d_name, &ep, 10);
574 if (!ep || ep == de->d_name || *ep != '\0')
578 len = readlinkat(dirfd(d), de->d_name,
579 current, sizeof(current));
580 if (len <= 0 || len >= (int)sizeof(current))
583 if (strcmp(current, path) == 0) {
585 unlinkat(dirfd(d), de->d_name, 0);
595 snprintf(nbuf, sizeof(nbuf), "%d", num);
596 symlinkat(path, dirfd(d), nbuf);
600 doc = call_ret(pane, "doc:open", p, -1, NULL, dirname);
602 pane_call(doc, "doc:notify:doc:revisit", p);
607 static void do_text_autosave(struct pane *p safe)
609 struct text *t = p->doc_data;
614 check_file_changed(p);
616 if (!t->autosave_name)
617 t->autosave_name = autosave_name(t->fname);
618 if (t->as.changes == 0) {
619 unlink(t->autosave_name);
620 t->autosave_exists = False;
621 autosaves_record(p, t->fname, False);
624 fd = open(t->autosave_name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
628 if (!do_text_output_file(p, NULL, NULL, fd)) {
630 unlink(t->autosave_name);
635 autosaves_record(p, t->fname, True);
638 DEF_CMD(text_autosave_delete)
640 struct pane *home = ci->home;
641 struct text *t = home->doc_data;
642 const char *name = ci->str;
645 if (!t->fname || !name)
648 if (!t->autosave_name)
649 t->autosave_name = autosave_name(t->fname);
651 if (strcmp(name, t->autosave_name) != 0 ||
652 unlink(t->autosave_name) < 0)
654 t->autosave_exists = False;
655 autosaves_record(home, t->fname, False);
660 DEF_CMD(text_autosave_tick)
662 struct pane *home = ci->home;
663 struct text *t = home->doc_data;
665 t->as.timer_started = 0;
668 if (t->as.changes == 0)
669 /* This will delete the file */
670 do_text_autosave(home);
671 if (time(NULL) - t->as.last_change >= 30)
672 do_text_autosave(home);
674 t->as.timer_started = 1;
675 call_comm("event:timer", home, &text_autosave_tick,
676 (t->as.last_change + 30 - time(NULL)) * 1000);
681 static void text_check_autosave(struct pane *p safe)
683 struct text *t = p->doc_data;
685 if (t->undo == t->saved)
689 t->as.last_change = time(NULL);
692 if (t->as.changes > 300 || t->as.changes == 0)
694 else if (!t->as.timer_started) {
695 t->as.timer_started = 1;
696 call_comm("event:timer", p, &text_autosave_tick,
701 DEF_CMD(text_save_file)
703 struct text *t = ci->home->doc_data;
706 int change_status = 0;
709 asprintf(&msg, "** No file name known for %s ***", t->doc.name);
712 ret = do_text_write_file(ci->home, NULL, NULL, t->fname);
714 asprintf(&msg, "Successfully wrote %s", t->fname);
720 asprintf(&msg, "*** Failed to write %s ***", t->fname);
722 call("Message", ci->focus, 0, NULL, msg);
725 call("doc:notify:doc:status-changed", ci->home);
726 text_check_autosave(ci->home);
732 DEF_CMD(text_write_file)
735 bool use_marks = ci->mark && ci->mark2;
738 ret = do_text_write_file(ci->home,
739 use_marks ? &ci->mark->ref: NULL,
740 use_marks ? &ci->mark2->ref: NULL,
742 return ret ? 1 : Efail;
744 if (ci->num >= 0 && ci->num != NO_NUMERIC) {
745 ret = do_text_output_file(ci->home,
746 use_marks ? &ci->mark->ref: NULL,
747 use_marks ? &ci->mark2->ref: NULL,
749 return ret ? 1 : Efail;
754 DEF_CMD(text_same_file)
756 struct text *t = ci->home->doc_data;
757 struct stat stb, stb2;
760 if (t->fname == NULL)
762 if (ci->str && strcmp(ci->str, t->fname) == 0)
765 if (fstat(fd, &stb) != 0)
767 } else if (ci->str) {
768 if (stat(ci->str, &stb) != 0)
772 if (t->stat.st_ino != stb.st_ino ||
773 t->stat.st_dev != stb.st_dev)
775 /* Must check file hasn't changed beneath us */
776 if (stat(t->fname, &stb2) != 0)
778 if (stb2.st_ino == stb.st_ino &&
779 stb2.st_dev == stb.st_dev)
784 static void text_add_edit(struct text *t safe, struct text_chunk *target safe,
785 bool *first safe, int at_start, int len)
792 if (t->saved == t->undo)
793 /* Must never merge undo entries across a save point */
797 /* Cannot add an edit before some redo edits, as they
798 * will get confused. We need to record the redo history
799 * here in the undo history, possibly allocating
800 * a nop edit (len == 0)
802 if (t->undo == NULL || t->undo->altnext != NULL) {
804 e->target = target; /* ignored */
807 e->len = 0; /* This is a no-op */
811 t->undo->altnext = t->redo;
812 t->undo->alt_is_second = 0;
815 /* factoring out t->undo here avoids a bug in smatch. */
817 if (e && e->len != 0 && e->len + len != 0 && !*first &&
818 e->target == target && e->at_start == at_start) {
819 /* This new edit can be merged with the previous one */
825 e->at_start = at_start;
830 e->alt_is_second = 0;
835 static void _text_add_str(struct text *t safe, struct doc_ref *pos safe,
836 const char *str safe, off_t len,
837 struct doc_ref *start, bool *first_edit safe)
839 /* Text is added to the end of the referenced chunk, or
840 * in new chunks which are added afterwards. This allows
841 * the caller to reliably updated any pointers to accommodate
843 * The added text has no attributes.
845 * 'pos' is moved to point to the end of the inserted text.
846 * 'start' is set to point to the start which may be the
847 * original 'pos', or may not if a chunk was inserted.
849 /* easy/common case first: pos is at the end of a chunk,
850 * which is the last chunk in the current allocation.
852 struct text_alloc *a = t->alloc;
863 if (pos->c && pos->o == pos->c->end &&
864 pos->c->txt + pos->o == a->text + a->free &&
865 str != a->text + a->free &&
866 (a->size - a->free >= len ||
867 (len2 = utf8_round_len(str, a->size - a->free)) > 0)) {
868 /* Some of this ('len2') can be added to the current chunk */
869 memcpy(a->text+a->free, str, len2);
874 text_add_edit(t, pos->c, first_edit, 0, len2);
879 /* Need a new chunk. Might need to split the current chunk first.
880 * Old chunk must be first to simplify updating of pointers */
881 if (pos->c == NULL || pos->o < pos->c->end) {
882 struct text_chunk *c;
884 if (pos->c == NULL || pos->o == pos->c->start) {
885 /* At the start of a chunk, so create a new one here */
886 c->txt = safe_cast NULL;
887 c->start = c->end = 0;
890 list_add_tail(&c->lst, &pos->c->lst);
892 list_add_tail(&c->lst, &t->text);
894 if (start && start->c == pos->c && start->o == pos->o) {
901 /* Not at the start, so we need to split at pos->o */
902 c->txt = pos->c->txt;
904 c->end = pos->c->end;
905 c->attrs = attr_copy_tail(pos->c->attrs, c->start);
906 pos->c->end = pos->o;
907 attr_trim(&pos->c->attrs, pos->c->end);
908 list_add(&c->lst, &pos->c->lst);
909 text_add_edit(t, c, first_edit, 0, c->end - c->start);
910 /* this implicitly truncates pos->c, so don't need
915 /* Make sure we have an empty chunk */
916 if (pos->c->end > pos->c->start) {
917 struct text_chunk *c;
919 c->start = c->end = 0;
921 list_add(&c->lst, &pos->c->lst);
922 if (start && start->c == pos->c && start->o == pos->o) {
929 /* Make sure we have some space in 'a' */
931 if (a->size - a->free < len &&
932 (len2 = utf8_round_len(str, a->size - a->free)) == 0) {
933 if (orig_len < 128 ||
934 t->alloc->size < DEFAULT_SIZE)
935 a = text_new_alloc(t, DEFAULT_SIZE);
936 else if (len > DEFAULT_SIZE && len > t->alloc->size)
937 a = text_new_alloc(t, ((len +256) | 4095) + 1 - 256);
938 else if (t->alloc->size * 2 < MAX_SIZE)
939 a = text_new_alloc(t, t->alloc->size * 2);
941 a = text_new_alloc(t, MAX_SIZE);
944 len2 = utf8_round_len(str, a->size);
946 pos->c->txt = a->text + a->free;
949 if (str != pos->c->txt)
950 memcpy(pos->c->txt, str, len2);
951 text_add_edit(t, pos->c, first_edit, 0, len2);
958 /* Text insertion, deletion, and undo can modify chunks which various
959 * marks point to - so those marks will need to be updated.
960 * Modification include splitting a chunk, inserting chunks,
961 * or deleting chunks and recombining chunks (for undo).
962 * Also reducing or increasing the range of a chunk.
963 * When a chunk is split, the original becomes the first part.
964 * So any mark pointing past the end of that original must be moved
966 * When a chunk is deleted, any mark pointing to a deleted chunk
967 * must be redirected to the (new) point of deletion.
968 * When a chunk is inserted, marks before the insertion mark must remain
969 * before the inserted chunk, marks after must remain after the insertion
971 * When two chunks are recombined it will appear that the second chunk
972 * was deleted. In this case though, references to the second chunk need
973 * to be repositioned in the first.
974 * When range is reduced, offset must be moved back into range.
975 * When range is increased, and this mark is after change, offset in this mark
976 * need to line up with changed point.
978 * So text_update_prior_after_change() is called on marks before the
979 * mark-of-change in reverse order until the function returns zero.
980 * If it finds a mark pointing to a deleted chunk, that mark changes to
981 * point the same place as the mark-of-change.
982 * If it finds a mark at, or immediately after, the mark-of-change,
983 * that mark is moved to point to the start of insert.
985 * Then text_update_following_after_change() is called on marks after
986 * the mark-of-change in order until that function returns zero.
987 * If a mark points outside the range of a chunk, the other half of the
988 * chunk is found (by walking forward) and the pointer is updated.
989 * If a deleted chunk is found, that mark is redirected to the mark-of-change.
990 * If a location at the start is found, it is move to the end.
993 static int text_update_prior_after_change(struct text *t safe,
994 struct doc_ref *pos safe,
995 struct doc_ref *spos safe,
996 struct doc_ref *epos safe)
1001 /* Was at the end, now must be at the start of the change */
1003 else if (pos->c->start >= pos->c->end)
1004 /* This chunk was deleted */
1006 else if (_text_ref_same(t, pos, epos))
1008 else if (pos->o < pos->c->start)
1009 /* Text deleted from under me */
1010 pos->o = pos->c->start;
1011 else if (pos->o > pos->c->end)
1012 /* Text deleted under me */
1013 pos->o = pos->c->end;
1014 else if (pos->o == pos->c->end)
1015 /* This mark is OK, but previous mark might be
1016 * at start of next chunk, so keep looking
1020 /* no insert or delete here, so all done */
1022 text_normalize(t, pos);
1026 static int text_update_following_after_change(struct text *t safe,
1027 struct doc_ref *pos safe,
1028 struct doc_ref *spos safe,
1029 struct doc_ref *epos safe)
1031 /* A change has happened between spos and epos. pos should be
1034 struct text_chunk *c;
1040 if (pos->c->start >= pos->c->end) {
1041 /* This chunk was deleted */
1043 pos->c->txt == epos->c->txt &&
1044 pos->o >= epos->c->start &&
1045 pos->o <= epos->c->end)
1046 /* chunks were rejoined */
1050 } else if (pos->c == epos->c &&
1052 /* Text inserted, need to push forward. */
1054 else if (pos->o < pos->c->start)
1055 /* must have been deleted... */
1056 pos->o = pos->c->start;
1057 else if (pos->o > pos->c->end) {
1058 /* This was split, or text was deleted off the end */
1062 list_for_each_entry_from(c, &t->text, lst) {
1063 if (c->txt == pos->c->txt &&
1064 c->start <= pos->o &&
1070 if (pos->o > pos->c->end)
1071 /* no split found, so just a delete */
1072 pos->o = pos->c->end;
1073 } else if (_text_ref_same(t, pos, spos))
1075 else if (pos->o == pos->c->start)
1076 /* This mark is OK, but next mark might be
1077 * at end of previous chunk, so keep looking
1081 /* This is beyond the change point and no deletion or split
1082 * happened here, so all done.
1085 text_normalize(t, pos);
1089 static void text_del(struct text *t safe, struct doc_ref *pos safe,
1090 unsigned int len, bool *first_edit safe)
1093 struct text_chunk *c = pos->c;
1095 /* nothing more to delete */
1097 if (pos->o == c->start &&
1098 len >= c->end - c->start) {
1099 /* The whole chunk is deleted, simply disconnect it */
1100 if (c != list_last_entry(&t->text,
1101 struct text_chunk, lst)) {
1102 pos->c = list_next_entry(c, lst);
1103 pos->o = pos->c->start;
1104 } else if (c != list_first_entry(&t->text,
1107 pos->c = list_prev_entry(c, lst);
1108 pos->o = pos->c->end;
1110 /* Deleted final chunk */
1114 __list_del(c->lst.prev, c->lst.next); /* no poison,
1117 attr_free(&c->attrs);
1118 text_add_edit(t, c, first_edit, 0, c->start - c->end);
1119 len -= c->end - c->start;
1120 /* make sure undo knows this is empty at not attached */
1122 } else if (pos->o == c->start) {
1123 /* If the start of the chunk is deleted, just update.
1124 * Note that len must be less that full size, else
1125 * previous branch would have been taken.
1130 s = attr_copy_tail(c->attrs, c->start);
1131 attr_free(&c->attrs);
1133 text_add_edit(t, c, first_edit, 1, len);
1135 } else if (c->end - pos->o <= len) {
1136 /* If the end of the chunk is deleted, just update
1137 * and move forward */
1138 int diff = c->end - pos->o;
1141 attr_trim(&c->attrs, c->end);
1142 text_add_edit(t, c, first_edit, 0, -diff);
1143 if (len && c != list_last_entry(&t->text,
1146 pos->c = list_next_entry(c, lst);
1147 pos->o = pos->c->start;
1151 /* must be deleting out of the middle of the chunk.
1152 * need to create new chunk for the 'after' bit.
1154 struct text_chunk *c2;
1157 c2->start = pos->o + len;
1160 c2->attrs = attr_copy_tail(c->attrs, c2->start);
1161 attr_trim(&c->attrs, c->end);
1162 list_add(&c2->lst, &c->lst);
1163 /* This implicitly trims c, so we only have len
1165 text_add_edit(t, c2, first_edit, 0,
1166 c2->end - c2->start);
1167 text_add_edit(t, c, first_edit, 0, -len);
1173 /* text_undo and text_redo:
1175 * The 'start' and 'end' reported identify the range changed. For a reversed
1176 * insertion they will be the same. If the undo results in the buffer being
1177 * empty, both start and end will point to a NULL chunk.
1178 * When undoing a split, both will be at the point of the split.
1180 static void text_undo(struct text *t safe, struct text_edit *e safe,
1181 struct doc_ref *start safe, struct doc_ref *end safe)
1186 if (e->target->end == e->target->start) {
1187 /* need to re-link */
1188 struct list_head *l = e->target->lst.prev;
1189 if (e->target->lst.next != l->next) abort();
1190 list_add(&e->target->lst, l);
1192 start->c = end->c = e->target;
1193 start->o = e->target->end; // incase was deletion at end
1194 end->o = e->target->start; // incase was deletion at start
1196 e->target->start -= e->len;
1198 /* was deletion, this is insertion */
1199 start->o = e->target->start;
1201 /* was insertion - not really possible */
1202 start->o = end->o = e->target->start;
1204 e->target->end -= e->len;
1206 /* Was insertion, now deleting */
1207 start->o = end->o = e->target->end;
1209 /* Was deletion, now inserting */
1210 end->o = e->target->end;
1212 if (e->target->start == e->target->end) {
1213 /* The undo deletes this chunk, so it must have been inserted,
1214 * either as new text or for a chunk-split.
1215 * If new text, leave start/end pointing just past the chunk.
1216 * if split, leave them at the point of splitting.
1218 if (e->target == list_last_entry(&t->text,
1219 struct text_chunk, lst)) {
1223 end->c = list_next_entry(e->target, lst);
1224 end->o = end->c->start;
1228 __list_del(e->target->lst.prev, e->target->lst.next);
1229 /* If this was created for a split, we need to extend the
1232 if (e->target != list_first_entry(&t->text,
1233 struct text_chunk, lst)) {
1234 struct text_chunk *c = list_prev_entry(e->target, lst);
1235 start->c = end->c = c;
1236 start->o = end->o = c->end;
1237 if (c->txt == e->target->txt &&
1238 c->end == e->target->start &&
1245 static void text_redo(struct text *t safe, struct text_edit *e safe,
1246 struct doc_ref *start safe, struct doc_ref *end safe)
1254 if (e->target->end == e->target->start) {
1255 /* need to re-link */
1256 struct list_head *l = e->target->lst.prev;
1257 if (e->target->lst.next != l->next) abort();
1258 list_add(&e->target->lst, l);
1259 /* If this is a split, need to truncate prior */
1260 if (e->target != list_first_entry(&t->text,
1261 struct text_chunk, lst)) {
1262 struct text_chunk *c = list_prev_entry(e->target, lst);
1263 if (c->txt == e->target->txt &&
1264 c->end > e->target->start) {
1265 c->end = e->target->start;
1270 start->c = end->c = e->target;
1271 end->o = e->target->start; // incase is insertion at start
1272 start->o = e->target->end; // incase inserting at end
1274 e->target->start += e->len;
1276 /* deletion at start */
1277 start->o = end->o = e->target->start;
1279 /* Insertion at start, not currently possible */
1280 start->o = e->target->start;
1282 e->target->end += e->len;
1284 start->o = end->o = e->target->start;
1285 else if (e->len > 0)
1286 /* Insertion at end */
1287 end->o = e->target->end;
1289 /* Deletion at end */
1290 start->o = end->o = e->target->end;
1292 if (e->target->start == e->target->end) {
1293 /* This chunk is deleted, so leave start/end pointing
1295 if (e->target->lst.next == &t->text) {
1299 end->c = list_next_entry(e->target, lst);
1300 end->o = end->c->start;
1304 __list_del(e->target->lst.prev, e->target->lst.next);
1308 static bool check_readonly(const struct cmd_info *ci safe)
1310 struct text *t = ci->home->doc_data;
1312 if (t->undo == t->saved &&
1313 check_file_changed(ci->home) &&
1315 call("doc:notify:doc:status-changed", ci->home);
1316 t->doc.readonly = 1;
1318 if (!t->doc.readonly)
1320 call("Message", ci->focus, 0, NULL, "Document is read-only");
1324 DEF_CMD(text_reundo)
1326 struct mark *m = ci->mark;
1327 struct doc_ref start, end;
1329 struct text_edit *ed = NULL;
1332 struct text *t = ci->home->doc_data;
1337 if (check_readonly(ci))
1341 /* New undo sequence - do redo first */
1342 t->prev_edit = Redo;
1344 status = (t->undo == t->saved);
1348 struct mark *early = NULL;
1353 if (t->prev_edit <= Redo && t->redo) {
1355 text_redo(t, ed, &start, &end);
1358 ed->alt_is_second = 0;
1359 t->prev_edit = Redo;
1361 last = t->redo == NULL || t->redo->first;
1362 } else if (t->prev_edit <= Undo &&
1364 t->undo->altnext && !t->undo->alt_is_second) {
1365 ed = t->undo->altnext;
1366 text_redo(t, ed, &start, &end);
1367 t->prev_edit = Redo;
1368 t->undo->altnext = t->redo;
1369 t->undo->alt_is_second = 1;
1372 ed->alt_is_second = 0;
1374 last = t->redo == NULL || t->redo->first;
1375 } else if (t->undo) {
1377 text_undo(t, ed, &start, &end);
1379 if (ed->alt_is_second) {
1380 t->prev_edit = AltUndo;
1381 ed->next = ed->altnext;
1382 ed->altnext = t->redo;
1384 t->prev_edit = Undo;
1394 /* That was just a no-op, keep going */
1397 text_normalize(t, &start);
1398 text_normalize(t, &end);
1401 where = text_locate(t, &m->ref, &end);
1403 /* Not nearby, look from the start */
1404 mark_reset(ci->home, m, 0);
1409 t->revising_marks = True;
1413 struct doc_ref tmp = m->ref;
1414 i = text_advance_towards(t, &tmp, &end);
1417 while ((m2 = mark_next(m)) != NULL &&
1418 m2->ref.c == tmp.c &&
1420 mark_to_mark_noref(m, m2);
1426 struct doc_ref tmp = m->ref;
1427 i = text_retreat_towards(t, &tmp, &end);
1430 while ((m2 = mark_prev(m)) != NULL &&
1431 m2->ref.c == tmp.c &&
1433 mark_to_mark_noref(m, m2);
1437 t->revising_marks = False;
1439 if (!_text_ref_same(t, &m->ref, &end))
1442 /* point is now at location of undo */
1445 hlist_for_each_entry_continue_reverse(m2, all)
1446 if (text_update_prior_after_change(t, &m2->ref,
1450 hlist_for_each_entry_continue(m2, all)
1451 if (text_update_following_after_change(t, &m2->ref,
1456 text_normalize(t, &m->ref);
1457 if (text_ref_same(t, &start, &end))
1460 early = mark_dup(m);
1461 mark_step(early, 0);
1462 /* There cannot be any mark between start and end,
1463 * so it is safe to assign 'ref' here.
1467 pane_notify("doc:replaced", ci->home,
1473 text_check_consistent(t);
1475 } while (ed && !last);
1477 text_check_consistent(t);
1479 if (status != (t->undo == t->saved))
1480 call("doc:notify:doc:status-changed", ci->home);
1481 text_check_autosave(ci->home);
1484 t->prev_edit = Redo;
1485 return ed ? 1 : Efalse;
1490 static int common_prefix(char *a, char *b, int l)
1499 /* Compare a string with the text.
1500 * Update the ref passed all matching chars.
1501 * Return length that was matched.
1503 static int text_str_cmp(struct text *t, struct doc_ref *r, char *s)
1505 struct text_chunk *c = r->c;
1512 list_for_each_entry_from(c, &t->text, lst) {
1518 l = common_prefix(c->txt+o, s, l);
1523 if (l == c->end - o)
1534 static void text_normalize(struct text *t safe, struct doc_ref *r safe)
1536 /* Adjust so at not at the end of a chunk - either ->o points
1537 * at a byte, or ->c is NULL.
1539 while (r->c && r->o >= r->c->end) {
1540 if (r->c->lst.next == &t->text) {
1544 r->c = list_next_entry(r->c, lst);
1550 static void text_denormalize(struct text *t safe, struct doc_ref *r safe)
1552 /* Ensure r->o is after some byte, or at start of file. */
1553 if (r->c && r->o > r->c->start)
1557 if (list_empty(&t->text))
1559 r->c = list_entry(t->text.prev, struct text_chunk, lst);
1563 if (r->c->lst.prev == &t->text)
1565 r->c = list_prev_entry(r->c, lst);
1569 static void text_add_str(struct text *t safe, struct mark *pm safe,
1570 const char *str safe, off_t size, bool *first safe)
1572 struct doc_ref start;
1575 text_denormalize(t, &pm->ref);
1576 _text_add_str(t, &pm->ref, str, size, &start, first);
1577 text_normalize(t, &pm->ref);
1578 for (m = mark_prev(pm);
1579 m && text_update_prior_after_change(t, &m->ref,
1583 for (m = mark_next(pm);
1584 m && text_update_following_after_change(t, &m->ref,
1590 static inline wint_t text_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
1592 struct text *t = p->doc_data;
1596 text_normalize(t, r);
1600 c = r->c->txt + r->o;
1602 ret = get_utf8(&c, r->c->txt + r->c->end);
1604 r->o = c - r->c->txt;
1606 ret = (unsigned char)r->c->txt[r->o++];
1607 text_normalize(t, r);
1611 static inline wint_t text_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
1613 struct text *t = p->doc_data;
1617 text_denormalize(t, r);
1618 if (list_empty(&t->text))
1620 if (r->c == NULL || r->o <= r->c->start)
1621 // assert (r->c->lst.prev == &t->text)
1627 r->o = r->c->start +
1628 utf8_round_len(r->c->txt+r->c->start,
1629 r->o - r->c->start - 1);
1630 c = r->c->txt + r->o;
1631 ret = get_utf8(&c, r->c->txt + r->c->end);
1636 ret = (unsigned char)r->c->txt[r->o];
1640 DEF_CMD(text_char_byte)
1642 return do_char_byte(ci);
1644 static bool _text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
1645 struct doc_ref *r2 safe)
1647 if (r1->c == r2->c) {
1648 return r1->o == r2->o;
1650 if (r1->c == NULL /*FIXME redundant*/ && r2->c != NULL) {
1651 if (list_empty(&t->text))
1653 return (r2->o == r2->c->end &&
1654 r2->c->lst.next == &t->text);
1656 if (r2->c == NULL /* FIXME redundant*/ && r1->c != NULL) {
1657 if (list_empty(&t->text))
1659 return (r1->o == r1->c->end &&
1660 r1->c->lst.next == &t->text);
1662 /* FIXME impossible */
1663 if (r1->c == NULL || r2->c == NULL) return False;
1665 if (r1->o == r1->c->end &&
1666 r2->o == r2->c->start &&
1667 list_next_entry(r1->c, lst) == r2->c)
1669 if (r1->o == r1->c->start &&
1670 r2->o == r2->c->end &&
1671 list_prev_entry(r1->c, lst) == r2->c)
1676 static bool text_ref_same(struct text *t safe, struct doc_ref *r1 safe,
1677 struct doc_ref *r2 safe)
1679 bool ret = _text_ref_same(t, r1, r2);
1680 ASSERT(ret == (r1->c == r2->c && r1->o == r2->o));
1684 DEF_LOOKUP_CMD(text_handle, text_map);
1691 p = doc_register(ci->home, &text_handle.c);
1695 t->alloc = safe_cast NULL;
1696 INIT_LIST_HEAD(&t->text);
1697 t->saved = t->undo = t->redo = NULL;
1698 t->prev_edit = Redo;
1700 t->file_changed = 0;
1703 t->as.timer_started = 0;
1704 t->as.last_change = 0;
1705 text_new_alloc(t, 0);
1707 return comm_call(ci->comm2, "callback:doc", p);
1712 if (ci->num2 != S_IFREG)
1713 return Efallthrough;
1714 return text_new_func(ci);
1717 static int count_bytes(struct text *t safe, struct mark *from, struct mark *to)
1719 struct text_chunk *c, *first, *last;
1720 int l = 0, head, tail;
1722 first = list_first_entry_or_null(&t->text, struct text_chunk, lst);
1724 if (from && from->ref.c) {
1725 first = from->ref.c;
1726 head = from->ref.o - first->start;
1730 if (to && to->ref.c) {
1732 tail = last->end - to->ref.o;
1735 list_for_each_entry_from(c, &t->text, lst) {
1736 l += c->end - c->start;
1747 DEF_CMD(text_content)
1749 struct mark *from = ci->mark, *to = ci->mark2;
1751 struct text *t = ci->home->doc_data;
1752 struct text_chunk *c, *first, *last;
1753 int bytes = strcmp(ci->key, "doc:content-bytes") == 0;
1754 int l = 0, head, tail;
1761 first = from->ref.c;
1763 head = from->ref.o - first->start;
1767 /* Calculate size so comm2 can pre-allocate */
1770 tail = last->end - to->ref.o;
1773 list_for_each_entry_from(c, &t->text, lst) {
1774 l += c->end - c->start;
1786 list_for_each_entry_from(c, &t->text, lst) {
1788 const char *s = c->txt + c->start;
1789 int ln = c->end - c->start;
1796 if (m->ref.c != c) {
1797 while ((m2 = mark_next(m)) &&
1798 m2->ref.c == m->ref.c)
1799 mark_to_mark(m, m2);
1801 m->ref.o = c->start;
1811 wc = get_utf8(&s, s+ln);
1816 while ((m2 = mark_next(m)) &&
1817 m2->ref.c == m->ref.c &&
1818 m2->ref.o <= s - c->txt)
1819 mark_to_mark(m, m2);
1820 m->ref.o = s - c->txt;
1821 text_normalize(t, &m->ref);
1824 /* Interpreted can see " unterminated" and know
1825 * than ->num2 is the length of ->str
1827 rv = comm_call(ci->comm2, "consume unterminated",
1829 wc, m, s, ln, NULL, NULL, size, 0);
1831 if (rv <= 0 || rv > ln + 1) {
1835 } else if (rv > 1) {
1836 /* consumed (some of) str */
1848 DEF_CMD(text_debug_mark)
1851 struct text_chunk *c;
1852 struct mark *m = ci->mark;
1854 if (!m || m->owner != ci->home || !ci->comm2)
1858 ret = strdup("M:FREED");
1860 ret = strdup("M:EOF");
1862 unsigned int len = c->end - c->start;
1863 unsigned int o = ci->mark->ref.o;
1865 if (o <= c->start + 4 || len <= 8) {
1868 asprintf(&ret, "M:(%.*s[%d])", len,
1869 c->txt + c->start, m->ref.o);
1871 len = c->end - m->ref.o;
1874 asprintf(&ret, "M:(%.4s..[%d]%.*s)",
1876 m->ref.o, len, c->txt + m->ref.o);
1879 comm_call(ci->comm2, "cb", ci->focus, 0, NULL, ret);
1884 DEF_CMD(text_val_marks)
1886 struct text *t = ci->home->doc_data;
1887 struct text_chunk *c;
1890 if (!ci->mark || !ci->mark2)
1893 if (t->revising_marks)
1896 if (ci->mark->ref.c == ci->mark2->ref.c) {
1897 if (ci->mark->ref.o < ci->mark2->ref.o)
1899 LOG("text_val_marks: same buf, bad offset: %u, %u",
1900 ci->mark->ref.o, ci->mark2->ref.o);
1904 list_for_each_entry(c, &t->text, lst) {
1905 if (ci->mark->ref.c == c)
1907 if (ci->mark2->ref.c == c) {
1910 LOG("text_val_marks: mark2.c found before mark1");
1914 if (ci->mark2->ref.c == NULL) {
1917 LOG("text_val_marks: mark2.c (NULL) found before mark1");
1921 LOG("text_val_marks: Neither mark found in chunk list");
1923 LOG("text_val_marks: mark2 not found in chunk list");
1927 DEF_CMD(text_set_ref)
1929 struct mark *m = ci->mark;
1930 struct text *t = ci->home->doc_data;
1934 mark_to_end(ci->home, m, ci->num != 1);
1935 if (list_empty(&t->text) || ci->num != 1) {
1939 m->ref.c = list_first_entry(&t->text, struct text_chunk, lst);
1940 m->ref.o = m->ref.c->start;
1945 static int text_advance_towards(struct text *t safe,
1946 struct doc_ref *ref safe,
1947 struct doc_ref *target safe)
1949 /* Move 'ref' towards 'target'.
1950 * If at end of chunk, step to next chunk, then
1951 * advance to 'target' or to end of chunk, whichever comes first.
1953 * 0 - reached end of text
1955 * 2 - on a new chunk, keep looking.
1957 if (ref->c && ref->o >= ref->c->end)
1958 text_normalize(t, ref);
1959 if (ref->c == target->c) {
1960 if (ref->o > target->o)
1961 /* will never find it */
1967 /* Reached EOF, haven't found */
1969 ref->o = ref->c->end;
1973 static int text_retreat_towards(struct text *t safe, struct doc_ref *ref safe,
1974 struct doc_ref *target safe)
1976 /* Move 'ref' towards 'target'.
1977 * If at start of chunk, step to previous chunk, then
1978 * retreat to 'target' or to start of chunk, whichever comes first.
1980 * 0 - reached start of text
1982 * 2 - on a new chunk, keep looking.
1985 if (ref->c != target->c && (!ref->c || ref->o <= ref->c->start))
1986 if (text_prev(safe_cast container_of(t, struct pane, doc_data[0]), ref, 1) == WEOF)
1989 if (ref->c == target->c) {
1992 if (target->o > ref->o)
1998 ref->o = ref->c->start;
2002 static int text_locate(struct text *t safe, struct doc_ref *r safe,
2003 struct doc_ref *dest safe)
2005 /* move back/forward a little from 'r' looking for 'dest'.
2006 * return 0 if not found, -1 if dest found before r.
2007 * return 1 if dest found after or at r.
2009 struct text_chunk *next, *prev;
2012 if (dest->c == NULL)
2017 if (dest->c == NULL)
2019 if (r->c == dest->c) {
2025 next = (r->c->lst.next == &t->text) ? NULL : list_next_entry(r->c, lst);
2026 prev = (r->c->lst.prev == &t->text) ? NULL : list_prev_entry(r->c, lst);
2027 if (next == dest->c)
2029 if (prev == dest->c)
2032 next = (next == NULL || next->lst.next == &t->text) ?
2033 NULL : list_next_entry(next, lst);
2034 prev = (prev == NULL || prev->lst.prev == &t->text) ?
2035 NULL : list_prev_entry(prev, lst);
2036 if (next == dest->c)
2038 if (prev == dest->c)
2043 static void check_allocated(struct text *t safe, char *buf safe, int len)
2045 struct text_alloc *ta = t->alloc;
2046 for (ta = t->alloc; ta; ta = ta->prev) {
2047 if (buf >= ta->text && buf+len <= ta->text + ta->free)
2053 static void text_ref_consistent(struct text *t safe, struct doc_ref *r safe,
2056 struct text_chunk *c;
2063 if (r->o >= r->c->end)
2065 if (r->o < r->c->start)
2067 list_for_each_entry(c, &t->text, lst) {
2068 if (r->c == c || *loops <= 0)
2075 static void text_check_consistent(struct text *t safe)
2077 /* make sure text is consistent, and abort if not.
2078 * - each chunk points to allocated space
2079 * - no two chunks overlap
2080 * - no chunks are empty
2081 * - every mark points to a valid chunk with valid offset
2082 * - all marks are in text order
2084 struct text_chunk *c;
2085 struct mark *m, *prev;
2086 struct doc *d = &t->doc;
2089 if (pane_no_consistency(safe_cast container_of(d, struct pane, doc)))
2092 list_for_each_entry(c, &t->text, lst) {
2093 check_allocated(t, c->txt, c->end);
2094 if (c->start >= c->end)
2099 list_for_each_entry(c, &t->text, lst) {
2100 struct text_chunk *c2;
2101 list_for_each_entry(c2, &t->text, lst) {
2107 if (c->start >= c2->end)
2109 if (c2->start >= c->end)
2117 /* This test is quadratic in the number of marks, so let's
2118 * give up rather then annoy the users.
2120 for (m = mark_first(d); m; m = mark_next(m))
2121 text_ref_consistent(t, &m->ref, &loops);
2124 for (m = mark_first(d); m; m = mark_next(m)) {
2126 struct doc_ref r = prev->ref;/* SMATCH Bug things prev
2129 struct doc_ref r2 = m->ref;
2130 text_normalize(t, &r2);
2131 while ((i = text_advance_towards(t, &r,
2141 doc_check_consistent(d);
2144 static void text_add_attrs(struct attrset **attrs safe,
2145 const char *new safe, int o)
2148 char *cpy = strdup(new);
2162 attr_set_str_key(attrs, k, v, o);
2168 DEF_CMD(text_replace)
2171 struct text *t = ci->home->doc_data;
2172 struct mark *pm = ci->mark2;
2173 struct mark *end = ci->mark;
2174 const char *str = ci->str;
2175 const char *newattrs = ci->str2;
2176 bool first = !ci->num2;
2177 struct mark *early = NULL;
2178 int status_change = 0;
2180 if (check_readonly(ci))
2184 /* Default to insert at end */
2185 pm = point_new(ci->home);
2188 mark_reset(ci->home, pm, 1);
2191 /* First delete, then insert */
2192 if (end && !text_ref_same(t, &pm->ref, &end->ref)) {
2193 struct mark *myend, *m;
2196 if (t->undo == t->saved)
2199 if (pm->seq >= end->seq) {
2200 myend = mark_dup(pm);
2201 mark_to_mark(pm, end);
2203 myend = mark_dup(end);
2204 /* pm is at the start, myend is at the end */
2205 l = count_bytes(t, pm, myend);
2207 text_del(t, &pm->ref, l, &first);
2208 text_normalize(t, &pm->ref);
2210 for (m = mark_prev(pm);
2211 m && text_update_prior_after_change(t, &m->ref,
2212 &pm->ref, &pm->ref);
2215 for (m = mark_next(pm);
2216 m && text_update_following_after_change(t, &m->ref,
2221 text_check_consistent(t);
2223 if (end && end != pm)
2226 early = mark_dup(pm);
2227 /* leave "early" at the start of the insertion, and
2228 * pm moves to the end - they are both currently at
2229 * the same location in the doc.
2231 mark_step(early, 0);
2234 if (t->undo == t->saved)
2237 text_add_str(t, pm, str, -1, &first);
2238 if (newattrs && early->ref.c)
2239 text_add_attrs(&early->ref.c->attrs, newattrs,
2241 text_check_consistent(t);
2244 text_check_autosave(ci->home);
2246 call("doc:notify:doc:status-changed", ci->home);
2247 pane_notify("doc:replaced", ci->home, 0, early, NULL,
2253 return first ? 1 : 2;
2256 static struct attrset *text_attrset(struct pane *p safe, struct mark *m safe,
2259 struct text_chunk *c;
2260 struct text *t = p->doc_data;
2270 /* End of chunk, need to look at next */
2271 if (c->lst.next == &t->text)
2273 c = list_next_entry(c, lst);
2281 DEF_CMD(text_doc_get_attr)
2283 struct mark *m = ci->mark;
2284 const char *attr = ci->str;
2291 a = text_attrset(ci->home, m, &o);
2292 val = attr_get_str(a, attr, o);
2293 if (!val && !ci->num2)
2294 return Efallthrough;
2295 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
2297 if (ci->num2 == 1) {
2298 const char *key = attr;
2299 int len = strlen(attr);
2300 while ((key = attr_get_next_key(a, key, o, &val)) != NULL &&
2301 strncmp(key, attr, len) == 0)
2302 comm_call(ci->comm2, "callback:get_attr", ci->focus,
2309 DEF_CMD(text_get_attr)
2311 struct text *t = ci->home->doc_data;
2312 const char *attr = ci->str;
2318 if ((val = attr_find(ci->home->attrs, attr)) != NULL)
2320 else if (strcmp(attr, "render-default") == 0)
2322 else if (strcmp(attr, "doc-type") == 0)
2324 else if (strcmp(attr, "doc:charset") == 0)
2326 else if (strcmp(attr, "filename") == 0)
2328 else if (strcmp(attr, "doc-file-changed") == 0)
2329 val = t->file_changed ? "yes" : "no";
2330 else if (strcmp(attr, "doc-modified") == 0)
2331 val = (t->saved != t->undo) ? "yes" : "no";
2332 else if (strcmp(attr, "autosave-exists") == 0)
2333 val = t->autosave_exists ? "yes" : "no";
2334 else if (strcmp(attr, "autosave-name") == 0) {
2335 if (!t->autosave_name && t->fname)
2336 t->autosave_name = autosave_name(t->fname);
2337 val = t->autosave_name;
2338 } else if (strcmp(attr, "is_backup") == 0) {
2339 const char *f = t->fname ?: "";
2340 const char *base = strrchr(f, '/');
2348 if (base[0] == '#' && base[l-1] == '#')
2350 else if (base[l-1] == '~' && strchr(base, '~') - base < l-1)
2354 } else if (strcmp(attr, "base-name") == 0) {
2355 char *f = strsave(ci->focus, t->fname ?: "");
2361 base = strrchr(f, '/');
2368 if (base[0] == '#' && base[l-1] == '#') {
2370 strcpy(base, base+1);
2371 } else if (base[l-1] == '~' && strchr(base, '~') - base < l-1) {
2372 while (l > 1 && base[l-2] != '~')
2378 return Efallthrough;
2380 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, NULL, val);
2384 DEF_CMD(text_set_attr)
2386 const char *attr = ci->str;
2387 const char *val = ci->str2;
2388 struct text_chunk *c, *c2;
2389 struct text *t = ci->home->doc_data;
2395 return Efallthrough;
2397 o = ci->mark->ref.o;
2398 c = ci->mark->ref.c;
2401 return Efallthrough;
2403 /* End of chunk, need to look at next */
2404 if (c->lst.next == &t->text)
2405 return Efallthrough;
2406 c = list_next_entry(c, lst);
2409 pane_notify("doc:replaced-attr", ci->home, 1, ci->mark, NULL,
2411 attr_set_str_key(&c->attrs, attr, val, o);
2412 if (!ci->mark2 || ci->mark2->seq <= ci->mark->seq)
2413 return Efallthrough;
2414 /* Delete all subsequent instances of attr */
2416 o2 = ci->mark2->ref.o;
2417 c2 = ci->mark2->ref.c;
2419 attr_del_all(&c->attrs, attr, o, c->end);
2420 c = list_next_entry(c, lst);
2421 o = c ? c->start : 0;
2424 attr_del_all(&c->attrs, attr, o, o2);
2425 return Efallthrough;
2428 DEF_CMD(text_modified)
2430 struct text *t = ci->home->doc_data;
2434 if (t->saved == t->undo)
2438 } else if (ci->num > 0)
2439 /* Set "is modified" */
2442 /* Clear "is modified" */
2444 text_check_autosave(ci->home);
2445 call("doc:notify:doc:status-changed", ci->home);
2449 DEF_CMD(text_revisited)
2451 struct text *t = ci->home->doc_data;
2454 /* Being buried, not visited */
2455 return Efallthrough;
2457 if (check_file_changed(ci->home) && t->saved == t->undo) {
2458 call("doc:load-file", ci->home, 2, NULL, NULL, -1);
2459 call("Message", ci->focus, 0, NULL, "File Reloaded");
2461 return Efallthrough;
2464 static void text_cleanout(struct text *t safe)
2466 struct text_alloc *ta;
2469 hlist_for_each_entry(m, &t->doc.marks, all) {
2474 while (!list_empty(&t->text)) {
2475 struct text_chunk *c = list_entry(t->text.next,
2476 struct text_chunk, lst);
2478 attr_free(&c->attrs);
2483 struct text_alloc *tmp = ta;
2485 unalloc_buf(tmp, sizeof(*tmp) + tmp->size, text);
2487 t->alloc = safe_cast NULL;
2489 struct text_edit *te = t->undo;
2491 if (te->altnext == NULL) {
2494 } else if (te->next == NULL) {
2495 t->undo = te->altnext;
2498 /* Make the ->altnext link shorted, until it
2501 t->undo = te->altnext;
2502 te->altnext = t->undo->next;
2507 struct text_edit *te = t->redo;
2509 if (te->altnext == NULL) {
2512 } else if (te->next == NULL) {
2513 t->redo = te->altnext;
2516 /* Make the ->altnext link shorted, until it
2519 t->redo = te->altnext;
2520 te->altnext = t->redo->next;
2526 DEF_CMD_CLOSED(text_destroy)
2528 struct text *t = ci->home->doc_data;
2531 free((void*)t->fname);
2533 free((void*)t->autosave_name);
2534 t->autosave_name = NULL;
2535 return Efallthrough;
2540 /* Clear the document, including undo/redo records
2541 * i.e. free all text
2543 struct text *t = ci->home->doc_data;
2547 text_new_alloc(t, 0);
2549 hlist_for_each_entry(m, &t->doc.marks, all) {
2553 pane_notify("doc:replaced", ci->home);
2558 void edlib_init(struct pane *ed safe)
2560 call_comm("global-set-command", ed, &text_new, 0, NULL,
2562 call_comm("global-set-command", ed, &text_new2, 0, NULL,
2565 text_map = key_alloc();
2567 key_add_chain(text_map, doc_default_cmd);
2568 key_add(text_map, "doc:load-file", &text_load_file);
2569 key_add(text_map, "doc:insert-file", &text_insert_file);
2570 key_add(text_map, "doc:same-file", &text_same_file);
2571 key_add(text_map, "doc:content", &text_content);
2572 key_add(text_map, "doc:content-bytes", &text_content);
2573 key_add(text_map, "doc:set-ref", &text_set_ref);
2574 key_add(text_map, "doc:save-file", &text_save_file);
2575 key_add(text_map, "doc:write-file", &text_write_file);
2576 key_add(text_map, "doc:reundo", &text_reundo);
2577 key_add(text_map, "doc:set-attr", &text_set_attr);
2578 key_add(text_map, "doc:get-attr", &text_doc_get_attr);
2579 key_add(text_map, "doc:replace", &text_replace);
2580 key_add(text_map, "doc:char", &text_char_byte);
2581 key_add(text_map, "doc:byte", &text_char_byte);
2582 key_add(text_map, "doc:modified", &text_modified);
2583 key_add(text_map, "doc:set:readonly", &text_readonly);
2584 key_add(text_map, "doc:notify:doc:revisit", &text_revisited);
2585 key_add(text_map, "doc:clear", &text_clear);
2586 key_add(text_map, "doc:autosave-delete", &text_autosave_delete);
2587 key_add(text_map, "doc:debug:mark", &text_debug_mark);
2588 key_add(text_map, "debug:validate-marks", &text_val_marks);
2590 key_add(text_map, "Close", &text_destroy);
2591 key_add(text_map, "get-attr", &text_get_attr);