Update GPL to version 2.
[bpt/emacs.git] / src / indent.c
CommitLineData
993b6404 1/* Indentation functions.
2ff4775b 2 Copyright (C) 1985,86,87,88,93,94,95 Free Software Foundation, Inc.
993b6404
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
7c938215 8the Free Software Foundation; either version 2, or (at your option)
993b6404
JB
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
18160b98 21#include <config.h>
993b6404
JB
22#include "lisp.h"
23#include "buffer.h"
24#include "indent.h"
502b9b64 25#include "frame.h"
993b6404
JB
26#include "window.h"
27#include "termchar.h"
28#include "termopts.h"
29#include "disptab.h"
5a05d3d2 30#include "intervals.h"
0aa01123 31#include "region-cache.h"
993b6404
JB
32
33/* Indentation can insert tabs if this is non-zero;
34 otherwise always uses spaces */
35int indent_tabs_mode;
36
37#define min(a, b) ((a) < (b) ? (a) : (b))
38#define max(a, b) ((a) > (b) ? (a) : (b))
39
40#define CR 015
41
42/* These three values memoize the current column to avoid recalculation */
43/* Some things in set last_known_column_point to -1
44 to mark the memoized value as invalid */
45/* Last value returned by current_column */
46int last_known_column;
47/* Value of point when current_column was called */
48int last_known_column_point;
49/* Value of MODIFF when current_column was called */
50int last_known_column_modified;
51
993b6404
JB
52/* Get the display table to use for the current buffer. */
53
54struct Lisp_Vector *
55buffer_display_table ()
56{
57 Lisp_Object thisbuf;
58
59 thisbuf = current_buffer->display_table;
eeaafd4f 60 if (VECTORP (thisbuf) && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE)
993b6404 61 return XVECTOR (thisbuf);
eeaafd4f 62 if (VECTORP (Vstandard_display_table)
993b6404
JB
63 && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
64 return XVECTOR (Vstandard_display_table);
65 return 0;
66}
67\f
0aa01123
JB
68/* Width run cache considerations. */
69
70/* Return the width of character C under display table DP. */
f845b8b2 71
0aa01123
JB
72static int
73character_width (c, dp)
74 int c;
75 struct Lisp_Vector *dp;
76{
77 Lisp_Object elt;
78
79 /* These width computations were determined by examining the cases
80 in display_text_line. */
81
f845b8b2 82 /* Everything can be handled by the display table, if it's
0aa01123 83 present and the element is right. */
f845b8b2 84 if (dp && (elt = DISP_CHAR_VECTOR (dp, c), VECTORP (elt)))
0aa01123
JB
85 return XVECTOR (elt)->size;
86
f845b8b2
RS
87 /* Some characters are special. */
88 if (c == '\n' || c == '\t' || c == '\015')
89 return 0;
90
91 /* Printing characters have width 1. */
0aa01123
JB
92 else if (c >= 040 && c < 0177)
93 return 1;
94
95 /* Everybody else (control characters, metacharacters) has other
96 widths. We could return their actual widths here, but they
97 depend on things like ctl_arrow and crud like that, and they're
98 not very common at all. So we'll just claim we don't know their
99 widths. */
100 else
101 return 0;
102}
103
104/* Return true iff the display table DISPTAB specifies the same widths
105 for characters as WIDTHTAB. We use this to decide when to
106 invalidate the buffer's width_run_cache. */
107int
108disptab_matches_widthtab (disptab, widthtab)
109 struct Lisp_Vector *disptab;
110 struct Lisp_Vector *widthtab;
111{
112 int i;
113
114 if (widthtab->size != 256)
115 abort ();
116
117 for (i = 0; i < 256; i++)
118 if (character_width (i, disptab)
119 != XFASTINT (widthtab->contents[i]))
120 return 0;
121
122 return 1;
2ff4775b 123}
0aa01123
JB
124
125/* Recompute BUF's width table, using the display table DISPTAB. */
126void
127recompute_width_table (buf, disptab)
128 struct buffer *buf;
129 struct Lisp_Vector *disptab;
130{
131 int i;
228a2e1a 132 struct Lisp_Vector *widthtab;
0aa01123 133
228a2e1a
KH
134 if (!VECTORP (buf->width_table))
135 buf->width_table = Fmake_vector (make_number (256), make_number (0));
136 widthtab = XVECTOR (buf->width_table);
0aa01123
JB
137 if (widthtab->size != 256)
138 abort ();
139
140 for (i = 0; i < 256; i++)
228a2e1a 141 XSETFASTINT (widthtab->contents[i], character_width (i, disptab));
0aa01123
JB
142}
143
144/* Allocate or free the width run cache, as requested by the current
145 state of current_buffer's cache_long_line_scans variable. */
146static void
147width_run_cache_on_off ()
148{
149 if (NILP (current_buffer->cache_long_line_scans))
150 {
151 /* It should be off. */
152 if (current_buffer->width_run_cache)
153 {
154 free_region_cache (current_buffer->width_run_cache);
155 current_buffer->width_run_cache = 0;
156 current_buffer->width_table = Qnil;
157 }
158 }
159 else
160 {
161 /* It should be on. */
162 if (current_buffer->width_run_cache == 0)
2ff4775b 163 {
0aa01123 164 current_buffer->width_run_cache = new_region_cache ();
0aa01123
JB
165 recompute_width_table (current_buffer, buffer_display_table ());
166 }
167 }
168}
169
170\f
993b6404
JB
171DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
172 "Return the horizontal position of point. Beginning of line is column 0.\n\
173This is calculated by adding together the widths of all the displayed\n\
174representations of the character between the start of the previous line\n\
175and point. (eg control characters will have a width of 2 or 4, tabs\n\
176will have a variable width)\n\
502b9b64
JB
177Ignores finite width of frame, which means that this function may return\n\
178values greater than (frame-width).\n\
993b6404
JB
179Whether the line is visible (if `selective-display' is t) has no effect;\n\
180however, ^M is treated as end of line when `selective-display' is t.")
181 ()
182{
183 Lisp_Object temp;
94d92e9c 184 XSETFASTINT (temp, current_column ());
993b6404
JB
185 return temp;
186}
187
e74928fc
JB
188/* Cancel any recorded value of the horizontal position. */
189
190invalidate_current_column ()
191{
192 last_known_column_point = 0;
193}
194
993b6404
JB
195int
196current_column ()
197{
198 register int col;
199 register unsigned char *ptr, *stop;
200 register int tab_seen;
201 int post_tab;
202 register int c;
203 register int tab_width = XINT (current_buffer->tab_width);
56a98455 204 int ctl_arrow = !NILP (current_buffer->ctl_arrow);
993b6404
JB
205 register struct Lisp_Vector *dp = buffer_display_table ();
206 int stopchar;
207
208 if (point == last_known_column_point
209 && MODIFF == last_known_column_modified)
210 return last_known_column;
211
212 /* Make a pointer for decrementing through the chars before point. */
213 ptr = &FETCH_CHAR (point - 1) + 1;
214 /* Make a pointer to where consecutive chars leave off,
215 going backwards from point. */
216 if (point == BEGV)
217 stop = ptr;
218 else if (point <= GPT || BEGV > GPT)
219 stop = BEGV_ADDR;
220 else
221 stop = GAP_END_ADDR;
222
ccdcf1f5 223 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
224
225 col = 0, tab_seen = 0, post_tab = 0;
226
227 while (1)
228 {
229 if (ptr == stop)
230 {
231 /* We stopped either for the beginning of the buffer
232 or for the gap. */
233 if (ptr == BEGV_ADDR)
234 break;
235 /* It was the gap. Jump back over it. */
236 stop = BEGV_ADDR;
237 ptr = GPT_ADDR;
238 /* Check whether that brings us to beginning of buffer. */
239 if (BEGV >= GPT) break;
240 }
241
242 c = *--ptr;
f845b8b2
RS
243 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
244 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
245 else if (c >= 040 && c < 0177)
246 col++;
993b6404
JB
247 else if (c == '\n')
248 break;
249 else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
250 break;
251 else if (c == '\t')
252 {
253 if (tab_seen)
254 col = ((col + tab_width) / tab_width) * tab_width;
255
256 post_tab += col;
257 col = 0;
258 tab_seen = 1;
259 }
993b6404
JB
260 else
261 col += (ctl_arrow && c < 0200) ? 2 : 4;
262 }
263
264 if (tab_seen)
265 {
266 col = ((col + tab_width) / tab_width) * tab_width;
267 col += post_tab;
268 }
269
270 last_known_column = col;
271 last_known_column_point = point;
272 last_known_column_modified = MODIFF;
273
274 return col;
275}
276\f
c412c808
RS
277/* Return the width in columns of the part of STRING from BEG to END.
278 If BEG is nil, that stands for the beginning of STRING.
279 If END is nil, that stands for the end of STRING. */
280
281static int
382ac0bd 282string_display_width (string, beg, end)
c412c808
RS
283 Lisp_Object string, beg, end;
284{
285 register int col;
286 register unsigned char *ptr, *stop;
287 register int tab_seen;
288 int post_tab;
289 register int c;
290 register int tab_width = XINT (current_buffer->tab_width);
291 int ctl_arrow = !NILP (current_buffer->ctl_arrow);
292 register struct Lisp_Vector *dp = buffer_display_table ();
293 int b, e;
294
295 if (NILP (end))
296 e = XSTRING (string)->size;
297 else
298 {
299 CHECK_NUMBER (end, 0);
300 e = XINT (end);
301 }
302
303 if (NILP (beg))
304 b = 0;
305 else
306 {
307 CHECK_NUMBER (beg, 0);
308 b = XINT (beg);
309 }
310
311 /* Make a pointer for decrementing through the chars before point. */
312 ptr = XSTRING (string)->data + e;
313 /* Make a pointer to where consecutive chars leave off,
314 going backwards from point. */
315 stop = XSTRING (string)->data + b;
316
317 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
318
319 col = 0, tab_seen = 0, post_tab = 0;
320
321 while (1)
322 {
323 if (ptr == stop)
324 break;
325
326 c = *--ptr;
327 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
328 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
329 else if (c >= 040 && c < 0177)
330 col++;
331 else if (c == '\n')
332 break;
333 else if (c == '\t')
334 {
335 if (tab_seen)
336 col = ((col + tab_width) / tab_width) * tab_width;
337
338 post_tab += col;
339 col = 0;
340 tab_seen = 1;
341 }
342 else
343 col += (ctl_arrow && c < 0200) ? 2 : 4;
344 }
345
346 if (tab_seen)
347 {
348 col = ((col + tab_width) / tab_width) * tab_width;
349 col += post_tab;
350 }
351
352 return col;
353}
354\f
993b6404
JB
355DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
356 "Indent from point with tabs and spaces until COLUMN is reached.\n\
357Optional second argument MIN says always do at least MIN spaces\n\
358even if that goes past COLUMN; by default, MIN is zero.")
359 (col, minimum)
360 Lisp_Object col, minimum;
361{
362 int mincol;
363 register int fromcol;
364 register int tab_width = XINT (current_buffer->tab_width);
365
366 CHECK_NUMBER (col, 0);
56a98455 367 if (NILP (minimum))
94d92e9c 368 XSETFASTINT (minimum, 0);
993b6404
JB
369 CHECK_NUMBER (minimum, 1);
370
371 fromcol = current_column ();
372 mincol = fromcol + XINT (minimum);
373 if (mincol < XINT (col)) mincol = XINT (col);
374
375 if (fromcol == mincol)
376 return make_number (mincol);
377
ccdcf1f5 378 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
379
380 if (indent_tabs_mode)
381 {
382 Lisp_Object n;
94d92e9c 383 XSETFASTINT (n, mincol / tab_width - fromcol / tab_width);
993b6404
JB
384 if (XFASTINT (n) != 0)
385 {
6d1bd1a5 386 Finsert_char (make_number ('\t'), n, Qt);
993b6404
JB
387
388 fromcol = (mincol / tab_width) * tab_width;
389 }
390 }
391
94d92e9c 392 XSETFASTINT (col, mincol - fromcol);
6d1bd1a5 393 Finsert_char (make_number (' '), col, Qt);
993b6404
JB
394
395 last_known_column = mincol;
396 last_known_column_point = point;
397 last_known_column_modified = MODIFF;
398
399 XSETINT (col, mincol);
400 return col;
401}
0aa01123 402
993b6404
JB
403\f
404DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
405 0, 0, 0,
406 "Return the indentation of the current line.\n\
407This is the horizontal position of the character\n\
408following any initial whitespace.")
409 ()
410{
411 Lisp_Object val;
412
94d92e9c 413 XSETFASTINT (val, position_indentation (find_next_newline (point, -1)));
993b6404
JB
414 return val;
415}
416
417position_indentation (pos)
418 register int pos;
419{
420 register int column = 0;
421 register int tab_width = XINT (current_buffer->tab_width);
422 register unsigned char *p;
423 register unsigned char *stop;
2ff4775b 424
ccdcf1f5 425 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
2ff4775b 426
993b6404
JB
427 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
428 p = &FETCH_CHAR (pos);
429 while (1)
430 {
431 while (p == stop)
432 {
433 if (pos == ZV)
434 return column;
435 pos += p - &FETCH_CHAR (pos);
436 p = &FETCH_CHAR (pos);
437 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
438 }
439 switch (*p++)
440 {
441 case ' ':
442 column++;
443 break;
444 case '\t':
445 column += tab_width - column % tab_width;
446 break;
447 default:
448 return column;
449 }
450 }
451}
1b15e576
KH
452
453/* Test whether the line beginning at POS is indented beyond COLUMN.
454 Blank lines are treated as if they had the same indentation as the
455 preceding line. */
456int
457indented_beyond_p (pos, column)
458 int pos, column;
459{
460 while (pos > BEGV && FETCH_CHAR (pos) == '\n')
04d25c3d 461 pos = find_next_newline_no_quit (pos - 1, -1);
1b15e576
KH
462 return (position_indentation (pos) >= column);
463}
0aa01123 464
993b6404
JB
465\f
466DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
467 "Move point to column COLUMN in the current line.\n\
468The column of a character is calculated by adding together the widths\n\
469as displayed of the previous characters in the line.\n\
470This function ignores line-continuation;\n\
471there is no upper limit on the column number a character can have\n\
230a4cbd
JB
472and horizontal scrolling has no effect.\n\
473\n\
993b6404
JB
474If specified column is within a character, point goes after that character.\n\
475If it's past end of line, point goes to end of line.\n\n\
476A non-nil second (optional) argument FORCE means, if the line\n\
477is too short to reach column COLUMN then add spaces/tabs to get there,\n\
478and if COLUMN is in the middle of a tab character, change it to spaces.")
479 (column, force)
480 Lisp_Object column, force;
481{
482 register int pos;
483 register int col = current_column ();
484 register int goal;
485 register int end;
486 register int tab_width = XINT (current_buffer->tab_width);
56a98455 487 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
993b6404
JB
488 register struct Lisp_Vector *dp = buffer_display_table ();
489
490 Lisp_Object val;
491 int prev_col;
492 int c;
493
ccdcf1f5 494 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
495 CHECK_NATNUM (column, 0);
496 goal = XINT (column);
497
498 retry:
499 pos = point;
500 end = ZV;
501
502 /* If we're starting past the desired column,
503 back up to beginning of line and scan from there. */
504 if (col > goal)
505 {
506 pos = find_next_newline (pos, -1);
507 col = 0;
508 }
509
510 while (col < goal && pos < end)
511 {
512 c = FETCH_CHAR (pos);
f845b8b2
RS
513 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
514 {
515 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
516 pos++;
0088a46e 517 continue;
f845b8b2 518 }
993b6404
JB
519 if (c == '\n')
520 break;
521 if (c == '\r' && EQ (current_buffer->selective_display, Qt))
522 break;
523 pos++;
524 if (c == '\t')
525 {
526 prev_col = col;
527 col += tab_width;
528 col = col / tab_width * tab_width;
529 }
993b6404 530 else if (ctl_arrow && (c < 040 || c == 0177))
bbc2998f 531 col += 2;
993b6404 532 else if (c < 040 || c >= 0177)
bbc2998f 533 col += 4;
993b6404
JB
534 else
535 col++;
536 }
537
538 SET_PT (pos);
539
540 /* If a tab char made us overshoot, change it to spaces
541 and scan through it again. */
56a98455 542 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
993b6404 543 {
70ee42f7
JB
544 int old_point;
545
993b6404 546 del_range (point - 1, point);
70ee42f7
JB
547 Findent_to (make_number (goal), Qnil);
548 old_point = point;
549 Findent_to (make_number (col), Qnil);
550 SET_PT (old_point);
5a05d3d2
RS
551 /* Set the last_known... vars consistently. */
552 col = goal;
993b6404
JB
553 }
554
555 /* If line ends prematurely, add space to the end. */
56a98455 556 if (col < goal && !NILP (force))
230a4cbd 557 Findent_to (make_number (col = goal), Qnil);
993b6404
JB
558
559 last_known_column = col;
560 last_known_column_point = point;
561 last_known_column_modified = MODIFF;
562
94d92e9c 563 XSETFASTINT (val, col);
993b6404
JB
564 return val;
565}
0aa01123 566
993b6404 567\f
0aa01123
JB
568/* compute_motion: compute buffer posn given screen posn and vice versa */
569
993b6404
JB
570struct position val_compute_motion;
571
572/* Scan the current buffer forward from offset FROM, pretending that
573 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
574 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
0aa01123
JB
575 and return the ending buffer position and screen location. If we
576 can't hit the requested column exactly (because of a tab or other
577 multi-column character), overshoot.
993b6404 578
2ab90d49
KH
579 DID_MOTION is 1 if FROMHPOS has already accounted for overlay strings
580 at FROM. This is the case if FROMVPOS and FROMVPOS came from an
581 earlier call to compute_motion. The other common case is that FROMHPOS
582 is zero and FROM is a position that "belongs" at column zero, but might
583 be shifted by overlay strings; in this case DID_MOTION should be 0.
584
993b6404
JB
585 WIDTH is the number of columns available to display text;
586 compute_motion uses this to handle continuation lines and such.
587 HSCROLL is the number of columns not being displayed at the left
588 margin; this is usually taken from a window's hscroll member.
a9764248
JB
589 TAB_OFFSET is the number of columns of the first tab that aren't
590 being displayed, perhaps because of a continuation line or
591 something.
993b6404
JB
592
593 compute_motion returns a pointer to a struct position. The bufpos
594 member gives the buffer position at the end of the scan, and hpos
0aa01123
JB
595 and vpos give its cartesian location. prevhpos is the column at
596 which the character before bufpos started, and contin is non-zero
597 if we reached the current line by continuing the previous.
598
599 Note that FROMHPOS and TOHPOS should be expressed in real screen
600 columns, taking HSCROLL and the truncation glyph at the left margin
601 into account. That is, beginning-of-line moves you to the hpos
602 -HSCROLL + (HSCROLL > 0).
993b6404 603
1827d036
KH
604 Note that FROMHPOS and TOHPOS should be expressed in real screen
605 columns, taking HSCROLL and the truncation glyph at the left margin
606 into account. That is, beginning-of-line moves you to the hpos
607 -HSCROLL + (HSCROLL > 0).
608
993b6404
JB
609 For example, to find the buffer position of column COL of line LINE
610 of a certain window, pass the window's starting location as FROM
611 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
612 Pass the buffer's ZV as TO, to limit the scan to the end of the
613 visible section of the buffer, and pass LINE and COL as TOVPOS and
2ff4775b 614 TOHPOS.
993b6404
JB
615
616 When displaying in window w, a typical formula for WIDTH is:
617
618 window_width - 1
a3c87d4e 619 - (has_vertical_scroll_bars
40284d6b 620 ? FRAME_SCROLL_BAR_COLS (XFRAME (window->frame))
fa61c701 621 : (window_width + window_left != frame_width))
993b6404
JB
622
623 where
624 window_width is XFASTINT (w->width),
625 window_left is XFASTINT (w->left),
a3c87d4e
JB
626 has_vertical_scroll_bars is
627 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
fa61c701 628 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
993b6404 629
1827d036
KH
630 Or you can let window_internal_width do this all for you, and write:
631 window_internal_width (w) - 1
fa61c701
JB
632
633 The `-1' accounts for the continuation-line backslashes; the rest
7e7a76b5 634 accounts for window borders if the window is split horizontally, and
1827d036 635 the scroll bars if they are turned on. */
993b6404
JB
636
637struct position *
2ab90d49 638compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, hscroll, tab_offset, win)
993b6404 639 int from, fromvpos, fromhpos, to, tovpos, tohpos;
2ab90d49 640 int did_motion;
993b6404
JB
641 register int width;
642 int hscroll, tab_offset;
88af3af4 643 struct window *win;
993b6404 644{
cde9337b
JB
645 register int hpos = fromhpos;
646 register int vpos = fromvpos;
647
993b6404
JB
648 register int pos;
649 register int c;
650 register int tab_width = XFASTINT (current_buffer->tab_width);
56a98455 651 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
88af3af4 652 register struct Lisp_Vector *dp = window_display_table (win);
993b6404 653 int selective
eeaafd4f 654 = (INTEGERP (current_buffer->selective_display)
69eaf10d
RS
655 ? XINT (current_buffer->selective_display)
656 : !NILP (current_buffer->selective_display) ? -1 : 0);
0aa01123 657 int prev_vpos = vpos, prev_hpos = 0;
993b6404 658 int selective_rlen
eeaafd4f 659 = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
dea4d2e6 660 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
2ab90d49
KH
661 /* The next location where the `invisible' property changes, or an
662 overlay starts or ends. */
663 int next_boundary = from;
993b6404 664
0aa01123
JB
665 /* For computing runs of characters with similar widths.
666 Invariant: width_run_width is zero, or all the characters
2ff4775b 667 from width_run_start to width_run_end have a fixed width of
0aa01123
JB
668 width_run_width. */
669 int width_run_start = from;
670 int width_run_end = from;
671 int width_run_width = 0;
672 Lisp_Object *width_table;
66c75ca5 673 Lisp_Object buffer;
0aa01123
JB
674
675 /* The next buffer pos where we should consult the width run cache. */
676 int next_width_run = from;
677
66c75ca5
RS
678 XSETBUFFER (buffer, current_buffer);
679
0aa01123
JB
680 width_run_cache_on_off ();
681 if (dp == buffer_display_table ())
682 width_table = (VECTORP (current_buffer->width_table)
683 ? XVECTOR (current_buffer->width_table)->contents
684 : 0);
685 else
686 /* If the window has its own display table, we can't use the width
687 run cache, because that's based on the buffer's display table. */
688 width_table = 0;
689
ccdcf1f5 690 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
cde9337b 691
2ab90d49
KH
692 pos = from;
693 while (1)
694 {
695 while (pos == next_boundary)
f75c0f8a 696 {
2ab90d49
KH
697 /* If the caller says that the screen position came from an earlier
698 call to compute_motion, then we've already accounted for the
699 overlay strings at point. This is only true the first time
700 through, so clear the flag after testing it. */
701 if (!did_motion)
702 /* We need to skip past the overlay strings. Currently those
703 strings must contain single-column printing characters;
704 if we want to relax that restriction, something will have
705 to be changed here. */
706 hpos += overlay_strings (pos, win, (char **)0);
707 did_motion = 0;
708
709 if (pos >= to)
710 break;
66c75ca5 711
f75c0f8a 712 {
2ab90d49
KH
713 Lisp_Object prop, position, end, limit, proplimit;
714
715 XSETFASTINT (position, pos);
716
717 /* Give faster response for overlay lookup near POS. */
718 recenter_overlay_lists (current_buffer, pos);
f75c0f8a 719
f845b8b2
RS
720 /* We must not advance farther than the next overlay change.
721 The overlay change might change the invisible property;
2ab90d49 722 or there might be overlay strings to be displayed there. */
7ea9c020 723 limit = Fnext_overlay_change (position);
f845b8b2
RS
724 /* As for text properties, this gives a lower bound
725 for where the invisible text property could change. */
66c75ca5 726 proplimit = Fnext_property_change (position, buffer, Qt);
f845b8b2
RS
727 if (XFASTINT (limit) < XFASTINT (proplimit))
728 proplimit = limit;
729 /* PROPLIMIT is now a lower bound for the next change
66c75ca5
RS
730 in invisible status. If that is plenty far away,
731 use that lower bound. */
f845b8b2 732 if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
2ab90d49 733 next_boundary = XFASTINT (proplimit);
66c75ca5 734 /* Otherwise, scan for the next `invisible' property change. */
f75c0f8a 735 else
66c75ca5
RS
736 {
737 /* Don't scan terribly far. */
f845b8b2
RS
738 XSETFASTINT (proplimit, min (pos + 100, to));
739 /* No matter what. don't go past next overlay change. */
740 if (XFASTINT (limit) < XFASTINT (proplimit))
741 proplimit = limit;
66c75ca5 742 end = Fnext_single_property_change (position, Qinvisible,
f845b8b2 743 buffer, proplimit);
2ab90d49 744 next_boundary = XFASTINT (end);
66c75ca5 745 }
2ab90d49
KH
746 /* if the `invisible' property is set, we can skip to
747 the next property change */
748 prop = Fget_char_property (position, Qinvisible,
749 Fcurrent_buffer ());
66c75ca5 750 if (TEXT_PROP_MEANS_INVISIBLE (prop))
2ab90d49 751 pos = next_boundary;
f75c0f8a
RS
752 }
753 }
2ab90d49
KH
754
755 /* Handle right margin. */
756 if (hpos >= width
757 && (hpos > width
758 || (pos < ZV && FETCH_CHAR (pos) != '\n')))
759 {
760 if (hscroll
761 || (truncate_partial_width_windows
762 && width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
763 || !NILP (current_buffer->truncate_lines))
764 {
765 /* Truncating: skip to newline. */
766 pos = find_before_next_newline (pos, to, 1);
767 hpos = width;
768 }
769 else
770 {
771 /* Continuing. */
772 vpos += hpos / width;
773 tab_offset += hpos - hpos % width;
774 hpos %= width;
775 }
776 }
777
778 /* Stop if past the target buffer position or screen position. */
5a05d3d2
RS
779 if (pos >= to)
780 break;
2ab90d49
KH
781 if (vpos > tovpos || (vpos == tovpos && hpos >= tohpos))
782 break;
783
784 prev_vpos = vpos;
785 prev_hpos = hpos;
0aa01123
JB
786
787 /* Consult the width run cache to see if we can avoid inspecting
788 the text character-by-character. */
789 if (current_buffer->width_run_cache && pos >= next_width_run)
790 {
791 int run_end;
792 int common_width
793 = region_cache_forward (current_buffer,
794 current_buffer->width_run_cache,
795 pos, &run_end);
796
797 /* A width of zero means the character's width varies (like
798 a tab), is meaningless (like a newline), or we just don't
799 want to skip over it for some other reason. */
800 if (common_width != 0)
801 {
802 int run_end_hpos;
803
804 /* Don't go past the final buffer posn the user
805 requested. */
806 if (run_end > to)
807 run_end = to;
808
809 run_end_hpos = hpos + (run_end - pos) * common_width;
810
811 /* Don't go past the final horizontal position the user
812 requested. */
813 if (vpos == tovpos && run_end_hpos > tohpos)
814 {
815 run_end = pos + (tohpos - hpos) / common_width;
816 run_end_hpos = hpos + (run_end - pos) * common_width;
817 }
2ff4775b 818
0aa01123
JB
819 /* Don't go past the margin. */
820 if (run_end_hpos >= width)
821 {
822 run_end = pos + (width - hpos) / common_width;
823 run_end_hpos = hpos + (run_end - pos) * common_width;
824 }
825
826 hpos = run_end_hpos;
827 if (run_end > pos)
828 prev_hpos = hpos - common_width;
829 pos = run_end;
830 }
831
832 next_width_run = run_end + 1;
833 }
834
835 /* We have to scan the text character-by-character. */
993b6404 836 else
2ab90d49
KH
837 {
838 c = FETCH_CHAR (pos);
839 pos++;
0aa01123 840
2ab90d49
KH
841 /* Perhaps add some info to the width_run_cache. */
842 if (current_buffer->width_run_cache)
843 {
844 /* Is this character part of the current run? If so, extend
845 the run. */
846 if (pos - 1 == width_run_end
847 && width_table[c] == width_run_width)
848 width_run_end = pos;
849
850 /* The previous run is over, since this is a character at a
851 different position, or a different width. */
852 else
853 {
854 /* Have we accumulated a run to put in the cache?
855 (Currently, we only cache runs of width == 1). */
856 if (width_run_start < width_run_end
857 && width_run_width == 1)
858 know_region_cache (current_buffer,
859 current_buffer->width_run_cache,
860 width_run_start, width_run_end);
861
862 /* Start recording a new width run. */
863 width_run_width = width_table[c];
864 width_run_start = pos - 1;
865 width_run_end = pos;
866 }
867 }
993b6404 868
2ab90d49
KH
869 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
870 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
871 else if (c >= 040 && c < 0177)
872 hpos++;
873 else if (c == '\t')
993b6404 874 {
2ab90d49
KH
875 int tem = (hpos + tab_offset + hscroll - (hscroll > 0)) % tab_width;
876 if (tem < 0)
877 tem += tab_width;
878 hpos += tab_width - tem;
993b6404 879 }
2ab90d49 880 else if (c == '\n')
993b6404 881 {
2ab90d49
KH
882 if (selective > 0 && indented_beyond_p (pos, selective))
883 {
884 /* Skip any number of invisible lines all at once */
885 do
886 pos = find_before_next_newline (pos, to, 1) + 1;
887 while (pos < to
888 && indented_beyond_p (pos, selective));
889 /* Allow for the " ..." that is displayed for them. */
890 if (selective_rlen)
891 {
892 hpos += selective_rlen;
893 if (hpos >= width)
894 hpos = width;
895 }
896 --pos;
897 /* We have skipped the invis text, but not the
898 newline after. */
899 }
900 else
901 {
902 /* A visible line. */
903 vpos++;
904 hpos = 0;
905 hpos -= hscroll;
906 /* Count the truncation glyph on column 0 */
907 if (hscroll > 0)
908 hpos++;
909 tab_offset = 0;
910 }
993b6404 911 }
2ab90d49
KH
912 else if (c == CR && selective < 0)
913 {
914 /* In selective display mode,
915 everything from a ^M to the end of the line is invisible.
916 Stop *before* the real newline. */
917 pos = find_before_next_newline (pos, to, 1);
918 /* Allow for the " ..." that is displayed for them. */
919 if (selective_rlen)
920 {
921 hpos += selective_rlen;
922 if (hpos >= width)
923 hpos = width;
924 }
925 }
926 else
927 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
993b6404
JB
928 }
929 }
930
0aa01123
JB
931 /* Remember any final width run in the cache. */
932 if (current_buffer->width_run_cache
933 && width_run_width == 1
934 && width_run_start < width_run_end)
935 know_region_cache (current_buffer, current_buffer->width_run_cache,
936 width_run_start, width_run_end);
937
993b6404 938 val_compute_motion.bufpos = pos;
cde9337b
JB
939 val_compute_motion.hpos = hpos;
940 val_compute_motion.vpos = vpos;
941 val_compute_motion.prevhpos = prev_hpos;
993b6404
JB
942
943 /* Nonzero if have just continued a line */
944 val_compute_motion.contin
cde9337b
JB
945 = (pos != from
946 && (val_compute_motion.vpos != prev_vpos)
947 && c != '\n');
993b6404
JB
948
949 return &val_compute_motion;
950}
993b6404 951
992371ca
KH
952#if 0 /* The doc string is too long for some compilers,
953 but make-docfile can find it in this comment. */
88af3af4 954DEFUN ("compute-motion", Ffoo, Sfoo, 7, 7, 0,
42918ba5
RS
955 "Scan through the current buffer, calculating screen position.\n\
956Scan the current buffer forward from offset FROM,\n\
957assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
958to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
959and return the ending buffer position and screen location.\n\
960\n\
88af3af4 961There are three additional arguments:\n\
42918ba5
RS
962\n\
963WIDTH is the number of columns available to display text;\n\
964this affects handling of continuation lines.\n\
992371ca
KH
965This is usually the value returned by `window-width', less one (to allow\n\
966for the continuation glyph).\n\
42918ba5
RS
967\n\
968OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
969HSCROLL is the number of columns not being displayed at the left\n\
970margin; this is usually taken from a window's hscroll member.\n\
971TAB-OFFSET is the number of columns of the first tab that aren't\n\
972being displayed, perhaps because the line was continued within it.\n\
4fb76787 973If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
69eaf10d 974\n\
88af3af4 975WINDOW is the window to operate on. Currently this is used only to\n\
69eaf10d
RS
976find the display table. It does not matter what buffer WINDOW displays;\n\
977`compute-motion' always operates on the current buffer.\n\
42918ba5
RS
978\n\
979The value is a list of five elements:\n\
faa5c515 980 (POS HPOS VPOS PREVHPOS CONTIN)\n\
42918ba5
RS
981POS is the buffer position where the scan stopped.\n\
982VPOS is the vertical position where the scan stopped.\n\
983HPOS is the horizontal position where the scan stopped.\n\
984\n\
985PREVHPOS is the horizontal position one character back from POS.\n\
986CONTIN is t if a line was continued after (or within) the previous character.\n\
987\n\
988For example, to find the buffer position of column COL of line LINE\n\
989of a certain window, pass the window's starting location as FROM\n\
990and the window's upper-left coordinates as FROMPOS.\n\
991Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
992visible section of the buffer, and pass LINE and COL as TOPOS.")
5844e1c4 993 (from, frompos, to, topos, width, offsets, window)
992371ca
KH
994#endif
995
88af3af4 996DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
992371ca 997 0)
88af3af4 998 (from, frompos, to, topos, width, offsets, window)
42918ba5 999 Lisp_Object from, frompos, to, topos;
88af3af4 1000 Lisp_Object width, offsets, window;
42918ba5
RS
1001{
1002 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
1003 struct position *pos;
1004 int hscroll, tab_offset;
1005
26a8d14b 1006 CHECK_NUMBER_COERCE_MARKER (from, 0);
42918ba5 1007 CHECK_CONS (frompos, 0);
26a8d14b
KH
1008 CHECK_NUMBER (XCONS (frompos)->car, 0);
1009 CHECK_NUMBER (XCONS (frompos)->cdr, 0);
1010 CHECK_NUMBER_COERCE_MARKER (to, 0);
42918ba5 1011 CHECK_CONS (topos, 0);
26a8d14b
KH
1012 CHECK_NUMBER (XCONS (topos)->car, 0);
1013 CHECK_NUMBER (XCONS (topos)->cdr, 0);
1014 CHECK_NUMBER (width, 0);
42918ba5
RS
1015 if (!NILP (offsets))
1016 {
1017 CHECK_CONS (offsets, 0);
26a8d14b
KH
1018 CHECK_NUMBER (XCONS (offsets)->car, 0);
1019 CHECK_NUMBER (XCONS (offsets)->cdr, 0);
42918ba5
RS
1020 hscroll = XINT (XCONS (offsets)->car);
1021 tab_offset = XINT (XCONS (offsets)->cdr);
1022 }
1023 else
1024 hscroll = tab_offset = 0;
1025
88af3af4
KH
1026 if (NILP (window))
1027 window = Fselected_window ();
1028 else
1029 CHECK_LIVE_WINDOW (window, 0);
1030
42918ba5 1031 pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
2ab90d49 1032 XINT (XCONS (frompos)->car), 0,
42918ba5
RS
1033 XINT (to), XINT (XCONS (topos)->cdr),
1034 XINT (XCONS (topos)->car),
88af3af4
KH
1035 XINT (width), hscroll, tab_offset,
1036 XWINDOW (window));
42918ba5 1037
94d92e9c 1038 XSETFASTINT (bufpos, pos->bufpos);
f8f645a1
KH
1039 XSETINT (hpos, pos->hpos);
1040 XSETINT (vpos, pos->vpos);
1041 XSETINT (prevhpos, pos->prevhpos);
42918ba5
RS
1042
1043 return Fcons (bufpos,
1044 Fcons (hpos,
1045 Fcons (vpos,
1046 Fcons (prevhpos,
1047 Fcons (pos->contin ? Qt : Qnil, Qnil)))));
1048
1049}
993b6404 1050\f
0aa01123
JB
1051/* Return the column of position POS in window W's buffer.
1052 The result is rounded down to a multiple of the internal width of W.
993b6404
JB
1053 This is the amount of indentation of position POS
1054 that is not visible in its horizontal position in the window. */
1055
1056int
1057pos_tab_offset (w, pos)
1058 struct window *w;
1059 register int pos;
1060{
3d94e943 1061 int opoint = PT;
993b6404 1062 int col;
fa61c701 1063 int width = window_internal_width (w) - 1;
993b6404
JB
1064
1065 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
1066 return 0;
3d94e943 1067 TEMP_SET_PT (pos);
993b6404 1068 col = current_column ();
3d94e943 1069 TEMP_SET_PT (opoint);
993b6404
JB
1070 return col - (col % width);
1071}
1072
0aa01123
JB
1073\f
1074/* Fvertical_motion and vmotion */
993b6404
JB
1075struct position val_vmotion;
1076
1077struct position *
99ce22d5
KH
1078vmotion (from, vtarget, w)
1079 register int from, vtarget;
1080 struct window *w;
993b6404 1081{
99ce22d5
KH
1082 int width = window_internal_width (w) - 1;
1083 int hscroll = XINT (w->hscroll);
993b6404
JB
1084 struct position pos;
1085 /* vpos is cumulative vertical position, changed as from is changed */
1086 register int vpos = 0;
92992c7e 1087 Lisp_Object prevline;
993b6404
JB
1088 register int first;
1089 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
1090 int selective
eeaafd4f
KH
1091 = (INTEGERP (current_buffer->selective_display)
1092 ? XINT (current_buffer->selective_display)
1093 : !NILP (current_buffer->selective_display) ? -1 : 0);
99ce22d5
KH
1094 Lisp_Object window;
1095 int start_hpos = 0;
2ab90d49 1096 int did_motion;
99ce22d5
KH
1097
1098 XSETWINDOW (window, w);
1099
cb1068e5 1100 /* The omission of the clause
99ce22d5 1101 && marker_position (w->start) == BEG
cb1068e5
KH
1102 here is deliberate; I think we want to measure from the prompt
1103 position even if the minibuffer window has scrolled. */
c412c808
RS
1104 if (EQ (window, minibuf_window))
1105 {
39fc6077 1106 if (minibuf_prompt_width == 0 && STRINGP (minibuf_prompt))
382ac0bd
RS
1107 minibuf_prompt_width
1108 = string_display_width (minibuf_prompt, Qnil, Qnil);
c412c808
RS
1109
1110 start_hpos = minibuf_prompt_width;
1111 }
993b6404 1112
99ce22d5 1113 if (vpos >= vtarget)
993b6404 1114 {
99ce22d5
KH
1115 /* To move upward, go a line at a time until
1116 we have gone at least far enough */
1117
1118 first = 1;
1119
1120 while ((vpos > vtarget || first) && from > BEGV)
993b6404 1121 {
66c75ca5
RS
1122 Lisp_Object propval;
1123
99ce22d5 1124 XSETFASTINT (prevline, find_next_newline_no_quit (from - 1, -1));
92992c7e 1125 while (XFASTINT (prevline) > BEGV
5a05d3d2 1126 && ((selective > 0
92992c7e 1127 && indented_beyond_p (XFASTINT (prevline), selective))
5a05d3d2
RS
1128#ifdef USE_TEXT_PROPERTIES
1129 /* watch out for newlines with `invisible' property */
66c75ca5
RS
1130 || (propval = Fget_char_property (prevline,
1131 Qinvisible,
1132 window),
1133 TEXT_PROP_MEANS_INVISIBLE (propval))
5a05d3d2 1134#endif
99ce22d5 1135 ))
94d92e9c
KH
1136 XSETFASTINT (prevline,
1137 find_next_newline_no_quit (XFASTINT (prevline) - 1,
1138 -1));
92992c7e 1139 pos = *compute_motion (XFASTINT (prevline), 0,
99ce22d5 1140 lmargin + (XFASTINT (prevline) == BEG
92992c7e 1141 ? start_hpos : 0),
2ab90d49 1142 0,
cde9337b 1143 from, 1 << (INTBITS - 2), 0,
99ce22d5
KH
1144 width, hscroll, 0, w);
1145 vpos -= pos.vpos;
1146 first = 0;
1147 from = XFASTINT (prevline);
993b6404 1148 }
99ce22d5
KH
1149
1150 /* If we made exactly the desired vertical distance,
1151 or if we hit beginning of buffer,
1152 return point found */
1153 if (vpos >= vtarget)
993b6404 1154 {
99ce22d5
KH
1155 val_vmotion.bufpos = from;
1156 val_vmotion.vpos = vpos;
1157 val_vmotion.hpos = lmargin;
1158 val_vmotion.contin = 0;
1159 val_vmotion.prevhpos = 0;
1160 return &val_vmotion;
993b6404 1161 }
993b6404 1162
99ce22d5
KH
1163 /* Otherwise find the correct spot by moving down */
1164 }
1165 /* Moving downward is simple, but must calculate from beg of line
1166 to determine hpos of starting point */
1167 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
993b6404 1168 {
2ab90d49 1169 Lisp_Object propval;
66c75ca5 1170
99ce22d5
KH
1171 XSETFASTINT (prevline, find_next_newline_no_quit (from, -1));
1172 while (XFASTINT (prevline) > BEGV
1173 && ((selective > 0
1174 && indented_beyond_p (XFASTINT (prevline), selective))
5a05d3d2 1175#ifdef USE_TEXT_PROPERTIES
99ce22d5
KH
1176 /* watch out for newlines with `invisible' property */
1177 || (propval = Fget_char_property (prevline, Qinvisible,
1178 window),
1179 TEXT_PROP_MEANS_INVISIBLE (propval))
5a05d3d2 1180#endif
99ce22d5
KH
1181 ))
1182 XSETFASTINT (prevline,
1183 find_next_newline_no_quit (XFASTINT (prevline) - 1,
1184 -1));
92992c7e 1185 pos = *compute_motion (XFASTINT (prevline), 0,
99ce22d5 1186 lmargin + (XFASTINT (prevline) == BEG
92992c7e 1187 ? start_hpos : 0),
2ab90d49 1188 0,
cde9337b 1189 from, 1 << (INTBITS - 2), 0,
99ce22d5 1190 width, hscroll, 0, w);
2ab90d49 1191 did_motion = 1;
993b6404 1192 }
99ce22d5 1193 else
993b6404 1194 {
99ce22d5
KH
1195 pos.hpos = lmargin + (from == BEG ? start_hpos : 0);
1196 pos.vpos = 0;
2ab90d49 1197 did_motion = 0;
993b6404 1198 }
2ab90d49 1199 return compute_motion (from, vpos, pos.hpos, did_motion,
99ce22d5
KH
1200 ZV, vtarget, - (1 << (INTBITS - 2)),
1201 width, hscroll, pos.vpos * width, w);
993b6404
JB
1202}
1203
f1ecfe9b 1204DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
993b6404
JB
1205 "Move to start of screen line LINES lines down.\n\
1206If LINES is negative, this is moving up.\n\
69eaf10d
RS
1207\n\
1208The optional second argument WINDOW specifies the window to use for\n\
1209parameters such as width, horizontal scrolling, and so on.\n\
1210the default is the selected window.\n\
1211It does not matter what buffer is displayed in WINDOW.\n\
1212`vertical-motion' always uses the current buffer.\n\
1213\n\
993b6404 1214Sets point to position found; this may be start of line\n\
69eaf10d 1215or just the start of a continuation line.\n\
993b6404 1216Returns number of lines moved; may be closer to zero than LINES\n\
69eaf10d 1217if beginning or end of buffer was reached.")
f1ecfe9b
RS
1218 (lines, window)
1219 Lisp_Object lines, window;
993b6404
JB
1220{
1221 struct position pos;
993b6404
JB
1222
1223 CHECK_NUMBER (lines, 0);
f1ecfe9b
RS
1224 if (! NILP (window))
1225 CHECK_WINDOW (window, 0);
1226 else
92992c7e 1227 window = selected_window;
993b6404 1228
ef423eac 1229 pos = *vmotion (point, (int) XINT (lines), XWINDOW (window));
993b6404
JB
1230
1231 SET_PT (pos.bufpos);
1232 return make_number (pos.vpos);
1233}
1234\f
0aa01123
JB
1235/* file's initialization. */
1236
993b6404
JB
1237syms_of_indent ()
1238{
1239 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
1240 "*Indentation can insert tabs if this is non-nil.\n\
1241Setting this variable automatically makes it local to the current buffer.");
1242 indent_tabs_mode = 1;
1243
1244 defsubr (&Scurrent_indentation);
1245 defsubr (&Sindent_to);
1246 defsubr (&Scurrent_column);
1247 defsubr (&Smove_to_column);
1248 defsubr (&Svertical_motion);
42918ba5 1249 defsubr (&Scompute_motion);
993b6404 1250}