(Fcompute_motion): Doc fix.
[emacs.git] / src / indent.c
blob10d4a594f427fd19a68d5a6bc7d9e6112b0d0c10
1 /* Indentation functions.
2 Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
9 any later version.
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.
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. */
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"
32 /* Indentation can insert tabs if this is non-zero;
33 otherwise always uses spaces */
34 int indent_tabs_mode;
36 #define min(a, b) ((a) < (b) ? (a) : (b))
37 #define max(a, b) ((a) > (b) ? (a) : (b))
39 #define CR 015
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 */
45 int last_known_column;
46 /* Value of point when current_column was called */
47 int last_known_column_point;
48 /* Value of MODIFF when current_column was called */
49 int last_known_column_modified;
51 /* Get the display table to use for the current buffer. */
53 struct Lisp_Vector *
54 buffer_display_table ()
56 Lisp_Object thisbuf;
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;
68 DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
69 "Return the horizontal position of point. Beginning of line is column 0.\n\
70 This is calculated by adding together the widths of all the displayed\n\
71 representations of the character between the start of the previous line\n\
72 and point. (eg control characters will have a width of 2 or 4, tabs\n\
73 will have a variable width)\n\
74 Ignores finite width of frame, which means that this function may return\n\
75 values greater than (frame-width).\n\
76 Whether the line is visible (if `selective-display' is t) has no effect;\n\
77 however, ^M is treated as end of line when `selective-display' is t.")
80 Lisp_Object temp;
81 XFASTINT (temp) = current_column ();
82 return temp;
85 /* Cancel any recorded value of the horizontal position. */
87 invalidate_current_column ()
89 last_known_column_point = 0;
92 int
93 current_column ()
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;
105 if (point == last_known_column_point
106 && MODIFF == last_known_column_modified)
107 return last_known_column;
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;
120 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
122 col = 0, tab_seen = 0, post_tab = 0;
124 while (1)
126 if (ptr == stop)
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;
139 c = *--ptr;
140 if (c >= 040 && c < 0177
141 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
143 col++;
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')
151 if (tab_seen)
152 col = ((col + tab_width) / tab_width) * tab_width;
154 post_tab += col;
155 col = 0;
156 tab_seen = 1;
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;
164 if (tab_seen)
166 col = ((col + tab_width) / tab_width) * tab_width;
167 col += post_tab;
170 last_known_column = col;
171 last_known_column_point = point;
172 last_known_column_modified = MODIFF;
174 return col;
178 DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
179 "Indent from point with tabs and spaces until COLUMN is reached.\n\
180 Optional second argument MIN says always do at least MIN spaces\n\
181 even if that goes past COLUMN; by default, MIN is zero.")
182 (col, minimum)
183 Lisp_Object col, minimum;
185 int mincol;
186 register int fromcol;
187 register int tab_width = XINT (current_buffer->tab_width);
189 CHECK_NUMBER (col, 0);
190 if (NILP (minimum))
191 XFASTINT (minimum) = 0;
192 CHECK_NUMBER (minimum, 1);
194 fromcol = current_column ();
195 mincol = fromcol + XINT (minimum);
196 if (mincol < XINT (col)) mincol = XINT (col);
198 if (fromcol == mincol)
199 return make_number (mincol);
201 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
203 if (indent_tabs_mode)
205 Lisp_Object n;
206 XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
207 if (XFASTINT (n) != 0)
209 Finsert_char (make_number ('\t'), n);
211 fromcol = (mincol / tab_width) * tab_width;
215 XFASTINT (col) = mincol - fromcol;
216 Finsert_char (make_number (' '), col);
218 last_known_column = mincol;
219 last_known_column_point = point;
220 last_known_column_modified = MODIFF;
222 XSETINT (col, mincol);
223 return col;
226 DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
227 0, 0, 0,
228 "Return the indentation of the current line.\n\
229 This is the horizontal position of the character\n\
230 following any initial whitespace.")
233 Lisp_Object val;
235 XFASTINT (val) = position_indentation (find_next_newline (point, -1));
236 return val;
239 position_indentation (pos)
240 register int pos;
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;
247 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
249 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
250 p = &FETCH_CHAR (pos);
251 while (1)
253 while (p == stop)
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;
261 switch (*p++)
263 case ' ':
264 column++;
265 break;
266 case '\t':
267 column += tab_width - column % tab_width;
268 break;
269 default:
270 return column;
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. */
279 indented_beyond_p (pos, column)
280 int pos, column;
282 while (pos > BEGV && FETCH_CHAR (pos) == '\n')
283 pos = find_next_newline (pos - 1, -1);
284 return (position_indentation (pos) >= column);
287 DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
288 "Move point to column COLUMN in the current line.\n\
289 The column of a character is calculated by adding together the widths\n\
290 as displayed of the previous characters in the line.\n\
291 This function ignores line-continuation;\n\
292 there is no upper limit on the column number a character can have\n\
293 and horizontal scrolling has no effect.\n\
295 If specified column is within a character, point goes after that character.\n\
296 If it's past end of line, point goes to end of line.\n\n\
297 A non-nil second (optional) argument FORCE means, if the line\n\
298 is too short to reach column COLUMN then add spaces/tabs to get there,\n\
299 and if COLUMN is in the middle of a tab character, change it to spaces.")
300 (column, force)
301 Lisp_Object column, force;
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 ();
311 Lisp_Object val;
312 int prev_col;
313 int c;
315 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
316 CHECK_NATNUM (column, 0);
317 goal = XINT (column);
319 retry:
320 pos = point;
321 end = ZV;
323 /* If we're starting past the desired column,
324 back up to beginning of line and scan from there. */
325 if (col > goal)
327 pos = find_next_newline (pos, -1);
328 col = 0;
331 while (col < goal && pos < end)
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')
341 prev_col = col;
342 col += tab_width;
343 col = col / tab_width * tab_width;
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++;
355 SET_PT (pos);
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)
361 int old_point;
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;
372 /* If line ends prematurely, add space to the end. */
373 if (col < goal && !NILP (force))
374 Findent_to (make_number (col = goal), Qnil);
376 last_known_column = col;
377 last_known_column_point = point;
378 last_known_column_modified = MODIFF;
380 XFASTINT (val) = col;
381 return val;
384 struct position val_compute_motion;
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.
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.
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.
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).
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.
416 When displaying in window w, a typical formula for WIDTH is:
418 window_width - 1
419 - (has_vertical_scroll_bars
420 ? VERTICAL_SCROLL_BAR_WIDTH
421 : (window_width + window_left != frame_width))
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))
430 Or you can let window_internal_width do this all for you, and write:
431 window_internal_width (w) - 1
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. */
437 struct position *
438 compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
439 int from, fromvpos, fromhpos, to, tovpos, tohpos;
440 register int width;
441 int hscroll, tab_offset;
443 register int hpos = fromhpos;
444 register int vpos = fromvpos;
446 register int pos;
447 register int c;
448 register int tab_width = XFASTINT (current_buffer->tab_width);
449 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
450 register struct Lisp_Vector *dp = buffer_display_table ();
451 int selective
452 = XTYPE (current_buffer->selective_display) == Lisp_Int
453 ? XINT (current_buffer->selective_display)
454 : !NILP (current_buffer->selective_display) ? -1 : 0;
455 int prev_vpos, prev_hpos = 0;
456 int selective_rlen
457 = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
458 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
459 #ifdef USE_TEXT_PROPERTIES
460 /* The next location where the `invisible' property changes */
461 int next_invisible = from;
462 Lisp_Object prop, position;
463 #endif
465 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
466 for (pos = from; pos < to; pos++)
468 /* Stop if past the target screen position. */
469 if (vpos > tovpos
470 || (vpos == tovpos && hpos >= tohpos))
471 break;
473 prev_vpos = vpos;
474 prev_hpos = hpos;
476 #ifdef USE_TEXT_PROPERTIES
477 /* if the `invisible' property is set, we can skip to
478 the next property change */
479 while (pos == next_invisible && pos < to)
481 XFASTINT (position) = pos;
482 prop = Fget_char_property (position,
483 Qinvisible,
484 Fcurrent_buffer ());
486 Lisp_Object end, limit;
488 /* This is just an estimate to give reasonable
489 performance; nothing should go wrong if it is too small. */
490 limit = Fnext_overlay_change (position);
491 if (XFASTINT (limit) > pos + 100)
492 XFASTINT (limit) = pos + 100;
493 end = Fnext_single_property_change (position, Qinvisible,
494 Fcurrent_buffer (), limit);
495 if (INTEGERP (end))
496 next_invisible = XINT (end);
497 else
498 next_invisible = to;
499 if (! NILP (prop))
500 pos = next_invisible;
503 if (pos >= to)
504 break;
505 #endif
506 c = FETCH_CHAR (pos);
507 if (c >= 040 && c < 0177
508 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
509 hpos++;
510 else if (c == '\t')
512 hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
513 /* Add tab_width here to make sure positive.
514 hpos can be negative after continuation
515 but can't be less than -tab_width. */
516 + tab_width)
517 % tab_width);
519 else if (c == '\n')
521 if (selective > 0 && indented_beyond_p (pos + 1, selective))
523 /* Skip any number of invisible lines all at once */
526 while (++pos < to && FETCH_CHAR (pos) != '\n');
528 while (pos < to && indented_beyond_p (pos + 1, selective));
529 pos--; /* Reread the newline on the next pass. */
530 /* Allow for the " ..." that is displayed for them. */
531 if (selective_rlen)
533 hpos += selective_rlen;
534 if (hpos >= width)
535 hpos = width;
537 /* We have skipped the invis text, but not the newline after. */
539 else
541 /* A visible line. */
542 vpos++;
543 hpos = 0;
544 hpos -= hscroll;
545 if (hscroll > 0) hpos++; /* Truncation glyph on column 0 */
546 tab_offset = 0;
549 else if (c == CR && selective < 0)
551 /* In selective display mode,
552 everything from a ^M to the end of the line is invisible */
553 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
554 /* Stop *before* the real newline. */
555 pos--;
556 /* Allow for the " ..." that is displayed for them. */
557 if (selective_rlen)
559 hpos += selective_rlen;
560 if (hpos >= width)
561 hpos = width;
564 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
565 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
566 else
567 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
569 /* Handle right margin. */
570 if (hpos >= width
571 && (hpos > width
572 || (pos < ZV - 1
573 && FETCH_CHAR (pos + 1) != '\n')))
575 if (vpos > tovpos
576 || (vpos == tovpos && hpos >= tohpos))
577 break;
578 if (hscroll
579 || (truncate_partial_width_windows
580 && width + 1 < FRAME_WIDTH (selected_frame))
581 || !NILP (current_buffer->truncate_lines))
583 /* Truncating: skip to newline. */
584 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
585 pos--;
586 hpos = width;
588 else
590 /* Continuing. */
591 vpos++;
592 hpos -= width;
593 tab_offset += width;
599 val_compute_motion.bufpos = pos;
600 val_compute_motion.hpos = hpos;
601 val_compute_motion.vpos = vpos;
602 val_compute_motion.prevhpos = prev_hpos;
604 /* Nonzero if have just continued a line */
605 val_compute_motion.contin
606 = (pos != from
607 && (val_compute_motion.vpos != prev_vpos)
608 && c != '\n');
610 return &val_compute_motion;
613 DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 6, 6, 0,
614 "Scan through the current buffer, calculating screen position.\n\
615 Scan the current buffer forward from offset FROM,\n\
616 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
617 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
618 and return the ending buffer position and screen location.\n\
620 There are two additional arguments:\n\
622 WIDTH is the number of columns available to display text;\n\
623 this affects handling of continuation lines.\n\
624 Use the value returned by `window-width' for the window of your choice.\n\
626 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
627 HSCROLL is the number of columns not being displayed at the left\n\
628 margin; this is usually taken from a window's hscroll member.\n\
629 TAB-OFFSET is the number of columns of the first tab that aren't\n\
630 being displayed, perhaps because the line was continued within it.\n\
631 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
633 The value is a list of five elements:\n\
634 (POS VPOS HPOS PREVHPOS CONTIN)\n\
635 POS is the buffer position where the scan stopped.\n\
636 VPOS is the vertical position where the scan stopped.\n\
637 HPOS is the horizontal position where the scan stopped.\n\
639 PREVHPOS is the horizontal position one character back from POS.\n\
640 CONTIN is t if a line was continued after (or within) the previous character.\n\
642 For example, to find the buffer position of column COL of line LINE\n\
643 of a certain window, pass the window's starting location as FROM\n\
644 and the window's upper-left coordinates as FROMPOS.\n\
645 Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
646 visible section of the buffer, and pass LINE and COL as TOPOS.")
647 (from, frompos, to, topos, width, offsets)
648 Lisp_Object from, frompos, to, topos;
649 Lisp_Object width, offsets;
651 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
652 struct position *pos;
653 int hscroll, tab_offset;
655 CHECK_NUMBER_COERCE_MARKER (from, 0);
656 CHECK_CONS (frompos, 0);
657 CHECK_NUMBER (XCONS (frompos)->car, 0);
658 CHECK_NUMBER (XCONS (frompos)->cdr, 0);
659 CHECK_NUMBER_COERCE_MARKER (to, 0);
660 CHECK_CONS (topos, 0);
661 CHECK_NUMBER (XCONS (topos)->car, 0);
662 CHECK_NUMBER (XCONS (topos)->cdr, 0);
663 CHECK_NUMBER (width, 0);
664 if (!NILP (offsets))
666 CHECK_CONS (offsets, 0);
667 CHECK_NUMBER (XCONS (offsets)->car, 0);
668 CHECK_NUMBER (XCONS (offsets)->cdr, 0);
669 hscroll = XINT (XCONS (offsets)->car);
670 tab_offset = XINT (XCONS (offsets)->cdr);
672 else
673 hscroll = tab_offset = 0;
675 pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
676 XINT (XCONS (frompos)->car),
677 XINT (to), XINT (XCONS (topos)->cdr),
678 XINT (XCONS (topos)->car),
679 XINT (width), hscroll, tab_offset);
681 XFASTINT (bufpos) = pos->bufpos;
682 XFASTINT (hpos) = pos->hpos;
683 XSET (vpos, Lisp_Int, pos->vpos);
684 XFASTINT (prevhpos) = pos->prevhpos;
686 return Fcons (bufpos,
687 Fcons (hpos,
688 Fcons (vpos,
689 Fcons (prevhpos,
690 Fcons (pos->contin ? Qt : Qnil, Qnil)))));
694 /* Return the column of position POS in window W's buffer,
695 rounded down to a multiple of the internal width of W.
696 This is the amount of indentation of position POS
697 that is not visible in its horizontal position in the window. */
700 pos_tab_offset (w, pos)
701 struct window *w;
702 register int pos;
704 int opoint = point;
705 int col;
706 int width = window_internal_width (w) - 1;
708 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
709 return 0;
710 SET_PT (pos);
711 col = current_column ();
712 SET_PT (opoint);
713 return col - (col % width);
716 /* start_hpos is the hpos of the first character of the buffer:
717 zero except for the minibuffer window,
718 where it is the width of the prompt. */
720 struct position val_vmotion;
722 struct position *
723 vmotion (from, vtarget, width, hscroll, window)
724 register int from, vtarget, width;
725 int hscroll;
726 Lisp_Object window;
728 struct position pos;
729 /* vpos is cumulative vertical position, changed as from is changed */
730 register int vpos = 0;
731 register int prevline;
732 register int first;
733 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
734 int selective
735 = XTYPE (current_buffer->selective_display) == Lisp_Int
736 ? XINT (current_buffer->selective_display)
737 : !NILP (current_buffer->selective_display) ? -1 : 0;
738 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
740 retry:
741 if (vtarget > vpos)
743 /* Moving downward is simple, but must calculate from beg of line
744 to determine hpos of starting point */
745 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
747 prevline = find_next_newline (from, -1);
748 while (prevline > BEGV
749 && ((selective > 0
750 && indented_beyond_p (prevline, selective))
751 #ifdef USE_TEXT_PROPERTIES
752 /* watch out for newlines with `invisible' property */
753 || ! NILP (Fget_char_property (XFASTINT (prevline),
754 Qinvisible,
755 window))
756 #endif
758 prevline = find_next_newline (prevline - 1, -1);
759 pos = *compute_motion (prevline, 0,
760 lmargin + (prevline == 1 ? start_hpos : 0),
761 from, 1 << (INTBITS - 2), 0,
762 width, hscroll, 0);
764 else
766 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
767 pos.vpos = 0;
769 return compute_motion (from, vpos, pos.hpos,
770 ZV, vtarget, - (1 << (INTBITS - 2)),
771 width, hscroll, pos.vpos * width);
774 /* To move upward, go a line at a time until
775 we have gone at least far enough */
777 first = 1;
779 while ((vpos > vtarget || first) && from > BEGV)
781 prevline = from;
782 while (1)
784 prevline = find_next_newline (prevline - 1, -1);
785 if (prevline == BEGV
786 || ((selective <= 0
787 || ! indented_beyond_p (prevline, selective))
788 #ifdef USE_TEXT_PROPERTIES
789 /* watch out for newlines with `invisible' property */
790 && NILP (Fget_char_property (XFASTINT (prevline),
791 Qinvisible,
792 window))
793 #endif
795 break;
797 pos = *compute_motion (prevline, 0,
798 lmargin + (prevline == 1 ? start_hpos : 0),
799 from, 1 << (INTBITS - 2), 0,
800 width, hscroll, 0);
801 vpos -= pos.vpos;
802 first = 0;
803 from = prevline;
806 /* If we made exactly the desired vertical distance,
807 or if we hit beginning of buffer,
808 return point found */
809 if (vpos >= vtarget)
811 val_vmotion.bufpos = from;
812 val_vmotion.vpos = vpos;
813 val_vmotion.hpos = lmargin;
814 val_vmotion.contin = 0;
815 val_vmotion.prevhpos = 0;
816 return &val_vmotion;
819 /* Otherwise find the correct spot by moving down */
820 goto retry;
823 DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
824 "Move to start of screen line LINES lines down.\n\
825 If LINES is negative, this is moving up.\n\
826 The optional second argument WINDOW specifies the window\n\
827 to use for computations.\n\
828 Sets point to position found; this may be start of line\n\
829 or just the start of a continuation line.\n\
830 Returns number of lines moved; may be closer to zero than LINES\n\
831 if beginning or end of buffer was reached.")
832 (lines, window)
833 Lisp_Object lines, window;
835 struct position pos;
836 register struct window *w = XWINDOW (selected_window);
837 int width = window_internal_width (w) - 1;
839 CHECK_NUMBER (lines, 0);
840 if (! NILP (window))
841 CHECK_WINDOW (window, 0);
842 else
843 XSET (window, Lisp_Window, selected_window);
845 pos = *vmotion (point, XINT (lines), width,
846 /* Not XFASTINT since perhaps could be negative */
847 XINT (w->hscroll), window);
849 SET_PT (pos.bufpos);
850 return make_number (pos.vpos);
853 syms_of_indent ()
855 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
856 "*Indentation can insert tabs if this is non-nil.\n\
857 Setting this variable automatically makes it local to the current buffer.");
858 indent_tabs_mode = 1;
860 defsubr (&Scurrent_indentation);
861 defsubr (&Sindent_to);
862 defsubr (&Scurrent_column);
863 defsubr (&Smove_to_column);
864 defsubr (&Svertical_motion);
865 defsubr (&Scompute_motion);