(Fdo_auto_save): Doc fix.
[bpt/emacs.git] / src / indent.c
1 /* Indentation functions.
2 Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21 #include <config.h>
22 #include "lisp.h"
23 #include "buffer.h"
24 #include "indent.h"
25 #include "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 */
34 int 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 */
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;
50
51 /* Get the display table to use for the current buffer. */
52
53 struct Lisp_Vector *
54 buffer_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
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.")
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
87 invalidate_current_column ()
88 {
89 last_known_column_point = 0;
90 }
91
92 int
93 current_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
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;
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
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.")
231 ()
232 {
233 Lisp_Object val;
234
235 XFASTINT (val) = position_indentation (find_next_newline (point, -1));
236 return val;
237 }
238
239 position_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. */
278 int
279 indented_beyond_p (pos, column)
280 int pos, column;
281 {
282 while (pos > BEGV && FETCH_CHAR (pos) == '\n')
283 pos = find_next_newline (pos - 1, -1);
284 return (position_indentation (pos) >= column);
285 }
286 \f
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\
294 \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;
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
384 struct 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 For example, to find the buffer position of column COL of line LINE
405 of a certain window, pass the window's starting location as FROM
406 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
407 Pass the buffer's ZV as TO, to limit the scan to the end of the
408 visible section of the buffer, and pass LINE and COL as TOVPOS and
409 TOHPOS.
410
411 When displaying in window w, a typical formula for WIDTH is:
412
413 window_width - 1
414 - (has_vertical_scroll_bars
415 ? VERTICAL_SCROLL_BAR_WIDTH
416 : (window_width + window_left != frame_width))
417
418 where
419 window_width is XFASTINT (w->width),
420 window_left is XFASTINT (w->left),
421 has_vertical_scroll_bars is
422 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
423 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
424
425 Or,
426 window_internal_width (w) - 1
427
428 The `-1' accounts for the continuation-line backslashes; the rest
429 accounts for window borders if the window is split horizontally, and
430 the scroll bars if the frame supports them. */
431
432 struct position *
433 compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
434 int from, fromvpos, fromhpos, to, tovpos, tohpos;
435 register int width;
436 int hscroll, tab_offset;
437 {
438 register int hpos = fromhpos;
439 register int vpos = fromvpos;
440
441 register int pos;
442 register int c;
443 register int tab_width = XFASTINT (current_buffer->tab_width);
444 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
445 register struct Lisp_Vector *dp = buffer_display_table ();
446 int selective
447 = XTYPE (current_buffer->selective_display) == Lisp_Int
448 ? XINT (current_buffer->selective_display)
449 : !NILP (current_buffer->selective_display) ? -1 : 0;
450 int prev_vpos, prev_hpos;
451 int selective_rlen
452 = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
453 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
454 #ifdef USE_TEXT_PROPERTIES
455 /* The next location where the `invisible' property changes */
456 int next_invisible = from;
457 Lisp_Object prop, position;
458 #endif
459
460 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
461 for (pos = from; pos < to; pos++)
462 {
463 /* Stop if past the target screen position. */
464 if (vpos > tovpos
465 || (vpos == tovpos && hpos >= tohpos))
466 break;
467
468 prev_vpos = vpos;
469 prev_hpos = hpos;
470
471 #ifdef USE_TEXT_PROPERTIES
472 /* if the `invisible' property is set, we can skip to
473 the next property change */
474 while (pos == next_invisible && pos < to)
475 {
476 XFASTINT (position) = pos;
477 prop = Fget_char_property (position,
478 Qinvisible,
479 Fcurrent_buffer ());
480 {
481 Lisp_Object end, limit;
482
483 /* This is just an estimate to give reasonable
484 performance; nothing should go wrong if it is too small. */
485 limit = Fnext_overlay_change (position);
486 if (XFASTINT (limit) > pos + 100)
487 XFASTINT (limit) = pos + 100;
488 end = Fnext_single_property_change (position, Qinvisible,
489 Fcurrent_buffer (), limit);
490 if (INTEGERP (end))
491 next_invisible = XINT (end);
492 else
493 next_invisible = to;
494 if (! NILP (prop))
495 pos = next_invisible;
496 }
497 }
498 if (pos >= to)
499 break;
500 #endif
501 c = FETCH_CHAR (pos);
502 if (c >= 040 && c < 0177
503 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
504 hpos++;
505 else if (c == '\t')
506 {
507 hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
508 /* Add tab_width here to make sure positive.
509 hpos can be negative after continuation
510 but can't be less than -tab_width. */
511 + tab_width)
512 % tab_width);
513 }
514 else if (c == '\n')
515 {
516 if (selective > 0 && indented_beyond_p (pos + 1, selective))
517 {
518 /* Skip any number of invisible lines all at once */
519 do
520 {
521 while (++pos < to && FETCH_CHAR (pos) != '\n');
522 }
523 while (pos < to && indented_beyond_p (pos + 1, selective));
524 pos--; /* Reread the newline on the next pass. */
525 /* Allow for the " ..." that is displayed for them. */
526 if (selective_rlen)
527 {
528 hpos += selective_rlen;
529 if (hpos >= width)
530 hpos = width;
531 }
532 /* We have skipped the invis text, but not the newline after. */
533 }
534 else
535 {
536 /* A visible line. */
537 vpos++;
538 hpos = 0;
539 hpos -= hscroll;
540 if (hscroll > 0) hpos++; /* Count the $ on column 0 */
541 tab_offset = 0;
542 }
543 }
544 else if (c == CR && selective < 0)
545 {
546 /* In selective display mode,
547 everything from a ^M to the end of the line is invisible */
548 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
549 /* Stop *before* the real newline. */
550 pos--;
551 /* Allow for the " ..." that is displayed for them. */
552 if (selective_rlen)
553 {
554 hpos += selective_rlen;
555 if (hpos >= width)
556 hpos = width;
557 }
558 }
559 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
560 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
561 else
562 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
563
564 /* Handle right margin. */
565 if (hpos >= width
566 && (hpos > width
567 || (pos < ZV - 1
568 && FETCH_CHAR (pos + 1) != '\n')))
569 {
570 if (vpos > tovpos
571 || (vpos == tovpos && hpos >= tohpos))
572 break;
573 if (hscroll
574 || (truncate_partial_width_windows
575 && width + 1 < FRAME_WIDTH (selected_frame))
576 || !NILP (current_buffer->truncate_lines))
577 {
578 /* Truncating: skip to newline. */
579 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
580 pos--;
581 hpos = width;
582 }
583 else
584 {
585 /* Continuing. */
586 vpos++;
587 hpos -= width;
588 tab_offset += width;
589 }
590
591 }
592 }
593
594 val_compute_motion.bufpos = pos;
595 val_compute_motion.hpos = hpos;
596 val_compute_motion.vpos = vpos;
597 val_compute_motion.prevhpos = prev_hpos;
598
599 /* Nonzero if have just continued a line */
600 val_compute_motion.contin
601 = (pos != from
602 && (val_compute_motion.vpos != prev_vpos)
603 && c != '\n');
604
605 return &val_compute_motion;
606 }
607
608 \f
609 /* Return the column of position POS in window W's buffer,
610 rounded down to a multiple of the internal width of W.
611 This is the amount of indentation of position POS
612 that is not visible in its horizontal position in the window. */
613
614 int
615 pos_tab_offset (w, pos)
616 struct window *w;
617 register int pos;
618 {
619 int opoint = point;
620 int col;
621 int width = window_internal_width (w) - 1;
622
623 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
624 return 0;
625 SET_PT (pos);
626 col = current_column ();
627 SET_PT (opoint);
628 return col - (col % width);
629 }
630
631 /* start_hpos is the hpos of the first character of the buffer:
632 zero except for the minibuffer window,
633 where it is the width of the prompt. */
634
635 struct position val_vmotion;
636
637 struct position *
638 vmotion (from, vtarget, width, hscroll, window)
639 register int from, vtarget, width;
640 int hscroll;
641 Lisp_Object window;
642 {
643 struct position pos;
644 /* vpos is cumulative vertical position, changed as from is changed */
645 register int vpos = 0;
646 register int prevline;
647 register int first;
648 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
649 int selective
650 = XTYPE (current_buffer->selective_display) == Lisp_Int
651 ? XINT (current_buffer->selective_display)
652 : !NILP (current_buffer->selective_display) ? -1 : 0;
653 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
654
655 retry:
656 if (vtarget > vpos)
657 {
658 /* Moving downward is simple, but must calculate from beg of line
659 to determine hpos of starting point */
660 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
661 {
662 prevline = find_next_newline (from, -1);
663 while (prevline > BEGV
664 && ((selective > 0
665 && indented_beyond_p (prevline, selective))
666 #ifdef USE_TEXT_PROPERTIES
667 /* watch out for newlines with `invisible' property */
668 || ! NILP (Fget_char_property (XFASTINT (prevline),
669 Qinvisible,
670 window))
671 #endif
672 ))
673 prevline = find_next_newline (prevline - 1, -1);
674 pos = *compute_motion (prevline, 0,
675 lmargin + (prevline == 1 ? start_hpos : 0),
676 from, 1 << (INTBITS - 2), 0,
677 width, hscroll, 0);
678 }
679 else
680 {
681 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
682 pos.vpos = 0;
683 }
684 return compute_motion (from, vpos, pos.hpos,
685 ZV, vtarget, - (1 << (INTBITS - 2)),
686 width, hscroll, pos.vpos * width);
687 }
688
689 /* To move upward, go a line at a time until
690 we have gone at least far enough */
691
692 first = 1;
693
694 while ((vpos > vtarget || first) && from > BEGV)
695 {
696 prevline = from;
697 while (1)
698 {
699 prevline = find_next_newline (prevline - 1, -1);
700 if (prevline == BEGV
701 || ((selective <= 0
702 || ! indented_beyond_p (prevline, selective))
703 #ifdef USE_TEXT_PROPERTIES
704 /* watch out for newlines with `invisible' property */
705 && NILP (Fget_char_property (XFASTINT (prevline),
706 Qinvisible,
707 window))
708 #endif
709 ))
710 break;
711 }
712 pos = *compute_motion (prevline, 0,
713 lmargin + (prevline == 1 ? start_hpos : 0),
714 from, 1 << (INTBITS - 2), 0,
715 width, hscroll, 0);
716 vpos -= pos.vpos;
717 first = 0;
718 from = prevline;
719 }
720
721 /* If we made exactly the desired vertical distance,
722 or if we hit beginning of buffer,
723 return point found */
724 if (vpos >= vtarget)
725 {
726 val_vmotion.bufpos = from;
727 val_vmotion.vpos = vpos;
728 val_vmotion.hpos = lmargin;
729 val_vmotion.contin = 0;
730 val_vmotion.prevhpos = 0;
731 return &val_vmotion;
732 }
733
734 /* Otherwise find the correct spot by moving down */
735 goto retry;
736 }
737
738 DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0,
739 "Move to start of screen line LINES lines down.\n\
740 If LINES is negative, this is moving up.\n\
741 Sets point to position found; this may be start of line\n\
742 or just the start of a continuation line.\n\
743 Returns number of lines moved; may be closer to zero than LINES\n\
744 if beginning or end of buffer was reached.")
745 (lines)
746 Lisp_Object lines;
747 {
748 struct position pos;
749 register struct window *w = XWINDOW (selected_window);
750 int width = window_internal_width (w) - 1;
751
752 CHECK_NUMBER (lines, 0);
753
754 pos = *vmotion (point, XINT (lines), width,
755 /* Not XFASTINT since perhaps could be negative */
756 XINT (w->hscroll), selected_window);
757
758 SET_PT (pos.bufpos);
759 return make_number (pos.vpos);
760 }
761 \f
762 syms_of_indent ()
763 {
764 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
765 "*Indentation can insert tabs if this is non-nil.\n\
766 Setting this variable automatically makes it local to the current buffer.");
767 indent_tabs_mode = 1;
768
769 defsubr (&Scurrent_indentation);
770 defsubr (&Sindent_to);
771 defsubr (&Scurrent_column);
772 defsubr (&Smove_to_column);
773 defsubr (&Svertical_motion);
774 }