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