Mercurial > vim
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. |