1 
2 
3 /* This version of microEmacs is based on the public domain C
4  * version written by Dave G. Conroy.
5  * The D programming language version is written by Walter Bright.
6  * http://www.digitalmars.com/d/
7  * This program is in the public domain.
8  */
9 
10 /*
11  * The functions in this file are a general set of line management utilities.
12  * They are the only routines that touch the text. They also touch the buffer
13  * and window structures, to make sure that the necessary updating gets done.
14  * There are routines in this file that handle the kill buffer too. It isn't
15  * here for any good reason.
16  *
17  * Note that this code only updates the dot and mark values in the window list.
18  * Since all the code acts on the current window, the buffer that we are
19  * editing must be being displayed, which means that "b_nwnd" is non zero,
20  * which means that the dot and mark values in the buffer headers are nonsense.
21  */
22 
23 module line;
24 
25 import core.memory;
26 import core.stdc..string;
27 
28 import std.utf;
29 
30 import ed;
31 import buffer;
32 import window;
33 import main;
34 import display;
35 import random;
36 
37 /*
38  * All text is kept in circularly linked lists of "LINE" structures. These
39  * begin at the header line (which is the blank line beyond the end of the
40  * buffer). This line is pointed to by the "BUFFER". Each line contains the
41  * number of bytes in the line (the "used" size), the size of the text array,
42  * and the text. The end of line is not stored as a byte; it's implied. Future
43  * additions will include update hints, and a list of marks into the line.
44  */
45 struct  LINE {
46         LINE *l_fp;             /* Link to the next line        */
47         LINE *l_bp;             /* Link to the previous line    */
48 	SyntaxState syntaxState; // state at the beginning of the line
49         char[] l_text;         /* A bunch of characters.       */
50 }
51 
52 LINE* lforw(LINE* lp) { return lp.l_fp; }
53 LINE* lback(LINE* lp) { return lp.l_bp; }
54 char lputc(LINE* lp, int n, char c) { return lp.l_text[n] = c; }
55 int llength(LINE* lp) { return cast(int)lp.l_text.length; }
56 char lgetc(LINE* lp, int n) { return lp.l_text[n]; }
57 
58 bool empty(LINE* lp, int n) { return lp == curbp.b_linep; }
59 int  front(LINE* lp, int n) { return n == llength(lp) ? '\n' : lgetc(lp, n); }
60 
61 void popFront(ref LINE* lp, ref int n)
62 {
63     if (n < llength(lp))
64         n += 1;
65     else
66     {
67 	lp = lforw(lp);
68 	n = 0;
69     }
70 }
71 
72 bool atFront(LINE* lp, int n) { return n == 0 && lback(lp) == curbp.b_linep; }
73 
74 void popBack(ref LINE* lp, ref int n)
75 {
76     if (n)
77 	n -= 1;
78     else
79     {
80 	lp = lback(lp);
81 	n = llength(lp);
82     }
83 }
84 
85 int peekBack(LINE* lp, int n)
86 {
87     popBack(lp, n);
88     return front(lp, n);
89 }
90 
91 /*
92  * This routine allocates a block of memory large enough to hold a LINE
93  * containing "used" characters. The block is always rounded up a bit. Return
94  * a pointer to the new block, or null if there isn't any memory left. Print a
95  * message in the message line if no space.
96  */
97 
98 LINE* line_realloc(LINE* lpold, int used)
99 {
100 	if (!lpold)
101 	    lpold = new LINE;
102 	lpold.l_text.length = used;
103         return lpold;
104 }
105 
106 /*
107  * Delete line "lp". Fix all of the links that might point at it (they are
108  * moved to offset 0 of the next line). Unlink the line from whatever buffer it
109  * might be in. Release the memory. The buffers are updated too; the magic
110  * conditions described in the above comments don't hold here.
111  */
112 void line_free(LINE* lp)
113 {
114 	foreach (wp; windows)
115 	{
116 	    if (wp.w_linep == lp)
117 		wp.w_linep = lp.l_fp;
118 	    if (wp.w_dotp  == lp) {
119 		wp.w_dotp  = lp.l_fp;
120 		wp.w_doto  = 0;
121 	    }
122 	    if (wp.w_markp == lp) {
123 		wp.w_markp = lp.l_fp;
124 		wp.w_marko = 0;
125 	    }
126 	}
127 	foreach (bp; buffers)
128 	{
129 assert(bp);
130 	    /* If there are windows on this buffer, the dot and mark	*/
131 	    /* values are nonsense.					*/
132 	    if (bp.b_nwnd == 0)	/* if no windows on this buffer	*/
133 	    {
134 		/* update dot or mark in the buffer	*/
135 		if (bp.b_dotp  == lp) {
136 			bp.b_dotp = lp.l_fp;
137 			bp.b_doto = 0;
138 		}
139 		if (bp.b_markp == lp) {
140 			bp.b_markp = lp.l_fp;
141 			bp.b_marko = 0;
142 		}
143 	    }
144         }
145         lp.l_bp.l_fp = lp.l_fp;
146         lp.l_fp.l_bp = lp.l_bp;
147         //delete lp;
148         core.memory.GC.free(lp);
149 }
150 
151 /*
152  * This routine gets called when a character is changed in place in the current
153  * buffer. It updates all of the required flags in the buffer and window
154  * system. The flag used is passed as an argument; if the buffer is being
155  * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
156  * mode line needs to be updated (the "*" has to be set).
157  */
158 void line_change(int flag)
159 {
160 	if (curwp.w_markp)			/* if marking		*/
161 	    curwp.w_flag |= WFMOVE;		/* so highlighting is updated */
162         if ((curbp.b_flag&(BFCHG|BFRDONLY)) == 0) /* First change, so     */
163 	{   flag |= WFMODE;			/* update mode lines	*/
164 	    curbp.b_flag |= BFCHG;
165         }
166 	foreach (wp; windows)
167 	{
168                 if (wp.w_bufp == curbp)
169 		{
170 		    wp.w_flag |= flag;
171 		    if (wp != curwp)
172 			wp.w_flag |= WFHARD;
173 		}
174         }
175 }
176 
177 /*
178  * Insert "n" copies of the character "c" at the current location of dot. In
179  * the easy case all that happens is the text is stored in the line. In the
180  * hard case, the line has to be reallocated. When the window list is updated,
181  * take special care; I screwed it up once. You always update dot in the
182  * current window. You update mark, and a dot in another window, if it is
183  * greater than the place where you did the insert. Return TRUE if all is
184  * well, and FALSE on errors.
185  */
186 int line_insert(int n, char c)
187 {
188         LINE   *lp1;
189         LINE   *lp2;
190         LINE   *lp3;
191         int    doto;
192 
193 	if (curbp.b_flag & BFRDONLY)		/* if buffer is read-only */
194 	    return FALSE;			/* error		*/
195         line_change(WFEDIT);
196         lp1 = curwp.w_dotp;                    /* Current line         */
197         if (lp1 == curbp.b_linep) {            /* At the end: special  */
198                 if (curwp.w_doto != 0) {
199                         mlwrite("bug: line_insert");
200                         return (FALSE);
201                 }
202 		lp2 = line_realloc(null, n);	/* Allocate new line    */
203                 lp3 = lp1.l_bp;                /* Previous line        */
204                 lp3.l_fp = lp2;                /* Link in              */
205                 lp2.l_fp = lp1;
206                 lp1.l_bp = lp2;
207                 lp2.l_bp = lp3;
208 		lp2.l_text[0 .. n] = c;
209                 curwp.w_dotp = lp2;
210                 curwp.w_doto = n;
211                 return (TRUE);
212         }
213         doto = curwp.w_doto;                   /* Save for later.      */
214 	lp2 = lp1;
215 	lp2.l_text.length = lp2.l_text.length + n;
216 
217 	memmove(lp2.l_text.ptr + doto + n,
218 		lp2.l_text.ptr + doto,
219 		(lp2.l_text.length - n - doto) * char.sizeof);
220 	if (n == 1)
221 	    lp2.l_text[doto] = c;
222 	else
223 	    lp2.l_text[doto .. doto + n] = c;
224 
225 	/* Update windows       */
226 	foreach (wp; windows)
227 	{   if (wp.w_linep == lp1)
228 		    wp.w_linep = lp2;
229 	    if (wp.w_dotp == lp1) {
230 		    wp.w_dotp = lp2;
231 		    if (wp==curwp || wp.w_doto>doto)
232 			    wp.w_doto += n;
233 	    }
234 	    if (wp.w_markp == lp1) {
235 		    wp.w_markp = lp2;
236 		    if (wp.w_marko > doto)
237 			    wp.w_marko += n;
238 	    }
239 	}
240 
241         return (TRUE);
242 }
243 
244 /***************************
245  * Same as line_insert(), but for overwrite mode.
246  */
247 
248 int line_overwrite(int n, char c)
249 {   int status = true;
250 
251     while (n-- > 0)
252     {	if (curwp.w_doto < llength(curwp.w_dotp))
253 	    status = random_forwdel(FALSE,1);
254 	if (status)
255 	    status = line_insert(1,c);
256 	if (!status)
257 	    break;
258     }
259     return status;
260 }
261 
262 /********************************************
263  * Insert a newline into the buffer at the current location of dot in the
264  * current window. The funny ass-backwards way it does things is not a botch;
265  * it just makes the last line in the file not a special case. Return TRUE if
266  * everything works out and FALSE on error (memory allocation failure). The
267  * update of dot and mark is a bit easier then in the above case, because the
268  * split forces more updating.
269  */
270 int line_newline()
271 {
272         LINE   *lp1;
273         LINE   *lp2;
274         int    doto;
275 
276 	if (curbp.b_flag & BFRDONLY)		/* if buffer is read-only */
277 	    return FALSE;			/* error		*/
278         lp1  = curwp.w_dotp;                   /* Get the address and  */
279         doto = curwp.w_doto;                   /* offset of "."        */
280 	lp2 = line_realloc(null,doto);		/* New first half line  */
281 	lp2.l_text[0 .. doto] = lp1.l_text[0 .. doto];
282 	memmove(lp1.l_text.ptr, lp1.l_text.ptr + doto, (lp1.l_text.length - doto) * char.sizeof);
283 	lp1.l_text.length = lp1.l_text.length - doto;
284 
285         lp2.l_bp = lp1.l_bp;
286         lp1.l_bp = lp2;
287         lp2.l_bp.l_fp = lp2;
288         lp2.l_fp = lp1;
289 
290 	foreach (wp; windows)
291 	{
292                 if (wp.w_linep == lp1)
293                         wp.w_linep = lp2;
294                 if (wp.w_dotp == lp1) {
295                         if (wp.w_doto < doto)
296                                 wp.w_dotp = lp2;
297                         else
298                                 wp.w_doto -= doto;
299                 }
300                 if (wp.w_markp == lp1) {
301                         if (wp.w_marko < doto)
302                                 wp.w_markp = lp2;
303                         else
304                                 wp.w_marko -= doto;
305                 }
306         }       
307         line_change(WFHARD);
308         return (TRUE);
309 }
310 
311 /*
312  * This function deletes "n" bytes, starting at dot. It understands how do deal
313  * with end of lines, etc. It returns TRUE if all of the characters were
314  * deleted, and FALSE if they were not (because dot ran into the end of the
315  * buffer). The "kflag" is TRUE if the text should be put in the kill buffer.
316  */
317 bool line_delete(int n, bool kflag)
318 {
319         LINE*  dotp;
320         int    doto;
321         int    chunk;
322 
323 	if (curbp.b_flag & BFRDONLY)		/* if buffer is read-only */
324 	    return FALSE;			/* error		*/
325         while (n != 0) {
326                 dotp = curwp.w_dotp;
327                 doto = curwp.w_doto;
328                 if (dotp == curbp.b_linep)     /* Hit end of buffer.   */
329                         return (FALSE);
330                 chunk = cast(int)dotp.l_text.length - doto;   /* Size of chunk.       */
331                 if (chunk > n)
332                         chunk = n;
333                 if (chunk == 0) {               /* End of line, merge.  */
334                         line_change(WFHARD);
335                         if (line_delnewline() == FALSE
336                         || (kflag!=FALSE && kill_appendchar('\n')==FALSE))
337                                 return (FALSE);
338                         --n;
339                         continue;
340                 }
341                 line_change(WFEDIT);
342                 if (kflag != FALSE) {           /* Kill?                */
343 		    if (!kill_appendstring(dotp.l_text[doto .. doto + chunk]))
344 			return FALSE;
345                 }
346 		memmove(dotp.l_text.ptr + doto,
347 			dotp.l_text.ptr + doto + chunk,
348 			(dotp.l_text.length - chunk - doto) * char.sizeof);
349 		dotp.l_text.length = dotp.l_text.length - chunk;
350 
351 		foreach (wp; windows)
352 		{
353                         if (wp.w_dotp==dotp && wp.w_doto>=doto) {
354                                 wp.w_doto -= chunk;
355                                 if (wp.w_doto < doto)
356                                         wp.w_doto = doto;
357                         }       
358                         if (wp.w_markp==dotp && wp.w_marko>=doto) {
359                                 wp.w_marko -= chunk;
360                                 if (wp.w_marko < doto)
361                                         wp.w_marko = doto;
362                         }
363                 }
364                 n -= chunk;
365         }
366         return (TRUE);
367 }
368 
369 /*
370  * Delete a newline. Join the current line with the next line. If the next line
371  * is the magic header line always return TRUE; merging the last line with the
372  * header line can be thought of as always being a successful operation, even
373  * if nothing is done, and this makes the kill buffer work "right". Easy cases
374  * can be done by shuffling data around. Hard cases require that lines be moved
375  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
376  * "line_delete" only.
377  */
378 bool line_delnewline()
379 {
380         LINE   *lp1;
381         LINE   *lp2;
382         LINE   *lp3;
383 	int	lp1used;
384 
385 	if (curbp.b_flag & BFRDONLY)		/* if buffer is read-only */
386 	    return FALSE;			/* error		*/
387         lp1 = curwp.w_dotp;
388         lp2 = lp1.l_fp;
389 	lp1used = cast(int)lp1.l_text.length;
390         if (lp2 == curbp.b_linep) {            /* At the buffer end.   */
391                 if (lp1used == 0)               /* Blank line.          */
392                         line_free(lp1);
393                 return (TRUE);
394         }
395 	lp3 = line_realloc(lp1, lp1used + cast(int)lp2.l_text.length);
396 	lp3.l_bp.l_fp = lp3;
397 
398 	memmove(lp3.l_text.ptr + lp1used, lp2.l_text.ptr, lp2.l_text.length * char.sizeof);
399         lp3.l_fp = lp2.l_fp;
400         lp3.l_fp.l_bp = lp3;
401 
402 	foreach (wp; windows)
403 	{
404                 if (wp.w_linep==lp1 || wp.w_linep==lp2)
405                         wp.w_linep = lp3;
406                 if (wp.w_dotp == lp1)
407                         wp.w_dotp  = lp3;
408                 else if (wp.w_dotp == lp2) {
409                         wp.w_dotp  = lp3;
410                         wp.w_doto += lp1used;
411                 }
412                 if (wp.w_markp == lp1)
413                         wp.w_markp  = lp3;
414                 else if (wp.w_markp == lp2) {
415                         wp.w_markp  = lp3;
416                         wp.w_marko += lp1used;
417                 }
418         }
419 
420 	//delete lp2.l_text;
421 	//delete lp2;
422         core.memory.GC.free(lp2.l_text.ptr);
423         core.memory.GC.free(lp2);
424 
425         return (TRUE);
426 }
427 
428 
429 /********************** KILL BUFFER STUFF *********************/
430 
431 struct killbuf_t
432 {
433     char[] buf;
434 }
435 
436 __gshared killbuf_t[4] killbuffer;
437 __gshared killbuf_t *kbp = &killbuffer[0];	/* current kill buffer	*/
438 
439 /************************************
440  * Set the current kill buffer to i.
441  */
442 
443 void kill_setbuffer(int i)
444 {
445     kbp = &killbuffer[i];
446 }
447 
448 void kill_toClipboard()
449 {
450     version (Windows)
451     {
452 	import console;
453 
454 	if (kbp == &killbuffer[0])
455 	    setClipboard(kbp.buf);
456     }
457 }
458 
459 /*
460  * Delete all of the text saved in the kill buffer. Called by commands when a
461  * new kill context is being created. The kill buffer array is released, just
462  * in case the buffer has grown to immense size. No errors.
463  */
464 void kill_freebuffer()
465 {
466     //delete kbp.buf;
467     core.memory.GC.free(kbp.buf.ptr);
468     kbp.buf = null;
469 }
470 
471 void kill_fromClipboard()
472 {
473     version (Windows)
474     {
475 	import console;
476 
477 	if (kbp == &killbuffer[0])
478 	{
479 	    auto s = getClipboard();
480 	    if (s)
481 	    {
482 		kill_freebuffer();
483 		kbp.buf = s;
484 	    }
485 	}
486     }
487 }
488 
489 /*
490  * Append a character to the kill buffer, enlarging the buffer if there isn't
491  * any room. Always grow the buffer in chunks, on the assumption that if you
492  * put something in the kill buffer you are going to put more stuff there too
493  * later. Return TRUE if all is well, and FALSE on errors.
494  */
495 bool kill_appendchar(char c)
496 {
497     kbp.buf ~= c;
498     return (TRUE);
499 }
500 
501 /********************************
502  * Append string to kill buffer.
503  */
504 
505 bool kill_appendstring(const char[] s)
506 {
507     kbp.buf ~= s;
508     return (TRUE);
509 }
510 
511 /*
512  * This function gets characters from the kill buffer. If the character index
513  * "n" is off the end, it returns "-1". This lets the caller just scan along
514  * until it gets a "-1" back.
515  */
516 int kill_remove(int n)
517 {
518 	return (n >= kbp.buf.length) ? -1 : kbp.buf[n];
519 }
520 
521 /********************************
522  * We're going to use at least size bytes, so make room for it.
523  * Returns:
524  *	FALSE	out of memory
525  */
526 
527 int kill_setsize(int size)
528 {
529     auto oldlength = kbp.buf.length;
530     kbp.buf.length = size;
531     kbp.buf.length = oldlength;
532     return TRUE;
533 }