Initial git import
[sxemacs] / src / indent.c
1 /* Indentation functions.
2    Copyright (C) 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995
4    Free Software Foundation, Inc.
5
6 This file is part of SXEmacs
7
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
20
21
22 /* This file has been Mule-ized. */
23
24 /* Synched up with: 19.30.  Diverges significantly from FSF. */
25
26 #include <config.h>
27 #include "lisp.h"
28
29 #include "buffer.h"
30 #include "ui/device.h"
31 #include "extents.h"
32 #include "ui/faces.h"
33 #include "ui/frame.h"
34 #include "ui/glyphs.h"
35 #include "ui/insdel.h"
36 #ifdef REGION_CACHE_NEEDS_WORK
37 #include "region-cache.h"
38 #endif
39 #include "ui/window.h"
40
41 Lisp_Object Qcoerce;
42
43 /* Indentation can insert tabs if this is non-zero;
44    otherwise always uses spaces */
45 int indent_tabs_mode;
46
47 /* Avoid recalculation by remembering things in these variables. */
48
49 /* Last value returned by current_column.
50
51    Some things set last_known_column_point to -1
52    to mark the memoized value as invalid */
53 static int last_known_column;
54
55 /* Last buffer searched by current_column */
56 static struct buffer *last_known_column_buffer;
57
58 /* Value of point when current_column was called */
59 static Bufpos last_known_column_point;
60
61 /* Value of MODIFF when current_column was called */
62 static int last_known_column_modified;
63
64 static Bufpos last_visible_position(Bufpos pos, struct buffer *buf)
65 {
66         Lisp_Object buffer;
67         Lisp_Object value;
68
69         XSETBUFFER(buffer, buf);
70         value = Fprevious_single_property_change(make_int(pos), Qinvisible,
71                                                  buffer, Qnil);
72         if (NILP(value))
73                 return 0;       /* no visible position found */
74         else
75                 /* #### bug bug bug!!! This will return the position of the beginning
76                    of an invisible extent; this extent is very likely to be start-closed,
77                    and thus the spaces inserted in `indent-to' will go inside the
78                    invisible extent.
79
80                    Not sure what the correct solution is here.  Rethink indent-to? */
81                 return XINT(value);
82 }
83
84 #ifdef REGION_CACHE_NEEDS_WORK
85
86 /* Allocate or free the width run cache, as requested by the current
87    state of current_buffer's cache_long_line_scans variable.  */
88 static void width_run_cache_on_off(struct buffer *buf)
89 {
90         if (NILP(buf->cache_long_line_scans)) {
91                 /* It should be off.  */
92                 if (buf->width_run_cache) {
93                         free_region_cache(buf->width_run_cache);
94                         buf->width_run_cache = 0;
95                         buf->width_table = Qnil;
96                 }
97         } else {
98                 /* It should be on.  */
99                 if (buf->width_run_cache == 0) {
100                         buf->width_run_cache = new_region_cache();
101                         recompute_width_table(buf, buffer_display_table());
102                 }
103         }
104 }
105
106 #endif                          /* REGION_CACHE_NEEDS_WORK */
107 \f
108 /* Cancel any recorded value of the horizontal position.  */
109
110 void invalidate_current_column(void)
111 {
112         last_known_column_point = -1;
113 }
114
115 int column_at_point(struct buffer *buf, Bufpos init_pos, int cur_col)
116 {
117         int col;
118         int tab_seen;
119         int tab_width = XINT(buf->tab_width);
120         int post_tab;
121         Bufpos pos = init_pos;
122         Emchar c;
123
124         if (tab_width <= 0 || tab_width > 1000)
125                 tab_width = 8;
126         col = tab_seen = post_tab = 0;
127
128         while (1) {
129                 if (pos <= BUF_BEGV(buf))
130                         break;
131
132                 pos--;
133                 c = BUF_FETCH_CHAR(buf, pos);
134                 if (c == '\t') {
135                         if (tab_seen)
136                                 col =
137                                     ((col + tab_width) / tab_width) * tab_width;
138
139                         post_tab += col;
140                         col = 0;
141                         tab_seen = 1;
142                 } else if (c == '\n' ||
143                            (EQ(buf->selective_display, Qt) && c == '\r'))
144                         break;
145                 else {
146                         /* #### This needs updating to handle the new redisplay. */
147                         /* #### FSFmacs looks at ctl_arrow, display tables.
148                            We need to do similar. */
149 #if 0
150                         displayed_glyphs = glyphs_from_bufpos(sel_frame, buf,
151                                                               XWINDOW
152                                                               (selected_window),
153                                                               pos, dp, 0, col,
154                                                               0, 0, 0);
155                         col +=
156                             (displayed_glyphs->columns -
157                              (displayed_glyphs->begin_columns +
158                               displayed_glyphs->end_columns));
159 #else                           /* SXEmacs */
160 #ifdef MULE
161                         {
162                                 Lisp_Object tmp = CHAR_CHARSET(c);
163                                 col += XCHARSET_COLUMNS(tmp);
164                         }
165 #else
166                         col++;
167 #endif                          /* MULE */
168 #endif                          /* SXEmacs */
169                 }
170         }
171
172         if (tab_seen) {
173                 col = ((col + tab_width) / tab_width) * tab_width;
174                 col += post_tab;
175         }
176
177         if (cur_col) {
178                 last_known_column_buffer = buf;
179                 last_known_column = col;
180                 last_known_column_point = init_pos;
181                 last_known_column_modified = BUF_MODIFF(buf);
182         }
183
184         return col;
185 }
186
187 int string_column_at_point(Lisp_String * s, Bufpos init_pos, int tab_width)
188 {
189         int col;
190         int tab_seen;
191         int post_tab;
192         Bufpos pos = init_pos;
193         Emchar c;
194
195         if (tab_width <= 0 || tab_width > 1000)
196                 tab_width = 8;
197         col = tab_seen = post_tab = 0;
198
199         while (1) {
200                 if (pos <= 0)
201                         break;
202
203                 pos--;
204                 c = string_char(s, pos);
205                 if (c == '\t') {
206                         if (tab_seen)
207                                 col =
208                                     ((col + tab_width) / tab_width) * tab_width;
209
210                         post_tab += col;
211                         col = 0;
212                         tab_seen = 1;
213                 } else if (c == '\n') {
214                         break;
215                 } else {
216 #ifdef MULE
217                         Lisp_Object tmp = CHAR_CHARSET(c);
218                         col += XCHARSET_COLUMNS(tmp);
219 #else
220                         col++;
221 #endif  /* MULE */
222                 }
223         }
224
225         if (tab_seen) {
226                 col = ((col + tab_width) / tab_width) * tab_width;
227                 col += post_tab;
228         }
229
230         return col;
231 }
232
233 int current_column(struct buffer *buf)
234 {
235         if (buf == last_known_column_buffer
236             && BUF_PT(buf) == last_known_column_point
237             && BUF_MODIFF(buf) == last_known_column_modified)
238                 return last_known_column;
239
240         return column_at_point(buf, BUF_PT(buf), 1);
241 }
242
243 DEFUN("current-column", Fcurrent_column, 0, 1, 0,       /*
244 Return the horizontal position of point.  Beginning of line is column 0.
245 This is calculated by adding together the widths of all the displayed
246 representations of the character between the start of the previous line
247 and point. (e.g. control characters will have a width of 2 or 4, tabs
248 will have a variable width.)
249 Ignores finite width of frame, which means that this function may return
250 values greater than (frame-width).
251 Whether the line is visible (if `selective-display' is t) has no effect;
252 however, ^M is treated as end of line when `selective-display' is t.
253 If BUFFER is nil, the current buffer is assumed.
254 */
255       (buffer))
256 {
257         return make_int(current_column(decode_buffer(buffer, 0)));
258 }
259 \f
260 DEFUN("indent-to", Findent_to, 1, 3, "NIndent to column: ",     /*
261 Indent from point with tabs and spaces until COLUMN is reached.
262 Optional second argument MINIMUM says always do at least MINIMUM spaces
263 even if that goes past COLUMN; by default, MINIMUM is zero.
264 If BUFFER is nil, the current buffer is assumed.
265 */
266       (column, minimum, buffer))
267 {
268         /* This function can GC */
269         int mincol;
270         int fromcol;
271         struct buffer *buf = decode_buffer(buffer, 0);
272         int tab_width = XINT(buf->tab_width);
273         Bufpos opoint = 0;
274
275         CHECK_INT(column);
276         if (NILP(minimum))
277                 minimum = Qzero;
278         else
279                 CHECK_INT(minimum);
280
281         XSETBUFFER(buffer, buf);
282
283         fromcol = current_column(buf);
284         mincol = fromcol + XINT(minimum);
285         if (mincol < XINT(column))
286                 mincol = XINT(column);
287
288         if (fromcol == mincol)
289                 return make_int(mincol);
290
291         if (tab_width <= 0 || tab_width > 1000)
292                 tab_width = 8;
293
294         if (!NILP(Fextent_at(make_int(BUF_PT(buf)), buffer, Qinvisible,
295                              Qnil, Qnil))) {
296                 Bufpos last_visible = last_visible_position(BUF_PT(buf), buf);
297
298                 opoint = BUF_PT(buf);
299                 if (last_visible >= BUF_BEGV(buf))
300                         BUF_SET_PT(buf, last_visible);
301                 else
302                         error("Visible portion of buffer not modifiable");
303         }
304
305         if (indent_tabs_mode) {
306                 int n = mincol / tab_width - fromcol / tab_width;
307                 if (n != 0) {
308                         Finsert_char(make_char('\t'), make_int(n), Qnil,
309                                      buffer);
310
311                         fromcol = (mincol / tab_width) * tab_width;
312                 }
313         }
314
315         Finsert_char(make_char(' '), make_int(mincol - fromcol), Qnil, buffer);
316
317         last_known_column_buffer = buf;
318         last_known_column = mincol;
319         last_known_column_point = BUF_PT(buf);
320         last_known_column_modified = BUF_MODIFF(buf);
321
322         /* Not in FSF: */
323         if (opoint > 0)
324                 BUF_SET_PT(buf, opoint);
325
326         return make_int(mincol);
327 }
328
329 int bi_spaces_at_point(struct buffer *b, Bytind bi_pos)
330 {
331         Bytind bi_end = BI_BUF_ZV(b);
332         int col = 0;
333         Emchar c;
334         int tab_width = XINT(b->tab_width);
335
336         if (tab_width <= 0 || tab_width > 1000)
337                 tab_width = 8;
338
339         while (bi_pos < bi_end &&
340                (c = BI_BUF_FETCH_CHAR(b, bi_pos),
341                 (c == '\t' ? (col += tab_width - col % tab_width)
342                  : (c == ' ' ? ++col : 0))))
343                 INC_BYTIND(b, bi_pos);
344
345         return col;
346 }
347 \f
348 DEFUN("current-indentation", Fcurrent_indentation, 0, 1, 0,     /*
349 Return the indentation of the current line.
350 This is the horizontal position of the character
351 following any initial whitespace.
352 */
353       (buffer))
354 {
355         struct buffer *buf = decode_buffer(buffer, 0);
356         Bufpos pos = find_next_newline(buf, BUF_PT(buf), -1);
357
358         XSETBUFFER(buffer, buf);
359
360         if (!NILP(Fextent_at(make_int(pos), buffer, Qinvisible, Qnil, Qnil)))
361                 return Qzero;
362
363         return make_int(bi_spaces_at_point(buf, bufpos_to_bytind(buf, pos)));
364 }
365 \f
366 DEFUN("move-to-column", Fmove_to_column, 1, 3, 0,       /*
367 Move point to column COLUMN in the current line.
368 The column of a character is calculated by adding together the widths
369 as displayed of the previous characters in the line.
370 This function ignores line-continuation;
371 there is no upper limit on the column number a character can have
372 and horizontal scrolling has no effect.
373
374 If specified column is within a character, point goes after that character.
375 If it's past end of line, point goes to end of line.
376
377 A value of 'coerce for the second (optional) argument FORCE means if
378 COLUMN is in the middle of a tab character, change it to spaces.
379 Any other non-nil value means the same, plus if the line is too short to
380 reach column COLUMN, then add spaces/tabs to get there.
381
382 Returns the actual column that it moved to.
383 */
384       (column, force, buffer))
385 {
386         /* This function can GC */
387         Bufpos pos;
388         struct buffer *buf = decode_buffer(buffer, 0);
389         int col = current_column(buf);
390         int goal;
391         Bufpos end;
392         int tab_width = XINT(buf->tab_width);
393
394         int prev_col = 0;
395         Emchar c = 0;
396
397         XSETBUFFER(buffer, buf);
398         if (tab_width <= 0 || tab_width > 1000)
399                 tab_width = 8;
400         CHECK_NATNUM(column);
401         goal = XINT(column);
402
403       retry:
404         pos = BUF_PT(buf);
405         end = BUF_ZV(buf);
406
407         /* If we're starting past the desired column,
408            back up to beginning of line and scan from there.  */
409         if (col > goal) {
410                 pos = find_next_newline(buf, pos, -1);
411                 col = 0;
412         }
413
414         while (col < goal && pos < end) {
415                 c = BUF_FETCH_CHAR(buf, pos);
416                 if (c == '\n')
417                         break;
418                 if (c == '\r' && EQ(buf->selective_display, Qt))
419                         break;
420                 if (c == '\t') {
421                         prev_col = col;
422                         col += tab_width;
423                         col = col / tab_width * tab_width;
424                 } else {
425                         /* #### oh for the days of the complete new redisplay */
426                         /* #### FSFmacs looks at ctl_arrow, display tables.
427                            We need to do similar. */
428 #if 0
429                         displayed_glyphs = glyphs_from_bufpos(selected_frame(),
430                                                               buf,
431                                                               XWINDOW
432                                                               (Fselected_window
433                                                                (Qnil)), pos, dp,
434                                                               0, col, 0, 0, 0);
435                         col +=
436                             (displayed_glyphs->columns -
437                              (displayed_glyphs->begin_columns +
438                               displayed_glyphs->end_columns));
439 #else                           /* SXEmacs */
440 #ifdef MULE
441                         {
442                                 Lisp_Object tmp = CHAR_CHARSET(c);
443                                 col += XCHARSET_COLUMNS(tmp);
444                         }
445 #else
446                         col++;
447 #endif                          /* MULE */
448 #endif                          /* SXEmacs */
449                 }
450
451                 pos++;
452         }
453
454         BUF_SET_PT(buf, pos);
455
456         /* If a tab char made us overshoot, change it to spaces
457            and scan through it again.  */
458         if (!NILP(force) && col > goal && c == '\t' && prev_col < goal) {
459                 buffer_delete_range(buf, BUF_PT(buf) - 1, BUF_PT(buf), 0);
460                 Findent_to(make_int(col - 1), Qzero, buffer);
461                 buffer_insert_emacs_char(buf, ' ');
462                 goto retry;
463         }
464
465         /* If line ends prematurely, add space to the end.  */
466         if (col < goal && !NILP(force) && !EQ(force, Qcoerce)) {
467                 col = goal;
468                 Findent_to(make_int(col), Qzero, buffer);
469         }
470
471         last_known_column_buffer = buf;
472         last_known_column = col;
473         last_known_column_point = BUF_PT(buf);
474         last_known_column_modified = BUF_MODIFF(buf);
475
476         return make_int(col);
477 }
478
479 #if 0                           /* #### OK boys, this function needs to be present, I think.
480                                    It was there before the 19.12 redisplay rewrite. */
481
482 xxDEFUN("compute-motion", Fcompute_motion, 7, 7, 0,     /*
483 "Scan through the current buffer, calculating screen position.
484 Scan the current buffer forward from offset FROM,
485 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--
486 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--
487 and return the ending buffer position and screen location.
488
489 There are three additional arguments:
490
491 WIDTH is the number of columns available to display text;
492 this affects handling of continuation lines.
493 This is usually the value returned by `window-width', less one (to allow
494 for the continuation glyph).
495
496 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).
497 HSCROLL is the number of columns not being displayed at the left
498 margin; this is usually taken from a window's hscroll member.
499 TAB-OFFSET is the number of columns of the first tab that aren't
500 being displayed, perhaps because the line was continued within it.
501 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.
502
503 WINDOW is the window to operate on.  Currently this is used only to
504 find the display table.  It does not matter what buffer WINDOW displays;
505 `compute-motion' always operates on the current buffer.
506
507 The value is a list of five elements:
508 (POS HPOS VPOS PREVHPOS CONTIN)
509 POS is the buffer position where the scan stopped.
510 VPOS is the vertical position where the scan stopped.
511 HPOS is the horizontal position where the scan stopped.
512
513 PREVHPOS is the horizontal position one character back from POS.
514 CONTIN is t if a line was continued after (or within) the previous character.
515
516 For example, to find the buffer position of column COL of line LINE
517 of a certain window, pass the window's starting location as FROM
518 and the window's upper-left coordinates as FROMPOS.
519 Pass the buffer's (point-max) as TO, to limit the scan to the end of the
520 visible section of the buffer, and pass LINE and COL as TOPOS.
521                                                          */
522         (from, frompos, to, topos, width, offsets, window)) {
523         Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
524         struct position *pos;
525         int hscroll, tab_offset;
526         struct window *w = decode_window(window);
527
528         CHECK_INT_COERCE_MARKER(from);
529         CHECK_CONS(frompos);
530         CHECK_INT(XCAR(frompos));
531         CHECK_INT(XCDR(frompos));
532         CHECK_INT_COERCE_MARKER(to);
533         CHECK_CONS(topos);
534         CHECK_INT(XCAR(topos));
535         CHECK_INT(XCDR(topos));
536         CHECK_INT(width);
537         if (!NILP(offsets)) {
538                 CHECK_CONS(offsets);
539                 CHECK_INT(XCAR(offsets));
540                 CHECK_INT(XCDR(offsets));
541                 hscroll = XINT(XCAR(offsets));
542                 tab_offset = XINT(XCDR(offsets));
543         } else
544                 hscroll = tab_offset = 0;
545
546         pos = compute_motion(XINT(from), XINT(XCDR(frompos)),
547                              XINT(XCAR(frompos)),
548                              XINT(to), XINT(XCDR(topos)),
549                              XINT(XCAR(topos)),
550                              XINT(width), hscroll, tab_offset, w);
551
552         XSETINT(bufpos, pos->bufpos);
553         XSETINT(hpos, pos->hpos);
554         XSETINT(vpos, pos->vpos);
555         XSETINT(prevhpos, pos->prevhpos);
556
557         return list5(bufpos, hpos, vpos, prevhpos, pos->contin ? Qt : Qnil);
558 }
559
560 #endif                          /* 0 */
561
562 /* Helper for vmotion_1 - compute vertical pixel motion between
563    START and END in the line start cache CACHE.  This just sums
564    the line heights, including both the starting and ending lines.
565 */
566 static int vpix_motion(line_start_cache_dynarr * cache, int start, int end)
567 {
568         int i, vpix;
569
570         assert(start <= end);
571         assert(start >= 0);
572         assert(end < Dynarr_length(cache));
573
574         vpix = 0;
575         for (i = start; i <= end; i++)
576                 vpix += Dynarr_atp(cache, i)->height;
577
578         return vpix;
579 }
580
581 /*****************************************************************************
582  vmotion_1
583
584  Given a starting position ORIG, move point VTARGET lines in WINDOW.
585  Returns the new value for point.  If the arg ret_vpos is not nil, it is
586  taken to be a pointer to an int and the number of lines actually moved is
587  returned in it.  If the arg ret_vpix is not nil, it is taken to be a
588  pointer to an int and the vertical pixel height of the motion which
589  took place is returned in it.
590  ****************************************************************************/
591 static Bufpos
592 vmotion_1(struct window *w, Bufpos orig, int vtarget,
593           int *ret_vpos, int *ret_vpix)
594 {
595         struct buffer *b = XBUFFER(w->buffer);
596         int elt;
597
598         elt = point_in_line_start_cache(w, orig, (vtarget < 0
599                                                   ? -vtarget : vtarget));
600
601         /* #### This assertion must be true before the if statements are hit
602            but may possibly be wrong after the call to
603            point_in_line_start_cache if orig is outside of the visible
604            region of the buffer.  Handle this. */
605         assert(elt >= 0);
606
607         /* Moving downward. */
608         if (vtarget > 0) {
609                 int cur_line = Dynarr_length(w->line_start_cache) - 1 - elt;
610                 Bufpos ret_pt;
611
612                 if (cur_line > vtarget)
613                         cur_line = vtarget;
614
615                 /* The traditional FSF behavior is to return the end of buffer
616                    position if we couldn't move far enough because we hit it.  */
617                 if (cur_line < vtarget)
618                         ret_pt = BUF_ZV(b);
619                 else
620                         ret_pt =
621                             Dynarr_atp(w->line_start_cache,
622                                        cur_line + elt)->start;
623
624                 while (ret_pt > BUF_ZV(b) && cur_line > 0) {
625                         cur_line--;
626                         ret_pt =
627                             Dynarr_atp(w->line_start_cache,
628                                        cur_line + elt)->start;
629                 }
630
631                 if (ret_vpos)
632                         *ret_vpos = cur_line;
633                 if (ret_vpix)
634                         *ret_vpix =
635                             vpix_motion(w->line_start_cache, elt,
636                                         cur_line + elt);
637                 return ret_pt;
638         } else if (vtarget < 0) {
639                 if (elt < -vtarget) {
640                         if (ret_vpos)
641                                 *ret_vpos = -elt;
642                         if (ret_vpix)
643                                 *ret_vpix =
644                                     vpix_motion(w->line_start_cache, 0, elt);
645                         /* #### This should be BUF_BEGV (b), right? */
646                         return Dynarr_atp(w->line_start_cache, 0)->start;
647                 } else {
648                         if (ret_vpos)
649                                 *ret_vpos = vtarget;
650                         if (ret_vpix)
651                                 *ret_vpix =
652                                     vpix_motion(w->line_start_cache,
653                                                 elt + vtarget, elt);
654                         return Dynarr_atp(w->line_start_cache,
655                                           elt + vtarget)->start;
656                 }
657         } else {
658                 /* No vertical motion requested so we just return the position
659                    of the beginning of the current line. */
660                 if (ret_vpos)
661                         *ret_vpos = 0;
662                 if (ret_vpix)
663                         *ret_vpix = vpix_motion(w->line_start_cache, elt, elt);
664
665                 return Dynarr_atp(w->line_start_cache, elt)->start;
666         }
667
668         RETURN_NOT_REACHED(0)   /* shut up compiler */
669 }
670
671 /*****************************************************************************
672  vmotion
673
674  Given a starting position ORIG, move point VTARGET lines in WINDOW.
675  Returns the new value for point.  If the arg ret_vpos is not nil, it is
676  taken to be a pointer to an int and the number of lines actually moved is
677  returned in it.
678  ****************************************************************************/
679 Bufpos vmotion(struct window * w, Bufpos orig, int vtarget, int *ret_vpos)
680 {
681         return vmotion_1(w, orig, vtarget, ret_vpos, NULL);
682 }
683
684 /* Helper for Fvertical_motion.
685  */
686 static
687 Lisp_Object vertical_motion_1(Lisp_Object lines, Lisp_Object window, int pixels)
688 {
689         Bufpos bufpos;
690         Bufpos orig;
691         int selected;
692         int *vpos, *vpix;
693         int value = 0;
694         struct window *w;
695
696         if (NILP(window))
697                 window = Fselected_window(Qnil);
698
699         CHECK_LIVE_WINDOW(window);
700         CHECK_INT(lines);
701
702         selected = (EQ(window, Fselected_window(Qnil)));
703
704         w = XWINDOW(window);
705
706         orig = selected ? BUF_PT(XBUFFER(w->buffer))
707             : marker_position(w->pointm[CURRENT_DISP]);
708
709         vpos = pixels ? NULL : &value;
710         vpix = pixels ? &value : NULL;
711
712         bufpos = vmotion_1(w, orig, XINT(lines), vpos, vpix);
713
714         /* Note that the buffer's point is set, not the window's point. */
715         if (selected)
716                 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
717         else
718                 set_marker_restricted(w->pointm[CURRENT_DISP],
719                                       make_int(bufpos), w->buffer);
720
721         return make_int(value);
722 }
723
724 DEFUN("vertical-motion", Fvertical_motion, 1, 3, 0,     /*
725 Move to start of frame line LINES lines down.
726 If LINES is negative, this is moving up.
727 Optional second argument is WINDOW to move in,
728 the default is the selected window.
729
730 Sets point to position found; this may be start of line
731 or just the start of a continuation line.
732 If optional third argument PIXELS is nil, returns number
733 of lines moved; may be closer to zero than LINES if beginning
734 or end of buffer was reached.  If PIXELS is non-nil, the
735 vertical pixel height of the motion which took place is
736 returned instead of the actual number of lines moved.  A
737 motion of zero lines returns the height of the current line.
738
739 Note that `vertical-motion' sets WINDOW's buffer's point, not
740 WINDOW's point. (This differs from FSF Emacs, which buggily always
741 sets current buffer's point, regardless of WINDOW.)
742 */
743       (lines, window, pixels))
744 {
745         return vertical_motion_1(lines, window, !NILP(pixels));
746 }
747
748 /*
749  * Like vmotion() but requested and returned movement is in pixels.
750  * HOW specifies the stopping condition.  Positive means move at least
751  * PIXELS.  Negative means at most.  Zero means as close as possible.
752  */
753 Bufpos
754 vmotion_pixels(Lisp_Object window, Bufpos start, int pixels, int how,
755                int *motion)
756 {
757         struct window *w;
758         Bufpos eobuf, bobuf;
759         int defheight;
760         int needed;
761         int line, next;
762         int remain, abspix, dirn;
763         int elt, nelt;
764         int i;
765         line_start_cache_dynarr *cache;
766         int previous = -1;
767         int lines;
768
769         if (NILP(window))
770                 window = Fselected_window(Qnil);
771
772         CHECK_LIVE_WINDOW(window);
773         w = XWINDOW(window);
774
775         eobuf = BUF_ZV(XBUFFER(w->buffer));
776         bobuf = BUF_BEGV(XBUFFER(w->buffer));
777
778         default_face_height_and_width(window, &defheight, NULL);
779
780         /* guess num lines needed in line start cache + a few extra */
781         abspix = abs(pixels);
782         needed = (abspix + defheight - 1) / defheight + 3;
783
784         dirn = (pixels >= 0) ? 1 : -1;
785
786         while (1) {
787                 elt = point_in_line_start_cache(w, start, needed);
788                 assert(elt >= 0);       /* in the cache */
789
790                 cache = w->line_start_cache;
791                 nelt = Dynarr_length(cache);
792
793                 *motion = 0;
794
795                 if (pixels == 0)
796                         /* No vertical motion requested so we just return the position
797                            of the beginning of the current display line. */
798                         return Dynarr_atp(cache, elt)->start;
799
800                 if ((dirn < 0 && elt == 0 &&
801                      Dynarr_atp(cache, elt)->start <= bobuf) ||
802                     (dirn > 0 && elt == nelt - 1 &&
803                      Dynarr_atp(cache, elt)->end >= eobuf))
804                         return Dynarr_atp(cache, elt)->start;
805
806                 remain = abspix;
807                 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn) {
808                         /* cache line we're considering moving over */
809                         int ii = (dirn > 0) ? i : i - 1;
810
811                         if (remain < 0)
812                                 return Dynarr_atp(cache, i)->start;
813
814                         line = Dynarr_atp(cache, ii)->height;
815                         next = remain - line;
816
817                         /* is stopping condition satisfied? */
818                         if ((how > 0 && remain <= 0) || /* at least */
819                             (how < 0 && next < 0) ||    /* at most */
820                             (how == 0 && remain <= abs(next)))  /* closest */
821                                 return Dynarr_atp(cache, i)->start;
822
823                         /* moving down and nowhere left to go? */
824                         if (dirn > 0 && Dynarr_atp(cache, ii)->end >= eobuf)
825                                 return Dynarr_atp(cache, ii)->start;
826
827                         /* take the step */
828                         remain = next;
829                         *motion += dirn * line;
830
831                         /* moving up and nowhere left to go? */
832                         if (dirn < 0 && Dynarr_atp(cache, ii)->start <= bobuf)
833                                 return Dynarr_atp(cache, ii)->start;
834                 }
835
836                 /* get here => need more cache lines.  try again. */
837                 assert(abs(*motion) > previous);        /* progress? */
838                 previous = abs(*motion);
839
840                 lines = (pixels < 0) ? elt : (nelt - elt);
841                 needed += (remain * lines + abspix - 1) / abspix + 3;
842         }
843
844         RETURN_NOT_REACHED(0)   /* shut up compiler */
845 }
846
847 DEFUN("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0,       /*
848 Move to start of frame line PIXELS vertical pixels down.
849 If PIXELS is negative, this is moving up.
850 The actual vertical motion in pixels is returned.
851
852 Optional second argument is WINDOW to move in,
853 the default is the selected window.
854
855 Optional third argument HOW specifies when to stop.  A value
856 less than zero indicates that the motion should be no more
857 than PIXELS.  A value greater than zero indicates that the
858 motion should be at least PIXELS.  Any other value indicates
859 that the motion should be as close as possible to PIXELS.
860 */
861       (pixels, window, how))
862 {
863         Bufpos bufpos;
864         Bufpos orig;
865         int selected;
866         int motion;
867         int howto;
868         struct window *w;
869
870         if (NILP(window))
871                 window = Fselected_window(Qnil);
872
873         CHECK_LIVE_WINDOW(window);
874         CHECK_INT(pixels);
875
876         selected = (EQ(window, Fselected_window(Qnil)));
877
878         w = XWINDOW(window);
879
880         orig = selected ? BUF_PT(XBUFFER(w->buffer))
881             : marker_position(w->pointm[CURRENT_DISP]);
882
883         howto = INTP(how) ? XINT(how) : 0;
884
885         bufpos = vmotion_pixels(window, orig, XINT(pixels), howto, &motion);
886
887         if (selected)
888                 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
889         else
890                 set_marker_restricted(w->pointm[CURRENT_DISP],
891                                       make_int(bufpos), w->buffer);
892
893         return make_int(motion);
894 }
895 \f
896 void syms_of_indent(void)
897 {
898         DEFSUBR(Fcurrent_indentation);
899         DEFSUBR(Findent_to);
900         DEFSUBR(Fcurrent_column);
901         DEFSUBR(Fmove_to_column);
902 #if 0                           /* #### */
903         DEFSUBR(Fcompute_motion);
904 #endif
905         DEFSUBR(Fvertical_motion);
906         DEFSUBR(Fvertical_motion_pixels);
907
908         defsymbol(&Qcoerce, "coerce");
909 }
910
911 void vars_of_indent(void)
912 {
913         DEFVAR_BOOL("indent-tabs-mode", &indent_tabs_mode       /*
914 *Indentation can insert tabs if this is non-nil.
915 Setting this variable automatically makes it local to the current buffer.
916                                                                  */ );
917         indent_tabs_mode = 1;
918 }