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