comparison src/search.c @ 20209:6ca6a372fef6 v8.2.0660

patch 8.2.0660: the search.c file is a bit big Commit: https://github.com/vim/vim/commit/ed8ce057b7a2fcd89b5f55680ae8f85d62a992a5 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Apr 29 21:04:15 2020 +0200 patch 8.2.0660: the search.c file is a bit big Problem: The search.c file is a bit big. Solution: Split off the text object code to a separate file. (Yegappan Lakshmanan, closes #6007)
author Bram Moolenaar <Bram@vim.org>
date Wed, 29 Apr 2020 21:15:05 +0200
parents aadd1cae2ff5
children 00fff78a929a
comparison
equal deleted inserted replaced
20208:061dfda170cd 20209:6ca6a372fef6
15 #ifdef FEAT_EVAL 15 #ifdef FEAT_EVAL
16 static void set_vv_searchforward(void); 16 static void set_vv_searchforward(void);
17 static int first_submatch(regmmatch_T *rp); 17 static int first_submatch(regmmatch_T *rp);
18 #endif 18 #endif
19 static int check_linecomment(char_u *line); 19 static int check_linecomment(char_u *line);
20 static int cls(void);
21 static int skip_chars(int, int);
22 #ifdef FEAT_FIND_ID 20 #ifdef FEAT_FIND_ID
23 static void show_pat_in_path(char_u *, int, 21 static void show_pat_in_path(char_u *, int,
24 int, int, FILE *, linenr_T *, long); 22 int, int, FILE *, linenr_T *, long);
25 #endif 23 #endif
26 static void search_stat(int dirc, pos_T *pos, int show_top_bot_msg, char_u *msgbuf, int recompute); 24 static void search_stat(int dirc, pos_T *pos, int show_top_bot_msg, char_u *msgbuf, int recompute);
2836 } 2834 }
2837 } 2835 }
2838 } 2836 }
2839 2837
2840 /* 2838 /*
2841 * Find the start of the next sentence, searching in the direction specified
2842 * by the "dir" argument. The cursor is positioned on the start of the next
2843 * sentence when found. If the next sentence is found, return OK. Return FAIL
2844 * otherwise. See ":h sentence" for the precise definition of a "sentence"
2845 * text object.
2846 */
2847 int
2848 findsent(int dir, long count)
2849 {
2850 pos_T pos, tpos;
2851 int c;
2852 int (*func)(pos_T *);
2853 int startlnum;
2854 int noskip = FALSE; // do not skip blanks
2855 int cpo_J;
2856 int found_dot;
2857
2858 pos = curwin->w_cursor;
2859 if (dir == FORWARD)
2860 func = incl;
2861 else
2862 func = decl;
2863
2864 while (count--)
2865 {
2866 /*
2867 * if on an empty line, skip up to a non-empty line
2868 */
2869 if (gchar_pos(&pos) == NUL)
2870 {
2871 do
2872 if ((*func)(&pos) == -1)
2873 break;
2874 while (gchar_pos(&pos) == NUL);
2875 if (dir == FORWARD)
2876 goto found;
2877 }
2878 /*
2879 * if on the start of a paragraph or a section and searching forward,
2880 * go to the next line
2881 */
2882 else if (dir == FORWARD && pos.col == 0 &&
2883 startPS(pos.lnum, NUL, FALSE))
2884 {
2885 if (pos.lnum == curbuf->b_ml.ml_line_count)
2886 return FAIL;
2887 ++pos.lnum;
2888 goto found;
2889 }
2890 else if (dir == BACKWARD)
2891 decl(&pos);
2892
2893 // go back to the previous non-white non-punctuation character
2894 found_dot = FALSE;
2895 while (c = gchar_pos(&pos), VIM_ISWHITE(c)
2896 || vim_strchr((char_u *)".!?)]\"'", c) != NULL)
2897 {
2898 tpos = pos;
2899 if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD))
2900 break;
2901
2902 if (found_dot)
2903 break;
2904 if (vim_strchr((char_u *) ".!?", c) != NULL)
2905 found_dot = TRUE;
2906
2907 if (vim_strchr((char_u *) ")]\"'", c) != NULL
2908 && vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL)
2909 break;
2910
2911 decl(&pos);
2912 }
2913
2914 // remember the line where the search started
2915 startlnum = pos.lnum;
2916 cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
2917
2918 for (;;) // find end of sentence
2919 {
2920 c = gchar_pos(&pos);
2921 if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
2922 {
2923 if (dir == BACKWARD && pos.lnum != startlnum)
2924 ++pos.lnum;
2925 break;
2926 }
2927 if (c == '.' || c == '!' || c == '?')
2928 {
2929 tpos = pos;
2930 do
2931 if ((c = inc(&tpos)) == -1)
2932 break;
2933 while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
2934 != NULL);
2935 if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
2936 || (cpo_J && (c == ' ' && inc(&tpos) >= 0
2937 && gchar_pos(&tpos) == ' ')))
2938 {
2939 pos = tpos;
2940 if (gchar_pos(&pos) == NUL) // skip NUL at EOL
2941 inc(&pos);
2942 break;
2943 }
2944 }
2945 if ((*func)(&pos) == -1)
2946 {
2947 if (count)
2948 return FAIL;
2949 noskip = TRUE;
2950 break;
2951 }
2952 }
2953 found:
2954 // skip white space
2955 while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
2956 if (incl(&pos) == -1)
2957 break;
2958 }
2959
2960 setpcmark();
2961 curwin->w_cursor = pos;
2962 return OK;
2963 }
2964
2965 /*
2966 * Find the next paragraph or section in direction 'dir'.
2967 * Paragraphs are currently supposed to be separated by empty lines.
2968 * If 'what' is NUL we go to the next paragraph.
2969 * If 'what' is '{' or '}' we go to the next section.
2970 * If 'both' is TRUE also stop at '}'.
2971 * Return TRUE if the next paragraph or section was found.
2972 */
2973 int
2974 findpar(
2975 int *pincl, // Return: TRUE if last char is to be included
2976 int dir,
2977 long count,
2978 int what,
2979 int both)
2980 {
2981 linenr_T curr;
2982 int did_skip; // TRUE after separating lines have been skipped
2983 int first; // TRUE on first line
2984 int posix = (vim_strchr(p_cpo, CPO_PARA) != NULL);
2985 #ifdef FEAT_FOLDING
2986 linenr_T fold_first; // first line of a closed fold
2987 linenr_T fold_last; // last line of a closed fold
2988 int fold_skipped; // TRUE if a closed fold was skipped this
2989 // iteration
2990 #endif
2991
2992 curr = curwin->w_cursor.lnum;
2993
2994 while (count--)
2995 {
2996 did_skip = FALSE;
2997 for (first = TRUE; ; first = FALSE)
2998 {
2999 if (*ml_get(curr) != NUL)
3000 did_skip = TRUE;
3001
3002 #ifdef FEAT_FOLDING
3003 // skip folded lines
3004 fold_skipped = FALSE;
3005 if (first && hasFolding(curr, &fold_first, &fold_last))
3006 {
3007 curr = ((dir > 0) ? fold_last : fold_first) + dir;
3008 fold_skipped = TRUE;
3009 }
3010 #endif
3011
3012 // POSIX has its own ideas of what a paragraph boundary is and it
3013 // doesn't match historical Vi: It also stops at a "{" in the
3014 // first column and at an empty line.
3015 if (!first && did_skip && (startPS(curr, what, both)
3016 || (posix && what == NUL && *ml_get(curr) == '{')))
3017 break;
3018
3019 #ifdef FEAT_FOLDING
3020 if (fold_skipped)
3021 curr -= dir;
3022 #endif
3023 if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
3024 {
3025 if (count)
3026 return FALSE;
3027 curr -= dir;
3028 break;
3029 }
3030 }
3031 }
3032 setpcmark();
3033 if (both && *ml_get(curr) == '}') // include line with '}'
3034 ++curr;
3035 curwin->w_cursor.lnum = curr;
3036 if (curr == curbuf->b_ml.ml_line_count && what != '}')
3037 {
3038 char_u *line = ml_get(curr);
3039
3040 // Put the cursor on the last character in the last line and make the
3041 // motion inclusive.
3042 if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0)
3043 {
3044 --curwin->w_cursor.col;
3045 curwin->w_cursor.col -=
3046 (*mb_head_off)(line, line + curwin->w_cursor.col);
3047 *pincl = TRUE;
3048 }
3049 }
3050 else
3051 curwin->w_cursor.col = 0;
3052 return TRUE;
3053 }
3054
3055 /*
3056 * check if the string 's' is a nroff macro that is in option 'opt'
3057 */
3058 static int
3059 inmacro(char_u *opt, char_u *s)
3060 {
3061 char_u *macro;
3062
3063 for (macro = opt; macro[0]; ++macro)
3064 {
3065 // Accept two characters in the option being equal to two characters
3066 // in the line. A space in the option matches with a space in the
3067 // line or the line having ended.
3068 if ( (macro[0] == s[0]
3069 || (macro[0] == ' '
3070 && (s[0] == NUL || s[0] == ' ')))
3071 && (macro[1] == s[1]
3072 || ((macro[1] == NUL || macro[1] == ' ')
3073 && (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
3074 break;
3075 ++macro;
3076 if (macro[0] == NUL)
3077 break;
3078 }
3079 return (macro[0] != NUL);
3080 }
3081
3082 /*
3083 * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
3084 * If 'para' is '{' or '}' only check for sections.
3085 * If 'both' is TRUE also stop at '}'
3086 */
3087 int
3088 startPS(linenr_T lnum, int para, int both)
3089 {
3090 char_u *s;
3091
3092 s = ml_get(lnum);
3093 if (*s == para || *s == '\f' || (both && *s == '}'))
3094 return TRUE;
3095 if (*s == '.' && (inmacro(p_sections, s + 1) ||
3096 (!para && inmacro(p_para, s + 1))))
3097 return TRUE;
3098 return FALSE;
3099 }
3100
3101 /*
3102 * The following routines do the word searches performed by the 'w', 'W',
3103 * 'b', 'B', 'e', and 'E' commands.
3104 */
3105
3106 /*
3107 * To perform these searches, characters are placed into one of three
3108 * classes, and transitions between classes determine word boundaries.
3109 *
3110 * The classes are:
3111 *
3112 * 0 - white space
3113 * 1 - punctuation
3114 * 2 or higher - keyword characters (letters, digits and underscore)
3115 */
3116
3117 static int cls_bigword; // TRUE for "W", "B" or "E"
3118
3119 /*
3120 * cls() - returns the class of character at curwin->w_cursor
3121 *
3122 * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
3123 * from class 2 and higher are reported as class 1 since only white space
3124 * boundaries are of interest.
3125 */
3126 static int
3127 cls(void)
3128 {
3129 int c;
3130
3131 c = gchar_cursor();
3132 if (c == ' ' || c == '\t' || c == NUL)
3133 return 0;
3134 if (enc_dbcs != 0 && c > 0xFF)
3135 {
3136 // If cls_bigword, report multi-byte chars as class 1.
3137 if (enc_dbcs == DBCS_KOR && cls_bigword)
3138 return 1;
3139
3140 // process code leading/trailing bytes
3141 return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
3142 }
3143 if (enc_utf8)
3144 {
3145 c = utf_class(c);
3146 if (c != 0 && cls_bigword)
3147 return 1;
3148 return c;
3149 }
3150
3151 // If cls_bigword is TRUE, report all non-blanks as class 1.
3152 if (cls_bigword)
3153 return 1;
3154
3155 if (vim_iswordc(c))
3156 return 2;
3157 return 1;
3158 }
3159
3160
3161 /*
3162 * fwd_word(count, type, eol) - move forward one word
3163 *
3164 * Returns FAIL if the cursor was already at the end of the file.
3165 * If eol is TRUE, last word stops at end of line (for operators).
3166 */
3167 int
3168 fwd_word(
3169 long count,
3170 int bigword, // "W", "E" or "B"
3171 int eol)
3172 {
3173 int sclass; // starting class
3174 int i;
3175 int last_line;
3176
3177 curwin->w_cursor.coladd = 0;
3178 cls_bigword = bigword;
3179 while (--count >= 0)
3180 {
3181 #ifdef FEAT_FOLDING
3182 // When inside a range of folded lines, move to the last char of the
3183 // last line.
3184 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
3185 coladvance((colnr_T)MAXCOL);
3186 #endif
3187 sclass = cls();
3188
3189 /*
3190 * We always move at least one character, unless on the last
3191 * character in the buffer.
3192 */
3193 last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
3194 i = inc_cursor();
3195 if (i == -1 || (i >= 1 && last_line)) // started at last char in file
3196 return FAIL;
3197 if (i >= 1 && eol && count == 0) // started at last char in line
3198 return OK;
3199
3200 /*
3201 * Go one char past end of current word (if any)
3202 */
3203 if (sclass != 0)
3204 while (cls() == sclass)
3205 {
3206 i = inc_cursor();
3207 if (i == -1 || (i >= 1 && eol && count == 0))
3208 return OK;
3209 }
3210
3211 /*
3212 * go to next non-white
3213 */
3214 while (cls() == 0)
3215 {
3216 /*
3217 * We'll stop if we land on a blank line
3218 */
3219 if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
3220 break;
3221
3222 i = inc_cursor();
3223 if (i == -1 || (i >= 1 && eol && count == 0))
3224 return OK;
3225 }
3226 }
3227 return OK;
3228 }
3229
3230 /*
3231 * bck_word() - move backward 'count' words
3232 *
3233 * If stop is TRUE and we are already on the start of a word, move one less.
3234 *
3235 * Returns FAIL if top of the file was reached.
3236 */
3237 int
3238 bck_word(long count, int bigword, int stop)
3239 {
3240 int sclass; // starting class
3241
3242 curwin->w_cursor.coladd = 0;
3243 cls_bigword = bigword;
3244 while (--count >= 0)
3245 {
3246 #ifdef FEAT_FOLDING
3247 // When inside a range of folded lines, move to the first char of the
3248 // first line.
3249 if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
3250 curwin->w_cursor.col = 0;
3251 #endif
3252 sclass = cls();
3253 if (dec_cursor() == -1) // started at start of file
3254 return FAIL;
3255
3256 if (!stop || sclass == cls() || sclass == 0)
3257 {
3258 /*
3259 * Skip white space before the word.
3260 * Stop on an empty line.
3261 */
3262 while (cls() == 0)
3263 {
3264 if (curwin->w_cursor.col == 0
3265 && LINEEMPTY(curwin->w_cursor.lnum))
3266 goto finished;
3267 if (dec_cursor() == -1) // hit start of file, stop here
3268 return OK;
3269 }
3270
3271 /*
3272 * Move backward to start of this word.
3273 */
3274 if (skip_chars(cls(), BACKWARD))
3275 return OK;
3276 }
3277
3278 inc_cursor(); // overshot - forward one
3279 finished:
3280 stop = FALSE;
3281 }
3282 return OK;
3283 }
3284
3285 /*
3286 * end_word() - move to the end of the word
3287 *
3288 * There is an apparent bug in the 'e' motion of the real vi. At least on the
3289 * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
3290 * motion crosses blank lines. When the real vi crosses a blank line in an
3291 * 'e' motion, the cursor is placed on the FIRST character of the next
3292 * non-blank line. The 'E' command, however, works correctly. Since this
3293 * appears to be a bug, I have not duplicated it here.
3294 *
3295 * Returns FAIL if end of the file was reached.
3296 *
3297 * If stop is TRUE and we are already on the end of a word, move one less.
3298 * If empty is TRUE stop on an empty line.
3299 */
3300 int
3301 end_word(
3302 long count,
3303 int bigword,
3304 int stop,
3305 int empty)
3306 {
3307 int sclass; // starting class
3308
3309 curwin->w_cursor.coladd = 0;
3310 cls_bigword = bigword;
3311 while (--count >= 0)
3312 {
3313 #ifdef FEAT_FOLDING
3314 // When inside a range of folded lines, move to the last char of the
3315 // last line.
3316 if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
3317 coladvance((colnr_T)MAXCOL);
3318 #endif
3319 sclass = cls();
3320 if (inc_cursor() == -1)
3321 return FAIL;
3322
3323 /*
3324 * If we're in the middle of a word, we just have to move to the end
3325 * of it.
3326 */
3327 if (cls() == sclass && sclass != 0)
3328 {
3329 /*
3330 * Move forward to end of the current word
3331 */
3332 if (skip_chars(sclass, FORWARD))
3333 return FAIL;
3334 }
3335 else if (!stop || sclass == 0)
3336 {
3337 /*
3338 * We were at the end of a word. Go to the end of the next word.
3339 * First skip white space, if 'empty' is TRUE, stop at empty line.
3340 */
3341 while (cls() == 0)
3342 {
3343 if (empty && curwin->w_cursor.col == 0
3344 && LINEEMPTY(curwin->w_cursor.lnum))
3345 goto finished;
3346 if (inc_cursor() == -1) // hit end of file, stop here
3347 return FAIL;
3348 }
3349
3350 /*
3351 * Move forward to the end of this word.
3352 */
3353 if (skip_chars(cls(), FORWARD))
3354 return FAIL;
3355 }
3356 dec_cursor(); // overshot - one char backward
3357 finished:
3358 stop = FALSE; // we move only one word less
3359 }
3360 return OK;
3361 }
3362
3363 /*
3364 * Move back to the end of the word.
3365 *
3366 * Returns FAIL if start of the file was reached.
3367 */
3368 int
3369 bckend_word(
3370 long count,
3371 int bigword, // TRUE for "B"
3372 int eol) // TRUE: stop at end of line.
3373 {
3374 int sclass; // starting class
3375 int i;
3376
3377 curwin->w_cursor.coladd = 0;
3378 cls_bigword = bigword;
3379 while (--count >= 0)
3380 {
3381 sclass = cls();
3382 if ((i = dec_cursor()) == -1)
3383 return FAIL;
3384 if (eol && i == 1)
3385 return OK;
3386
3387 /*
3388 * Move backward to before the start of this word.
3389 */
3390 if (sclass != 0)
3391 {
3392 while (cls() == sclass)
3393 if ((i = dec_cursor()) == -1 || (eol && i == 1))
3394 return OK;
3395 }
3396
3397 /*
3398 * Move backward to end of the previous word
3399 */
3400 while (cls() == 0)
3401 {
3402 if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum))
3403 break;
3404 if ((i = dec_cursor()) == -1 || (eol && i == 1))
3405 return OK;
3406 }
3407 }
3408 return OK;
3409 }
3410
3411 /*
3412 * Skip a row of characters of the same class.
3413 * Return TRUE when end-of-file reached, FALSE otherwise.
3414 */
3415 static int
3416 skip_chars(int cclass, int dir)
3417 {
3418 while (cls() == cclass)
3419 if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
3420 return TRUE;
3421 return FALSE;
3422 }
3423
3424 #ifdef FEAT_TEXTOBJ
3425 /*
3426 * Go back to the start of the word or the start of white space
3427 */
3428 static void
3429 back_in_line(void)
3430 {
3431 int sclass; // starting class
3432
3433 sclass = cls();
3434 for (;;)
3435 {
3436 if (curwin->w_cursor.col == 0) // stop at start of line
3437 break;
3438 dec_cursor();
3439 if (cls() != sclass) // stop at start of word
3440 {
3441 inc_cursor();
3442 break;
3443 }
3444 }
3445 }
3446
3447 static void
3448 find_first_blank(pos_T *posp)
3449 {
3450 int c;
3451
3452 while (decl(posp) != -1)
3453 {
3454 c = gchar_pos(posp);
3455 if (!VIM_ISWHITE(c))
3456 {
3457 incl(posp);
3458 break;
3459 }
3460 }
3461 }
3462
3463 /*
3464 * Skip count/2 sentences and count/2 separating white spaces.
3465 */
3466 static void
3467 findsent_forward(
3468 long count,
3469 int at_start_sent) // cursor is at start of sentence
3470 {
3471 while (count--)
3472 {
3473 findsent(FORWARD, 1L);
3474 if (at_start_sent)
3475 find_first_blank(&curwin->w_cursor);
3476 if (count == 0 || at_start_sent)
3477 decl(&curwin->w_cursor);
3478 at_start_sent = !at_start_sent;
3479 }
3480 }
3481
3482 /*
3483 * Find word under cursor, cursor at end.
3484 * Used while an operator is pending, and in Visual mode.
3485 */
3486 int
3487 current_word(
3488 oparg_T *oap,
3489 long count,
3490 int include, // TRUE: include word and white space
3491 int bigword) // FALSE == word, TRUE == WORD
3492 {
3493 pos_T start_pos;
3494 pos_T pos;
3495 int inclusive = TRUE;
3496 int include_white = FALSE;
3497
3498 cls_bigword = bigword;
3499 CLEAR_POS(&start_pos);
3500
3501 // Correct cursor when 'selection' is exclusive
3502 if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
3503 dec_cursor();
3504
3505 /*
3506 * When Visual mode is not active, or when the VIsual area is only one
3507 * character, select the word and/or white space under the cursor.
3508 */
3509 if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual))
3510 {
3511 /*
3512 * Go to start of current word or white space.
3513 */
3514 back_in_line();
3515 start_pos = curwin->w_cursor;
3516
3517 /*
3518 * If the start is on white space, and white space should be included
3519 * (" word"), or start is not on white space, and white space should
3520 * not be included ("word"), find end of word.
3521 */
3522 if ((cls() == 0) == include)
3523 {
3524 if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
3525 return FAIL;
3526 }
3527 else
3528 {
3529 /*
3530 * If the start is not on white space, and white space should be
3531 * included ("word "), or start is on white space and white
3532 * space should not be included (" "), find start of word.
3533 * If we end up in the first column of the next line (single char
3534 * word) back up to end of the line.
3535 */
3536 fwd_word(1L, bigword, TRUE);
3537 if (curwin->w_cursor.col == 0)
3538 decl(&curwin->w_cursor);
3539 else
3540 oneleft();
3541
3542 if (include)
3543 include_white = TRUE;
3544 }
3545
3546 if (VIsual_active)
3547 {
3548 // should do something when inclusive == FALSE !
3549 VIsual = start_pos;
3550 redraw_curbuf_later(INVERTED); // update the inversion
3551 }
3552 else
3553 {
3554 oap->start = start_pos;
3555 oap->motion_type = MCHAR;
3556 }
3557 --count;
3558 }
3559
3560 /*
3561 * When count is still > 0, extend with more objects.
3562 */
3563 while (count > 0)
3564 {
3565 inclusive = TRUE;
3566 if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
3567 {
3568 /*
3569 * In Visual mode, with cursor at start: move cursor back.
3570 */
3571 if (decl(&curwin->w_cursor) == -1)
3572 return FAIL;
3573 if (include != (cls() != 0))
3574 {
3575 if (bck_word(1L, bigword, TRUE) == FAIL)
3576 return FAIL;
3577 }
3578 else
3579 {
3580 if (bckend_word(1L, bigword, TRUE) == FAIL)
3581 return FAIL;
3582 (void)incl(&curwin->w_cursor);
3583 }
3584 }
3585 else
3586 {
3587 /*
3588 * Move cursor forward one word and/or white area.
3589 */
3590 if (incl(&curwin->w_cursor) == -1)
3591 return FAIL;
3592 if (include != (cls() == 0))
3593 {
3594 if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1)
3595 return FAIL;
3596 /*
3597 * If end is just past a new-line, we don't want to include
3598 * the first character on the line.
3599 * Put cursor on last char of white.
3600 */
3601 if (oneleft() == FAIL)
3602 inclusive = FALSE;
3603 }
3604 else
3605 {
3606 if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
3607 return FAIL;
3608 }
3609 }
3610 --count;
3611 }
3612
3613 if (include_white && (cls() != 0
3614 || (curwin->w_cursor.col == 0 && !inclusive)))
3615 {
3616 /*
3617 * If we don't include white space at the end, move the start
3618 * to include some white space there. This makes "daw" work
3619 * better on the last word in a sentence (and "2daw" on last-but-one
3620 * word). Also when "2daw" deletes "word." at the end of the line
3621 * (cursor is at start of next line).
3622 * But don't delete white space at start of line (indent).
3623 */
3624 pos = curwin->w_cursor; // save cursor position
3625 curwin->w_cursor = start_pos;
3626 if (oneleft() == OK)
3627 {
3628 back_in_line();
3629 if (cls() == 0 && curwin->w_cursor.col > 0)
3630 {
3631 if (VIsual_active)
3632 VIsual = curwin->w_cursor;
3633 else
3634 oap->start = curwin->w_cursor;
3635 }
3636 }
3637 curwin->w_cursor = pos; // put cursor back at end
3638 }
3639
3640 if (VIsual_active)
3641 {
3642 if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor))
3643 inc_cursor();
3644 if (VIsual_mode == 'V')
3645 {
3646 VIsual_mode = 'v';
3647 redraw_cmdline = TRUE; // show mode later
3648 }
3649 }
3650 else
3651 oap->inclusive = inclusive;
3652
3653 return OK;
3654 }
3655
3656 /*
3657 * Find sentence(s) under the cursor, cursor at end.
3658 * When Visual active, extend it by one or more sentences.
3659 */
3660 int
3661 current_sent(oparg_T *oap, long count, int include)
3662 {
3663 pos_T start_pos;
3664 pos_T pos;
3665 int start_blank;
3666 int c;
3667 int at_start_sent;
3668 long ncount;
3669
3670 start_pos = curwin->w_cursor;
3671 pos = start_pos;
3672 findsent(FORWARD, 1L); // Find start of next sentence.
3673
3674 /*
3675 * When the Visual area is bigger than one character: Extend it.
3676 */
3677 if (VIsual_active && !EQUAL_POS(start_pos, VIsual))
3678 {
3679 extend:
3680 if (LT_POS(start_pos, VIsual))
3681 {
3682 /*
3683 * Cursor at start of Visual area.
3684 * Find out where we are:
3685 * - in the white space before a sentence
3686 * - in a sentence or just after it
3687 * - at the start of a sentence
3688 */
3689 at_start_sent = TRUE;
3690 decl(&pos);
3691 while (LT_POS(pos, curwin->w_cursor))
3692 {
3693 c = gchar_pos(&pos);
3694 if (!VIM_ISWHITE(c))
3695 {
3696 at_start_sent = FALSE;
3697 break;
3698 }
3699 incl(&pos);
3700 }
3701 if (!at_start_sent)
3702 {
3703 findsent(BACKWARD, 1L);
3704 if (EQUAL_POS(curwin->w_cursor, start_pos))
3705 at_start_sent = TRUE; // exactly at start of sentence
3706 else
3707 // inside a sentence, go to its end (start of next)
3708 findsent(FORWARD, 1L);
3709 }
3710 if (include) // "as" gets twice as much as "is"
3711 count *= 2;
3712 while (count--)
3713 {
3714 if (at_start_sent)
3715 find_first_blank(&curwin->w_cursor);
3716 c = gchar_cursor();
3717 if (!at_start_sent || (!include && !VIM_ISWHITE(c)))
3718 findsent(BACKWARD, 1L);
3719 at_start_sent = !at_start_sent;
3720 }
3721 }
3722 else
3723 {
3724 /*
3725 * Cursor at end of Visual area.
3726 * Find out where we are:
3727 * - just before a sentence
3728 * - just before or in the white space before a sentence
3729 * - in a sentence
3730 */
3731 incl(&pos);
3732 at_start_sent = TRUE;
3733 // not just before a sentence
3734 if (!EQUAL_POS(pos, curwin->w_cursor))
3735 {
3736 at_start_sent = FALSE;
3737 while (LT_POS(pos, curwin->w_cursor))
3738 {
3739 c = gchar_pos(&pos);
3740 if (!VIM_ISWHITE(c))
3741 {
3742 at_start_sent = TRUE;
3743 break;
3744 }
3745 incl(&pos);
3746 }
3747 if (at_start_sent) // in the sentence
3748 findsent(BACKWARD, 1L);
3749 else // in/before white before a sentence
3750 curwin->w_cursor = start_pos;
3751 }
3752
3753 if (include) // "as" gets twice as much as "is"
3754 count *= 2;
3755 findsent_forward(count, at_start_sent);
3756 if (*p_sel == 'e')
3757 ++curwin->w_cursor.col;
3758 }
3759 return OK;
3760 }
3761
3762 /*
3763 * If the cursor started on a blank, check if it is just before the start
3764 * of the next sentence.
3765 */
3766 while (c = gchar_pos(&pos), VIM_ISWHITE(c)) // VIM_ISWHITE() is a macro
3767 incl(&pos);
3768 if (EQUAL_POS(pos, curwin->w_cursor))
3769 {
3770 start_blank = TRUE;
3771 find_first_blank(&start_pos); // go back to first blank
3772 }
3773 else
3774 {
3775 start_blank = FALSE;
3776 findsent(BACKWARD, 1L);
3777 start_pos = curwin->w_cursor;
3778 }
3779 if (include)
3780 ncount = count * 2;
3781 else
3782 {
3783 ncount = count;
3784 if (start_blank)
3785 --ncount;
3786 }
3787 if (ncount > 0)
3788 findsent_forward(ncount, TRUE);
3789 else
3790 decl(&curwin->w_cursor);
3791
3792 if (include)
3793 {
3794 /*
3795 * If the blank in front of the sentence is included, exclude the
3796 * blanks at the end of the sentence, go back to the first blank.
3797 * If there are no trailing blanks, try to include leading blanks.
3798 */
3799 if (start_blank)
3800 {
3801 find_first_blank(&curwin->w_cursor);
3802 c = gchar_pos(&curwin->w_cursor); // VIM_ISWHITE() is a macro
3803 if (VIM_ISWHITE(c))
3804 decl(&curwin->w_cursor);
3805 }
3806 else if (c = gchar_cursor(), !VIM_ISWHITE(c))
3807 find_first_blank(&start_pos);
3808 }
3809
3810 if (VIsual_active)
3811 {
3812 // Avoid getting stuck with "is" on a single space before a sentence.
3813 if (EQUAL_POS(start_pos, curwin->w_cursor))
3814 goto extend;
3815 if (*p_sel == 'e')
3816 ++curwin->w_cursor.col;
3817 VIsual = start_pos;
3818 VIsual_mode = 'v';
3819 redraw_cmdline = TRUE; // show mode later
3820 redraw_curbuf_later(INVERTED); // update the inversion
3821 }
3822 else
3823 {
3824 // include a newline after the sentence, if there is one
3825 if (incl(&curwin->w_cursor) == -1)
3826 oap->inclusive = TRUE;
3827 else
3828 oap->inclusive = FALSE;
3829 oap->start = start_pos;
3830 oap->motion_type = MCHAR;
3831 }
3832 return OK;
3833 }
3834
3835 /*
3836 * Find block under the cursor, cursor at end.
3837 * "what" and "other" are two matching parenthesis/brace/etc.
3838 */
3839 int
3840 current_block(
3841 oparg_T *oap,
3842 long count,
3843 int include, // TRUE == include white space
3844 int what, // '(', '{', etc.
3845 int other) // ')', '}', etc.
3846 {
3847 pos_T old_pos;
3848 pos_T *pos = NULL;
3849 pos_T start_pos;
3850 pos_T *end_pos;
3851 pos_T old_start, old_end;
3852 char_u *save_cpo;
3853 int sol = FALSE; // '{' at start of line
3854
3855 old_pos = curwin->w_cursor;
3856 old_end = curwin->w_cursor; // remember where we started
3857 old_start = old_end;
3858
3859 /*
3860 * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
3861 */
3862 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
3863 {
3864 setpcmark();
3865 if (what == '{') // ignore indent
3866 while (inindent(1))
3867 if (inc_cursor() != 0)
3868 break;
3869 if (gchar_cursor() == what)
3870 // cursor on '(' or '{', move cursor just after it
3871 ++curwin->w_cursor.col;
3872 }
3873 else if (LT_POS(VIsual, curwin->w_cursor))
3874 {
3875 old_start = VIsual;
3876 curwin->w_cursor = VIsual; // cursor at low end of Visual
3877 }
3878 else
3879 old_end = VIsual;
3880
3881 /*
3882 * Search backwards for unclosed '(', '{', etc..
3883 * Put this position in start_pos.
3884 * Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
3885 * user wants.
3886 */
3887 save_cpo = p_cpo;
3888 p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
3889 while (count-- > 0)
3890 {
3891 if ((pos = findmatch(NULL, what)) == NULL)
3892 break;
3893 curwin->w_cursor = *pos;
3894 start_pos = *pos; // the findmatch for end_pos will overwrite *pos
3895 }
3896 p_cpo = save_cpo;
3897
3898 /*
3899 * Search for matching ')', '}', etc.
3900 * Put this position in curwin->w_cursor.
3901 */
3902 if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
3903 {
3904 curwin->w_cursor = old_pos;
3905 return FAIL;
3906 }
3907 curwin->w_cursor = *end_pos;
3908
3909 /*
3910 * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
3911 * If the ending '}', ')' or ']' is only preceded by indent, skip that
3912 * indent. But only if the resulting area is not smaller than what we
3913 * started with.
3914 */
3915 while (!include)
3916 {
3917 incl(&start_pos);
3918 sol = (curwin->w_cursor.col == 0);
3919 decl(&curwin->w_cursor);
3920 while (inindent(1))
3921 {
3922 sol = TRUE;
3923 if (decl(&curwin->w_cursor) != 0)
3924 break;
3925 }
3926
3927 /*
3928 * In Visual mode, when the resulting area is not bigger than what we
3929 * started with, extend it to the next block, and then exclude again.
3930 */
3931 if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor)
3932 && VIsual_active)
3933 {
3934 curwin->w_cursor = old_start;
3935 decl(&curwin->w_cursor);
3936 if ((pos = findmatch(NULL, what)) == NULL)
3937 {
3938 curwin->w_cursor = old_pos;
3939 return FAIL;
3940 }
3941 start_pos = *pos;
3942 curwin->w_cursor = *pos;
3943 if ((end_pos = findmatch(NULL, other)) == NULL)
3944 {
3945 curwin->w_cursor = old_pos;
3946 return FAIL;
3947 }
3948 curwin->w_cursor = *end_pos;
3949 }
3950 else
3951 break;
3952 }
3953
3954 if (VIsual_active)
3955 {
3956 if (*p_sel == 'e')
3957 inc(&curwin->w_cursor);
3958 if (sol && gchar_cursor() != NUL)
3959 inc(&curwin->w_cursor); // include the line break
3960 VIsual = start_pos;
3961 VIsual_mode = 'v';
3962 redraw_curbuf_later(INVERTED); // update the inversion
3963 showmode();
3964 }
3965 else
3966 {
3967 oap->start = start_pos;
3968 oap->motion_type = MCHAR;
3969 oap->inclusive = FALSE;
3970 if (sol)
3971 incl(&curwin->w_cursor);
3972 else if (LTOREQ_POS(start_pos, curwin->w_cursor))
3973 // Include the character under the cursor.
3974 oap->inclusive = TRUE;
3975 else
3976 // End is before the start (no text in between <>, [], etc.): don't
3977 // operate on any text.
3978 curwin->w_cursor = start_pos;
3979 }
3980
3981 return OK;
3982 }
3983
3984 /*
3985 * Return TRUE if the cursor is on a "<aaa>" tag. Ignore "<aaa/>".
3986 * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>".
3987 */
3988 static int
3989 in_html_tag(
3990 int end_tag)
3991 {
3992 char_u *line = ml_get_curline();
3993 char_u *p;
3994 int c;
3995 int lc = NUL;
3996 pos_T pos;
3997
3998 if (enc_dbcs)
3999 {
4000 char_u *lp = NULL;
4001
4002 // We search forward until the cursor, because searching backwards is
4003 // very slow for DBCS encodings.
4004 for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p))
4005 if (*p == '>' || *p == '<')
4006 {
4007 lc = *p;
4008 lp = p;
4009 }
4010 if (*p != '<') // check for '<' under cursor
4011 {
4012 if (lc != '<')
4013 return FALSE;
4014 p = lp;
4015 }
4016 }
4017 else
4018 {
4019 for (p = line + curwin->w_cursor.col; p > line; )
4020 {
4021 if (*p == '<') // find '<' under/before cursor
4022 break;
4023 MB_PTR_BACK(line, p);
4024 if (*p == '>') // find '>' before cursor
4025 break;
4026 }
4027 if (*p != '<')
4028 return FALSE;
4029 }
4030
4031 pos.lnum = curwin->w_cursor.lnum;
4032 pos.col = (colnr_T)(p - line);
4033
4034 MB_PTR_ADV(p);
4035 if (end_tag)
4036 // check that there is a '/' after the '<'
4037 return *p == '/';
4038
4039 // check that there is no '/' after the '<'
4040 if (*p == '/')
4041 return FALSE;
4042
4043 // check that the matching '>' is not preceded by '/'
4044 for (;;)
4045 {
4046 if (inc(&pos) < 0)
4047 return FALSE;
4048 c = *ml_get_pos(&pos);
4049 if (c == '>')
4050 break;
4051 lc = c;
4052 }
4053 return lc != '/';
4054 }
4055
4056 /*
4057 * Find tag block under the cursor, cursor at end.
4058 */
4059 int
4060 current_tagblock(
4061 oparg_T *oap,
4062 long count_arg,
4063 int include) // TRUE == include white space
4064 {
4065 long count = count_arg;
4066 long n;
4067 pos_T old_pos;
4068 pos_T start_pos;
4069 pos_T end_pos;
4070 pos_T old_start, old_end;
4071 char_u *spat, *epat;
4072 char_u *p;
4073 char_u *cp;
4074 int len;
4075 int r;
4076 int do_include = include;
4077 int save_p_ws = p_ws;
4078 int retval = FAIL;
4079 int is_inclusive = TRUE;
4080
4081 p_ws = FALSE;
4082
4083 old_pos = curwin->w_cursor;
4084 old_end = curwin->w_cursor; // remember where we started
4085 old_start = old_end;
4086 if (!VIsual_active || *p_sel == 'e')
4087 decl(&old_end); // old_end is inclusive
4088
4089 /*
4090 * If we start on "<aaa>" select that block.
4091 */
4092 if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
4093 {
4094 setpcmark();
4095
4096 // ignore indent
4097 while (inindent(1))
4098 if (inc_cursor() != 0)
4099 break;
4100
4101 if (in_html_tag(FALSE))
4102 {
4103 // cursor on start tag, move to its '>'
4104 while (*ml_get_cursor() != '>')
4105 if (inc_cursor() < 0)
4106 break;
4107 }
4108 else if (in_html_tag(TRUE))
4109 {
4110 // cursor on end tag, move to just before it
4111 while (*ml_get_cursor() != '<')
4112 if (dec_cursor() < 0)
4113 break;
4114 dec_cursor();
4115 old_end = curwin->w_cursor;
4116 }
4117 }
4118 else if (LT_POS(VIsual, curwin->w_cursor))
4119 {
4120 old_start = VIsual;
4121 curwin->w_cursor = VIsual; // cursor at low end of Visual
4122 }
4123 else
4124 old_end = VIsual;
4125
4126 again:
4127 /*
4128 * Search backwards for unclosed "<aaa>".
4129 * Put this position in start_pos.
4130 */
4131 for (n = 0; n < count; ++n)
4132 {
4133 if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
4134 (char_u *)"",
4135 (char_u *)"</[^>]*>", BACKWARD, NULL, 0,
4136 NULL, (linenr_T)0, 0L) <= 0)
4137 {
4138 curwin->w_cursor = old_pos;
4139 goto theend;
4140 }
4141 }
4142 start_pos = curwin->w_cursor;
4143
4144 /*
4145 * Search for matching "</aaa>". First isolate the "aaa".
4146 */
4147 inc_cursor();
4148 p = ml_get_cursor();
4149 for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp))
4150 ;
4151 len = (int)(cp - p);
4152 if (len == 0)
4153 {
4154 curwin->w_cursor = old_pos;
4155 goto theend;
4156 }
4157 spat = alloc(len + 31);
4158 epat = alloc(len + 9);
4159 if (spat == NULL || epat == NULL)
4160 {
4161 vim_free(spat);
4162 vim_free(epat);
4163 curwin->w_cursor = old_pos;
4164 goto theend;
4165 }
4166 sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p);
4167 sprintf((char *)epat, "</%.*s>\\c", len, p);
4168
4169 r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL,
4170 0, NULL, (linenr_T)0, 0L);
4171
4172 vim_free(spat);
4173 vim_free(epat);
4174
4175 if (r < 1 || LT_POS(curwin->w_cursor, old_end))
4176 {
4177 // Can't find other end or it's before the previous end. Could be a
4178 // HTML tag that doesn't have a matching end. Search backwards for
4179 // another starting tag.
4180 count = 1;
4181 curwin->w_cursor = start_pos;
4182 goto again;
4183 }
4184
4185 if (do_include)
4186 {
4187 // Include up to the '>'.
4188 while (*ml_get_cursor() != '>')
4189 if (inc_cursor() < 0)
4190 break;
4191 }
4192 else
4193 {
4194 char_u *c = ml_get_cursor();
4195
4196 // Exclude the '<' of the end tag.
4197 // If the closing tag is on new line, do not decrement cursor, but
4198 // make operation exclusive, so that the linefeed will be selected
4199 if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0)
4200 // do not decrement cursor
4201 is_inclusive = FALSE;
4202 else if (*c == '<')
4203 dec_cursor();
4204 }
4205 end_pos = curwin->w_cursor;
4206
4207 if (!do_include)
4208 {
4209 // Exclude the start tag.
4210 curwin->w_cursor = start_pos;
4211 while (inc_cursor() >= 0)
4212 if (*ml_get_cursor() == '>')
4213 {
4214 inc_cursor();
4215 start_pos = curwin->w_cursor;
4216 break;
4217 }
4218 curwin->w_cursor = end_pos;
4219
4220 // If we are in Visual mode and now have the same text as before set
4221 // "do_include" and try again.
4222 if (VIsual_active && EQUAL_POS(start_pos, old_start)
4223 && EQUAL_POS(end_pos, old_end))
4224 {
4225 do_include = TRUE;
4226 curwin->w_cursor = old_start;
4227 count = count_arg;
4228 goto again;
4229 }
4230 }
4231
4232 if (VIsual_active)
4233 {
4234 // If the end is before the start there is no text between tags, select
4235 // the char under the cursor.
4236 if (LT_POS(end_pos, start_pos))
4237 curwin->w_cursor = start_pos;
4238 else if (*p_sel == 'e')
4239 inc_cursor();
4240 VIsual = start_pos;
4241 VIsual_mode = 'v';
4242 redraw_curbuf_later(INVERTED); // update the inversion
4243 showmode();
4244 }
4245 else
4246 {
4247 oap->start = start_pos;
4248 oap->motion_type = MCHAR;
4249 if (LT_POS(end_pos, start_pos))
4250 {
4251 // End is before the start: there is no text between tags; operate
4252 // on an empty area.
4253 curwin->w_cursor = start_pos;
4254 oap->inclusive = FALSE;
4255 }
4256 else
4257 oap->inclusive = is_inclusive;
4258 }
4259 retval = OK;
4260
4261 theend:
4262 p_ws = save_p_ws;
4263 return retval;
4264 }
4265
4266 int
4267 current_par(
4268 oparg_T *oap,
4269 long count,
4270 int include, // TRUE == include white space
4271 int type) // 'p' for paragraph, 'S' for section
4272 {
4273 linenr_T start_lnum;
4274 linenr_T end_lnum;
4275 int white_in_front;
4276 int dir;
4277 int start_is_white;
4278 int prev_start_is_white;
4279 int retval = OK;
4280 int do_white = FALSE;
4281 int t;
4282 int i;
4283
4284 if (type == 'S') // not implemented yet
4285 return FAIL;
4286
4287 start_lnum = curwin->w_cursor.lnum;
4288
4289 /*
4290 * When visual area is more than one line: extend it.
4291 */
4292 if (VIsual_active && start_lnum != VIsual.lnum)
4293 {
4294 extend:
4295 if (start_lnum < VIsual.lnum)
4296 dir = BACKWARD;
4297 else
4298 dir = FORWARD;
4299 for (i = count; --i >= 0; )
4300 {
4301 if (start_lnum ==
4302 (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
4303 {
4304 retval = FAIL;
4305 break;
4306 }
4307
4308 prev_start_is_white = -1;
4309 for (t = 0; t < 2; ++t)
4310 {
4311 start_lnum += dir;
4312 start_is_white = linewhite(start_lnum);
4313 if (prev_start_is_white == start_is_white)
4314 {
4315 start_lnum -= dir;
4316 break;
4317 }
4318 for (;;)
4319 {
4320 if (start_lnum == (dir == BACKWARD
4321 ? 1 : curbuf->b_ml.ml_line_count))
4322 break;
4323 if (start_is_white != linewhite(start_lnum + dir)
4324 || (!start_is_white
4325 && startPS(start_lnum + (dir > 0
4326 ? 1 : 0), 0, 0)))
4327 break;
4328 start_lnum += dir;
4329 }
4330 if (!include)
4331 break;
4332 if (start_lnum == (dir == BACKWARD
4333 ? 1 : curbuf->b_ml.ml_line_count))
4334 break;
4335 prev_start_is_white = start_is_white;
4336 }
4337 }
4338 curwin->w_cursor.lnum = start_lnum;
4339 curwin->w_cursor.col = 0;
4340 return retval;
4341 }
4342
4343 /*
4344 * First move back to the start_lnum of the paragraph or white lines
4345 */
4346 white_in_front = linewhite(start_lnum);
4347 while (start_lnum > 1)
4348 {
4349 if (white_in_front) // stop at first white line
4350 {
4351 if (!linewhite(start_lnum - 1))
4352 break;
4353 }
4354 else // stop at first non-white line of start of paragraph
4355 {
4356 if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
4357 break;
4358 }
4359 --start_lnum;
4360 }
4361
4362 /*
4363 * Move past the end of any white lines.
4364 */
4365 end_lnum = start_lnum;
4366 while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum))
4367 ++end_lnum;
4368
4369 --end_lnum;
4370 i = count;
4371 if (!include && white_in_front)
4372 --i;
4373 while (i--)
4374 {
4375 if (end_lnum == curbuf->b_ml.ml_line_count)
4376 return FAIL;
4377
4378 if (!include)
4379 do_white = linewhite(end_lnum + 1);
4380
4381 if (include || !do_white)
4382 {
4383 ++end_lnum;
4384 /*
4385 * skip to end of paragraph
4386 */
4387 while (end_lnum < curbuf->b_ml.ml_line_count
4388 && !linewhite(end_lnum + 1)
4389 && !startPS(end_lnum + 1, 0, 0))
4390 ++end_lnum;
4391 }
4392
4393 if (i == 0 && white_in_front && include)
4394 break;
4395
4396 /*
4397 * skip to end of white lines after paragraph
4398 */
4399 if (include || do_white)
4400 while (end_lnum < curbuf->b_ml.ml_line_count
4401 && linewhite(end_lnum + 1))
4402 ++end_lnum;
4403 }
4404
4405 /*
4406 * If there are no empty lines at the end, try to find some empty lines at
4407 * the start (unless that has been done already).
4408 */
4409 if (!white_in_front && !linewhite(end_lnum) && include)
4410 while (start_lnum > 1 && linewhite(start_lnum - 1))
4411 --start_lnum;
4412
4413 if (VIsual_active)
4414 {
4415 // Problem: when doing "Vipipip" nothing happens in a single white
4416 // line, we get stuck there. Trap this here.
4417 if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
4418 goto extend;
4419 if (VIsual.lnum != start_lnum)
4420 {
4421 VIsual.lnum = start_lnum;
4422 VIsual.col = 0;
4423 }
4424 VIsual_mode = 'V';
4425 redraw_curbuf_later(INVERTED); // update the inversion
4426 showmode();
4427 }
4428 else
4429 {
4430 oap->start.lnum = start_lnum;
4431 oap->start.col = 0;
4432 oap->motion_type = MLINE;
4433 }
4434 curwin->w_cursor.lnum = end_lnum;
4435 curwin->w_cursor.col = 0;
4436
4437 return OK;
4438 }
4439
4440 /*
4441 * Search quote char from string line[col].
4442 * Quote character escaped by one of the characters in "escape" is not counted
4443 * as a quote.
4444 * Returns column number of "quotechar" or -1 when not found.
4445 */
4446 static int
4447 find_next_quote(
4448 char_u *line,
4449 int col,
4450 int quotechar,
4451 char_u *escape) // escape characters, can be NULL
4452 {
4453 int c;
4454
4455 for (;;)
4456 {
4457 c = line[col];
4458 if (c == NUL)
4459 return -1;
4460 else if (escape != NULL && vim_strchr(escape, c))
4461 ++col;
4462 else if (c == quotechar)
4463 break;
4464 if (has_mbyte)
4465 col += (*mb_ptr2len)(line + col);
4466 else
4467 ++col;
4468 }
4469 return col;
4470 }
4471
4472 /*
4473 * Search backwards in "line" from column "col_start" to find "quotechar".
4474 * Quote character escaped by one of the characters in "escape" is not counted
4475 * as a quote.
4476 * Return the found column or zero.
4477 */
4478 static int
4479 find_prev_quote(
4480 char_u *line,
4481 int col_start,
4482 int quotechar,
4483 char_u *escape) // escape characters, can be NULL
4484 {
4485 int n;
4486
4487 while (col_start > 0)
4488 {
4489 --col_start;
4490 col_start -= (*mb_head_off)(line, line + col_start);
4491 n = 0;
4492 if (escape != NULL)
4493 while (col_start - n > 0 && vim_strchr(escape,
4494 line[col_start - n - 1]) != NULL)
4495 ++n;
4496 if (n & 1)
4497 col_start -= n; // uneven number of escape chars, skip it
4498 else if (line[col_start] == quotechar)
4499 break;
4500 }
4501 return col_start;
4502 }
4503
4504 /*
4505 * Find quote under the cursor, cursor at end.
4506 * Returns TRUE if found, else FALSE.
4507 */
4508 int
4509 current_quote(
4510 oparg_T *oap,
4511 long count,
4512 int include, // TRUE == include quote char
4513 int quotechar) // Quote character
4514 {
4515 char_u *line = ml_get_curline();
4516 int col_end;
4517 int col_start = curwin->w_cursor.col;
4518 int inclusive = FALSE;
4519 int vis_empty = TRUE; // Visual selection <= 1 char
4520 int vis_bef_curs = FALSE; // Visual starts before cursor
4521 int did_exclusive_adj = FALSE; // adjusted pos for 'selection'
4522 int inside_quotes = FALSE; // Looks like "i'" done before
4523 int selected_quote = FALSE; // Has quote inside selection
4524 int i;
4525 int restore_vis_bef = FALSE; // restore VIsual on abort
4526
4527 // When 'selection' is "exclusive" move the cursor to where it would be
4528 // with 'selection' "inclusive", so that the logic is the same for both.
4529 // The cursor then is moved forward after adjusting the area.
4530 if (VIsual_active)
4531 {
4532 // this only works within one line
4533 if (VIsual.lnum != curwin->w_cursor.lnum)
4534 return FALSE;
4535
4536 vis_bef_curs = LT_POS(VIsual, curwin->w_cursor);
4537 vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
4538 if (*p_sel == 'e')
4539 {
4540 if (vis_bef_curs)
4541 {
4542 dec_cursor();
4543 did_exclusive_adj = TRUE;
4544 }
4545 else if (!vis_empty)
4546 {
4547 dec(&VIsual);
4548 did_exclusive_adj = TRUE;
4549 }
4550 vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
4551 if (!vis_bef_curs && !vis_empty)
4552 {
4553 // VIsual needs to be the start of Visual selection.
4554 pos_T t = curwin->w_cursor;
4555
4556 curwin->w_cursor = VIsual;
4557 VIsual = t;
4558 vis_bef_curs = TRUE;
4559 restore_vis_bef = TRUE;
4560 }
4561 }
4562 }
4563
4564 if (!vis_empty)
4565 {
4566 // Check if the existing selection exactly spans the text inside
4567 // quotes.
4568 if (vis_bef_curs)
4569 {
4570 inside_quotes = VIsual.col > 0
4571 && line[VIsual.col - 1] == quotechar
4572 && line[curwin->w_cursor.col] != NUL
4573 && line[curwin->w_cursor.col + 1] == quotechar;
4574 i = VIsual.col;
4575 col_end = curwin->w_cursor.col;
4576 }
4577 else
4578 {
4579 inside_quotes = curwin->w_cursor.col > 0
4580 && line[curwin->w_cursor.col - 1] == quotechar
4581 && line[VIsual.col] != NUL
4582 && line[VIsual.col + 1] == quotechar;
4583 i = curwin->w_cursor.col;
4584 col_end = VIsual.col;
4585 }
4586
4587 // Find out if we have a quote in the selection.
4588 while (i <= col_end)
4589 if (line[i++] == quotechar)
4590 {
4591 selected_quote = TRUE;
4592 break;
4593 }
4594 }
4595
4596 if (!vis_empty && line[col_start] == quotechar)
4597 {
4598 // Already selecting something and on a quote character. Find the
4599 // next quoted string.
4600 if (vis_bef_curs)
4601 {
4602 // Assume we are on a closing quote: move to after the next
4603 // opening quote.
4604 col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
4605 if (col_start < 0)
4606 goto abort_search;
4607 col_end = find_next_quote(line, col_start + 1, quotechar,
4608 curbuf->b_p_qe);
4609 if (col_end < 0)
4610 {
4611 // We were on a starting quote perhaps?
4612 col_end = col_start;
4613 col_start = curwin->w_cursor.col;
4614 }
4615 }
4616 else
4617 {
4618 col_end = find_prev_quote(line, col_start, quotechar, NULL);
4619 if (line[col_end] != quotechar)
4620 goto abort_search;
4621 col_start = find_prev_quote(line, col_end, quotechar,
4622 curbuf->b_p_qe);
4623 if (line[col_start] != quotechar)
4624 {
4625 // We were on an ending quote perhaps?
4626 col_start = col_end;
4627 col_end = curwin->w_cursor.col;
4628 }
4629 }
4630 }
4631 else
4632
4633 if (line[col_start] == quotechar || !vis_empty)
4634 {
4635 int first_col = col_start;
4636
4637 if (!vis_empty)
4638 {
4639 if (vis_bef_curs)
4640 first_col = find_next_quote(line, col_start, quotechar, NULL);
4641 else
4642 first_col = find_prev_quote(line, col_start, quotechar, NULL);
4643 }
4644
4645 // The cursor is on a quote, we don't know if it's the opening or
4646 // closing quote. Search from the start of the line to find out.
4647 // Also do this when there is a Visual area, a' may leave the cursor
4648 // in between two strings.
4649 col_start = 0;
4650 for (;;)
4651 {
4652 // Find open quote character.
4653 col_start = find_next_quote(line, col_start, quotechar, NULL);
4654 if (col_start < 0 || col_start > first_col)
4655 goto abort_search;
4656 // Find close quote character.
4657 col_end = find_next_quote(line, col_start + 1, quotechar,
4658 curbuf->b_p_qe);
4659 if (col_end < 0)
4660 goto abort_search;
4661 // If is cursor between start and end quote character, it is
4662 // target text object.
4663 if (col_start <= first_col && first_col <= col_end)
4664 break;
4665 col_start = col_end + 1;
4666 }
4667 }
4668 else
4669 {
4670 // Search backward for a starting quote.
4671 col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
4672 if (line[col_start] != quotechar)
4673 {
4674 // No quote before the cursor, look after the cursor.
4675 col_start = find_next_quote(line, col_start, quotechar, NULL);
4676 if (col_start < 0)
4677 goto abort_search;
4678 }
4679
4680 // Find close quote character.
4681 col_end = find_next_quote(line, col_start + 1, quotechar,
4682 curbuf->b_p_qe);
4683 if (col_end < 0)
4684 goto abort_search;
4685 }
4686
4687 // When "include" is TRUE, include spaces after closing quote or before
4688 // the starting quote.
4689 if (include)
4690 {
4691 if (VIM_ISWHITE(line[col_end + 1]))
4692 while (VIM_ISWHITE(line[col_end + 1]))
4693 ++col_end;
4694 else
4695 while (col_start > 0 && VIM_ISWHITE(line[col_start - 1]))
4696 --col_start;
4697 }
4698
4699 // Set start position. After vi" another i" must include the ".
4700 // For v2i" include the quotes.
4701 if (!include && count < 2 && (vis_empty || !inside_quotes))
4702 ++col_start;
4703 curwin->w_cursor.col = col_start;
4704 if (VIsual_active)
4705 {
4706 // Set the start of the Visual area when the Visual area was empty, we
4707 // were just inside quotes or the Visual area didn't start at a quote
4708 // and didn't include a quote.
4709 if (vis_empty
4710 || (vis_bef_curs
4711 && !selected_quote
4712 && (inside_quotes
4713 || (line[VIsual.col] != quotechar
4714 && (VIsual.col == 0
4715 || line[VIsual.col - 1] != quotechar)))))
4716 {
4717 VIsual = curwin->w_cursor;
4718 redraw_curbuf_later(INVERTED);
4719 }
4720 }
4721 else
4722 {
4723 oap->start = curwin->w_cursor;
4724 oap->motion_type = MCHAR;
4725 }
4726
4727 // Set end position.
4728 curwin->w_cursor.col = col_end;
4729 if ((include || count > 1 // After vi" another i" must include the ".
4730 || (!vis_empty && inside_quotes)
4731 ) && inc_cursor() == 2)
4732 inclusive = TRUE;
4733 if (VIsual_active)
4734 {
4735 if (vis_empty || vis_bef_curs)
4736 {
4737 // decrement cursor when 'selection' is not exclusive
4738 if (*p_sel != 'e')
4739 dec_cursor();
4740 }
4741 else
4742 {
4743 // Cursor is at start of Visual area. Set the end of the Visual
4744 // area when it was just inside quotes or it didn't end at a
4745 // quote.
4746 if (inside_quotes
4747 || (!selected_quote
4748 && line[VIsual.col] != quotechar
4749 && (line[VIsual.col] == NUL
4750 || line[VIsual.col + 1] != quotechar)))
4751 {
4752 dec_cursor();
4753 VIsual = curwin->w_cursor;
4754 }
4755 curwin->w_cursor.col = col_start;
4756 }
4757 if (VIsual_mode == 'V')
4758 {
4759 VIsual_mode = 'v';
4760 redraw_cmdline = TRUE; // show mode later
4761 }
4762 }
4763 else
4764 {
4765 // Set inclusive and other oap's flags.
4766 oap->inclusive = inclusive;
4767 }
4768
4769 return OK;
4770
4771 abort_search:
4772 if (VIsual_active && *p_sel == 'e')
4773 {
4774 if (did_exclusive_adj)
4775 inc_cursor();
4776 if (restore_vis_bef)
4777 {
4778 pos_T t = curwin->w_cursor;
4779
4780 curwin->w_cursor = VIsual;
4781 VIsual = t;
4782 }
4783 }
4784 return FALSE;
4785 }
4786
4787 #endif // FEAT_TEXTOBJ
4788
4789 /*
4790 * Check if the pattern is zero-width. 2839 * Check if the pattern is zero-width.
4791 * If move is TRUE, check from the beginning of the buffer, else from position 2840 * If move is TRUE, check from the beginning of the buffer, else from position
4792 * "cur". 2841 * "cur".
4793 * "direction" is FORWARD or BACKWARD. 2842 * "direction" is FORWARD or BACKWARD.
4794 * Returns TRUE, FALSE or -1 for failure. 2843 * Returns TRUE, FALSE or -1 for failure.