1 /* Indentation functions.
2 Copyright (C) 1985, 1986, 1987, 1988 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. */
31 /* Indentation can insert tabs if this is non-zero;
32 otherwise always uses spaces */
35 #define min(a, b) ((a) < (b) ? (a) : (b))
36 #define max(a, b) ((a) > (b) ? (a) : (b))
40 /* These three values memoize the current column to avoid recalculation */
41 /* Some things in set last_known_column_point to -1
42 to mark the memoized value as invalid */
43 /* Last value returned by current_column */
44 int last_known_column
;
45 /* Value of point when current_column was called */
46 int last_known_column_point
;
47 /* Value of MODIFF when current_column was called */
48 int last_known_column_modified
;
50 extern int minibuf_prompt_width
;
52 /* Get the display table to use for the current buffer. */
55 buffer_display_table ()
59 thisbuf
= current_buffer
->display_table
;
60 if (XTYPE (thisbuf
) == Lisp_Vector
61 && XVECTOR (thisbuf
)->size
== DISP_TABLE_SIZE
)
62 return XVECTOR (thisbuf
);
63 if (XTYPE (Vstandard_display_table
) == Lisp_Vector
64 && XVECTOR (Vstandard_display_table
)->size
== DISP_TABLE_SIZE
)
65 return XVECTOR (Vstandard_display_table
);
69 DEFUN ("current-column", Fcurrent_column
, Scurrent_column
, 0, 0, 0,
70 "Return the horizontal position of point. Beginning of line is column 0.\n\
71 This is calculated by adding together the widths of all the displayed\n\
72 representations of the character between the start of the previous line\n\
73 and point. (eg control characters will have a width of 2 or 4, tabs\n\
74 will have a variable width)\n\
75 Ignores finite width of screen, which means that this function may return\n\
76 values greater than (screen-width).\n\
77 Whether the line is visible (if `selective-display' is t) has no effect;\n\
78 however, ^M is treated as end of line when `selective-display' is t.")
82 XFASTINT (temp
) = current_column ();
86 /* Cancel any recorded value of the horizontal position. */
88 invalidate_current_column ()
90 last_known_column_point
= 0;
97 register unsigned char *ptr
, *stop
;
98 register int tab_seen
;
101 register int tab_width
= XINT (current_buffer
->tab_width
);
102 int ctl_arrow
= !NULL (current_buffer
->ctl_arrow
);
103 register struct Lisp_Vector
*dp
= buffer_display_table ();
106 if (point
== last_known_column_point
107 && MODIFF
== last_known_column_modified
)
108 return last_known_column
;
110 /* Make a pointer for decrementing through the chars before point. */
111 ptr
= &FETCH_CHAR (point
- 1) + 1;
112 /* Make a pointer to where consecutive chars leave off,
113 going backwards from point. */
116 else if (point
<= GPT
|| BEGV
> GPT
)
121 if (tab_width
<= 0 || tab_width
> 20) tab_width
= 8;
123 col
= 0, tab_seen
= 0, post_tab
= 0;
129 /* We stopped either for the beginning of the buffer
131 if (ptr
== BEGV_ADDR
)
133 /* It was the gap. Jump back over it. */
136 /* Check whether that brings us to beginning of buffer. */
137 if (BEGV
>= GPT
) break;
141 if (c
>= 040 && c
< 0177
142 && (dp
== 0 || XTYPE (DISP_CHAR_ROPE (dp
, c
)) != Lisp_String
))
148 else if (c
== '\r' && EQ (current_buffer
->selective_display
, Qt
))
153 col
= ((col
+ tab_width
) / tab_width
) * tab_width
;
159 else if (dp
!= 0 && XTYPE (DISP_CHAR_ROPE (dp
, c
)) == Lisp_String
)
160 col
+= XSTRING (DISP_CHAR_ROPE (dp
, c
))->size
/ sizeof (GLYPH
);
162 col
+= (ctl_arrow
&& c
< 0200) ? 2 : 4;
167 col
= ((col
+ tab_width
) / tab_width
) * tab_width
;
171 last_known_column
= col
;
172 last_known_column_point
= point
;
173 last_known_column_modified
= MODIFF
;
179 DEFUN ("indent-to", Findent_to
, Sindent_to
, 1, 2, "NIndent to column: ",
180 "Indent from point with tabs and spaces until COLUMN is reached.\n\
181 Optional second argument MIN says always do at least MIN spaces\n\
182 even if that goes past COLUMN; by default, MIN is zero.")
184 Lisp_Object col
, minimum
;
187 register int fromcol
;
188 register int tab_width
= XINT (current_buffer
->tab_width
);
190 CHECK_NUMBER (col
, 0);
192 XFASTINT (minimum
) = 0;
193 CHECK_NUMBER (minimum
, 1);
195 fromcol
= current_column ();
196 mincol
= fromcol
+ XINT (minimum
);
197 if (mincol
< XINT (col
)) mincol
= XINT (col
);
199 if (fromcol
== mincol
)
200 return make_number (mincol
);
202 if (tab_width
<= 0 || tab_width
> 20) tab_width
= 8;
204 if (indent_tabs_mode
)
207 XFASTINT (n
) = mincol
/ tab_width
- fromcol
/ tab_width
;
208 if (XFASTINT (n
) != 0)
210 Finsert_char (make_number ('\t'), n
);
212 fromcol
= (mincol
/ tab_width
) * tab_width
;
216 XFASTINT (col
) = mincol
- fromcol
;
217 Finsert_char (make_number (' '), col
);
219 last_known_column
= mincol
;
220 last_known_column_point
= point
;
221 last_known_column_modified
= MODIFF
;
223 XSETINT (col
, mincol
);
227 DEFUN ("current-indentation", Fcurrent_indentation
, Scurrent_indentation
,
229 "Return the indentation of the current line.\n\
230 This is the horizontal position of the character\n\
231 following any initial whitespace.")
236 XFASTINT (val
) = position_indentation (find_next_newline (point
, -1));
240 position_indentation (pos
)
243 register int column
= 0;
244 register int tab_width
= XINT (current_buffer
->tab_width
);
245 register unsigned char *p
;
246 register unsigned char *stop
;
248 if (tab_width
<= 0 || tab_width
> 20) tab_width
= 8;
250 stop
= &FETCH_CHAR (BUFFER_CEILING_OF (pos
)) + 1;
251 p
= &FETCH_CHAR (pos
);
258 pos
+= p
- &FETCH_CHAR (pos
);
259 p
= &FETCH_CHAR (pos
);
260 stop
= &FETCH_CHAR (BUFFER_CEILING_OF (pos
)) + 1;
268 column
+= tab_width
- column
% tab_width
;
276 DEFUN ("move-to-column", Fmove_to_column
, Smove_to_column
, 1, 2, 0,
277 "Move point to column COLUMN in the current line.\n\
278 The column of a character is calculated by adding together the widths\n\
279 as displayed of the previous characters in the line.\n\
280 This function ignores line-continuation;\n\
281 there is no upper limit on the column number a character can have\n\
282 and horizontal scrolling has no effect.\n\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
= !NULL (current_buffer
->ctl_arrow
);
297 register struct Lisp_Vector
*dp
= buffer_display_table ();
303 if (tab_width
<= 0 || tab_width
> 20) 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_ROPE (dp
, c
)) == Lisp_String
)
334 col
+= XSTRING (DISP_CHAR_ROPE (dp
, c
))->size
/ sizeof (GLYPH
);
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 (!NULL (force
) && col
> goal
&& c
== '\t' && prev_col
< goal
)
349 del_range (point
- 1, point
);
350 Findent_to (make_number (col
- 1));
355 /* If line ends prematurely, add space to the end. */
356 if (col
< goal
&& !NULL (force
))
357 Findent_to (make_number (col
= goal
));
359 last_known_column
= col
;
360 last_known_column_point
= point
;
361 last_known_column_modified
= MODIFF
;
363 XFASTINT (val
) = col
;
367 struct position val_compute_motion
;
369 /* Scan the current buffer forward from offset FROM, pretending that
370 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
371 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
372 and return the ending buffer position and screen location.
374 WIDTH is the number of columns available to display text;
375 compute_motion uses this to handle continuation lines and such.
376 HSCROLL is the number of columns not being displayed at the left
377 margin; this is usually taken from a window's hscroll member.
378 TAB_OFFSET is a mysterious value, perhaps the number of columns of
379 the first tab that aren't being displayed, perhaps because of a
380 continuation line or something.
382 compute_motion returns a pointer to a struct position. The bufpos
383 member gives the buffer position at the end of the scan, and hpos
384 and vpos give its cartesian location. I'm not clear on what the
387 For example, to find the buffer position of column COL of line LINE
388 of a certain window, pass the window's starting location as FROM
389 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
390 Pass the buffer's ZV as TO, to limit the scan to the end of the
391 visible section of the buffer, and pass LINE and COL as TOVPOS and
394 When displaying in window w, a typical formula for WIDTH is:
397 - (window_width + window_left != screen_width)
400 window_width is XFASTINT (w->width),
401 window_left is XFASTINT (w->left),
402 and screen_width = SCREEN_WIDTH (XSCREEN (window->screen))
404 This accounts for the continuation-line backslashes, and the window
405 borders if the window is split vertically. */
408 compute_motion (from
, fromvpos
, fromhpos
, to
, tovpos
, tohpos
, width
, hscroll
, tab_offset
)
409 int from
, fromvpos
, fromhpos
, to
, tovpos
, tohpos
;
411 int hscroll
, tab_offset
;
413 /* Note that `cpos' is CURRENT_VPOS << SHORTBITS + CURRENT_HPOS,
414 and that CURRENT_HPOS may be negative. Use these macros
415 to extract the hpos or the vpos from cpos or anything like it.
417 #ifndef SHORT_CAST_BUG
418 #define HPOS(VAR) (short) (VAR)
420 #define HPOS(VAR) (((VAR) & (1 << (SHORTBITS - 1)) \
421 ? ~((1 << SHORTBITS) - 1) : 0) \
422 | (VAR) & ((1 << SHORTBITS) - 1))
423 /* #define HPOS(VAR) (((VAR) & 0x8000 ? 0xffff0000 : 0) | ((VAR) & 0xffff)) */
424 #endif /* SHORT_CAST_BUG */
426 #define VPOS(VAR) (((VAR) >> SHORTBITS) + (HPOS (VAR) < 0))
429 #ifndef TAHOE_REGISTER_BUG
431 #endif /* TAHOE_REGISTER_BUG */
432 int cpos
= fromhpos
+ (fromvpos
<< SHORTBITS
);
433 register int target
= tohpos
+ (tovpos
<< SHORTBITS
);
436 register int tab_width
= XFASTINT (current_buffer
->tab_width
);
437 register int ctl_arrow
= !NULL (current_buffer
->ctl_arrow
);
438 register struct Lisp_Vector
*dp
= buffer_display_table ();
440 = XTYPE (current_buffer
->selective_display
) == Lisp_Int
441 ? XINT (current_buffer
->selective_display
)
442 : !NULL (current_buffer
->selective_display
) ? -1 : 0;
445 = (selective
&& dp
&& XTYPE (DISP_INVIS_ROPE (dp
)) == Lisp_String
446 ? XSTRING (DISP_INVIS_ROPE (dp
))->size
/ sizeof (GLYPH
) : 0);
448 if (tab_width
<= 0 || tab_width
> 20) tab_width
= 8;
449 for (pos
= from
; pos
< to
&& cpos
< target
; pos
++)
452 c
= FETCH_CHAR (pos
);
453 if (c
>= 040 && c
< 0177
454 && (dp
== 0 || XTYPE (DISP_CHAR_ROPE (dp
, c
)) != Lisp_String
))
459 - HPOS (cpos
+ tab_offset
+ hscroll
- (hscroll
> 0)
460 /* Add tab_width here to make sure positive.
461 cpos can be negative after continuation
462 but can't be less than -tab_width. */
468 if (selective
> 0 && position_indentation (pos
+ 1) >= selective
)
470 /* Skip any number of invisible lines all at once */
473 while (++pos
< to
&& FETCH_CHAR(pos
) != '\n');
475 while (selective
> 0 && position_indentation (pos
+ 1) >= selective
);
477 /* Allow for the " ..." that is displayed for them. */
480 cpos
+= selective_rlen
;
481 if (HPOS (cpos
) >= width
)
482 cpos
-= HPOS (cpos
) - width
;
486 cpos
+= (1 << SHORTBITS
) - HPOS (cpos
);
488 if (hscroll
> 0) cpos
++; /* Count the ! on column 0 */
491 else if (c
== CR
&& selective
< 0)
493 /* In selective display mode,
494 everything from a ^M to the end of the line is invisible */
495 while (pos
< to
&& FETCH_CHAR(pos
) != '\n') pos
++;
497 /* Allow for the " ..." that is displayed for them. */
500 cpos
+= selective_rlen
;
501 if (HPOS (cpos
) >= width
)
502 cpos
-= HPOS (cpos
) - width
;
505 else if (dp
!= 0 && XTYPE (DISP_CHAR_ROPE (dp
, c
)) == Lisp_String
)
506 cpos
+= XSTRING (DISP_CHAR_ROPE (dp
, c
))->size
/ sizeof (GLYPH
);
508 cpos
+= (ctl_arrow
&& c
< 0200) ? 2 : 4;
510 if (HPOS (cpos
) >= width
511 && (HPOS (cpos
) > width
513 && FETCH_CHAR (pos
+ 1) != '\n')))
518 || (truncate_partial_width_windows
519 && width
+ 1 < SCREEN_WIDTH (selected_screen
))
520 || !NULL (current_buffer
->truncate_lines
))
522 while (pos
< to
&& FETCH_CHAR(pos
) != '\n') pos
++;
527 cpos
+= (1 << SHORTBITS
) - width
;
534 val_compute_motion
.bufpos
= pos
;
535 val_compute_motion
.hpos
= HPOS (cpos
);
536 val_compute_motion
.vpos
= VPOS (cpos
);
537 val_compute_motion
.prevhpos
= HPOS (prevpos
);
539 /* Nonzero if have just continued a line */
540 val_compute_motion
.contin
542 && (val_compute_motion
.vpos
!= VPOS (prevpos
))
545 return &val_compute_motion
;
551 /* Return the column of position POS in window W's buffer,
552 rounded down to a multiple of the internal width of W.
553 This is the amount of indentation of position POS
554 that is not visible in its horizontal position in the window. */
557 pos_tab_offset (w
, pos
)
563 int width
= XFASTINT (w
->width
) - 1
564 - (XFASTINT (w
->width
) + XFASTINT (w
->left
)
565 != SCREEN_WIDTH (XSCREEN (w
->screen
)));
567 if (pos
== BEGV
|| FETCH_CHAR (pos
- 1) == '\n')
570 col
= current_column ();
572 return col
- (col
% width
);
575 /* start_hpos is the hpos of the first character of the buffer:
576 zero except for the minibuffer window,
577 where it is the width of the prompt. */
579 struct position val_vmotion
;
582 vmotion (from
, vtarget
, width
, hscroll
, window
)
583 register int from
, vtarget
, width
;
588 /* vpos is cumulative vertical position, changed as from is changed */
589 register int vpos
= 0;
590 register int prevline
;
592 int lmargin
= hscroll
> 0 ? 1 - hscroll
: 0;
594 = XTYPE (current_buffer
->selective_display
) == Lisp_Int
595 ? XINT (current_buffer
->selective_display
)
596 : !NULL (current_buffer
->selective_display
) ? -1 : 0;
597 int start_hpos
= (EQ (window
, minibuf_window
) ? minibuf_prompt_width
: 0);
602 /* Moving downward is simple, but must calculate from beg of line
603 to determine hpos of starting point */
604 if (from
> BEGV
&& FETCH_CHAR (from
- 1) != '\n')
606 prevline
= find_next_newline (from
, -1);
609 && position_indentation (prevline
) >= selective
)
610 prevline
= find_next_newline (prevline
- 1, -1);
611 pos
= *compute_motion (prevline
, 0,
612 lmargin
+ (prevline
== 1 ? start_hpos
: 0),
618 pos
.hpos
= lmargin
+ (from
== 1 ? start_hpos
: 0);
621 return compute_motion (from
, vpos
, pos
.hpos
,
622 ZV
, vtarget
, - (1 << (SHORTBITS
- 1)),
623 width
, hscroll
, pos
.vpos
* width
);
626 /* To move upward, go a line at a time until
627 we have gone at least far enough */
631 while ((vpos
> vtarget
|| first
) && from
> BEGV
)
636 prevline
= find_next_newline (prevline
- 1, -1);
639 || position_indentation (prevline
) < selective
)
642 pos
= *compute_motion (prevline
, 0,
643 lmargin
+ (prevline
== 1 ? start_hpos
: 0),
651 /* If we made exactly the desired vertical distance,
652 or if we hit beginning of buffer,
653 return point found */
656 val_vmotion
.bufpos
= from
;
657 val_vmotion
.vpos
= vpos
;
658 val_vmotion
.hpos
= lmargin
;
659 val_vmotion
.contin
= 0;
660 val_vmotion
.prevhpos
= 0;
664 /* Otherwise find the correct spot by moving down */
668 DEFUN ("vertical-motion", Fvertical_motion
, Svertical_motion
, 1, 1, 0,
669 "Move to start of screen line LINES lines down.\n\
670 If LINES is negative, this is moving up.\n\
671 Sets point to position found; this may be start of line\n\
672 or just the start of a continuation line.\n\
673 Returns number of lines moved; may be closer to zero than LINES\n\
674 if beginning or end of buffer was reached.")
679 register struct window
*w
= XWINDOW (selected_window
);
680 int width
= XFASTINT (w
->width
) - 1
681 - (XFASTINT (w
->width
) + XFASTINT (w
->left
)
682 != SCREEN_WIDTH (XSCREEN (w
->screen
)));
684 CHECK_NUMBER (lines
, 0);
686 pos
= *vmotion (point
, XINT (lines
), width
,
687 /* Not XFASTINT since perhaps could be negative */
688 XINT (w
->hscroll
), selected_window
);
691 return make_number (pos
.vpos
);
696 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode
,
697 "*Indentation can insert tabs if this is non-nil.\n\
698 Setting this variable automatically makes it local to the current buffer.");
699 indent_tabs_mode
= 1;
701 defsubr (&Scurrent_indentation
);
702 defsubr (&Sindent_to
);
703 defsubr (&Scurrent_column
);
704 defsubr (&Smove_to_column
);
705 defsubr (&Svertical_motion
);