diff options
| author | Richard M. Stallman | 1996-06-23 18:47:37 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1996-06-23 18:47:37 +0000 |
| commit | 9a21bb6444091c11d10f076c6c1b4dd4f9be57cb (patch) | |
| tree | 1884344a97402d0d74c2e7113c51d012b7130562 /src | |
| parent | 5aa29679e1b852b258d6e7895cd55c250d568111 (diff) | |
| download | emacs-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.c | 249 |
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 | |||
| 194 | static int | ||
| 195 | skip_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 | |||
| 171 | DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0, | 247 | DEFUN ("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\ |
| 173 | This is calculated by adding together the widths of all the displayed\n\ | 249 | This 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 | |||
| 368 | static int | ||
| 369 | current_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 | ||
| 466 | DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, "p", | 645 | DEFUN ("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. */ |