comparison src/indent.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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
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 (struct 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
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
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
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
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 }