Mercurial > hg > xemacs-beta
annotate src/indent.c @ 5258:1ed4cefddd12
Add a couple of extra docstring backslashes, #'format-time-string
2010-09-05 Aidan Kehoe <kehoea@parhasard.net>
* editfns.c (Fformat_time_string):
Use two backslashes so that there is at least one present in the
output of describe function, when describing the Roman month
number syntax in this function's docstring. Thanks for provoking
me to look at this, Stephen Turnbull.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sun, 05 Sep 2010 19:22:37 +0100 |
parents | 07dcc7000bbf |
children | c096d8051f89 308d34e9f07d |
rev | line source |
---|---|
428 | 1 /* Indentation functions. |
2 Copyright (C) 1995 Board of Trustees, University of Illinois. | |
3 Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995 | |
826 | 4 Free Software Foundation, Inc. |
3025 | 5 Copyright (C) 2002, 2005 Ben Wing. |
428 | 6 |
7 This file is part of XEmacs. | |
8 | |
9 XEmacs is free software; you can redistribute it and/or modify it | |
10 under the terms of the GNU General Public License as published by the | |
11 Free Software Foundation; either version 2, or (at your option) any | |
12 later version. | |
13 | |
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 for more details. | |
18 | |
19 You should have received a copy of the GNU General Public License | |
20 along with XEmacs; see the file COPYING. If not, write to | |
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
22 Boston, MA 02111-1307, USA. */ | |
23 | |
24 /* This file has been Mule-ized. */ | |
25 | |
26 /* Synched up with: 19.30. Diverges significantly from FSF. */ | |
27 | |
28 | |
29 #include <config.h> | |
30 #include "lisp.h" | |
31 | |
32 #include "buffer.h" | |
33 #include "device.h" | |
34 #include "extents.h" | |
35 #include "faces.h" | |
36 #include "frame.h" | |
37 #include "glyphs.h" | |
38 #include "insdel.h" | |
39 #ifdef REGION_CACHE_NEEDS_WORK | |
40 #include "region-cache.h" | |
41 #endif | |
42 #include "window.h" | |
43 | |
44 /* Indentation can insert tabs if this is non-zero; | |
45 otherwise always uses spaces */ | |
46 int indent_tabs_mode; | |
47 | |
48 /* Avoid recalculation by remembering things in these variables. */ | |
49 | |
50 /* Last value returned by current_column. | |
51 | |
52 Some things set last_known_column_point to -1 | |
53 to mark the memoized value as invalid */ | |
54 static int last_known_column; | |
55 | |
56 /* Last buffer searched by current_column */ | |
57 static struct buffer *last_known_column_buffer; | |
58 | |
59 /* Value of point when current_column was called */ | |
665 | 60 static Charbpos last_known_column_point; |
428 | 61 |
62 /* Value of MODIFF when current_column was called */ | |
63 static int last_known_column_modified; | |
64 | |
665 | 65 static Charbpos |
66 last_visible_position (Charbpos pos, struct buffer *buf) | |
428 | 67 { |
68 Lisp_Object buffer; | |
69 Lisp_Object value; | |
70 | |
793 | 71 buffer = wrap_buffer (buf); |
2506 | 72 value = Fprevious_single_char_property_change (make_int (pos), Qinvisible, |
73 buffer, Qnil); | |
428 | 74 if (NILP (value)) |
75 return 0; /* no visible position found */ | |
76 else | |
77 /* #### bug bug bug!!! This will return the position of the beginning | |
78 of an invisible extent; this extent is very likely to be start-closed, | |
79 and thus the spaces inserted in `indent-to' will go inside the | |
80 invisible extent. | |
81 | |
82 Not sure what the correct solution is here. Rethink indent-to? */ | |
83 return XINT (value); | |
84 } | |
85 | |
86 #ifdef REGION_CACHE_NEEDS_WORK | |
87 | |
88 /* Allocate or free the width run cache, as requested by the current | |
89 state of current_buffer's cache_long_line_scans variable. */ | |
90 static void | |
91 width_run_cache_on_off (struct buffer *buf) | |
92 { | |
93 if (NILP (buf->cache_long_line_scans)) | |
94 { | |
95 /* It should be off. */ | |
96 if (buf->width_run_cache) | |
97 { | |
98 free_region_cache (buf->width_run_cache); | |
99 buf->width_run_cache = 0; | |
100 buf->width_table = Qnil; | |
101 } | |
102 } | |
103 else | |
104 { | |
105 /* It should be on. */ | |
106 if (buf->width_run_cache == 0) | |
107 { | |
108 buf->width_run_cache = new_region_cache (); | |
109 recompute_width_table (buf, buffer_display_table ()); | |
110 } | |
111 } | |
112 } | |
113 | |
114 #endif /* REGION_CACHE_NEEDS_WORK */ | |
115 | |
116 | |
117 /* Cancel any recorded value of the horizontal position. */ | |
118 | |
119 void | |
120 invalidate_current_column (void) | |
121 { | |
122 last_known_column_point = -1; | |
123 } | |
124 | |
125 int | |
665 | 126 column_at_point (struct buffer *buf, Charbpos init_pos, int cur_col) |
428 | 127 { |
128 int col; | |
129 int tab_seen; | |
130 int tab_width = XINT (buf->tab_width); | |
131 int post_tab; | |
665 | 132 Charbpos pos = init_pos; |
867 | 133 Ichar c; |
428 | 134 |
135 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; | |
136 col = tab_seen = post_tab = 0; | |
137 | |
138 while (1) | |
139 { | |
140 if (pos <= BUF_BEGV (buf)) | |
141 break; | |
142 | |
143 pos--; | |
144 c = BUF_FETCH_CHAR (buf, pos); | |
145 if (c == '\t') | |
146 { | |
147 if (tab_seen) | |
148 col = ((col + tab_width) / tab_width) * tab_width; | |
149 | |
150 post_tab += col; | |
151 col = 0; | |
152 tab_seen = 1; | |
153 } | |
154 else if (c == '\n' || | |
155 (EQ (buf->selective_display, Qt) && c == '\r')) | |
156 break; | |
157 else | |
158 { | |
159 /* #### This needs updating to handle the new redisplay. */ | |
160 /* #### FSFmacs looks at ctl_arrow, display tables. | |
161 We need to do similar. */ | |
162 #if 0 | |
665 | 163 displayed_glyphs = glyphs_from_charbpos (sel_frame, buf, |
428 | 164 XWINDOW (selected_window), |
165 pos, dp, 0, col, 0, 0, 0); | |
166 col += (displayed_glyphs->columns | |
167 - (displayed_glyphs->begin_columns | |
168 + displayed_glyphs->end_columns)); | |
169 #else /* XEmacs */ | |
170 #ifdef MULE | |
867 | 171 col += XCHARSET_COLUMNS (ichar_charset (c)); |
428 | 172 #else |
173 col ++; | |
174 #endif /* MULE */ | |
175 #endif /* XEmacs */ | |
176 } | |
177 } | |
178 | |
179 if (tab_seen) | |
180 { | |
181 col = ((col + tab_width) / tab_width) * tab_width; | |
182 col += post_tab; | |
183 } | |
184 | |
185 if (cur_col) | |
186 { | |
187 last_known_column_buffer = buf; | |
188 last_known_column = col; | |
189 last_known_column_point = init_pos; | |
190 last_known_column_modified = BUF_MODIFF (buf); | |
191 } | |
192 | |
193 return col; | |
194 } | |
195 | |
196 int | |
793 | 197 string_column_at_point (Lisp_Object s, Charbpos init_pos, int tab_width) |
428 | 198 { |
199 int col; | |
200 int tab_seen; | |
201 int post_tab; | |
665 | 202 Charbpos pos = init_pos; |
867 | 203 Ichar c; |
428 | 204 |
205 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; | |
206 col = tab_seen = post_tab = 0; | |
207 | |
208 while (1) | |
209 { | |
210 if (pos <= 0) | |
211 break; | |
212 | |
213 pos--; | |
867 | 214 c = string_ichar (s, pos); |
428 | 215 if (c == '\t') |
216 { | |
217 if (tab_seen) | |
218 col = ((col + tab_width) / tab_width) * tab_width; | |
219 | |
220 post_tab += col; | |
221 col = 0; | |
222 tab_seen = 1; | |
223 } | |
224 else if (c == '\n') | |
225 break; | |
226 else | |
227 #ifdef MULE | |
867 | 228 col += XCHARSET_COLUMNS (ichar_charset (c)); |
428 | 229 #else |
230 col ++; | |
231 #endif /* MULE */ | |
232 } | |
233 | |
234 if (tab_seen) | |
235 { | |
236 col = ((col + tab_width) / tab_width) * tab_width; | |
237 col += post_tab; | |
238 } | |
239 | |
240 return col; | |
241 } | |
242 | |
243 int | |
244 current_column (struct buffer *buf) | |
245 { | |
246 if (buf == last_known_column_buffer | |
247 && BUF_PT (buf) == last_known_column_point | |
248 && BUF_MODIFF (buf) == last_known_column_modified) | |
249 return last_known_column; | |
250 | |
251 return column_at_point (buf, BUF_PT (buf), 1); | |
252 } | |
253 | |
254 DEFUN ("current-column", Fcurrent_column, 0, 1, 0, /* | |
255 Return the horizontal position of point. Beginning of line is column 0. | |
256 This is calculated by adding together the widths of all the displayed | |
257 representations of the character between the start of the previous line | |
258 and point. (e.g. control characters will have a width of 2 or 4, tabs | |
259 will have a variable width.) | |
260 Ignores finite width of frame, which means that this function may return | |
261 values greater than (frame-width). | |
262 Whether the line is visible (if `selective-display' is t) has no effect; | |
263 however, ^M is treated as end of line when `selective-display' is t. | |
264 If BUFFER is nil, the current buffer is assumed. | |
265 */ | |
266 (buffer)) | |
267 { | |
268 return make_int (current_column (decode_buffer (buffer, 0))); | |
269 } | |
270 | |
271 | |
272 DEFUN ("indent-to", Findent_to, 1, 3, "NIndent to column: ", /* | |
273 Indent from point with tabs and spaces until COLUMN is reached. | |
444 | 274 Optional second argument MINIMUM says always do at least MINIMUM spaces |
275 even if that goes past COLUMN; by default, MINIMUM is zero. | |
428 | 276 If BUFFER is nil, the current buffer is assumed. |
277 */ | |
444 | 278 (column, minimum, buffer)) |
428 | 279 { |
280 /* This function can GC */ | |
281 int mincol; | |
282 int fromcol; | |
283 struct buffer *buf = decode_buffer (buffer, 0); | |
284 int tab_width = XINT (buf->tab_width); | |
665 | 285 Charbpos opoint = 0; |
428 | 286 |
444 | 287 CHECK_INT (column); |
428 | 288 if (NILP (minimum)) |
289 minimum = Qzero; | |
290 else | |
291 CHECK_INT (minimum); | |
292 | |
793 | 293 buffer = wrap_buffer (buf); |
428 | 294 |
295 fromcol = current_column (buf); | |
296 mincol = fromcol + XINT (minimum); | |
444 | 297 if (mincol < XINT (column)) mincol = XINT (column); |
428 | 298 |
299 if (fromcol == mincol) | |
300 return make_int (mincol); | |
301 | |
302 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; | |
303 | |
304 if (!NILP (Fextent_at (make_int (BUF_PT (buf)), buffer, Qinvisible, | |
305 Qnil, Qnil))) | |
306 { | |
665 | 307 Charbpos last_visible = last_visible_position (BUF_PT (buf), buf); |
428 | 308 |
309 opoint = BUF_PT (buf); | |
310 if (last_visible >= BUF_BEGV (buf)) | |
311 BUF_SET_PT (buf, last_visible); | |
312 else | |
563 | 313 invalid_operation ("Visible portion of buffer not modifiable", Qunbound); |
428 | 314 } |
315 | |
316 if (indent_tabs_mode) | |
317 { | |
318 int n = mincol / tab_width - fromcol / tab_width; | |
319 if (n != 0) | |
320 { | |
321 Finsert_char (make_char ('\t'), make_int (n), Qnil, buffer); | |
322 | |
323 fromcol = (mincol / tab_width) * tab_width; | |
324 } | |
325 } | |
326 | |
327 Finsert_char (make_char (' '), make_int (mincol - fromcol), Qnil, buffer); | |
328 | |
329 last_known_column_buffer = buf; | |
330 last_known_column = mincol; | |
331 last_known_column_point = BUF_PT (buf); | |
332 last_known_column_modified = BUF_MODIFF (buf); | |
333 | |
334 /* Not in FSF: */ | |
335 if (opoint > 0) | |
336 BUF_SET_PT (buf, opoint); | |
337 | |
338 return make_int (mincol); | |
339 } | |
340 | |
341 int | |
826 | 342 byte_spaces_at_point (struct buffer *b, Bytebpos byte_pos) |
428 | 343 { |
826 | 344 Bytebpos byte_end = BYTE_BUF_ZV (b); |
428 | 345 int col = 0; |
867 | 346 Ichar c; |
428 | 347 int tab_width = XINT (b->tab_width); |
348 | |
349 if (tab_width <= 0 || tab_width > 1000) | |
350 tab_width = 8; | |
351 | |
826 | 352 while (byte_pos < byte_end && |
353 (c = BYTE_BUF_FETCH_CHAR (b, byte_pos), | |
428 | 354 (c == '\t' |
355 ? (col += tab_width - col % tab_width) | |
356 : (c == ' ' ? ++col : 0)))) | |
826 | 357 INC_BYTEBPOS (b, byte_pos); |
428 | 358 |
359 return col; | |
360 } | |
361 | |
362 | |
363 DEFUN ("current-indentation", Fcurrent_indentation, 0, 1, 0, /* | |
364 Return the indentation of the current line. | |
365 This is the horizontal position of the character | |
366 following any initial whitespace. | |
367 */ | |
368 (buffer)) | |
369 { | |
370 struct buffer *buf = decode_buffer (buffer, 0); | |
665 | 371 Charbpos pos = find_next_newline (buf, BUF_PT (buf), -1); |
428 | 372 |
793 | 373 buffer = wrap_buffer (buf); |
428 | 374 |
375 if (!NILP (Fextent_at (make_int (pos), buffer, Qinvisible, Qnil, Qnil))) | |
376 return Qzero; | |
377 | |
826 | 378 return make_int (byte_spaces_at_point (buf, charbpos_to_bytebpos (buf, pos))); |
428 | 379 } |
380 | |
381 | |
382 DEFUN ("move-to-column", Fmove_to_column, 1, 3, 0, /* | |
383 Move point to column COLUMN in the current line. | |
384 The column of a character is calculated by adding together the widths | |
385 as displayed of the previous characters in the line. | |
386 This function ignores line-continuation; | |
387 there is no upper limit on the column number a character can have | |
388 and horizontal scrolling has no effect. | |
389 | |
390 If specified column is within a character, point goes after that character. | |
391 If it's past end of line, point goes to end of line. | |
392 | |
3025 | 393 A value of `coerce' for the second (optional) argument FORCE means if |
428 | 394 COLUMN is in the middle of a tab character, change it to spaces. |
395 Any other non-nil value means the same, plus if the line is too short to | |
396 reach column COLUMN, then add spaces/tabs to get there. | |
397 | |
398 Returns the actual column that it moved to. | |
399 */ | |
400 (column, force, buffer)) | |
401 { | |
402 /* This function can GC */ | |
665 | 403 Charbpos pos; |
428 | 404 struct buffer *buf = decode_buffer (buffer, 0); |
405 int col = current_column (buf); | |
406 int goal; | |
665 | 407 Charbpos end; |
428 | 408 int tab_width = XINT (buf->tab_width); |
409 | |
410 int prev_col = 0; | |
867 | 411 Ichar c = 0; |
428 | 412 |
793 | 413 buffer = wrap_buffer (buf); |
428 | 414 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; |
415 CHECK_NATNUM (column); | |
416 goal = XINT (column); | |
417 | |
418 retry: | |
419 pos = BUF_PT (buf); | |
420 end = BUF_ZV (buf); | |
421 | |
422 /* If we're starting past the desired column, | |
423 back up to beginning of line and scan from there. */ | |
424 if (col > goal) | |
425 { | |
426 pos = find_next_newline (buf, pos, -1); | |
427 col = 0; | |
428 } | |
429 | |
430 while (col < goal && pos < end) | |
431 { | |
432 c = BUF_FETCH_CHAR (buf, pos); | |
433 if (c == '\n') | |
434 break; | |
435 if (c == '\r' && EQ (buf->selective_display, Qt)) | |
436 break; | |
437 if (c == '\t') | |
438 { | |
439 prev_col = col; | |
440 col += tab_width; | |
441 col = col / tab_width * tab_width; | |
442 } | |
443 else | |
444 { | |
445 /* #### oh for the days of the complete new redisplay */ | |
446 /* #### FSFmacs looks at ctl_arrow, display tables. | |
447 We need to do similar. */ | |
448 #if 0 | |
665 | 449 displayed_glyphs = glyphs_from_charbpos (selected_frame (), |
428 | 450 buf, |
451 XWINDOW (Fselected_window (Qnil)), | |
452 pos, dp, 0, col, 0, 0, 0); | |
453 col += (displayed_glyphs->columns | |
454 - (displayed_glyphs->begin_columns | |
455 + displayed_glyphs->end_columns)); | |
456 #else /* XEmacs */ | |
457 #ifdef MULE | |
867 | 458 col += XCHARSET_COLUMNS (ichar_charset (c)); |
428 | 459 #else |
460 col ++; | |
461 #endif /* MULE */ | |
462 #endif /* XEmacs */ | |
463 } | |
464 | |
465 pos++; | |
466 } | |
467 | |
468 BUF_SET_PT (buf, pos); | |
469 | |
470 /* If a tab char made us overshoot, change it to spaces | |
471 and scan through it again. */ | |
472 if (!NILP (force) && col > goal && c == '\t' && prev_col < goal) | |
473 { | |
474 buffer_delete_range (buf, BUF_PT (buf) - 1, BUF_PT (buf), 0); | |
475 Findent_to (make_int (col - 1), Qzero, buffer); | |
476 buffer_insert_emacs_char (buf, ' '); | |
477 goto retry; | |
478 } | |
479 | |
480 /* If line ends prematurely, add space to the end. */ | |
481 if (col < goal && !NILP (force) && !EQ (force, Qcoerce)) | |
482 { | |
483 col = goal; | |
484 Findent_to (make_int (col), Qzero, buffer); | |
485 } | |
486 | |
487 last_known_column_buffer = buf; | |
488 last_known_column = col; | |
489 last_known_column_point = BUF_PT (buf); | |
490 last_known_column_modified = BUF_MODIFF (buf); | |
491 | |
492 return make_int (col); | |
493 } | |
494 | |
495 #if 0 /* #### OK boys, this function needs to be present, I think. | |
496 It was there before the 19.12 redisplay rewrite. */ | |
497 | |
826 | 498 DEFUN ("compute-motion", Fcompute_motion, 7, 7, 0, /* |
428 | 499 "Scan through the current buffer, calculating screen position. |
500 Scan the current buffer forward from offset FROM, | |
501 assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)-- | |
502 to position TO or position TOPOS--another cons of the form (HPOS . VPOS)-- | |
503 and return the ending buffer position and screen location. | |
504 | |
505 There are three additional arguments: | |
506 | |
507 WIDTH is the number of columns available to display text; | |
508 this affects handling of continuation lines. | |
509 This is usually the value returned by `window-width', less one (to allow | |
510 for the continuation glyph). | |
511 | |
512 OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET). | |
513 HSCROLL is the number of columns not being displayed at the left | |
514 margin; this is usually taken from a window's hscroll member. | |
515 TAB-OFFSET is the number of columns of the first tab that aren't | |
516 being displayed, perhaps because the line was continued within it. | |
517 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero. | |
518 | |
519 WINDOW is the window to operate on. Currently this is used only to | |
520 find the display table. It does not matter what buffer WINDOW displays; | |
521 `compute-motion' always operates on the current buffer. | |
522 | |
523 The value is a list of five elements: | |
524 (POS HPOS VPOS PREVHPOS CONTIN) | |
525 POS is the buffer position where the scan stopped. | |
526 VPOS is the vertical position where the scan stopped. | |
527 HPOS is the horizontal position where the scan stopped. | |
528 | |
529 PREVHPOS is the horizontal position one character back from POS. | |
530 CONTIN is t if a line was continued after (or within) the previous character. | |
531 | |
532 For example, to find the buffer position of column COL of line LINE | |
533 of a certain window, pass the window's starting location as FROM | |
534 and the window's upper-left coordinates as FROMPOS. | |
535 Pass the buffer's (point-max) as TO, to limit the scan to the end of the | |
536 visible section of the buffer, and pass LINE and COL as TOPOS. | |
537 */ | |
538 (from, frompos, to, topos, width, offsets, window)) | |
539 { | |
665 | 540 Lisp_Object charbpos, hpos, vpos, prevhpos, contin; |
428 | 541 struct position *pos; |
542 int hscroll, tab_offset; | |
543 struct window *w = decode_window (window); | |
544 | |
545 CHECK_INT_COERCE_MARKER (from); | |
546 CHECK_CONS (frompos); | |
547 CHECK_INT (XCAR (frompos)); | |
548 CHECK_INT (XCDR (frompos)); | |
549 CHECK_INT_COERCE_MARKER (to); | |
550 CHECK_CONS (topos); | |
551 CHECK_INT (XCAR (topos)); | |
552 CHECK_INT (XCDR (topos)); | |
553 CHECK_INT (width); | |
554 if (!NILP (offsets)) | |
555 { | |
556 CHECK_CONS (offsets); | |
557 CHECK_INT (XCAR (offsets)); | |
558 CHECK_INT (XCDR (offsets)); | |
559 hscroll = XINT (XCAR (offsets)); | |
560 tab_offset = XINT (XCDR (offsets)); | |
561 } | |
562 else | |
563 hscroll = tab_offset = 0; | |
564 | |
565 pos = compute_motion (XINT (from), XINT (XCDR (frompos)), | |
566 XINT (XCAR (frompos)), | |
567 XINT (to), XINT (XCDR (topos)), | |
568 XINT (XCAR (topos)), | |
569 XINT (width), hscroll, tab_offset, w); | |
570 | |
793 | 571 charbpos = make_int (pos->charbpos); |
572 hpos = make_int (pos->hpos); | |
573 vpos = make_int (pos->vpos); | |
574 prevhpos = make_int (pos->prevhpos); | |
428 | 575 |
665 | 576 return list5 (charbpos, hpos, vpos, prevhpos, |
428 | 577 pos->contin ? Qt : Qnil); |
578 } | |
579 | |
580 #endif /* 0 */ | |
581 | |
582 /* Helper for vmotion_1 - compute vertical pixel motion between | |
583 START and END in the line start cache CACHE. This just sums | |
584 the line heights, including both the starting and ending lines. | |
585 */ | |
586 static int | |
587 vpix_motion (line_start_cache_dynarr *cache, int start, int end) | |
588 { | |
589 int i, vpix; | |
590 | |
591 assert (start <= end); | |
592 assert (start >= 0); | |
593 assert (end < Dynarr_length (cache)); | |
594 | |
595 vpix = 0; | |
596 for (i = start; i <= end; i++) | |
597 vpix += Dynarr_atp (cache, i)->height; | |
598 | |
599 return vpix; | |
600 } | |
601 | |
602 /***************************************************************************** | |
603 vmotion_1 | |
604 | |
605 Given a starting position ORIG, move point VTARGET lines in WINDOW. | |
606 Returns the new value for point. If the arg ret_vpos is not nil, it is | |
607 taken to be a pointer to an int and the number of lines actually moved is | |
608 returned in it. If the arg ret_vpix is not nil, it is taken to be a | |
609 pointer to an int and the vertical pixel height of the motion which | |
610 took place is returned in it. | |
611 ****************************************************************************/ | |
665 | 612 static Charbpos |
613 vmotion_1 (struct window *w, Charbpos orig, int vtarget, | |
428 | 614 int *ret_vpos, int *ret_vpix) |
615 { | |
616 struct buffer *b = XBUFFER (w->buffer); | |
617 int elt; | |
618 | |
619 elt = point_in_line_start_cache (w, orig, (vtarget < 0 | |
620 ? -vtarget | |
621 : vtarget)); | |
622 | |
623 /* #### This assertion must be true before the if statements are hit | |
624 but may possibly be wrong after the call to | |
625 point_in_line_start_cache if orig is outside of the visible | |
626 region of the buffer. Handle this. */ | |
627 assert (elt >= 0); | |
628 | |
629 /* Moving downward. */ | |
630 if (vtarget > 0) | |
631 { | |
632 int cur_line = Dynarr_length (w->line_start_cache) - 1 - elt; | |
665 | 633 Charbpos ret_pt; |
428 | 634 |
635 if (cur_line > vtarget) | |
636 cur_line = vtarget; | |
637 | |
638 /* The traditional FSF behavior is to return the end of buffer | |
639 position if we couldn't move far enough because we hit it. */ | |
640 if (cur_line < vtarget) | |
641 ret_pt = BUF_ZV (b); | |
642 else | |
643 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start; | |
644 | |
645 while (ret_pt > BUF_ZV (b) && cur_line > 0) | |
646 { | |
647 cur_line--; | |
648 ret_pt = Dynarr_atp (w->line_start_cache, cur_line + elt)->start; | |
649 } | |
650 | |
651 if (ret_vpos) *ret_vpos = cur_line; | |
652 if (ret_vpix) | |
653 *ret_vpix = vpix_motion (w->line_start_cache, elt, cur_line + elt); | |
654 return ret_pt; | |
655 } | |
656 else if (vtarget < 0) | |
657 { | |
658 if (elt < -vtarget) | |
659 { | |
660 if (ret_vpos) *ret_vpos = -elt; | |
661 if (ret_vpix) | |
662 *ret_vpix = vpix_motion (w->line_start_cache, 0, elt); | |
663 /* #### This should be BUF_BEGV (b), right? */ | |
4967 | 664 return Dynarr_begin (w->line_start_cache)->start; |
428 | 665 } |
666 else | |
667 { | |
668 if (ret_vpos) *ret_vpos = vtarget; | |
669 if (ret_vpix) | |
670 *ret_vpix = vpix_motion (w->line_start_cache, elt + vtarget, elt); | |
671 return Dynarr_atp (w->line_start_cache, elt + vtarget)->start; | |
672 } | |
673 } | |
674 else | |
675 { | |
676 /* No vertical motion requested so we just return the position | |
677 of the beginning of the current line. */ | |
678 if (ret_vpos) *ret_vpos = 0; | |
679 if (ret_vpix) | |
680 *ret_vpix = vpix_motion (w->line_start_cache, elt, elt); | |
681 | |
682 return Dynarr_atp (w->line_start_cache, elt)->start; | |
683 } | |
684 | |
1204 | 685 RETURN_NOT_REACHED(0); /* shut up compiler */ |
428 | 686 } |
687 | |
688 /***************************************************************************** | |
689 vmotion | |
690 | |
691 Given a starting position ORIG, move point VTARGET lines in WINDOW. | |
692 Returns the new value for point. If the arg ret_vpos is not nil, it is | |
693 taken to be a pointer to an int and the number of lines actually moved is | |
694 returned in it. | |
695 ****************************************************************************/ | |
665 | 696 Charbpos |
697 vmotion (struct window *w, Charbpos orig, int vtarget, int *ret_vpos) | |
428 | 698 { |
699 return vmotion_1 (w, orig, vtarget, ret_vpos, NULL); | |
700 } | |
701 | |
702 /* Helper for Fvertical_motion. | |
703 */ | |
704 static | |
705 Lisp_Object vertical_motion_1 (Lisp_Object lines, Lisp_Object window, | |
706 int pixels) | |
707 { | |
665 | 708 Charbpos charbpos; |
709 Charbpos orig; | |
428 | 710 int selected; |
711 int *vpos, *vpix; | |
712 int value=0; | |
713 struct window *w; | |
714 | |
715 if (NILP (window)) | |
716 window = Fselected_window (Qnil); | |
717 | |
718 CHECK_LIVE_WINDOW (window); | |
719 CHECK_INT (lines); | |
720 | |
721 selected = (EQ (window, Fselected_window (Qnil))); | |
722 | |
723 w = XWINDOW (window); | |
724 | |
725 orig = selected ? BUF_PT (XBUFFER (w->buffer)) | |
726 : marker_position (w->pointm[CURRENT_DISP]); | |
727 | |
728 vpos = pixels ? NULL : &value; | |
729 vpix = pixels ? &value : NULL; | |
730 | |
665 | 731 charbpos = vmotion_1 (w, orig, XINT (lines), vpos, vpix); |
428 | 732 |
733 /* Note that the buffer's point is set, not the window's point. */ | |
734 if (selected) | |
665 | 735 BUF_SET_PT (XBUFFER (w->buffer), charbpos); |
428 | 736 else |
737 set_marker_restricted (w->pointm[CURRENT_DISP], | |
665 | 738 make_int(charbpos), |
428 | 739 w->buffer); |
740 | |
741 return make_int (value); | |
742 } | |
743 | |
744 DEFUN ("vertical-motion", Fvertical_motion, 1, 3, 0, /* | |
745 Move to start of frame line LINES lines down. | |
746 If LINES is negative, this is moving up. | |
747 Optional second argument is WINDOW to move in, | |
748 the default is the selected window. | |
749 | |
750 Sets point to position found; this may be start of line | |
751 or just the start of a continuation line. | |
752 If optional third argument PIXELS is nil, returns number | |
753 of lines moved; may be closer to zero than LINES if beginning | |
754 or end of buffer was reached. If PIXELS is non-nil, the | |
755 vertical pixel height of the motion which took place is | |
756 returned instead of the actual number of lines moved. A | |
757 motion of zero lines returns the height of the current line. | |
758 | |
1268 | 759 NOTE NOTE NOTE: GNU Emacs/XEmacs difference. |
760 | |
761 What `vertical-motion' actually does is set WINDOW's buffer's point | |
762 if WINDOW is the selected window; else, it sets WINDOW's point. | |
763 This is unfortunately somewhat tricky to work with, and different | |
764 from GNU Emacs, which always uses the current buffer, not WINDOW's | |
765 buffer, always sets current buffer's point, and, from the | |
766 perspective of this function, temporarily makes WINDOW display | |
767 the current buffer if it wasn't already. | |
428 | 768 */ |
769 (lines, window, pixels)) | |
770 { | |
771 return vertical_motion_1 (lines, window, !NILP (pixels)); | |
772 } | |
773 | |
774 /* | |
775 * Like vmotion() but requested and returned movement is in pixels. | |
776 * HOW specifies the stopping condition. Positive means move at least | |
777 * PIXELS. Negative means at most. Zero means as close as possible. | |
778 */ | |
665 | 779 Charbpos |
780 vmotion_pixels (Lisp_Object window, Charbpos start, int pixels, int how, | |
428 | 781 int *motion) |
782 { | |
783 struct window *w; | |
665 | 784 Charbpos eobuf, bobuf; |
428 | 785 int defheight; |
786 int needed; | |
787 int line, next; | |
788 int remain, abspix, dirn; | |
789 int elt, nelt; | |
790 int i; | |
791 line_start_cache_dynarr *cache; | |
792 int previous = -1; | |
793 int lines; | |
794 | |
795 if (NILP (window)) | |
796 window = Fselected_window (Qnil); | |
797 | |
798 CHECK_LIVE_WINDOW (window); | |
799 w = XWINDOW (window); | |
800 | |
801 eobuf = BUF_ZV (XBUFFER (w->buffer)); | |
802 bobuf = BUF_BEGV (XBUFFER (w->buffer)); | |
803 | |
5047
07dcc7000bbf
put width before height consistently, fix a real bug found in the process
Ben Wing <ben@xemacs.org>
parents:
4998
diff
changeset
|
804 default_face_width_and_height (window, NULL, &defheight); |
428 | 805 |
806 /* guess num lines needed in line start cache + a few extra */ | |
807 abspix = abs (pixels); | |
808 needed = (abspix + defheight-1)/defheight + 3; | |
809 | |
810 dirn = (pixels >= 0) ? 1 : -1; | |
811 | |
812 while (1) | |
813 { | |
814 elt = point_in_line_start_cache (w, start, needed); | |
815 assert (elt >= 0); /* in the cache */ | |
816 | |
817 cache = w->line_start_cache; | |
818 nelt = Dynarr_length (cache); | |
819 | |
820 *motion = 0; | |
821 | |
822 if (pixels == 0) | |
823 /* No vertical motion requested so we just return the position | |
824 of the beginning of the current display line. */ | |
825 return Dynarr_atp (cache, elt)->start; | |
826 | |
827 if ((dirn < 0 && elt == 0 && | |
828 Dynarr_atp (cache, elt)->start <= bobuf) || | |
829 (dirn > 0 && elt == nelt-1 && | |
830 Dynarr_atp (cache, elt)->end >= eobuf)) | |
831 return Dynarr_atp (cache, elt)->start; | |
832 | |
833 remain = abspix; | |
834 for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn) | |
835 { | |
836 /* cache line we're considering moving over */ | |
837 int ii = (dirn > 0) ? i : i-1; | |
838 | |
839 if (remain < 0) | |
840 return Dynarr_atp (cache, i)->start; | |
841 | |
842 line = Dynarr_atp (cache, ii)->height; | |
843 next = remain - line; | |
844 | |
845 /* is stopping condition satisfied? */ | |
846 if ((how > 0 && remain <= 0) || /* at least */ | |
847 (how < 0 && next < 0) || /* at most */ | |
848 (how == 0 && remain <= abs (next))) /* closest */ | |
849 return Dynarr_atp (cache, i)->start; | |
850 | |
851 /* moving down and nowhere left to go? */ | |
852 if (dirn > 0 && Dynarr_atp (cache, ii)->end >= eobuf) | |
853 return Dynarr_atp (cache, ii)->start; | |
854 | |
855 /* take the step */ | |
856 remain = next; | |
857 *motion += dirn * line; | |
858 | |
859 /* moving up and nowhere left to go? */ | |
860 if (dirn < 0 && Dynarr_atp (cache, ii)->start <= bobuf) | |
861 return Dynarr_atp (cache, ii)->start; | |
862 } | |
863 | |
864 /* get here => need more cache lines. try again. */ | |
865 assert (abs (*motion) > previous); /* progress? */ | |
866 previous = abs (*motion); | |
867 | |
868 lines = (pixels < 0) ? elt : (nelt - elt); | |
869 needed += (remain*lines + abspix-1)/abspix + 3; | |
870 } | |
871 | |
1204 | 872 RETURN_NOT_REACHED(0); /* shut up compiler */ |
428 | 873 } |
874 | |
875 DEFUN ("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /* | |
876 Move to start of frame line PIXELS vertical pixels down. | |
877 If PIXELS is negative, this is moving up. | |
878 The actual vertical motion in pixels is returned. | |
879 | |
880 Optional second argument is WINDOW to move in, | |
881 the default is the selected window. | |
882 | |
883 Optional third argument HOW specifies when to stop. A value | |
884 less than zero indicates that the motion should be no more | |
885 than PIXELS. A value greater than zero indicates that the | |
886 motion should be at least PIXELS. Any other value indicates | |
887 that the motion should be as close as possible to PIXELS. | |
888 */ | |
889 (pixels, window, how)) | |
890 { | |
665 | 891 Charbpos charbpos; |
892 Charbpos orig; | |
428 | 893 int selected; |
894 int motion; | |
895 int howto; | |
896 struct window *w; | |
897 | |
898 if (NILP (window)) | |
899 window = Fselected_window (Qnil); | |
900 | |
901 CHECK_LIVE_WINDOW (window); | |
902 CHECK_INT (pixels); | |
903 | |
904 selected = (EQ (window, Fselected_window (Qnil))); | |
905 | |
906 w = XWINDOW (window); | |
907 | |
908 orig = selected ? BUF_PT (XBUFFER (w->buffer)) | |
909 : marker_position (w->pointm[CURRENT_DISP]); | |
910 | |
911 howto = INTP (how) ? XINT (how) : 0; | |
912 | |
665 | 913 charbpos = vmotion_pixels (window, orig, XINT (pixels), howto, &motion); |
428 | 914 |
915 if (selected) | |
665 | 916 BUF_SET_PT (XBUFFER (w->buffer), charbpos); |
428 | 917 else |
918 set_marker_restricted (w->pointm[CURRENT_DISP], | |
665 | 919 make_int(charbpos), |
428 | 920 w->buffer); |
921 | |
922 return make_int (motion); | |
923 } | |
924 | |
925 | |
926 void | |
927 syms_of_indent (void) | |
928 { | |
929 DEFSUBR (Fcurrent_indentation); | |
930 DEFSUBR (Findent_to); | |
931 DEFSUBR (Fcurrent_column); | |
932 DEFSUBR (Fmove_to_column); | |
933 #if 0 /* #### */ | |
934 DEFSUBR (Fcompute_motion); | |
935 #endif | |
936 DEFSUBR (Fvertical_motion); | |
937 DEFSUBR (Fvertical_motion_pixels); | |
938 } | |
939 | |
940 void | |
941 vars_of_indent (void) | |
942 { | |
943 DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode /* | |
944 *Indentation can insert tabs if this is non-nil. | |
945 Setting this variable automatically makes it local to the current buffer. | |
946 */ ); | |
947 indent_tabs_mode = 1; | |
948 } |