*** empty log message ***
[emacs.git] / src / indent.c
blobe9031d54069c70672d96a7c76dc3a30dfa90ea28
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)
9 any later version.
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. */
21 #include "config.h"
22 #include "lisp.h"
23 #include "buffer.h"
24 #include "indent.h"
25 #include "screen.h"
26 #include "window.h"
27 #include "termchar.h"
28 #include "termopts.h"
29 #include "disptab.h"
31 /* Indentation can insert tabs if this is non-zero;
32 otherwise always uses spaces */
33 int indent_tabs_mode;
35 #define min(a, b) ((a) < (b) ? (a) : (b))
36 #define max(a, b) ((a) > (b) ? (a) : (b))
38 #define CR 015
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 /* Get the display table to use for the current buffer. */
52 struct Lisp_Vector *
53 buffer_display_table ()
55 Lisp_Object thisbuf;
57 thisbuf = current_buffer->display_table;
58 if (XTYPE (thisbuf) == Lisp_Vector
59 && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE)
60 return XVECTOR (thisbuf);
61 if (XTYPE (Vstandard_display_table) == Lisp_Vector
62 && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
63 return XVECTOR (Vstandard_display_table);
64 return 0;
67 DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
68 "Return the horizontal position of point. Beginning of line is column 0.\n\
69 This is calculated by adding together the widths of all the displayed\n\
70 representations of the character between the start of the previous line\n\
71 and point. (eg control characters will have a width of 2 or 4, tabs\n\
72 will have a variable width)\n\
73 Ignores finite width of screen, which means that this function may return\n\
74 values greater than (screen-width).\n\
75 Whether the line is visible (if `selective-display' is t) has no effect;\n\
76 however, ^M is treated as end of line when `selective-display' is t.")
79 Lisp_Object temp;
80 XFASTINT (temp) = current_column ();
81 return temp;
84 /* Cancel any recorded value of the horizontal position. */
86 invalidate_current_column ()
88 last_known_column_point = 0;
91 int
92 current_column ()
94 register int col;
95 register unsigned char *ptr, *stop;
96 register int tab_seen;
97 int post_tab;
98 register int c;
99 register int tab_width = XINT (current_buffer->tab_width);
100 int ctl_arrow = !NILP (current_buffer->ctl_arrow);
101 register struct Lisp_Vector *dp = buffer_display_table ();
102 int stopchar;
104 if (point == last_known_column_point
105 && MODIFF == last_known_column_modified)
106 return last_known_column;
108 /* Make a pointer for decrementing through the chars before point. */
109 ptr = &FETCH_CHAR (point - 1) + 1;
110 /* Make a pointer to where consecutive chars leave off,
111 going backwards from point. */
112 if (point == BEGV)
113 stop = ptr;
114 else if (point <= GPT || BEGV > GPT)
115 stop = BEGV_ADDR;
116 else
117 stop = GAP_END_ADDR;
119 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
121 col = 0, tab_seen = 0, post_tab = 0;
123 while (1)
125 if (ptr == stop)
127 /* We stopped either for the beginning of the buffer
128 or for the gap. */
129 if (ptr == BEGV_ADDR)
130 break;
131 /* It was the gap. Jump back over it. */
132 stop = BEGV_ADDR;
133 ptr = GPT_ADDR;
134 /* Check whether that brings us to beginning of buffer. */
135 if (BEGV >= GPT) break;
138 c = *--ptr;
139 if (c >= 040 && c < 0177
140 && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String))
142 col++;
144 else if (c == '\n')
145 break;
146 else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
147 break;
148 else if (c == '\t')
150 if (tab_seen)
151 col = ((col + tab_width) / tab_width) * tab_width;
153 post_tab += col;
154 col = 0;
155 tab_seen = 1;
157 else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String)
158 col += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH);
159 else
160 col += (ctl_arrow && c < 0200) ? 2 : 4;
163 if (tab_seen)
165 col = ((col + tab_width) / tab_width) * tab_width;
166 col += post_tab;
169 last_known_column = col;
170 last_known_column_point = point;
171 last_known_column_modified = MODIFF;
173 return col;
177 DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
178 "Indent from point with tabs and spaces until COLUMN is reached.\n\
179 Optional second argument MIN says always do at least MIN spaces\n\
180 even if that goes past COLUMN; by default, MIN is zero.")
181 (col, minimum)
182 Lisp_Object col, minimum;
184 int mincol;
185 register int fromcol;
186 register int tab_width = XINT (current_buffer->tab_width);
188 CHECK_NUMBER (col, 0);
189 if (NILP (minimum))
190 XFASTINT (minimum) = 0;
191 CHECK_NUMBER (minimum, 1);
193 fromcol = current_column ();
194 mincol = fromcol + XINT (minimum);
195 if (mincol < XINT (col)) mincol = XINT (col);
197 if (fromcol == mincol)
198 return make_number (mincol);
200 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
202 if (indent_tabs_mode)
204 Lisp_Object n;
205 XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
206 if (XFASTINT (n) != 0)
208 Finsert_char (make_number ('\t'), n);
210 fromcol = (mincol / tab_width) * tab_width;
214 XFASTINT (col) = mincol - fromcol;
215 Finsert_char (make_number (' '), col);
217 last_known_column = mincol;
218 last_known_column_point = point;
219 last_known_column_modified = MODIFF;
221 XSETINT (col, mincol);
222 return col;
225 DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
226 0, 0, 0,
227 "Return the indentation of the current line.\n\
228 This is the horizontal position of the character\n\
229 following any initial whitespace.")
232 Lisp_Object val;
234 XFASTINT (val) = position_indentation (find_next_newline (point, -1));
235 return val;
238 position_indentation (pos)
239 register int pos;
241 register int column = 0;
242 register int tab_width = XINT (current_buffer->tab_width);
243 register unsigned char *p;
244 register unsigned char *stop;
246 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
248 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
249 p = &FETCH_CHAR (pos);
250 while (1)
252 while (p == stop)
254 if (pos == ZV)
255 return column;
256 pos += p - &FETCH_CHAR (pos);
257 p = &FETCH_CHAR (pos);
258 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
260 switch (*p++)
262 case ' ':
263 column++;
264 break;
265 case '\t':
266 column += tab_width - column % tab_width;
267 break;
268 default:
269 return column;
274 DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
275 "Move point to column COLUMN in the current line.\n\
276 The column of a character is calculated by adding together the widths\n\
277 as displayed of the previous characters in the line.\n\
278 This function ignores line-continuation;\n\
279 there is no upper limit on the column number a character can have\n\
280 and horizontal scrolling has no effect.\n\n\
281 If specified column is within a character, point goes after that character.\n\
282 If it's past end of line, point goes to end of line.\n\n\
283 A non-nil second (optional) argument FORCE means, if the line\n\
284 is too short to reach column COLUMN then add spaces/tabs to get there,\n\
285 and if COLUMN is in the middle of a tab character, change it to spaces.")
286 (column, force)
287 Lisp_Object column, force;
289 register int pos;
290 register int col = current_column ();
291 register int goal;
292 register int end;
293 register int tab_width = XINT (current_buffer->tab_width);
294 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
295 register struct Lisp_Vector *dp = buffer_display_table ();
297 Lisp_Object val;
298 int prev_col;
299 int c;
301 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
302 CHECK_NATNUM (column, 0);
303 goal = XINT (column);
305 retry:
306 pos = point;
307 end = ZV;
309 /* If we're starting past the desired column,
310 back up to beginning of line and scan from there. */
311 if (col > goal)
313 pos = find_next_newline (pos, -1);
314 col = 0;
317 while (col < goal && pos < end)
319 c = FETCH_CHAR (pos);
320 if (c == '\n')
321 break;
322 if (c == '\r' && EQ (current_buffer->selective_display, Qt))
323 break;
324 pos++;
325 if (c == '\t')
327 prev_col = col;
328 col += tab_width;
329 col = col / tab_width * tab_width;
331 else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String)
332 col += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH);
333 else if (ctl_arrow && (c < 040 || c == 0177))
334 col++;
335 else if (c < 040 || c >= 0177)
336 col += 3;
337 else
338 col++;
341 SET_PT (pos);
343 /* If a tab char made us overshoot, change it to spaces
344 and scan through it again. */
345 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
347 del_range (point - 1, point);
348 Findent_to (make_number (col - 1));
349 insert_char (' ');
350 goto retry;
353 /* If line ends prematurely, add space to the end. */
354 if (col < goal && !NILP (force))
355 Findent_to (make_number (col = goal));
357 last_known_column = col;
358 last_known_column_point = point;
359 last_known_column_modified = MODIFF;
361 XFASTINT (val) = col;
362 return val;
365 struct position val_compute_motion;
367 /* Scan the current buffer forward from offset FROM, pretending that
368 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
369 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
370 and return the ending buffer position and screen location.
372 WIDTH is the number of columns available to display text;
373 compute_motion uses this to handle continuation lines and such.
374 HSCROLL is the number of columns not being displayed at the left
375 margin; this is usually taken from a window's hscroll member.
376 TAB_OFFSET is a mysterious value, perhaps the number of columns of
377 the first tab that aren't being displayed, perhaps because of a
378 continuation line or something.
380 compute_motion returns a pointer to a struct position. The bufpos
381 member gives the buffer position at the end of the scan, and hpos
382 and vpos give its cartesian location. I'm not clear on what the
383 other members are.
385 For example, to find the buffer position of column COL of line LINE
386 of a certain window, pass the window's starting location as FROM
387 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
388 Pass the buffer's ZV as TO, to limit the scan to the end of the
389 visible section of the buffer, and pass LINE and COL as TOVPOS and
390 TOHPOS.
392 When displaying in window w, a typical formula for WIDTH is:
394 window_width - 1
395 - (window_width + window_left != screen_width)
397 where
398 window_width is XFASTINT (w->width),
399 window_left is XFASTINT (w->left),
400 and screen_width = SCREEN_WIDTH (XSCREEN (window->screen))
402 This accounts for the continuation-line backslashes, and the window
403 borders if the window is split vertically. */
405 struct position *
406 compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
407 int from, fromvpos, fromhpos, to, tovpos, tohpos;
408 register int width;
409 int hscroll, tab_offset;
411 /* Note that `cpos' is CURRENT_VPOS << SHORTBITS + CURRENT_HPOS,
412 and that CURRENT_HPOS may be negative. Use these macros
413 to extract the hpos or the vpos from cpos or anything like it.
415 #ifndef SHORT_CAST_BUG
416 #define HPOS(VAR) (short) (VAR)
417 #else
418 #define HPOS(VAR) (((VAR) & (1 << (SHORTBITS - 1)) \
419 ? ~((1 << SHORTBITS) - 1) : 0) \
420 | (VAR) & ((1 << SHORTBITS) - 1))
421 /* #define HPOS(VAR) (((VAR) & 0x8000 ? 0xffff0000 : 0) | ((VAR) & 0xffff)) */
422 #endif /* SHORT_CAST_BUG */
424 #define VPOS(VAR) (((VAR) >> SHORTBITS) + (HPOS (VAR) < 0))
427 #ifndef TAHOE_REGISTER_BUG
428 register
429 #endif /* TAHOE_REGISTER_BUG */
430 int cpos = fromhpos + (fromvpos << SHORTBITS);
431 register int target = tohpos + (tovpos << SHORTBITS);
432 register int pos;
433 register int c;
434 register int tab_width = XFASTINT (current_buffer->tab_width);
435 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
436 register struct Lisp_Vector *dp = buffer_display_table ();
437 int selective
438 = XTYPE (current_buffer->selective_display) == Lisp_Int
439 ? XINT (current_buffer->selective_display)
440 : !NILP (current_buffer->selective_display) ? -1 : 0;
441 int prevpos;
442 int selective_rlen
443 = (selective && dp && XTYPE (DISP_INVIS_ROPE (dp)) == Lisp_String
444 ? XSTRING (DISP_INVIS_ROPE (dp))->size / sizeof (GLYPH) : 0);
446 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
447 for (pos = from; pos < to && cpos < target; pos++)
449 prevpos = cpos;
450 c = FETCH_CHAR (pos);
451 if (c >= 040 && c < 0177
452 && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String))
453 cpos++;
454 else if (c == '\t')
456 cpos += tab_width
457 - HPOS (cpos + tab_offset + hscroll - (hscroll > 0)
458 /* Add tab_width here to make sure positive.
459 cpos can be negative after continuation
460 but can't be less than -tab_width. */
461 + tab_width)
462 % tab_width;
464 else if (c == '\n')
466 if (selective > 0 && position_indentation (pos + 1) >= selective)
468 /* Skip any number of invisible lines all at once */
471 while (++pos < to && FETCH_CHAR(pos) != '\n');
473 while (selective > 0 && position_indentation (pos + 1) >= selective);
474 pos--;
475 /* Allow for the " ..." that is displayed for them. */
476 if (selective_rlen)
478 cpos += selective_rlen;
479 if (HPOS (cpos) >= width)
480 cpos -= HPOS (cpos) - width;
483 else
484 cpos += (1 << SHORTBITS) - HPOS (cpos);
485 cpos -= hscroll;
486 if (hscroll > 0) cpos++; /* Count the ! on column 0 */
487 tab_offset = 0;
489 else if (c == CR && selective < 0)
491 /* In selective display mode,
492 everything from a ^M to the end of the line is invisible */
493 while (pos < to && FETCH_CHAR(pos) != '\n') pos++;
494 pos--;
495 /* Allow for the " ..." that is displayed for them. */
496 if (selective_rlen)
498 cpos += selective_rlen;
499 if (HPOS (cpos) >= width)
500 cpos -= HPOS (cpos) - width;
503 else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String)
504 cpos += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH);
505 else
506 cpos += (ctl_arrow && c < 0200) ? 2 : 4;
508 if (HPOS (cpos) >= width
509 && (HPOS (cpos) > width
510 || (pos < (ZV - 1)
511 && FETCH_CHAR (pos + 1) != '\n')))
513 if (cpos >= target)
514 break;
515 if (hscroll
516 || (truncate_partial_width_windows
517 && width + 1 < SCREEN_WIDTH (selected_screen))
518 || !NILP (current_buffer->truncate_lines))
520 while (pos < to && FETCH_CHAR(pos) != '\n') pos++;
521 pos--;
523 else
525 cpos += (1 << SHORTBITS) - width;
526 tab_offset += width;
532 val_compute_motion.bufpos = pos;
533 val_compute_motion.hpos = HPOS (cpos);
534 val_compute_motion.vpos = VPOS (cpos);
535 val_compute_motion.prevhpos = HPOS (prevpos);
537 /* Nonzero if have just continued a line */
538 val_compute_motion.contin
539 = pos != from
540 && (val_compute_motion.vpos != VPOS (prevpos))
541 && c != '\n';
543 return &val_compute_motion;
545 #undef HPOS
546 #undef VPOS
549 /* Return the column of position POS in window W's buffer,
550 rounded down to a multiple of the internal width of W.
551 This is the amount of indentation of position POS
552 that is not visible in its horizontal position in the window. */
555 pos_tab_offset (w, pos)
556 struct window *w;
557 register int pos;
559 int opoint = point;
560 int col;
561 int width = XFASTINT (w->width) - 1
562 - (XFASTINT (w->width) + XFASTINT (w->left)
563 != SCREEN_WIDTH (XSCREEN (w->screen)));
565 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
566 return 0;
567 SET_PT (pos);
568 col = current_column ();
569 SET_PT (opoint);
570 return col - (col % width);
573 /* start_hpos is the hpos of the first character of the buffer:
574 zero except for the minibuffer window,
575 where it is the width of the prompt. */
577 struct position val_vmotion;
579 struct position *
580 vmotion (from, vtarget, width, hscroll, window)
581 register int from, vtarget, width;
582 int hscroll;
583 Lisp_Object window;
585 struct position pos;
586 /* vpos is cumulative vertical position, changed as from is changed */
587 register int vpos = 0;
588 register int prevline;
589 register int first;
590 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
591 int selective
592 = XTYPE (current_buffer->selective_display) == Lisp_Int
593 ? XINT (current_buffer->selective_display)
594 : !NILP (current_buffer->selective_display) ? -1 : 0;
595 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
597 retry:
598 if (vtarget > vpos)
600 /* Moving downward is simple, but must calculate from beg of line
601 to determine hpos of starting point */
602 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
604 prevline = find_next_newline (from, -1);
605 while (selective > 0
606 && prevline > BEGV
607 && position_indentation (prevline) >= selective)
608 prevline = find_next_newline (prevline - 1, -1);
609 pos = *compute_motion (prevline, 0,
610 lmargin + (prevline == 1 ? start_hpos : 0),
611 from, 10000, 10000,
612 width, hscroll, 0);
614 else
616 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
617 pos.vpos = 0;
619 return compute_motion (from, vpos, pos.hpos,
620 ZV, vtarget, - (1 << (SHORTBITS - 1)),
621 width, hscroll, pos.vpos * width);
624 /* To move upward, go a line at a time until
625 we have gone at least far enough */
627 first = 1;
629 while ((vpos > vtarget || first) && from > BEGV)
631 prevline = from;
632 while (1)
634 prevline = find_next_newline (prevline - 1, -1);
635 if (prevline == BEGV
636 || selective <= 0
637 || position_indentation (prevline) < selective)
638 break;
640 pos = *compute_motion (prevline, 0,
641 lmargin + (prevline == 1 ? start_hpos : 0),
642 from, 10000, 10000,
643 width, hscroll, 0);
644 vpos -= pos.vpos;
645 first = 0;
646 from = prevline;
649 /* If we made exactly the desired vertical distance,
650 or if we hit beginning of buffer,
651 return point found */
652 if (vpos >= vtarget)
654 val_vmotion.bufpos = from;
655 val_vmotion.vpos = vpos;
656 val_vmotion.hpos = lmargin;
657 val_vmotion.contin = 0;
658 val_vmotion.prevhpos = 0;
659 return &val_vmotion;
662 /* Otherwise find the correct spot by moving down */
663 goto retry;
666 DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0,
667 "Move to start of screen line LINES lines down.\n\
668 If LINES is negative, this is moving up.\n\
669 Sets point to position found; this may be start of line\n\
670 or just the start of a continuation line.\n\
671 Returns number of lines moved; may be closer to zero than LINES\n\
672 if beginning or end of buffer was reached.")
673 (lines)
674 Lisp_Object lines;
676 struct position pos;
677 register struct window *w = XWINDOW (selected_window);
678 int width = XFASTINT (w->width) - 1
679 - (XFASTINT (w->width) + XFASTINT (w->left)
680 != SCREEN_WIDTH (XSCREEN (w->screen)));
682 CHECK_NUMBER (lines, 0);
684 pos = *vmotion (point, XINT (lines), width,
685 /* Not XFASTINT since perhaps could be negative */
686 XINT (w->hscroll), selected_window);
688 SET_PT (pos.bufpos);
689 return make_number (pos.vpos);
692 syms_of_indent ()
694 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
695 "*Indentation can insert tabs if this is non-nil.\n\
696 Setting this variable automatically makes it local to the current buffer.");
697 indent_tabs_mode = 1;
699 defsubr (&Scurrent_indentation);
700 defsubr (&Sindent_to);
701 defsubr (&Scurrent_column);
702 defsubr (&Smove_to_column);
703 defsubr (&Svertical_motion);