1 /* Indentation functions.
2 Copyright (C) 1985, 1986, 1987, 1988, 1993 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
30 #include "intervals.h"
32 /* Indentation can insert tabs if this is non-zero;
33 otherwise always uses spaces */
36 #define min(a, b) ((a) < (b) ? (a) : (b))
37 #define max(a, b) ((a) > (b) ? (a) : (b))
41 /* These three values memoize the current column to avoid recalculation */
42 /* Some things in set last_known_column_point to -1
43 to mark the memoized value as invalid */
44 /* Last value returned by current_column */
45 int last_known_column
;
46 /* Value of point when current_column was called */
47 int last_known_column_point
;
48 /* Value of MODIFF when current_column was called */
49 int last_known_column_modified
;
51 /* Get the display table to use for the current buffer. */
54 buffer_display_table ()
58 thisbuf
= current_buffer
->display_table
;
59 if (XTYPE (thisbuf
) == Lisp_Vector
60 && XVECTOR (thisbuf
)->size
== DISP_TABLE_SIZE
)
61 return XVECTOR (thisbuf
);
62 if (XTYPE (Vstandard_display_table
) == Lisp_Vector
63 && XVECTOR (Vstandard_display_table
)->size
== DISP_TABLE_SIZE
)
64 return XVECTOR (Vstandard_display_table
);
68 DEFUN ("current-column", Fcurrent_column
, Scurrent_column
, 0, 0, 0,
69 "Return the horizontal position of point. Beginning of line is column 0.\n\
70 This is calculated by adding together the widths of all the displayed\n\
71 representations of the character between the start of the previous line\n\
72 and point. (eg control characters will have a width of 2 or 4, tabs\n\
73 will have a variable width)\n\
74 Ignores finite width of frame, which means that this function may return\n\
75 values greater than (frame-width).\n\
76 Whether the line is visible (if `selective-display' is t) has no effect;\n\
77 however, ^M is treated as end of line when `selective-display' is t.")
81 XFASTINT (temp
) = current_column ();
85 /* Cancel any recorded value of the horizontal position. */
87 invalidate_current_column ()
89 last_known_column_point
= 0;
96 register unsigned char *ptr
, *stop
;
97 register int tab_seen
;
100 register int tab_width
= XINT (current_buffer
->tab_width
);
101 int ctl_arrow
= !NILP (current_buffer
->ctl_arrow
);
102 register struct Lisp_Vector
*dp
= buffer_display_table ();
105 if (point
== last_known_column_point
106 && MODIFF
== last_known_column_modified
)
107 return last_known_column
;
109 /* Make a pointer for decrementing through the chars before point. */
110 ptr
= &FETCH_CHAR (point
- 1) + 1;
111 /* Make a pointer to where consecutive chars leave off,
112 going backwards from point. */
115 else if (point
<= GPT
|| BEGV
> GPT
)
120 if (tab_width
<= 0 || tab_width
> 1000) tab_width
= 8;
122 col
= 0, tab_seen
= 0, post_tab
= 0;
128 /* We stopped either for the beginning of the buffer
130 if (ptr
== BEGV_ADDR
)
132 /* It was the gap. Jump back over it. */
135 /* Check whether that brings us to beginning of buffer. */
136 if (BEGV
>= GPT
) break;
140 if (c
>= 040 && c
< 0177
141 && (dp
== 0 || XTYPE (DISP_CHAR_VECTOR (dp
, c
)) != Lisp_Vector
))
147 else if (c
== '\r' && EQ (current_buffer
->selective_display
, Qt
))
152 col
= ((col
+ tab_width
) / tab_width
) * tab_width
;
158 else if (dp
!= 0 && XTYPE (DISP_CHAR_VECTOR (dp
, c
)) == Lisp_Vector
)
159 col
+= XVECTOR (DISP_CHAR_VECTOR (dp
, c
))->size
;
161 col
+= (ctl_arrow
&& c
< 0200) ? 2 : 4;
166 col
= ((col
+ tab_width
) / tab_width
) * tab_width
;
170 last_known_column
= col
;
171 last_known_column_point
= point
;
172 last_known_column_modified
= MODIFF
;
178 DEFUN ("indent-to", Findent_to
, Sindent_to
, 1, 2, "NIndent to column: ",
179 "Indent from point with tabs and spaces until COLUMN is reached.\n\
180 Optional second argument MIN says always do at least MIN spaces\n\
181 even if that goes past COLUMN; by default, MIN is zero.")
183 Lisp_Object col
, minimum
;
186 register int fromcol
;
187 register int tab_width
= XINT (current_buffer
->tab_width
);
189 CHECK_NUMBER (col
, 0);
191 XFASTINT (minimum
) = 0;
192 CHECK_NUMBER (minimum
, 1);
194 fromcol
= current_column ();
195 mincol
= fromcol
+ XINT (minimum
);
196 if (mincol
< XINT (col
)) mincol
= XINT (col
);
198 if (fromcol
== mincol
)
199 return make_number (mincol
);
201 if (tab_width
<= 0 || tab_width
> 1000) tab_width
= 8;
203 if (indent_tabs_mode
)
206 XFASTINT (n
) = mincol
/ tab_width
- fromcol
/ tab_width
;
207 if (XFASTINT (n
) != 0)
209 Finsert_char (make_number ('\t'), n
);
211 fromcol
= (mincol
/ tab_width
) * tab_width
;
215 XFASTINT (col
) = mincol
- fromcol
;
216 Finsert_char (make_number (' '), col
);
218 last_known_column
= mincol
;
219 last_known_column_point
= point
;
220 last_known_column_modified
= MODIFF
;
222 XSETINT (col
, mincol
);
226 DEFUN ("current-indentation", Fcurrent_indentation
, Scurrent_indentation
,
228 "Return the indentation of the current line.\n\
229 This is the horizontal position of the character\n\
230 following any initial whitespace.")
235 XFASTINT (val
) = position_indentation (find_next_newline (point
, -1));
239 position_indentation (pos
)
242 register int column
= 0;
243 register int tab_width
= XINT (current_buffer
->tab_width
);
244 register unsigned char *p
;
245 register unsigned char *stop
;
247 if (tab_width
<= 0 || tab_width
> 1000) tab_width
= 8;
249 stop
= &FETCH_CHAR (BUFFER_CEILING_OF (pos
)) + 1;
250 p
= &FETCH_CHAR (pos
);
257 pos
+= p
- &FETCH_CHAR (pos
);
258 p
= &FETCH_CHAR (pos
);
259 stop
= &FETCH_CHAR (BUFFER_CEILING_OF (pos
)) + 1;
267 column
+= tab_width
- column
% tab_width
;
275 DEFUN ("move-to-column", Fmove_to_column
, Smove_to_column
, 1, 2, 0,
276 "Move point to column COLUMN in the current line.\n\
277 The column of a character is calculated by adding together the widths\n\
278 as displayed of the previous characters in the line.\n\
279 This function ignores line-continuation;\n\
280 there is no upper limit on the column number a character can have\n\
281 and horizontal scrolling has no effect.\n\
283 If specified column is within a character, point goes after that character.\n\
284 If it's past end of line, point goes to end of line.\n\n\
285 A non-nil second (optional) argument FORCE means, if the line\n\
286 is too short to reach column COLUMN then add spaces/tabs to get there,\n\
287 and if COLUMN is in the middle of a tab character, change it to spaces.")
289 Lisp_Object column
, force
;
292 register int col
= current_column ();
295 register int tab_width
= XINT (current_buffer
->tab_width
);
296 register int ctl_arrow
= !NILP (current_buffer
->ctl_arrow
);
297 register struct Lisp_Vector
*dp
= buffer_display_table ();
303 if (tab_width
<= 0 || tab_width
> 1000) tab_width
= 8;
304 CHECK_NATNUM (column
, 0);
305 goal
= XINT (column
);
311 /* If we're starting past the desired column,
312 back up to beginning of line and scan from there. */
315 pos
= find_next_newline (pos
, -1);
319 while (col
< goal
&& pos
< end
)
321 c
= FETCH_CHAR (pos
);
324 if (c
== '\r' && EQ (current_buffer
->selective_display
, Qt
))
331 col
= col
/ tab_width
* tab_width
;
333 else if (dp
!= 0 && XTYPE (DISP_CHAR_VECTOR (dp
, c
)) == Lisp_Vector
)
334 col
+= XVECTOR (DISP_CHAR_VECTOR (dp
, c
))->size
;
335 else if (ctl_arrow
&& (c
< 040 || c
== 0177))
337 else if (c
< 040 || c
>= 0177)
345 /* If a tab char made us overshoot, change it to spaces
346 and scan through it again. */
347 if (!NILP (force
) && col
> goal
&& c
== '\t' && prev_col
< goal
)
351 del_range (point
- 1, point
);
352 Findent_to (make_number (goal
), Qnil
);
354 Findent_to (make_number (col
), Qnil
);
356 /* Set the last_known... vars consistently. */
360 /* If line ends prematurely, add space to the end. */
361 if (col
< goal
&& !NILP (force
))
362 Findent_to (make_number (col
= goal
), Qnil
);
364 last_known_column
= col
;
365 last_known_column_point
= point
;
366 last_known_column_modified
= MODIFF
;
368 XFASTINT (val
) = col
;
372 struct position val_compute_motion
;
374 /* Scan the current buffer forward from offset FROM, pretending that
375 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
376 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
377 and return the ending buffer position and screen location.
379 WIDTH is the number of columns available to display text;
380 compute_motion uses this to handle continuation lines and such.
381 HSCROLL is the number of columns not being displayed at the left
382 margin; this is usually taken from a window's hscroll member.
383 TAB_OFFSET is the number of columns of the first tab that aren't
384 being displayed, perhaps because of a continuation line or
387 compute_motion returns a pointer to a struct position. The bufpos
388 member gives the buffer position at the end of the scan, and hpos
389 and vpos give its cartesian location. I'm not clear on what the
392 For example, to find the buffer position of column COL of line LINE
393 of a certain window, pass the window's starting location as FROM
394 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
395 Pass the buffer's ZV as TO, to limit the scan to the end of the
396 visible section of the buffer, and pass LINE and COL as TOVPOS and
399 When displaying in window w, a typical formula for WIDTH is:
402 - (has_vertical_scroll_bars
403 ? VERTICAL_SCROLL_BAR_WIDTH
404 : (window_width + window_left != frame_width))
407 window_width is XFASTINT (w->width),
408 window_left is XFASTINT (w->left),
409 has_vertical_scroll_bars is
410 FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
411 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
414 window_internal_width (w) - 1
416 The `-1' accounts for the continuation-line backslashes; the rest
417 accounts for window borders if the window is split vertically, and
418 the scroll bars if the frame supports them. */
421 compute_motion (from
, fromvpos
, fromhpos
, to
, tovpos
, tohpos
, width
, hscroll
, tab_offset
)
422 int from
, fromvpos
, fromhpos
, to
, tovpos
, tohpos
;
424 int hscroll
, tab_offset
;
426 register int hpos
= fromhpos
;
427 register int vpos
= fromvpos
;
431 register int tab_width
= XFASTINT (current_buffer
->tab_width
);
432 register int ctl_arrow
= !NILP (current_buffer
->ctl_arrow
);
433 register struct Lisp_Vector
*dp
= buffer_display_table ();
435 = XTYPE (current_buffer
->selective_display
) == Lisp_Int
436 ? XINT (current_buffer
->selective_display
)
437 : !NILP (current_buffer
->selective_display
) ? -1 : 0;
438 int prev_vpos
, prev_hpos
;
440 = (selective
&& dp
&& XTYPE (DISP_INVIS_VECTOR (dp
)) == Lisp_Vector
441 ? XVECTOR (DISP_INVIS_VECTOR (dp
))->size
: 0);
442 #ifdef USE_TEXT_PROPERTIES
443 /* The next location where the `invisible' property changes */
444 int next_invisible
= from
;
445 Lisp_Object prop
, position
;
448 if (tab_width
<= 0 || tab_width
> 1000) tab_width
= 8;
449 for (pos
= from
; pos
< to
; pos
++)
451 /* Stop if past the target screen position. */
453 || (vpos
== tovpos
&& hpos
>= tohpos
))
459 #ifdef USE_TEXT_PROPERTIES
460 /* if the `invisible' property is set, we can skip to
461 the next property change */
462 while (pos
== next_invisible
&& pos
< to
)
464 XFASTINT (position
) = pos
;
465 prop
= Fget_text_property (position
,
471 end
= Fnext_single_property_change (position
,
475 next_invisible
= XINT (end
);
479 pos
= next_invisible
;
485 c
= FETCH_CHAR (pos
);
486 if (c
>= 040 && c
< 0177
487 && (dp
== 0 || XTYPE (DISP_CHAR_VECTOR (dp
, c
)) != Lisp_Vector
))
491 hpos
+= tab_width
- ((hpos
+ tab_offset
+ hscroll
- (hscroll
> 0)
492 /* Add tab_width here to make sure positive.
493 hpos can be negative after continuation
494 but can't be less than -tab_width. */
500 if (selective
> 0 && position_indentation (pos
+ 1) >= selective
)
502 /* Skip any number of invisible lines all at once */
505 while (++pos
< to
&& FETCH_CHAR (pos
) != '\n');
507 while (pos
< to
&& position_indentation (pos
+ 1) >= selective
);
509 /* Allow for the " ..." that is displayed for them. */
512 hpos
+= selective_rlen
;
516 /* We have skipped the invis text, but not the newline after. */
520 /* A visible line. */
524 if (hscroll
> 0) hpos
++; /* Count the ! on column 0 */
528 else if (c
== CR
&& selective
< 0)
530 /* In selective display mode,
531 everything from a ^M to the end of the line is invisible */
532 while (pos
< to
&& FETCH_CHAR (pos
) != '\n') pos
++;
533 /* Stop *before* the real newline. */
535 /* Allow for the " ..." that is displayed for them. */
538 hpos
+= selective_rlen
;
543 else if (dp
!= 0 && XTYPE (DISP_CHAR_VECTOR (dp
, c
)) == Lisp_Vector
)
544 hpos
+= XVECTOR (DISP_CHAR_VECTOR (dp
, c
))->size
;
546 hpos
+= (ctl_arrow
&& c
< 0200) ? 2 : 4;
548 /* Handle right margin. */
552 && FETCH_CHAR (pos
+ 1) != '\n')))
555 || (vpos
== tovpos
&& hpos
>= tohpos
))
558 || (truncate_partial_width_windows
559 && width
+ 1 < FRAME_WIDTH (selected_frame
))
560 || !NILP (current_buffer
->truncate_lines
))
562 /* Truncating: skip to newline. */
563 while (pos
< to
&& FETCH_CHAR (pos
) != '\n') pos
++;
578 val_compute_motion
.bufpos
= pos
;
579 val_compute_motion
.hpos
= hpos
;
580 val_compute_motion
.vpos
= vpos
;
581 val_compute_motion
.prevhpos
= prev_hpos
;
583 /* Nonzero if have just continued a line */
584 val_compute_motion
.contin
586 && (val_compute_motion
.vpos
!= prev_vpos
)
589 return &val_compute_motion
;
593 /* Return the column of position POS in window W's buffer,
594 rounded down to a multiple of the internal width of W.
595 This is the amount of indentation of position POS
596 that is not visible in its horizontal position in the window. */
599 pos_tab_offset (w
, pos
)
605 int width
= window_internal_width (w
) - 1;
607 if (pos
== BEGV
|| FETCH_CHAR (pos
- 1) == '\n')
610 col
= current_column ();
612 return col
- (col
% width
);
615 /* start_hpos is the hpos of the first character of the buffer:
616 zero except for the minibuffer window,
617 where it is the width of the prompt. */
619 struct position val_vmotion
;
622 vmotion (from
, vtarget
, width
, hscroll
, window
)
623 register int from
, vtarget
, width
;
628 /* vpos is cumulative vertical position, changed as from is changed */
629 register int vpos
= 0;
630 register int prevline
;
632 int lmargin
= hscroll
> 0 ? 1 - hscroll
: 0;
634 = XTYPE (current_buffer
->selective_display
) == Lisp_Int
635 ? XINT (current_buffer
->selective_display
)
636 : !NILP (current_buffer
->selective_display
) ? -1 : 0;
637 int start_hpos
= (EQ (window
, minibuf_window
) ? minibuf_prompt_width
: 0);
642 /* Moving downward is simple, but must calculate from beg of line
643 to determine hpos of starting point */
644 if (from
> BEGV
&& FETCH_CHAR (from
- 1) != '\n')
646 prevline
= find_next_newline (from
, -1);
647 while (prevline
> BEGV
649 && position_indentation (prevline
) >= selective
)
650 #ifdef USE_TEXT_PROPERTIES
651 /* watch out for newlines with `invisible' property */
652 || ! NILP (Fget_text_property (XFASTINT (prevline
),
657 prevline
= find_next_newline (prevline
- 1, -1);
658 pos
= *compute_motion (prevline
, 0,
659 lmargin
+ (prevline
== 1 ? start_hpos
: 0),
660 from
, 1 << (INTBITS
- 2), 0,
665 pos
.hpos
= lmargin
+ (from
== 1 ? start_hpos
: 0);
668 return compute_motion (from
, vpos
, pos
.hpos
,
669 ZV
, vtarget
, - (1 << (INTBITS
- 2)),
670 width
, hscroll
, pos
.vpos
* width
);
673 /* To move upward, go a line at a time until
674 we have gone at least far enough */
678 while ((vpos
> vtarget
|| first
) && from
> BEGV
)
683 prevline
= find_next_newline (prevline
- 1, -1);
686 || position_indentation (prevline
) < selective
)
687 #ifdef USE_TEXT_PROPERTIES
688 /* watch out for newlines with `invisible' property */
689 && NILP (Fget_text_property (XFASTINT (prevline
),
696 pos
= *compute_motion (prevline
, 0,
697 lmargin
+ (prevline
== 1 ? start_hpos
: 0),
698 from
, 1 << (INTBITS
- 2), 0,
705 /* If we made exactly the desired vertical distance,
706 or if we hit beginning of buffer,
707 return point found */
710 val_vmotion
.bufpos
= from
;
711 val_vmotion
.vpos
= vpos
;
712 val_vmotion
.hpos
= lmargin
;
713 val_vmotion
.contin
= 0;
714 val_vmotion
.prevhpos
= 0;
718 /* Otherwise find the correct spot by moving down */
722 DEFUN ("vertical-motion", Fvertical_motion
, Svertical_motion
, 1, 1, 0,
723 "Move to start of screen line LINES lines down.\n\
724 If LINES is negative, this is moving up.\n\
725 Sets point to position found; this may be start of line\n\
726 or just the start of a continuation line.\n\
727 Returns number of lines moved; may be closer to zero than LINES\n\
728 if beginning or end of buffer was reached.")
733 register struct window
*w
= XWINDOW (selected_window
);
734 int width
= window_internal_width (w
) - 1;
736 CHECK_NUMBER (lines
, 0);
738 pos
= *vmotion (point
, XINT (lines
), width
,
739 /* Not XFASTINT since perhaps could be negative */
740 XINT (w
->hscroll
), selected_window
);
743 return make_number (pos
.vpos
);
748 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode
,
749 "*Indentation can insert tabs if this is non-nil.\n\
750 Setting this variable automatically makes it local to the current buffer.");
751 indent_tabs_mode
= 1;
753 defsubr (&Scurrent_indentation
);
754 defsubr (&Sindent_to
);
755 defsubr (&Scurrent_column
);
756 defsubr (&Smove_to_column
);
757 defsubr (&Svertical_motion
);