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