Mercurial > vim
comparison src/search.c @ 20647:8a2b86a39ef4 v8.2.0877
patch 8.2.0877: cannot get the search statistics
Commit: https://github.com/vim/vim/commit/e8f5ec0d30b629d7166f0ad03434065d8bc822df
Author: Bram Moolenaar <Bram@vim.org>
Date: Mon Jun 1 17:28:35 2020 +0200
patch 8.2.0877: cannot get the search statistics
Problem: Cannot get the search statistics.
Solution: Add the searchcount() function. (Fujiwara Takuya, closes https://github.com/vim/vim/issues/4446)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Mon, 01 Jun 2020 17:30:04 +0200 |
parents | 00fff78a929a |
children | 3e03edae7e6f |
comparison
equal
deleted
inserted
replaced
20646:ad14bd3f8c4f | 20647:8a2b86a39ef4 |
---|---|
19 static int check_linecomment(char_u *line); | 19 static int check_linecomment(char_u *line); |
20 #ifdef FEAT_FIND_ID | 20 #ifdef FEAT_FIND_ID |
21 static void show_pat_in_path(char_u *, int, | 21 static void show_pat_in_path(char_u *, int, |
22 int, int, FILE *, linenr_T *, long); | 22 int, int, FILE *, linenr_T *, long); |
23 #endif | 23 #endif |
24 static void search_stat(int dirc, pos_T *pos, int show_top_bot_msg, char_u *msgbuf, int recompute); | 24 |
25 typedef struct searchstat | |
26 { | |
27 int cur; // current position of found words | |
28 int cnt; // total count of found words | |
29 int exact_match; // TRUE if matched exactly on specified position | |
30 int incomplete; // 0: search was fully completed | |
31 // 1: recomputing was timed out | |
32 // 2: max count exceeded | |
33 int last_maxcount; // the max count of the last search | |
34 } searchstat_T; | |
35 | |
36 static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, int show_top_bot_msg, char_u *msgbuf, int recompute, int maxcount, long timeout); | |
37 static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, int recompute, int maxcount, long timeout); | |
38 | |
39 #define SEARCH_STAT_DEF_TIMEOUT 20L | |
40 #define SEARCH_STAT_DEF_MAX_COUNT 99 | |
41 #define SEARCH_STAT_BUF_LEN 12 | |
25 | 42 |
26 /* | 43 /* |
27 * This file contains various searching-related routines. These fall into | 44 * This file contains various searching-related routines. These fall into |
28 * three groups: | 45 * three groups: |
29 * 1. string searches (for /, ?, n, and N) | 46 * 1. string searches (for /, ?, n, and N) |
1201 char_u *strcopy = NULL; | 1218 char_u *strcopy = NULL; |
1202 char_u *ps; | 1219 char_u *ps; |
1203 char_u *msgbuf = NULL; | 1220 char_u *msgbuf = NULL; |
1204 size_t len; | 1221 size_t len; |
1205 int has_offset = FALSE; | 1222 int has_offset = FALSE; |
1206 #define SEARCH_STAT_BUF_LEN 12 | |
1207 | 1223 |
1208 /* | 1224 /* |
1209 * A line offset is not remembered, this is vi compatible. | 1225 * A line offset is not remembered, this is vi compatible. |
1210 */ | 1226 */ |
1211 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) | 1227 if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) |
1589 && messaging() | 1605 && messaging() |
1590 && !msg_silent | 1606 && !msg_silent |
1591 && c != FAIL | 1607 && c != FAIL |
1592 && !shortmess(SHM_SEARCHCOUNT) | 1608 && !shortmess(SHM_SEARCHCOUNT) |
1593 && msgbuf != NULL) | 1609 && msgbuf != NULL) |
1594 search_stat(dirc, &pos, show_top_bot_msg, msgbuf, | 1610 cmdline_search_stat(dirc, &pos, &curwin->w_cursor, |
1595 (count != 1 || has_offset | 1611 show_top_bot_msg, msgbuf, |
1612 (count != 1 || has_offset | |
1596 #ifdef FEAT_FOLDING | 1613 #ifdef FEAT_FOLDING |
1597 || (!(fdo_flags & FDO_SEARCH) && | 1614 || (!(fdo_flags & FDO_SEARCH) |
1598 hasFolding(curwin->w_cursor.lnum, NULL, NULL)) | 1615 && hasFolding(curwin->w_cursor.lnum, |
1599 #endif | 1616 NULL, NULL)) |
1600 )); | 1617 #endif |
1618 ), | |
1619 SEARCH_STAT_DEF_MAX_COUNT, | |
1620 SEARCH_STAT_DEF_TIMEOUT); | |
1601 | 1621 |
1602 /* | 1622 /* |
1603 * The search command can be followed by a ';' to do another search. | 1623 * The search command can be followed by a ';' to do another search. |
1604 * For example: "/pat/;/foo/+3;?bar" | 1624 * For example: "/pat/;/foo/+3;?bar" |
1605 * This is like doing another search command, except: | 1625 * This is like doing another search command, except: |
3059 } | 3079 } |
3060 #endif | 3080 #endif |
3061 | 3081 |
3062 /* | 3082 /* |
3063 * Add the search count "[3/19]" to "msgbuf". | 3083 * Add the search count "[3/19]" to "msgbuf". |
3064 * When "recompute" is TRUE always recompute the numbers. | 3084 * See update_search_stat() for other arguments. |
3065 */ | 3085 */ |
3066 static void | 3086 static void |
3067 search_stat( | 3087 cmdline_search_stat( |
3068 int dirc, | 3088 int dirc, |
3069 pos_T *pos, | 3089 pos_T *pos, |
3070 int show_top_bot_msg, | 3090 pos_T *cursor_pos, |
3071 char_u *msgbuf, | 3091 int show_top_bot_msg, |
3072 int recompute) | 3092 char_u *msgbuf, |
3093 int recompute, | |
3094 int maxcount, | |
3095 long timeout) | |
3096 { | |
3097 searchstat_T stat; | |
3098 | |
3099 update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, | |
3100 timeout); | |
3101 if (stat.cur > 0) | |
3102 { | |
3103 char t[SEARCH_STAT_BUF_LEN]; | |
3104 size_t len; | |
3105 | |
3106 #ifdef FEAT_RIGHTLEFT | |
3107 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') | |
3108 { | |
3109 if (stat.incomplete == 1) | |
3110 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); | |
3111 else if (stat.cnt > maxcount && stat.cur > maxcount) | |
3112 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", | |
3113 maxcount, maxcount); | |
3114 else if (stat.cnt > maxcount) | |
3115 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", | |
3116 maxcount, stat.cur); | |
3117 else | |
3118 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", | |
3119 stat.cnt, stat.cur); | |
3120 } | |
3121 else | |
3122 #endif | |
3123 { | |
3124 if (stat.incomplete == 1) | |
3125 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); | |
3126 else if (stat.cnt > maxcount && stat.cur > maxcount) | |
3127 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", | |
3128 maxcount, maxcount); | |
3129 else if (stat.cnt > maxcount) | |
3130 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", | |
3131 stat.cur, maxcount); | |
3132 else | |
3133 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", | |
3134 stat.cur, stat.cnt); | |
3135 } | |
3136 | |
3137 len = STRLEN(t); | |
3138 if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) | |
3139 { | |
3140 mch_memmove(t + 2, t, len); | |
3141 t[0] = 'W'; | |
3142 t[1] = ' '; | |
3143 len += 2; | |
3144 } | |
3145 | |
3146 mch_memmove(msgbuf + STRLEN(msgbuf) - len, t, len); | |
3147 if (dirc == '?' && stat.cur == maxcount + 1) | |
3148 stat.cur = -1; | |
3149 | |
3150 // keep the message even after redraw, but don't put in history | |
3151 msg_hist_off = TRUE; | |
3152 give_warning(msgbuf, FALSE); | |
3153 msg_hist_off = FALSE; | |
3154 } | |
3155 } | |
3156 | |
3157 /* | |
3158 * Add the search count information to "stat". | |
3159 * "stat" must not be NULL. | |
3160 * When "recompute" is TRUE always recompute the numbers. | |
3161 * dirc == 0: don't find the next/previous match (only set the result to "stat") | |
3162 * dirc == '/': find the next match | |
3163 * dirc == '?': find the previous match | |
3164 */ | |
3165 static void | |
3166 update_search_stat( | |
3167 int dirc, | |
3168 pos_T *pos, | |
3169 pos_T *cursor_pos, | |
3170 searchstat_T *stat, | |
3171 int recompute, | |
3172 int maxcount, | |
3173 long timeout) | |
3073 { | 3174 { |
3074 int save_ws = p_ws; | 3175 int save_ws = p_ws; |
3075 int wraparound = FALSE; | 3176 int wraparound = FALSE; |
3076 pos_T p = (*pos); | 3177 pos_T p = (*pos); |
3077 static pos_T lastpos = {0, 0, 0}; | 3178 static pos_T lastpos = {0, 0, 0}; |
3078 static int cur = 0; | 3179 static int cur = 0; |
3079 static int cnt = 0; | 3180 static int cnt = 0; |
3181 static int exact_match = FALSE; | |
3182 static int incomplete = 0; | |
3183 static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; | |
3080 static int chgtick = 0; | 3184 static int chgtick = 0; |
3081 static char_u *lastpat = NULL; | 3185 static char_u *lastpat = NULL; |
3082 static buf_T *lbuf = NULL; | 3186 static buf_T *lbuf = NULL; |
3083 #ifdef FEAT_RELTIME | 3187 #ifdef FEAT_RELTIME |
3084 proftime_T start; | 3188 proftime_T start; |
3085 #endif | 3189 #endif |
3086 #define OUT_OF_TIME 999 | 3190 |
3191 vim_memset(stat, 0, sizeof(searchstat_T)); | |
3192 | |
3193 if (dirc == 0 && !recompute && !EMPTY_POS(lastpos)) | |
3194 { | |
3195 stat->cur = cur; | |
3196 stat->cnt = cnt; | |
3197 stat->exact_match = exact_match; | |
3198 stat->incomplete = incomplete; | |
3199 stat->last_maxcount = last_maxcount; | |
3200 return; | |
3201 } | |
3202 last_maxcount = maxcount; | |
3087 | 3203 |
3088 wraparound = ((dirc == '?' && LT_POS(lastpos, p)) | 3204 wraparound = ((dirc == '?' && LT_POS(lastpos, p)) |
3089 || (dirc == '/' && LT_POS(p, lastpos))); | 3205 || (dirc == '/' && LT_POS(p, lastpos))); |
3090 | 3206 |
3091 // If anything relevant changed the count has to be recomputed. | 3207 // If anything relevant changed the count has to be recomputed. |
3092 // MB_STRNICMP ignores case, but we should not ignore case. | 3208 // MB_STRNICMP ignores case, but we should not ignore case. |
3093 // Unfortunately, there is no MB_STRNICMP function. | 3209 // Unfortunately, there is no MB_STRNICMP function. |
3210 // XXX: above comment should be "no MB_STRCMP function" ? | |
3094 if (!(chgtick == CHANGEDTICK(curbuf) | 3211 if (!(chgtick == CHANGEDTICK(curbuf) |
3095 && MB_STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 | 3212 && MB_STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 |
3096 && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) | 3213 && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) |
3097 && EQUAL_POS(lastpos, curwin->w_cursor) | 3214 && EQUAL_POS(lastpos, *cursor_pos) |
3098 && lbuf == curbuf) || wraparound || cur < 0 || cur > 99 || recompute) | 3215 && lbuf == curbuf) || wraparound || cur < 0 |
3216 || (maxcount > 0 && cur > maxcount) || recompute) | |
3099 { | 3217 { |
3100 cur = 0; | 3218 cur = 0; |
3101 cnt = 0; | 3219 cnt = 0; |
3220 exact_match = FALSE; | |
3221 incomplete = 0; | |
3102 CLEAR_POS(&lastpos); | 3222 CLEAR_POS(&lastpos); |
3103 lbuf = curbuf; | 3223 lbuf = curbuf; |
3104 } | 3224 } |
3105 | 3225 |
3106 if (EQUAL_POS(lastpos, curwin->w_cursor) && !wraparound | 3226 if (EQUAL_POS(lastpos, *cursor_pos) && !wraparound |
3107 && (dirc == '/' ? cur < cnt : cur > 0)) | 3227 && (dirc == 0 || dirc == '/' ? cur < cnt : cur > 0)) |
3108 cur += dirc == '/' ? 1 : -1; | 3228 cur += dirc == 0 ? 0 : dirc == '/' ? 1 : -1; |
3109 else | 3229 else |
3110 { | 3230 { |
3231 int done_search = FALSE; | |
3232 pos_T endpos = {0, 0, 0}; | |
3233 | |
3111 p_ws = FALSE; | 3234 p_ws = FALSE; |
3112 #ifdef FEAT_RELTIME | 3235 #ifdef FEAT_RELTIME |
3113 profile_setlimit(20L, &start); | 3236 if (timeout > 0) |
3114 #endif | 3237 profile_setlimit(timeout, &start); |
3115 while (!got_int && searchit(curwin, curbuf, &lastpos, NULL, | 3238 #endif |
3239 while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos, | |
3116 FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL) | 3240 FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL) |
3117 { | 3241 { |
3242 done_search = TRUE; | |
3118 #ifdef FEAT_RELTIME | 3243 #ifdef FEAT_RELTIME |
3119 // Stop after passing the time limit. | 3244 // Stop after passing the time limit. |
3120 if (profile_passed_limit(&start)) | 3245 if (timeout > 0 && profile_passed_limit(&start)) |
3121 { | 3246 { |
3122 cnt = OUT_OF_TIME; | 3247 incomplete = 1; |
3123 cur = OUT_OF_TIME; | |
3124 break; | 3248 break; |
3125 } | 3249 } |
3126 #endif | 3250 #endif |
3127 cnt++; | 3251 cnt++; |
3128 if (LTOREQ_POS(lastpos, p)) | 3252 if (LTOREQ_POS(lastpos, p)) |
3129 cur++; | 3253 { |
3254 cur = cnt; | |
3255 if (LTOREQ_POS(p, endpos)) | |
3256 exact_match = TRUE; | |
3257 } | |
3130 fast_breakcheck(); | 3258 fast_breakcheck(); |
3131 if (cnt > 99) | 3259 if (maxcount > 0 && cnt > maxcount) |
3260 { | |
3261 incomplete = 2; // max count exceeded | |
3132 break; | 3262 break; |
3263 } | |
3133 } | 3264 } |
3134 if (got_int) | 3265 if (got_int) |
3135 cur = -1; // abort | 3266 cur = -1; // abort |
3136 } | 3267 if (done_search) |
3137 if (cur > 0) | 3268 { |
3138 { | 3269 vim_free(lastpat); |
3139 char t[SEARCH_STAT_BUF_LEN] = ""; | 3270 lastpat = vim_strsave(spats[last_idx].pat); |
3140 size_t len; | 3271 chgtick = CHANGEDTICK(curbuf); |
3141 | 3272 lbuf = curbuf; |
3142 #ifdef FEAT_RIGHTLEFT | 3273 lastpos = p; |
3143 if (curwin->w_p_rl && *curwin->w_p_rlc == 's') | 3274 } |
3144 { | 3275 } |
3145 if (cur == OUT_OF_TIME) | 3276 stat->cur = cur; |
3146 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); | 3277 stat->cnt = cnt; |
3147 else if (cnt > 99 && cur > 99) | 3278 stat->exact_match = exact_match; |
3148 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/>99]"); | 3279 stat->incomplete = incomplete; |
3149 else if (cnt > 99) | 3280 stat->last_maxcount = last_maxcount; |
3150 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/%d]", cur); | |
3151 else | |
3152 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", cnt, cur); | |
3153 } | |
3154 else | |
3155 #endif | |
3156 { | |
3157 if (cur == OUT_OF_TIME) | |
3158 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); | |
3159 else if (cnt > 99 && cur > 99) | |
3160 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>99/>99]"); | |
3161 else if (cnt > 99) | |
3162 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>99]", cur); | |
3163 else | |
3164 vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", cur, cnt); | |
3165 } | |
3166 | |
3167 len = STRLEN(t); | |
3168 if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) | |
3169 { | |
3170 mch_memmove(t + 2, t, len); | |
3171 t[0] = 'W'; | |
3172 t[1] = ' '; | |
3173 len += 2; | |
3174 } | |
3175 | |
3176 mch_memmove(msgbuf + STRLEN(msgbuf) - len, t, len); | |
3177 if (dirc == '?' && cur == 100) | |
3178 cur = -1; | |
3179 | |
3180 vim_free(lastpat); | |
3181 lastpat = vim_strsave(spats[last_idx].pat); | |
3182 chgtick = CHANGEDTICK(curbuf); | |
3183 lbuf = curbuf; | |
3184 lastpos = p; | |
3185 | |
3186 // keep the message even after redraw, but don't put in history | |
3187 msg_hist_off = TRUE; | |
3188 give_warning(msgbuf, FALSE); | |
3189 msg_hist_off = FALSE; | |
3190 } | |
3191 p_ws = save_ws; | 3281 p_ws = save_ws; |
3192 } | 3282 } |
3193 | 3283 |
3194 #if defined(FEAT_FIND_ID) || defined(PROTO) | 3284 #if defined(FEAT_FIND_ID) || defined(PROTO) |
3195 /* | 3285 /* |
3957 get_spat_last_idx(void) | 4047 get_spat_last_idx(void) |
3958 { | 4048 { |
3959 return last_idx; | 4049 return last_idx; |
3960 } | 4050 } |
3961 #endif | 4051 #endif |
4052 | |
4053 #ifdef FEAT_EVAL | |
4054 /* | |
4055 * "searchcount()" function | |
4056 */ | |
4057 void | |
4058 f_searchcount(typval_T *argvars, typval_T *rettv) | |
4059 { | |
4060 pos_T pos = curwin->w_cursor; | |
4061 char_u *pattern = NULL; | |
4062 int maxcount = SEARCH_STAT_DEF_MAX_COUNT; | |
4063 long timeout = SEARCH_STAT_DEF_TIMEOUT; | |
4064 int recompute = TRUE; | |
4065 searchstat_T stat; | |
4066 | |
4067 if (rettv_dict_alloc(rettv) == FAIL) | |
4068 return; | |
4069 | |
4070 if (shortmess(SHM_SEARCHCOUNT)) // 'shortmess' contains 'S' flag | |
4071 recompute = TRUE; | |
4072 | |
4073 if (argvars[0].v_type != VAR_UNKNOWN) | |
4074 { | |
4075 dict_T *dict = argvars[0].vval.v_dict; | |
4076 dictitem_T *di; | |
4077 listitem_T *li; | |
4078 int error = FALSE; | |
4079 | |
4080 di = dict_find(dict, (char_u *)"timeout", -1); | |
4081 if (di != NULL) | |
4082 { | |
4083 timeout = (long)tv_get_number_chk(&di->di_tv, &error); | |
4084 if (error) | |
4085 return; | |
4086 } | |
4087 di = dict_find(dict, (char_u *)"maxcount", -1); | |
4088 if (di != NULL) | |
4089 { | |
4090 maxcount = (int)tv_get_number_chk(&di->di_tv, &error); | |
4091 if (error) | |
4092 return; | |
4093 } | |
4094 di = dict_find(dict, (char_u *)"recompute", -1); | |
4095 if (di != NULL) | |
4096 { | |
4097 recompute = tv_get_number_chk(&di->di_tv, &error); | |
4098 if (error) | |
4099 return; | |
4100 } | |
4101 di = dict_find(dict, (char_u *)"pattern", -1); | |
4102 if (di != NULL) | |
4103 { | |
4104 pattern = tv_get_string_chk(&di->di_tv); | |
4105 if (pattern == NULL) | |
4106 return; | |
4107 } | |
4108 di = dict_find(dict, (char_u *)"pos", -1); | |
4109 if (di != NULL) | |
4110 { | |
4111 if (di->di_tv.v_type != VAR_LIST) | |
4112 { | |
4113 semsg(_(e_invarg2), "pos"); | |
4114 return; | |
4115 } | |
4116 if (list_len(di->di_tv.vval.v_list) != 3) | |
4117 { | |
4118 semsg(_(e_invarg2), "List format should be [lnum, col, off]"); | |
4119 return; | |
4120 } | |
4121 li = list_find(di->di_tv.vval.v_list, 0L); | |
4122 if (li != NULL) | |
4123 { | |
4124 pos.lnum = tv_get_number_chk(&li->li_tv, &error); | |
4125 if (error) | |
4126 return; | |
4127 } | |
4128 li = list_find(di->di_tv.vval.v_list, 1L); | |
4129 if (li != NULL) | |
4130 { | |
4131 pos.col = tv_get_number_chk(&li->li_tv, &error) - 1; | |
4132 if (error) | |
4133 return; | |
4134 } | |
4135 li = list_find(di->di_tv.vval.v_list, 2L); | |
4136 if (li != NULL) | |
4137 { | |
4138 pos.coladd = tv_get_number_chk(&li->li_tv, &error); | |
4139 if (error) | |
4140 return; | |
4141 } | |
4142 } | |
4143 } | |
4144 | |
4145 save_last_search_pattern(); | |
4146 if (pattern != NULL) | |
4147 { | |
4148 if (*pattern == NUL) | |
4149 goto the_end; | |
4150 spats[last_idx].pat = vim_strsave(pattern); | |
4151 } | |
4152 if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) | |
4153 goto the_end; // the previous pattern was never defined | |
4154 | |
4155 update_search_stat(0, &pos, &pos, &stat, recompute, maxcount, timeout); | |
4156 | |
4157 dict_add_number(rettv->vval.v_dict, "current", stat.cur); | |
4158 dict_add_number(rettv->vval.v_dict, "total", stat.cnt); | |
4159 dict_add_number(rettv->vval.v_dict, "exact_match", stat.exact_match); | |
4160 dict_add_number(rettv->vval.v_dict, "incomplete", stat.incomplete); | |
4161 dict_add_number(rettv->vval.v_dict, "maxcount", stat.last_maxcount); | |
4162 | |
4163 the_end: | |
4164 restore_last_search_pattern(); | |
4165 } | |
4166 #endif |