|
|
1.1 root 1: /* Indentation functions.
2: Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.
3:
4: This file is part of GNU Emacs.
5:
6: GNU Emacs is distributed in the hope that it will be useful,
7: but WITHOUT ANY WARRANTY. No author or distributor
8: accepts responsibility to anyone for the consequences of using it
9: or for whether it serves any particular purpose or works at all,
10: unless he says so in writing. Refer to the GNU Emacs General Public
11: License for full details.
12:
13: Everyone is granted permission to copy, modify and redistribute
14: GNU Emacs, but only under the conditions described in the
15: GNU Emacs General Public License. A copy of this license is
16: supposed to have been given to you along with GNU Emacs so you
17: can know your rights and responsibilities. It should be in a
18: file named COPYING. Among other things, the copyright notice
19: and this notice must be preserved on all copies. */
20:
21:
22: #include "config.h"
23: #include "lisp.h"
24: #include "buffer.h"
25: #include "indent.h"
26: #include "window.h"
27: #include "termchar.h"
28: #include "termopts.h"
29:
30: #define CR '\015'
31:
32: /* Indentation can insert tabs if this is non-zero;
33: otherwise always uses spaces */
34: int indent_tabs_mode;
35:
36: #define min(a, b) ((a) < (b) ? (a) : (b))
37: #define max(a, b) ((a) > (b) ? (a) : (b))
38:
39: /* These three values memoize the current column to avoid recalculation */
40: /* Some things in set last_known_column_point to -1
41: to mark the memoized value as invalid */
42: /* Last value returned by current_column */
43: int last_known_column;
44: /* Value of point when current_column was called */
45: int last_known_column_point;
46: /* Value of bf_modified when current_column was called */
47: int last_known_column_modified;
48:
49: extern int minibuf_prompt_width;
50:
51: DEFSIMPLE ("current-column", Fcurrent_column, Scurrent_column,
52: "Return the horizontal position of point. Beginning of line is column 0.\n\
53: This is calculated by adding together the widths of all the displayed\n\
54: representations of the character between the start of the previous line\n\
55: and point. (eg control characters will have a width of 2 or 4, tabs\n\
56: will have a variable width)\n\
57: Ignores finite width of screen, which means that this function may return\n\
58: values greater than (screen-width).\n\
59: Whether the line is visible (if `selective-display' is t) has no effect.",
60: Lisp_Int, XSETINT, current_column ())
61:
62: int
63: current_column ()
64: {
65: register int col;
66: register unsigned char *ptr, *stop, c;
67: register int tab_seen;
68: register int post_tab;
69: register int tab_width = XINT (bf_cur->tab_width);
70: int ctl_arrow = !NULL (bf_cur->ctl_arrow);
71:
72: if (point == last_known_column_point
73: && bf_modified == last_known_column_modified)
74: return last_known_column;
75:
76: ptr = &CharAt (point - 1) + 1;
77: stop = point <= bf_s1 + 1 ? bf_p1 + 1 : bf_p2 + bf_s1 + 1;
78: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
79:
80: col = 0, tab_seen = 0, post_tab = 0;
81:
82: while (1)
83: {
84: if (ptr == stop)
85: {
86: if (ptr == bf_p1 + 1)
87: break;
88: stop = bf_p1 + 1;
89: ptr = stop + bf_s1;
90: if (!bf_s1) break;
91: }
92:
93: c = *--ptr;
94: if (c >= 040 && c < 0177)
95: {
96: col++;
97: }
98: else if (c == '\n')
99: break;
100: else if (c == '\r' && EQ (bf_cur->selective_display, Qt))
101: break;
102: else if (c == '\t')
103: {
104: if (tab_seen)
105: col = ((col + tab_width) / tab_width) * tab_width;
106:
107: post_tab += col;
108: col = 0;
109: tab_seen = 1;
110: }
111: else
112: col += (ctl_arrow && c < 0200) ? 2 : 4;
113: }
114:
115: if (tab_seen)
116: {
117: col = ((col + tab_width) / tab_width) * tab_width;
118: col += post_tab;
119: }
120:
121: last_known_column = col;
122: last_known_column_point = point;
123: last_known_column_modified = bf_modified;
124:
125: return col;
126: }
127:
128: ToCol (col)
129: int col;
130: {
131: register int fromcol = current_column ();
132: register int n;
133: register int tab_width = XINT (bf_cur->tab_width);
134:
135: if (fromcol > col)
136: return;
137:
138: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
139:
140: if (indent_tabs_mode)
141: {
142: n = col / tab_width - fromcol / tab_width;
143: if (n)
144: {
145: while (n-- > 0)
146: InsCStr ("\t", 1);
147:
148: fromcol = (col / tab_width) * tab_width;
149: }
150: }
151:
152: while (fromcol < col)
153: {
154: InsCStr (" ", min (8, col - fromcol));
155: fromcol += min (8, col - fromcol);
156: }
157:
158: last_known_column = col;
159: last_known_column_point = point;
160: last_known_column_modified = bf_modified;
161: }
162:
163: DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
164: "Indent from point with tabs and spaces until COLUMN is reached.\n\
165: Always do at least MIN spaces even if that goes past COLUMN;\n\
166: by default, MIN is zero.")
167: (col, minimum)
168: Lisp_Object col, minimum;
169: {
170: int mincol;
171:
172: CHECK_NUMBER (col, 0);
173: if (NULL (minimum))
174: XFASTINT (minimum) = 0;
175: CHECK_NUMBER (minimum, 1);
176:
177: mincol = current_column () + XINT (minimum);
178: if (mincol < XINT (col)) mincol = XINT (col);
179:
180: ToCol (mincol);
181:
182: XSETINT (col, mincol);
183: return col;
184: }
185:
186: DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
187: 0, 0, 0,
188: "Return the indentation of the current line.\n\
189: This is the horizontal position of the character\n\
190: following any initial whitespace.")
191: ()
192: {
193: Lisp_Object val;
194:
195: XFASTINT (val) = position_indentation (find_next_newline (point, -1));
196: return val;
197: }
198:
199: position_indentation (pos)
200: register int pos;
201: {
202: register int col = 0;
203: register int c;
204: register int end = NumCharacters + 1;
205: register int tab_width = XINT (bf_cur->tab_width);
206:
207: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
208:
209: while (pos < end &&
210: (c = CharAt (pos),
211: c == '\t' ? (col += tab_width - col % tab_width)
212: : (c == ' ' ? ++col : 0)))
213: pos++;
214:
215: return col;
216: }
217:
218: DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 1, 0,
219: "Move point to column COLUMN in the current line.\n\
220: COLUMN is calculated by adding together the widths of all the displayed\n\
221: representations of the character between the start of the previous line\n\
222: and point. (eg control characters will have a width of 2 or 4, tabs\n\
223: will have a variable width)\n\
224: Ignores finite width of screen, which means that this function may be\n\
225: passed values greater than (screen-width)")
226: (column)
227: Lisp_Object column;
228: {
229: register int pos = point;
230: register int col = current_column ();
231: register int goal;
232: register int end = NumCharacters;
233: register int tab_width = XINT (bf_cur->tab_width);
234: register int ctl_arrow = !NULL (bf_cur->ctl_arrow);
235:
236: Lisp_Object val;
237:
238: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
239: CHECK_NUMBER (column, 0);
240: goal = XINT (column);
241: if (col > goal)
242: {
243: pos = find_next_newline (pos, -1);
244: col = 0;
245: }
246:
247: while (col < goal && pos <= end)
248: {
249: int c = CharAt (pos);
250: if (c == '\n')
251: break;
252: if (c == '\r' && EQ (bf_cur->selective_display, Qt))
253: break;
254: pos++;
255: col++;
256: if (c == '\t')
257: {
258: col += tab_width - 1;
259: col = col / tab_width * tab_width;
260: }
261: else if (ctl_arrow && (c < 040 || c == 0177))
262: col++;
263: else if (c < 040 || c >= 0177)
264: col += 3;
265: }
266:
267: SetPoint (pos);
268:
269: last_known_column = col;
270: last_known_column_point = point;
271: last_known_column_modified = bf_modified;
272:
273: XFASTINT (val) = col;
274: return val;
275: }
276:
277: struct position val_compute_motion;
278:
279: struct position *
280: compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset)
281: int from, fromvpos, fromhpos, to, tovpos, tohpos;
282: register int width;
283: int hscroll, tab_offset;
284: {
285: /* Note that `cpos' is CURRENT_VPOS << SHORTBITS + CURRENT_HPOS,
286: and that CURRENT_HPOS may be negative. Use these macros
287: to extract the hpos or the vpos from cpos or anything like it.
288: */
289: #ifndef SHORT_CAST_BUG
290: #define HPOS(VAR) (short) (VAR)
291: #else
292: #define HPOS(VAR) (((VAR) & (1 << (SHORTBITS - 1)) \
293: ? ~((1 << SHORTBITS) - 1) : 0) \
294: | (VAR) & ((1 << SHORTBITS) - 1))
295: /* #define HPOS(VAR) (((VAR) & 0x8000 ? 0xffff0000 : 0) | ((VAR) & 0xffff)) */
296: #endif /* SHORT_CAST_BUG */
297:
298: #define VPOS(VAR) (((VAR) >> SHORTBITS) + (HPOS (VAR) < 0))
299:
300:
301: #ifndef TAHOE_REGISTER_BUG
302: register
303: #endif /* TAHOE_REGISTER_BUG */
304: int cpos = fromhpos + (fromvpos << SHORTBITS);
305: register int target = tohpos + (tovpos << SHORTBITS);
306: register int pos;
307: register int c;
308: register int tab_width = XFASTINT (bf_cur->tab_width);
309: register int ctl_arrow = !NULL (bf_cur->ctl_arrow);
310: int selective
311: = XTYPE (bf_cur->selective_display) == Lisp_Int
312: ? XINT (bf_cur->selective_display)
313: : !NULL (bf_cur->selective_display) ? -1 : 0;
314: int prevpos;
315:
316: if (tab_width <= 0 || tab_width > 20) tab_width = 8;
317: for (pos = from; pos < to && cpos < target; pos++)
318: {
319: prevpos = cpos;
320: c = CharAt (pos);
321: if (c >= 040 && c < 0177)
322: cpos++;
323: else if (c == '\t')
324: {
325: cpos += tab_width
326: - HPOS (cpos + tab_offset + hscroll - (hscroll > 0)
327: /* Add tab_width here to make sure positive.
328: cpos can be negative after continuation
329: but can't be less than -tab_width. */
330: + tab_width)
331: % tab_width;
332: }
333: else if (c == '\n')
334: {
335: if (selective > 0 && position_indentation (pos + 1) >= selective)
336: {
337: /* Skip any number of invisible lines all at once */
338: do
339: {
340: while (++pos < to && CharAt(pos) != '\n');
341: }
342: while (selective > 0 && position_indentation (pos + 1) >= selective);
343: pos--;
344: /* Allow for the " ..." that is displayed for them. */
345: if (!NULL (bf_cur->selective_display_ellipses))
346: {
347: cpos += 4;
348: if (HPOS (cpos) >= width)
349: cpos -= HPOS (cpos) - width;
350: }
351: }
352: else
353: cpos += (1 << SHORTBITS) - HPOS (cpos);
354: cpos -= hscroll;
355: if (hscroll > 0) cpos++; /* Count the ! on column 0 */
356: tab_offset = 0;
357: }
358: else if (c == CR && selective < 0)
359: {
360: /* In selective display mode,
361: everything from a ^M to the end of the line is invisible */
362: while (pos < to && CharAt(pos) != '\n') pos++;
363: pos--;
364: /* Allow for the " ..." that is displayed for them. */
365: if (!NULL (bf_cur->selective_display_ellipses))
366: {
367: cpos += 4;
368: if (HPOS (cpos) >= width)
369: cpos -= HPOS (cpos) - width;
370: }
371: }
372: else
373: cpos += (ctl_arrow && c < 0200) ? 2 : 4;
374:
375: if (HPOS (cpos) >= width
376: && (HPOS (cpos) > width
377: || (pos < NumCharacters
378: && CharAt (pos + 1) != '\n')))
379: {
380: if (cpos >= target)
381: break;
382: if (hscroll
383: || (truncate_partial_width_windows
384: && width + 1 < screen_width)
385: || !NULL (bf_cur->truncate_lines))
386: {
387: while (pos < to && CharAt(pos) != '\n') pos++;
388: pos--;
389: }
390: else
391: {
392: cpos += (1 << SHORTBITS) - width;
393: tab_offset += width;
394: }
395:
396: }
397: }
398:
399: val_compute_motion.bufpos = pos;
400: val_compute_motion.hpos = HPOS (cpos);
401: val_compute_motion.vpos = VPOS (cpos);
402: val_compute_motion.prevhpos = HPOS (prevpos);
403:
404: /* Nonzero if have just continued a line */
405: val_compute_motion.contin
406: = pos != from
407: && (val_compute_motion.vpos != VPOS (prevpos))
408: && c != '\n';
409:
410: return &val_compute_motion;
411: }
412: #undef HPOS
413: #undef VPOS
414:
415:
416: pos_tab_offset (w, pos)
417: struct window *w;
418: register int pos;
419: {
420: int opoint = point;
421: int col;
422:
423: if (pos == FirstCharacter || CharAt (pos - 1) == '\n')
424: return 0;
425: SetPoint (pos);
426: col = current_column ();
427: SetPoint (opoint);
428: return col - (col % (XFASTINT (w->width) - 1));
429: }
430:
431: /* start_hpos is the hpos of the first character of the buffer:
432: zero except for the minibuffer window,
433: where it is the width of the prompt. */
434:
435: struct position val_vmotion;
436:
437: struct position *
438: vmotion (from, vtarget, width, hscroll, window)
439: register int from, vtarget, width;
440: int hscroll;
441: Lisp_Object window;
442: {
443: struct position pos;
444: /* vpos is cumulative vertical position, changed as from is changed */
445: register int vpos = 0;
446: register int prevline;
447: register int first;
448: int lmargin = hscroll > 0 ? 1 - hscroll : 0;
449: int selective
450: = XTYPE (bf_cur->selective_display) == Lisp_Int
451: ? XINT (bf_cur->selective_display)
452: : !NULL (bf_cur->selective_display) ? -1 : 0;
453: int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
454:
455: retry:
456: if (vtarget > vpos)
457: {
458: /* Moving downward is simple, but must calculate from beg of line
459: to determine hpos of starting point */
460: if (from > FirstCharacter && CharAt (from - 1) != '\n')
461: {
462: prevline = find_next_newline (from, -1);
463: while (selective > 0
464: && prevline > FirstCharacter
465: && position_indentation (prevline) >= selective)
466: prevline = find_next_newline (prevline - 1, -1);
467: pos = *compute_motion (prevline, 0,
468: lmargin + (prevline == 1 ? start_hpos : 0),
469: from, 10000, 10000,
470: width, hscroll, 0);
471: }
472: else
473: {
474: pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
475: pos.vpos = 0;
476: }
477: return compute_motion (from, vpos, pos.hpos,
478: 1 + NumCharacters, vtarget, - (1 << (SHORTBITS - 1)),
479: width, hscroll, pos.vpos * width);
480: }
481:
482: /* To move upward, go a line at a time until
483: we have gone at least far enough */
484:
485: first = 1;
486:
487: while ((vpos > vtarget || first) && from > FirstCharacter)
488: {
489: prevline = from;
490: while (1)
491: {
492: prevline = find_next_newline (prevline - 1, -1);
493: if (prevline == FirstCharacter
494: || selective <= 0
495: || position_indentation (prevline) < selective)
496: break;
497: }
498: pos = *compute_motion (prevline, 0,
499: lmargin + (prevline == 1 ? start_hpos : 0),
500: from, 10000, 10000,
501: width, hscroll, 0);
502: vpos -= pos.vpos;
503: first = 0;
504: from = prevline;
505: }
506:
507: /* If we made exactly the desired vertical distance,
508: or if we hit beginning of buffer,
509: return point found */
510: if (vpos >= vtarget)
511: {
512: val_vmotion.bufpos = from;
513: val_vmotion.vpos = vpos;
514: val_vmotion.hpos = lmargin;
515: val_vmotion.contin = 0;
516: val_vmotion.prevhpos = 0;
517: return &val_vmotion;
518: }
519:
520: /* Otherwise find the correct spot by moving down */
521: goto retry;
522: }
523:
524: DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 1, 0,
525: "Move to start of screen line LINES lines down.\n\
526: If LINES is negative, this is moving up.\n\
527: Sets point to position found; this may be start of line\n\
528: or just the start of a continuation line.\n\
529: Returns number of lines moved; may be closer to zero than LINES\n\
530: if beginning or end of buffer was reached.")
531: (lines)
532: Lisp_Object lines;
533: {
534: struct position pos;
535: register struct window *w = XWINDOW (selected_window);
536:
537: CHECK_NUMBER (lines, 0);
538:
539: pos = *vmotion (point, XINT (lines),
540: XFASTINT (w->width) - 1
541: - (XFASTINT (w->width) + XFASTINT (w->left)
542: != XFASTINT (XWINDOW (minibuf_window)->width)),
543: /* Not XFASTINT since perhaps could be negative */
544: XINT (w->hscroll), selected_window);
545:
546: SetPoint (pos.bufpos);
547: return make_number (pos.vpos);
548: }
549:
550: syms_of_indent ()
551: {
552: DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
553: "*Indentation can insert tabs if this is non-nil.\n\
554: Setting this variable automatically makes it local to the current buffer.");
555: indent_tabs_mode = 1;
556:
557: defsubr (&Scurrent_indentation);
558: defsubr (&Sindent_to);
559: defsubr (&Scurrent_column);
560: defsubr (&Smove_to_column);
561: defsubr (&Svertical_motion);
562: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.