1: /* Indentation functions. 2: Copyright (C) 1985 Richard M. Stallman. 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: int indent_tabs_mode; 33: 34: #define min(a, b) ((a) < (b) ? (a) : (b)) 35: #define max(a, b) ((a) > (b) ? (a) : (b)) 36: 37: /* These three values memoize the current column to avoid recalculation */ 38: /* Some things in buflow.c set last_known_column_point to -1 39: to mark the memoized value as invalid */ 40: 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 bf_modified when current_column was called */ 46: int last_known_column_modified; 47: 48: extern int minibuf_prompt_width; 49: 50: DEFSIMPLE ("current-column", Fcurrent_column, Scurrent_column, 51: "Return the horizontal position of point. The left margin is column 0.\n\ 52: Ignores finite width of screen,", 53: Lisp_Int, XSETINT, current_column ()) 54: 55: current_column () 56: { 57: register int col; 58: register unsigned char *ptr, *stop, c; 59: register int tab_seen; 60: register int post_tab; 61: register int tab_width = XINT (bf_cur->tab_width); 62: int ctl_arrow = !NULL (bf_cur->ctl_arrow); 63: 64: if (point == last_known_column_point 65: && bf_modified == last_known_column_modified) 66: return last_known_column; 67: 68: ptr = &CharAt (point - 1) + 1; 69: stop = point <= bf_s1 + 1 ? bf_p1 + 1 : bf_p2 + bf_s1 + 1; 70: if (tab_width <= 0) tab_width = 1; 71: 72: col = 0, tab_seen = 0, post_tab = 0; 73: 74: while (1) 75: { 76: if (ptr == stop) 77: { 78: if (ptr == bf_p1 + 1) 79: break; 80: stop = bf_p1 + 1; 81: ptr = stop + bf_s1; 82: if (!bf_s1) break; 83: } 84: 85: c = *--ptr; 86: if (c >= 040 && c < 0177) 87: { 88: col++; 89: } 90: else if (c == '\n') 91: break; 92: else if (c == '\t') 93: { 94: if (tab_seen) 95: col = ((col + tab_width) / tab_width) * tab_width; 96: 97: post_tab += col; 98: col = 0; 99: tab_seen = 1; 100: } 101: else 102: col += (ctl_arrow && c < 0200) ? 2 : 4; 103: } 104: 105: if (tab_seen) 106: { 107: col = ((col + tab_width) / tab_width) * tab_width; 108: col += post_tab; 109: } 110: 111: last_known_column = col; 112: last_known_column_point = point; 113: last_known_column_modified = bf_modified; 114: 115: return col; 116: } 117: 118: ToCol (col) 119: int col; 120: { 121: register int fromcol = current_column (); 122: register int n; 123: register int tab_width = XINT (bf_cur->tab_width); 124: 125: if (fromcol > col) 126: return; 127: 128: if (tab_width <= 0) tab_width = 1; 129: 130: if (indent_tabs_mode) 131: { 132: n = col / tab_width - fromcol / tab_width; 133: if (n) 134: { 135: while (n-- > 0) 136: InsCStr ("\t", 1); 137: 138: fromcol = (col / tab_width) * tab_width; 139: } 140: } 141: 142: while (fromcol < col) 143: { 144: InsCStr (" ", min (8, col - fromcol)); 145: fromcol += min (8, col - fromcol); 146: } 147: 148: last_known_column = col; 149: last_known_column_point = point; 150: last_known_column_modified = bf_modified; 151: } 152: 153: DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "nIndent to column: ", 154: "Indent from point with tabs and spaces until COLUMN is reached.\n\ 155: Always do at least MIN spaces even if that goes past COLUMN;\n\ 156: by default, MIN is zero.") 157: (col, minimum) 158: Lisp_Object col, minimum; 159: { 160: int mincol; 161: 162: CHECK_NUMBER (col, 0); 163: if (NULL (minimum)) 164: XFASTINT (minimum) = 0; 165: CHECK_NUMBER (minimum, 1); 166: 167: mincol = current_column () + XINT (minimum); 168: if (mincol < XINT (col)) mincol = XINT (col); 169: 170: ToCol (mincol); 171: 172: XSETINT (col, mincol); 173: return col; 174: } 175: 176: DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation, 177: 0, 0, 0, 178: "Return the indentation of the current line.\n\ 179: This is the horizontal position of the character\n\ 180: following any initial whitespace.") 181: () 182: { 183: Lisp_Object val; 184: 185: XFASTINT (val) = position_indentation (ScanBf ('\n', point, -1)); 186: return val; 187: } 188: 189: position_indentation (pos) 190: register int pos; 191: { 192: register int col = 0; 193: register int c; 194: register int end = NumCharacters + 1; 195: register int tab_width = XINT (bf_cur->tab_width); 196: 197: if (tab_width <= 0) tab_width = 1; 198: 199: while (pos < end && 200: (c = CharAt (pos), 201: c == '\t' ? (col += tab_width - col % tab_width) 202: : (c == ' ' ? ++col : 0))) 203: pos++; 204: 205: return col; 206: } 207: 208: DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 1, 0, 209: "Move point to column COLUMN in the current line.\n\ 210: Does not change the text, only point.\n\ 211: Ignores finite width of screen.") 212: (column) 213: Lisp_Object column; 214: { 215: register int pos = point; 216: register int col = current_column (); 217: register int goal; 218: register int end = NumCharacters; 219: register int tab_width = XINT (bf_cur->tab_width); 220: register int ctl_arrow = !NULL (bf_cur->ctl_arrow); 221: 222: Lisp_Object val; 223: 224: if (tab_width <= 0) tab_width = 1; 225: CHECK_NUMBER (column, 0); 226: goal = XINT (column); 227: if (col > goal) 228: { 229: pos = ScanBf ('\n', pos, -1); 230: col = 0; 231: } 232: 233: while (col < goal && pos <= end) 234: { 235: char c = CharAt (pos); 236: if (c == '\n') 237: break; 238: pos++; 239: col++; 240: if (c == '\t') 241: { 242: col += tab_width - 1; 243: col = col / tab_width * tab_width; 244: } 245: else if (ctl_arrow && (c < 040 || c == 0177)) 246: col++; 247: else if (c < 040 || c >= 0177) 248: col += 3; 249: } 250: 251: SetPoint (pos); 252: 253: last_known_column = col; 254: last_known_column_point = point; 255: last_known_column_modified = bf_modified; 256: 257: XFASTINT (val) = col; 258: return val; 259: } 260: 261: struct position val_compute_motion; 262: 263: struct position * 264: compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset) 265: int from, fromvpos, fromhpos, to, tovpos, tohpos; 266: register int width; 267: int hscroll, tab_offset; 268: { 269: /* Note that cpos is CURRENT_VPOS << SHORTBITS + CURRENT_HPOS, 270: and the CURRENT_HPOS may be negative. Use these macros 271: to extract the hpos or the vpos from cpos or anything like it. */ 272: #ifdef celerity 273: /* On the Celerity, the usual definition fails to work. 274: This definition (which ought to be equivalent) does work. */ 275: #define HPOS(VAR) (((VAR) & 0x8000 ? 0xffff0000 : 0) | ((VAR) & 0xffff)) 276: #else 277: #define HPOS(VAR) (short) (VAR) 278: #endif 279: 280: #define VPOS(VAR) (((VAR) >> SHORTBITS) + (HPOS (VAR) < 0)) 281: 282: #ifndef TAHOE_REGISTER_BUG 283: register 284: #endif /* TAHOE_REGISTER_BUG */ 285: int cpos = fromhpos + (fromvpos << SHORTBITS); 286: register int target = tohpos + (tovpos << SHORTBITS); 287: register int pos; 288: register int c; 289: register int tab_width = XFASTINT (bf_cur->tab_width); 290: register int ctl_arrow = !NULL (bf_cur->ctl_arrow); 291: int selective 292: = XTYPE (bf_cur->selective_display) == Lisp_Int 293: ? XINT (bf_cur->selective_display) 294: : !NULL (bf_cur->selective_display) ? -1 : 0; 295: int prevpos; 296: struct position val; 297: 298: if (tab_width <= 0) tab_width = 1; 299: for (pos = from; pos < to && cpos < target; pos++) 300: { 301: prevpos = cpos; 302: c = CharAt (pos); 303: if (c >= 040 && c < 0177) 304: cpos++; 305: else if (c == '\t') 306: { 307: cpos += tab_width 308: - HPOS (cpos + tab_offset + hscroll - (hscroll > 0) 309: /* Add tab_width here to make sure positive. 310: cpos can be negative after continuation 311: but can't be less than -tab_width. */ 312: + tab_width) 313: % tab_width; 314: } 315: else if (c == '\n') 316: { 317: if (selective > 0 && position_indentation (pos + 1) >= selective) 318: { 319: /* Skip any number of invisible lines all at once */ 320: do 321: { 322: while (++pos < to && CharAt(pos) != '\n'); 323: } 324: while (selective > 0 && position_indentation (pos + 1) >= selective); 325: pos--; 326: /* Allow for the " ..." that is displayed for them. */ 327: cpos += 4; 328: if (HPOS (cpos) >= width) 329: cpos -= HPOS (cpos) - width; 330: } 331: else 332: cpos += (1 << SHORTBITS) - HPOS (cpos); 333: cpos -= hscroll; 334: if (hscroll > 0) cpos++; /* Count the ! on column 0 */ 335: tab_offset = 0; 336: } 337: else if (c == CR && selective < 0) 338: { 339: /* In selective display mode, 340: everything from a ^M to the end of the line is invisible */ 341: while (pos < to && CharAt(pos) != '\n') pos++; 342: pos--; 343: } 344: else 345: cpos += (ctl_arrow && c < 0200) ? 2 : 4; 346: 347: if (HPOS (cpos) >= width 348: && (HPOS (cpos) > width 349: || (pos < NumCharacters 350: && CharAt (pos + 1) != '\n'))) 351: { 352: if (cpos >= target) 353: break; 354: if (hscroll 355: || (truncate_partial_width_windows 356: && width + 1 < screen_width) 357: || !NULL (bf_cur->truncate_lines)) 358: { 359: while (pos < to && CharAt(pos) != '\n') pos++; 360: pos--; 361: } 362: else 363: { 364: cpos += (1 << SHORTBITS) - width; 365: tab_offset += width; 366: } 367: 368: } 369: } 370: 371: val_compute_motion.bufpos = pos; 372: val_compute_motion.hpos = HPOS (cpos); 373: val_compute_motion.vpos = VPOS (cpos); 374: val_compute_motion.prevhpos = HPOS (prevpos); 375: 376: /* Nonzero if have just continued a line */ 377: val_compute_motion.contin 378: = pos != from 379: && (val_compute_motion.vpos != VPOS (prevpos)) 380: && c != '\n'; 381: 382: return &val_compute_motion; 383: } 384: 385: pos_tab_offset (w, pos) 386: struct window *w; 387: register int pos; 388: { 389: int opoint = point; 390: int col; 391: 392: if (pos == FirstCharacter || CharAt (pos - 1) == '\n') 393: return 0; 394: SetPoint (pos); 395: col = current_column (); 396: SetPoint (opoint); 397: return col - (col % (XFASTINT (w->width) - 1)); 398: } 399: 400: /* start_hpos is the hpos of the first character of the buffer: 401: zero except for the minibuffer window, 402: where it is the width of the prompt. */ 403: 404: struct position val_vmotion; 405: 406: struct position * 407: vmotion (from, vtarget, width, hscroll, window) 408: register int from, vtarget, width; 409: int hscroll; 410: Lisp_Object window; 411: { 412: struct position pos; 413: /* vpos is cumulative vertical position, changed as from is changed */ 414: register int vpos = 0; 415: register int prevline; 416: register int first; 417: int lmargin = hscroll > 0 ? 1 - hscroll : 0; 418: int selective 419: = XTYPE (bf_cur->selective_display) == Lisp_Int 420: ? XINT (bf_cur->selective_display) 421: : !NULL (bf_cur->selective_display) ? -1 : 0; 422: int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0); 423: 424: retry: 425: if (vtarget > vpos) 426: { 427: /* Moving downward is simple, but must calculate from beg of line 428: to determine hpos of starting point */ 429: if (from > FirstCharacter && CharAt (from - 1) != '\n') 430: { 431: prevline = ScanBf ('\n', from, -1); 432: while (selective > 0 433: && prevline > FirstCharacter 434: && position_indentation (prevline) >= selective) 435: prevline = ScanBf ('\n', prevline - 1, -1); 436: pos = *compute_motion (prevline, 0, 437: lmargin + (prevline == 1 ? start_hpos : 0), 438: from, 10000, 10000, 439: width, hscroll, 0); 440: } 441: else 442: { 443: pos.hpos = lmargin + (from == 1 ? start_hpos : 0); 444: pos.vpos = 0; 445: } 446: return compute_motion (from, vpos, pos.hpos, 447: 1 + NumCharacters, vtarget, - (1 << (SHORTBITS - 1)), 448: width, hscroll, pos.vpos * width); 449: } 450: 451: /* To move upward, go a line at a time until 452: we have gone at least far enough */ 453: 454: first = 1; 455: 456: while ((vpos > vtarget || first) && from > FirstCharacter) 457: { 458: prevline = from; 459: while (1) 460: { 461: prevline = ScanBf ('\n', prevline - 1, -1); 462: if (prevline == FirstCharacter 463: || selective <= 0 464: || position_indentation (prevline) < selective) 465: break; 466: } 467: pos = *compute_motion (prevline, 0, 468: lmargin + (prevline == 1 ? start_hpos : 0), 469: from, 10000, 10000, 470: width, hscroll, 0); 471: vpos -= pos.vpos; 472: first = 0; 473: from = prevline; 474: } 475: 476: /* If we made exactly the desired vertical distance, 477: or if we hit beginning of buffer, 478: return point found */ 479: if (vpos >= vtarget) 480: { 481: val_vmotion.bufpos = from; 482: val_vmotion.vpos = vpos; 483: val_vmotion.hpos = lmargin; 484: val_vmotion.contin = 0; 485: val_vmotion.prevhpos = 0; 486: return &val_vmotion; 487: } 488: 489: /* Otherwise find the correct spot by moving down */ 490: goto retry; 491: } 492: 493: DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0, 494: "Move to start of screen line LINES lines down.\n\ 495: If LINES is negative, this is moving up.\n\ 496: Sets point to position found; this may be start of line\n\ 497: or just the start of a continuation line.\n\ 498: Returns number of lines moved; may be closer to zero than LINES\n\ 499: if end of buffer was reached.") 500: (lines) 501: Lisp_Object lines; 502: { 503: struct position pos; 504: register struct window *w = XWINDOW (selected_window); 505: 506: CHECK_NUMBER (lines, 0); 507: 508: pos = *vmotion (point, XINT (lines), 509: XFASTINT (w->width) - 1 510: - (XFASTINT (w->width) + XFASTINT (w->left) 511: != XFASTINT (XWINDOW (minibuf_window)->width)), 512: /* Not XFASTINT since perhaps could be negative */ 513: XINT (w->hscroll), selected_window); 514: 515: SetPoint (pos.bufpos); 516: return make_number (pos.vpos); 517: } 518: 519: syms_of_indent () 520: { 521: DefBoolVar ("indent-tabs-mode", &indent_tabs_mode, 522: "*Indentation can insert tabs if this is non-nil."); 523: indent_tabs_mode = 1; 524: 525: defsubr (&Scurrent_indentation); 526: defsubr (&Sindent_to); 527: defsubr (&Scurrent_column); 528: defsubr (&Smove_to_column); 529: defsubr (&Svertical_motion); 530: }