(Fstart_process): GCPRO some things.
[bpt/emacs.git] / src / indent.c
CommitLineData
993b6404 1/* Indentation functions.
c6c5df7f 2 Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
993b6404
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
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
18160b98 21#include <config.h>
993b6404
JB
22#include "lisp.h"
23#include "buffer.h"
24#include "indent.h"
502b9b64 25#include "frame.h"
993b6404
JB
26#include "window.h"
27#include "termchar.h"
28#include "termopts.h"
29#include "disptab.h"
5a05d3d2 30#include "intervals.h"
993b6404
JB
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
993b6404
JB
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\
502b9b64
JB
74Ignores finite width of frame, which means that this function may return\n\
75values greater than (frame-width).\n\
993b6404
JB
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
e74928fc
JB
85/* Cancel any recorded value of the horizontal position. */
86
87invalidate_current_column ()
88{
89 last_known_column_point = 0;
90}
91
993b6404
JB
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);
56a98455 101 int ctl_arrow = !NILP (current_buffer->ctl_arrow);
993b6404
JB
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
ccdcf1f5 120 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
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
dea4d2e6 141 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
993b6404
JB
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 }
dea4d2e6
RS
158 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
159 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
993b6404
JB
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);
56a98455 190 if (NILP (minimum))
993b6404
JB
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
ccdcf1f5 201 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
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
ccdcf1f5 247 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
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}
1b15e576
KH
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 (pos - 1, -1);
284 return (position_indentation (pos) >= column);
285}
993b6404
JB
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\
230a4cbd
JB
293and horizontal scrolling has no effect.\n\
294\n\
993b6404
JB
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);
56a98455 308 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
993b6404
JB
309 register struct Lisp_Vector *dp = buffer_display_table ();
310
311 Lisp_Object val;
312 int prev_col;
313 int c;
314
ccdcf1f5 315 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
993b6404
JB
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 }
dea4d2e6
RS
345 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
346 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
993b6404 347 else if (ctl_arrow && (c < 040 || c == 0177))
bbc2998f 348 col += 2;
993b6404 349 else if (c < 040 || c >= 0177)
bbc2998f 350 col += 4;
993b6404
JB
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. */
56a98455 359 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
993b6404 360 {
70ee42f7
JB
361 int old_point;
362
993b6404 363 del_range (point - 1, point);
70ee42f7
JB
364 Findent_to (make_number (goal), Qnil);
365 old_point = point;
366 Findent_to (make_number (col), Qnil);
367 SET_PT (old_point);
5a05d3d2
RS
368 /* Set the last_known... vars consistently. */
369 col = goal;
993b6404
JB
370 }
371
372 /* If line ends prematurely, add space to the end. */
56a98455 373 if (col < goal && !NILP (force))
230a4cbd 374 Findent_to (make_number (col = goal), Qnil);
993b6404
JB
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.
a9764248
JB
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.
993b6404
JB
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
a3c87d4e
JB
414 - (has_vertical_scroll_bars
415 ? VERTICAL_SCROLL_BAR_WIDTH
fa61c701 416 : (window_width + window_left != frame_width))
993b6404
JB
417
418 where
419 window_width is XFASTINT (w->width),
420 window_left is XFASTINT (w->left),
a3c87d4e
JB
421 has_vertical_scroll_bars is
422 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
fa61c701 423 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
993b6404 424
fa61c701
JB
425 Or,
426 window_internal_width (w) - 1
427
428 The `-1' accounts for the continuation-line backslashes; the rest
7e7a76b5 429 accounts for window borders if the window is split horizontally, and
a3c87d4e 430 the scroll bars if the frame supports them. */
993b6404
JB
431
432struct position *
433compute_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{
cde9337b
JB
438 register int hpos = fromhpos;
439 register int vpos = fromvpos;
440
993b6404
JB
441 register int pos;
442 register int c;
443 register int tab_width = XFASTINT (current_buffer->tab_width);
56a98455 444 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
993b6404
JB
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)
56a98455 449 : !NILP (current_buffer->selective_display) ? -1 : 0;
42918ba5 450 int prev_vpos, prev_hpos = 0;
993b6404 451 int selective_rlen
dea4d2e6
RS
452 = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
453 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
5a05d3d2
RS
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
993b6404 459
ccdcf1f5 460 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
cde9337b 461 for (pos = from; pos < to; pos++)
993b6404 462 {
cde9337b
JB
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
5a05d3d2
RS
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)
f75c0f8a
RS
475 {
476 XFASTINT (position) = pos;
67406ef4 477 prop = Fget_char_property (position,
f75c0f8a
RS
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. */
7ea9c020
KH
485 limit = Fnext_overlay_change (position);
486 if (XFASTINT (limit) > pos + 100)
487 XFASTINT (limit) = pos + 100;
f75c0f8a
RS
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 }
5a05d3d2
RS
498 if (pos >= to)
499 break;
500#endif
993b6404
JB
501 c = FETCH_CHAR (pos);
502 if (c >= 040 && c < 0177
dea4d2e6 503 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
cde9337b 504 hpos++;
993b6404
JB
505 else if (c == '\t')
506 {
cde9337b
JB
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);
993b6404
JB
513 }
514 else if (c == '\n')
515 {
1b15e576 516 if (selective > 0 && indented_beyond_p (pos + 1, selective))
993b6404
JB
517 {
518 /* Skip any number of invisible lines all at once */
519 do
520 {
cde9337b 521 while (++pos < to && FETCH_CHAR (pos) != '\n');
993b6404 522 }
1b15e576 523 while (pos < to && indented_beyond_p (pos + 1, selective));
7e7a76b5 524 pos--; /* Reread the newline on the next pass. */
993b6404
JB
525 /* Allow for the " ..." that is displayed for them. */
526 if (selective_rlen)
527 {
cde9337b
JB
528 hpos += selective_rlen;
529 if (hpos >= width)
530 hpos = width;
993b6404 531 }
e51eab7c 532 /* We have skipped the invis text, but not the newline after. */
993b6404
JB
533 }
534 else
cde9337b
JB
535 {
536 /* A visible line. */
537 vpos++;
538 hpos = 0;
525a87de 539 hpos -= hscroll;
7e7a76b5 540 if (hscroll > 0) hpos++; /* Count the $ on column 0 */
525a87de
KH
541 tab_offset = 0;
542 }
993b6404
JB
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 */
cde9337b
JB
548 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
549 /* Stop *before* the real newline. */
993b6404
JB
550 pos--;
551 /* Allow for the " ..." that is displayed for them. */
552 if (selective_rlen)
553 {
cde9337b
JB
554 hpos += selective_rlen;
555 if (hpos >= width)
556 hpos = width;
993b6404
JB
557 }
558 }
dea4d2e6
RS
559 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
560 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
993b6404 561 else
cde9337b 562 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
993b6404 563
cde9337b
JB
564 /* Handle right margin. */
565 if (hpos >= width
566 && (hpos > width
567 || (pos < ZV - 1
993b6404
JB
568 && FETCH_CHAR (pos + 1) != '\n')))
569 {
cde9337b
JB
570 if (vpos > tovpos
571 || (vpos == tovpos && hpos >= tohpos))
993b6404
JB
572 break;
573 if (hscroll
574 || (truncate_partial_width_windows
502b9b64 575 && width + 1 < FRAME_WIDTH (selected_frame))
56a98455 576 || !NILP (current_buffer->truncate_lines))
993b6404 577 {
cde9337b
JB
578 /* Truncating: skip to newline. */
579 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
993b6404 580 pos--;
e51eab7c 581 hpos = width;
993b6404
JB
582 }
583 else
584 {
cde9337b
JB
585 /* Continuing. */
586 vpos++;
587 hpos -= width;
993b6404
JB
588 tab_offset += width;
589 }
590
591 }
592 }
593
594 val_compute_motion.bufpos = pos;
cde9337b
JB
595 val_compute_motion.hpos = hpos;
596 val_compute_motion.vpos = vpos;
597 val_compute_motion.prevhpos = prev_hpos;
993b6404
JB
598
599 /* Nonzero if have just continued a line */
600 val_compute_motion.contin
cde9337b
JB
601 = (pos != from
602 && (val_compute_motion.vpos != prev_vpos)
603 && c != '\n');
993b6404
JB
604
605 return &val_compute_motion;
606}
993b6404 607
42918ba5
RS
608DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 6, 6, 0,
609 "Scan through the current buffer, calculating screen position.\n\
610Scan the current buffer forward from offset FROM,\n\
611assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
612to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
613and return the ending buffer position and screen location.\n\
614\n\
615There are two additional arguments:\n\
616\n\
617WIDTH is the number of columns available to display text;\n\
618this affects handling of continuation lines.\n\
619Use the value returned by `window-width' for the window of your choice.\n\
620\n\
621OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
622HSCROLL is the number of columns not being displayed at the left\n\
623margin; this is usually taken from a window's hscroll member.\n\
624TAB-OFFSET is the number of columns of the first tab that aren't\n\
625being displayed, perhaps because the line was continued within it.\n\
626\n\
627The value is a list of five elements:\n\
628 (POS VPOS HPOS PREVHPOS CONTIN)\n\
629POS is the buffer position where the scan stopped.\n\
630VPOS is the vertical position where the scan stopped.\n\
631HPOS is the horizontal position where the scan stopped.\n\
632\n\
633PREVHPOS is the horizontal position one character back from POS.\n\
634CONTIN is t if a line was continued after (or within) the previous character.\n\
635\n\
636For example, to find the buffer position of column COL of line LINE\n\
637of a certain window, pass the window's starting location as FROM\n\
638and the window's upper-left coordinates as FROMPOS.\n\
639Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
640visible section of the buffer, and pass LINE and COL as TOPOS.")
641 (from, frompos, to, topos, width, offsets)
642 Lisp_Object from, frompos, to, topos;
643 Lisp_Object width, offsets;
644{
645 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
646 struct position *pos;
647 int hscroll, tab_offset;
648
649 CHECK_CONS (frompos, 0);
650 CHECK_CONS (topos, 0);
651 if (!NILP (offsets))
652 {
653 CHECK_CONS (offsets, 0);
654 hscroll = XINT (XCONS (offsets)->car);
655 tab_offset = XINT (XCONS (offsets)->cdr);
656 }
657 else
658 hscroll = tab_offset = 0;
659
660 pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
661 XINT (XCONS (frompos)->car),
662 XINT (to), XINT (XCONS (topos)->cdr),
663 XINT (XCONS (topos)->car),
664 XINT (width), hscroll, tab_offset);
665
666 XFASTINT (bufpos) = pos->bufpos;
667 XFASTINT (hpos) = pos->hpos;
668 XSET (vpos, Lisp_Int, pos->vpos);
669 XFASTINT (prevhpos) = pos->prevhpos;
670
671 return Fcons (bufpos,
672 Fcons (hpos,
673 Fcons (vpos,
674 Fcons (prevhpos,
675 Fcons (pos->contin ? Qt : Qnil, Qnil)))));
676
677}
993b6404
JB
678\f
679/* Return the column of position POS in window W's buffer,
680 rounded down to a multiple of the internal width of W.
681 This is the amount of indentation of position POS
682 that is not visible in its horizontal position in the window. */
683
684int
685pos_tab_offset (w, pos)
686 struct window *w;
687 register int pos;
688{
689 int opoint = point;
690 int col;
fa61c701 691 int width = window_internal_width (w) - 1;
993b6404
JB
692
693 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
694 return 0;
695 SET_PT (pos);
696 col = current_column ();
697 SET_PT (opoint);
698 return col - (col % width);
699}
700
701/* start_hpos is the hpos of the first character of the buffer:
702 zero except for the minibuffer window,
703 where it is the width of the prompt. */
704
705struct position val_vmotion;
706
707struct position *
708vmotion (from, vtarget, width, hscroll, window)
709 register int from, vtarget, width;
710 int hscroll;
711 Lisp_Object window;
712{
713 struct position pos;
714 /* vpos is cumulative vertical position, changed as from is changed */
715 register int vpos = 0;
716 register int prevline;
717 register int first;
718 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
719 int selective
720 = XTYPE (current_buffer->selective_display) == Lisp_Int
721 ? XINT (current_buffer->selective_display)
56a98455 722 : !NILP (current_buffer->selective_display) ? -1 : 0;
993b6404
JB
723 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
724
725 retry:
726 if (vtarget > vpos)
727 {
728 /* Moving downward is simple, but must calculate from beg of line
729 to determine hpos of starting point */
730 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
731 {
732 prevline = find_next_newline (from, -1);
5a05d3d2
RS
733 while (prevline > BEGV
734 && ((selective > 0
1b15e576 735 && indented_beyond_p (prevline, selective))
5a05d3d2
RS
736#ifdef USE_TEXT_PROPERTIES
737 /* watch out for newlines with `invisible' property */
67406ef4 738 || ! NILP (Fget_char_property (XFASTINT (prevline),
5a05d3d2 739 Qinvisible,
67406ef4 740 window))
5a05d3d2
RS
741#endif
742 ))
993b6404
JB
743 prevline = find_next_newline (prevline - 1, -1);
744 pos = *compute_motion (prevline, 0,
745 lmargin + (prevline == 1 ? start_hpos : 0),
cde9337b 746 from, 1 << (INTBITS - 2), 0,
993b6404
JB
747 width, hscroll, 0);
748 }
749 else
750 {
751 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
752 pos.vpos = 0;
753 }
754 return compute_motion (from, vpos, pos.hpos,
cde9337b 755 ZV, vtarget, - (1 << (INTBITS - 2)),
993b6404
JB
756 width, hscroll, pos.vpos * width);
757 }
758
759 /* To move upward, go a line at a time until
760 we have gone at least far enough */
761
762 first = 1;
763
764 while ((vpos > vtarget || first) && from > BEGV)
765 {
766 prevline = from;
767 while (1)
768 {
769 prevline = find_next_newline (prevline - 1, -1);
770 if (prevline == BEGV
5a05d3d2 771 || ((selective <= 0
1b15e576 772 || ! indented_beyond_p (prevline, selective))
5a05d3d2
RS
773#ifdef USE_TEXT_PROPERTIES
774 /* watch out for newlines with `invisible' property */
67406ef4 775 && NILP (Fget_char_property (XFASTINT (prevline),
5a05d3d2 776 Qinvisible,
67406ef4 777 window))
5a05d3d2
RS
778#endif
779 ))
993b6404
JB
780 break;
781 }
782 pos = *compute_motion (prevline, 0,
783 lmargin + (prevline == 1 ? start_hpos : 0),
cde9337b 784 from, 1 << (INTBITS - 2), 0,
993b6404
JB
785 width, hscroll, 0);
786 vpos -= pos.vpos;
787 first = 0;
788 from = prevline;
789 }
790
791 /* If we made exactly the desired vertical distance,
792 or if we hit beginning of buffer,
793 return point found */
794 if (vpos >= vtarget)
795 {
796 val_vmotion.bufpos = from;
797 val_vmotion.vpos = vpos;
798 val_vmotion.hpos = lmargin;
799 val_vmotion.contin = 0;
800 val_vmotion.prevhpos = 0;
801 return &val_vmotion;
802 }
803
804 /* Otherwise find the correct spot by moving down */
805 goto retry;
806}
807
f1ecfe9b 808DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
993b6404
JB
809 "Move to start of screen line LINES lines down.\n\
810If LINES is negative, this is moving up.\n\
f1ecfe9b
RS
811The optional second argument WINDOW specifies the window\n\
812 to use for computations.\n\
993b6404
JB
813Sets point to position found; this may be start of line\n\
814 or just the start of a continuation line.\n\
815Returns number of lines moved; may be closer to zero than LINES\n\
816 if beginning or end of buffer was reached.")
f1ecfe9b
RS
817 (lines, window)
818 Lisp_Object lines, window;
993b6404
JB
819{
820 struct position pos;
821 register struct window *w = XWINDOW (selected_window);
fa61c701 822 int width = window_internal_width (w) - 1;
993b6404
JB
823
824 CHECK_NUMBER (lines, 0);
f1ecfe9b
RS
825 if (! NILP (window))
826 CHECK_WINDOW (window, 0);
827 else
828 XSET (window, Lisp_Window, selected_window);
993b6404
JB
829
830 pos = *vmotion (point, XINT (lines), width,
831 /* Not XFASTINT since perhaps could be negative */
f1ecfe9b 832 XINT (w->hscroll), window);
993b6404
JB
833
834 SET_PT (pos.bufpos);
835 return make_number (pos.vpos);
836}
837\f
838syms_of_indent ()
839{
840 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
841 "*Indentation can insert tabs if this is non-nil.\n\
842Setting this variable automatically makes it local to the current buffer.");
843 indent_tabs_mode = 1;
844
845 defsubr (&Scurrent_indentation);
846 defsubr (&Sindent_to);
847 defsubr (&Scurrent_column);
848 defsubr (&Smove_to_column);
849 defsubr (&Svertical_motion);
42918ba5 850 defsubr (&Scompute_motion);
993b6404 851}