*** empty log message ***
[bpt/emacs.git] / src / indent.c
1 /* Indentation functions.
2 Copyright (C) 1985, 1986, 1987, 1988, 1992 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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.
10
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.
15
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. */
19
20
21 #include "config.h"
22 #include "lisp.h"
23 #include "buffer.h"
24 #include "indent.h"
25 #include "frame.h"
26 #include "window.h"
27 #include "termchar.h"
28 #include "termopts.h"
29 #include "disptab.h"
30
31 /* Indentation can insert tabs if this is non-zero;
32 otherwise always uses spaces */
33 int indent_tabs_mode;
34
35 #define min(a, b) ((a) < (b) ? (a) : (b))
36 #define max(a, b) ((a) > (b) ? (a) : (b))
37
38 #define CR 015
39
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;
49
50 /* Get the display table to use for the current buffer. */
51
52 struct Lisp_Vector *
53 buffer_display_table ()
54 {
55 Lisp_Object thisbuf;
56
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;
65 }
66 \f
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 frame, which means that this function may return\n\
74 values greater than (frame-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.")
77 ()
78 {
79 Lisp_Object temp;
80 XFASTINT (temp) = current_column ();
81 return temp;
82 }
83
84 /* Cancel any recorded value of the horizontal position. */
85
86 invalidate_current_column ()
87 {
88 last_known_column_point = 0;
89 }
90
91 int
92 current_column ()
93 {
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;
103
104 if (point == last_known_column_point
105 && MODIFF == last_known_column_modified)
106 return last_known_column;
107
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;
118
119 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
120
121 col = 0, tab_seen = 0, post_tab = 0;
122
123 while (1)
124 {
125 if (ptr == stop)
126 {
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;
136 }
137
138 c = *--ptr;
139 if (c >= 040 && c < 0177
140 && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String))
141 {
142 col++;
143 }
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')
149 {
150 if (tab_seen)
151 col = ((col + tab_width) / tab_width) * tab_width;
152
153 post_tab += col;
154 col = 0;
155 tab_seen = 1;
156 }
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;
161 }
162
163 if (tab_seen)
164 {
165 col = ((col + tab_width) / tab_width) * tab_width;
166 col += post_tab;
167 }
168
169 last_known_column = col;
170 last_known_column_point = point;
171 last_known_column_modified = MODIFF;
172
173 return col;
174 }
175 \f
176
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;
183 {
184 int mincol;
185 register int fromcol;
186 register int tab_width = XINT (current_buffer->tab_width);
187
188 CHECK_NUMBER (col, 0);
189 if (NILP (minimum))
190 XFASTINT (minimum) = 0;
191 CHECK_NUMBER (minimum, 1);
192
193 fromcol = current_column ();
194 mincol = fromcol + XINT (minimum);
195 if (mincol < XINT (col)) mincol = XINT (col);
196
197 if (fromcol == mincol)
198 return make_number (mincol);
199
200 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
201
202 if (indent_tabs_mode)
203 {
204 Lisp_Object n;
205 XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
206 if (XFASTINT (n) != 0)
207 {
208 Finsert_char (make_number ('\t'), n);
209
210 fromcol = (mincol / tab_width) * tab_width;
211 }
212 }
213
214 XFASTINT (col) = mincol - fromcol;
215 Finsert_char (make_number (' '), col);
216
217 last_known_column = mincol;
218 last_known_column_point = point;
219 last_known_column_modified = MODIFF;
220
221 XSETINT (col, mincol);
222 return col;
223 }
224 \f
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.")
230 ()
231 {
232 Lisp_Object val;
233
234 XFASTINT (val) = position_indentation (find_next_newline (point, -1));
235 return val;
236 }
237
238 position_indentation (pos)
239 register int pos;
240 {
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;
245
246 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
247
248 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
249 p = &FETCH_CHAR (pos);
250 while (1)
251 {
252 while (p == stop)
253 {
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;
259 }
260 switch (*p++)
261 {
262 case ' ':
263 column++;
264 break;
265 case '\t':
266 column += tab_width - column % tab_width;
267 break;
268 default:
269 return column;
270 }
271 }
272 }
273 \f
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;
288 {
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 ();
296
297 Lisp_Object val;
298 int prev_col;
299 int c;
300
301 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
302 CHECK_NATNUM (column, 0);
303 goal = XINT (column);
304
305 retry:
306 pos = point;
307 end = ZV;
308
309 /* If we're starting past the desired column,
310 back up to beginning of line and scan from there. */
311 if (col > goal)
312 {
313 pos = find_next_newline (pos, -1);
314 col = 0;
315 }
316
317 while (col < goal && pos < end)
318 {
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')
326 {
327 prev_col = col;
328 col += tab_width;
329 col = col / tab_width * tab_width;
330 }
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++;
339 }
340
341 SET_PT (pos);
342
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)
346 {
347 int old_point;
348
349 del_range (point - 1, point);
350 Findent_to (make_number (goal), Qnil);
351 old_point = point;
352 Findent_to (make_number (col), Qnil);
353 SET_PT (old_point);
354 }
355
356 /* If line ends prematurely, add space to the end. */
357 if (col < goal && !NILP (force))
358 Findent_to (make_number (col = goal));
359
360 last_known_column = col;
361 last_known_column_point = point;
362 last_known_column_modified = MODIFF;
363
364 XFASTINT (val) = col;
365 return val;
366 }
367 \f
368 struct position val_compute_motion;
369
370 /* Scan the current buffer forward from offset FROM, pretending that
371 this is at line FROMVPOS, column FROMHPOS, until reaching buffer
372 offset TO or line TOVPOS, column TOHPOS (whichever comes first),
373 and return the ending buffer position and screen location.
374
375 WIDTH is the number of columns available to display text;
376 compute_motion uses this to handle continuation lines and such.
377 HSCROLL is the number of columns not being displayed at the left
378 margin; this is usually taken from a window's hscroll member.
379 TAB_OFFSET is the number of columns of the first tab that aren't
380 being displayed, perhaps because of a continuation line or
381 something.
382
383 compute_motion returns a pointer to a struct position. The bufpos
384 member gives the buffer position at the end of the scan, and hpos
385 and vpos give its cartesian location. I'm not clear on what the
386 other members are.
387
388 For example, to find the buffer position of column COL of line LINE
389 of a certain window, pass the window's starting location as FROM
390 and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
391 Pass the buffer's ZV as TO, to limit the scan to the end of the
392 visible section of the buffer, and pass LINE and COL as TOVPOS and
393 TOHPOS.
394
395 When displaying in window w, a typical formula for WIDTH is:
396
397 window_width - 1
398 - (window_width + window_left != frame_width)
399
400 where
401 window_width is XFASTINT (w->width),
402 window_left is XFASTINT (w->left),
403 and frame_width = FRAME_WIDTH (XFRAME (window->frame))
404
405 This accounts for the continuation-line backslashes, and the window
406 borders if the window is split vertically. */
407
408 struct position *
409 compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
410 int from, fromvpos, fromhpos, to, tovpos, tohpos;
411 register int width;
412 int hscroll, tab_offset;
413 {
414 register int hpos = fromhpos;
415 register int vpos = fromvpos;
416
417 register int pos;
418 register int c;
419 register int tab_width = XFASTINT (current_buffer->tab_width);
420 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
421 register struct Lisp_Vector *dp = buffer_display_table ();
422 int selective
423 = XTYPE (current_buffer->selective_display) == Lisp_Int
424 ? XINT (current_buffer->selective_display)
425 : !NILP (current_buffer->selective_display) ? -1 : 0;
426 int prev_vpos, prev_hpos;
427 int selective_rlen
428 = (selective && dp && XTYPE (DISP_INVIS_ROPE (dp)) == Lisp_String
429 ? XSTRING (DISP_INVIS_ROPE (dp))->size / sizeof (GLYPH) : 0);
430
431 if (tab_width <= 0 || tab_width > 20) tab_width = 8;
432 for (pos = from; pos < to; pos++)
433 {
434 /* Stop if past the target screen position. */
435 if (vpos > tovpos
436 || (vpos == tovpos && hpos >= tohpos))
437 break;
438
439 prev_vpos = vpos;
440 prev_hpos = hpos;
441
442 c = FETCH_CHAR (pos);
443 if (c >= 040 && c < 0177
444 && (dp == 0 || XTYPE (DISP_CHAR_ROPE (dp, c)) != Lisp_String))
445 hpos++;
446 else if (c == '\t')
447 {
448 hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
449 /* Add tab_width here to make sure positive.
450 hpos can be negative after continuation
451 but can't be less than -tab_width. */
452 + tab_width)
453 % tab_width);
454 }
455 else if (c == '\n')
456 {
457 if (selective > 0 && position_indentation (pos + 1) >= selective)
458 {
459 /* Skip any number of invisible lines all at once */
460 do
461 {
462 while (++pos < to && FETCH_CHAR (pos) != '\n');
463 }
464 while (pos < to && position_indentation (pos + 1) >= selective);
465 pos--;
466 /* Allow for the " ..." that is displayed for them. */
467 if (selective_rlen)
468 {
469 hpos += selective_rlen;
470 if (hpos >= width)
471 hpos = width;
472 }
473 /* We have skipped the invis text, but not the newline after. */
474 }
475 else
476 {
477 /* A visible line. */
478 vpos++;
479 hpos = 0;
480 hpos -= hscroll;
481 if (hscroll > 0) hpos++; /* Count the ! on column 0 */
482 tab_offset = 0;
483 }
484 }
485 else if (c == CR && selective < 0)
486 {
487 /* In selective display mode,
488 everything from a ^M to the end of the line is invisible */
489 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
490 /* Stop *before* the real newline. */
491 pos--;
492 /* Allow for the " ..." that is displayed for them. */
493 if (selective_rlen)
494 {
495 hpos += selective_rlen;
496 if (hpos >= width)
497 hpos = width;
498 }
499 }
500 else if (dp != 0 && XTYPE (DISP_CHAR_ROPE (dp, c)) == Lisp_String)
501 hpos += XSTRING (DISP_CHAR_ROPE (dp, c))->size / sizeof (GLYPH);
502 else
503 hpos += (ctl_arrow && c < 0200) ? 2 : 4;
504
505 /* Handle right margin. */
506 if (hpos >= width
507 && (hpos > width
508 || (pos < ZV - 1
509 && FETCH_CHAR (pos + 1) != '\n')))
510 {
511 if (vpos > tovpos
512 || (vpos == tovpos && hpos >= tohpos))
513 break;
514 if (hscroll
515 || (truncate_partial_width_windows
516 && width + 1 < FRAME_WIDTH (selected_frame))
517 || !NILP (current_buffer->truncate_lines))
518 {
519 /* Truncating: skip to newline. */
520 while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
521 pos--;
522 hpos = width;
523 }
524 else
525 {
526 /* Continuing. */
527 vpos++;
528 hpos -= width;
529 tab_offset += width;
530 }
531
532 }
533 }
534
535 val_compute_motion.bufpos = pos;
536 val_compute_motion.hpos = hpos;
537 val_compute_motion.vpos = vpos;
538 val_compute_motion.prevhpos = prev_hpos;
539
540 /* Nonzero if have just continued a line */
541 val_compute_motion.contin
542 = (pos != from
543 && (val_compute_motion.vpos != prev_vpos)
544 && c != '\n');
545
546 return &val_compute_motion;
547 }
548
549 \f
550 /* Return the column of position POS in window W's buffer,
551 rounded down to a multiple of the internal width of W.
552 This is the amount of indentation of position POS
553 that is not visible in its horizontal position in the window. */
554
555 int
556 pos_tab_offset (w, pos)
557 struct window *w;
558 register int pos;
559 {
560 int opoint = point;
561 int col;
562 int width = XFASTINT (w->width) - 1
563 - (XFASTINT (w->width) + XFASTINT (w->left)
564 != FRAME_WIDTH (XFRAME (w->frame)));
565
566 if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
567 return 0;
568 SET_PT (pos);
569 col = current_column ();
570 SET_PT (opoint);
571 return col - (col % width);
572 }
573
574 /* start_hpos is the hpos of the first character of the buffer:
575 zero except for the minibuffer window,
576 where it is the width of the prompt. */
577
578 struct position val_vmotion;
579
580 struct position *
581 vmotion (from, vtarget, width, hscroll, window)
582 register int from, vtarget, width;
583 int hscroll;
584 Lisp_Object window;
585 {
586 struct position pos;
587 /* vpos is cumulative vertical position, changed as from is changed */
588 register int vpos = 0;
589 register int prevline;
590 register int first;
591 int lmargin = hscroll > 0 ? 1 - hscroll : 0;
592 int selective
593 = XTYPE (current_buffer->selective_display) == Lisp_Int
594 ? XINT (current_buffer->selective_display)
595 : !NILP (current_buffer->selective_display) ? -1 : 0;
596 int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
597
598 retry:
599 if (vtarget > vpos)
600 {
601 /* Moving downward is simple, but must calculate from beg of line
602 to determine hpos of starting point */
603 if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
604 {
605 prevline = find_next_newline (from, -1);
606 while (selective > 0
607 && prevline > BEGV
608 && position_indentation (prevline) >= selective)
609 prevline = find_next_newline (prevline - 1, -1);
610 pos = *compute_motion (prevline, 0,
611 lmargin + (prevline == 1 ? start_hpos : 0),
612 from, 1 << (INTBITS - 2), 0,
613 width, hscroll, 0);
614 }
615 else
616 {
617 pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
618 pos.vpos = 0;
619 }
620 return compute_motion (from, vpos, pos.hpos,
621 ZV, vtarget, - (1 << (INTBITS - 2)),
622 width, hscroll, pos.vpos * width);
623 }
624
625 /* To move upward, go a line at a time until
626 we have gone at least far enough */
627
628 first = 1;
629
630 while ((vpos > vtarget || first) && from > BEGV)
631 {
632 prevline = from;
633 while (1)
634 {
635 prevline = find_next_newline (prevline - 1, -1);
636 if (prevline == BEGV
637 || selective <= 0
638 || position_indentation (prevline) < selective)
639 break;
640 }
641 pos = *compute_motion (prevline, 0,
642 lmargin + (prevline == 1 ? start_hpos : 0),
643 from, 1 << (INTBITS - 2), 0,
644 width, hscroll, 0);
645 vpos -= pos.vpos;
646 first = 0;
647 from = prevline;
648 }
649
650 /* If we made exactly the desired vertical distance,
651 or if we hit beginning of buffer,
652 return point found */
653 if (vpos >= vtarget)
654 {
655 val_vmotion.bufpos = from;
656 val_vmotion.vpos = vpos;
657 val_vmotion.hpos = lmargin;
658 val_vmotion.contin = 0;
659 val_vmotion.prevhpos = 0;
660 return &val_vmotion;
661 }
662
663 /* Otherwise find the correct spot by moving down */
664 goto retry;
665 }
666
667 DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0,
668 "Move to start of screen line LINES lines down.\n\
669 If LINES is negative, this is moving up.\n\
670 Sets point to position found; this may be start of line\n\
671 or just the start of a continuation line.\n\
672 Returns number of lines moved; may be closer to zero than LINES\n\
673 if beginning or end of buffer was reached.")
674 (lines)
675 Lisp_Object lines;
676 {
677 struct position pos;
678 register struct window *w = XWINDOW (selected_window);
679 int width = XFASTINT (w->width) - 1
680 - (XFASTINT (w->width) + XFASTINT (w->left)
681 != FRAME_WIDTH (XFRAME (w->frame)));
682
683 CHECK_NUMBER (lines, 0);
684
685 pos = *vmotion (point, XINT (lines), width,
686 /* Not XFASTINT since perhaps could be negative */
687 XINT (w->hscroll), selected_window);
688
689 SET_PT (pos.bufpos);
690 return make_number (pos.vpos);
691 }
692 \f
693 syms_of_indent ()
694 {
695 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
696 "*Indentation can insert tabs if this is non-nil.\n\
697 Setting this variable automatically makes it local to the current buffer.");
698 indent_tabs_mode = 1;
699
700 defsubr (&Scurrent_indentation);
701 defsubr (&Sindent_to);
702 defsubr (&Scurrent_column);
703 defsubr (&Smove_to_column);
704 defsubr (&Svertical_motion);
705 }