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 SXEmacs
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 /* This file has been Mule-ized. */
24 /* Synched up with: 19.30. Diverges significantly from FSF. */
30 #include "ui/device.h"
34 #include "ui/glyphs.h"
35 #include "ui/insdel.h"
36 #ifdef REGION_CACHE_NEEDS_WORK
37 #include "region-cache.h"
39 #include "ui/window.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;
64 static Bufpos last_visible_position(Bufpos pos, struct buffer *buf)
69 XSETBUFFER(buffer, buf);
70 value = Fprevious_single_property_change(make_int(pos), Qinvisible,
73 return 0; /* no visible position found */
75 /* #### bug bug bug!!! This will return the position of the beginning
76 of an invisible extent; this extent is very likely to be start-closed,
77 and thus the spaces inserted in `indent-to' will go inside the
80 Not sure what the correct solution is here. Rethink indent-to? */
84 #ifdef REGION_CACHE_NEEDS_WORK
86 /* Allocate or free the width run cache, as requested by the current
87 state of current_buffer's cache_long_line_scans variable. */
88 static void width_run_cache_on_off(struct buffer *buf)
90 if (NILP(buf->cache_long_line_scans)) {
91 /* It should be off. */
92 if (buf->width_run_cache) {
93 free_region_cache(buf->width_run_cache);
94 buf->width_run_cache = 0;
95 buf->width_table = Qnil;
98 /* It should be on. */
99 if (buf->width_run_cache == 0) {
100 buf->width_run_cache = new_region_cache();
101 recompute_width_table(buf, buffer_display_table());
106 #endif /* REGION_CACHE_NEEDS_WORK */
108 /* Cancel any recorded value of the horizontal position. */
110 void invalidate_current_column(void)
112 last_known_column_point = -1;
115 int column_at_point(struct buffer *buf, Bufpos init_pos, int cur_col)
119 int tab_width = XINT(buf->tab_width);
121 Bufpos pos = init_pos;
124 if (tab_width <= 0 || tab_width > 1000)
126 col = tab_seen = post_tab = 0;
129 if (pos <= BUF_BEGV(buf))
133 c = BUF_FETCH_CHAR(buf, pos);
137 ((col + tab_width) / tab_width) * tab_width;
142 } else if (c == '\n' ||
143 (EQ(buf->selective_display, Qt) && c == '\r'))
146 /* #### This needs updating to handle the new redisplay. */
147 /* #### FSFmacs looks at ctl_arrow, display tables.
148 We need to do similar. */
150 displayed_glyphs = glyphs_from_bufpos(sel_frame, buf,
156 (displayed_glyphs->columns -
157 (displayed_glyphs->begin_columns +
158 displayed_glyphs->end_columns));
162 Lisp_Object tmp = CHAR_CHARSET(c);
163 col += XCHARSET_COLUMNS(tmp);
173 col = ((col + tab_width) / tab_width) * tab_width;
178 last_known_column_buffer = buf;
179 last_known_column = col;
180 last_known_column_point = init_pos;
181 last_known_column_modified = BUF_MODIFF(buf);
187 int string_column_at_point(Lisp_String * s, Bufpos init_pos, int tab_width)
192 Bufpos pos = init_pos;
195 if (tab_width <= 0 || tab_width > 1000)
197 col = tab_seen = post_tab = 0;
204 c = string_char(s, pos);
208 ((col + tab_width) / tab_width) * tab_width;
213 } else if (c == '\n') {
217 Lisp_Object tmp = CHAR_CHARSET(c);
218 col += XCHARSET_COLUMNS(tmp);
226 col = ((col + tab_width) / tab_width) * tab_width;
233 int current_column(struct buffer *buf)
235 if (buf == last_known_column_buffer
236 && BUF_PT(buf) == last_known_column_point
237 && BUF_MODIFF(buf) == last_known_column_modified)
238 return last_known_column;
240 return column_at_point(buf, BUF_PT(buf), 1);
243 DEFUN("current-column", Fcurrent_column, 0, 1, 0, /*
244 Return the horizontal position of point. Beginning of line is column 0.
245 This is calculated by adding together the widths of all the displayed
246 representations of the character between the start of the previous line
247 and point. (e.g. control characters will have a width of 2 or 4, tabs
248 will have a variable width.)
249 Ignores finite width of frame, which means that this function may return
250 values greater than (frame-width).
251 Whether the line is visible (if `selective-display' is t) has no effect;
252 however, ^M is treated as end of line when `selective-display' is t.
253 If BUFFER is nil, the current buffer is assumed.
257 return make_int(current_column(decode_buffer(buffer, 0)));
260 DEFUN("indent-to", Findent_to, 1, 3, "NIndent to column: ", /*
261 Indent from point with tabs and spaces until COLUMN is reached.
262 Optional second argument MINIMUM says always do at least MINIMUM spaces
263 even if that goes past COLUMN; by default, MINIMUM is zero.
264 If BUFFER is nil, the current buffer is assumed.
266 (column, minimum, buffer))
268 /* This function can GC */
271 struct buffer *buf = decode_buffer(buffer, 0);
272 int tab_width = XINT(buf->tab_width);
281 XSETBUFFER(buffer, buf);
283 fromcol = current_column(buf);
284 mincol = fromcol + XINT(minimum);
285 if (mincol < XINT(column))
286 mincol = XINT(column);
288 if (fromcol == mincol)
289 return make_int(mincol);
291 if (tab_width <= 0 || tab_width > 1000)
294 if (!NILP(Fextent_at(make_int(BUF_PT(buf)), buffer, Qinvisible,
296 Bufpos last_visible = last_visible_position(BUF_PT(buf), buf);
298 opoint = BUF_PT(buf);
299 if (last_visible >= BUF_BEGV(buf))
300 BUF_SET_PT(buf, last_visible);
302 error("Visible portion of buffer not modifiable");
305 if (indent_tabs_mode) {
306 int n = mincol / tab_width - fromcol / tab_width;
308 Finsert_char(make_char('\t'), make_int(n), Qnil,
311 fromcol = (mincol / tab_width) * tab_width;
315 Finsert_char(make_char(' '), make_int(mincol - fromcol), Qnil, buffer);
317 last_known_column_buffer = buf;
318 last_known_column = mincol;
319 last_known_column_point = BUF_PT(buf);
320 last_known_column_modified = BUF_MODIFF(buf);
324 BUF_SET_PT(buf, opoint);
326 return make_int(mincol);
329 int bi_spaces_at_point(struct buffer *b, Bytind bi_pos)
331 Bytind bi_end = BI_BUF_ZV(b);
334 int tab_width = XINT(b->tab_width);
336 if (tab_width <= 0 || tab_width > 1000)
339 while (bi_pos < bi_end &&
340 (c = BI_BUF_FETCH_CHAR(b, bi_pos),
341 (c == '\t' ? (col += tab_width - col % tab_width)
342 : (c == ' ' ? ++col : 0))))
343 INC_BYTIND(b, bi_pos);
348 DEFUN("current-indentation", Fcurrent_indentation, 0, 1, 0, /*
349 Return the indentation of the current line.
350 This is the horizontal position of the character
351 following any initial whitespace.
355 struct buffer *buf = decode_buffer(buffer, 0);
356 Bufpos pos = find_next_newline(buf, BUF_PT(buf), -1);
358 XSETBUFFER(buffer, buf);
360 if (!NILP(Fextent_at(make_int(pos), buffer, Qinvisible, Qnil, Qnil)))
363 return make_int(bi_spaces_at_point(buf, bufpos_to_bytind(buf, pos)));
366 DEFUN("move-to-column", Fmove_to_column, 1, 3, 0, /*
367 Move point to column COLUMN in the current line.
368 The column of a character is calculated by adding together the widths
369 as displayed of the previous characters in the line.
370 This function ignores line-continuation;
371 there is no upper limit on the column number a character can have
372 and horizontal scrolling has no effect.
374 If specified column is within a character, point goes after that character.
375 If it's past end of line, point goes to end of line.
377 A value of 'coerce for the second (optional) argument FORCE means if
378 COLUMN is in the middle of a tab character, change it to spaces.
379 Any other non-nil value means the same, plus if the line is too short to
380 reach column COLUMN, then add spaces/tabs to get there.
382 Returns the actual column that it moved to.
384 (column, force, buffer))
386 /* This function can GC */
388 struct buffer *buf = decode_buffer(buffer, 0);
389 int col = current_column(buf);
392 int tab_width = XINT(buf->tab_width);
397 XSETBUFFER(buffer, buf);
398 if (tab_width <= 0 || tab_width > 1000)
400 CHECK_NATNUM(column);
407 /* If we're starting past the desired column,
408 back up to beginning of line and scan from there. */
410 pos = find_next_newline(buf, pos, -1);
414 while (col < goal && pos < end) {
415 c = BUF_FETCH_CHAR(buf, pos);
418 if (c == '\r' && EQ(buf->selective_display, Qt))
423 col = col / tab_width * tab_width;
425 /* #### oh for the days of the complete new redisplay */
426 /* #### FSFmacs looks at ctl_arrow, display tables.
427 We need to do similar. */
429 displayed_glyphs = glyphs_from_bufpos(selected_frame(),
436 (displayed_glyphs->columns -
437 (displayed_glyphs->begin_columns +
438 displayed_glyphs->end_columns));
442 Lisp_Object tmp = CHAR_CHARSET(c);
443 col += XCHARSET_COLUMNS(tmp);
454 BUF_SET_PT(buf, pos);
456 /* If a tab char made us overshoot, change it to spaces
457 and scan through it again. */
458 if (!NILP(force) && col > goal && c == '\t' && prev_col < goal) {
459 buffer_delete_range(buf, BUF_PT(buf) - 1, BUF_PT(buf), 0);
460 Findent_to(make_int(col - 1), Qzero, buffer);
461 buffer_insert_emacs_char(buf, ' ');
465 /* If line ends prematurely, add space to the end. */
466 if (col < goal && !NILP(force) && !EQ(force, Qcoerce)) {
468 Findent_to(make_int(col), Qzero, buffer);
471 last_known_column_buffer = buf;
472 last_known_column = col;
473 last_known_column_point = BUF_PT(buf);
474 last_known_column_modified = BUF_MODIFF(buf);
476 return make_int(col);
479 #if 0 /* #### OK boys, this function needs to be present, I think.
480 It was there before the 19.12 redisplay rewrite. */
482 xxDEFUN("compute-motion", Fcompute_motion, 7, 7, 0, /*
483 "Scan through the current buffer, calculating screen position.
484 Scan the current buffer forward from offset FROM,
485 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--
486 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--
487 and return the ending buffer position and screen location.
489 There are three additional arguments:
491 WIDTH is the number of columns available to display text;
492 this affects handling of continuation lines.
493 This is usually the value returned by `window-width', less one (to allow
494 for the continuation glyph).
496 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).
497 HSCROLL is the number of columns not being displayed at the left
498 margin; this is usually taken from a window's hscroll member.
499 TAB-OFFSET is the number of columns of the first tab that aren't
500 being displayed, perhaps because the line was continued within it.
501 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.
503 WINDOW is the window to operate on. Currently this is used only to
504 find the display table. It does not matter what buffer WINDOW displays;
505 `compute-motion' always operates on the current buffer.
507 The value is a list of five elements:
508 (POS HPOS VPOS PREVHPOS CONTIN)
509 POS is the buffer position where the scan stopped.
510 VPOS is the vertical position where the scan stopped.
511 HPOS is the horizontal position where the scan stopped.
513 PREVHPOS is the horizontal position one character back from POS.
514 CONTIN is t if a line was continued after (or within) the previous character.
516 For example, to find the buffer position of column COL of line LINE
517 of a certain window, pass the window's starting location as FROM
518 and the window's upper-left coordinates as FROMPOS.
519 Pass the buffer's (point-max) as TO, to limit the scan to the end of the
520 visible section of the buffer, and pass LINE and COL as TOPOS.
522 (from, frompos, to, topos, width, offsets, window)) {
523 Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
524 struct position *pos;
525 int hscroll, tab_offset;
526 struct window *w = decode_window(window);
528 CHECK_INT_COERCE_MARKER(from);
530 CHECK_INT(XCAR(frompos));
531 CHECK_INT(XCDR(frompos));
532 CHECK_INT_COERCE_MARKER(to);
534 CHECK_INT(XCAR(topos));
535 CHECK_INT(XCDR(topos));
537 if (!NILP(offsets)) {
539 CHECK_INT(XCAR(offsets));
540 CHECK_INT(XCDR(offsets));
541 hscroll = XINT(XCAR(offsets));
542 tab_offset = XINT(XCDR(offsets));
544 hscroll = tab_offset = 0;
546 pos = compute_motion(XINT(from), XINT(XCDR(frompos)),
548 XINT(to), XINT(XCDR(topos)),
550 XINT(width), hscroll, tab_offset, w);
552 XSETINT(bufpos, pos->bufpos);
553 XSETINT(hpos, pos->hpos);
554 XSETINT(vpos, pos->vpos);
555 XSETINT(prevhpos, pos->prevhpos);
557 return list5(bufpos, hpos, vpos, prevhpos, pos->contin ? Qt : Qnil);
562 /* Helper for vmotion_1 - compute vertical pixel motion between
563 START and END in the line start cache CACHE. This just sums
564 the line heights, including both the starting and ending lines.
566 static int vpix_motion(line_start_cache_dynarr * cache, int start, int end)
570 assert(start <= end);
572 assert(end < Dynarr_length(cache));
574 if (start<0 || end<0 || end>start) {
575 /* Least bad thing in case of in fatal_failure, where
576 assert will not terminate this function... */
581 for (i = start; i <= end; i++)
582 vpix += Dynarr_atp(cache, i)->height;
586 /*****************************************************************************
589 Given a starting position ORIG, move point VTARGET lines in WINDOW.
590 Returns the new value for point. If the arg ret_vpos is not nil, it is
591 taken to be a pointer to an int and the number of lines actually moved is
592 returned in it. If the arg ret_vpix is not nil, it is taken to be a
593 pointer to an int and the vertical pixel height of the motion which
594 took place is returned in it.
595 ****************************************************************************/
597 vmotion_1(struct window *w, Bufpos orig, int vtarget,
598 int *ret_vpos, int *ret_vpix)
600 struct buffer *b = XBUFFER(w->buffer);
603 elt = point_in_line_start_cache(w, orig, (vtarget < 0
604 ? -vtarget : vtarget));
606 /* #### This assertion must be true before the if statements are hit
607 but may possibly be wrong after the call to
608 point_in_line_start_cache if orig is outside of the visible
609 region of the buffer. Handle this. */
612 /* Moving downward. */
614 int cur_line = Dynarr_length(w->line_start_cache) - 1 - elt;
617 if (cur_line > vtarget)
620 /* The traditional FSF behavior is to return the end of buffer
621 position if we couldn't move far enough because we hit it. */
622 if (cur_line < vtarget)
626 Dynarr_atp(w->line_start_cache,
627 cur_line + elt)->start;
629 while (ret_pt > BUF_ZV(b) && cur_line > 0) {
632 Dynarr_atp(w->line_start_cache,
633 cur_line + elt)->start;
637 *ret_vpos = cur_line;
640 vpix_motion(w->line_start_cache, elt,
643 } else if (vtarget < 0) {
644 if (elt < -vtarget) {
649 vpix_motion(w->line_start_cache, 0, elt);
650 /* #### This should be BUF_BEGV (b), right? */
651 return Dynarr_atp(w->line_start_cache, 0)->start;
657 vpix_motion(w->line_start_cache,
659 return Dynarr_atp(w->line_start_cache,
660 elt + vtarget)->start;
663 /* No vertical motion requested so we just return the position
664 of the beginning of the current line. */
668 *ret_vpix = vpix_motion(w->line_start_cache, elt, elt);
670 return Dynarr_atp(w->line_start_cache, elt)->start;
673 RETURN_NOT_REACHED(0) /* shut up compiler */
676 /*****************************************************************************
679 Given a starting position ORIG, move point VTARGET lines in WINDOW.
680 Returns the new value for point. If the arg ret_vpos is not nil, it is
681 taken to be a pointer to an int and the number of lines actually moved is
683 ****************************************************************************/
684 Bufpos vmotion(struct window * w, Bufpos orig, int vtarget, int *ret_vpos)
686 return vmotion_1(w, orig, vtarget, ret_vpos, NULL);
689 /* Helper for Fvertical_motion.
692 Lisp_Object vertical_motion_1(Lisp_Object lines, Lisp_Object window, int pixels)
702 window = Fselected_window(Qnil);
704 CHECK_LIVE_WINDOW(window);
707 selected = (EQ(window, Fselected_window(Qnil)));
711 orig = selected ? BUF_PT(XBUFFER(w->buffer))
712 : marker_position(w->pointm[CURRENT_DISP]);
714 vpos = pixels ? NULL : &value;
715 vpix = pixels ? &value : NULL;
717 bufpos = vmotion_1(w, orig, XINT(lines), vpos, vpix);
719 /* Note that the buffer's point is set, not the window's point. */
721 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
723 set_marker_restricted(w->pointm[CURRENT_DISP],
724 make_int(bufpos), w->buffer);
726 return make_int(value);
729 DEFUN("vertical-motion", Fvertical_motion, 1, 3, 0, /*
730 Move to start of frame line LINES lines down.
731 If LINES is negative, this is moving up.
732 Optional second argument is WINDOW to move in,
733 the default is the selected window.
735 Sets point to position found; this may be start of line
736 or just the start of a continuation line.
737 If optional third argument PIXELS is nil, returns number
738 of lines moved; may be closer to zero than LINES if beginning
739 or end of buffer was reached. If PIXELS is non-nil, the
740 vertical pixel height of the motion which took place is
741 returned instead of the actual number of lines moved. A
742 motion of zero lines returns the height of the current line.
744 Note that `vertical-motion' sets WINDOW's buffer's point, not
745 WINDOW's point. (This differs from FSF Emacs, which buggily always
746 sets current buffer's point, regardless of WINDOW.)
748 (lines, window, pixels))
750 return vertical_motion_1(lines, window, !NILP(pixels));
754 * Like vmotion() but requested and returned movement is in pixels.
755 * HOW specifies the stopping condition. Positive means move at least
756 * PIXELS. Negative means at most. Zero means as close as possible.
759 vmotion_pixels(Lisp_Object window, Bufpos start, int pixels, int how,
767 int remain, abspix, dirn;
770 line_start_cache_dynarr *cache;
775 window = Fselected_window(Qnil);
777 CHECK_LIVE_WINDOW(window);
780 eobuf = BUF_ZV(XBUFFER(w->buffer));
781 bobuf = BUF_BEGV(XBUFFER(w->buffer));
783 default_face_height_and_width(window, &defheight, NULL);
785 /* guess num lines needed in line start cache + a few extra */
786 abspix = abs(pixels);
787 needed = (abspix + defheight - 1) / defheight + 3;
789 dirn = (pixels >= 0) ? 1 : -1;
792 elt = point_in_line_start_cache(w, start, needed);
793 assert(elt >= 0); /* in the cache */
795 cache = w->line_start_cache;
796 nelt = Dynarr_length(cache);
801 /* No vertical motion requested so we just return the position
802 of the beginning of the current display line. */
803 return Dynarr_atp(cache, elt)->start;
805 if ((dirn < 0 && elt == 0 &&
806 Dynarr_atp(cache, elt)->start <= bobuf) ||
807 (dirn > 0 && elt == nelt - 1 &&
808 Dynarr_atp(cache, elt)->end >= eobuf))
809 return Dynarr_atp(cache, elt)->start;
812 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn) {
813 /* cache line we're considering moving over */
814 int ii = (dirn > 0) ? i : i - 1;
817 return Dynarr_atp(cache, i)->start;
819 line = Dynarr_atp(cache, ii)->height;
820 next = remain - line;
822 /* is stopping condition satisfied? */
823 if ((how > 0 && remain <= 0) || /* at least */
824 (how < 0 && next < 0) || /* at most */
825 (how == 0 && remain <= abs(next))) /* closest */
826 return Dynarr_atp(cache, i)->start;
828 /* moving down and nowhere left to go? */
829 if (dirn > 0 && Dynarr_atp(cache, ii)->end >= eobuf)
830 return Dynarr_atp(cache, ii)->start;
834 *motion += dirn * line;
836 /* moving up and nowhere left to go? */
837 if (dirn < 0 && Dynarr_atp(cache, ii)->start <= bobuf)
838 return Dynarr_atp(cache, ii)->start;
841 /* get here => need more cache lines. try again. */
842 assert(abs(*motion) > previous); /* progress? */
843 previous = abs(*motion);
845 lines = (pixels < 0) ? elt : (nelt - elt);
846 needed += (remain * lines + abspix - 1) / abspix + 3;
849 RETURN_NOT_REACHED(0) /* shut up compiler */
852 DEFUN("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /*
853 Move to start of frame line PIXELS vertical pixels down.
854 If PIXELS is negative, this is moving up.
855 The actual vertical motion in pixels is returned.
857 Optional second argument is WINDOW to move in,
858 the default is the selected window.
860 Optional third argument HOW specifies when to stop. A value
861 less than zero indicates that the motion should be no more
862 than PIXELS. A value greater than zero indicates that the
863 motion should be at least PIXELS. Any other value indicates
864 that the motion should be as close as possible to PIXELS.
866 (pixels, window, how))
876 window = Fselected_window(Qnil);
878 CHECK_LIVE_WINDOW(window);
881 selected = (EQ(window, Fselected_window(Qnil)));
885 orig = selected ? BUF_PT(XBUFFER(w->buffer))
886 : marker_position(w->pointm[CURRENT_DISP]);
888 howto = INTP(how) ? XINT(how) : 0;
890 bufpos = vmotion_pixels(window, orig, XINT(pixels), howto, &motion);
893 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
895 set_marker_restricted(w->pointm[CURRENT_DISP],
896 make_int(bufpos), w->buffer);
898 return make_int(motion);
901 void syms_of_indent(void)
903 DEFSUBR(Fcurrent_indentation);
905 DEFSUBR(Fcurrent_column);
906 DEFSUBR(Fmove_to_column);
908 DEFSUBR(Fcompute_motion);
910 DEFSUBR(Fvertical_motion);
911 DEFSUBR(Fvertical_motion_pixels);
913 defsymbol(&Qcoerce, "coerce");
916 void vars_of_indent(void)
918 DEFVAR_BOOL("indent-tabs-mode", &indent_tabs_mode /*
919 *Indentation can insert tabs if this is non-nil.
920 Setting this variable automatically makes it local to the current buffer.
922 indent_tabs_mode = 1;