Put stdio.h, sys/types.h and sys/stat.h after config.h.
[bpt/emacs.git] / src / indent.c
... / ...
CommitLineData
1/* Indentation functions.
2 Copyright (C) 1985,86,87,88,93,94 Free Software Foundation, Inc.
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
8the Free Software Foundation; either version 1, or (at your option)
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
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
32/* Indentation can insert tabs if this is non-zero;
33 otherwise always uses spaces */
34int indent_tabs_mode;
35
36#define min(a, b) ((a) < (b) ? (a) : (b))
37#define max(a, b) ((a) > (b) ? (a) : (b))
38
39#define CR 015
40
41/* These three values memoize the current column to avoid recalculation */
42/* Some things in set last_known_column_point to -1
43 to mark the memoized value as invalid */
44/* Last value returned by current_column */
45int last_known_column;
46/* Value of point when current_column was called */
47int last_known_column_point;
48/* Value of MODIFF when current_column was called */
49int last_known_column_modified;
50
51/* Get the display table to use for the current buffer. */
52
53struct Lisp_Vector *
54buffer_display_table ()
55{
56 Lisp_Object thisbuf;
57
58 thisbuf = current_buffer->display_table;
59 if (XTYPE (thisbuf) == Lisp_Vector
60 && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE)
61 return XVECTOR (thisbuf);
62 if (XTYPE (Vstandard_display_table) == Lisp_Vector
63 && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
64 return XVECTOR (Vstandard_display_table);
65 return 0;
66}
67\f
68DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
69 "Return the horizontal position of point. Beginning of line is column 0.\n\
70This is calculated by adding together the widths of all the displayed\n\
71representations of the character between the start of the previous line\n\
72and point. (eg control characters will have a width of 2 or 4, tabs\n\
73will have a variable width)\n\
74Ignores finite width of frame, which means that this function may return\n\
75values greater than (frame-width).\n\
76Whether the line is visible (if `selective-display' is t) has no effect;\n\
77however, ^M is treated as end of line when `selective-display' is t.")
78 ()
79{
80 Lisp_Object temp;
81 XFASTINT (temp) = current_column ();
82 return temp;
83}
84
85/* Cancel any recorded value of the horizontal position. */
86
87invalidate_current_column ()
88{
89 last_known_column_point = 0;
90}
91
92int
93current_column ()
94{
95 register int col;
96 register unsigned char *ptr, *stop;
97 register int tab_seen;
98 int post_tab;
99 register int c;
100 register int tab_width = XINT (current_buffer->tab_width);
101 int ctl_arrow = !NILP (current_buffer->ctl_arrow);
102 register struct Lisp_Vector *dp = buffer_display_table ();
103 int stopchar;
104
105 if (point == last_known_column_point
106 && MODIFF == last_known_column_modified)
107 return last_known_column;
108
109 /* Make a pointer for decrementing through the chars before point. */
110 ptr = &FETCH_CHAR (point - 1) + 1;
111 /* Make a pointer to where consecutive chars leave off,
112 going backwards from point. */
113 if (point == BEGV)
114 stop = ptr;
115 else if (point <= GPT || BEGV > GPT)
116 stop = BEGV_ADDR;
117 else
118 stop = GAP_END_ADDR;
119
120 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
121
122 col = 0, tab_seen = 0, post_tab = 0;
123
124 while (1)
125 {
126 if (ptr == stop)
127 {
128 /* We stopped either for the beginning of the buffer
129 or for the gap. */
130 if (ptr == BEGV_ADDR)
131 break;
132 /* It was the gap. Jump back over it. */
133 stop = BEGV_ADDR;
134 ptr = GPT_ADDR;
135 /* Check whether that brings us to beginning of buffer. */
136 if (BEGV >= GPT) break;
137 }
138
139 c = *--ptr;
140 if (c >= 040 && c < 0177
141 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
142 {
143 col++;
144 }
145 else if (c == '\n')
146 break;
147 else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
148 break;
149 else if (c == '\t')
150 {
151 if (tab_seen)
152 col = ((col + tab_width) / tab_width) * tab_width;
153
154 post_tab += col;
155 col = 0;
156 tab_seen = 1;
157 }
158 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
159 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
160 else
161 col += (ctl_arrow && c < 0200) ? 2 : 4;
162 }
163
164 if (tab_seen)
165 {
166 col = ((col + tab_width) / tab_width) * tab_width;
167 col += post_tab;
168 }
169
170 last_known_column = col;
171 last_known_column_point = point;
172 last_known_column_modified = MODIFF;
173
174 return col;
175}
176\f
177
178DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
179 "Indent from point with tabs and spaces until COLUMN is reached.\n\
180Optional second argument MIN says always do at least MIN spaces\n\
181even if that goes past COLUMN; by default, MIN is zero.")
182 (col, minimum)
183 Lisp_Object col, minimum;
184{
185 int mincol;
186 register int fromcol;
187 register int tab_width = XINT (current_buffer->tab_width);
188
189 CHECK_NUMBER (col, 0);
190 if (NILP (minimum))
191 XFASTINT (minimum) = 0;
192 CHECK_NUMBER (minimum, 1);
193
194 fromcol = current_column ();
195 mincol = fromcol + XINT (minimum);
196 if (mincol < XINT (col)) mincol = XINT (col);
197
198 if (fromcol == mincol)
199 return make_number (mincol);
200
201 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
202
203 if (indent_tabs_mode)
204 {
205 Lisp_Object n;
206 XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
207 if (XFASTINT (n) != 0)
208 {
209 Finsert_char (make_number ('\t'), n);
210
211 fromcol = (mincol / tab_width) * tab_width;
212 }
213 }
214
215 XFASTINT (col) = mincol - fromcol;
216 Finsert_char (make_number (' '), col);
217
218 last_known_column = mincol;
219 last_known_column_point = point;
220 last_known_column_modified = MODIFF;
221
222 XSETINT (col, mincol);
223 return col;
224}
225\f
226DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
227 0, 0, 0,
228 "Return the indentation of the current line.\n\
229This is the horizontal position of the character\n\
230following any initial whitespace.")
231 ()
232{
233 Lisp_Object val;
234
235 XFASTINT (val) = position_indentation (find_next_newline (point, -1));
236 return val;
237}
238
239position_indentation (pos)
240 register int pos;
241{
242 register int column = 0;
243 register int tab_width = XINT (current_buffer->tab_width);
244 register unsigned char *p;
245 register unsigned char *stop;
246
247 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
248
249 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
250 p = &FETCH_CHAR (pos);
251 while (1)
252 {
253 while (p == stop)
254 {
255 if (pos == ZV)
256 return column;
257 pos += p - &FETCH_CHAR (pos);
258 p = &FETCH_CHAR (pos);
259 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
260 }
261 switch (*p++)
262 {
263 case ' ':
264 column++;
265 break;
266 case '\t':
267 column += tab_width - column % tab_width;
268 break;
269 default:
270 return column;
271 }
272 }
273}
274
275/* Test whether the line beginning at POS is indented beyond COLUMN.
276 Blank lines are treated as if they had the same indentation as the
277 preceding line. */
278int
279indented_beyond_p (pos, column)
280 int pos, column;
281{
282 while (pos > BEGV && FETCH_CHAR (pos) == '\n')
283 pos = find_next_newline_no_quit (pos - 1, -1);
284 return (position_indentation (pos) >= column);
285}
286\f
287DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
288 "Move point to column COLUMN in the current line.\n\
289The column of a character is calculated by adding together the widths\n\
290as displayed of the previous characters in the line.\n\
291This function ignores line-continuation;\n\
292there is no upper limit on the column number a character can have\n\
293and horizontal scrolling has no effect.\n\
294\n\
295If specified column is within a character, point goes after that character.\n\
296If it's past end of line, point goes to end of line.\n\n\
297A non-nil second (optional) argument FORCE means, if the line\n\
298is too short to reach column COLUMN then add spaces/tabs to get there,\n\
299and if COLUMN is in the middle of a tab character, change it to spaces.")
300 (column, force)
301 Lisp_Object column, force;
302{
303 register int pos;
304 register int col = current_column ();
305 register int goal;
306 register int end;
307 register int tab_width = XINT (current_buffer->tab_width);
308 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
309 register struct Lisp_Vector *dp = buffer_display_table ();
310
311 Lisp_Object val;
312 int prev_col;
313 int c;
314
315 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
316 CHECK_NATNUM (column, 0);
317 goal = XINT (column);
318
319 retry:
320 pos = point;
321 end = ZV;
322
323 /* If we're starting past the desired column,
324 back up to beginning of line and scan from there. */
325 if (col > goal)
326 {
327 pos = find_next_newline (pos, -1);
328 col = 0;
329 }
330
331 while (col < goal && pos < end)
332 {
333 c = FETCH_CHAR (pos);
334 if (c == '\n')
335 break;
336 if (c == '\r' && EQ (current_buffer->selective_display, Qt))
337 break;
338 pos++;
339 if (c == '\t')
340 {
341 prev_col = col;
342 col += tab_width;
343 col = col / tab_width * tab_width;
344 }
345 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
346 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
347 else if (ctl_arrow && (c < 040 || c == 0177))
348 col += 2;
349 else if (c < 040 || c >= 0177)
350 col += 4;
351 else
352 col++;
353 }
354
355 SET_PT (pos);
356
357 /* If a tab char made us overshoot, change it to spaces
358 and scan through it again. */
359 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
360 {
361 int old_point;
362
363 del_range (point - 1, point);
364 Findent_to (make_number (goal), Qnil);
365 old_point = point;
366 Findent_to (make_number (col), Qnil);
367 SET_PT (old_point);
368 /* Set the last_known... vars consistently. */
369 col = goal;
370 }
371
372 /* If line ends prematurely, add space to the end. */
373 if (col < goal && !NILP (force))
374 Findent_to (make_number (col = goal), Qnil);
375
376 last_known_column = col;
377 last_known_column_point = point;
378 last_known_column_modified = MODIFF;
379
380 XFASTINT (val) = col;
381 return val;
382}
383\f
384struct position val_compute_motion;
385
386/* Scan the current buffer forward from offset FROM, pretending that
387 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
388 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
389 and return the ending buffer position and screen location.
390
391 WIDTH is the number of columns available to display text;
392 compute_motion uses this to handle continuation lines and such.
393 HSCROLL is the number of columns not being displayed at the left
394 margin; this is usually taken from a window's hscroll member.
395 TAB_OFFSET is the number of columns of the first tab that aren't
396 being displayed, perhaps because of a continuation line or
397 something.
398
399 compute_motion returns a pointer to a struct position. The bufpos
400 member gives the buffer position at the end of the scan, and hpos
401 and vpos give its cartesian location. I'm not clear on what the
402 other members are.
403
404 Note that FROMHPOS and TOHPOS should be expressed in real screen
405 columns, taking HSCROLL and the truncation glyph at the left margin
406 into account. That is, beginning-of-line moves you to the hpos
407 -HSCROLL + (HSCROLL > 0).
408
409 For example, to find the buffer position of column COL of line LINE
410 of a certain window, pass the window's starting location as FROM
411 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
412 Pass the buffer's ZV as TO, to limit the scan to the end of the
413 visible section of the buffer, and pass LINE and COL as TOVPOS and
414 TOHPOS.
415
416 When displaying in window w, a typical formula for WIDTH is:
417
418 window_width - 1
419 - (has_vertical_scroll_bars
420 ? VERTICAL_SCROLL_BAR_WIDTH
421 : (window_width + window_left != frame_width))
422
423 where
424 window_width is XFASTINT (w->width),
425 window_left is XFASTINT (w->left),
426 has_vertical_scroll_bars is
427 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
428 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
429
430 Or you can let window_internal_width do this all for you, and write:
431 window_internal_width (w) - 1
432
433 The `-1' accounts for the continuation-line backslashes; the rest
434 accounts for window borders if the window is split horizontally, and
435 the scroll bars if they are turned on. */
436
437struct position *
438compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset, win)
439 int from, fromvpos, fromhpos, to, tovpos, tohpos;
440 register int width;
441 int hscroll, tab_offset;
442 struct window *win;
443{
444 register int hpos = fromhpos;
445 register int vpos = fromvpos;
446
447 register int pos;
448 register int c;
449 register int tab_width = XFASTINT (current_buffer->tab_width);
450 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
451 register struct Lisp_Vector *dp = window_display_table (win);
452 int selective
453 = (XTYPE (current_buffer->selective_display) == Lisp_Int
454 ? XINT (current_buffer->selective_display)
455 : !NILP (current_buffer->selective_display) ? -1 : 0);
456 int prev_vpos, prev_hpos = 0;
457 int selective_rlen
458 = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
459 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
460#ifdef USE_TEXT_PROPERTIES
461 /* The next location where the `invisible' property changes */
462 int next_invisible = from;
463 Lisp_Object prop, position;
464#endif
465
466 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
467 for (pos = from; pos < to; pos++)
468 {
469 /* Stop if past the target screen position. */
470 if (vpos > tovpos
471 || (vpos == tovpos && hpos >= tohpos))
472 break;
473
474 prev_vpos = vpos;
475 prev_hpos = hpos;
476
477#ifdef USE_TEXT_PROPERTIES
478 /* if the `invisible' property is set, we can skip to
479 the next property change */
480 while (pos == next_invisible && pos < to)
481 {
482 XFASTINT (position) = pos;
483 prop = Fget_char_property (position,
484 Qinvisible,
485 Fcurrent_buffer ());
486 {
487 Lisp_Object end, limit;
488
489 /* This is just an estimate to give reasonable
490 performance; nothing should go wrong if it is too small. */
491 limit = Fnext_overlay_change (position);
492 if (XFASTINT (limit) > pos + 100)
493 XFASTINT (limit) = pos + 100;
494 end = Fnext_single_property_change (position, Qinvisible,
495 Fcurrent_buffer (), limit);
496 if (INTEGERP (end))
497 next_invisible = XINT (end);
498 else
499 next_invisible = to;
500 if (! NILP (prop))
501 pos = next_invisible;
502 }
503 }
504 if (pos >= to)
505 break;
506#endif
507 c = FETCH_CHAR (pos);
508 if (c >= 040 && c < 0177
509 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
510 hpos++;
511 else if (c == '\t')
512 {
513 hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
514 /* Add tab_width here to make sure positive.
515 hpos can be negative after continuation
516 but can't be less than -tab_width. */
517 + tab_width)
518 % tab_width);
519 }
520 else if (c == '\n')
521 {
522 if (selective > 0 && indented_beyond_p (pos + 1, selective))
523 {
524 /* Skip any number of invisible lines all at once */
525 do
526 {
527 while (++pos < to && FETCH_CHAR (pos) != '\n');
528 }
529 while (pos < to && indented_beyond_p (pos + 1, selective));
530 pos--; /* Reread the newline on the next pass. */
531 /* Allow for the " ..." that is displayed for them. */
532 if (selective_rlen)
533 {
534 hpos += selective_rlen;
535 if (hpos >= width)
536 hpos = width;
537 }
538 /* We have skipped the invis text, but not the newline after. */
539 }
540 else
541 {
542 /* A visible line. */
543 vpos++;
544 hpos = 0;
545 hpos -= hscroll;
546 if (hscroll > 0) hpos++; /* Truncation glyph on column 0 */
547 tab_offset = 0;
548 }
549 }
550 else if (c == CR && selective < 0)
551 {
552 /* In selective display mode,
553 everything from a ^M to the end of the line is invisible */
554 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
555 /* Stop *before* the real newline. */
556 pos--;
557 /* Allow for the " ..." that is displayed for them. */
558 if (selective_rlen)
559 {
560 hpos += selective_rlen;
561 if (hpos >= width)
562 hpos = width;
563 }
564 }
565 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
566 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
567 else
568 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
569
570 /* Handle right margin. */
571 if (hpos >= width
572 && (hpos > width
573 || (pos < ZV - 1
574 && FETCH_CHAR (pos + 1) != '\n')))
575 {
576 if (vpos > tovpos
577 || (vpos == tovpos && hpos >= tohpos))
578 break;
579 if (hscroll
580 || (truncate_partial_width_windows
581 && width + 1 < FRAME_WIDTH (selected_frame))
582 || !NILP (current_buffer->truncate_lines))
583 {
584 /* Truncating: skip to newline. */
585 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
586 pos--;
587 hpos = width;
588 }
589 else
590 {
591 /* Continuing. */
592 vpos++;
593 hpos -= width;
594 tab_offset += width;
595 }
596
597 }
598 }
599
600 val_compute_motion.bufpos = pos;
601 val_compute_motion.hpos = hpos;
602 val_compute_motion.vpos = vpos;
603 val_compute_motion.prevhpos = prev_hpos;
604
605 /* Nonzero if have just continued a line */
606 val_compute_motion.contin
607 = (pos != from
608 && (val_compute_motion.vpos != prev_vpos)
609 && c != '\n');
610
611 return &val_compute_motion;
612}
613
614#if 0 /* The doc string is too long for some compilers,
615 but make-docfile can find it in this comment. */
616DEFUN ("compute-motion", Ffoo, Sfoo, 7, 7, 0,
617 "Scan through the current buffer, calculating screen position.\n\
618Scan the current buffer forward from offset FROM,\n\
619assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
620to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
621and return the ending buffer position and screen location.\n\
622\n\
623There are three additional arguments:\n\
624\n\
625WIDTH is the number of columns available to display text;\n\
626this affects handling of continuation lines.\n\
627This is usually the value returned by `window-width', less one (to allow\n\
628for the continuation glyph).\n\
629\n\
630OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
631HSCROLL is the number of columns not being displayed at the left\n\
632margin; this is usually taken from a window's hscroll member.\n\
633TAB-OFFSET is the number of columns of the first tab that aren't\n\
634being displayed, perhaps because the line was continued within it.\n\
635If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
636\n\
637WINDOW is the window to operate on. Currently this is used only to\n\
638find the display table. It does not matter what buffer WINDOW displays;\n\
639`compute-motion' always operates on the current buffer.\n\
640\n\
641The value is a list of five elements:\n\
642 (POS HPOS VPOS PREVHPOS CONTIN)\n\
643POS is the buffer position where the scan stopped.\n\
644VPOS is the vertical position where the scan stopped.\n\
645HPOS is the horizontal position where the scan stopped.\n\
646\n\
647PREVHPOS is the horizontal position one character back from POS.\n\
648CONTIN is t if a line was continued after (or within) the previous character.\n\
649\n\
650For example, to find the buffer position of column COL of line LINE\n\
651of a certain window, pass the window's starting location as FROM\n\
652and the window's upper-left coordinates as FROMPOS.\n\
653Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
654visible section of the buffer, and pass LINE and COL as TOPOS.")
655 (from, frompos, to, topos, width, offsets, window)
656#endif
657
658DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
659 0)
660 (from, frompos, to, topos, width, offsets, window)
661 Lisp_Object from, frompos, to, topos;
662 Lisp_Object width, offsets, window;
663{
664 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
665 struct position *pos;
666 int hscroll, tab_offset;
667
668 CHECK_NUMBER_COERCE_MARKER (from, 0);
669 CHECK_CONS (frompos, 0);
670 CHECK_NUMBER (XCONS (frompos)->car, 0);
671 CHECK_NUMBER (XCONS (frompos)->cdr, 0);
672 CHECK_NUMBER_COERCE_MARKER (to, 0);
673 CHECK_CONS (topos, 0);
674 CHECK_NUMBER (XCONS (topos)->car, 0);
675 CHECK_NUMBER (XCONS (topos)->cdr, 0);
676 CHECK_NUMBER (width, 0);
677 if (!NILP (offsets))
678 {
679 CHECK_CONS (offsets, 0);
680 CHECK_NUMBER (XCONS (offsets)->car, 0);
681 CHECK_NUMBER (XCONS (offsets)->cdr, 0);
682 hscroll = XINT (XCONS (offsets)->car);
683 tab_offset = XINT (XCONS (offsets)->cdr);
684 }
685 else
686 hscroll = tab_offset = 0;
687
688 if (NILP (window))
689 window = Fselected_window ();
690 else
691 CHECK_LIVE_WINDOW (window, 0);
692
693 pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
694 XINT (XCONS (frompos)->car),
695 XINT (to), XINT (XCONS (topos)->cdr),
696 XINT (XCONS (topos)->car),
697 XINT (width), hscroll, tab_offset,
698 XWINDOW (window));
699
700 XFASTINT (bufpos) = pos->bufpos;
701 XSET (hpos, Lisp_Int, pos->hpos);
702 XSET (vpos, Lisp_Int, pos->vpos);
703 XSET (prevhpos, Lisp_Int, pos->prevhpos);
704
705 return Fcons (bufpos,
706 Fcons (hpos,
707 Fcons (vpos,
708 Fcons (prevhpos,
709 Fcons (pos->contin ? Qt : Qnil, Qnil)))));
710
711}
712\f
713/* Return the column of position POS in window W's buffer,
714 rounded down to a multiple of the internal width of W.
715 This is the amount of indentation of position POS
716 that is not visible in its horizontal position in the window. */
717
718int
719pos_tab_offset (w, pos)
720 struct window *w;
721 register int pos;
722{
723 int opoint = point;
724 int col;
725 int width = window_internal_width (w) - 1;
726
727 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
728 return 0;
729 SET_PT (pos);
730 col = current_column ();
731 SET_PT (opoint);
732 return col - (col % width);
733}
734
735/* start_hpos is the hpos of the first character of the buffer:
736 zero except for the minibuffer window,
737 where it is the width of the prompt. */
738
739struct position val_vmotion;
740
741struct position *
742vmotion (from, vtarget, width, hscroll, window)
743 register int from, vtarget, width;
744 int hscroll;
745 Lisp_Object window;
746{
747 struct position pos;
748 /* vpos is cumulative vertical position, changed as from is changed */
749 register int vpos = 0;
750 register int prevline;
751 register int first;
752 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
753 int selective
754 = XTYPE (current_buffer->selective_display) == Lisp_Int
755 ? XINT (current_buffer->selective_display)
756 : !NILP (current_buffer->selective_display) ? -1 : 0;
757 /* The omission of the clause
758 && marker_position (XWINDOW (window)->start) == BEG
759 here is deliberate; I think we want to measure from the prompt
760 position even if the minibuffer window has scrolled. */
761 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
762
763 retry:
764 if (vtarget > vpos)
765 {
766 /* Moving downward is simple, but must calculate from beg of line
767 to determine hpos of starting point */
768 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
769 {
770 prevline = find_next_newline_no_quit (from, -1);
771 while (prevline > BEGV
772 && ((selective > 0
773 && indented_beyond_p (prevline, selective))
774#ifdef USE_TEXT_PROPERTIES
775 /* watch out for newlines with `invisible' property */
776 || ! NILP (Fget_char_property (XFASTINT (prevline),
777 Qinvisible,
778 window))
779#endif
780 ))
781 prevline = find_next_newline_no_quit (prevline - 1, -1);
782 pos = *compute_motion (prevline, 0,
783 lmargin + (prevline == 1 ? start_hpos : 0),
784 from, 1 << (INTBITS - 2), 0,
785 width, hscroll, 0, XWINDOW (window));
786 }
787 else
788 {
789 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
790 pos.vpos = 0;
791 }
792 return compute_motion (from, vpos, pos.hpos,
793 ZV, vtarget, - (1 << (INTBITS - 2)),
794 width, hscroll, pos.vpos * width,
795 XWINDOW (window));
796 }
797
798 /* To move upward, go a line at a time until
799 we have gone at least far enough */
800
801 first = 1;
802
803 while ((vpos > vtarget || first) && from > BEGV)
804 {
805 prevline = from;
806 while (1)
807 {
808 prevline = find_next_newline_no_quit (prevline - 1, -1);
809 if (prevline == BEGV
810 || ((selective <= 0
811 || ! indented_beyond_p (prevline, selective))
812#ifdef USE_TEXT_PROPERTIES
813 /* watch out for newlines with `invisible' property */
814 && NILP (Fget_char_property (XFASTINT (prevline),
815 Qinvisible,
816 window))
817#endif
818 ))
819 break;
820 }
821 pos = *compute_motion (prevline, 0,
822 lmargin + (prevline == 1 ? start_hpos : 0),
823 from, 1 << (INTBITS - 2), 0,
824 width, hscroll, 0, XWINDOW (window));
825 vpos -= pos.vpos;
826 first = 0;
827 from = prevline;
828 }
829
830 /* If we made exactly the desired vertical distance,
831 or if we hit beginning of buffer,
832 return point found */
833 if (vpos >= vtarget)
834 {
835 val_vmotion.bufpos = from;
836 val_vmotion.vpos = vpos;
837 val_vmotion.hpos = lmargin;
838 val_vmotion.contin = 0;
839 val_vmotion.prevhpos = 0;
840 return &val_vmotion;
841 }
842
843 /* Otherwise find the correct spot by moving down */
844 goto retry;
845}
846
847DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
848 "Move to start of screen line LINES lines down.\n\
849If LINES is negative, this is moving up.\n\
850\n\
851The optional second argument WINDOW specifies the window to use for\n\
852parameters such as width, horizontal scrolling, and so on.\n\
853the default is the selected window.\n\
854It does not matter what buffer is displayed in WINDOW.\n\
855`vertical-motion' always uses the current buffer.\n\
856\n\
857Sets point to position found; this may be start of line\n\
858or just the start of a continuation line.\n\
859Returns number of lines moved; may be closer to zero than LINES\n\
860if beginning or end of buffer was reached.")
861 (lines, window)
862 Lisp_Object lines, window;
863{
864 struct position pos;
865 register struct window *w;
866
867 CHECK_NUMBER (lines, 0);
868 if (! NILP (window))
869 CHECK_WINDOW (window, 0);
870 else
871 XSET (window, Lisp_Window, selected_window);
872
873 w = XWINDOW (window);
874
875 pos = *vmotion (point, XINT (lines), window_internal_width (w) - 1,
876 /* Not XFASTINT since perhaps could be negative */
877 XINT (w->hscroll), window);
878
879 SET_PT (pos.bufpos);
880 return make_number (pos.vpos);
881}
882\f
883syms_of_indent ()
884{
885 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
886 "*Indentation can insert tabs if this is non-nil.\n\
887Setting this variable automatically makes it local to the current buffer.");
888 indent_tabs_mode = 1;
889
890 defsubr (&Scurrent_indentation);
891 defsubr (&Sindent_to);
892 defsubr (&Scurrent_column);
893 defsubr (&Smove_to_column);
894 defsubr (&Svertical_motion);
895 defsubr (&Scompute_motion);
896}