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"
45 /* Indentation can insert tabs if this is non-zero;
46 otherwise always uses spaces */
49 /* Avoid recalculation by remembering things in these variables. */
51 /* Last value returned by current_column.
53 Some things set last_known_column_point to -1
54 to mark the memoized value as invalid */
55 static int last_known_column;
57 /* Last buffer searched by current_column */
58 static struct buffer *last_known_column_buffer;
60 /* Value of point when current_column was called */
61 static Bufpos last_known_column_point;
63 /* Value of MODIFF when current_column was called */
64 static int last_known_column_modified;
67 last_visible_position (Bufpos pos, struct buffer *buf)
72 XSETBUFFER (buffer, buf);
73 value = Fprevious_single_property_change (make_int (pos), Qinvisible,
76 return 0; /* no visible position found */
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
83 Not sure what the correct solution is here. Rethink indent-to? */
87 #ifdef REGION_CACHE_NEEDS_WORK
89 /* Allocate or free the width run cache, as requested by the current
90 state of current_buffer's cache_long_line_scans variable. */
92 width_run_cache_on_off (struct buffer *buf)
94 if (NILP (buf->cache_long_line_scans))
96 /* It should be off. */
97 if (buf->width_run_cache)
99 free_region_cache (buf->width_run_cache);
100 buf->width_run_cache = 0;
101 buf->width_table = Qnil;
106 /* It should be on. */
107 if (buf->width_run_cache == 0)
109 buf->width_run_cache = new_region_cache ();
110 recompute_width_table (buf, buffer_display_table ());
115 #endif /* REGION_CACHE_NEEDS_WORK */
118 /* Cancel any recorded value of the horizontal position. */
121 invalidate_current_column (void)
123 last_known_column_point = -1;
127 column_at_point (struct buffer *buf, Bufpos init_pos, int cur_col)
131 int tab_width = XINT (buf->tab_width);
133 Bufpos pos = init_pos;
136 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
137 col = tab_seen = post_tab = 0;
141 if (pos <= BUF_BEGV (buf))
145 c = BUF_FETCH_CHAR (buf, pos);
149 col = ((col + tab_width) / tab_width) * tab_width;
155 else if (c == '\n' ||
156 (EQ (buf->selective_display, Qt) && c == '\r'))
160 /* #### This needs updating to handle the new redisplay. */
161 /* #### FSFmacs looks at ctl_arrow, display tables.
162 We need to do similar. */
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));
172 col += CHAR_COLUMNS (c);
182 col = ((col + tab_width) / tab_width) * tab_width;
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);
198 string_column_at_point (Lisp_String* s, Bufpos init_pos, int tab_width)
203 Bufpos pos = init_pos;
206 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
207 col = tab_seen = post_tab = 0;
215 c = string_char (s, pos);
219 col = ((col + tab_width) / tab_width) * tab_width;
229 col += CHAR_COLUMNS (c);
237 col = ((col + tab_width) / tab_width) * tab_width;
245 current_column (struct buffer *buf)
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;
252 return column_at_point (buf, BUF_PT (buf), 1);
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.
269 return make_int (current_column (decode_buffer (buffer, 0)));
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 MINIMUM says always do at least MINIMUM spaces
276 even if that goes past COLUMN; by default, MINIMUM is zero.
277 If BUFFER is nil, the current buffer is assumed.
279 (column, minimum, buffer))
281 /* This function can GC */
284 struct buffer *buf = decode_buffer (buffer, 0);
285 int tab_width = XINT (buf->tab_width);
294 XSETBUFFER (buffer, buf);
296 fromcol = current_column (buf);
297 mincol = fromcol + XINT (minimum);
298 if (mincol < XINT (column)) mincol = XINT (column);
300 if (fromcol == mincol)
301 return make_int (mincol);
303 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
305 if (!NILP (Fextent_at (make_int (BUF_PT (buf)), buffer, Qinvisible,
308 Bufpos last_visible = last_visible_position (BUF_PT (buf), buf);
310 opoint = BUF_PT (buf);
311 if (last_visible >= BUF_BEGV (buf))
312 BUF_SET_PT (buf, last_visible);
314 error ("Visible portion of buffer not modifiable");
317 if (indent_tabs_mode)
319 int n = mincol / tab_width - fromcol / tab_width;
322 Finsert_char (make_char ('\t'), make_int (n), Qnil, buffer);
324 fromcol = (mincol / tab_width) * tab_width;
328 Finsert_char (make_char (' '), make_int (mincol - fromcol), Qnil, buffer);
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);
337 BUF_SET_PT (buf, opoint);
339 return make_int (mincol);
343 bi_spaces_at_point (struct buffer *b, Bytind bi_pos)
345 Bytind bi_end = BI_BUF_ZV (b);
348 int tab_width = XINT (b->tab_width);
350 if (tab_width <= 0 || tab_width > 1000)
353 while (bi_pos < bi_end &&
354 (c = BI_BUF_FETCH_CHAR (b, bi_pos),
356 ? (col += tab_width - col % tab_width)
357 : (c == ' ' ? ++col : 0))))
358 INC_BYTIND (b, bi_pos);
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.
371 struct buffer *buf = decode_buffer (buffer, 0);
372 Bufpos pos = find_next_newline (buf, BUF_PT (buf), -1);
374 XSETBUFFER (buffer, buf);
376 if (!NILP (Fextent_at (make_int (pos), buffer, Qinvisible, Qnil, Qnil)))
379 return make_int (bi_spaces_at_point (buf, bufpos_to_bytind (buf, pos)));
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.
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.
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.
399 Returns the actual column that it moved to.
401 (column, force, buffer))
403 /* This function can GC */
405 struct buffer *buf = decode_buffer (buffer, 0);
406 int col = current_column (buf);
409 int tab_width = XINT (buf->tab_width);
414 XSETBUFFER (buffer, buf);
415 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
416 CHECK_NATNUM (column);
417 goal = XINT (column);
423 /* If we're starting past the desired column,
424 back up to beginning of line and scan from there. */
427 pos = find_next_newline (buf, pos, -1);
431 while (col < goal && pos < end)
433 c = BUF_FETCH_CHAR (buf, pos);
436 if (c == '\r' && EQ (buf->selective_display, Qt))
442 col = col / tab_width * tab_width;
446 /* #### oh for the days of the complete new redisplay */
447 /* #### FSFmacs looks at ctl_arrow, display tables.
448 We need to do similar. */
450 displayed_glyphs = glyphs_from_bufpos (selected_frame (),
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));
459 col += CHAR_COLUMNS (c);
469 BUF_SET_PT (buf, pos);
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)
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, ' ');
481 /* If line ends prematurely, add space to the end. */
482 if (col < goal && !NILP (force) && !EQ (force, Qcoerce))
485 Findent_to (make_int (col), Qzero, buffer);
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);
493 return make_int (col);
496 #if 0 /* #### OK boys, this function needs to be present, I think.
497 It was there before the 19.12 redisplay rewrite. */
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.
506 There are three additional arguments:
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).
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.
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.
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.
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.
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.
539 (from, frompos, to, topos, width, offsets, window))
541 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
542 struct position *pos;
543 int hscroll, tab_offset;
544 struct window *w = decode_window (window);
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);
552 CHECK_INT (XCAR (topos));
553 CHECK_INT (XCDR (topos));
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));
564 hscroll = tab_offset = 0;
566 pos = compute_motion (XINT (from), XINT (XCDR (frompos)),
567 XINT (XCAR (frompos)),
568 XINT (to), XINT (XCDR (topos)),
570 XINT (width), hscroll, tab_offset, w);
572 XSETINT (bufpos, pos->bufpos);
573 XSETINT (hpos, pos->hpos);
574 XSETINT (vpos, pos->vpos);
575 XSETINT (prevhpos, pos->prevhpos);
577 return list5 (bufpos, hpos, vpos, prevhpos,
578 pos->contin ? Qt : Qnil);
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.
588 vpix_motion (line_start_cache_dynarr *cache, int start, int end)
592 assert (start <= end);
594 assert (end < Dynarr_length (cache));
597 for (i = start; i <= end; i++)
598 vpix += Dynarr_atp (cache, i)->height;
603 /*****************************************************************************
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 ****************************************************************************/
614 vmotion_1 (struct window *w, Bufpos orig, int vtarget,
615 int *ret_vpos, int *ret_vpix)
617 struct buffer *b = XBUFFER (w->buffer);
620 elt = point_in_line_start_cache (w, orig, (vtarget < 0
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. */
630 /* Moving downward. */
633 int cur_line = Dynarr_length (w->line_start_cache) - 1 - elt;
636 if (cur_line > vtarget)
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)
644 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
646 while (ret_pt > BUF_ZV (b) && cur_line > 0)
649 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
652 if (ret_vpos) *ret_vpos = cur_line;
654 *ret_vpix = vpix_motion (w->line_start_cache, elt, cur_line + elt);
657 else if (vtarget < 0)
661 if (ret_vpos) *ret_vpos = -elt;
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;
669 if (ret_vpos) *ret_vpos = vtarget;
671 *ret_vpix = vpix_motion (w->line_start_cache, elt + vtarget, elt);
672 return Dynarr_atp (w->line_start_cache, elt + vtarget)->start;
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;
681 *ret_vpix = vpix_motion (w->line_start_cache, elt, elt);
683 return Dynarr_atp (w->line_start_cache, elt)->start;
686 RETURN_NOT_REACHED(0) /* shut up compiler */
689 /*****************************************************************************
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
696 ****************************************************************************/
698 vmotion (struct window *w, Bufpos orig, int vtarget, int *ret_vpos)
700 return vmotion_1 (w, orig, vtarget, ret_vpos, NULL);
703 /* Helper for Fvertical_motion.
706 Lisp_Object vertical_motion_1 (Lisp_Object lines, Lisp_Object window,
717 window = Fselected_window (Qnil);
719 CHECK_LIVE_WINDOW (window);
722 selected = (EQ (window, Fselected_window (Qnil)));
724 w = XWINDOW (window);
726 orig = selected ? BUF_PT (XBUFFER (w->buffer))
727 : marker_position (w->pointm[CURRENT_DISP]);
729 vpos = pixels ? NULL : &value;
730 vpix = pixels ? &value : NULL;
732 bufpos = vmotion_1 (w, orig, XINT (lines), vpos, vpix);
734 /* Note that the buffer's point is set, not the window's point. */
736 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
738 set_marker_restricted (w->pointm[CURRENT_DISP],
742 return make_int (value);
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.
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.
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.)
764 (lines, window, pixels))
766 return vertical_motion_1 (lines, window, !NILP (pixels));
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.
775 vmotion_pixels (Lisp_Object window, Bufpos start, int pixels, int how,
783 int remain, abspix, dirn;
786 line_start_cache_dynarr *cache;
791 window = Fselected_window (Qnil);
793 CHECK_LIVE_WINDOW (window);
794 w = XWINDOW (window);
796 eobuf = BUF_ZV (XBUFFER (w->buffer));
797 bobuf = BUF_BEGV (XBUFFER (w->buffer));
799 default_face_height_and_width (window, &defheight, NULL);
801 /* guess num lines needed in line start cache + a few extra */
802 abspix = abs (pixels);
803 needed = (abspix + defheight-1)/defheight + 3;
805 dirn = (pixels >= 0) ? 1 : -1;
809 elt = point_in_line_start_cache (w, start, needed);
810 assert (elt >= 0); /* in the cache */
812 cache = w->line_start_cache;
813 nelt = Dynarr_length (cache);
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;
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;
829 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn)
831 /* cache line we're considering moving over */
832 int ii = (dirn > 0) ? i : i-1;
835 return Dynarr_atp (cache, i)->start;
837 line = Dynarr_atp (cache, ii)->height;
838 next = remain - line;
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;
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;
852 *motion += dirn * line;
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;
859 /* get here => need more cache lines. try again. */
860 assert (abs (*motion) > previous); /* progress? */
861 previous = abs (*motion);
863 lines = (pixels < 0) ? elt : (nelt - elt);
864 needed += (remain*lines + abspix-1)/abspix + 3;
867 RETURN_NOT_REACHED(0) /* shut up compiler */
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.
875 Optional second argument is WINDOW to move in,
876 the default is the selected window.
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.
884 (pixels, window, how))
894 window = Fselected_window (Qnil);
896 CHECK_LIVE_WINDOW (window);
899 selected = (EQ (window, Fselected_window (Qnil)));
901 w = XWINDOW (window);
903 orig = selected ? BUF_PT (XBUFFER (w->buffer))
904 : marker_position (w->pointm[CURRENT_DISP]);
906 howto = INTP (how) ? XINT (how) : 0;
908 bufpos = vmotion_pixels (window, orig, XINT (pixels), howto, &motion);
911 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
913 set_marker_restricted (w->pointm[CURRENT_DISP],
917 return make_int (motion);
922 syms_of_indent (void)
924 DEFSUBR (Fcurrent_indentation);
925 DEFSUBR (Findent_to);
926 DEFSUBR (Fcurrent_column);
927 DEFSUBR (Fmove_to_column);
929 DEFSUBR (Fcompute_motion);
931 DEFSUBR (Fvertical_motion);
932 DEFSUBR (Fvertical_motion_pixels);
934 defsymbol (&Qcoerce, "coerce");
938 vars_of_indent (void)
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.
944 indent_tabs_mode = 1;