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