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(®ion)) != 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(®ion)) != 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