[!MULTI_FRAME] (FOR_EACH_FRAME): Fix Lisp_Object vs. int problems.
[bpt/emacs.git] / src / indent.c
CommitLineData
993b6404 1/* Indentation functions.
3a22ee35 2 Copyright (C) 1985,86,87,88,93,94 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 {
6d1bd1a5 209 Finsert_char (make_number ('\t'), n, Qt);
993b6404
JB
210
211 fromcol = (mincol / tab_width) * tab_width;
212 }
213 }
214
215 XFASTINT (col) = mincol - fromcol;
6d1bd1a5 216 Finsert_char (make_number (' '), col, Qt);
993b6404
JB
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')
04d25c3d 283 pos = find_next_newline_no_quit (pos - 1, -1);
1b15e576
KH
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
1827d036
KH
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
993b6404
JB
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
a3c87d4e
JB
419 - (has_vertical_scroll_bars
420 ? VERTICAL_SCROLL_BAR_WIDTH
fa61c701 421 : (window_width + window_left != frame_width))
993b6404
JB
422
423 where
424 window_width is XFASTINT (w->width),
425 window_left is XFASTINT (w->left),
a3c87d4e
JB
426 has_vertical_scroll_bars is
427 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
fa61c701 428 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
993b6404 429
1827d036
KH
430 Or you can let window_internal_width do this all for you, and write:
431 window_internal_width (w) - 1
fa61c701
JB
432
433 The `-1' accounts for the continuation-line backslashes; the rest
7e7a76b5 434 accounts for window borders if the window is split horizontally, and
1827d036 435 the scroll bars if they are turned on. */
993b6404
JB
436
437struct position *
88af3af4 438compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset, win)
993b6404
JB
439 int from, fromvpos, fromhpos, to, tovpos, tohpos;
440 register int width;
441 int hscroll, tab_offset;
88af3af4 442 struct window *win;
993b6404 443{
cde9337b
JB
444 register int hpos = fromhpos;
445 register int vpos = fromvpos;
446
993b6404
JB
447 register int pos;
448 register int c;
449 register int tab_width = XFASTINT (current_buffer->tab_width);
56a98455 450 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
88af3af4 451 register struct Lisp_Vector *dp = window_display_table (win);
993b6404 452 int selective
69eaf10d
RS
453 = (XTYPE (current_buffer->selective_display) == Lisp_Int
454 ? XINT (current_buffer->selective_display)
455 : !NILP (current_buffer->selective_display) ? -1 : 0);
42918ba5 456 int prev_vpos, prev_hpos = 0;
993b6404 457 int selective_rlen
dea4d2e6
RS
458 = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
459 ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
5a05d3d2
RS
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
993b6404 465
ccdcf1f5 466 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
cde9337b 467 for (pos = from; pos < to; pos++)
993b6404 468 {
cde9337b
JB
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
5a05d3d2
RS
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)
f75c0f8a
RS
481 {
482 XFASTINT (position) = pos;
67406ef4 483 prop = Fget_char_property (position,
f75c0f8a
RS
484 Qinvisible,
485 Fcurrent_buffer ());
486 {
487 Lisp_Object end, limit;
488
90ed3777 489 recenter_overlay_lists (current_buffer, pos);
f75c0f8a
RS
490 /* This is just an estimate to give reasonable
491 performance; nothing should go wrong if it is too small. */
7ea9c020
KH
492 limit = Fnext_overlay_change (position);
493 if (XFASTINT (limit) > pos + 100)
494 XFASTINT (limit) = pos + 100;
f75c0f8a
RS
495 end = Fnext_single_property_change (position, Qinvisible,
496 Fcurrent_buffer (), limit);
497 if (INTEGERP (end))
498 next_invisible = XINT (end);
499 else
500 next_invisible = to;
501 if (! NILP (prop))
502 pos = next_invisible;
503 }
504 }
5a05d3d2
RS
505 if (pos >= to)
506 break;
507#endif
993b6404
JB
508 c = FETCH_CHAR (pos);
509 if (c >= 040 && c < 0177
dea4d2e6 510 && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
cde9337b 511 hpos++;
993b6404
JB
512 else if (c == '\t')
513 {
cde9337b
JB
514 hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
515 /* Add tab_width here to make sure positive.
516 hpos can be negative after continuation
517 but can't be less than -tab_width. */
518 + tab_width)
519 % tab_width);
993b6404
JB
520 }
521 else if (c == '\n')
522 {
1b15e576 523 if (selective > 0 && indented_beyond_p (pos + 1, selective))
993b6404
JB
524 {
525 /* Skip any number of invisible lines all at once */
526 do
527 {
cde9337b 528 while (++pos < to && FETCH_CHAR (pos) != '\n');
993b6404 529 }
1b15e576 530 while (pos < to && indented_beyond_p (pos + 1, selective));
7e7a76b5 531 pos--; /* Reread the newline on the next pass. */
993b6404
JB
532 /* Allow for the " ..." that is displayed for them. */
533 if (selective_rlen)
534 {
cde9337b
JB
535 hpos += selective_rlen;
536 if (hpos >= width)
537 hpos = width;
993b6404 538 }
e51eab7c 539 /* We have skipped the invis text, but not the newline after. */
993b6404
JB
540 }
541 else
cde9337b
JB
542 {
543 /* A visible line. */
544 vpos++;
545 hpos = 0;
525a87de 546 hpos -= hscroll;
1827d036 547 if (hscroll > 0) hpos++; /* Truncation glyph on column 0 */
525a87de
KH
548 tab_offset = 0;
549 }
993b6404
JB
550 }
551 else if (c == CR && selective < 0)
552 {
553 /* In selective display mode,
554 everything from a ^M to the end of the line is invisible */
cde9337b
JB
555 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
556 /* Stop *before* the real newline. */
993b6404
JB
557 pos--;
558 /* Allow for the " ..." that is displayed for them. */
559 if (selective_rlen)
560 {
cde9337b
JB
561 hpos += selective_rlen;
562 if (hpos >= width)
563 hpos = width;
993b6404
JB
564 }
565 }
dea4d2e6
RS
566 else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
567 hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
993b6404 568 else
cde9337b 569 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
993b6404 570
cde9337b
JB
571 /* Handle right margin. */
572 if (hpos >= width
573 && (hpos > width
574 || (pos < ZV - 1
993b6404
JB
575 && FETCH_CHAR (pos + 1) != '\n')))
576 {
cde9337b
JB
577 if (vpos > tovpos
578 || (vpos == tovpos && hpos >= tohpos))
993b6404
JB
579 break;
580 if (hscroll
581 || (truncate_partial_width_windows
5d83b593 582 && width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
56a98455 583 || !NILP (current_buffer->truncate_lines))
993b6404 584 {
cde9337b
JB
585 /* Truncating: skip to newline. */
586 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
993b6404 587 pos--;
e51eab7c 588 hpos = width;
993b6404
JB
589 }
590 else
591 {
cde9337b
JB
592 /* Continuing. */
593 vpos++;
594 hpos -= width;
993b6404
JB
595 tab_offset += width;
596 }
597
598 }
599 }
600
601 val_compute_motion.bufpos = pos;
cde9337b
JB
602 val_compute_motion.hpos = hpos;
603 val_compute_motion.vpos = vpos;
604 val_compute_motion.prevhpos = prev_hpos;
993b6404
JB
605
606 /* Nonzero if have just continued a line */
607 val_compute_motion.contin
cde9337b
JB
608 = (pos != from
609 && (val_compute_motion.vpos != prev_vpos)
610 && c != '\n');
993b6404
JB
611
612 return &val_compute_motion;
613}
993b6404 614
992371ca
KH
615#if 0 /* The doc string is too long for some compilers,
616 but make-docfile can find it in this comment. */
88af3af4 617DEFUN ("compute-motion", Ffoo, Sfoo, 7, 7, 0,
42918ba5
RS
618 "Scan through the current buffer, calculating screen position.\n\
619Scan the current buffer forward from offset FROM,\n\
620assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
621to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
622and return the ending buffer position and screen location.\n\
623\n\
88af3af4 624There are three additional arguments:\n\
42918ba5
RS
625\n\
626WIDTH is the number of columns available to display text;\n\
627this affects handling of continuation lines.\n\
992371ca
KH
628This is usually the value returned by `window-width', less one (to allow\n\
629for the continuation glyph).\n\
42918ba5
RS
630\n\
631OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
632HSCROLL is the number of columns not being displayed at the left\n\
633margin; this is usually taken from a window's hscroll member.\n\
634TAB-OFFSET is the number of columns of the first tab that aren't\n\
635being displayed, perhaps because the line was continued within it.\n\
4fb76787 636If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
69eaf10d 637\n\
88af3af4 638WINDOW is the window to operate on. Currently this is used only to\n\
69eaf10d
RS
639find the display table. It does not matter what buffer WINDOW displays;\n\
640`compute-motion' always operates on the current buffer.\n\
42918ba5
RS
641\n\
642The value is a list of five elements:\n\
faa5c515 643 (POS HPOS VPOS PREVHPOS CONTIN)\n\
42918ba5
RS
644POS is the buffer position where the scan stopped.\n\
645VPOS is the vertical position where the scan stopped.\n\
646HPOS is the horizontal position where the scan stopped.\n\
647\n\
648PREVHPOS is the horizontal position one character back from POS.\n\
649CONTIN is t if a line was continued after (or within) the previous character.\n\
650\n\
651For example, to find the buffer position of column COL of line LINE\n\
652of a certain window, pass the window's starting location as FROM\n\
653and the window's upper-left coordinates as FROMPOS.\n\
654Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
655visible section of the buffer, and pass LINE and COL as TOPOS.")
5844e1c4 656 (from, frompos, to, topos, width, offsets, window)
992371ca
KH
657#endif
658
88af3af4 659DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
992371ca 660 0)
88af3af4 661 (from, frompos, to, topos, width, offsets, window)
42918ba5 662 Lisp_Object from, frompos, to, topos;
88af3af4 663 Lisp_Object width, offsets, window;
42918ba5
RS
664{
665 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
666 struct position *pos;
667 int hscroll, tab_offset;
668
26a8d14b 669 CHECK_NUMBER_COERCE_MARKER (from, 0);
42918ba5 670 CHECK_CONS (frompos, 0);
26a8d14b
KH
671 CHECK_NUMBER (XCONS (frompos)->car, 0);
672 CHECK_NUMBER (XCONS (frompos)->cdr, 0);
673 CHECK_NUMBER_COERCE_MARKER (to, 0);
42918ba5 674 CHECK_CONS (topos, 0);
26a8d14b
KH
675 CHECK_NUMBER (XCONS (topos)->car, 0);
676 CHECK_NUMBER (XCONS (topos)->cdr, 0);
677 CHECK_NUMBER (width, 0);
42918ba5
RS
678 if (!NILP (offsets))
679 {
680 CHECK_CONS (offsets, 0);
26a8d14b
KH
681 CHECK_NUMBER (XCONS (offsets)->car, 0);
682 CHECK_NUMBER (XCONS (offsets)->cdr, 0);
42918ba5
RS
683 hscroll = XINT (XCONS (offsets)->car);
684 tab_offset = XINT (XCONS (offsets)->cdr);
685 }
686 else
687 hscroll = tab_offset = 0;
688
88af3af4
KH
689 if (NILP (window))
690 window = Fselected_window ();
691 else
692 CHECK_LIVE_WINDOW (window, 0);
693
42918ba5
RS
694 pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
695 XINT (XCONS (frompos)->car),
696 XINT (to), XINT (XCONS (topos)->cdr),
697 XINT (XCONS (topos)->car),
88af3af4
KH
698 XINT (width), hscroll, tab_offset,
699 XWINDOW (window));
42918ba5
RS
700
701 XFASTINT (bufpos) = pos->bufpos;
faa5c515 702 XSET (hpos, Lisp_Int, pos->hpos);
42918ba5 703 XSET (vpos, Lisp_Int, pos->vpos);
faa5c515 704 XSET (prevhpos, Lisp_Int, pos->prevhpos);
42918ba5
RS
705
706 return Fcons (bufpos,
707 Fcons (hpos,
708 Fcons (vpos,
709 Fcons (prevhpos,
710 Fcons (pos->contin ? Qt : Qnil, Qnil)))));
711
712}
993b6404
JB
713\f
714/* Return the column of position POS in window W's buffer,
715 rounded down to a multiple of the internal width of W.
716 This is the amount of indentation of position POS
717 that is not visible in its horizontal position in the window. */
718
719int
720pos_tab_offset (w, pos)
721 struct window *w;
722 register int pos;
723{
3d94e943 724 int opoint = PT;
993b6404 725 int col;
fa61c701 726 int width = window_internal_width (w) - 1;
993b6404
JB
727
728 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
729 return 0;
3d94e943 730 TEMP_SET_PT (pos);
993b6404 731 col = current_column ();
3d94e943 732 TEMP_SET_PT (opoint);
993b6404
JB
733 return col - (col % width);
734}
735
736/* start_hpos is the hpos of the first character of the buffer:
737 zero except for the minibuffer window,
738 where it is the width of the prompt. */
739
740struct position val_vmotion;
741
742struct position *
743vmotion (from, vtarget, width, hscroll, window)
744 register int from, vtarget, width;
745 int hscroll;
746 Lisp_Object window;
747{
748 struct position pos;
749 /* vpos is cumulative vertical position, changed as from is changed */
750 register int vpos = 0;
751 register int prevline;
752 register int first;
753 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
754 int selective
755 = XTYPE (current_buffer->selective_display) == Lisp_Int
756 ? XINT (current_buffer->selective_display)
56a98455 757 : !NILP (current_buffer->selective_display) ? -1 : 0;
cb1068e5
KH
758 /* The omission of the clause
759 && marker_position (XWINDOW (window)->start) == BEG
760 here is deliberate; I think we want to measure from the prompt
761 position even if the minibuffer window has scrolled. */
762 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
993b6404
JB
763
764 retry:
765 if (vtarget > vpos)
766 {
767 /* Moving downward is simple, but must calculate from beg of line
768 to determine hpos of starting point */
769 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
770 {
04d25c3d 771 prevline = find_next_newline_no_quit (from, -1);
5a05d3d2
RS
772 while (prevline > BEGV
773 && ((selective > 0
1b15e576 774 && indented_beyond_p (prevline, selective))
5a05d3d2
RS
775#ifdef USE_TEXT_PROPERTIES
776 /* watch out for newlines with `invisible' property */
67406ef4 777 || ! NILP (Fget_char_property (XFASTINT (prevline),
5a05d3d2 778 Qinvisible,
67406ef4 779 window))
5a05d3d2
RS
780#endif
781 ))
04d25c3d 782 prevline = find_next_newline_no_quit (prevline - 1, -1);
993b6404
JB
783 pos = *compute_motion (prevline, 0,
784 lmargin + (prevline == 1 ? start_hpos : 0),
cde9337b 785 from, 1 << (INTBITS - 2), 0,
88af3af4 786 width, hscroll, 0, XWINDOW (window));
993b6404
JB
787 }
788 else
789 {
790 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
791 pos.vpos = 0;
792 }
793 return compute_motion (from, vpos, pos.hpos,
cde9337b 794 ZV, vtarget, - (1 << (INTBITS - 2)),
88af3af4
KH
795 width, hscroll, pos.vpos * width,
796 XWINDOW (window));
993b6404
JB
797 }
798
799 /* To move upward, go a line at a time until
800 we have gone at least far enough */
801
802 first = 1;
803
804 while ((vpos > vtarget || first) && from > BEGV)
805 {
806 prevline = from;
807 while (1)
808 {
04d25c3d 809 prevline = find_next_newline_no_quit (prevline - 1, -1);
993b6404 810 if (prevline == BEGV
5a05d3d2 811 || ((selective <= 0
1b15e576 812 || ! indented_beyond_p (prevline, selective))
5a05d3d2
RS
813#ifdef USE_TEXT_PROPERTIES
814 /* watch out for newlines with `invisible' property */
67406ef4 815 && NILP (Fget_char_property (XFASTINT (prevline),
5a05d3d2 816 Qinvisible,
67406ef4 817 window))
5a05d3d2
RS
818#endif
819 ))
993b6404
JB
820 break;
821 }
822 pos = *compute_motion (prevline, 0,
823 lmargin + (prevline == 1 ? start_hpos : 0),
cde9337b 824 from, 1 << (INTBITS - 2), 0,
88af3af4 825 width, hscroll, 0, XWINDOW (window));
993b6404
JB
826 vpos -= pos.vpos;
827 first = 0;
828 from = prevline;
829 }
830
831 /* If we made exactly the desired vertical distance,
832 or if we hit beginning of buffer,
833 return point found */
834 if (vpos >= vtarget)
835 {
836 val_vmotion.bufpos = from;
837 val_vmotion.vpos = vpos;
838 val_vmotion.hpos = lmargin;
839 val_vmotion.contin = 0;
840 val_vmotion.prevhpos = 0;
841 return &val_vmotion;
842 }
843
844 /* Otherwise find the correct spot by moving down */
845 goto retry;
846}
847
f1ecfe9b 848DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
993b6404
JB
849 "Move to start of screen line LINES lines down.\n\
850If LINES is negative, this is moving up.\n\
69eaf10d
RS
851\n\
852The optional second argument WINDOW specifies the window to use for\n\
853parameters such as width, horizontal scrolling, and so on.\n\
854the default is the selected window.\n\
855It does not matter what buffer is displayed in WINDOW.\n\
856`vertical-motion' always uses the current buffer.\n\
857\n\
993b6404 858Sets point to position found; this may be start of line\n\
69eaf10d 859or just the start of a continuation line.\n\
993b6404 860Returns number of lines moved; may be closer to zero than LINES\n\
69eaf10d 861if beginning or end of buffer was reached.")
f1ecfe9b
RS
862 (lines, window)
863 Lisp_Object lines, window;
993b6404
JB
864{
865 struct position pos;
230a75fc 866 register struct window *w;
993b6404
JB
867
868 CHECK_NUMBER (lines, 0);
f1ecfe9b
RS
869 if (! NILP (window))
870 CHECK_WINDOW (window, 0);
871 else
872 XSET (window, Lisp_Window, selected_window);
993b6404 873
230a75fc 874 w = XWINDOW (window);
69eaf10d 875
230a75fc 876 pos = *vmotion (point, XINT (lines), window_internal_width (w) - 1,
993b6404 877 /* Not XFASTINT since perhaps could be negative */
f1ecfe9b 878 XINT (w->hscroll), window);
993b6404
JB
879
880 SET_PT (pos.bufpos);
881 return make_number (pos.vpos);
882}
883\f
884syms_of_indent ()
885{
886 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
887 "*Indentation can insert tabs if this is non-nil.\n\
888Setting this variable automatically makes it local to the current buffer.");
889 indent_tabs_mode = 1;
890
891 defsubr (&Scurrent_indentation);
892 defsubr (&Sindent_to);
893 defsubr (&Scurrent_column);
894 defsubr (&Smove_to_column);
895 defsubr (&Svertical_motion);
42918ba5 896 defsubr (&Scompute_motion);
993b6404 897}