teco

Check-in [60afa1285f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Undo/redo bringup
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | master | trunk
Files: files | file ages | folders
SHA3-256:60afa1285f7a5a46e46d6950ca691702d9f93f2f8038be0c14c750702a43e076
User & Date: ajv-899-334-8894@vsta.org 2013-12-18 20:29:27
Context
2013-12-18
21:51
Fix compile errors, add :fu ops to control undo versioning. Refactor errors through common function. check-in: 52a94f34e9 user: ajv-899-334-8894@vsta.org tags: master, trunk
20:29
Undo/redo bringup check-in: 60afa1285f user: ajv-899-334-8894@vsta.org tags: master, trunk
2013-12-17
21:47
Add undo hook for search/replace check-in: 48baaa179d user: ajv-899-334-8894@vsta.org tags: master, trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to data.c.

49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
    "Y command suppressed",
    "Invalid W arg",
    "Numeric arg with FR",
    "Internal error",
    "EOF read from std input",
    "Invalid A arg",
    "Ambiguous file specification ",
    "System fork or pipe error"

};

/* declare global variables */
struct buffcell *freebuff = NULL;	/* buffcell free-list pointer */
struct buffcell *dly_freebuff = NULL;	/* delayed free-list pointer */
struct qp *freedcell = NULL;		/* cell free-list pointer */








|
>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    "Y command suppressed",
    "Invalid W arg",
    "Numeric arg with FR",
    "Internal error",
    "EOF read from std input",
    "Invalid A arg",
    "Ambiguous file specification ",
    "System fork or pipe error",
    "Invalid undo arg"
};

/* declare global variables */
struct buffcell *freebuff = NULL;	/* buffcell free-list pointer */
struct buffcell *dly_freebuff = NULL;	/* delayed free-list pointer */
struct qp *freedcell = NULL;		/* cell free-list pointer */

Changes to defs.h.

114
115
116
117
118
119
120

121
122
123
124
125
126
127
...
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238


239
240
241
242
243
244
245
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
...
424
425
426
427
428
429
430


431
432

433
434
#define E_IWA 39
#define E_NFR 40
#define E_INT 41
#define E_EFI 42
#define E_IAA 43
#define E_AMB 44
#define E_SYS 45


/* define names for window control registers */
#define WN_type win_data[0]
#define WN_width win_data[1]
#define WN_height win_data[2]
#define WN_seeall win_data[3]
#define WN_mark win_data[4]
................................................................................
    int count;		/* iteration count */
    int dflag;		/* definite iteration flag */
};

/* Undo record */
struct undo {
    struct undo *f, *b;	/* Forward/back list of changes */
    int op;		/* Type of change */

    struct buffcell *p;	/* Text associated with this change, if any */
    int dot;		/* Position at which this change occurred */
    int count;		/* Size of change */
    int grpid;		/* Undo/redo are grouped WRT CLI operations */
};
/* Values of undo "op" */
#define UNDO_DEL (1)	/* This text was deleted */
#define UNDO_INS (2)	/* This much text was inserted */



/* define expression stack entry */
struct exp_entry {
    int val1;		/* first value */
    int flag1;		/* nonzero if there is a first value */
    int val2;		/* second value (set by 'comma') */
    int flag2;		/* nonzero if there is one */
................................................................................
extern void pop_iteration(int);
extern int get_value(int);
extern int lines(int);
extern void insert1(void);
extern int fwdc(struct qp *), fwdcx(struct qp *);
extern void moveuntil(struct qp *from, struct qp *to,
    char c, int *n, int max, int trace);
extern void insert2(int);
extern int line_args(int, struct qp *);
extern void set_pointer(int, struct qp *);
extern void delete1(int);
extern void dly_free_blist(struct buffcell *);
extern void movenchars(struct qp *from, struct qp *to, int n);
extern void free_blist(struct buffcell *);
extern int gettty_nowait(void), gettty(void);
................................................................................
extern int do_eq(void), do_en(void);
extern void vt(int);
extern int do_fb(void);
extern int read_cmdstr(void);
extern void set_term_par(int lines, int cols);
extern void recalc_tsize(int);
extern int backc(struct qp *arg);


extern void rev_undo(void);
extern void undo_insert(int, int), undo_del(int, int);


#endif /* TE_DEFS_H */







>







 







|
>








>
>







 







|







 







>
>


>


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
#define E_IWA 39
#define E_NFR 40
#define E_INT 41
#define E_EFI 42
#define E_IAA 43
#define E_AMB 44
#define E_SYS 45
#define E_UND 46

/* define names for window control registers */
#define WN_type win_data[0]
#define WN_width win_data[1]
#define WN_height win_data[2]
#define WN_seeall win_data[3]
#define WN_mark win_data[4]
................................................................................
    int count;		/* iteration count */
    int dflag;		/* definite iteration flag */
};

/* Undo record */
struct undo {
    struct undo *f, *b;	/* Forward/back list of changes */
    short op;		/* Type of change */
    short flags;	/* undo usage state */
    struct buffcell *p;	/* Text associated with this change, if any */
    int dot;		/* Position at which this change occurred */
    int count;		/* Size of change */
    int grpid;		/* Undo/redo are grouped WRT CLI operations */
};
/* Values of undo "op" */
#define UNDO_DEL (1)	/* This text was deleted */
#define UNDO_INS (2)	/* This much text was inserted */
/* Bits in "flags" */
#define UNDOF_UNDONE (0x01)	/* This undo event is not in buffer */

/* define expression stack entry */
struct exp_entry {
    int val1;		/* first value */
    int flag1;		/* nonzero if there is a first value */
    int val2;		/* second value (set by 'comma') */
    int flag2;		/* nonzero if there is one */
................................................................................
extern void pop_iteration(int);
extern int get_value(int);
extern int lines(int);
extern void insert1(void);
extern int fwdc(struct qp *), fwdcx(struct qp *);
extern void moveuntil(struct qp *from, struct qp *to,
    char c, int *n, int max, int trace);
extern void insert2(int, int);
extern int line_args(int, struct qp *);
extern void set_pointer(int, struct qp *);
extern void delete1(int);
extern void dly_free_blist(struct buffcell *);
extern void movenchars(struct qp *from, struct qp *to, int n);
extern void free_blist(struct buffcell *);
extern int gettty_nowait(void), gettty(void);
................................................................................
extern int do_eq(void), do_en(void);
extern void vt(int);
extern int do_fb(void);
extern int read_cmdstr(void);
extern void set_term_par(int lines, int cols);
extern void recalc_tsize(int);
extern int backc(struct qp *arg);

/* Stuff from undo.c */
extern void rev_undo(void);
extern void undo_insert(int, int), undo_del(int, int);
extern void roll_back(void), roll_forward(void);

#endif /* TE_DEFS_H */

Changes to exec1.c.

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
...
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
...
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
            if (command == TAB) {
                ++ins_count;
            }

            /* indicate advance over that many chars */
            cptr.dot += ins_count;
        }
        insert2(ins_count);		/* finish insert */
        getcmdc(trace_sw);		/* flush terminating char */

        /* clear args */
        colonflag = atflag = esp->flag1 = esp->flag2 = 0;
        break;

    /* type text from text buffer */
................................................................................
                }
            } else {
                /*
                 * Insert copy of Q-reg text
                 */
                insert1();
                movenchars(&cc, &bb, qreg[mm].z);
                insert2(qreg[mm].z);
            }
        }
        colonflag = 0;
        break;

    /* q-register push and pop */
    case '[':
................................................................................
            }
            insert1();			/* start insert */
            cc.p = &t_bcell;	/* point cc to the temp cell */
            cc.c = 0;

            /* copy the char string */
            moveuntil(&cc, &bb, '\0', &ins_count, CELLSIZE-1, 0);
            insert2(ins_count);		/* finish the insert */
            esp->flag1 = 0;		/* consume argument */
            esp->op = OP_START;
        }
        break;

    case CTL('T'):			/* type or input character */
        /* type */







|







 







|







 







|







564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
...
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
...
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
            if (command == TAB) {
                ++ins_count;
            }

            /* indicate advance over that many chars */
            cptr.dot += ins_count;
        }
        insert2(ins_count, 1);		/* finish insert */
        getcmdc(trace_sw);		/* flush terminating char */

        /* clear args */
        colonflag = atflag = esp->flag1 = esp->flag2 = 0;
        break;

    /* type text from text buffer */
................................................................................
                }
            } else {
                /*
                 * Insert copy of Q-reg text
                 */
                insert1();
                movenchars(&cc, &bb, qreg[mm].z);
                insert2(qreg[mm].z, 1);
            }
        }
        colonflag = 0;
        break;

    /* q-register push and pop */
    case '[':
................................................................................
            }
            insert1();			/* start insert */
            cc.p = &t_bcell;	/* point cc to the temp cell */
            cc.c = 0;

            /* copy the char string */
            moveuntil(&cc, &bb, '\0', &ins_count, CELLSIZE-1, 0);
            insert2(ins_count, 1);	/* finish the insert */
            esp->flag1 = 0;		/* consume argument */
            esp->op = OP_START;
        }
        break;

    case CTL('T'):			/* type or input character */
        /* type */

Changes to exec2.c.

1378
1379
1380
1381
1382
1383
1384





















1385
1386
1387
1388
1389
1390
1391
        /* add # of chars inserted */
        z += ins_count;
        dot += ins_count;
        ctrl_s = -ins_count;		/* save string length */
        esp->flag1 = esp->flag2 = 0;	/* and consume arguments */
        esp->op = OP_START;
        break;






















    default:
        ERROR(E_IFC);
    }
}

/*







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
        /* add # of chars inserted */
        z += ins_count;
        dot += ins_count;
        ctrl_s = -ins_count;		/* save string length */
        esp->flag1 = esp->flag2 = 0;	/* and consume arguments */
        esp->op = OP_START;
        break;

    case 'u':		/* Control undo */

	/* Unexpected modifiers */
	if (esp->flag2 || colonflag) {
            ERROR(E_UND);
	}

	/* fu, -1fu -- roll back one level */
	if ((!esp->flag1) || (esp->val1 == -1)) {
	    roll_back();

	/* 1fu -- roll forward one previously undone level */
	} else if (esp->val1 == 1) {
	    roll_forward();

	/* Unknown */
	} else {
            ERROR(E_UND);
	}
	break;

    default:
        ERROR(E_IFC);
    }
}

/*

Changes to subs.c.

371
372
373
374
375
376
377
378
379
380

381

382
383
384
385
386
387
388
     * nchars is the number of chars before dot
     */
    movenchars(&aa, &bb, nchars);	/* copy cell up to dot */
}

/* count is the number of chars added */
void
insert2(int count)
{
    /* Record the change */

    undo_insert(dot, count);


    /* put the new cell where the old one was */
    aa.p->b->f = insert_p;
    insert_p->b = aa.p->b;
    insert_p = NULL;

    /* splice rest of buffer to end */







|


>
|
>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
     * nchars is the number of chars before dot
     */
    movenchars(&aa, &bb, nchars);	/* copy cell up to dot */
}

/* count is the number of chars added */
void
insert2(int count, int undoable)
{
    /* Record the change */
    if (undoable) {
	undo_insert(dot, count);
    }

    /* put the new cell where the old one was */
    aa.p->b->f = insert_p;
    insert_p->b = aa.p->b;
    insert_p = NULL;

    /* splice rest of buffer to end */

Changes to undo.c.

36
37
38
39
40
41
42





















43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
..
91
92
93
94
95
96
97


98
99
100
101
102
103
104
105
106
107
108
109















110
111
112
113
114
115
116
...
146
147
148
149
150
151
152










































































































































































    struct undo *u = (struct undo *)get_dcell();

    bzero(u, sizeof(struct undo));
    u->op = op;
    u->grpid = grpid;
    return(u);
}






















/*
 * add_undo()
 *	Put a "struct undo" entry onto the undo/redo chain
 */
static void
add_undo(struct undo *u)
{
    struct undo *t;

    ASSERT(u->f == NULL);
    ASSERT(u->b == NULL);

    /* First undo entry in list */
    if (undo_head == NULL) {
	ASSERT(undo == NULL);
	undo_head = undo = u;
	return;
    }
    ASSERT(undo != NULL);

    /* Trim off undo history older than NUNDO */
    for (t = undo_head; (grpid - t->grpid) > NUNDO; t = t->f) {
	;
    }
    if ((t != NULL) && (t != undo_head)) {
	t->b->f = NULL;
	t->b = NULL;
	free_dcell((struct qp *)undo_head);
	undo_head = t;
    }

    /* We've undone, and now are going forward with new changes */
    if (undo->f) {
	free_dcell((struct qp *)undo->f);
	undo->f = NULL;
    }

    /* Add this entry to the tail */
    undo->f = u;
    u->b = undo;
    undo = u;
}
................................................................................
 * Undo/redo is done at the granularity of transactions, by default
 *  the changes between one teco prompt and the next.  teco macros
 *  can also bump this value, useful for editor macros.
 */
void
rev_undo(void)
{


    /* No undo state, so no need */
    if (undo == NULL) {
	return;
    }

    /* Nothing has happened since the last rev, no need */
    if (undo->grpid != grpid) {
	return;
    }

    /* Ok, advance by one */
    grpid += 1;















}

/*
 * undo_insert()
 *	Add undo record for insertion of this much text
 */
void
................................................................................
    dest.c = 0;
    movenchars(&src, &dest, nchars);

    /* Record the change */
    u->p = bc;
    add_undo(u);
}

















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








<
<











<
<
<
<
<
<
<
<
<
<
<


<
|







 







>
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71


72
73
74
75
76
77
78
79
80
81
82











83
84

85
86
87
88
89
90
91
92
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    struct undo *u = (struct undo *)get_dcell();

    bzero(u, sizeof(struct undo));
    u->op = op;
    u->grpid = grpid;
    return(u);
}

/*
 * free_undo()
 *	Release undo header chain, along with any text storage
 */
static void
free_undo(struct undo *u)
{
    struct undo *u2;

    /* Scan chain, releasing buffcell storage */
    for (u2 = u; u2; u2 = u2->f) {
	if (u2->p) {
	    free_blist(u2->p);
	    u2->p = NULL;
	}
    }

    /* Now release the struct undo headers */
    free_dcell((struct qp *)u);
}

/*
 * add_undo()
 *	Put a "struct undo" entry onto the undo/redo chain
 */
static void
add_undo(struct undo *u)
{


    ASSERT(u->f == NULL);
    ASSERT(u->b == NULL);

    /* First undo entry in list */
    if (undo_head == NULL) {
	ASSERT(undo == NULL);
	undo_head = undo = u;
	return;
    }
    ASSERT(undo != NULL);












    /* We've undone, and now are going forward with new changes */
    if (undo->f) {

	free_undo(undo->f);
    }

    /* Add this entry to the tail */
    undo->f = u;
    u->b = undo;
    undo = u;
}
................................................................................
 * Undo/redo is done at the granularity of transactions, by default
 *  the changes between one teco prompt and the next.  teco macros
 *  can also bump this value, useful for editor macros.
 */
void
rev_undo(void)
{
    struct undo *t;

    /* No undo state, so no need */
    if (undo == NULL) {
	return;
    }

    /* Nothing has happened since the last rev, no need */
    if (undo->grpid != grpid) {
	return;
    }

    /* Ok, advance by one */
    grpid += 1;

    /* Trim off undo history older than NUNDO */
    for (t = undo_head; (grpid - t->grpid) > NUNDO; t = t->f) {
	/* Break off the search when we reach the current undo point */
	if (t == undo) {
	    break;
	}
    }
    if ((t != NULL) && (t != undo_head)) {
	t->b->f = NULL;
	t->b = NULL;
	free_undo(undo_head);
	undo_head = t;
    }

}

/*
 * undo_insert()
 *	Add undo record for insertion of this much text
 */
void
................................................................................
    dest.c = 0;
    movenchars(&src, &dest, nchars);

    /* Record the change */
    u->p = bc;
    add_undo(u);
}

/*
 * roll_back()
 *	Revert one generation of changes
 */
void
roll_back(void)
{
    int gid, uc;
    struct qp src, dest;
    struct buffcell *bc;

    /* Nothing to undo */
    if (undo == NULL) {
	return;
    }

    /*
     * Don't apply things already applied; this deals with the
     *  ambiguity of the head of the undo/undo_head list, where
     *  "undo" points to the first thing, and we don't know if it's
     *  been undone yet.
     */
    if (undo->flags & UNDOF_UNDONE) {
	return;
    }

    /* Roll back everything with this grpid */
    gid = undo->grpid;
    while (undo->grpid == gid) {

	/* Move to position of change */
	dot = undo->dot;
	if (dot < buff_mod) {
	    buff_mod = dot;
	}

	/* Put chars back into text buffer */
	uc = undo->count;
	if (undo->op == UNDO_DEL) {
	    cc.p = undo->p;
	    cc.c = 0;
	    insert1();
	    movenchars(&cc, &bb, uc);
	    insert2(uc, 0);

	/* Remove chars from text buffer */
	} else if (undo->op == UNDO_INS) {
	    /*
	     * Save this text first time we undo this insertion; this
	     *  is in case we are asked to "redo" the undo.
	     */
	    if (undo->p == NULL) {
		set_pointer(undo->dot, &src);
		bc = dest.p = get_bcell();
		dest.c = 0;
		movenchars(&src, &dest, uc);
		undo->p = bc;
	    }

	    /* Cribbed from delete1() */
	    set_pointer(dot, &dest);
	    set_pointer(dot + uc, &src);
	    movenchars(&src, &dest, z - (dot + uc));
	    free_blist(dest.p->f);
	    z -= uc;

	/* Shouldn't happen */
	} else {
	    ASSERT(0);
	}

	/* Ok, this effect is undone */
	undo->flags |= UNDOF_UNDONE;

	/* Continue backwards until head of chain */
	if (undo->b == NULL) {
	    break;
	}
	undo = undo->b;
    }
}

/*
 * roll_forward()
 *	Re-apply one generation of changes
 */
void
roll_forward(void)
{
    int gid, uc;
    struct qp src, dest;

    /* Nothing to redo */
    if (undo == NULL) {
	return;
    }

    /*
     * Is our current position one before the latest
     *  undone operation?
     */
    if ((undo->flags & UNDOF_UNDONE) == 0) {
	/*
	 * Nope, so nothing to do here
	 */
	if (undo->f == NULL) {
	    return;
	}
	if ((undo->f->flags & UNDOF_UNDONE) == 0) {
	    return;
	}

	/*
	 * Yes, so point at first of operations to be redone
	 */
	undo = undo->f;

    /*
     * If we're pointing directly at an un-done operation, then
     *  this should be the first in the chain.
     */
    } else {
	if (undo->b != NULL) {
	    ASSERT((undo->b->flags & UNDOF_UNDONE) == 0);
	}
    }

    /* Re-do everything with this grpid */
    gid = undo->grpid;
    while (undo->grpid == gid) {

	/* Move to position of change */
	dot = undo->dot;
	if (dot < buff_mod) {
	    buff_mod = dot;
	}

	/* Make the delete happen again */
	uc = undo->count;
	if (undo->op == UNDO_DEL) {

	    /* Cribbed from delete1() */
	    set_pointer(dot, &dest);
	    set_pointer(dot + uc, &src);
	    movenchars(&src, &dest, z - (dot + uc));
	    free_blist(dest.p->f);
	    z -= uc;

	/* Make the insertion happen again */
	} else if (undo->op == UNDO_INS) {
	    ASSERT(undo->p);
	    cc.p = undo->p;
	    cc.c = 0;
	    insert1();
	    movenchars(&cc, &bb, uc);
	    insert2(uc, 0);

	/* Shouldn't happen */
	} else {
	    ASSERT(0);
	}

	undo->flags &= ~(UNDOF_UNDONE);
	if (undo->f == NULL) {
	    break;
	}
	undo = undo->f;
    }
}