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.
6 This file is part of XEmacs.
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
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
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. */
23 /* This file has been Mule-ized. */
25 /* Synched up with: 19.30. Diverges significantly from FSF. */
38 #ifdef REGION_CACHE_NEEDS_WORK
39 #include "region-cache.h"
43 /* Indentation can insert tabs if this is non-zero;
44 otherwise always uses spaces */
47 /* Avoid recalculation by remembering things in these variables. */
49 /* Last value returned by current_column.
51 Some things set last_known_column_point to -1
52 to mark the memoized value as invalid */
53 static int last_known_column;
55 /* Last buffer searched by current_column */
56 static struct buffer *last_known_column_buffer;
58 /* Value of point when current_column was called */
59 static Bufpos last_known_column_point;
61 /* Value of MODIFF when current_column was called */
62 static int last_known_column_modified;
65 last_visible_position (Bufpos pos, struct buffer *buf)
70 XSETBUFFER (buffer, buf);
71 value = Fprevious_single_property_change (make_int (pos), Qinvisible,
74 return 0; /* no visible position found */
76 /* #### bug bug bug!!! This will return the position of the beginning
77 of an invisible extent; this extent is very likely to be start-closed,
78 and thus the spaces inserted in `indent-to' will go inside the
81 Not sure what the correct solution is here. Rethink indent-to? */
85 #ifdef REGION_CACHE_NEEDS_WORK
87 /* Allocate or free the width run cache, as requested by the current
88 state of current_buffer's cache_long_line_scans variable. */
90 width_run_cache_on_off (struct buffer *buf)
92 if (NILP (buf->cache_long_line_scans))
94 /* It should be off. */
95 if (buf->width_run_cache)
97 free_region_cache (buf->width_run_cache);
98 buf->width_run_cache = 0;
99 buf->width_table = Qnil;
104 /* It should be on. */
105 if (buf->width_run_cache == 0)
107 buf->width_run_cache = new_region_cache ();
108 recompute_width_table (buf, buffer_display_table ());
113 #endif /* REGION_CACHE_NEEDS_WORK */
116 /* Cancel any recorded value of the horizontal position. */
119 invalidate_current_column (void)
121 last_known_column_point = -1;
125 column_at_point (struct buffer *buf, Bufpos init_pos, int cur_col)
129 int tab_width = XINT (buf->tab_width);
131 Bufpos pos = init_pos;
134 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
135 col = tab_seen = post_tab = 0;
139 if (pos <= BUF_BEGV (buf))
143 c = BUF_FETCH_CHAR (buf, pos);
147 col = ((col + tab_width) / tab_width) * tab_width;
153 else if (c == '\n' ||
154 (EQ (buf->selective_display, Qt) && c == '\r'))
158 /* #### This needs updating to handle the new redisplay. */
159 /* #### FSFmacs looks at ctl_arrow, display tables.
160 We need to do similar. */
162 displayed_glyphs = glyphs_from_bufpos (sel_frame, buf,
163 XWINDOW (selected_window),
164 pos, dp, 0, col, 0, 0, 0);
165 col += (displayed_glyphs->columns
166 - (displayed_glyphs->begin_columns
167 + displayed_glyphs->end_columns));
170 col += XCHARSET_COLUMNS (CHAR_CHARSET (c));
180 col = ((col + tab_width) / tab_width) * tab_width;
186 last_known_column_buffer = buf;
187 last_known_column = col;
188 last_known_column_point = init_pos;
189 last_known_column_modified = BUF_MODIFF (buf);
196 current_column (struct buffer *buf)
198 if (buf == last_known_column_buffer
199 && BUF_PT (buf) == last_known_column_point
200 && BUF_MODIFF (buf) == last_known_column_modified)
201 return last_known_column;
203 return column_at_point (buf, BUF_PT (buf), 1);
206 DEFUN ("current-column", Fcurrent_column, 0, 1, 0, /*
207 Return the horizontal position of point. Beginning of line is column 0.
208 This is calculated by adding together the widths of all the displayed
209 representations of the character between the start of the previous line
210 and point. (e.g. control characters will have a width of 2 or 4, tabs
211 will have a variable width.)
212 Ignores finite width of frame, which means that this function may return
213 values greater than (frame-width).
214 Whether the line is visible (if `selective-display' is t) has no effect;
215 however, ^M is treated as end of line when `selective-display' is t.
216 If BUFFER is nil, the current buffer is assumed.
220 return make_int (current_column (decode_buffer (buffer, 0)));
224 DEFUN ("indent-to", Findent_to, 1, 3, "NIndent to column: ", /*
225 Indent from point with tabs and spaces until COLUMN is reached.
226 Optional second argument MIN says always do at least MIN spaces
227 even if that goes past COLUMN; by default, MIN is zero.
228 If BUFFER is nil, the current buffer is assumed.
230 (col, minimum, buffer))
232 /* This function can GC */
235 struct buffer *buf = decode_buffer (buffer, 0);
236 int tab_width = XINT (buf->tab_width);
245 XSETBUFFER (buffer, buf);
247 fromcol = current_column (buf);
248 mincol = fromcol + XINT (minimum);
249 if (mincol < XINT (col)) mincol = XINT (col);
251 if (fromcol == mincol)
252 return make_int (mincol);
254 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
256 if (!NILP (Fextent_at (make_int (BUF_PT (buf)), buffer, Qinvisible,
259 Bufpos last_visible = last_visible_position (BUF_PT (buf), buf);
261 opoint = BUF_PT (buf);
262 if (last_visible >= BUF_BEGV (buf))
263 BUF_SET_PT (buf, last_visible);
265 error ("Visible portion of buffer not modifiable");
268 if (indent_tabs_mode)
270 int n = mincol / tab_width - fromcol / tab_width;
273 Finsert_char (make_char ('\t'), make_int (n), Qnil, buffer);
275 fromcol = (mincol / tab_width) * tab_width;
279 Finsert_char (make_char (' '), make_int (mincol - fromcol), Qnil, buffer);
281 last_known_column_buffer = buf;
282 last_known_column = mincol;
283 last_known_column_point = BUF_PT (buf);
284 last_known_column_modified = BUF_MODIFF (buf);
288 BUF_SET_PT (buf, opoint);
290 return make_int (mincol);
294 bi_spaces_at_point (struct buffer *b, Bytind bi_pos)
296 Bytind bi_end = BI_BUF_ZV (b);
299 int tab_width = XINT (b->tab_width);
301 if (tab_width <= 0 || tab_width > 1000)
304 while (bi_pos < bi_end &&
305 (c = BI_BUF_FETCH_CHAR (b, bi_pos),
307 ? (col += tab_width - col % tab_width)
308 : (c == ' ' ? ++col : 0))))
309 INC_BYTIND (b, bi_pos);
315 DEFUN ("current-indentation", Fcurrent_indentation, 0, 1, 0, /*
316 Return the indentation of the current line.
317 This is the horizontal position of the character
318 following any initial whitespace.
322 struct buffer *buf = decode_buffer (buffer, 0);
323 Bufpos pos = find_next_newline (buf, BUF_PT (buf), -1);
325 XSETBUFFER (buffer, buf);
327 if (!NILP (Fextent_at (make_int (pos), buffer, Qinvisible, Qnil, Qnil)))
330 return make_int (bi_spaces_at_point (buf, bufpos_to_bytind (buf, pos)));
334 DEFUN ("move-to-column", Fmove_to_column, 1, 3, 0, /*
335 Move point to column COLUMN in the current line.
336 The column of a character is calculated by adding together the widths
337 as displayed of the previous characters in the line.
338 This function ignores line-continuation;
339 there is no upper limit on the column number a character can have
340 and horizontal scrolling has no effect.
342 If specified column is within a character, point goes after that character.
343 If it's past end of line, point goes to end of line.
345 A non-nil second (optional) argument FORCE means, if the line
346 is too short to reach column COLUMN then add spaces/tabs to get there,
347 and if COLUMN is in the middle of a tab character, change it to spaces.
348 Returns the actual column that it moved to.
350 (column, force, buffer))
352 /* This function can GC */
354 struct buffer *buf = decode_buffer (buffer, 0);
355 int col = current_column (buf);
358 int tab_width = XINT (buf->tab_width);
363 XSETBUFFER (buffer, buf);
364 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
365 CHECK_NATNUM (column);
366 goal = XINT (column);
372 /* If we're starting past the desired column,
373 back up to beginning of line and scan from there. */
376 pos = find_next_newline (buf, pos, -1);
380 while (col < goal && pos < end)
382 c = BUF_FETCH_CHAR (buf, pos);
385 if (c == '\r' && EQ (buf->selective_display, Qt))
391 col = col / tab_width * tab_width;
395 /* #### oh for the days of the complete new redisplay */
396 /* #### FSFmacs looks at ctl_arrow, display tables.
397 We need to do similar. */
399 displayed_glyphs = glyphs_from_bufpos (selected_frame (),
401 XWINDOW (Fselected_window (Qnil)),
402 pos, dp, 0, col, 0, 0, 0);
403 col += (displayed_glyphs->columns
404 - (displayed_glyphs->begin_columns
405 + displayed_glyphs->end_columns));
408 col += XCHARSET_COLUMNS (CHAR_CHARSET (c));
418 BUF_SET_PT (buf, pos);
420 /* If a tab char made us overshoot, change it to spaces
421 and scan through it again. */
422 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
424 buffer_delete_range (buf, BUF_PT (buf) - 1, BUF_PT (buf), 0);
425 Findent_to (make_int (col - 1), Qzero, buffer);
426 buffer_insert_emacs_char (buf, ' ');
430 /* If line ends prematurely, add space to the end. */
431 if (col < goal && !NILP (force))
434 Findent_to (make_int (col), Qzero, buffer);
437 last_known_column_buffer = buf;
438 last_known_column = col;
439 last_known_column_point = BUF_PT (buf);
440 last_known_column_modified = BUF_MODIFF (buf);
442 return make_int (col);
445 #if 0 /* #### OK boys, this function needs to be present, I think.
446 It was there before the 19.12 redisplay rewrite. */
448 xxDEFUN ("compute-motion", Fcompute_motion, 7, 7, 0, /*
449 "Scan through the current buffer, calculating screen position.
450 Scan the current buffer forward from offset FROM,
451 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--
452 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--
453 and return the ending buffer position and screen location.
455 There are three additional arguments:
457 WIDTH is the number of columns available to display text;
458 this affects handling of continuation lines.
459 This is usually the value returned by `window-width', less one (to allow
460 for the continuation glyph).
462 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).
463 HSCROLL is the number of columns not being displayed at the left
464 margin; this is usually taken from a window's hscroll member.
465 TAB-OFFSET is the number of columns of the first tab that aren't
466 being displayed, perhaps because the line was continued within it.
467 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.
469 WINDOW is the window to operate on. Currently this is used only to
470 find the display table. It does not matter what buffer WINDOW displays;
471 `compute-motion' always operates on the current buffer.
473 The value is a list of five elements:
474 (POS HPOS VPOS PREVHPOS CONTIN)
475 POS is the buffer position where the scan stopped.
476 VPOS is the vertical position where the scan stopped.
477 HPOS is the horizontal position where the scan stopped.
479 PREVHPOS is the horizontal position one character back from POS.
480 CONTIN is t if a line was continued after (or within) the previous character.
482 For example, to find the buffer position of column COL of line LINE
483 of a certain window, pass the window's starting location as FROM
484 and the window's upper-left coordinates as FROMPOS.
485 Pass the buffer's (point-max) as TO, to limit the scan to the end of the
486 visible section of the buffer, and pass LINE and COL as TOPOS.
488 (from, frompos, to, topos, width, offsets, window))
490 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
491 struct position *pos;
492 int hscroll, tab_offset;
493 struct window *w = decode_window (window);
495 CHECK_INT_COERCE_MARKER (from);
496 CHECK_CONS (frompos);
497 CHECK_INT (XCAR (frompos));
498 CHECK_INT (XCDR (frompos));
499 CHECK_INT_COERCE_MARKER (to);
501 CHECK_INT (XCAR (topos));
502 CHECK_INT (XCDR (topos));
506 CHECK_CONS (offsets);
507 CHECK_INT (XCAR (offsets));
508 CHECK_INT (XCDR (offsets));
509 hscroll = XINT (XCAR (offsets));
510 tab_offset = XINT (XCDR (offsets));
513 hscroll = tab_offset = 0;
515 pos = compute_motion (XINT (from), XINT (XCDR (frompos)),
516 XINT (XCAR (frompos)),
517 XINT (to), XINT (XCDR (topos)),
519 XINT (width), hscroll, tab_offset, w);
521 XSETINT (bufpos, pos->bufpos);
522 XSETINT (hpos, pos->hpos);
523 XSETINT (vpos, pos->vpos);
524 XSETINT (prevhpos, pos->prevhpos);
526 return list5 (bufpos, hpos, vpos, prevhpos,
527 pos->contin ? Qt : Qnil);
532 /* Helper for vmotion_1 - compute vertical pixel motion between
533 START and END in the line start cache CACHE. This just sums
534 the line heights, including both the starting and ending lines.
537 vpix_motion (line_start_cache_dynarr *cache, int start, int end)
541 assert (start <= end);
543 assert (end < Dynarr_length (cache));
546 for (i = start; i <= end; i++)
547 vpix += Dynarr_atp (cache, i)->height;
552 /*****************************************************************************
555 Given a starting position ORIG, move point VTARGET lines in WINDOW.
556 Returns the new value for point. If the arg ret_vpos is not nil, it is
557 taken to be a pointer to an int and the number of lines actually moved is
558 returned in it. If the arg ret_vpix is not nil, it is taken to be a
559 pointer to an int and the vertical pixel height of the motion which
560 took place is returned in it.
561 ****************************************************************************/
563 vmotion_1 (struct window *w, Bufpos orig, int vtarget,
564 int *ret_vpos, int *ret_vpix)
566 struct buffer *b = XBUFFER (w->buffer);
569 elt = point_in_line_start_cache (w, orig, (vtarget < 0
573 /* #### This assertion must be true before the if statements are hit
574 but may possibly be wrong after the call to
575 point_in_line_start_cache if orig is outside of the visible
576 region of the buffer. Handle this. */
579 /* Moving downward. */
582 int cur_line = Dynarr_length (w->line_start_cache) - 1 - elt;
585 if (cur_line > vtarget)
588 /* The traditional FSF behavior is to return the end of buffer
589 position if we couldn't move far enough because we hit it. */
590 if (cur_line < vtarget)
593 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
595 while (ret_pt > BUF_ZV (b) && cur_line > 0)
598 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
601 if (ret_vpos) *ret_vpos = cur_line;
603 *ret_vpix = vpix_motion (w->line_start_cache, elt, cur_line + elt);
606 else if (vtarget < 0)
610 if (ret_vpos) *ret_vpos = -elt;
612 *ret_vpix = vpix_motion (w->line_start_cache, 0, elt);
613 /* #### This should be BUF_BEGV (b), right? */
614 return Dynarr_atp (w->line_start_cache, 0)->start;
618 if (ret_vpos) *ret_vpos = vtarget;
620 *ret_vpix = vpix_motion (w->line_start_cache, elt + vtarget, elt);
621 return Dynarr_atp (w->line_start_cache, elt + vtarget)->start;
626 /* No vertical motion requested so we just return the position
627 of the beginning of the current line. */
628 if (ret_vpos) *ret_vpos = 0;
630 *ret_vpix = vpix_motion (w->line_start_cache, elt, elt);
632 return Dynarr_atp (w->line_start_cache, elt)->start;
635 RETURN_NOT_REACHED(0) /* shut up compiler */
638 /*****************************************************************************
641 Given a starting position ORIG, move point VTARGET lines in WINDOW.
642 Returns the new value for point. If the arg ret_vpos is not nil, it is
643 taken to be a pointer to an int and the number of lines actually moved is
645 ****************************************************************************/
647 vmotion (struct window *w, Bufpos orig, int vtarget, int *ret_vpos)
649 return vmotion_1 (w, orig, vtarget, ret_vpos, NULL);
652 /* Helper for Fvertical_motion.
655 Lisp_Object vertical_motion_1 (Lisp_Object lines, Lisp_Object window,
666 window = Fselected_window (Qnil);
668 CHECK_LIVE_WINDOW (window);
671 selected = (EQ (window, Fselected_window (Qnil)));
673 w = XWINDOW (window);
675 orig = selected ? BUF_PT (XBUFFER (w->buffer))
676 : marker_position (w->pointm[CURRENT_DISP]);
678 vpos = pixels ? NULL : &value;
679 vpix = pixels ? &value : NULL;
681 bufpos = vmotion_1 (w, orig, XINT (lines), vpos, vpix);
683 /* Note that the buffer's point is set, not the window's point. */
685 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
687 set_marker_restricted (w->pointm[CURRENT_DISP],
691 return make_int (value);
694 DEFUN ("vertical-motion", Fvertical_motion, 1, 3, 0, /*
695 Move to start of frame line LINES lines down.
696 If LINES is negative, this is moving up.
697 Optional second argument is WINDOW to move in,
698 the default is the selected window.
700 Sets point to position found; this may be start of line
701 or just the start of a continuation line.
702 If optional third argument PIXELS is nil, returns number
703 of lines moved; may be closer to zero than LINES if beginning
704 or end of buffer was reached. If PIXELS is non-nil, the
705 vertical pixel height of the motion which took place is
706 returned instead of the actual number of lines moved. A
707 motion of zero lines returns the height of the current line.
709 Note that `vertical-motion' sets WINDOW's buffer's point, not
710 WINDOW's point. (This differs from FSF Emacs, which buggily always
711 sets current buffer's point, regardless of WINDOW.)
713 (lines, window, pixels))
715 return vertical_motion_1 (lines, window, !NILP (pixels));
719 * Like vmotion() but requested and returned movement is in pixels.
720 * HOW specifies the stopping condition. Positive means move at least
721 * PIXELS. Negative means at most. Zero means as close as possible.
724 vmotion_pixels (Lisp_Object window, Bufpos start, int pixels, int how,
732 int remain, abspix, dirn;
735 line_start_cache_dynarr *cache;
740 window = Fselected_window (Qnil);
742 CHECK_LIVE_WINDOW (window);
743 w = XWINDOW (window);
745 eobuf = BUF_ZV (XBUFFER (w->buffer));
746 bobuf = BUF_BEGV (XBUFFER (w->buffer));
748 default_face_height_and_width (window, &defheight, NULL);
750 /* guess num lines needed in line start cache + a few extra */
751 abspix = abs (pixels);
752 needed = (abspix + defheight-1)/defheight + 3;
754 dirn = (pixels >= 0) ? 1 : -1;
758 elt = point_in_line_start_cache (w, start, needed);
759 assert (elt >= 0); /* in the cache */
761 cache = w->line_start_cache;
762 nelt = Dynarr_length (cache);
767 /* No vertical motion requested so we just return the position
768 of the beginning of the current display line. */
769 return Dynarr_atp (cache, elt)->start;
771 if ((dirn < 0 && elt == 0 &&
772 Dynarr_atp (cache, elt)->start <= bobuf) ||
773 (dirn > 0 && elt == nelt-1 &&
774 Dynarr_atp (cache, elt)->end >= eobuf))
775 return Dynarr_atp (cache, elt)->start;
778 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn)
780 /* cache line we're considering moving over */
781 int ii = (dirn > 0) ? i : i-1;
784 return Dynarr_atp (cache, i)->start;
786 line = Dynarr_atp (cache, ii)->height;
787 next = remain - line;
789 /* is stopping condition satisfied? */
790 if ((how > 0 && remain <= 0) || /* at least */
791 (how < 0 && next < 0) || /* at most */
792 (how == 0 && remain <= abs (next))) /* closest */
793 return Dynarr_atp (cache, i)->start;
795 /* moving down and nowhere left to go? */
796 if (dirn > 0 && Dynarr_atp (cache, ii)->end >= eobuf)
797 return Dynarr_atp (cache, ii)->start;
801 *motion += dirn * line;
803 /* moving up and nowhere left to go? */
804 if (dirn < 0 && Dynarr_atp (cache, ii)->start <= bobuf)
805 return Dynarr_atp (cache, ii)->start;
808 /* get here => need more cache lines. try again. */
809 assert (abs (*motion) > previous); /* progress? */
810 previous = abs (*motion);
812 lines = (pixels < 0) ? elt : (nelt - elt);
813 needed += (remain*lines + abspix-1)/abspix + 3;
816 RETURN_NOT_REACHED(0) /* shut up compiler */
819 DEFUN ("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /*
820 Move to start of frame line PIXELS vertical pixels down.
821 If PIXELS is negative, this is moving up.
822 The actual vertical motion in pixels is returned.
824 Optional second argument is WINDOW to move in,
825 the default is the selected window.
827 Optional third argument HOW specifies when to stop. A value
828 less than zero indicates that the motion should be no more
829 than PIXELS. A value greater than zero indicates that the
830 motion should be at least PIXELS. Any other value indicates
831 that the motion should be as close as possible to PIXELS.
833 (pixels, window, how))
843 window = Fselected_window (Qnil);
845 CHECK_LIVE_WINDOW (window);
848 selected = (EQ (window, Fselected_window (Qnil)));
850 w = XWINDOW (window);
852 orig = selected ? BUF_PT (XBUFFER (w->buffer))
853 : marker_position (w->pointm[CURRENT_DISP]);
855 howto = INTP (how) ? XINT (how) : 0;
857 bufpos = vmotion_pixels (window, orig, XINT (pixels), howto, &motion);
860 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
862 set_marker_restricted (w->pointm[CURRENT_DISP],
866 return make_int (motion);
871 syms_of_indent (void)
873 DEFSUBR (Fcurrent_indentation);
874 DEFSUBR (Findent_to);
875 DEFSUBR (Fcurrent_column);
876 DEFSUBR (Fmove_to_column);
878 DEFSUBR (Fcompute_motion);
880 DEFSUBR (Fvertical_motion);
881 DEFSUBR (Fvertical_motion_pixels);
885 vars_of_indent (void)
887 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode /*
888 *Indentation can insert tabs if this is non-nil.
889 Setting this variable automatically makes it local to the current buffer.
891 indent_tabs_mode = 1;