aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard M. Stallman1996-06-23 18:47:37 +0000
committerRichard M. Stallman1996-06-23 18:47:37 +0000
commit9a21bb6444091c11d10f076c6c1b4dd4f9be57cb (patch)
tree1884344a97402d0d74c2e7113c51d012b7130562 /src
parent5aa29679e1b852b258d6e7895cd55c250d568111 (diff)
downloademacs-9a21bb6444091c11d10f076c6c1b4dd4f9be57cb.tar.gz
emacs-9a21bb6444091c11d10f076c6c1b4dd4f9be57cb.zip
Make current-column, move-to-column and current-indentation
handle invisible chars (both text properties and overlays). (skip_invisible): New function. (Fmove_to_column): Use skip_invisible. Get rid of `retry' label. (compute_motion): Use skip_invisible. (current_column_1): New function. (current_column): Use current_column_1 if might have invisible text.
Diffstat (limited to 'src')
-rw-r--r--src/indent.c249
1 files changed, 200 insertions, 49 deletions
diff --git a/src/indent.c b/src/indent.c
index 452718cac77..a40006b9a5a 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -168,6 +168,82 @@ width_run_cache_on_off ()
168} 168}
169 169
170 170
171/* Skip some invisible characters starting from POS.
172 This includes characters invisible because of text properties
173 and characters invisible because of overlays.
174
175 If position POS is followed by invisible characters,
176 skip some of them and return the position after them.
177 Otherwise return POS itself.
178
179 Set *NEXT_BOUNDARY_P to the next position at which
180 it will be necessary to call this function again.
181
182 Don't scan past TO, and don't set *NEXT_BOUNDARY_P
183 to a value greater than TO.
184
185 If WINDOW is non-nil, and this buffer is displayed in WINDOW,
186 take account of overlays that apply only in WINDOW.
187
188 We don't necessarily skip all the invisible characters after POS
189 because that could take a long time. We skip a reasonable number
190 which can be skipped quickly. If there might be more invisible
191 characters immediately following, then *NEXT_BOUNDARY_P
192 will equal the return value. */
193
194static int
195skip_invisible (pos, next_boundary_p, to, window)
196 int pos;
197 int *next_boundary_p;
198 int to;
199 Lisp_Object window;
200{
201 Lisp_Object prop, position, end, overlay_limit, proplimit;
202 Lisp_Object buffer;
203
204 XSETFASTINT (position, pos);
205 XSETBUFFER (buffer, current_buffer);
206
207 /* Give faster response for overlay lookup near POS. */
208 recenter_overlay_lists (current_buffer, pos);
209
210 /* We must not advance farther than the next overlay change.
211 The overlay change might change the invisible property;
212 or there might be overlay strings to be displayed there. */
213 overlay_limit = Fnext_overlay_change (position);
214 /* As for text properties, this gives a lower bound
215 for where the invisible text property could change. */
216 proplimit = Fnext_property_change (position, buffer, Qt);
217 if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
218 proplimit = overlay_limit;
219 /* PROPLIMIT is now a lower bound for the next change
220 in invisible status. If that is plenty far away,
221 use that lower bound. */
222 if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
223 *next_boundary_p = XFASTINT (proplimit);
224 /* Otherwise, scan for the next `invisible' property change. */
225 else
226 {
227 /* Don't scan terribly far. */
228 XSETFASTINT (proplimit, min (pos + 100, to));
229 /* No matter what. don't go past next overlay change. */
230 if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
231 proplimit = overlay_limit;
232 end = Fnext_single_property_change (position, Qinvisible,
233 buffer, proplimit);
234 *next_boundary_p = XFASTINT (end);
235 }
236 /* if the `invisible' property is set, we can skip to
237 the next property change */
238 if (!NILP (window) && EQ (XWINDOW (window)->buffer, buffer))
239 prop = Fget_char_property (position, Qinvisible, window);
240 else
241 prop = Fget_char_property (position, Qinvisible, buffer);
242 if (TEXT_PROP_MEANS_INVISIBLE (prop))
243 return *next_boundary_p;
244 return pos;
245}
246
171DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0, 247DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
172 "Return the horizontal position of point. Beginning of line is column 0.\n\ 248 "Return the horizontal position of point. Beginning of line is column 0.\n\
173This is calculated by adding together the widths of all the displayed\n\ 249This is calculated by adding together the widths of all the displayed\n\
@@ -209,6 +285,16 @@ current_column ()
209 && MODIFF == last_known_column_modified) 285 && MODIFF == last_known_column_modified)
210 return last_known_column; 286 return last_known_column;
211 287
288 /* If the buffer has overlays or text properties,
289 use a more general algorithm. */
290 if (BUF_INTERVALS (current_buffer)
291 || !NILP (current_buffer->overlays_before)
292 || !NILP (current_buffer->overlays_after))
293 return current_column_1 (point);
294
295 /* Scan backwards from point to the previous newline,
296 counting width. Tab characters are the only complicated case. */
297
212 /* Make a pointer for decrementing through the chars before point. */ 298 /* Make a pointer for decrementing through the chars before point. */
213 ptr = &FETCH_CHAR (point - 1) + 1; 299 ptr = &FETCH_CHAR (point - 1) + 1;
214 /* Make a pointer to where consecutive chars leave off, 300 /* Make a pointer to where consecutive chars leave off,
@@ -274,6 +360,75 @@ current_column ()
274 return col; 360 return col;
275} 361}
276 362
363/* Return the column number of position POS
364 by scanning forward from the beginning of the line.
365 This function handles characters that are invisible
366 due to text properties or overlays. */
367
368static int
369current_column_1 (pos)
370 int pos;
371{
372 register int tab_width = XINT (current_buffer->tab_width);
373 register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
374 register struct Lisp_Char_Table *dp = buffer_display_table ();
375
376 /* Start the scan at the beginning of this line with column number 0. */
377 register int col = 0;
378 int scan = find_next_newline (pos, -1);
379 int next_boundary = scan;
380
381 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
382
383 /* Scan forward to the target position. */
384 while (scan < pos)
385 {
386 int c;
387
388 /* Occasionally we may need to skip invisible text. */
389 while (scan == next_boundary)
390 {
391 /* This updates NEXT_BOUNDARY to the next place
392 where we might need to skip more invisible text. */
393 scan = skip_invisible (scan, &next_boundary, pos, Qnil);
394 if (scan >= pos)
395 goto endloop;
396 }
397
398 c = FETCH_CHAR (scan);
399 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
400 {
401 col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
402 scan++;
403 continue;
404 }
405 if (c == '\n')
406 break;
407 if (c == '\r' && EQ (current_buffer->selective_display, Qt))
408 break;
409 scan++;
410 if (c == '\t')
411 {
412 int prev_col = col;
413 col += tab_width;
414 col = col / tab_width * tab_width;
415 }
416 else if (ctl_arrow && (c < 040 || c == 0177))
417 col += 2;
418 else if (c < 040 || c >= 0177)
419 col += 4;
420 else
421 col++;
422 }
423 endloop:
424
425 last_known_column = col;
426 last_known_column_point = point;
427 last_known_column_modified = MODIFF;
428
429 return col;
430}
431
277/* Return the width in columns of the part of STRING from BEG to END. 432/* Return the width in columns of the part of STRING from BEG to END.
278 If BEG is nil, that stands for the beginning of STRING. 433 If BEG is nil, that stands for the beginning of STRING.
279 If END is nil, that stands for the end of STRING. */ 434 If END is nil, that stands for the end of STRING. */
@@ -421,20 +576,45 @@ position_indentation (pos)
421 register int tab_width = XINT (current_buffer->tab_width); 576 register int tab_width = XINT (current_buffer->tab_width);
422 register unsigned char *p; 577 register unsigned char *p;
423 register unsigned char *stop; 578 register unsigned char *stop;
579 unsigned char *start;
580 int next_boundary = pos;
581 int ceiling = pos;
424 582
425 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; 583 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
426 584
427 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
428 p = &FETCH_CHAR (pos); 585 p = &FETCH_CHAR (pos);
586 /* STOP records the value of P at which we will need
587 to think about the gap, or about invisible text,
588 or about the end of the buffer. */
589 stop = p;
590 /* START records the starting value of P. */
591 start = p;
429 while (1) 592 while (1)
430 { 593 {
431 while (p == stop) 594 while (p == stop)
432 { 595 {
596 int stop_pos;
597
598 /* If we have updated P, set POS to match.
599 The first time we enter the loop, POS is already right. */
600 if (p != start)
601 pos = PTR_CHAR_POS (p);
602 /* Consider the various reasons STOP might have been set here. */
433 if (pos == ZV) 603 if (pos == ZV)
434 return column; 604 return column;
435 pos += p - &FETCH_CHAR (pos); 605 if (pos == next_boundary)
606 pos = skip_invisible (pos, &next_boundary, ZV, Qnil);
607 if (pos >= ceiling)
608 ceiling = BUFFER_CEILING_OF (pos) + 1;
609 /* Compute the next place we need to stop and think,
610 and set STOP accordingly. */
611 stop_pos = min (ceiling, next_boundary);
612 /* The -1 and +1 arrange to point at the first byte of gap
613 (if STOP_POS is the position of the gap)
614 rather than at the data after the gap. */
615
616 stop = &FETCH_CHAR (stop_pos - 1) + 1;
436 p = &FETCH_CHAR (pos); 617 p = &FETCH_CHAR (pos);
437 stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
438 } 618 }
439 switch (*p++) 619 switch (*p++)
440 { 620 {
@@ -461,7 +641,6 @@ indented_beyond_p (pos, column)
461 pos = find_next_newline_no_quit (pos - 1, -1); 641 pos = find_next_newline_no_quit (pos - 1, -1);
462 return (position_indentation (pos) >= column); 642 return (position_indentation (pos) >= column);
463} 643}
464
465 644
466DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, "p", 645DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, "p",
467 "Move point to column COLUMN in the current line.\n\ 646 "Move point to column COLUMN in the current line.\n\
@@ -493,24 +672,34 @@ The return value is the current column.")
493 int prev_col; 672 int prev_col;
494 int c; 673 int c;
495 674
675 int next_boundary;
676
496 if (tab_width <= 0 || tab_width > 1000) tab_width = 8; 677 if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
497 CHECK_NATNUM (column, 0); 678 CHECK_NATNUM (column, 0);
498 goal = XINT (column); 679 goal = XINT (column);
499 680
500 retry:
501 pos = point; 681 pos = point;
502 end = ZV; 682 end = ZV;
683 next_boundary = pos;
503 684
504 /* If we're starting past the desired column, 685 /* If we're starting past the desired column,
505 back up to beginning of line and scan from there. */ 686 back up to beginning of line and scan from there. */
506 if (col > goal) 687 if (col > goal)
507 { 688 {
689 end = pos;
508 pos = find_next_newline (pos, -1); 690 pos = find_next_newline (pos, -1);
509 col = 0; 691 col = 0;
510 } 692 }
511 693
512 while (col < goal && pos < end) 694 while (col < goal && pos < end)
513 { 695 {
696 while (pos == next_boundary)
697 {
698 pos = skip_invisible (pos, &next_boundary, end, Qnil);
699 if (pos >= end)
700 goto endloop;
701 }
702
514 c = FETCH_CHAR (pos); 703 c = FETCH_CHAR (pos);
515 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))) 704 if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
516 { 705 {
@@ -536,6 +725,7 @@ The return value is the current column.")
536 else 725 else
537 col++; 726 col++;
538 } 727 }
728 endloop:
539 729
540 SET_PT (pos); 730 SET_PT (pos);
541 731
@@ -565,7 +755,6 @@ The return value is the current column.")
565 XSETFASTINT (val, col); 755 XSETFASTINT (val, col);
566 return val; 756 return val;
567} 757}
568
569 758
570/* compute_motion: compute buffer posn given screen posn and vice versa */ 759/* compute_motion: compute buffer posn given screen posn and vice versa */
571 760
@@ -713,49 +902,11 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width,
713 if (pos >= to) 902 if (pos >= to)
714 break; 903 break;
715 904
716 { 905 /* Advance POS past invisible characters
717 Lisp_Object prop, position, end, limit, proplimit; 906 (but not necessarily all that there are here),
718 907 and store in next_boundary the next position where
719 XSETFASTINT (position, pos); 908 we need to call skip_invisible. */
720 909 pos = skip_invisible (pos, &next_boundary, to, window);
721 /* Give faster response for overlay lookup near POS. */
722 recenter_overlay_lists (current_buffer, pos);
723
724 /* We must not advance farther than the next overlay change.
725 The overlay change might change the invisible property;
726 or there might be overlay strings to be displayed there. */
727 limit = Fnext_overlay_change (position);
728 /* As for text properties, this gives a lower bound
729 for where the invisible text property could change. */
730 proplimit = Fnext_property_change (position, buffer, Qt);
731 if (XFASTINT (limit) < XFASTINT (proplimit))
732 proplimit = limit;
733 /* PROPLIMIT is now a lower bound for the next change
734 in invisible status. If that is plenty far away,
735 use that lower bound. */
736 if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
737 next_boundary = XFASTINT (proplimit);
738 /* Otherwise, scan for the next `invisible' property change. */
739 else
740 {
741 /* Don't scan terribly far. */
742 XSETFASTINT (proplimit, min (pos + 100, to));
743 /* No matter what. don't go past next overlay change. */
744 if (XFASTINT (limit) < XFASTINT (proplimit))
745 proplimit = limit;
746 end = Fnext_single_property_change (position, Qinvisible,
747 buffer, proplimit);
748 next_boundary = XFASTINT (end);
749 }
750 /* if the `invisible' property is set, we can skip to
751 the next property change */
752 if (EQ (win->buffer, buffer))
753 prop = Fget_char_property (position, Qinvisible, window);
754 else
755 prop = Fget_char_property (position, Qinvisible, buffer);
756 if (TEXT_PROP_MEANS_INVISIBLE (prop))
757 pos = next_boundary;
758 }
759 } 910 }
760 911
761 /* Handle right margin. */ 912 /* Handle right margin. */