|
|
1.1 ! root 1: /* Indentation functions. ! 2: Copyright (C) 1985, 1986, 1987, 1988, 1990 Free Software Foundation, Inc. ! 3: ! 4: This file is part of GNU Emacs. ! 5: ! 6: GNU Emacs is free software; you can redistribute it and/or modify ! 7: it under the terms of the GNU General Public License as published by ! 8: the Free Software Foundation; either version 1, or (at your option) ! 9: any later version. ! 10: ! 11: GNU Emacs is distributed in the hope that it will be useful, ! 12: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: GNU General Public License for more details. ! 15: ! 16: You should have received a copy of the GNU General Public License ! 17: along with GNU Emacs; see the file COPYING. If not, write to ! 18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 19: ! 20: ! 21: #include "config.h" ! 22: #include "lisp.h" ! 23: #include "buffer.h" ! 24: #include "indent.h" ! 25: #include "window.h" ! 26: #include "termchar.h" ! 27: #include "termopts.h" ! 28: ! 29: #define CR '\015' ! 30: ! 31: /* Indentation can insert tabs if this is non-zero; ! 32: otherwise always uses spaces */ ! 33: int indent_tabs_mode; ! 34: ! 35: #define min(a, b) ((a) < (b) ? (a) : (b)) ! 36: #define max(a, b) ((a) > (b) ? (a) : (b)) ! 37: ! 38: /* These three values memoize the current column to avoid recalculation */ ! 39: /* Some things in set last_known_column_point to -1 ! 40: to mark the memoized value as invalid */ ! 41: /* Last value returned by current_column */ ! 42: int last_known_column; ! 43: /* Value of point when current_column was called */ ! 44: int last_known_column_point; ! 45: /* Value of MODIFF when current_column was called */ ! 46: int last_known_column_modified; ! 47: ! 48: extern int minibuf_prompt_width; ! 49: ! 50: DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0, ! 51: "Return the horizontal position of point. Beginning of line is column 0.\n\ ! 52: This is calculated by adding together the widths of all the displayed\n\ ! 53: representations of the character between the start of the previous line\n\ ! 54: and point. (eg control characters will have a width of 2 or 4, tabs\n\ ! 55: will have a variable width)\n\ ! 56: Ignores finite width of screen, which means that this function may return\n\ ! 57: values greater than (screen-width).\n\ ! 58: Whether the line is visible (if `selective-display' is t) has no effect.") ! 59: () ! 60: { ! 61: Lisp_Object temp; ! 62: XFASTINT (temp) = current_column (); ! 63: return temp; ! 64: } ! 65: ! 66: /* Cancel any recorded value of the horizontal position. */ ! 67: ! 68: invalidate_current_column () ! 69: { ! 70: last_known_column_point = 0; ! 71: } ! 72: ! 73: int ! 74: current_column () ! 75: { ! 76: register int col; ! 77: register unsigned char *ptr, *stop, c; ! 78: register int tab_seen; ! 79: register int post_tab; ! 80: register int tab_width = XINT (current_buffer->tab_width); ! 81: int ctl_arrow = !NULL (current_buffer->ctl_arrow); ! 82: ! 83: if (point == last_known_column_point ! 84: && MODIFF == last_known_column_modified) ! 85: return last_known_column; ! 86: ! 87: /* Make a pointer for decrementing through the chars before point. */ ! 88: ptr = &FETCH_CHAR (point - 1) + 1; ! 89: /* Make a pointer to where consecutive chars leave off, ! 90: going backwards from point. */ ! 91: if (point == BEGV) ! 92: stop = ptr; ! 93: else if (point <= GPT || BEGV > GPT) ! 94: stop = BEGV_ADDR; ! 95: else ! 96: stop = GAP_END_ADDR; ! 97: ! 98: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 99: ! 100: col = 0, tab_seen = 0, post_tab = 0; ! 101: ! 102: while (1) ! 103: { ! 104: if (ptr == stop) ! 105: { ! 106: /* We stopped either for the beginning of the buffer ! 107: or for the gap. */ ! 108: if (ptr == BEGV_ADDR) ! 109: break; ! 110: /* It was the gap. Jump back over it. */ ! 111: stop = BEGV_ADDR; ! 112: ptr = GPT_ADDR; ! 113: /* Check whether that brings us to beginning of buffer. */ ! 114: if (BEGV >= GPT) break; ! 115: } ! 116: ! 117: c = *--ptr; ! 118: if (c >= 040 && c < 0177) ! 119: { ! 120: col++; ! 121: } ! 122: else if (c == '\n') ! 123: break; ! 124: else if (c == '\r' && EQ (current_buffer->selective_display, Qt)) ! 125: break; ! 126: else if (c == '\t') ! 127: { ! 128: if (tab_seen) ! 129: col = ((col + tab_width) / tab_width) * tab_width; ! 130: ! 131: post_tab += col; ! 132: col = 0; ! 133: tab_seen = 1; ! 134: } ! 135: else ! 136: col += (ctl_arrow && c < 0200) ? 2 : 4; ! 137: } ! 138: ! 139: if (tab_seen) ! 140: { ! 141: col = ((col + tab_width) / tab_width) * tab_width; ! 142: col += post_tab; ! 143: } ! 144: ! 145: last_known_column = col; ! 146: last_known_column_point = point; ! 147: last_known_column_modified = MODIFF; ! 148: ! 149: return col; ! 150: } ! 151: ! 152: ToCol (col) ! 153: int col; ! 154: { ! 155: register int fromcol = current_column (); ! 156: register int n; ! 157: register int tab_width = XINT (current_buffer->tab_width); ! 158: ! 159: if (fromcol > col) ! 160: return; ! 161: ! 162: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 163: ! 164: if (indent_tabs_mode) ! 165: { ! 166: n = col / tab_width - fromcol / tab_width; ! 167: if (n) ! 168: { ! 169: while (n-- > 0) ! 170: insert ("\t", 1); ! 171: ! 172: fromcol = (col / tab_width) * tab_width; ! 173: } ! 174: } ! 175: ! 176: while (fromcol < col) ! 177: { ! 178: insert (" ", min (8, col - fromcol)); ! 179: fromcol += min (8, col - fromcol); ! 180: } ! 181: ! 182: last_known_column = col; ! 183: last_known_column_point = point; ! 184: last_known_column_modified = MODIFF; ! 185: } ! 186: ! 187: DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ", ! 188: "Indent from point with tabs and spaces until COLUMN is reached.\n\ ! 189: Always do at least MIN spaces even if that goes past COLUMN;\n\ ! 190: by default, MIN is zero.") ! 191: (col, minimum) ! 192: Lisp_Object col, minimum; ! 193: { ! 194: int mincol; ! 195: register int fromcol; ! 196: register int tab_width = XINT (current_buffer->tab_width); ! 197: ! 198: CHECK_NUMBER (col, 0); ! 199: if (NULL (minimum)) ! 200: XFASTINT (minimum) = 0; ! 201: CHECK_NUMBER (minimum, 1); ! 202: ! 203: fromcol = current_column (); ! 204: mincol = fromcol + XINT (minimum); ! 205: if (mincol < XINT (col)) mincol = XINT (col); ! 206: ! 207: if (fromcol == mincol) ! 208: return make_number (fromcol); ! 209: ! 210: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 211: ! 212: if (indent_tabs_mode) ! 213: { ! 214: Lisp_Object n; ! 215: XFASTINT (n) = mincol / tab_width - fromcol / tab_width; ! 216: if (XFASTINT (n) != 0) ! 217: { ! 218: Finsert_char (make_number ('\t'), n); ! 219: ! 220: fromcol = (mincol / tab_width) * tab_width; ! 221: } ! 222: } ! 223: ! 224: XFASTINT (col) = mincol - fromcol; ! 225: Finsert_char (make_number (' '), col); ! 226: ! 227: last_known_column = mincol; ! 228: last_known_column_point = point; ! 229: last_known_column_modified = MODIFF; ! 230: ! 231: XSETINT (col, mincol); ! 232: return col; ! 233: } ! 234: ! 235: DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation, ! 236: 0, 0, 0, ! 237: "Return the indentation of the current line.\n\ ! 238: This is the horizontal position of the character\n\ ! 239: following any initial whitespace.") ! 240: () ! 241: { ! 242: Lisp_Object val; ! 243: ! 244: XFASTINT (val) = position_indentation (find_next_newline (point, -1)); ! 245: return val; ! 246: } ! 247: ! 248: position_indentation (pos) ! 249: register int pos; ! 250: { ! 251: register int column = 0; ! 252: register int tab_width = XINT (current_buffer->tab_width); ! 253: register unsigned char *p; ! 254: register unsigned char *stop; ! 255: ! 256: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 257: ! 258: stop = &FETCH_CHAR (BufferSafeCeiling (pos)) + 1; ! 259: p = &FETCH_CHAR (pos); ! 260: while (1) ! 261: { ! 262: while (p == stop) ! 263: { ! 264: if (pos == ZV) ! 265: return column; ! 266: pos += p - &FETCH_CHAR (pos); ! 267: p = &FETCH_CHAR (pos); ! 268: stop = &FETCH_CHAR (BufferSafeCeiling (pos)) + 1; ! 269: } ! 270: switch (*p++) ! 271: { ! 272: case ' ': ! 273: column++; ! 274: break; ! 275: case '\t': ! 276: column += tab_width - column % tab_width; ! 277: break; ! 278: default: ! 279: return column; ! 280: } ! 281: } ! 282: } ! 283: ! 284: DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 1, 0, ! 285: "Move point to column COLUMN in the current line.\n\ ! 286: COLUMN is calculated by adding together the widths of all the displayed\n\ ! 287: representations of the character between the start of the previous line\n\ ! 288: and point. (eg control characters will have a width of 2 or 4, tabs\n\ ! 289: will have a variable width)\n\ ! 290: Ignores finite width of screen, which means that this function may be\n\ ! 291: passed values greater than (screen-width)") ! 292: (column) ! 293: Lisp_Object column; ! 294: { ! 295: register int pos = point; ! 296: register int col = current_column (); ! 297: register int goal; ! 298: register int end = ZV; ! 299: register int tab_width = XINT (current_buffer->tab_width); ! 300: register int ctl_arrow = !NULL (current_buffer->ctl_arrow); ! 301: ! 302: Lisp_Object val; ! 303: ! 304: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 305: CHECK_NUMBER (column, 0); ! 306: goal = XINT (column); ! 307: if (col > goal) ! 308: { ! 309: pos = find_next_newline (pos, -1); ! 310: col = 0; ! 311: } ! 312: ! 313: while (col < goal && pos < end) ! 314: { ! 315: int c = FETCH_CHAR (pos); ! 316: if (c == '\n') ! 317: break; ! 318: if (c == '\r' && EQ (current_buffer->selective_display, Qt)) ! 319: break; ! 320: pos++; ! 321: col++; ! 322: if (c == '\t') ! 323: { ! 324: col += tab_width - 1; ! 325: col = col / tab_width * tab_width; ! 326: } ! 327: else if (ctl_arrow && (c < 040 || c == 0177)) ! 328: col++; ! 329: else if (c < 040 || c >= 0177) ! 330: col += 3; ! 331: } ! 332: ! 333: SET_PT (pos); ! 334: ! 335: last_known_column = col; ! 336: last_known_column_point = point; ! 337: last_known_column_modified = MODIFF; ! 338: ! 339: XFASTINT (val) = col; ! 340: return val; ! 341: } ! 342: ! 343: struct position val_compute_motion; ! 344: ! 345: struct position * ! 346: compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset) ! 347: int from, fromvpos, fromhpos, to, tovpos, tohpos; ! 348: register int width; ! 349: int hscroll, tab_offset; ! 350: { ! 351: register int hpos = fromhpos; ! 352: register int vpos = fromvpos; ! 353: ! 354: register int pos; ! 355: register int c; ! 356: register int tab_width = XFASTINT (current_buffer->tab_width); ! 357: register int ctl_arrow = !NULL (current_buffer->ctl_arrow); ! 358: int selective ! 359: = XTYPE (current_buffer->selective_display) == Lisp_Int ! 360: ? XINT (current_buffer->selective_display) ! 361: : !NULL (current_buffer->selective_display) ? -1 : 0; ! 362: int prev_vpos, prev_hpos; ! 363: ! 364: if (tab_width <= 0 || tab_width > 20) tab_width = 8; ! 365: for (pos = from; pos < to; pos++) ! 366: { ! 367: /* Stop if past the target screen position. */ ! 368: if (vpos > tovpos ! 369: || (vpos == tovpos && hpos >= tohpos)) ! 370: break; ! 371: ! 372: prev_vpos = vpos; ! 373: prev_hpos = hpos; ! 374: ! 375: c = FETCH_CHAR (pos); ! 376: if (c >= 040 && c < 0177) ! 377: hpos++; ! 378: else if (c == '\t') ! 379: { ! 380: hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0) ! 381: /* Add tab_width here to make sure positive. ! 382: hpos can be negative after continuation ! 383: but can't be less than -tab_width. */ ! 384: + tab_width) ! 385: % tab_width); ! 386: } ! 387: else if (c == '\n') ! 388: { ! 389: if (selective > 0 && position_indentation (pos + 1) >= selective) ! 390: { ! 391: /* Skip any number of invisible lines all at once */ ! 392: do ! 393: { ! 394: while (++pos < to && FETCH_CHAR (pos) != '\n'); ! 395: } ! 396: while (pos < to && position_indentation (pos + 1) >= selective); ! 397: pos--; ! 398: /* Allow for the " ..." that is displayed for them. */ ! 399: if (!NULL (current_buffer->selective_display_ellipses)) ! 400: { ! 401: hpos += 4; ! 402: if (hpos >= width) ! 403: hpos = width; ! 404: } ! 405: /* We have skipped the invis text, but not the newline after. */ ! 406: } ! 407: else ! 408: { ! 409: /* A visible line follows. ! 410: Skip this newline and advance to next line. */ ! 411: vpos++; ! 412: hpos = 0; ! 413: hpos -= hscroll; ! 414: if (hscroll > 0) hpos++; /* Count the ! on column 0 */ ! 415: tab_offset = 0; ! 416: } ! 417: } ! 418: else if (c == CR && selective < 0) ! 419: { ! 420: /* In selective display mode, ! 421: everything from a ^M to the end of the line is invisible */ ! 422: while (pos < to && FETCH_CHAR (pos) != '\n') pos++; ! 423: /* Stop *before* the real newline. */ ! 424: pos--; ! 425: /* Allow for the " ..." that is displayed for them. */ ! 426: if (!NULL (current_buffer->selective_display_ellipses)) ! 427: { ! 428: hpos += 4; ! 429: if (hpos >= width) ! 430: hpos = width; ! 431: } ! 432: } ! 433: else ! 434: hpos += (ctl_arrow && c < 0200) ? 2 : 4; ! 435: ! 436: /* Handle right margin. */ ! 437: if (hpos >= width ! 438: && (hpos > width ! 439: || (pos < ZV - 1 ! 440: && FETCH_CHAR (pos + 1) != '\n'))) ! 441: { ! 442: if (vpos > tovpos ! 443: || (vpos == tovpos && hpos >= tohpos)) ! 444: break; ! 445: if (hscroll ! 446: || (truncate_partial_width_windows ! 447: && width + 1 < screen_width) ! 448: || !NULL (current_buffer->truncate_lines)) ! 449: { ! 450: /* Truncating: skip to newline. */ ! 451: while (pos < to && FETCH_CHAR (pos) != '\n') pos++; ! 452: pos--; ! 453: hpos = width; ! 454: } ! 455: else ! 456: { ! 457: /* Continuing. */ ! 458: vpos++; ! 459: hpos -= width; ! 460: tab_offset += width; ! 461: } ! 462: ! 463: } ! 464: } ! 465: ! 466: val_compute_motion.bufpos = pos; ! 467: val_compute_motion.hpos = hpos; ! 468: val_compute_motion.vpos = vpos; ! 469: val_compute_motion.prevhpos = prev_hpos; ! 470: ! 471: /* Nonzero if have just continued a line */ ! 472: val_compute_motion.contin ! 473: = (pos != from ! 474: && (val_compute_motion.vpos != prev_vpos) ! 475: && c != '\n'); ! 476: ! 477: return &val_compute_motion; ! 478: } ! 479: ! 480: pos_tab_offset (w, pos) ! 481: struct window *w; ! 482: register int pos; ! 483: { ! 484: int opoint = point; ! 485: int col; ! 486: ! 487: if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n') ! 488: return 0; ! 489: SET_PT (pos); ! 490: col = current_column (); ! 491: SET_PT (opoint); ! 492: return (col ! 493: - (col % (XFASTINT (w->width) - 1 ! 494: - (XFASTINT (w->width) + XFASTINT (w->left) ! 495: != screen_width)))); ! 496: } ! 497: ! 498: /* start_hpos is the hpos of the first character of the buffer: ! 499: zero except for the minibuffer window, ! 500: where it is the width of the prompt. */ ! 501: ! 502: struct position val_vmotion; ! 503: ! 504: struct position * ! 505: vmotion (from, vtarget, width, hscroll, window) ! 506: register int from, vtarget, width; ! 507: int hscroll; ! 508: Lisp_Object window; ! 509: { ! 510: struct position pos; ! 511: /* vpos is cumulative vertical position, changed as from is changed */ ! 512: register int vpos = 0; ! 513: register int prevline; ! 514: register int first; ! 515: int lmargin = hscroll > 0 ? 1 - hscroll : 0; ! 516: int selective ! 517: = XTYPE (current_buffer->selective_display) == Lisp_Int ! 518: ? XINT (current_buffer->selective_display) ! 519: : !NULL (current_buffer->selective_display) ? -1 : 0; ! 520: int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0); ! 521: ! 522: retry: ! 523: if (vtarget > vpos) ! 524: { ! 525: /* Moving downward is simple, but must calculate from beg of line ! 526: to determine hpos of starting point */ ! 527: if (from > BEGV && FETCH_CHAR (from - 1) != '\n') ! 528: { ! 529: prevline = find_next_newline (from, -1); ! 530: while (selective > 0 ! 531: && prevline > BEGV ! 532: && position_indentation (prevline) >= selective) ! 533: prevline = find_next_newline (prevline - 1, -1); ! 534: pos = *compute_motion (prevline, 0, ! 535: lmargin + (prevline == 1 ? start_hpos : 0), ! 536: from, 1 << (INTBITS - 2), 0, ! 537: width, hscroll, 0); ! 538: } ! 539: else ! 540: { ! 541: pos.hpos = lmargin + (from == 1 ? start_hpos : 0); ! 542: pos.vpos = 0; ! 543: } ! 544: return compute_motion (from, vpos, pos.hpos, ! 545: ZV, vtarget, - (1 << (INTBITS - 2)), ! 546: width, hscroll, pos.vpos * width); ! 547: } ! 548: ! 549: /* To move upward, go a line at a time until ! 550: we have gone at least far enough */ ! 551: ! 552: first = 1; ! 553: ! 554: while ((vpos > vtarget || first) && from > BEGV) ! 555: { ! 556: prevline = from; ! 557: while (1) ! 558: { ! 559: prevline = find_next_newline (prevline - 1, -1); ! 560: if (prevline == BEGV ! 561: || selective <= 0 ! 562: || position_indentation (prevline) < selective) ! 563: break; ! 564: } ! 565: pos = *compute_motion (prevline, 0, ! 566: lmargin + (prevline == 1 ? start_hpos : 0), ! 567: from, 1 << (INTBITS - 2), 0, ! 568: width, hscroll, 0); ! 569: vpos -= pos.vpos; ! 570: first = 0; ! 571: from = prevline; ! 572: } ! 573: ! 574: /* If we made exactly the desired vertical distance, ! 575: or if we hit beginning of buffer, ! 576: return point found */ ! 577: if (vpos >= vtarget) ! 578: { ! 579: val_vmotion.bufpos = from; ! 580: val_vmotion.vpos = vpos; ! 581: val_vmotion.hpos = lmargin; ! 582: val_vmotion.contin = 0; ! 583: val_vmotion.prevhpos = 0; ! 584: return &val_vmotion; ! 585: } ! 586: ! 587: /* Otherwise find the correct spot by moving down */ ! 588: goto retry; ! 589: } ! 590: ! 591: DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0, ! 592: "Move to start of screen line LINES lines down.\n\ ! 593: If LINES is negative, this is moving up.\n\ ! 594: Sets point to position found; this may be start of line\n\ ! 595: or just the start of a continuation line.\n\ ! 596: Returns number of lines moved; may be closer to zero than LINES\n\ ! 597: if beginning or end of buffer was reached.") ! 598: (lines) ! 599: Lisp_Object lines; ! 600: { ! 601: struct position pos; ! 602: register struct window *w = XWINDOW (selected_window); ! 603: ! 604: CHECK_NUMBER (lines, 0); ! 605: ! 606: pos = *vmotion (point, XINT (lines), ! 607: XFASTINT (w->width) - 1 ! 608: - (XFASTINT (w->width) + XFASTINT (w->left) ! 609: != XFASTINT (XWINDOW (minibuf_window)->width)), ! 610: /* Not XFASTINT since perhaps could be negative */ ! 611: XINT (w->hscroll), selected_window); ! 612: ! 613: SET_PT (pos.bufpos); ! 614: return make_number (pos.vpos); ! 615: } ! 616: ! 617: syms_of_indent () ! 618: { ! 619: DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode, ! 620: "*Indentation can insert tabs if this is non-nil.\n\ ! 621: Setting this variable automatically makes it local to the current buffer."); ! 622: indent_tabs_mode = 1; ! 623: ! 624: defsubr (&Scurrent_indentation); ! 625: defsubr (&Sindent_to); ! 626: defsubr (&Scurrent_column); ! 627: defsubr (&Smove_to_column); ! 628: defsubr (&Svertical_motion); ! 629: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.