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));
575 for (i = start; i <= end; i++)
576 vpix += Dynarr_atp(cache, i)->height;
581 /*****************************************************************************
584 Given a starting position ORIG, move point VTARGET lines in WINDOW.
585 Returns the new value for point. If the arg ret_vpos is not nil, it is
586 taken to be a pointer to an int and the number of lines actually moved is
587 returned in it. If the arg ret_vpix is not nil, it is taken to be a
588 pointer to an int and the vertical pixel height of the motion which
589 took place is returned in it.
590 ****************************************************************************/
592 vmotion_1(struct window *w, Bufpos orig, int vtarget,
593 int *ret_vpos, int *ret_vpix)
595 struct buffer *b = XBUFFER(w->buffer);
598 elt = point_in_line_start_cache(w, orig, (vtarget < 0
599 ? -vtarget : vtarget));
601 /* #### This assertion must be true before the if statements are hit
602 but may possibly be wrong after the call to
603 point_in_line_start_cache if orig is outside of the visible
604 region of the buffer. Handle this. */
607 /* Moving downward. */
609 int cur_line = Dynarr_length(w->line_start_cache) - 1 - elt;
612 if (cur_line > vtarget)
615 /* The traditional FSF behavior is to return the end of buffer
616 position if we couldn't move far enough because we hit it. */
617 if (cur_line < vtarget)
621 Dynarr_atp(w->line_start_cache,
622 cur_line + elt)->start;
624 while (ret_pt > BUF_ZV(b) && cur_line > 0) {
627 Dynarr_atp(w->line_start_cache,
628 cur_line + elt)->start;
632 *ret_vpos = cur_line;
635 vpix_motion(w->line_start_cache, elt,
638 } else if (vtarget < 0) {
639 if (elt < -vtarget) {
644 vpix_motion(w->line_start_cache, 0, elt);
645 /* #### This should be BUF_BEGV (b), right? */
646 return Dynarr_atp(w->line_start_cache, 0)->start;
652 vpix_motion(w->line_start_cache,
654 return Dynarr_atp(w->line_start_cache,
655 elt + vtarget)->start;
658 /* No vertical motion requested so we just return the position
659 of the beginning of the current line. */
663 *ret_vpix = vpix_motion(w->line_start_cache, elt, elt);
665 return Dynarr_atp(w->line_start_cache, elt)->start;
668 RETURN_NOT_REACHED(0) /* shut up compiler */
671 /*****************************************************************************
674 Given a starting position ORIG, move point VTARGET lines in WINDOW.
675 Returns the new value for point. If the arg ret_vpos is not nil, it is
676 taken to be a pointer to an int and the number of lines actually moved is
678 ****************************************************************************/
679 Bufpos vmotion(struct window * w, Bufpos orig, int vtarget, int *ret_vpos)
681 return vmotion_1(w, orig, vtarget, ret_vpos, NULL);
684 /* Helper for Fvertical_motion.
687 Lisp_Object vertical_motion_1(Lisp_Object lines, Lisp_Object window, int pixels)
697 window = Fselected_window(Qnil);
699 CHECK_LIVE_WINDOW(window);
702 selected = (EQ(window, Fselected_window(Qnil)));
706 orig = selected ? BUF_PT(XBUFFER(w->buffer))
707 : marker_position(w->pointm[CURRENT_DISP]);
709 vpos = pixels ? NULL : &value;
710 vpix = pixels ? &value : NULL;
712 bufpos = vmotion_1(w, orig, XINT(lines), vpos, vpix);
714 /* Note that the buffer's point is set, not the window's point. */
716 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
718 set_marker_restricted(w->pointm[CURRENT_DISP],
719 make_int(bufpos), w->buffer);
721 return make_int(value);
724 DEFUN("vertical-motion", Fvertical_motion, 1, 3, 0, /*
725 Move to start of frame line LINES lines down.
726 If LINES is negative, this is moving up.
727 Optional second argument is WINDOW to move in,
728 the default is the selected window.
730 Sets point to position found; this may be start of line
731 or just the start of a continuation line.
732 If optional third argument PIXELS is nil, returns number
733 of lines moved; may be closer to zero than LINES if beginning
734 or end of buffer was reached. If PIXELS is non-nil, the
735 vertical pixel height of the motion which took place is
736 returned instead of the actual number of lines moved. A
737 motion of zero lines returns the height of the current line.
739 Note that `vertical-motion' sets WINDOW's buffer's point, not
740 WINDOW's point. (This differs from FSF Emacs, which buggily always
741 sets current buffer's point, regardless of WINDOW.)
743 (lines, window, pixels))
745 return vertical_motion_1(lines, window, !NILP(pixels));
749 * Like vmotion() but requested and returned movement is in pixels.
750 * HOW specifies the stopping condition. Positive means move at least
751 * PIXELS. Negative means at most. Zero means as close as possible.
754 vmotion_pixels(Lisp_Object window, Bufpos start, int pixels, int how,
762 int remain, abspix, dirn;
765 line_start_cache_dynarr *cache;
770 window = Fselected_window(Qnil);
772 CHECK_LIVE_WINDOW(window);
775 eobuf = BUF_ZV(XBUFFER(w->buffer));
776 bobuf = BUF_BEGV(XBUFFER(w->buffer));
778 default_face_height_and_width(window, &defheight, NULL);
780 /* guess num lines needed in line start cache + a few extra */
781 abspix = abs(pixels);
782 needed = (abspix + defheight - 1) / defheight + 3;
784 dirn = (pixels >= 0) ? 1 : -1;
787 elt = point_in_line_start_cache(w, start, needed);
788 assert(elt >= 0); /* in the cache */
790 cache = w->line_start_cache;
791 nelt = Dynarr_length(cache);
796 /* No vertical motion requested so we just return the position
797 of the beginning of the current display line. */
798 return Dynarr_atp(cache, elt)->start;
800 if ((dirn < 0 && elt == 0 &&
801 Dynarr_atp(cache, elt)->start <= bobuf) ||
802 (dirn > 0 && elt == nelt - 1 &&
803 Dynarr_atp(cache, elt)->end >= eobuf))
804 return Dynarr_atp(cache, elt)->start;
807 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn) {
808 /* cache line we're considering moving over */
809 int ii = (dirn > 0) ? i : i - 1;
812 return Dynarr_atp(cache, i)->start;
814 line = Dynarr_atp(cache, ii)->height;
815 next = remain - line;
817 /* is stopping condition satisfied? */
818 if ((how > 0 && remain <= 0) || /* at least */
819 (how < 0 && next < 0) || /* at most */
820 (how == 0 && remain <= abs(next))) /* closest */
821 return Dynarr_atp(cache, i)->start;
823 /* moving down and nowhere left to go? */
824 if (dirn > 0 && Dynarr_atp(cache, ii)->end >= eobuf)
825 return Dynarr_atp(cache, ii)->start;
829 *motion += dirn * line;
831 /* moving up and nowhere left to go? */
832 if (dirn < 0 && Dynarr_atp(cache, ii)->start <= bobuf)
833 return Dynarr_atp(cache, ii)->start;
836 /* get here => need more cache lines. try again. */
837 assert(abs(*motion) > previous); /* progress? */
838 previous = abs(*motion);
840 lines = (pixels < 0) ? elt : (nelt - elt);
841 needed += (remain * lines + abspix - 1) / abspix + 3;
844 RETURN_NOT_REACHED(0) /* shut up compiler */
847 DEFUN("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /*
848 Move to start of frame line PIXELS vertical pixels down.
849 If PIXELS is negative, this is moving up.
850 The actual vertical motion in pixels is returned.
852 Optional second argument is WINDOW to move in,
853 the default is the selected window.
855 Optional third argument HOW specifies when to stop. A value
856 less than zero indicates that the motion should be no more
857 than PIXELS. A value greater than zero indicates that the
858 motion should be at least PIXELS. Any other value indicates
859 that the motion should be as close as possible to PIXELS.
861 (pixels, window, how))
871 window = Fselected_window(Qnil);
873 CHECK_LIVE_WINDOW(window);
876 selected = (EQ(window, Fselected_window(Qnil)));
880 orig = selected ? BUF_PT(XBUFFER(w->buffer))
881 : marker_position(w->pointm[CURRENT_DISP]);
883 howto = INTP(how) ? XINT(how) : 0;
885 bufpos = vmotion_pixels(window, orig, XINT(pixels), howto, &motion);
888 BUF_SET_PT(XBUFFER(w->buffer), bufpos);
890 set_marker_restricted(w->pointm[CURRENT_DISP],
891 make_int(bufpos), w->buffer);
893 return make_int(motion);
896 void syms_of_indent(void)
898 DEFSUBR(Fcurrent_indentation);
900 DEFSUBR(Fcurrent_column);
901 DEFSUBR(Fmove_to_column);
903 DEFSUBR(Fcompute_motion);
905 DEFSUBR(Fvertical_motion);
906 DEFSUBR(Fvertical_motion_pixels);
908 defsymbol(&Qcoerce, "coerce");
911 void vars_of_indent(void)
913 DEFVAR_BOOL("indent-tabs-mode", &indent_tabs_mode /*
914 *Indentation can insert tabs if this is non-nil.
915 Setting this variable automatically makes it local to the current buffer.
917 indent_tabs_mode = 1;