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 += XCHARSET_COLUMNS (CHAR_CHARSET (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 current_column (struct buffer *buf)
200 if (buf == last_known_column_buffer
201 && BUF_PT (buf) == last_known_column_point
202 && BUF_MODIFF (buf) == last_known_column_modified)
203 return last_known_column;
205 return column_at_point (buf, BUF_PT (buf), 1);
208 DEFUN ("current-column", Fcurrent_column, 0, 1, 0, /*
209 Return the horizontal position of point. Beginning of line is column 0.
210 This is calculated by adding together the widths of all the displayed
211 representations of the character between the start of the previous line
212 and point. (e.g. control characters will have a width of 2 or 4, tabs
213 will have a variable width.)
214 Ignores finite width of frame, which means that this function may return
215 values greater than (frame-width).
216 Whether the line is visible (if `selective-display' is t) has no effect;
217 however, ^M is treated as end of line when `selective-display' is t.
218 If BUFFER is nil, the current buffer is assumed.
222 return make_int (current_column (decode_buffer (buffer, 0)));
226 DEFUN ("indent-to", Findent_to, 1, 3, "NIndent to column: ", /*
227 Indent from point with tabs and spaces until COLUMN is reached.
228 Optional second argument MIN says always do at least MIN spaces
229 even if that goes past COLUMN; by default, MIN is zero.
230 If BUFFER is nil, the current buffer is assumed.
232 (col, minimum, buffer))
234 /* This function can GC */
237 struct buffer *buf = decode_buffer (buffer, 0);
238 int tab_width = XINT (buf->tab_width);
247 XSETBUFFER (buffer, buf);
249 fromcol = current_column (buf);
250 mincol = fromcol + XINT (minimum);
251 if (mincol < XINT (col)) mincol = XINT (col);
253 if (fromcol == mincol)
254 return make_int (mincol);
256 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
258 if (!NILP (Fextent_at (make_int (BUF_PT (buf)), buffer, Qinvisible,
261 Bufpos last_visible = last_visible_position (BUF_PT (buf), buf);
263 opoint = BUF_PT (buf);
264 if (last_visible >= BUF_BEGV (buf))
265 BUF_SET_PT (buf, last_visible);
267 error ("Visible portion of buffer not modifiable");
270 if (indent_tabs_mode)
272 int n = mincol / tab_width - fromcol / tab_width;
275 Finsert_char (make_char ('\t'), make_int (n), Qnil, buffer);
277 fromcol = (mincol / tab_width) * tab_width;
281 Finsert_char (make_char (' '), make_int (mincol - fromcol), Qnil, buffer);
283 last_known_column_buffer = buf;
284 last_known_column = mincol;
285 last_known_column_point = BUF_PT (buf);
286 last_known_column_modified = BUF_MODIFF (buf);
290 BUF_SET_PT (buf, opoint);
292 return make_int (mincol);
296 bi_spaces_at_point (struct buffer *b, Bytind bi_pos)
298 Bytind bi_end = BI_BUF_ZV (b);
301 int tab_width = XINT (b->tab_width);
303 if (tab_width <= 0 || tab_width > 1000)
306 while (bi_pos < bi_end &&
307 (c = BI_BUF_FETCH_CHAR (b, bi_pos),
309 ? (col += tab_width - col % tab_width)
310 : (c == ' ' ? ++col : 0))))
311 INC_BYTIND (b, bi_pos);
317 DEFUN ("current-indentation", Fcurrent_indentation, 0, 1, 0, /*
318 Return the indentation of the current line.
319 This is the horizontal position of the character
320 following any initial whitespace.
324 struct buffer *buf = decode_buffer (buffer, 0);
325 Bufpos pos = find_next_newline (buf, BUF_PT (buf), -1);
327 XSETBUFFER (buffer, buf);
329 if (!NILP (Fextent_at (make_int (pos), buffer, Qinvisible, Qnil, Qnil)))
332 return make_int (bi_spaces_at_point (buf, bufpos_to_bytind (buf, pos)));
336 DEFUN ("move-to-column", Fmove_to_column, 1, 3, 0, /*
337 Move point to column COLUMN in the current line.
338 The column of a character is calculated by adding together the widths
339 as displayed of the previous characters in the line.
340 This function ignores line-continuation;
341 there is no upper limit on the column number a character can have
342 and horizontal scrolling has no effect.
344 If specified column is within a character, point goes after that character.
345 If it's past end of line, point goes to end of line.
347 A value of 'coerce for the second (optional) argument FORCE means if
348 COLUMN is in the middle of a tab character, change it to spaces.
349 Any other non-nil value means the same, plus if the line is too short to
350 reach column COLUMN, then add spaces/tabs to get there.
352 Returns the actual column that it moved to.
354 (column, force, buffer))
356 /* This function can GC */
358 struct buffer *buf = decode_buffer (buffer, 0);
359 int col = current_column (buf);
362 int tab_width = XINT (buf->tab_width);
367 XSETBUFFER (buffer, buf);
368 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
369 CHECK_NATNUM (column);
370 goal = XINT (column);
376 /* If we're starting past the desired column,
377 back up to beginning of line and scan from there. */
380 pos = find_next_newline (buf, pos, -1);
384 while (col < goal && pos < end)
386 c = BUF_FETCH_CHAR (buf, pos);
389 if (c == '\r' && EQ (buf->selective_display, Qt))
395 col = col / tab_width * tab_width;
399 /* #### oh for the days of the complete new redisplay */
400 /* #### FSFmacs looks at ctl_arrow, display tables.
401 We need to do similar. */
403 displayed_glyphs = glyphs_from_bufpos (selected_frame (),
405 XWINDOW (Fselected_window (Qnil)),
406 pos, dp, 0, col, 0, 0, 0);
407 col += (displayed_glyphs->columns
408 - (displayed_glyphs->begin_columns
409 + displayed_glyphs->end_columns));
412 col += XCHARSET_COLUMNS (CHAR_CHARSET (c));
422 BUF_SET_PT (buf, pos);
424 /* If a tab char made us overshoot, change it to spaces
425 and scan through it again. */
426 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
428 buffer_delete_range (buf, BUF_PT (buf) - 1, BUF_PT (buf), 0);
429 Findent_to (make_int (col - 1), Qzero, buffer);
430 buffer_insert_emacs_char (buf, ' ');
434 /* If line ends prematurely, add space to the end. */
435 if (col < goal && !NILP (force) && !EQ (force, Qcoerce))
438 Findent_to (make_int (col), Qzero, buffer);
441 last_known_column_buffer = buf;
442 last_known_column = col;
443 last_known_column_point = BUF_PT (buf);
444 last_known_column_modified = BUF_MODIFF (buf);
446 return make_int (col);
449 #if 0 /* #### OK boys, this function needs to be present, I think.
450 It was there before the 19.12 redisplay rewrite. */
452 xxDEFUN ("compute-motion", Fcompute_motion, 7, 7, 0, /*
453 "Scan through the current buffer, calculating screen position.
454 Scan the current buffer forward from offset FROM,
455 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--
456 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--
457 and return the ending buffer position and screen location.
459 There are three additional arguments:
461 WIDTH is the number of columns available to display text;
462 this affects handling of continuation lines.
463 This is usually the value returned by `window-width', less one (to allow
464 for the continuation glyph).
466 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).
467 HSCROLL is the number of columns not being displayed at the left
468 margin; this is usually taken from a window's hscroll member.
469 TAB-OFFSET is the number of columns of the first tab that aren't
470 being displayed, perhaps because the line was continued within it.
471 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.
473 WINDOW is the window to operate on. Currently this is used only to
474 find the display table. It does not matter what buffer WINDOW displays;
475 `compute-motion' always operates on the current buffer.
477 The value is a list of five elements:
478 (POS HPOS VPOS PREVHPOS CONTIN)
479 POS is the buffer position where the scan stopped.
480 VPOS is the vertical position where the scan stopped.
481 HPOS is the horizontal position where the scan stopped.
483 PREVHPOS is the horizontal position one character back from POS.
484 CONTIN is t if a line was continued after (or within) the previous character.
486 For example, to find the buffer position of column COL of line LINE
487 of a certain window, pass the window's starting location as FROM
488 and the window's upper-left coordinates as FROMPOS.
489 Pass the buffer's (point-max) as TO, to limit the scan to the end of the
490 visible section of the buffer, and pass LINE and COL as TOPOS.
492 (from, frompos, to, topos, width, offsets, window))
494 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
495 struct position *pos;
496 int hscroll, tab_offset;
497 struct window *w = decode_window (window);
499 CHECK_INT_COERCE_MARKER (from);
500 CHECK_CONS (frompos);
501 CHECK_INT (XCAR (frompos));
502 CHECK_INT (XCDR (frompos));
503 CHECK_INT_COERCE_MARKER (to);
505 CHECK_INT (XCAR (topos));
506 CHECK_INT (XCDR (topos));
510 CHECK_CONS (offsets);
511 CHECK_INT (XCAR (offsets));
512 CHECK_INT (XCDR (offsets));
513 hscroll = XINT (XCAR (offsets));
514 tab_offset = XINT (XCDR (offsets));
517 hscroll = tab_offset = 0;
519 pos = compute_motion (XINT (from), XINT (XCDR (frompos)),
520 XINT (XCAR (frompos)),
521 XINT (to), XINT (XCDR (topos)),
523 XINT (width), hscroll, tab_offset, w);
525 XSETINT (bufpos, pos->bufpos);
526 XSETINT (hpos, pos->hpos);
527 XSETINT (vpos, pos->vpos);
528 XSETINT (prevhpos, pos->prevhpos);
530 return list5 (bufpos, hpos, vpos, prevhpos,
531 pos->contin ? Qt : Qnil);
536 /* Helper for vmotion_1 - compute vertical pixel motion between
537 START and END in the line start cache CACHE. This just sums
538 the line heights, including both the starting and ending lines.
541 vpix_motion (line_start_cache_dynarr *cache, int start, int end)
545 assert (start <= end);
547 assert (end < Dynarr_length (cache));
550 for (i = start; i <= end; i++)
551 vpix += Dynarr_atp (cache, i)->height;
556 /*****************************************************************************
559 Given a starting position ORIG, move point VTARGET lines in WINDOW.
560 Returns the new value for point. If the arg ret_vpos is not nil, it is
561 taken to be a pointer to an int and the number of lines actually moved is
562 returned in it. If the arg ret_vpix is not nil, it is taken to be a
563 pointer to an int and the vertical pixel height of the motion which
564 took place is returned in it.
565 ****************************************************************************/
567 vmotion_1 (struct window *w, Bufpos orig, int vtarget,
568 int *ret_vpos, int *ret_vpix)
570 struct buffer *b = XBUFFER (w->buffer);
573 elt = point_in_line_start_cache (w, orig, (vtarget < 0
577 /* #### This assertion must be true before the if statements are hit
578 but may possibly be wrong after the call to
579 point_in_line_start_cache if orig is outside of the visible
580 region of the buffer. Handle this. */
583 /* Moving downward. */
586 int cur_line = Dynarr_length (w->line_start_cache) - 1 - elt;
589 if (cur_line > vtarget)
592 /* The traditional FSF behavior is to return the end of buffer
593 position if we couldn't move far enough because we hit it. */
594 if (cur_line < vtarget)
597 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
599 while (ret_pt > BUF_ZV (b) && cur_line > 0)
602 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start;
605 if (ret_vpos) *ret_vpos = cur_line;
607 *ret_vpix = vpix_motion (w->line_start_cache, elt, cur_line + elt);
610 else if (vtarget < 0)
614 if (ret_vpos) *ret_vpos = -elt;
616 *ret_vpix = vpix_motion (w->line_start_cache, 0, elt);
617 /* #### This should be BUF_BEGV (b), right? */
618 return Dynarr_atp (w->line_start_cache, 0)->start;
622 if (ret_vpos) *ret_vpos = vtarget;
624 *ret_vpix = vpix_motion (w->line_start_cache, elt + vtarget, elt);
625 return Dynarr_atp (w->line_start_cache, elt + vtarget)->start;
630 /* No vertical motion requested so we just return the position
631 of the beginning of the current line. */
632 if (ret_vpos) *ret_vpos = 0;
634 *ret_vpix = vpix_motion (w->line_start_cache, elt, elt);
636 return Dynarr_atp (w->line_start_cache, elt)->start;
639 RETURN_NOT_REACHED(0) /* shut up compiler */
642 /*****************************************************************************
645 Given a starting position ORIG, move point VTARGET lines in WINDOW.
646 Returns the new value for point. If the arg ret_vpos is not nil, it is
647 taken to be a pointer to an int and the number of lines actually moved is
649 ****************************************************************************/
651 vmotion (struct window *w, Bufpos orig, int vtarget, int *ret_vpos)
653 return vmotion_1 (w, orig, vtarget, ret_vpos, NULL);
656 /* Helper for Fvertical_motion.
659 Lisp_Object vertical_motion_1 (Lisp_Object lines, Lisp_Object window,
670 window = Fselected_window (Qnil);
672 CHECK_LIVE_WINDOW (window);
675 selected = (EQ (window, Fselected_window (Qnil)));
677 w = XWINDOW (window);
679 orig = selected ? BUF_PT (XBUFFER (w->buffer))
680 : marker_position (w->pointm[CURRENT_DISP]);
682 vpos = pixels ? NULL : &value;
683 vpix = pixels ? &value : NULL;
685 bufpos = vmotion_1 (w, orig, XINT (lines), vpos, vpix);
687 /* Note that the buffer's point is set, not the window's point. */
689 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
691 set_marker_restricted (w->pointm[CURRENT_DISP],
695 return make_int (value);
698 DEFUN ("vertical-motion", Fvertical_motion, 1, 3, 0, /*
699 Move to start of frame line LINES lines down.
700 If LINES is negative, this is moving up.
701 Optional second argument is WINDOW to move in,
702 the default is the selected window.
704 Sets point to position found; this may be start of line
705 or just the start of a continuation line.
706 If optional third argument PIXELS is nil, returns number
707 of lines moved; may be closer to zero than LINES if beginning
708 or end of buffer was reached. If PIXELS is non-nil, the
709 vertical pixel height of the motion which took place is
710 returned instead of the actual number of lines moved. A
711 motion of zero lines returns the height of the current line.
713 Note that `vertical-motion' sets WINDOW's buffer's point, not
714 WINDOW's point. (This differs from FSF Emacs, which buggily always
715 sets current buffer's point, regardless of WINDOW.)
717 (lines, window, pixels))
719 return vertical_motion_1 (lines, window, !NILP (pixels));
723 * Like vmotion() but requested and returned movement is in pixels.
724 * HOW specifies the stopping condition. Positive means move at least
725 * PIXELS. Negative means at most. Zero means as close as possible.
728 vmotion_pixels (Lisp_Object window, Bufpos start, int pixels, int how,
736 int remain, abspix, dirn;
739 line_start_cache_dynarr *cache;
744 window = Fselected_window (Qnil);
746 CHECK_LIVE_WINDOW (window);
747 w = XWINDOW (window);
749 eobuf = BUF_ZV (XBUFFER (w->buffer));
750 bobuf = BUF_BEGV (XBUFFER (w->buffer));
752 default_face_height_and_width (window, &defheight, NULL);
754 /* guess num lines needed in line start cache + a few extra */
755 abspix = abs (pixels);
756 needed = (abspix + defheight-1)/defheight + 3;
758 dirn = (pixels >= 0) ? 1 : -1;
762 elt = point_in_line_start_cache (w, start, needed);
763 assert (elt >= 0); /* in the cache */
765 cache = w->line_start_cache;
766 nelt = Dynarr_length (cache);
771 /* No vertical motion requested so we just return the position
772 of the beginning of the current display line. */
773 return Dynarr_atp (cache, elt)->start;
775 if ((dirn < 0 && elt == 0 &&
776 Dynarr_atp (cache, elt)->start <= bobuf) ||
777 (dirn > 0 && elt == nelt-1 &&
778 Dynarr_atp (cache, elt)->end >= eobuf))
779 return Dynarr_atp (cache, elt)->start;
782 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn)
784 /* cache line we're considering moving over */
785 int ii = (dirn > 0) ? i : i-1;
788 return Dynarr_atp (cache, i)->start;
790 line = Dynarr_atp (cache, ii)->height;
791 next = remain - line;
793 /* is stopping condition satisfied? */
794 if ((how > 0 && remain <= 0) || /* at least */
795 (how < 0 && next < 0) || /* at most */
796 (how == 0 && remain <= abs (next))) /* closest */
797 return Dynarr_atp (cache, i)->start;
799 /* moving down and nowhere left to go? */
800 if (dirn > 0 && Dynarr_atp (cache, ii)->end >= eobuf)
801 return Dynarr_atp (cache, ii)->start;
805 *motion += dirn * line;
807 /* moving up and nowhere left to go? */
808 if (dirn < 0 && Dynarr_atp (cache, ii)->start <= bobuf)
809 return Dynarr_atp (cache, ii)->start;
812 /* get here => need more cache lines. try again. */
813 assert (abs (*motion) > previous); /* progress? */
814 previous = abs (*motion);
816 lines = (pixels < 0) ? elt : (nelt - elt);
817 needed += (remain*lines + abspix-1)/abspix + 3;
820 RETURN_NOT_REACHED(0) /* shut up compiler */
823 DEFUN ("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /*
824 Move to start of frame line PIXELS vertical pixels down.
825 If PIXELS is negative, this is moving up.
826 The actual vertical motion in pixels is returned.
828 Optional second argument is WINDOW to move in,
829 the default is the selected window.
831 Optional third argument HOW specifies when to stop. A value
832 less than zero indicates that the motion should be no more
833 than PIXELS. A value greater than zero indicates that the
834 motion should be at least PIXELS. Any other value indicates
835 that the motion should be as close as possible to PIXELS.
837 (pixels, window, how))
847 window = Fselected_window (Qnil);
849 CHECK_LIVE_WINDOW (window);
852 selected = (EQ (window, Fselected_window (Qnil)));
854 w = XWINDOW (window);
856 orig = selected ? BUF_PT (XBUFFER (w->buffer))
857 : marker_position (w->pointm[CURRENT_DISP]);
859 howto = INTP (how) ? XINT (how) : 0;
861 bufpos = vmotion_pixels (window, orig, XINT (pixels), howto, &motion);
864 BUF_SET_PT (XBUFFER (w->buffer), bufpos);
866 set_marker_restricted (w->pointm[CURRENT_DISP],
870 return make_int (motion);
875 syms_of_indent (void)
877 DEFSUBR (Fcurrent_indentation);
878 DEFSUBR (Findent_to);
879 DEFSUBR (Fcurrent_column);
880 DEFSUBR (Fmove_to_column);
882 DEFSUBR (Fcompute_motion);
884 DEFSUBR (Fvertical_motion);
885 DEFSUBR (Fvertical_motion_pixels);
887 defsymbol (&Qcoerce, "coerce");
891 vars_of_indent (void)
893 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode /*
894 *Indentation can insert tabs if this is non-nil.
895 Setting this variable automatically makes it local to the current buffer.
897 indent_tabs_mode = 1;