|
|
1.1 root 1: /* Indentation functions.
2: Copyright (C) 1985, 1986, 1987, 1988, 1990 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 "window.h"
26: #include "termchar.h"
27: #include "termopts.h"
28:
29: #define CR '\015'
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: /* These three values memoize the current column to avoid recalculation */
39: /* Some things in set last_known_column_point to -1
40: to mark the memoized value as invalid */
41: /* Last value returned by current_column */
42: int last_known_column;
43: /* Value of point when current_column was called */
44: int last_known_column_point;
45: /* Value of MODIFF when current_column was called */
46: int last_known_column_modified;
47:
48: extern int minibuf_prompt_width;
49:
50: DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
51: "Return the horizontal position of point. Beginning of line is column 0.\n\
52: This is calculated by adding together the widths of all the displayed\n\
53: representations of the character between the start of the previous line\n\
54: and point. (eg control characters will have a width of 2 or 4, tabs\n\
55: will have a variable width)\n\
56: Ignores finite width of screen, which means that this function may return\n\
57: values greater than (screen-width).\n\
58: Whether the line is visible (if `selective-display' is t) has no effect.")
59: ()
60: {
61: Lisp_Object temp;
62: XFASTINT (temp) = current_column ();
63: return temp;
64: }
65:
66: /* Cancel any recorded value of the horizontal position. */
67:
68: invalidate_current_column ()
69: {
70: last_known_column_point = 0;
71: }
72:
73: int
74: current_column ()
75: {
76: register int col;
77: register unsigned char *ptr, *stop, c;
78: register int tab_seen;
79: register int post_tab;
80: register int tab_width = XINT (current_buffer->tab_width);
81: int ctl_arrow = !NULL (current_buffer->ctl_arrow);
82:
83: if (point == last_known_column_point
84: && MODIFF == last_known_column_modified)
85: return last_known_column;
86:
87: /* Make a pointer for decrementing through the chars before point. */
88: ptr = &FETCH_CHAR (point - 1) + 1;
89: /* Make a pointer to where consecutive chars leave off,
90: going backwards from point. */
91: if (point == BEGV)
92: stop = ptr;
93: else if (point <= GPT || BEGV > GPT)
94: stop = BEGV_ADDR;
95: else
96: stop = GAP_END_ADDR;
97:
98: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
99:
100: col = 0, tab_seen = 0, post_tab = 0;
101:
102: while (1)
103: {
104: if (ptr == stop)
105: {
106: /* We stopped either for the beginning of the buffer
107: or for the gap. */
108: if (ptr == BEGV_ADDR)
109: break;
110: /* It was the gap. Jump back over it. */
111: stop = BEGV_ADDR;
112: ptr = GPT_ADDR;
113: /* Check whether that brings us to beginning of buffer. */
114: if (BEGV >= GPT) break;
115: }
116:
117: c = *--ptr;
118: if (c >= 040 && c < 0177)
119: {
120: col++;
121: }
122: else if (c == '\n')
123: break;
124: else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
125: break;
126: else if (c == '\t')
127: {
128: if (tab_seen)
129: col = ((col + tab_width) / tab_width) * tab_width;
130:
131: post_tab += col;
132: col = 0;
133: tab_seen = 1;
134: }
135: else
136: col += (ctl_arrow && c < 0200) ? 2 : 4;
137: }
138:
139: if (tab_seen)
140: {
141: col = ((col + tab_width) / tab_width) * tab_width;
142: col += post_tab;
143: }
144:
145: last_known_column = col;
146: last_known_column_point = point;
147: last_known_column_modified = MODIFF;
148:
149: return col;
150: }
151:
152: ToCol (col)
153: int col;
154: {
155: register int fromcol = current_column ();
156: register int n;
157: register int tab_width = XINT (current_buffer->tab_width);
158:
159: if (fromcol > col)
160: return;
161:
162: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
163:
164: if (indent_tabs_mode)
165: {
166: n = col / tab_width - fromcol / tab_width;
167: if (n)
168: {
169: while (n-- > 0)
170: insert ("\t", 1);
171:
172: fromcol = (col / tab_width) * tab_width;
173: }
174: }
175:
176: while (fromcol < col)
177: {
178: insert (" ", min (8, col - fromcol));
179: fromcol += min (8, col - fromcol);
180: }
181:
182: last_known_column = col;
183: last_known_column_point = point;
184: last_known_column_modified = MODIFF;
185: }
186:
187: DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
188: "Indent from point with tabs and spaces until COLUMN is reached.\n\
189: Always do at least MIN spaces even if that goes past COLUMN;\n\
190: by default, MIN is zero.")
191: (col, minimum)
192: Lisp_Object col, minimum;
193: {
194: int mincol;
195: register int fromcol;
196: register int tab_width = XINT (current_buffer->tab_width);
197:
198: CHECK_NUMBER (col, 0);
199: if (NULL (minimum))
200: XFASTINT (minimum) = 0;
201: CHECK_NUMBER (minimum, 1);
202:
203: fromcol = current_column ();
204: mincol = fromcol + XINT (minimum);
205: if (mincol < XINT (col)) mincol = XINT (col);
206:
207: if (fromcol == mincol)
208: return make_number (fromcol);
209:
210: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
211:
212: if (indent_tabs_mode)
213: {
214: Lisp_Object n;
215: XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
216: if (XFASTINT (n) != 0)
217: {
218: Finsert_char (make_number ('\t'), n);
219:
220: fromcol = (mincol / tab_width) * tab_width;
221: }
222: }
223:
224: XFASTINT (col) = mincol - fromcol;
225: Finsert_char (make_number (' '), col);
226:
227: last_known_column = mincol;
228: last_known_column_point = point;
229: last_known_column_modified = MODIFF;
230:
231: XSETINT (col, mincol);
232: return col;
233: }
234:
235: DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
236: 0, 0, 0,
237: "Return the indentation of the current line.\n\
238: This is the horizontal position of the character\n\
239: following any initial whitespace.")
240: ()
241: {
242: Lisp_Object val;
243:
244: XFASTINT (val) = position_indentation (find_next_newline (point, -1));
245: return val;
246: }
247:
248: position_indentation (pos)
249: register int pos;
250: {
251: register int column = 0;
252: register int tab_width = XINT (current_buffer->tab_width);
253: register unsigned char *p;
254: register unsigned char *stop;
255:
256: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
257:
258: stop = &FETCH_CHAR (BufferSafeCeiling (pos)) + 1;
259: p = &FETCH_CHAR (pos);
260: while (1)
261: {
262: while (p == stop)
263: {
264: if (pos == ZV)
265: return column;
266: pos += p - &FETCH_CHAR (pos);
267: p = &FETCH_CHAR (pos);
268: stop = &FETCH_CHAR (BufferSafeCeiling (pos)) + 1;
269: }
270: switch (*p++)
271: {
272: case ' ':
273: column++;
274: break;
275: case '\t':
276: column += tab_width - column % tab_width;
277: break;
278: default:
279: return column;
280: }
281: }
282: }
283:
284: DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 1, 0,
285: "Move point to column COLUMN in the current line.\n\
286: COLUMN is calculated by adding together the widths of all the displayed\n\
287: representations of the character between the start of the previous line\n\
288: and point. (eg control characters will have a width of 2 or 4, tabs\n\
289: will have a variable width)\n\
290: Ignores finite width of screen, which means that this function may be\n\
291: passed values greater than (screen-width)")
292: (column)
293: Lisp_Object column;
294: {
295: register int pos = point;
296: register int col = current_column ();
297: register int goal;
298: register int end = ZV;
299: register int tab_width = XINT (current_buffer->tab_width);
300: register int ctl_arrow = !NULL (current_buffer->ctl_arrow);
301:
302: Lisp_Object val;
303:
304: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
305: CHECK_NUMBER (column, 0);
306: goal = XINT (column);
307: if (col > goal)
308: {
309: pos = find_next_newline (pos, -1);
310: col = 0;
311: }
312:
313: while (col < goal && pos < end)
314: {
315: int c = FETCH_CHAR (pos);
316: if (c == '\n')
317: break;
318: if (c == '\r' && EQ (current_buffer->selective_display, Qt))
319: break;
320: pos++;
321: col++;
322: if (c == '\t')
323: {
324: col += tab_width - 1;
325: col = col / tab_width * tab_width;
326: }
327: else if (ctl_arrow && (c < 040 || c == 0177))
328: col++;
329: else if (c < 040 || c >= 0177)
330: col += 3;
331: }
332:
333: SET_PT (pos);
334:
335: last_known_column = col;
336: last_known_column_point = point;
337: last_known_column_modified = MODIFF;
338:
339: XFASTINT (val) = col;
340: return val;
341: }
342:
343: struct position val_compute_motion;
344:
345: struct position *
346: compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
347: int from, fromvpos, fromhpos, to, tovpos, tohpos;
348: register int width;
349: int hscroll, tab_offset;
350: {
351: register int hpos = fromhpos;
352: register int vpos = fromvpos;
353:
354: register int pos;
355: register int c;
356: register int tab_width = XFASTINT (current_buffer->tab_width);
357: register int ctl_arrow = !NULL (current_buffer->ctl_arrow);
358: int selective
359: = XTYPE (current_buffer->selective_display) == Lisp_Int
360: ? XINT (current_buffer->selective_display)
361: : !NULL (current_buffer->selective_display) ? -1 : 0;
362: int prev_vpos, prev_hpos;
363:
364: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
365: for (pos = from; pos < to; pos++)
366: {
367: /* Stop if past the target screen position. */
368: if (vpos > tovpos
369: || (vpos == tovpos && hpos >= tohpos))
370: break;
371:
372: prev_vpos = vpos;
373: prev_hpos = hpos;
374:
375: c = FETCH_CHAR (pos);
376: if (c >= 040 && c < 0177)
377: hpos++;
378: else if (c == '\t')
379: {
380: hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
381: /* Add tab_width here to make sure positive.
382: hpos can be negative after continuation
383: but can't be less than -tab_width. */
384: + tab_width)
385: % tab_width);
386: }
387: else if (c == '\n')
388: {
389: if (selective > 0 && position_indentation (pos + 1) >= selective)
390: {
391: /* Skip any number of invisible lines all at once */
392: do
393: {
394: while (++pos < to && FETCH_CHAR (pos) != '\n');
395: }
396: while (pos < to && position_indentation (pos + 1) >= selective);
397: pos--;
398: /* Allow for the " ..." that is displayed for them. */
399: if (!NULL (current_buffer->selective_display_ellipses))
400: {
401: hpos += 4;
402: if (hpos >= width)
403: hpos = width;
404: }
405: /* We have skipped the invis text, but not the newline after. */
406: }
407: else
408: {
409: /* A visible line follows.
410: Skip this newline and advance to next line. */
411: vpos++;
412: hpos = 0;
413: hpos -= hscroll;
414: if (hscroll > 0) hpos++; /* Count the ! on column 0 */
415: tab_offset = 0;
416: }
417: }
418: else if (c == CR && selective < 0)
419: {
420: /* In selective display mode,
421: everything from a ^M to the end of the line is invisible */
422: while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
423: /* Stop *before* the real newline. */
424: pos--;
425: /* Allow for the " ..." that is displayed for them. */
426: if (!NULL (current_buffer->selective_display_ellipses))
427: {
428: hpos += 4;
429: if (hpos >= width)
430: hpos = width;
431: }
432: }
433: else
434: hpos += (ctl_arrow && c < 0200) ? 2 : 4;
435:
436: /* Handle right margin. */
437: if (hpos >= width
438: && (hpos > width
439: || (pos < ZV - 1
440: && FETCH_CHAR (pos + 1) != '\n')))
441: {
442: if (vpos > tovpos
443: || (vpos == tovpos && hpos >= tohpos))
444: break;
445: if (hscroll
446: || (truncate_partial_width_windows
447: && width + 1 < screen_width)
448: || !NULL (current_buffer->truncate_lines))
449: {
450: /* Truncating: skip to newline. */
451: while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
452: pos--;
453: hpos = width;
454: }
455: else
456: {
457: /* Continuing. */
458: vpos++;
459: hpos -= width;
460: tab_offset += width;
461: }
462:
463: }
464: }
465:
466: val_compute_motion.bufpos = pos;
467: val_compute_motion.hpos = hpos;
468: val_compute_motion.vpos = vpos;
469: val_compute_motion.prevhpos = prev_hpos;
470:
471: /* Nonzero if have just continued a line */
472: val_compute_motion.contin
473: = (pos != from
474: && (val_compute_motion.vpos != prev_vpos)
475: && c != '\n');
476:
477: return &val_compute_motion;
478: }
479:
480: pos_tab_offset (w, pos)
481: struct window *w;
482: register int pos;
483: {
484: int opoint = point;
485: int col;
486:
487: if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
488: return 0;
489: SET_PT (pos);
490: col = current_column ();
491: SET_PT (opoint);
492: return (col
493: - (col % (XFASTINT (w->width) - 1
494: - (XFASTINT (w->width) + XFASTINT (w->left)
495: != screen_width))));
496: }
497:
498: /* start_hpos is the hpos of the first character of the buffer:
499: zero except for the minibuffer window,
500: where it is the width of the prompt. */
501:
502: struct position val_vmotion;
503:
504: struct position *
505: vmotion (from, vtarget, width, hscroll, window)
506: register int from, vtarget, width;
507: int hscroll;
508: Lisp_Object window;
509: {
510: struct position pos;
511: /* vpos is cumulative vertical position, changed as from is changed */
512: register int vpos = 0;
513: register int prevline;
514: register int first;
515: int lmargin = hscroll > 0 ? 1 - hscroll : 0;
516: int selective
517: = XTYPE (current_buffer->selective_display) == Lisp_Int
518: ? XINT (current_buffer->selective_display)
519: : !NULL (current_buffer->selective_display) ? -1 : 0;
520: int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
521:
522: retry:
523: if (vtarget > vpos)
524: {
525: /* Moving downward is simple, but must calculate from beg of line
526: to determine hpos of starting point */
527: if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
528: {
529: prevline = find_next_newline (from, -1);
530: while (selective > 0
531: && prevline > BEGV
532: && position_indentation (prevline) >= selective)
533: prevline = find_next_newline (prevline - 1, -1);
534: pos = *compute_motion (prevline, 0,
535: lmargin + (prevline == 1 ? start_hpos : 0),
536: from, 1 << (INTBITS - 2), 0,
537: width, hscroll, 0);
538: }
539: else
540: {
541: pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
542: pos.vpos = 0;
543: }
544: return compute_motion (from, vpos, pos.hpos,
545: ZV, vtarget, - (1 << (INTBITS - 2)),
546: width, hscroll, pos.vpos * width);
547: }
548:
549: /* To move upward, go a line at a time until
550: we have gone at least far enough */
551:
552: first = 1;
553:
554: while ((vpos > vtarget || first) && from > BEGV)
555: {
556: prevline = from;
557: while (1)
558: {
559: prevline = find_next_newline (prevline - 1, -1);
560: if (prevline == BEGV
561: || selective <= 0
562: || position_indentation (prevline) < selective)
563: break;
564: }
565: pos = *compute_motion (prevline, 0,
566: lmargin + (prevline == 1 ? start_hpos : 0),
567: from, 1 << (INTBITS - 2), 0,
568: width, hscroll, 0);
569: vpos -= pos.vpos;
570: first = 0;
571: from = prevline;
572: }
573:
574: /* If we made exactly the desired vertical distance,
575: or if we hit beginning of buffer,
576: return point found */
577: if (vpos >= vtarget)
578: {
579: val_vmotion.bufpos = from;
580: val_vmotion.vpos = vpos;
581: val_vmotion.hpos = lmargin;
582: val_vmotion.contin = 0;
583: val_vmotion.prevhpos = 0;
584: return &val_vmotion;
585: }
586:
587: /* Otherwise find the correct spot by moving down */
588: goto retry;
589: }
590:
591: DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0,
592: "Move to start of screen line LINES lines down.\n\
593: If LINES is negative, this is moving up.\n\
594: Sets point to position found; this may be start of line\n\
595: or just the start of a continuation line.\n\
596: Returns number of lines moved; may be closer to zero than LINES\n\
597: if beginning or end of buffer was reached.")
598: (lines)
599: Lisp_Object lines;
600: {
601: struct position pos;
602: register struct window *w = XWINDOW (selected_window);
603:
604: CHECK_NUMBER (lines, 0);
605:
606: pos = *vmotion (point, XINT (lines),
607: XFASTINT (w->width) - 1
608: - (XFASTINT (w->width) + XFASTINT (w->left)
609: != XFASTINT (XWINDOW (minibuf_window)->width)),
610: /* Not XFASTINT since perhaps could be negative */
611: XINT (w->hscroll), selected_window);
612:
613: SET_PT (pos.bufpos);
614: return make_number (pos.vpos);
615: }
616:
617: syms_of_indent ()
618: {
619: DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
620: "*Indentation can insert tabs if this is non-nil.\n\
621: Setting this variable automatically makes it local to the current buffer.");
622: indent_tabs_mode = 1;
623:
624: defsubr (&Scurrent_indentation);
625: defsubr (&Sindent_to);
626: defsubr (&Scurrent_column);
627: defsubr (&Smove_to_column);
628: defsubr (&Svertical_motion);
629: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.