diff options
| author | Jim Blandy | 1991-01-15 21:47:47 +0000 |
|---|---|---|
| committer | Jim Blandy | 1991-01-15 21:47:47 +0000 |
| commit | 993b64044ac3d6a314c645a6505bdc89756f6e51 (patch) | |
| tree | 2fe1ac4d92ce98197e1db94e1d4a5ffc44034891 /src | |
| parent | a8a7afbecb2262f3841e05b2f54def6c33ac86cf (diff) | |
| download | emacs-993b64044ac3d6a314c645a6505bdc89756f6e51.tar.gz emacs-993b64044ac3d6a314c645a6505bdc89756f6e51.zip | |
Initial revision
Diffstat (limited to 'src')
| -rw-r--r-- | src/indent.c | 699 |
1 files changed, 699 insertions, 0 deletions
diff --git a/src/indent.c b/src/indent.c new file mode 100644 index 00000000000..9561927167d --- /dev/null +++ b/src/indent.c | |||
| @@ -0,0 +1,699 @@ | |||
| 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 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 "screen.h" | ||
| 26 | #include "window.h" | ||
| 27 | #include "termchar.h" | ||
| 28 | #include "termopts.h" | ||
| 29 | #include "disptab.h" | ||
| 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 | #define CR 015 | ||
| 39 | |||
| 40 | /* These three values memoize the current column to avoid recalculation */ | ||
| 41 | /* Some things in set last_known_column_point to -1 | ||
| 42 | to mark the memoized value as invalid */ | ||
| 43 | /* Last value returned by current_column */ | ||
| 44 | int last_known_column; | ||
| 45 | /* Value of point when current_column was called */ | ||
| 46 | int last_known_column_point; | ||
| 47 | /* Value of MODIFF when current_column was called */ | ||
| 48 | int last_known_column_modified; | ||
| 49 | |||
| 50 | extern int minibuf_prompt_width; | ||
| 51 | |||
| 52 | /* Get the display table to use for the current buffer. */ | ||
| 53 | |||
| 54 | struct Lisp_Vector * | ||
| 55 | buffer_display_table () | ||
| 56 | { | ||
| 57 | Lisp_Object thisbuf; | ||
| 58 | |||
| 59 | thisbuf = current_buffer->display_table; | ||
| 60 | if (XTYPE (thisbuf) == Lisp_Vector | ||
| 61 | && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE) | ||
| 62 | return XVECTOR (thisbuf); | ||
| 63 | if (XTYPE (Vstandard_display_table) == Lisp_Vector | ||
| 64 | && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE) | ||
| 65 | return XVECTOR (Vstandard_display_table); | ||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0, | ||
| 70 | "Return the horizontal position of point. Beginning of line is column 0.\n\ | ||
| 71 | This is calculated by adding together the widths of all the displayed\n\ | ||
| 72 | representations of the character between the start of the previous line\n\ | ||
| 73 | and point. (eg control characters will have a width of 2 or 4, tabs\n\ | ||
| 74 | will have a variable width)\n\ | ||
| 75 | Ignores finite width of screen, which means that this function may return\n\ | ||
| 76 | values greater than (screen-width).\n\ | ||
| 77 | Whether the line is visible (if `selective-display' is t) has no effect;\n\ | ||
| 78 | however, ^M is treated as end of line when `selective-display' is t.") | ||
| 79 | () | ||
| 80 | { | ||
| 81 | Lisp_Object temp; | ||
| 82 | XFASTINT (temp) = current_column (); | ||
| 83 | return temp; | ||
| 84 | } | ||
| 85 | |||
| 86 | int | ||
| 87 | current_column () | ||
| 88 | { | ||
| 89 | register int col; | ||
| 90 | register unsigned char *ptr, *stop; | ||
| 91 | register int tab_seen; | ||
| 92 | int post_tab; | ||
| 93 | register int c; | ||
| 94 | register int tab_width = XINT (current_buffer->tab_width); | ||
| 95 | int ctl_arrow = !NULL (current_buffer->ctl_arrow); | ||
| 96 | register struct Lisp_Vector *dp = buffer_display_table (); | ||
| 97 | int stopchar; | ||
| 98 | |||
| 99 | if (point == last_known_column_point | ||
| 100 | && MODIFF == last_known_column_modified) | ||
| 101 | return last_known_column; | ||
| 102 | |||
| 103 | /* Make a pointer for decrementing through the chars before point. */ | ||
| 104 | ptr = &FETCH_CHAR (point - 1) + 1; | ||
| 105 | /* Make a pointer to where consecutive chars leave off, | ||
| 106 | going backwards from point. */ | ||
| 107 | if (point == BEGV) | ||
| 108 | stop = ptr; | ||
| 109 | else if (point <= GPT || BEGV > GPT) | ||
| 110 | stop = BEGV_ADDR; | ||
| 111 | else | ||
| 112 | stop = GAP_END_ADDR; | ||
| 113 | |||
| 114 | if (tab_width <= 0 || tab_width > 20) tab_width = 8; | ||
| 115 | |||
| 116 | col = 0, tab_seen = 0, post_tab = 0; | ||
| 117 | |||
| 118 | while (1) | ||
| 119 | { | ||
| 120 | if (ptr == stop) | ||
| 121 | { | ||
| 122 | /* We stopped either for the beginning of the buffer | ||
| 123 | or for the gap. */ | ||
| 124 | if (ptr == BEGV_ADDR) | ||
| 125 | break; | ||
| 126 | /* It was the gap. Jump back over it. */ | ||
| 127 | stop = BEGV_ADDR; | ||
| 128 | ptr = GPT_ADDR; | ||
| 129 | /* Check whether that brings us to beginning of buffer. */ | ||
| 130 | if (BEGV >= GPT) break; | ||
| 131 | } | ||
| 132 | |||
| 133 | c = *--ptr; | ||
| 134 | if (c >= 040 && c < 0177 | ||
| 135 | && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String)) | ||
| 136 | { | ||
| 137 | col++; | ||
| 138 | } | ||
| 139 | else if (c == '\n') | ||
| 140 | break; | ||
| 141 | else if (c == '\r' && EQ (current_buffer->selective_display, Qt)) | ||
| 142 | break; | ||
| 143 | else if (c == '\t') | ||
| 144 | { | ||
| 145 | if (tab_seen) | ||
| 146 | col = ((col + tab_width) / tab_width) * tab_width; | ||
| 147 | |||
| 148 | post_tab += col; | ||
| 149 | col = 0; | ||
| 150 | tab_seen = 1; | ||
| 151 | } | ||
| 152 | else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String) | ||
| 153 | col += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH); | ||
| 154 | else | ||
| 155 | col += (ctl_arrow && c < 0200) ? 2 : 4; | ||
| 156 | } | ||
| 157 | |||
| 158 | if (tab_seen) | ||
| 159 | { | ||
| 160 | col = ((col + tab_width) / tab_width) * tab_width; | ||
| 161 | col += post_tab; | ||
| 162 | } | ||
| 163 | |||
| 164 | last_known_column = col; | ||
| 165 | last_known_column_point = point; | ||
| 166 | last_known_column_modified = MODIFF; | ||
| 167 | |||
| 168 | return col; | ||
| 169 | } | ||
| 170 | |||
| 171 | |||
| 172 | DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ", | ||
| 173 | "Indent from point with tabs and spaces until COLUMN is reached.\n\ | ||
| 174 | Optional second argument MIN says always do at least MIN spaces\n\ | ||
| 175 | even if that goes past COLUMN; by default, MIN is zero.") | ||
| 176 | (col, minimum) | ||
| 177 | Lisp_Object col, minimum; | ||
| 178 | { | ||
| 179 | int mincol; | ||
| 180 | register int fromcol; | ||
| 181 | register int tab_width = XINT (current_buffer->tab_width); | ||
| 182 | |||
| 183 | CHECK_NUMBER (col, 0); | ||
| 184 | if (NULL (minimum)) | ||
| 185 | XFASTINT (minimum) = 0; | ||
| 186 | CHECK_NUMBER (minimum, 1); | ||
| 187 | |||
| 188 | fromcol = current_column (); | ||
| 189 | mincol = fromcol + XINT (minimum); | ||
| 190 | if (mincol < XINT (col)) mincol = XINT (col); | ||
| 191 | |||
| 192 | if (fromcol == mincol) | ||
| 193 | return make_number (mincol); | ||
| 194 | |||
| 195 | if (tab_width <= 0 || tab_width > 20) tab_width = 8; | ||
| 196 | |||
| 197 | if (indent_tabs_mode) | ||
| 198 | { | ||
| 199 | Lisp_Object n; | ||
| 200 | XFASTINT (n) = mincol / tab_width - fromcol / tab_width; | ||
| 201 | if (XFASTINT (n) != 0) | ||
| 202 | { | ||
| 203 | Finsert_char (make_number ('\t'), n); | ||
| 204 | |||
| 205 | fromcol = (mincol / tab_width) * tab_width; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | XFASTINT (col) = mincol - fromcol; | ||
| 210 | Finsert_char (make_number (' '), col); | ||
| 211 | |||
| 212 | last_known_column = mincol; | ||
| 213 | last_known_column_point = point; | ||
| 214 | last_known_column_modified = MODIFF; | ||
| 215 | |||
| 216 | XSETINT (col, mincol); | ||
| 217 | return col; | ||
| 218 | } | ||
| 219 | |||
| 220 | DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation, | ||
| 221 | 0, 0, 0, | ||
| 222 | "Return the indentation of the current line.\n\ | ||
| 223 | This is the horizontal position of the character\n\ | ||
| 224 | following any initial whitespace.") | ||
| 225 | () | ||
| 226 | { | ||
| 227 | Lisp_Object val; | ||
| 228 | |||
| 229 | XFASTINT (val) = position_indentation (find_next_newline (point, -1)); | ||
| 230 | return val; | ||
| 231 | } | ||
| 232 | |||
| 233 | position_indentation (pos) | ||
| 234 | register int pos; | ||
| 235 | { | ||
| 236 | register int column = 0; | ||
| 237 | register int tab_width = XINT (current_buffer->tab_width); | ||
| 238 | register unsigned char *p; | ||
| 239 | register unsigned char *stop; | ||
| 240 | |||
| 241 | if (tab_width <= 0 || tab_width > 20) tab_width = 8; | ||
| 242 | |||
| 243 | stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1; | ||
| 244 | p = &FETCH_CHAR (pos); | ||
| 245 | while (1) | ||
| 246 | { | ||
| 247 | while (p == stop) | ||
| 248 | { | ||
| 249 | if (pos == ZV) | ||
| 250 | return column; | ||
| 251 | pos += p - &FETCH_CHAR (pos); | ||
| 252 | p = &FETCH_CHAR (pos); | ||
| 253 | stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1; | ||
| 254 | } | ||
| 255 | switch (*p++) | ||
| 256 | { | ||
| 257 | case ' ': | ||
| 258 | column++; | ||
| 259 | break; | ||
| 260 | case '\t': | ||
| 261 | column += tab_width - column % tab_width; | ||
| 262 | break; | ||
| 263 | default: | ||
| 264 | return column; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0, | ||
| 270 | "Move point to column COLUMN in the current line.\n\ | ||
| 271 | The column of a character is calculated by adding together the widths\n\ | ||
| 272 | as displayed of the previous characters in the line.\n\ | ||
| 273 | This function ignores line-continuation;\n\ | ||
| 274 | there is no upper limit on the column number a character can have\n\ | ||
| 275 | and horizontal scrolling has no effect.\n\n\ | ||
| 276 | If specified column is within a character, point goes after that character.\n\ | ||
| 277 | If it's past end of line, point goes to end of line.\n\n\ | ||
| 278 | A non-nil second (optional) argument FORCE means, if the line\n\ | ||
| 279 | is too short to reach column COLUMN then add spaces/tabs to get there,\n\ | ||
| 280 | and if COLUMN is in the middle of a tab character, change it to spaces.") | ||
| 281 | (column, force) | ||
| 282 | Lisp_Object column, force; | ||
| 283 | { | ||
| 284 | register int pos; | ||
| 285 | register int col = current_column (); | ||
| 286 | register int goal; | ||
| 287 | register int end; | ||
| 288 | register int tab_width = XINT (current_buffer->tab_width); | ||
| 289 | register int ctl_arrow = !NULL (current_buffer->ctl_arrow); | ||
| 290 | register struct Lisp_Vector *dp = buffer_display_table (); | ||
| 291 | |||
| 292 | Lisp_Object val; | ||
| 293 | int prev_col; | ||
| 294 | int c; | ||
| 295 | |||
| 296 | if (tab_width <= 0 || tab_width > 20) tab_width = 8; | ||
| 297 | CHECK_NATNUM (column, 0); | ||
| 298 | goal = XINT (column); | ||
| 299 | |||
| 300 | retry: | ||
| 301 | pos = point; | ||
| 302 | end = ZV; | ||
| 303 | |||
| 304 | /* If we're starting past the desired column, | ||
| 305 | back up to beginning of line and scan from there. */ | ||
| 306 | if (col > goal) | ||
| 307 | { | ||
| 308 | pos = find_next_newline (pos, -1); | ||
| 309 | col = 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | while (col < goal && pos < end) | ||
| 313 | { | ||
| 314 | c = FETCH_CHAR (pos); | ||
| 315 | if (c == '\n') | ||
| 316 | break; | ||
| 317 | if (c == '\r' && EQ (current_buffer->selective_display, Qt)) | ||
| 318 | break; | ||
| 319 | pos++; | ||
| 320 | if (c == '\t') | ||
| 321 | { | ||
| 322 | prev_col = col; | ||
| 323 | col += tab_width; | ||
| 324 | col = col / tab_width * tab_width; | ||
| 325 | } | ||
| 326 | else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String) | ||
| 327 | col += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH); | ||
| 328 | else if (ctl_arrow && (c < 040 || c == 0177)) | ||
| 329 | col++; | ||
| 330 | else if (c < 040 || c >= 0177) | ||
| 331 | col += 3; | ||
| 332 | else | ||
| 333 | col++; | ||
| 334 | } | ||
| 335 | |||
| 336 | SET_PT (pos); | ||
| 337 | |||
| 338 | /* If a tab char made us overshoot, change it to spaces | ||
| 339 | and scan through it again. */ | ||
| 340 | if (!NULL (force) && col > goal && c == '\t' && prev_col < goal) | ||
| 341 | { | ||
| 342 | del_range (point - 1, point); | ||
| 343 | Findent_to (make_number (col - 1)); | ||
| 344 | insert_char (' '); | ||
| 345 | goto retry; | ||
| 346 | } | ||
| 347 | |||
| 348 | /* If line ends prematurely, add space to the end. */ | ||
| 349 | if (col < goal && !NULL (force)) | ||
| 350 | Findent_to (make_number (col = goal)); | ||
| 351 | |||
| 352 | last_known_column = col; | ||
| 353 | last_known_column_point = point; | ||
| 354 | last_known_column_modified = MODIFF; | ||
| 355 | |||
| 356 | XFASTINT (val) = col; | ||
| 357 | return val; | ||
| 358 | } | ||
| 359 | |||
| 360 | struct position val_compute_motion; | ||
| 361 | |||
| 362 | /* Scan the current buffer forward from offset FROM, pretending that | ||
| 363 | this is at line FROMVPOS, column FROMHPOS, until reaching buffer | ||
| 364 | offset TO or line TOVPOS, column TOHPOS (whichever comes first), | ||
| 365 | and return the ending buffer position and screen location. | ||
| 366 | |||
| 367 | WIDTH is the number of columns available to display text; | ||
| 368 | compute_motion uses this to handle continuation lines and such. | ||
| 369 | HSCROLL is the number of columns not being displayed at the left | ||
| 370 | margin; this is usually taken from a window's hscroll member. | ||
| 371 | TAB_OFFSET is a mysterious value, perhaps the number of columns of | ||
| 372 | the first tab that aren't being displayed, perhaps because of a | ||
| 373 | continuation line or something. | ||
| 374 | |||
| 375 | compute_motion returns a pointer to a struct position. The bufpos | ||
| 376 | member gives the buffer position at the end of the scan, and hpos | ||
| 377 | and vpos give its cartesian location. I'm not clear on what the | ||
| 378 | other members are. | ||
| 379 | |||
| 380 | For example, to find the buffer position of column COL of line LINE | ||
| 381 | of a certain window, pass the window's starting location as FROM | ||
| 382 | and the window's upper-left coordinates as FROMVPOS and FROMHPOS. | ||
| 383 | Pass the buffer's ZV as TO, to limit the scan to the end of the | ||
| 384 | visible section of the buffer, and pass LINE and COL as TOVPOS and | ||
| 385 | TOHPOS. | ||
| 386 | |||
| 387 | When displaying in window w, a typical formula for WIDTH is: | ||
| 388 | |||
| 389 | window_width - 1 | ||
| 390 | - (window_width + window_left != screen_width) | ||
| 391 | |||
| 392 | where | ||
| 393 | window_width is XFASTINT (w->width), | ||
| 394 | window_left is XFASTINT (w->left), | ||
| 395 | and screen_width = SCREEN_WIDTH (XSCREEN (window->screen)) | ||
| 396 | |||
| 397 | This accounts for the continuation-line backslashes, and the window | ||
| 398 | borders if the window is split vertically. */ | ||
| 399 | |||
| 400 | struct position * | ||
| 401 | compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset) | ||
| 402 | int from, fromvpos, fromhpos, to, tovpos, tohpos; | ||
| 403 | register int width; | ||
| 404 | int hscroll, tab_offset; | ||
| 405 | { | ||
| 406 | /* Note that `cpos' is CURRENT_VPOS << SHORTBITS + CURRENT_HPOS, | ||
| 407 | and that CURRENT_HPOS may be negative. Use these macros | ||
| 408 | to extract the hpos or the vpos from cpos or anything like it. | ||
| 409 | */ | ||
| 410 | #ifndef SHORT_CAST_BUG | ||
| 411 | #define HPOS(VAR) (short) (VAR) | ||
| 412 | #else | ||
| 413 | #define HPOS(VAR) (((VAR) & (1 << (SHORTBITS - 1)) \ | ||
| 414 | ? ~((1 << SHORTBITS) - 1) : 0) \ | ||
| 415 | | (VAR) & ((1 << SHORTBITS) - 1)) | ||
| 416 | /* #define HPOS(VAR) (((VAR) & 0x8000 ? 0xffff0000 : 0) | ((VAR) & 0xffff)) */ | ||
| 417 | #endif /* SHORT_CAST_BUG */ | ||
| 418 | |||
| 419 | #define VPOS(VAR) (((VAR) >> SHORTBITS) + (HPOS (VAR) < 0)) | ||
| 420 | |||
| 421 | |||
| 422 | #ifndef TAHOE_REGISTER_BUG | ||
| 423 | register | ||
| 424 | #endif /* TAHOE_REGISTER_BUG */ | ||
| 425 | int cpos = fromhpos + (fromvpos << SHORTBITS); | ||
| 426 | register int target = tohpos + (tovpos << SHORTBITS); | ||
| 427 | register int pos; | ||
| 428 | register int c; | ||
| 429 | register int tab_width = XFASTINT (current_buffer->tab_width); | ||
| 430 | register int ctl_arrow = !NULL (current_buffer->ctl_arrow); | ||
| 431 | register struct Lisp_Vector *dp = buffer_display_table (); | ||
| 432 | int selective | ||
| 433 | = XTYPE (current_buffer->selective_display) == Lisp_Int | ||
| 434 | ? XINT (current_buffer->selective_display) | ||
| 435 | : !NULL (current_buffer->selective_display) ? -1 : 0; | ||
| 436 | int prevpos; | ||
| 437 | int selective_rlen | ||
| 438 | = (selective && dp && XTYPE (DISP_INVIS_ROPE (dp)) == Lisp_String | ||
| 439 | ? XSTRING (DISP_INVIS_ROPE (dp))->size / sizeof (GLYPH) : 0); | ||
| 440 | |||
| 441 | if (tab_width <= 0 || tab_width > 20) tab_width = 8; | ||
| 442 | for (pos = from; pos < to && cpos < target; pos++) | ||
| 443 | { | ||
| 444 | prevpos = cpos; | ||
| 445 | c = FETCH_CHAR (pos); | ||
| 446 | if (c >= 040 && c < 0177 | ||
| 447 | && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String)) | ||
| 448 | cpos++; | ||
| 449 | else if (c == '\t') | ||
| 450 | { | ||
| 451 | cpos += tab_width | ||
| 452 | - HPOS (cpos + tab_offset + hscroll - (hscroll > 0) | ||
| 453 | /* Add tab_width here to make sure positive. | ||
| 454 | cpos can be negative after continuation | ||
| 455 | but can't be less than -tab_width. */ | ||
| 456 | + tab_width) | ||
| 457 | % tab_width; | ||
| 458 | } | ||
| 459 | else if (c == '\n') | ||
| 460 | { | ||
| 461 | if (selective > 0 && position_indentation (pos + 1) >= selective) | ||
| 462 | { | ||
| 463 | /* Skip any number of invisible lines all at once */ | ||
| 464 | do | ||
| 465 | { | ||
| 466 | while (++pos < to && FETCH_CHAR(pos) != '\n'); | ||
| 467 | } | ||
| 468 | while (selective > 0 && position_indentation (pos + 1) >= selective); | ||
| 469 | pos--; | ||
| 470 | /* Allow for the " ..." that is displayed for them. */ | ||
| 471 | if (selective_rlen) | ||
| 472 | { | ||
| 473 | cpos += selective_rlen; | ||
| 474 | if (HPOS (cpos) >= width) | ||
| 475 | cpos -= HPOS (cpos) - width; | ||
| 476 | } | ||
| 477 | } | ||
| 478 | else | ||
| 479 | cpos += (1 << SHORTBITS) - HPOS (cpos); | ||
| 480 | cpos -= hscroll; | ||
| 481 | if (hscroll > 0) cpos++; /* Count the ! on column 0 */ | ||
| 482 | tab_offset = 0; | ||
| 483 | } | ||
| 484 | else if (c == CR && selective < 0) | ||
| 485 | { | ||
| 486 | /* In selective display mode, | ||
| 487 | everything from a ^M to the end of the line is invisible */ | ||
| 488 | while (pos < to && FETCH_CHAR(pos) != '\n') pos++; | ||
| 489 | pos--; | ||
| 490 | /* Allow for the " ..." that is displayed for them. */ | ||
| 491 | if (selective_rlen) | ||
| 492 | { | ||
| 493 | cpos += selective_rlen; | ||
| 494 | if (HPOS (cpos) >= width) | ||
| 495 | cpos -= HPOS (cpos) - width; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String) | ||
| 499 | cpos += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH); | ||
| 500 | else | ||
| 501 | cpos += (ctl_arrow && c < 0200) ? 2 : 4; | ||
| 502 | |||
| 503 | if (HPOS (cpos) >= width | ||
| 504 | && (HPOS (cpos) > width | ||
| 505 | || (pos < (ZV - 1) | ||
| 506 | && FETCH_CHAR (pos + 1) != '\n'))) | ||
| 507 | { | ||
| 508 | if (cpos >= target) | ||
| 509 | break; | ||
| 510 | if (hscroll | ||
| 511 | || (truncate_partial_width_windows | ||
| 512 | && width + 1 < SCREEN_WIDTH (selected_screen)) | ||
| 513 | || !NULL (current_buffer->truncate_lines)) | ||
| 514 | { | ||
| 515 | while (pos < to && FETCH_CHAR(pos) != '\n') pos++; | ||
| 516 | pos--; | ||
| 517 | } | ||
| 518 | else | ||
| 519 | { | ||
| 520 | cpos += (1 << SHORTBITS) - width; | ||
| 521 | tab_offset += width; | ||
| 522 | } | ||
| 523 | |||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | val_compute_motion.bufpos = pos; | ||
| 528 | val_compute_motion.hpos = HPOS (cpos); | ||
| 529 | val_compute_motion.vpos = VPOS (cpos); | ||
| 530 | val_compute_motion.prevhpos = HPOS (prevpos); | ||
| 531 | |||
| 532 | /* Nonzero if have just continued a line */ | ||
| 533 | val_compute_motion.contin | ||
| 534 | = pos != from | ||
| 535 | && (val_compute_motion.vpos != VPOS (prevpos)) | ||
| 536 | && c != '\n'; | ||
| 537 | |||
| 538 | return &val_compute_motion; | ||
| 539 | } | ||
| 540 | #undef HPOS | ||
| 541 | #undef VPOS | ||
| 542 | |||
| 543 | |||
| 544 | /* Return the column of position POS in window W's buffer, | ||
| 545 | rounded down to a multiple of the internal width of W. | ||
| 546 | This is the amount of indentation of position POS | ||
| 547 | that is not visible in its horizontal position in the window. */ | ||
| 548 | |||
| 549 | int | ||
| 550 | pos_tab_offset (w, pos) | ||
| 551 | struct window *w; | ||
| 552 | register int pos; | ||
| 553 | { | ||
| 554 | int opoint = point; | ||
| 555 | int col; | ||
| 556 | int width = XFASTINT (w->width) - 1 | ||
| 557 | - (XFASTINT (w->width) + XFASTINT (w->left) | ||
| 558 | != SCREEN_WIDTH (XSCREEN (w->screen))); | ||
| 559 | |||
| 560 | if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n') | ||
| 561 | return 0; | ||
| 562 | SET_PT (pos); | ||
| 563 | col = current_column (); | ||
| 564 | SET_PT (opoint); | ||
| 565 | return col - (col % width); | ||
| 566 | } | ||
| 567 | |||
| 568 | /* start_hpos is the hpos of the first character of the buffer: | ||
| 569 | zero except for the minibuffer window, | ||
| 570 | where it is the width of the prompt. */ | ||
| 571 | |||
| 572 | struct position val_vmotion; | ||
| 573 | |||
| 574 | struct position * | ||
| 575 | vmotion (from, vtarget, width, hscroll, window) | ||
| 576 | register int from, vtarget, width; | ||
| 577 | int hscroll; | ||
| 578 | Lisp_Object window; | ||
| 579 | { | ||
| 580 | struct position pos; | ||
| 581 | /* vpos is cumulative vertical position, changed as from is changed */ | ||
| 582 | register int vpos = 0; | ||
| 583 | register int prevline; | ||
| 584 | register int first; | ||
| 585 | int lmargin = hscroll > 0 ? 1 - hscroll : 0; | ||
| 586 | int selective | ||
| 587 | = XTYPE (current_buffer->selective_display) == Lisp_Int | ||
| 588 | ? XINT (current_buffer->selective_display) | ||
| 589 | : !NULL (current_buffer->selective_display) ? -1 : 0; | ||
| 590 | int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0); | ||
| 591 | |||
| 592 | retry: | ||
| 593 | if (vtarget > vpos) | ||
| 594 | { | ||
| 595 | /* Moving downward is simple, but must calculate from beg of line | ||
| 596 | to determine hpos of starting point */ | ||
| 597 | if (from > BEGV && FETCH_CHAR (from - 1) != '\n') | ||
| 598 | { | ||
| 599 | prevline = find_next_newline (from, -1); | ||
| 600 | while (selective > 0 | ||
| 601 | && prevline > BEGV | ||
| 602 | && position_indentation (prevline) >= selective) | ||
| 603 | prevline = find_next_newline (prevline - 1, -1); | ||
| 604 | pos = *compute_motion (prevline, 0, | ||
| 605 | lmargin + (prevline == 1 ? start_hpos : 0), | ||
| 606 | from, 10000, 10000, | ||
| 607 | width, hscroll, 0); | ||
| 608 | } | ||
| 609 | else | ||
| 610 | { | ||
| 611 | pos.hpos = lmargin + (from == 1 ? start_hpos : 0); | ||
| 612 | pos.vpos = 0; | ||
| 613 | } | ||
| 614 | return compute_motion (from, vpos, pos.hpos, | ||
| 615 | ZV, vtarget, - (1 << (SHORTBITS - 1)), | ||
| 616 | width, hscroll, pos.vpos * width); | ||
| 617 | } | ||
| 618 | |||
| 619 | /* To move upward, go a line at a time until | ||
| 620 | we have gone at least far enough */ | ||
| 621 | |||
| 622 | first = 1; | ||
| 623 | |||
| 624 | while ((vpos > vtarget || first) && from > BEGV) | ||
| 625 | { | ||
| 626 | prevline = from; | ||
| 627 | while (1) | ||
| 628 | { | ||
| 629 | prevline = find_next_newline (prevline - 1, -1); | ||
| 630 | if (prevline == BEGV | ||
| 631 | || selective <= 0 | ||
| 632 | || position_indentation (prevline) < selective) | ||
| 633 | break; | ||
| 634 | } | ||
| 635 | pos = *compute_motion (prevline, 0, | ||
| 636 | lmargin + (prevline == 1 ? start_hpos : 0), | ||
| 637 | from, 10000, 10000, | ||
| 638 | width, hscroll, 0); | ||
| 639 | vpos -= pos.vpos; | ||
| 640 | first = 0; | ||
| 641 | from = prevline; | ||
| 642 | } | ||
| 643 | |||
| 644 | /* If we made exactly the desired vertical distance, | ||
| 645 | or if we hit beginning of buffer, | ||
| 646 | return point found */ | ||
| 647 | if (vpos >= vtarget) | ||
| 648 | { | ||
| 649 | val_vmotion.bufpos = from; | ||
| 650 | val_vmotion.vpos = vpos; | ||
| 651 | val_vmotion.hpos = lmargin; | ||
| 652 | val_vmotion.contin = 0; | ||
| 653 | val_vmotion.prevhpos = 0; | ||
| 654 | return &val_vmotion; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* Otherwise find the correct spot by moving down */ | ||
| 658 | goto retry; | ||
| 659 | } | ||
| 660 | |||
| 661 | DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0, | ||
| 662 | "Move to start of screen line LINES lines down.\n\ | ||
| 663 | If LINES is negative, this is moving up.\n\ | ||
| 664 | Sets point to position found; this may be start of line\n\ | ||
| 665 | or just the start of a continuation line.\n\ | ||
| 666 | Returns number of lines moved; may be closer to zero than LINES\n\ | ||
| 667 | if beginning or end of buffer was reached.") | ||
| 668 | (lines) | ||
| 669 | Lisp_Object lines; | ||
| 670 | { | ||
| 671 | struct position pos; | ||
| 672 | register struct window *w = XWINDOW (selected_window); | ||
| 673 | int width = XFASTINT (w->width) - 1 | ||
| 674 | - (XFASTINT (w->width) + XFASTINT (w->left) | ||
| 675 | != SCREEN_WIDTH (XSCREEN (w->screen))); | ||
| 676 | |||
| 677 | CHECK_NUMBER (lines, 0); | ||
| 678 | |||
| 679 | pos = *vmotion (point, XINT (lines), width, | ||
| 680 | /* Not XFASTINT since perhaps could be negative */ | ||
| 681 | XINT (w->hscroll), selected_window); | ||
| 682 | |||
| 683 | SET_PT (pos.bufpos); | ||
| 684 | return make_number (pos.vpos); | ||
| 685 | } | ||
| 686 | |||
| 687 | syms_of_indent () | ||
| 688 | { | ||
| 689 | DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode, | ||
| 690 | "*Indentation can insert tabs if this is non-nil.\n\ | ||
| 691 | Setting this variable automatically makes it local to the current buffer."); | ||
| 692 | indent_tabs_mode = 1; | ||
| 693 | |||
| 694 | defsubr (&Scurrent_indentation); | ||
| 695 | defsubr (&Sindent_to); | ||
| 696 | defsubr (&Scurrent_column); | ||
| 697 | defsubr (&Smove_to_column); | ||
| 698 | defsubr (&Svertical_motion); | ||
| 699 | } | ||