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