1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10 /* 11 * textobject.c: functions for text objects 12 */ 13 #include "vim.h" 14 15 static int cls(void); 16 static int skip_chars(int, int); 17 18 /* 19 * Find the start of the next sentence, searching in the direction specified 20 * by the "dir" argument. The cursor is positioned on the start of the next 21 * sentence when found. If the next sentence is found, return OK. Return FAIL 22 * otherwise. See ":h sentence" for the precise definition of a "sentence" 23 * text object. 24 */ 25 int 26 findsent(int dir, long count) 27 { 28 pos_T pos, tpos; 29 pos_T prev_pos; 30 int c; 31 int (*func)(pos_T *); 32 int startlnum; 33 int noskip = FALSE; // do not skip blanks 34 int cpo_J; 35 int found_dot; 36 37 pos = curwin->w_cursor; 38 if (dir == FORWARD) 39 func = incl; 40 else 41 func = decl; 42 43 while (count--) 44 { 45 prev_pos = pos; 46 47 /* 48 * if on an empty line, skip up to a non-empty line 49 */ 50 if (gchar_pos(&pos) == NUL) 51 { 52 do 53 if ((*func)(&pos) == -1) 54 break; 55 while (gchar_pos(&pos) == NUL); 56 if (dir == FORWARD) 57 goto found; 58 } 59 /* 60 * if on the start of a paragraph or a section and searching forward, 61 * go to the next line 62 */ 63 else if (dir == FORWARD && pos.col == 0 && 64 startPS(pos.lnum, NUL, FALSE)) 65 { 66 if (pos.lnum == curbuf->b_ml.ml_line_count) 67 return FAIL; 68 ++pos.lnum; 69 goto found; 70 } 71 else if (dir == BACKWARD) 72 decl(&pos); 73 74 // go back to the previous non-white non-punctuation character 75 found_dot = FALSE; 76 while (c = gchar_pos(&pos), VIM_ISWHITE(c) 77 || vim_strchr((char_u *)".!?)]\"'", c) != NULL) 78 { 79 tpos = pos; 80 if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) 81 break; 82 83 if (found_dot) 84 break; 85 if (vim_strchr((char_u *) ".!?", c) != NULL) 86 found_dot = TRUE; 87 88 if (vim_strchr((char_u *) ")]\"'", c) != NULL 89 && vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL) 90 break; 91 92 decl(&pos); 93 } 94 95 // remember the line where the search started 96 startlnum = pos.lnum; 97 cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; 98 99 for (;;) // find end of sentence 100 { 101 c = gchar_pos(&pos); 102 if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) 103 { 104 if (dir == BACKWARD && pos.lnum != startlnum) 105 ++pos.lnum; 106 break; 107 } 108 if (c == '.' || c == '!' || c == '?') 109 { 110 tpos = pos; 111 do 112 if ((c = inc(&tpos)) == -1) 113 break; 114 while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos)) 115 != NULL); 116 if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL 117 || (cpo_J && (c == ' ' && inc(&tpos) >= 0 118 && gchar_pos(&tpos) == ' '))) 119 { 120 pos = tpos; 121 if (gchar_pos(&pos) == NUL) // skip NUL at EOL 122 inc(&pos); 123 break; 124 } 125 } 126 if ((*func)(&pos) == -1) 127 { 128 if (count) 129 return FAIL; 130 noskip = TRUE; 131 break; 132 } 133 } 134 found: 135 // skip white space 136 while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) 137 if (incl(&pos) == -1) 138 break; 139 140 if (EQUAL_POS(prev_pos, pos)) 141 { 142 // didn't actually move, advance one character and try again 143 if ((*func)(&pos) == -1) 144 { 145 if (count) 146 return FAIL; 147 break; 148 } 149 ++count; 150 } 151 } 152 153 setpcmark(); 154 curwin->w_cursor = pos; 155 return OK; 156 } 157 158 /* 159 * Find the next paragraph or section in direction 'dir'. 160 * Paragraphs are currently supposed to be separated by empty lines. 161 * If 'what' is NUL we go to the next paragraph. 162 * If 'what' is '{' or '}' we go to the next section. 163 * If 'both' is TRUE also stop at '}'. 164 * Return TRUE if the next paragraph or section was found. 165 */ 166 int 167 findpar( 168 int *pincl, // Return: TRUE if last char is to be included 169 int dir, 170 long count, 171 int what, 172 int both) 173 { 174 linenr_T curr; 175 int did_skip; // TRUE after separating lines have been skipped 176 int first; // TRUE on first line 177 int posix = (vim_strchr(p_cpo, CPO_PARA) != NULL); 178 #ifdef FEAT_FOLDING 179 linenr_T fold_first; // first line of a closed fold 180 linenr_T fold_last; // last line of a closed fold 181 int fold_skipped; // TRUE if a closed fold was skipped this 182 // iteration 183 #endif 184 185 curr = curwin->w_cursor.lnum; 186 187 while (count--) 188 { 189 did_skip = FALSE; 190 for (first = TRUE; ; first = FALSE) 191 { 192 if (*ml_get(curr) != NUL) 193 did_skip = TRUE; 194 195 #ifdef FEAT_FOLDING 196 // skip folded lines 197 fold_skipped = FALSE; 198 if (first && hasFolding(curr, &fold_first, &fold_last)) 199 { 200 curr = ((dir > 0) ? fold_last : fold_first) + dir; 201 fold_skipped = TRUE; 202 } 203 #endif 204 205 // POSIX has its own ideas of what a paragraph boundary is and it 206 // doesn't match historical Vi: It also stops at a "{" in the 207 // first column and at an empty line. 208 if (!first && did_skip && (startPS(curr, what, both) 209 || (posix && what == NUL && *ml_get(curr) == '{'))) 210 break; 211 212 #ifdef FEAT_FOLDING 213 if (fold_skipped) 214 curr -= dir; 215 #endif 216 if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) 217 { 218 if (count) 219 return FALSE; 220 curr -= dir; 221 break; 222 } 223 } 224 } 225 setpcmark(); 226 if (both && *ml_get(curr) == '}') // include line with '}' 227 ++curr; 228 curwin->w_cursor.lnum = curr; 229 if (curr == curbuf->b_ml.ml_line_count && what != '}') 230 { 231 char_u *line = ml_get(curr); 232 233 // Put the cursor on the last character in the last line and make the 234 // motion inclusive. 235 if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) 236 { 237 --curwin->w_cursor.col; 238 curwin->w_cursor.col -= 239 (*mb_head_off)(line, line + curwin->w_cursor.col); 240 *pincl = TRUE; 241 } 242 } 243 else 244 curwin->w_cursor.col = 0; 245 return TRUE; 246 } 247 248 /* 249 * check if the string 's' is a nroff macro that is in option 'opt' 250 */ 251 static int 252 inmacro(char_u *opt, char_u *s) 253 { 254 char_u *macro; 255 256 for (macro = opt; macro[0]; ++macro) 257 { 258 // Accept two characters in the option being equal to two characters 259 // in the line. A space in the option matches with a space in the 260 // line or the line having ended. 261 if ( (macro[0] == s[0] 262 || (macro[0] == ' ' 263 && (s[0] == NUL || s[0] == ' '))) 264 && (macro[1] == s[1] 265 || ((macro[1] == NUL || macro[1] == ' ') 266 && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) 267 break; 268 ++macro; 269 if (macro[0] == NUL) 270 break; 271 } 272 return (macro[0] != NUL); 273 } 274 275 /* 276 * startPS: return TRUE if line 'lnum' is the start of a section or paragraph. 277 * If 'para' is '{' or '}' only check for sections. 278 * If 'both' is TRUE also stop at '}' 279 */ 280 int 281 startPS(linenr_T lnum, int para, int both) 282 { 283 char_u *s; 284 285 s = ml_get(lnum); 286 if (*s == para || *s == '\f' || (both && *s == '}')) 287 return TRUE; 288 if (*s == '.' && (inmacro(p_sections, s + 1) || 289 (!para && inmacro(p_para, s + 1)))) 290 return TRUE; 291 return FALSE; 292 } 293 294 /* 295 * The following routines do the word searches performed by the 'w', 'W', 296 * 'b', 'B', 'e', and 'E' commands. 297 */ 298 299 /* 300 * To perform these searches, characters are placed into one of three 301 * classes, and transitions between classes determine word boundaries. 302 * 303 * The classes are: 304 * 305 * 0 - white space 306 * 1 - punctuation 307 * 2 or higher - keyword characters (letters, digits and underscore) 308 */ 309 310 static int cls_bigword; // TRUE for "W", "B" or "E" 311 312 /* 313 * cls() - returns the class of character at curwin->w_cursor 314 * 315 * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars 316 * from class 2 and higher are reported as class 1 since only white space 317 * boundaries are of interest. 318 */ 319 static int 320 cls(void) 321 { 322 int c; 323 324 c = gchar_cursor(); 325 if (c == ' ' || c == '\t' || c == NUL) 326 return 0; 327 if (enc_dbcs != 0 && c > 0xFF) 328 { 329 // If cls_bigword, report multi-byte chars as class 1. 330 if (enc_dbcs == DBCS_KOR && cls_bigword) 331 return 1; 332 333 // process code leading/trailing bytes 334 return dbcs_class(((unsigned)c >> 8), (c & 0xFF)); 335 } 336 if (enc_utf8) 337 { 338 c = utf_class(c); 339 if (c != 0 && cls_bigword) 340 return 1; 341 return c; 342 } 343 344 // If cls_bigword is TRUE, report all non-blanks as class 1. 345 if (cls_bigword) 346 return 1; 347 348 if (vim_iswordc(c)) 349 return 2; 350 return 1; 351 } 352 353 354 /* 355 * fwd_word(count, type, eol) - move forward one word 356 * 357 * Returns FAIL if the cursor was already at the end of the file. 358 * If eol is TRUE, last word stops at end of line (for operators). 359 */ 360 int 361 fwd_word( 362 long count, 363 int bigword, // "W", "E" or "B" 364 int eol) 365 { 366 int sclass; // starting class 367 int i; 368 int last_line; 369 370 curwin->w_cursor.coladd = 0; 371 cls_bigword = bigword; 372 while (--count >= 0) 373 { 374 #ifdef FEAT_FOLDING 375 // When inside a range of folded lines, move to the last char of the 376 // last line. 377 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 378 coladvance((colnr_T)MAXCOL); 379 #endif 380 sclass = cls(); 381 382 /* 383 * We always move at least one character, unless on the last 384 * character in the buffer. 385 */ 386 last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count); 387 i = inc_cursor(); 388 if (i == -1 || (i >= 1 && last_line)) // started at last char in file 389 return FAIL; 390 if (i >= 1 && eol && count == 0) // started at last char in line 391 return OK; 392 393 /* 394 * Go one char past end of current word (if any) 395 */ 396 if (sclass != 0) 397 while (cls() == sclass) 398 { 399 i = inc_cursor(); 400 if (i == -1 || (i >= 1 && eol && count == 0)) 401 return OK; 402 } 403 404 /* 405 * go to next non-white 406 */ 407 while (cls() == 0) 408 { 409 /* 410 * We'll stop if we land on a blank line 411 */ 412 if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL) 413 break; 414 415 i = inc_cursor(); 416 if (i == -1 || (i >= 1 && eol && count == 0)) 417 return OK; 418 } 419 } 420 return OK; 421 } 422 423 /* 424 * bck_word() - move backward 'count' words 425 * 426 * If stop is TRUE and we are already on the start of a word, move one less. 427 * 428 * Returns FAIL if top of the file was reached. 429 */ 430 int 431 bck_word(long count, int bigword, int stop) 432 { 433 int sclass; // starting class 434 435 curwin->w_cursor.coladd = 0; 436 cls_bigword = bigword; 437 while (--count >= 0) 438 { 439 #ifdef FEAT_FOLDING 440 // When inside a range of folded lines, move to the first char of the 441 // first line. 442 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) 443 curwin->w_cursor.col = 0; 444 #endif 445 sclass = cls(); 446 if (dec_cursor() == -1) // started at start of file 447 return FAIL; 448 449 if (!stop || sclass == cls() || sclass == 0) 450 { 451 /* 452 * Skip white space before the word. 453 * Stop on an empty line. 454 */ 455 while (cls() == 0) 456 { 457 if (curwin->w_cursor.col == 0 458 && LINEEMPTY(curwin->w_cursor.lnum)) 459 goto finished; 460 if (dec_cursor() == -1) // hit start of file, stop here 461 return OK; 462 } 463 464 /* 465 * Move backward to start of this word. 466 */ 467 if (skip_chars(cls(), BACKWARD)) 468 return OK; 469 } 470 471 inc_cursor(); // overshot - forward one 472 finished: 473 stop = FALSE; 474 } 475 return OK; 476 } 477 478 /* 479 * end_word() - move to the end of the word 480 * 481 * There is an apparent bug in the 'e' motion of the real vi. At least on the 482 * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e' 483 * motion crosses blank lines. When the real vi crosses a blank line in an 484 * 'e' motion, the cursor is placed on the FIRST character of the next 485 * non-blank line. The 'E' command, however, works correctly. Since this 486 * appears to be a bug, I have not duplicated it here. 487 * 488 * Returns FAIL if end of the file was reached. 489 * 490 * If stop is TRUE and we are already on the end of a word, move one less. 491 * If empty is TRUE stop on an empty line. 492 */ 493 int 494 end_word( 495 long count, 496 int bigword, 497 int stop, 498 int empty) 499 { 500 int sclass; // starting class 501 502 curwin->w_cursor.coladd = 0; 503 cls_bigword = bigword; 504 while (--count >= 0) 505 { 506 #ifdef FEAT_FOLDING 507 // When inside a range of folded lines, move to the last char of the 508 // last line. 509 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) 510 coladvance((colnr_T)MAXCOL); 511 #endif 512 sclass = cls(); 513 if (inc_cursor() == -1) 514 return FAIL; 515 516 /* 517 * If we're in the middle of a word, we just have to move to the end 518 * of it. 519 */ 520 if (cls() == sclass && sclass != 0) 521 { 522 /* 523 * Move forward to end of the current word 524 */ 525 if (skip_chars(sclass, FORWARD)) 526 return FAIL; 527 } 528 else if (!stop || sclass == 0) 529 { 530 /* 531 * We were at the end of a word. Go to the end of the next word. 532 * First skip white space, if 'empty' is TRUE, stop at empty line. 533 */ 534 while (cls() == 0) 535 { 536 if (empty && curwin->w_cursor.col == 0 537 && LINEEMPTY(curwin->w_cursor.lnum)) 538 goto finished; 539 if (inc_cursor() == -1) // hit end of file, stop here 540 return FAIL; 541 } 542 543 /* 544 * Move forward to the end of this word. 545 */ 546 if (skip_chars(cls(), FORWARD)) 547 return FAIL; 548 } 549 dec_cursor(); // overshot - one char backward 550 finished: 551 stop = FALSE; // we move only one word less 552 } 553 return OK; 554 } 555 556 /* 557 * Move back to the end of the word. 558 * 559 * Returns FAIL if start of the file was reached. 560 */ 561 int 562 bckend_word( 563 long count, 564 int bigword, // TRUE for "B" 565 int eol) // TRUE: stop at end of line. 566 { 567 int sclass; // starting class 568 int i; 569 570 curwin->w_cursor.coladd = 0; 571 cls_bigword = bigword; 572 while (--count >= 0) 573 { 574 sclass = cls(); 575 if ((i = dec_cursor()) == -1) 576 return FAIL; 577 if (eol && i == 1) 578 return OK; 579 580 /* 581 * Move backward to before the start of this word. 582 */ 583 if (sclass != 0) 584 { 585 while (cls() == sclass) 586 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 587 return OK; 588 } 589 590 /* 591 * Move backward to end of the previous word 592 */ 593 while (cls() == 0) 594 { 595 if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) 596 break; 597 if ((i = dec_cursor()) == -1 || (eol && i == 1)) 598 return OK; 599 } 600 } 601 return OK; 602 } 603 604 /* 605 * Skip a row of characters of the same class. 606 * Return TRUE when end-of-file reached, FALSE otherwise. 607 */ 608 static int 609 skip_chars(int cclass, int dir) 610 { 611 while (cls() == cclass) 612 if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) 613 return TRUE; 614 return FALSE; 615 } 616 617 #if defined(FEAT_TEXTOBJ) || defined(PROTO) 618 /* 619 * Go back to the start of the word or the start of white space 620 */ 621 static void 622 back_in_line(void) 623 { 624 int sclass; // starting class 625 626 sclass = cls(); 627 for (;;) 628 { 629 if (curwin->w_cursor.col == 0) // stop at start of line 630 break; 631 dec_cursor(); 632 if (cls() != sclass) // stop at start of word 633 { 634 inc_cursor(); 635 break; 636 } 637 } 638 } 639 640 static void 641 find_first_blank(pos_T *posp) 642 { 643 int c; 644 645 while (decl(posp) != -1) 646 { 647 c = gchar_pos(posp); 648 if (!VIM_ISWHITE(c)) 649 { 650 incl(posp); 651 break; 652 } 653 } 654 } 655 656 /* 657 * Skip count/2 sentences and count/2 separating white spaces. 658 */ 659 static void 660 findsent_forward( 661 long count, 662 int at_start_sent) // cursor is at start of sentence 663 { 664 while (count--) 665 { 666 findsent(FORWARD, 1L); 667 if (at_start_sent) 668 find_first_blank(&curwin->w_cursor); 669 if (count == 0 || at_start_sent) 670 decl(&curwin->w_cursor); 671 at_start_sent = !at_start_sent; 672 } 673 } 674 675 /* 676 * Find word under cursor, cursor at end. 677 * Used while an operator is pending, and in Visual mode. 678 */ 679 int 680 current_word( 681 oparg_T *oap, 682 long count, 683 int include, // TRUE: include word and white space 684 int bigword) // FALSE == word, TRUE == WORD 685 { 686 pos_T start_pos; 687 pos_T pos; 688 int inclusive = TRUE; 689 int include_white = FALSE; 690 691 cls_bigword = bigword; 692 CLEAR_POS(&start_pos); 693 694 // Correct cursor when 'selection' is exclusive 695 if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor)) 696 dec_cursor(); 697 698 /* 699 * When Visual mode is not active, or when the VIsual area is only one 700 * character, select the word and/or white space under the cursor. 701 */ 702 if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual)) 703 { 704 /* 705 * Go to start of current word or white space. 706 */ 707 back_in_line(); 708 start_pos = curwin->w_cursor; 709 710 /* 711 * If the start is on white space, and white space should be included 712 * (" word"), or start is not on white space, and white space should 713 * not be included ("word"), find end of word. 714 */ 715 if ((cls() == 0) == include) 716 { 717 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 718 return FAIL; 719 } 720 else 721 { 722 /* 723 * If the start is not on white space, and white space should be 724 * included ("word "), or start is on white space and white 725 * space should not be included (" "), find start of word. 726 * If we end up in the first column of the next line (single char 727 * word) back up to end of the line. 728 */ 729 fwd_word(1L, bigword, TRUE); 730 if (curwin->w_cursor.col == 0) 731 decl(&curwin->w_cursor); 732 else 733 oneleft(); 734 735 if (include) 736 include_white = TRUE; 737 } 738 739 if (VIsual_active) 740 { 741 // should do something when inclusive == FALSE ! 742 VIsual = start_pos; 743 redraw_curbuf_later(INVERTED); // update the inversion 744 } 745 else 746 { 747 oap->start = start_pos; 748 oap->motion_type = MCHAR; 749 } 750 --count; 751 } 752 753 /* 754 * When count is still > 0, extend with more objects. 755 */ 756 while (count > 0) 757 { 758 inclusive = TRUE; 759 if (VIsual_active && LT_POS(curwin->w_cursor, VIsual)) 760 { 761 /* 762 * In Visual mode, with cursor at start: move cursor back. 763 */ 764 if (decl(&curwin->w_cursor) == -1) 765 return FAIL; 766 if (include != (cls() != 0)) 767 { 768 if (bck_word(1L, bigword, TRUE) == FAIL) 769 return FAIL; 770 } 771 else 772 { 773 if (bckend_word(1L, bigword, TRUE) == FAIL) 774 return FAIL; 775 (void)incl(&curwin->w_cursor); 776 } 777 } 778 else 779 { 780 /* 781 * Move cursor forward one word and/or white area. 782 */ 783 if (incl(&curwin->w_cursor) == -1) 784 return FAIL; 785 if (include != (cls() == 0)) 786 { 787 if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) 788 return FAIL; 789 /* 790 * If end is just past a new-line, we don't want to include 791 * the first character on the line. 792 * Put cursor on last char of white. 793 */ 794 if (oneleft() == FAIL) 795 inclusive = FALSE; 796 } 797 else 798 { 799 if (end_word(1L, bigword, TRUE, TRUE) == FAIL) 800 return FAIL; 801 } 802 } 803 --count; 804 } 805 806 if (include_white && (cls() != 0 807 || (curwin->w_cursor.col == 0 && !inclusive))) 808 { 809 /* 810 * If we don't include white space at the end, move the start 811 * to include some white space there. This makes "daw" work 812 * better on the last word in a sentence (and "2daw" on last-but-one 813 * word). Also when "2daw" deletes "word." at the end of the line 814 * (cursor is at start of next line). 815 * But don't delete white space at start of line (indent). 816 */ 817 pos = curwin->w_cursor; // save cursor position 818 curwin->w_cursor = start_pos; 819 if (oneleft() == OK) 820 { 821 back_in_line(); 822 if (cls() == 0 && curwin->w_cursor.col > 0) 823 { 824 if (VIsual_active) 825 VIsual = curwin->w_cursor; 826 else 827 oap->start = curwin->w_cursor; 828 } 829 } 830 curwin->w_cursor = pos; // put cursor back at end 831 } 832 833 if (VIsual_active) 834 { 835 if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor)) 836 inc_cursor(); 837 if (VIsual_mode == 'V') 838 { 839 VIsual_mode = 'v'; 840 redraw_cmdline = TRUE; // show mode later 841 } 842 } 843 else 844 oap->inclusive = inclusive; 845 846 return OK; 847 } 848 849 /* 850 * Find sentence(s) under the cursor, cursor at end. 851 * When Visual active, extend it by one or more sentences. 852 */ 853 int 854 current_sent(oparg_T *oap, long count, int include) 855 { 856 pos_T start_pos; 857 pos_T pos; 858 int start_blank; 859 int c; 860 int at_start_sent; 861 long ncount; 862 863 start_pos = curwin->w_cursor; 864 pos = start_pos; 865 findsent(FORWARD, 1L); // Find start of next sentence. 866 867 /* 868 * When the Visual area is bigger than one character: Extend it. 869 */ 870 if (VIsual_active && !EQUAL_POS(start_pos, VIsual)) 871 { 872 extend: 873 if (LT_POS(start_pos, VIsual)) 874 { 875 /* 876 * Cursor at start of Visual area. 877 * Find out where we are: 878 * - in the white space before a sentence 879 * - in a sentence or just after it 880 * - at the start of a sentence 881 */ 882 at_start_sent = TRUE; 883 decl(&pos); 884 while (LT_POS(pos, curwin->w_cursor)) 885 { 886 c = gchar_pos(&pos); 887 if (!VIM_ISWHITE(c)) 888 { 889 at_start_sent = FALSE; 890 break; 891 } 892 incl(&pos); 893 } 894 if (!at_start_sent) 895 { 896 findsent(BACKWARD, 1L); 897 if (EQUAL_POS(curwin->w_cursor, start_pos)) 898 at_start_sent = TRUE; // exactly at start of sentence 899 else 900 // inside a sentence, go to its end (start of next) 901 findsent(FORWARD, 1L); 902 } 903 if (include) // "as" gets twice as much as "is" 904 count *= 2; 905 while (count--) 906 { 907 if (at_start_sent) 908 find_first_blank(&curwin->w_cursor); 909 c = gchar_cursor(); 910 if (!at_start_sent || (!include && !VIM_ISWHITE(c))) 911 findsent(BACKWARD, 1L); 912 at_start_sent = !at_start_sent; 913 } 914 } 915 else 916 { 917 /* 918 * Cursor at end of Visual area. 919 * Find out where we are: 920 * - just before a sentence 921 * - just before or in the white space before a sentence 922 * - in a sentence 923 */ 924 incl(&pos); 925 at_start_sent = TRUE; 926 // not just before a sentence 927 if (!EQUAL_POS(pos, curwin->w_cursor)) 928 { 929 at_start_sent = FALSE; 930 while (LT_POS(pos, curwin->w_cursor)) 931 { 932 c = gchar_pos(&pos); 933 if (!VIM_ISWHITE(c)) 934 { 935 at_start_sent = TRUE; 936 break; 937 } 938 incl(&pos); 939 } 940 if (at_start_sent) // in the sentence 941 findsent(BACKWARD, 1L); 942 else // in/before white before a sentence 943 curwin->w_cursor = start_pos; 944 } 945 946 if (include) // "as" gets twice as much as "is" 947 count *= 2; 948 findsent_forward(count, at_start_sent); 949 if (*p_sel == 'e') 950 ++curwin->w_cursor.col; 951 } 952 return OK; 953 } 954 955 /* 956 * If the cursor started on a blank, check if it is just before the start 957 * of the next sentence. 958 */ 959 while (c = gchar_pos(&pos), VIM_ISWHITE(c)) // VIM_ISWHITE() is a macro 960 incl(&pos); 961 if (EQUAL_POS(pos, curwin->w_cursor)) 962 { 963 start_blank = TRUE; 964 find_first_blank(&start_pos); // go back to first blank 965 } 966 else 967 { 968 start_blank = FALSE; 969 findsent(BACKWARD, 1L); 970 start_pos = curwin->w_cursor; 971 } 972 if (include) 973 ncount = count * 2; 974 else 975 { 976 ncount = count; 977 if (start_blank) 978 --ncount; 979 } 980 if (ncount > 0) 981 findsent_forward(ncount, TRUE); 982 else 983 decl(&curwin->w_cursor); 984 985 if (include) 986 { 987 /* 988 * If the blank in front of the sentence is included, exclude the 989 * blanks at the end of the sentence, go back to the first blank. 990 * If there are no trailing blanks, try to include leading blanks. 991 */ 992 if (start_blank) 993 { 994 find_first_blank(&curwin->w_cursor); 995 c = gchar_pos(&curwin->w_cursor); // VIM_ISWHITE() is a macro 996 if (VIM_ISWHITE(c)) 997 decl(&curwin->w_cursor); 998 } 999 else if (c = gchar_cursor(), !VIM_ISWHITE(c)) 1000 find_first_blank(&start_pos); 1001 } 1002 1003 if (VIsual_active) 1004 { 1005 // Avoid getting stuck with "is" on a single space before a sentence. 1006 if (EQUAL_POS(start_pos, curwin->w_cursor)) 1007 goto extend; 1008 if (*p_sel == 'e') 1009 ++curwin->w_cursor.col; 1010 VIsual = start_pos; 1011 VIsual_mode = 'v'; 1012 redraw_cmdline = TRUE; // show mode later 1013 redraw_curbuf_later(INVERTED); // update the inversion 1014 } 1015 else 1016 { 1017 // include a newline after the sentence, if there is one 1018 if (incl(&curwin->w_cursor) == -1) 1019 oap->inclusive = TRUE; 1020 else 1021 oap->inclusive = FALSE; 1022 oap->start = start_pos; 1023 oap->motion_type = MCHAR; 1024 } 1025 return OK; 1026 } 1027 1028 /* 1029 * Find block under the cursor, cursor at end. 1030 * "what" and "other" are two matching parenthesis/brace/etc. 1031 */ 1032 int 1033 current_block( 1034 oparg_T *oap, 1035 long count, 1036 int include, // TRUE == include white space 1037 int what, // '(', '{', etc. 1038 int other) // ')', '}', etc. 1039 { 1040 pos_T old_pos; 1041 pos_T *pos = NULL; 1042 pos_T start_pos; 1043 pos_T *end_pos; 1044 pos_T old_start, old_end; 1045 char_u *save_cpo; 1046 int sol = FALSE; // '{' at start of line 1047 1048 old_pos = curwin->w_cursor; 1049 old_end = curwin->w_cursor; // remember where we started 1050 old_start = old_end; 1051 1052 /* 1053 * If we start on '(', '{', ')', '}', etc., use the whole block inclusive. 1054 */ 1055 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor)) 1056 { 1057 setpcmark(); 1058 if (what == '{') // ignore indent 1059 while (inindent(1)) 1060 if (inc_cursor() != 0) 1061 break; 1062 if (gchar_cursor() == what) 1063 // cursor on '(' or '{', move cursor just after it 1064 ++curwin->w_cursor.col; 1065 } 1066 else if (LT_POS(VIsual, curwin->w_cursor)) 1067 { 1068 old_start = VIsual; 1069 curwin->w_cursor = VIsual; // cursor at low end of Visual 1070 } 1071 else 1072 old_end = VIsual; 1073 1074 /* 1075 * Search backwards for unclosed '(', '{', etc.. 1076 * Put this position in start_pos. 1077 * Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the 1078 * user wants. 1079 */ 1080 save_cpo = p_cpo; 1081 p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%"); 1082 if ((pos = findmatch(NULL, what)) != NULL) 1083 { 1084 while (count-- > 0) 1085 { 1086 if ((pos = findmatch(NULL, what)) == NULL) 1087 break; 1088 curwin->w_cursor = *pos; 1089 start_pos = *pos; // the findmatch for end_pos will overwrite *pos 1090 } 1091 } 1092 else 1093 { 1094 while (count-- > 0) 1095 { 1096 if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) 1097 break; 1098 curwin->w_cursor = *pos; 1099 start_pos = *pos; // the findmatch for end_pos will overwrite *pos 1100 } 1101 } 1102 p_cpo = save_cpo; 1103 1104 /* 1105 * Search for matching ')', '}', etc. 1106 * Put this position in curwin->w_cursor. 1107 */ 1108 if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) 1109 { 1110 curwin->w_cursor = old_pos; 1111 return FAIL; 1112 } 1113 curwin->w_cursor = *end_pos; 1114 1115 /* 1116 * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE. 1117 * If the ending '}', ')' or ']' is only preceded by indent, skip that 1118 * indent. But only if the resulting area is not smaller than what we 1119 * started with. 1120 */ 1121 while (!include) 1122 { 1123 incl(&start_pos); 1124 sol = (curwin->w_cursor.col == 0); 1125 decl(&curwin->w_cursor); 1126 while (inindent(1)) 1127 { 1128 sol = TRUE; 1129 if (decl(&curwin->w_cursor) != 0) 1130 break; 1131 } 1132 1133 /* 1134 * In Visual mode, when the resulting area is not bigger than what we 1135 * started with, extend it to the next block, and then exclude again. 1136 */ 1137 if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor) 1138 && VIsual_active) 1139 { 1140 curwin->w_cursor = old_start; 1141 decl(&curwin->w_cursor); 1142 if ((pos = findmatch(NULL, what)) == NULL) 1143 { 1144 curwin->w_cursor = old_pos; 1145 return FAIL; 1146 } 1147 start_pos = *pos; 1148 curwin->w_cursor = *pos; 1149 if ((end_pos = findmatch(NULL, other)) == NULL) 1150 { 1151 curwin->w_cursor = old_pos; 1152 return FAIL; 1153 } 1154 curwin->w_cursor = *end_pos; 1155 } 1156 else 1157 break; 1158 } 1159 1160 if (VIsual_active) 1161 { 1162 if (*p_sel == 'e') 1163 inc(&curwin->w_cursor); 1164 if (sol && gchar_cursor() != NUL) 1165 inc(&curwin->w_cursor); // include the line break 1166 VIsual = start_pos; 1167 VIsual_mode = 'v'; 1168 redraw_curbuf_later(INVERTED); // update the inversion 1169 showmode(); 1170 } 1171 else 1172 { 1173 oap->start = start_pos; 1174 oap->motion_type = MCHAR; 1175 oap->inclusive = FALSE; 1176 if (sol) 1177 incl(&curwin->w_cursor); 1178 else if (LTOREQ_POS(start_pos, curwin->w_cursor)) 1179 // Include the character under the cursor. 1180 oap->inclusive = TRUE; 1181 else 1182 // End is before the start (no text in between <>, [], etc.): don't 1183 // operate on any text. 1184 curwin->w_cursor = start_pos; 1185 } 1186 1187 return OK; 1188 } 1189 1190 /* 1191 * Return TRUE if the cursor is on a "<aaa>" tag. Ignore "<aaa/>". 1192 * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>". 1193 */ 1194 static int 1195 in_html_tag( 1196 int end_tag) 1197 { 1198 char_u *line = ml_get_curline(); 1199 char_u *p; 1200 int c; 1201 int lc = NUL; 1202 pos_T pos; 1203 1204 if (enc_dbcs) 1205 { 1206 char_u *lp = NULL; 1207 1208 // We search forward until the cursor, because searching backwards is 1209 // very slow for DBCS encodings. 1210 for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p)) 1211 if (*p == '>' || *p == '<') 1212 { 1213 lc = *p; 1214 lp = p; 1215 } 1216 if (*p != '<') // check for '<' under cursor 1217 { 1218 if (lc != '<') 1219 return FALSE; 1220 p = lp; 1221 } 1222 } 1223 else 1224 { 1225 for (p = line + curwin->w_cursor.col; p > line; ) 1226 { 1227 if (*p == '<') // find '<' under/before cursor 1228 break; 1229 MB_PTR_BACK(line, p); 1230 if (*p == '>') // find '>' before cursor 1231 break; 1232 } 1233 if (*p != '<') 1234 return FALSE; 1235 } 1236 1237 pos.lnum = curwin->w_cursor.lnum; 1238 pos.col = (colnr_T)(p - line); 1239 1240 MB_PTR_ADV(p); 1241 if (end_tag) 1242 // check that there is a '/' after the '<' 1243 return *p == '/'; 1244 1245 // check that there is no '/' after the '<' 1246 if (*p == '/') 1247 return FALSE; 1248 1249 // check that the matching '>' is not preceded by '/' 1250 for (;;) 1251 { 1252 if (inc(&pos) < 0) 1253 return FALSE; 1254 c = *ml_get_pos(&pos); 1255 if (c == '>') 1256 break; 1257 lc = c; 1258 } 1259 return lc != '/'; 1260 } 1261 1262 /* 1263 * Find tag block under the cursor, cursor at end. 1264 */ 1265 int 1266 current_tagblock( 1267 oparg_T *oap, 1268 long count_arg, 1269 int include) // TRUE == include white space 1270 { 1271 long count = count_arg; 1272 long n; 1273 pos_T old_pos; 1274 pos_T start_pos; 1275 pos_T end_pos; 1276 pos_T old_start, old_end; 1277 char_u *spat, *epat; 1278 char_u *p; 1279 char_u *cp; 1280 int len; 1281 int r; 1282 int do_include = include; 1283 int save_p_ws = p_ws; 1284 int retval = FAIL; 1285 int is_inclusive = TRUE; 1286 1287 p_ws = FALSE; 1288 1289 old_pos = curwin->w_cursor; 1290 old_end = curwin->w_cursor; // remember where we started 1291 old_start = old_end; 1292 if (!VIsual_active || *p_sel == 'e') 1293 decl(&old_end); // old_end is inclusive 1294 1295 /* 1296 * If we start on "<aaa>" select that block. 1297 */ 1298 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor)) 1299 { 1300 setpcmark(); 1301 1302 // ignore indent 1303 while (inindent(1)) 1304 if (inc_cursor() != 0) 1305 break; 1306 1307 if (in_html_tag(FALSE)) 1308 { 1309 // cursor on start tag, move to its '>' 1310 while (*ml_get_cursor() != '>') 1311 if (inc_cursor() < 0) 1312 break; 1313 } 1314 else if (in_html_tag(TRUE)) 1315 { 1316 // cursor on end tag, move to just before it 1317 while (*ml_get_cursor() != '<') 1318 if (dec_cursor() < 0) 1319 break; 1320 dec_cursor(); 1321 old_end = curwin->w_cursor; 1322 } 1323 } 1324 else if (LT_POS(VIsual, curwin->w_cursor)) 1325 { 1326 old_start = VIsual; 1327 curwin->w_cursor = VIsual; // cursor at low end of Visual 1328 } 1329 else 1330 old_end = VIsual; 1331 1332 again: 1333 /* 1334 * Search backwards for unclosed "<aaa>". 1335 * Put this position in start_pos. 1336 */ 1337 for (n = 0; n < count; ++n) 1338 { 1339 if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", 1340 (char_u *)"", 1341 (char_u *)"</[^>]*>", BACKWARD, NULL, 0, 1342 NULL, (linenr_T)0, 0L) <= 0) 1343 { 1344 curwin->w_cursor = old_pos; 1345 goto theend; 1346 } 1347 } 1348 start_pos = curwin->w_cursor; 1349 1350 /* 1351 * Search for matching "</aaa>". First isolate the "aaa". 1352 */ 1353 inc_cursor(); 1354 p = ml_get_cursor(); 1355 for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp)) 1356 ; 1357 len = (int)(cp - p); 1358 if (len == 0) 1359 { 1360 curwin->w_cursor = old_pos; 1361 goto theend; 1362 } 1363 spat = alloc(len + 39); 1364 epat = alloc(len + 9); 1365 if (spat == NULL || epat == NULL) 1366 { 1367 vim_free(spat); 1368 vim_free(epat); 1369 curwin->w_cursor = old_pos; 1370 goto theend; 1371 } 1372 sprintf((char *)spat, "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p); 1373 sprintf((char *)epat, "</%.*s>\\c", len, p); 1374 1375 r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL, 1376 0, NULL, (linenr_T)0, 0L); 1377 1378 vim_free(spat); 1379 vim_free(epat); 1380 1381 if (r < 1 || LT_POS(curwin->w_cursor, old_end)) 1382 { 1383 // Can't find other end or it's before the previous end. Could be a 1384 // HTML tag that doesn't have a matching end. Search backwards for 1385 // another starting tag. 1386 count = 1; 1387 curwin->w_cursor = start_pos; 1388 goto again; 1389 } 1390 1391 if (do_include) 1392 { 1393 // Include up to the '>'. 1394 while (*ml_get_cursor() != '>') 1395 if (inc_cursor() < 0) 1396 break; 1397 } 1398 else 1399 { 1400 char_u *c = ml_get_cursor(); 1401 1402 // Exclude the '<' of the end tag. 1403 // If the closing tag is on new line, do not decrement cursor, but 1404 // make operation exclusive, so that the linefeed will be selected 1405 if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) 1406 // do not decrement cursor 1407 is_inclusive = FALSE; 1408 else if (*c == '<') 1409 dec_cursor(); 1410 } 1411 end_pos = curwin->w_cursor; 1412 1413 if (!do_include) 1414 { 1415 // Exclude the start tag. 1416 curwin->w_cursor = start_pos; 1417 while (inc_cursor() >= 0) 1418 if (*ml_get_cursor() == '>') 1419 { 1420 inc_cursor(); 1421 start_pos = curwin->w_cursor; 1422 break; 1423 } 1424 curwin->w_cursor = end_pos; 1425 1426 // If we are in Visual mode and now have the same text as before set 1427 // "do_include" and try again. 1428 if (VIsual_active && EQUAL_POS(start_pos, old_start) 1429 && EQUAL_POS(end_pos, old_end)) 1430 { 1431 do_include = TRUE; 1432 curwin->w_cursor = old_start; 1433 count = count_arg; 1434 goto again; 1435 } 1436 } 1437 1438 if (VIsual_active) 1439 { 1440 // If the end is before the start there is no text between tags, select 1441 // the char under the cursor. 1442 if (LT_POS(end_pos, start_pos)) 1443 curwin->w_cursor = start_pos; 1444 else if (*p_sel == 'e') 1445 inc_cursor(); 1446 VIsual = start_pos; 1447 VIsual_mode = 'v'; 1448 redraw_curbuf_later(INVERTED); // update the inversion 1449 showmode(); 1450 } 1451 else 1452 { 1453 oap->start = start_pos; 1454 oap->motion_type = MCHAR; 1455 if (LT_POS(end_pos, start_pos)) 1456 { 1457 // End is before the start: there is no text between tags; operate 1458 // on an empty area. 1459 curwin->w_cursor = start_pos; 1460 oap->inclusive = FALSE; 1461 } 1462 else 1463 oap->inclusive = is_inclusive; 1464 } 1465 retval = OK; 1466 1467 theend: 1468 p_ws = save_p_ws; 1469 return retval; 1470 } 1471 1472 int 1473 current_par( 1474 oparg_T *oap, 1475 long count, 1476 int include, // TRUE == include white space 1477 int type) // 'p' for paragraph, 'S' for section 1478 { 1479 linenr_T start_lnum; 1480 linenr_T end_lnum; 1481 int white_in_front; 1482 int dir; 1483 int start_is_white; 1484 int prev_start_is_white; 1485 int retval = OK; 1486 int do_white = FALSE; 1487 int t; 1488 int i; 1489 1490 if (type == 'S') // not implemented yet 1491 return FAIL; 1492 1493 start_lnum = curwin->w_cursor.lnum; 1494 1495 /* 1496 * When visual area is more than one line: extend it. 1497 */ 1498 if (VIsual_active && start_lnum != VIsual.lnum) 1499 { 1500 extend: 1501 if (start_lnum < VIsual.lnum) 1502 dir = BACKWARD; 1503 else 1504 dir = FORWARD; 1505 for (i = count; --i >= 0; ) 1506 { 1507 if (start_lnum == 1508 (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) 1509 { 1510 retval = FAIL; 1511 break; 1512 } 1513 1514 prev_start_is_white = -1; 1515 for (t = 0; t < 2; ++t) 1516 { 1517 start_lnum += dir; 1518 start_is_white = linewhite(start_lnum); 1519 if (prev_start_is_white == start_is_white) 1520 { 1521 start_lnum -= dir; 1522 break; 1523 } 1524 for (;;) 1525 { 1526 if (start_lnum == (dir == BACKWARD 1527 ? 1 : curbuf->b_ml.ml_line_count)) 1528 break; 1529 if (start_is_white != linewhite(start_lnum + dir) 1530 || (!start_is_white 1531 && startPS(start_lnum + (dir > 0 1532 ? 1 : 0), 0, 0))) 1533 break; 1534 start_lnum += dir; 1535 } 1536 if (!include) 1537 break; 1538 if (start_lnum == (dir == BACKWARD 1539 ? 1 : curbuf->b_ml.ml_line_count)) 1540 break; 1541 prev_start_is_white = start_is_white; 1542 } 1543 } 1544 curwin->w_cursor.lnum = start_lnum; 1545 curwin->w_cursor.col = 0; 1546 return retval; 1547 } 1548 1549 /* 1550 * First move back to the start_lnum of the paragraph or white lines 1551 */ 1552 white_in_front = linewhite(start_lnum); 1553 while (start_lnum > 1) 1554 { 1555 if (white_in_front) // stop at first white line 1556 { 1557 if (!linewhite(start_lnum - 1)) 1558 break; 1559 } 1560 else // stop at first non-white line of start of paragraph 1561 { 1562 if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) 1563 break; 1564 } 1565 --start_lnum; 1566 } 1567 1568 /* 1569 * Move past the end of any white lines. 1570 */ 1571 end_lnum = start_lnum; 1572 while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) 1573 ++end_lnum; 1574 1575 --end_lnum; 1576 i = count; 1577 if (!include && white_in_front) 1578 --i; 1579 while (i--) 1580 { 1581 if (end_lnum == curbuf->b_ml.ml_line_count) 1582 return FAIL; 1583 1584 if (!include) 1585 do_white = linewhite(end_lnum + 1); 1586 1587 if (include || !do_white) 1588 { 1589 ++end_lnum; 1590 /* 1591 * skip to end of paragraph 1592 */ 1593 while (end_lnum < curbuf->b_ml.ml_line_count 1594 && !linewhite(end_lnum + 1) 1595 && !startPS(end_lnum + 1, 0, 0)) 1596 ++end_lnum; 1597 } 1598 1599 if (i == 0 && white_in_front && include) 1600 break; 1601 1602 /* 1603 * skip to end of white lines after paragraph 1604 */ 1605 if (include || do_white) 1606 while (end_lnum < curbuf->b_ml.ml_line_count 1607 && linewhite(end_lnum + 1)) 1608 ++end_lnum; 1609 } 1610 1611 /* 1612 * If there are no empty lines at the end, try to find some empty lines at 1613 * the start (unless that has been done already). 1614 */ 1615 if (!white_in_front && !linewhite(end_lnum) && include) 1616 while (start_lnum > 1 && linewhite(start_lnum - 1)) 1617 --start_lnum; 1618 1619 if (VIsual_active) 1620 { 1621 // Problem: when doing "Vipipip" nothing happens in a single white 1622 // line, we get stuck there. Trap this here. 1623 if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) 1624 goto extend; 1625 if (VIsual.lnum != start_lnum) 1626 { 1627 VIsual.lnum = start_lnum; 1628 VIsual.col = 0; 1629 } 1630 VIsual_mode = 'V'; 1631 redraw_curbuf_later(INVERTED); // update the inversion 1632 showmode(); 1633 } 1634 else 1635 { 1636 oap->start.lnum = start_lnum; 1637 oap->start.col = 0; 1638 oap->motion_type = MLINE; 1639 } 1640 curwin->w_cursor.lnum = end_lnum; 1641 curwin->w_cursor.col = 0; 1642 1643 return OK; 1644 } 1645 1646 /* 1647 * Search quote char from string line[col]. 1648 * Quote character escaped by one of the characters in "escape" is not counted 1649 * as a quote. 1650 * Returns column number of "quotechar" or -1 when not found. 1651 */ 1652 static int 1653 find_next_quote( 1654 char_u *line, 1655 int col, 1656 int quotechar, 1657 char_u *escape) // escape characters, can be NULL 1658 { 1659 int c; 1660 1661 for (;;) 1662 { 1663 c = line[col]; 1664 if (c == NUL) 1665 return -1; 1666 else if (escape != NULL && vim_strchr(escape, c)) 1667 ++col; 1668 else if (c == quotechar) 1669 break; 1670 if (has_mbyte) 1671 col += (*mb_ptr2len)(line + col); 1672 else 1673 ++col; 1674 } 1675 return col; 1676 } 1677 1678 /* 1679 * Search backwards in "line" from column "col_start" to find "quotechar". 1680 * Quote character escaped by one of the characters in "escape" is not counted 1681 * as a quote. 1682 * Return the found column or zero. 1683 */ 1684 static int 1685 find_prev_quote( 1686 char_u *line, 1687 int col_start, 1688 int quotechar, 1689 char_u *escape) // escape characters, can be NULL 1690 { 1691 int n; 1692 1693 while (col_start > 0) 1694 { 1695 --col_start; 1696 col_start -= (*mb_head_off)(line, line + col_start); 1697 n = 0; 1698 if (escape != NULL) 1699 while (col_start - n > 0 && vim_strchr(escape, 1700 line[col_start - n - 1]) != NULL) 1701 ++n; 1702 if (n & 1) 1703 col_start -= n; // uneven number of escape chars, skip it 1704 else if (line[col_start] == quotechar) 1705 break; 1706 } 1707 return col_start; 1708 } 1709 1710 /* 1711 * Find quote under the cursor, cursor at end. 1712 * Returns TRUE if found, else FALSE. 1713 */ 1714 int 1715 current_quote( 1716 oparg_T *oap, 1717 long count, 1718 int include, // TRUE == include quote char 1719 int quotechar) // Quote character 1720 { 1721 char_u *line = ml_get_curline(); 1722 int col_end; 1723 int col_start = curwin->w_cursor.col; 1724 int inclusive = FALSE; 1725 int vis_empty = TRUE; // Visual selection <= 1 char 1726 int vis_bef_curs = FALSE; // Visual starts before cursor 1727 int did_exclusive_adj = FALSE; // adjusted pos for 'selection' 1728 int inside_quotes = FALSE; // Looks like "i'" done before 1729 int selected_quote = FALSE; // Has quote inside selection 1730 int i; 1731 int restore_vis_bef = FALSE; // restore VIsual on abort 1732 1733 // When 'selection' is "exclusive" move the cursor to where it would be 1734 // with 'selection' "inclusive", so that the logic is the same for both. 1735 // The cursor then is moved forward after adjusting the area. 1736 if (VIsual_active) 1737 { 1738 // this only works within one line 1739 if (VIsual.lnum != curwin->w_cursor.lnum) 1740 return FALSE; 1741 1742 vis_bef_curs = LT_POS(VIsual, curwin->w_cursor); 1743 vis_empty = EQUAL_POS(VIsual, curwin->w_cursor); 1744 if (*p_sel == 'e') 1745 { 1746 if (vis_bef_curs) 1747 { 1748 dec_cursor(); 1749 did_exclusive_adj = TRUE; 1750 } 1751 else if (!vis_empty) 1752 { 1753 dec(&VIsual); 1754 did_exclusive_adj = TRUE; 1755 } 1756 vis_empty = EQUAL_POS(VIsual, curwin->w_cursor); 1757 if (!vis_bef_curs && !vis_empty) 1758 { 1759 // VIsual needs to be the start of Visual selection. 1760 pos_T t = curwin->w_cursor; 1761 1762 curwin->w_cursor = VIsual; 1763 VIsual = t; 1764 vis_bef_curs = TRUE; 1765 restore_vis_bef = TRUE; 1766 } 1767 } 1768 } 1769 1770 if (!vis_empty) 1771 { 1772 // Check if the existing selection exactly spans the text inside 1773 // quotes. 1774 if (vis_bef_curs) 1775 { 1776 inside_quotes = VIsual.col > 0 1777 && line[VIsual.col - 1] == quotechar 1778 && line[curwin->w_cursor.col] != NUL 1779 && line[curwin->w_cursor.col + 1] == quotechar; 1780 i = VIsual.col; 1781 col_end = curwin->w_cursor.col; 1782 } 1783 else 1784 { 1785 inside_quotes = curwin->w_cursor.col > 0 1786 && line[curwin->w_cursor.col - 1] == quotechar 1787 && line[VIsual.col] != NUL 1788 && line[VIsual.col + 1] == quotechar; 1789 i = curwin->w_cursor.col; 1790 col_end = VIsual.col; 1791 } 1792 1793 // Find out if we have a quote in the selection. 1794 while (i <= col_end) 1795 if (line[i++] == quotechar) 1796 { 1797 selected_quote = TRUE; 1798 break; 1799 } 1800 } 1801 1802 if (!vis_empty && line[col_start] == quotechar) 1803 { 1804 // Already selecting something and on a quote character. Find the 1805 // next quoted string. 1806 if (vis_bef_curs) 1807 { 1808 // Assume we are on a closing quote: move to after the next 1809 // opening quote. 1810 col_start = find_next_quote(line, col_start + 1, quotechar, NULL); 1811 if (col_start < 0) 1812 goto abort_search; 1813 col_end = find_next_quote(line, col_start + 1, quotechar, 1814 curbuf->b_p_qe); 1815 if (col_end < 0) 1816 { 1817 // We were on a starting quote perhaps? 1818 col_end = col_start; 1819 col_start = curwin->w_cursor.col; 1820 } 1821 } 1822 else 1823 { 1824 col_end = find_prev_quote(line, col_start, quotechar, NULL); 1825 if (line[col_end] != quotechar) 1826 goto abort_search; 1827 col_start = find_prev_quote(line, col_end, quotechar, 1828 curbuf->b_p_qe); 1829 if (line[col_start] != quotechar) 1830 { 1831 // We were on an ending quote perhaps? 1832 col_start = col_end; 1833 col_end = curwin->w_cursor.col; 1834 } 1835 } 1836 } 1837 else 1838 1839 if (line[col_start] == quotechar || !vis_empty) 1840 { 1841 int first_col = col_start; 1842 1843 if (!vis_empty) 1844 { 1845 if (vis_bef_curs) 1846 first_col = find_next_quote(line, col_start, quotechar, NULL); 1847 else 1848 first_col = find_prev_quote(line, col_start, quotechar, NULL); 1849 } 1850 1851 // The cursor is on a quote, we don't know if it's the opening or 1852 // closing quote. Search from the start of the line to find out. 1853 // Also do this when there is a Visual area, a' may leave the cursor 1854 // in between two strings. 1855 col_start = 0; 1856 for (;;) 1857 { 1858 // Find open quote character. 1859 col_start = find_next_quote(line, col_start, quotechar, NULL); 1860 if (col_start < 0 || col_start > first_col) 1861 goto abort_search; 1862 // Find close quote character. 1863 col_end = find_next_quote(line, col_start + 1, quotechar, 1864 curbuf->b_p_qe); 1865 if (col_end < 0) 1866 goto abort_search; 1867 // If is cursor between start and end quote character, it is 1868 // target text object. 1869 if (col_start <= first_col && first_col <= col_end) 1870 break; 1871 col_start = col_end + 1; 1872 } 1873 } 1874 else 1875 { 1876 // Search backward for a starting quote. 1877 col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe); 1878 if (line[col_start] != quotechar) 1879 { 1880 // No quote before the cursor, look after the cursor. 1881 col_start = find_next_quote(line, col_start, quotechar, NULL); 1882 if (col_start < 0) 1883 goto abort_search; 1884 } 1885 1886 // Find close quote character. 1887 col_end = find_next_quote(line, col_start + 1, quotechar, 1888 curbuf->b_p_qe); 1889 if (col_end < 0) 1890 goto abort_search; 1891 } 1892 1893 // When "include" is TRUE, include spaces after closing quote or before 1894 // the starting quote. 1895 if (include) 1896 { 1897 if (VIM_ISWHITE(line[col_end + 1])) 1898 while (VIM_ISWHITE(line[col_end + 1])) 1899 ++col_end; 1900 else 1901 while (col_start > 0 && VIM_ISWHITE(line[col_start - 1])) 1902 --col_start; 1903 } 1904 1905 // Set start position. After vi" another i" must include the ". 1906 // For v2i" include the quotes. 1907 if (!include && count < 2 && (vis_empty || !inside_quotes)) 1908 ++col_start; 1909 curwin->w_cursor.col = col_start; 1910 if (VIsual_active) 1911 { 1912 // Set the start of the Visual area when the Visual area was empty, we 1913 // were just inside quotes or the Visual area didn't start at a quote 1914 // and didn't include a quote. 1915 if (vis_empty 1916 || (vis_bef_curs 1917 && !selected_quote 1918 && (inside_quotes 1919 || (line[VIsual.col] != quotechar 1920 && (VIsual.col == 0 1921 || line[VIsual.col - 1] != quotechar))))) 1922 { 1923 VIsual = curwin->w_cursor; 1924 redraw_curbuf_later(INVERTED); 1925 } 1926 } 1927 else 1928 { 1929 oap->start = curwin->w_cursor; 1930 oap->motion_type = MCHAR; 1931 } 1932 1933 // Set end position. 1934 curwin->w_cursor.col = col_end; 1935 if ((include || count > 1 // After vi" another i" must include the ". 1936 || (!vis_empty && inside_quotes) 1937 ) && inc_cursor() == 2) 1938 inclusive = TRUE; 1939 if (VIsual_active) 1940 { 1941 if (vis_empty || vis_bef_curs) 1942 { 1943 // decrement cursor when 'selection' is not exclusive 1944 if (*p_sel != 'e') 1945 dec_cursor(); 1946 } 1947 else 1948 { 1949 // Cursor is at start of Visual area. Set the end of the Visual 1950 // area when it was just inside quotes or it didn't end at a 1951 // quote. 1952 if (inside_quotes 1953 || (!selected_quote 1954 && line[VIsual.col] != quotechar 1955 && (line[VIsual.col] == NUL 1956 || line[VIsual.col + 1] != quotechar))) 1957 { 1958 dec_cursor(); 1959 VIsual = curwin->w_cursor; 1960 } 1961 curwin->w_cursor.col = col_start; 1962 } 1963 if (VIsual_mode == 'V') 1964 { 1965 VIsual_mode = 'v'; 1966 redraw_cmdline = TRUE; // show mode later 1967 } 1968 } 1969 else 1970 { 1971 // Set inclusive and other oap's flags. 1972 oap->inclusive = inclusive; 1973 } 1974 1975 return OK; 1976 1977 abort_search: 1978 if (VIsual_active && *p_sel == 'e') 1979 { 1980 if (did_exclusive_adj) 1981 inc_cursor(); 1982 if (restore_vis_bef) 1983 { 1984 pos_T t = curwin->w_cursor; 1985 1986 curwin->w_cursor = VIsual; 1987 VIsual = t; 1988 } 1989 } 1990 return FALSE; 1991 } 1992 1993 #endif // FEAT_TEXTOBJ 1994