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  * This file contains the command processing functions for a number of random
12  * commands. There is no functional grouping here, for sure.
13  */
14 
15 module random;
16 
17 import std..string;
18 
19 import ed;
20 import line;
21 import main;
22 import buffer;
23 import window;
24 import region;
25 import display;
26 import basic;
27 import terminal;
28 
29 int     tabsize;                        /* Tab size (0: use real tabs)  */
30 
31 /*****************************
32  * Set fill column to n if n was given, otherwise set to current column. 
33  */
34 
35 int random_setfillcol(bool f, int n)
36 {
37     fillcol = f ? n : getcol(curwp.w_dotp,curwp.w_doto);
38     return TRUE;
39 }
40 
41 /*
42  * Display the current position of the cursor, in origin 1 X-Y coordinates,
43  * the character that is under the cursor (in octal), and the fraction of the
44  * text that is before the cursor. The displayed column is not the current
45  * column, but the column that would be used on an infinite width display.
46  * Normally this is bound to "C-X =".
47  */
48 int random_showcpos(bool f, int n)
49 {
50         LINE   *clp;
51 	int     numchars;
52 	int     cbo;
53 	int    thischar;
54 	int    charatdot;
55 	int    ratio;
56 	int    col;
57 	int	thisline;
58 	int	numlines;
59 
60         clp = lforw(curbp.b_linep);            /* Grovel the data.     */
61         cbo = 0;
62         numchars = 0;
63 	numlines = 1;
64         for (;;) {
65 		/* if on the current dot, save the character at dot	*/
66 		/* and the character and line position of the dot	*/
67                 if (clp==curwp.w_dotp && cbo==curwp.w_doto) {
68 			thisline = numlines;
69                         thischar = numchars;
70                         if (cbo == llength(clp))
71                                 charatdot = '\n';
72                         else
73                                 charatdot = lgetc(clp, cbo);
74                 }
75                 if (cbo == llength(clp)) {
76                         if (clp == curbp.b_linep)
77                                 break;
78                         clp = lforw(clp);
79                         cbo = 0;
80 			numlines++;
81                 } else
82                         ++cbo;
83                 ++numchars;
84         }
85 	col = getcol(curwp.w_dotp,curwp.w_doto); /* Get real column	*/
86         ratio = 0;                              /* Ratio before dot.    */
87         if (numchars != 0)
88                 ratio = cast(int)((100*thischar) / numchars);
89         mlwrite("row=%d col=%d CH=0x%x .=%d (%d%% of %d) line %d of %d",
90                 currow+1, col+1, charatdot, thischar,
91 		ratio, numchars, thisline, numlines - 1);
92         return (TRUE);
93 }
94 
95 /*
96  * Twiddle the two characters on either side of dot. If dot is at the end of
97  * the line twiddle the two characters before it. Return with an error if dot
98  * is at the beginning of line; it seems to be a bit pointless to make this
99  * work. This fixes up a very common typo with a single stroke. Normally bound
100  * to "C-T". This always works within a line, so "WFEDIT" is good enough.
101  */
102 int random_twiddle(bool f, int n)
103 {
104         LINE   *dotp;
105         int    doto;
106         char    cl;
107         char    cr;
108 
109         dotp = curwp.w_dotp;
110         doto = curwp.w_doto;
111         if (doto==llength(dotp) && --doto<0)
112                 return (FALSE);
113         cr = lgetc(dotp, doto);
114         if (--doto < 0)
115                 return (FALSE);
116         cl = lgetc(dotp, doto);
117         lputc(dotp, doto+0, cr);
118         lputc(dotp, doto+1, cl);
119         line_change(WFEDIT);
120         return (TRUE);
121 }
122 
123 /*
124  * Quote the next character, and insert it into the buffer. All the characters
125  * are taken literally, with the exception of the newline, which always has
126  * its line splitting meaning. The character is always read, even if it is
127  * inserted 0 times, for regularity. Bound to "M-Q" (for me) and "C-Q" (for
128  * Rich, and only on terminals that don't need XON-XOFF).
129  */
130 int random_quote(bool f, int n)
131 {
132         int    s;
133         int    c;
134 
135         c = term.t_getchar();
136         if (c & ~0xFF || n < 0)
137                 return (FALSE);
138         if (n == 0)
139                 return (TRUE);
140         if (c == '\n') {
141                 do {
142                         s = line_newline();
143                 } while (s==TRUE && --n);
144                 return (s);
145         }
146         return (line_insert(n, cast(char)c));
147 }
148 
149 /*
150  * Set tab size if given non-default argument (n <> 1).  Otherwise, insert a
151  * tab into file.  If given argument, n, of zero, change to true tabs.
152  * If n > 1, simulate tab stop every n-characters using spaces. This has to be
153  * done in this slightly funny way because the tab (in ASCII) has been turned
154  * into "C-I" (in 10 bit code) already. Bound to "C-I".
155  */
156 int random_tab(bool f, int n)
157 {
158         if (n < 0)
159                 return (FALSE);
160         if (n == 0 || n > 1) {
161                 tabsize = n;
162                 return(TRUE);
163         }
164         if (! tabsize)
165                 return(line_insert(1, '\t'));
166         return(line_insert(tabsize - (getcol(curwp.w_dotp,curwp.w_doto) % tabsize), ' '));
167 }
168 
169 /*
170  * Set hardware tab size if given non-default argument (n <> 1).
171  */
172 int random_hardtab(bool f, int n)
173 {
174         if (n < 0)
175                 return (FALSE);
176         if (n == 0 || n > 1)
177 	    hardtabsize = n;
178 	else if (hardtabsize == 8)
179 	    hardtabsize = 4;
180 	else if (hardtabsize == 4)
181 	    hardtabsize = 8;
182 	display_recalc();
183 	return TRUE;
184 }
185 
186 /*
187  * Open up some blank space. The basic plan is to insert a bunch of newlines,
188  * and then back up over them. Everything is done by the subcommand
189  * procerssors. They even handle the looping. Normally this is bound to "C-O".
190  */
191 int random_openline(bool f, int n)
192 {
193         int    i;
194         int    s;
195 
196         if (n < 0)
197                 return (FALSE);
198         if (n == 0)
199                 return (TRUE);
200         i = n;                                  /* Insert newlines.     */
201         do {
202                 s = line_newline();
203 	} while (s==TRUE && --i);
204         if (s == TRUE)                          /* Then back up overtop */
205                 s = backchar(f, n);             /* of them all.         */
206         return (s);
207 }
208 
209 /*
210  * Insert a newline. Bound to "C-M". If you are at the end of the line and the
211  * next line is a blank line, just move into the blank line. This makes "C-O"
212  * and "C-X C-O" work nicely, and reduces the ammount of screen update that
213  * has to be done. This would not be as critical if screen update were a lot
214  * more efficient.
215  */
216 int random_newline(bool f, int n)
217 {
218         int nicol;
219         LINE   *lp;
220         int    s;
221 
222         if (n < 0)
223                 return (FALSE);
224         while (n--) {
225 		    if ((s=line_newline()) != TRUE)
226                         return (s);
227         }
228         return (TRUE);
229 }
230 
231 /*
232  * Delete blank lines around dot. What this command does depends if dot is
233  * sitting on a blank line. If dot is sitting on a blank line, this command
234  * deletes all the blank lines above and below the current line. If it is
235  * sitting on a non blank line then it deletes all of the blank lines after
236  * the line. Normally this command is bound to "C-X C-O". Any argument is
237  * ignored.
238  */
239 int random_deblank(bool f, int n)
240 {
241         LINE   *lp1;
242         LINE   *lp2;
243         int    nld;
244 
245         lp1 = curwp.w_dotp;
246         while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp.b_linep)
247                 lp1 = lp2;
248         lp2 = lp1;
249         nld = 0;
250         while ((lp2=lforw(lp2))!=curbp.b_linep && llength(lp2)==0)
251                 ++nld;
252         if (nld == 0)
253                 return (TRUE);
254         curwp.w_dotp = lforw(lp1);
255         curwp.w_doto = 0;
256         return (line_delete(nld,TRUE));
257 }
258 
259 /*
260  * Insert a newline, then enough tabs and spaces to duplicate the indentation
261  * of the previous line. Assumes tabs are every eight characters. Quite simple.
262  * Figure out the indentation of the current line. Insert a newline by calling
263  * the standard routine. Insert the indentation by inserting the right number
264  * of tabs and spaces. Return TRUE if all ok. Return FALSE if one of the
265  * subcomands failed. Normally bound to "C-J".
266  */
267 
268 /+
269 int random_indent(bool f, int n)
270 {
271         int    nicol;
272         int    c;
273         int    i;
274 
275         if (n < 0)
276                 return (FALSE);
277         while (n--) {
278                 nicol = 0;
279                 for (i=0; i<llength(curwp.w_dotp); ++i) {
280                         c = lgetc(curwp.w_dotp, i);
281                         if (c!=' ' && c!='\t')
282                                 break;
283                         if (c == '\t')
284                                 nicol |= 0x07;
285                         ++nicol;
286                 }
287                 if (line_newline() == FALSE
288                 || ((i=nicol/8)!=0 && line_insert(i, '\t')==FALSE)
289                 || ((i=nicol%8)!=0 && line_insert(i,  ' ')==FALSE))
290                         return (FALSE);
291         }
292         return (TRUE);
293 }
294 +/
295 
296 /*********************************
297  * Determine indentation level of current line.
298  * Increase it by 4.
299  * Step to next line.
300  */
301 
302 int random_incindent(bool f, int n)	{ return changeindent(f,n,4); }
303 int random_decindent(bool f, int n)	{ return changeindent(f,n,-4); }
304 
305 private int changeindent(bool f, int n, int val)
306 {
307         int    nicol;
308         int    c;
309         int    i;
310 	LINE*  dotpsave;
311 	int    dotosave;
312 
313         if (n < 0)
314 	    goto err;
315 	if (window_marking(curwp))
316 	{   REGION region;
317 	    int s;
318 
319 	    if ((s = getregion(&region)) != TRUE)
320 		return s;
321 	    dotpsave = curwp.w_dotp;
322 	    dotosave = curwp.w_doto;
323 	    curwp.w_dotp = region.r_linep;
324 	    curwp.w_doto = region.r_offset;
325 	    n = region.r_nlines;
326 	}
327         while (n--)
328 	{   int len;
329 
330 	    len = llength(curwp.w_dotp);
331 	    if (len)
332 	    {
333                 nicol = 0;
334 		for (i=0; i < len; ++i) {
335 		    c = lgetc(curwp.w_dotp, i);
336 		    if (c == '{' && i + 1 < len) /* } to balance things out */
337 		    {	int j;
338 
339 			/* Next non-white char after bracket is indented by 3 */
340 			for (j = i + 1; j < len; j++)
341 			{   c = lgetc(curwp.w_dotp,j);
342 			    if (c != ' ' && c != '\t')
343 				break;
344 			}
345 			curwp.w_doto = i + 1;
346 			line_delete(j - (i + 1),FALSE);
347 			auto absval = val < 0 ? -val : val;
348 			line_insert(absval - 1,' ');
349 			break;
350 		    }
351 		    if (c!=' ' && c!='\t')
352 			break;
353 		    if (c == '\t')
354 			nicol |= 0x07;
355 		    ++nicol;
356                 }
357 		curwp.w_doto = 0;	/* move to beginning of line	*/
358 		line_delete(i,FALSE);	/* delete existing blanks	*/
359 		nicol += val;
360 		if (nicol < 0)
361 		    nicol = 0;
362 		if (((i=nicol/8)!=0 && line_insert(i, '\t')==FALSE)
363 		 || ((i=nicol%8)!=0 && line_insert(i,  ' ')==FALSE))
364 		    goto err;
365 	    }
366 	    if (forwline(FALSE,1) == FALSE)
367 		goto err;
368         }
369 	if (window_marking(curwp))
370 	{
371 	    if (dotosave > llength(dotpsave))
372 		dotosave = llength(dotpsave);
373 	    curwp.w_dotp = dotpsave;
374 	    curwp.w_doto = dotosave;
375 	}
376         return (TRUE);
377 
378 err:
379 	return FALSE;
380 }
381 
382 
383 /*********************************
384  * Optimally tab a line or region.
385  * Step to next line.
386  */
387 
388 int random_opttab(bool f, int n)
389 {   
390     int    c;
391     int    i;
392     LINE *dotpsave;
393     int dotosave;
394 
395     if (n < 0)
396 	goto err;
397     if (window_marking(curwp))
398     {   REGION region;
399 	int s;
400 
401 	if ((s = getregion(&region)) != TRUE)
402 	    return s;
403 	dotpsave = curwp.w_dotp;
404 	dotosave = curwp.w_doto;
405 	curwp.w_dotp = region.r_linep;
406 	curwp.w_doto = region.r_offset;
407 	n = region.r_nlines;
408     }
409     while (n--)
410     {   
411 	int nspaces = 0;
412 	int nwhite = 0;
413 	int nicol = 0;			/* column number		*/
414 
415 	for (i=0; i < llength(curwp.w_dotp); i++)
416 	{   
417 	    c = lgetc(curwp.w_dotp, i);
418 	    switch (c)
419 	    {   
420 		case '\t':
421 		    nwhite++;
422 		    if (nspaces)
423 		    {	int ntabs;
424 
425 			i -= nspaces;
426 			nwhite -= nspaces;
427 			curwp.w_doto = i;
428 			line_delete(nspaces,FALSE);
429 			ntabs = nspaces >> 3;
430 			line_insert(ntabs,'\t');
431 			i += ntabs;
432 			nwhite += ntabs;
433 			nspaces = 0;
434 		    }
435 		    nicol = (nicol | 7) + 1;
436 		    break;
437 		default:
438 		    if (nspaces >= 2 && (nicol & 7) == 0)
439 		    {   i -= nspaces;
440 			curwp.w_doto = i;
441 			line_delete(nspaces,FALSE);
442 			line_insert((nspaces + 7) >> 3,'\t');
443 			i += (nspaces + 7) >> 3;
444 			nspaces = 0;
445 		    }
446 		    if (c == ' ')
447 		    {	nwhite++;
448 			nspaces++;
449 		    }
450 		    else
451 		    {	nwhite = 0;
452 			nspaces = 0;
453 		    }
454 		    nicol++;
455 		    break;
456 	    }
457 	}
458 
459 	/* Truncate any trailing spaces or tabs	*/
460 	if (nwhite)
461 	{   curwp.w_doto = i - nwhite;
462 	    line_delete(nwhite,FALSE);
463 	}
464 
465 	if (forwline(FALSE,1) == FALSE)		/* proceed to next line	*/
466 	    goto err;
467     }
468     if (window_marking(curwp))
469     {   
470 	if (dotosave > llength(dotpsave))
471 	    dotosave = llength(dotpsave);
472 	curwp.w_dotp = dotpsave;
473 	curwp.w_doto = dotosave;
474     }
475     return (TRUE);
476 
477 err:
478     return FALSE;
479 }
480 
481 
482 /*
483  * Delete forward. This is real easy, because the basic delete routine does
484  * all of the work. Watches for negative arguments, and does the right thing.
485  * If any argument is present, it kills rather than deletes, to prevent loss
486  * of text if typed with a big argument. Normally bound to "C-D".
487  */
488 
489 static dchar undelch;
490 
491 int random_forwdel(bool f, int n)
492 {
493         if (n < 0)
494                 return (random_backdel(f, -n));
495 	if (f) {                       /* Really a random_kill.       */
496                 if ((lastflag&CFKILL) == 0)
497                         kill_freebuffer();
498                 thisflag |= CFKILL;
499         }
500 	else if (curwp.w_doto == llength(curwp.w_dotp))
501 		undelch = '\n';
502 	else
503 		undelch = lgetc(curwp.w_dotp,curwp.w_doto);
504         return (line_delete(n, f));
505 }
506 
507 int random_undelchar(bool f, int n)
508 {
509 	if (undelch == '\n')
510 	    return random_newline(f, n);
511 	return line_insert(n,cast(char)undelch);
512 }
513 
514 /*
515  * Delete backwards. This is quite easy too, because it's all done with other
516  * functions.
517  * If marking, then cut the marked region.
518  * Just move the cursor back, and delete forwards. Like delete
519  * forward, this actually does a kill if presented with an argument.
520  * Bound to both "RUBOUT" and "C-H".
521  */
522 int random_backdel(bool f, int n)
523 {
524         int    s;
525 
526         if (n < 0)
527                 return (random_forwdel(f, -n));
528 	if (curwp.w_markp)			/* if marking		*/
529 		return region_kill(f,n);
530         if (f != FALSE) {                       /* Really a kill.       */
531                 if ((lastflag&CFKILL) == 0)
532                         kill_freebuffer();
533                 thisflag |= CFKILL;
534         }
535         if ((s=backchar(f, n)) == TRUE)
536                 s = line_delete(n, f);
537         return (s);
538 }
539 
540 /*
541  * Kill text. If called without an argument, it kills from dot to the end of
542  * the line, unless it is at the end of the line, when it kills the newline.
543  * If called with an argument of 0, it kills from the start of the line to dot.
544  * If called with a positive argument, it kills from dot forward over that
545  * number of newlines. If called with a negative argument it kills backwards
546  * that number of newlines. Normally bound to "C-K".
547  */
548 int random_kill(bool f, int n)
549 {
550         int    chunk;
551         LINE   *nextp;
552 
553         if ((lastflag&CFKILL) == 0)             /* Clear kill buffer if */
554                 kill_freebuffer();                      /* last wasn't a kill.  */
555         thisflag |= CFKILL;
556         if (f == FALSE) {
557                 chunk = llength(curwp.w_dotp)-curwp.w_doto;
558                 if (chunk == 0)
559                         chunk = 1;
560         } else if (n == 0) {
561                 chunk = curwp.w_doto;
562                 curwp.w_doto = 0;
563         } else if (n > 0) {
564                 chunk = llength(curwp.w_dotp)-curwp.w_doto+1;
565                 nextp = lforw(curwp.w_dotp);
566                 while (--n) {
567                         if (nextp == curbp.b_linep)
568                                 return (FALSE);
569                         chunk += llength(nextp)+1;
570                         nextp = lforw(nextp);
571                 }
572         } else {
573                 mlwrite("neg kill");
574                 return (FALSE);
575         }
576         return (line_delete(chunk, TRUE));
577 }
578 
579 /*
580  * Yank text back from the kill buffer. This is really easy. All of the work
581  * is done by the standard insert routines. All you do is run the loop, and
582  * check for errors. Bound to "C-Y". The blank lines are inserted with a call
583  * to "newline" instead of a call to "line_newline" so that the magic stuff that
584  * happens when you type a carriage return also happens when a carriage return
585  * is yanked back from the kill buffer.
586  */
587 int random_yank(bool f, int n)
588 {
589         int    c;
590         int    i;
591         extern   int    kused;
592 
593         if (n < 0)
594 	    goto err;
595         kill_fromClipboard();
596         while (n--)
597 	{
598 	    i = 0;
599 	    if (column_mode)
600 	    {	int col;
601 
602 		col = getcol(curwp.w_dotp,curwp.w_doto);
603 		while ((c=kill_remove(i)) >= 0)
604 		{
605 		    if (c == '\r')
606 		    {
607 		    }
608 		    else if (c == '\n')
609 		    {	int nspaces;
610 
611 			if (curwp.w_dotp == curbp.b_linep)
612 			{   if (random_newline(FALSE, 1) == FALSE)
613 				goto err;
614 			}
615 			else
616 			    curwp.w_dotp = lforw(curwp.w_dotp);
617 			curwp.w_flag |= WFHARD;
618 			curwp.w_doto = coltodoto(curwp.w_dotp,col);
619 			if (kill_remove(i + 1) >= 0)
620 			{
621 			    nspaces = col - getcol(curwp.w_dotp,curwp.w_doto);
622 			    while (nspaces--)
623 				if (!line_insert(1,' '))
624 				    goto err;
625 			}
626 		    }
627 		    else
628 		    {
629 			if (line_insert(1, cast(char)c) == FALSE)
630 			    goto err;
631 		    }
632 		    ++i;
633 		}
634 	    }
635 	    else
636 		while ((c=kill_remove(i)) >= 0)
637 		{
638 		    if (c == '\r')
639 		    {
640 		    }
641 		    else if (c == '\n') {
642 			if (random_newline(FALSE, 1) == FALSE)
643 			    goto err;
644 		    } else {
645 			if (line_insert(1, cast(char)c) == FALSE)
646 			    goto err;
647 		    }
648 		    ++i;
649 		}
650         }
651         return (TRUE);
652 
653 err:
654     return FALSE;
655 }
656