diff options
author | James McCoy <jamessan@jamessan.com> | 2016-08-19 21:42:42 -0400 |
---|---|---|
committer | James McCoy <jamessan@jamessan.com> | 2016-08-20 23:55:26 -0400 |
commit | c317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba (patch) | |
tree | ea2900718c45a8b56450ea3f5ba0e9396eb46ff6 /src/nvim/ex_cmds.c | |
parent | d9ab9160b8b306ca36cb52b57fc95a1c922a42d5 (diff) | |
download | rneovim-c317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba.tar.gz rneovim-c317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba.tar.bz2 rneovim-c317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba.zip |
ex_cmds: Factor pieces of functionality out of do_sub
* sub_joining_lines: Optimization for :%s/\n//
* sub_grow_buf: Allocation of buffer to contain replacement text
* sub_parse_flags: Parse {flags} from :s command into subflags_T
Although this doesn't reduce do_sub's size enough to satisfy lint, it
covers the more straightforward pieces.
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r-- | src/nvim/ex_cmds.c | 330 |
1 files changed, 186 insertions, 144 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2f522ac6d0..b7691997d7 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -64,6 +64,24 @@ */ typedef struct sign sign_T; +/// Case matching style to use for :substitute +typedef enum { + kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options + kSubIgnoreCase, ///< Ignore case of the search + kSubMatchCase, ///< Match case of the search +} SubIgnoreType; + +/// Flags kept between calls to :substitute. +typedef struct { + bool do_all; ///< do multiple substitutions per line + bool do_ask; ///< ask for confirmation + bool do_count; ///< count only + bool do_error; ///< if false, ignore errors + bool do_print; ///< print last line with subs + bool do_list; ///< list last line with subs + bool do_number; ///< list last line with line nr + SubIgnoreType do_ic; ///< ignore case flag +} subflags_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" @@ -2878,25 +2896,6 @@ static SubReplacementString old_sub = {NULL, 0, NULL}; static int global_need_beginline; // call beginline() after ":g" -/// Case matching style to use for :substitute -typedef enum { - kSubHonorOptions = 0, ///< Honor the user's 'ignorecase'/'smartcase' options - kSubIgnoreCase, ///< Ignore case of the search - kSubMatchCase, ///< Match case of the search -} SubIgnoreType; - -/// Flags kept between calls to :substitute. -typedef struct { - bool do_all; ///< do multiple substitutions per line - bool do_ask; ///< ask for confirmation - bool do_count; ///< count only - bool do_error; ///< if false, ignore errors - bool do_print; ///< print last line with subs - bool do_list; ///< list last line with subs - bool do_number; ///< list last line with line nr - SubIgnoreType do_ic; ///< ignore case flag -} subflags_T; - /// Get old substitute replacement string /// /// @param[out] ret_sub Location where old string will be saved. @@ -2920,6 +2919,159 @@ void sub_set_replacement(SubReplacementString sub) old_sub = sub; } +/// Recognize ":%s/\n//" and turn it into a join command, which is much +/// more efficient. +/// +/// @param[in] eap Ex arguments +/// @param[in] pat Search pattern +/// @param[in] sub Replacement string +/// @param[in] cmd Command from :s_flags +/// +/// @returns true if :substitute can be replaced with a join command +static bool sub_joining_lines(exarg_T *eap, char_u *pat, + char_u *sub, char_u *cmd) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) +{ + // TODO(vim): find a generic solution to make line-joining operations more + // efficient, avoid allocating a string that grows in size. + if (pat != NULL + && strcmp((const char *)pat, "\\n") == 0 + && *sub == NUL + && (*cmd == NUL || (cmd[1] == NUL + && (*cmd == 'g' + || *cmd == 'l' + || *cmd == 'p' + || *cmd == '#')))) { + curwin->w_cursor.lnum = eap->line1; + if (*cmd == 'l') { + eap->flags = EXFLAG_LIST; + } else if (*cmd == '#') { + eap->flags = EXFLAG_NR; + } else if (*cmd == 'p') { + eap->flags = EXFLAG_PRINT; + } + + // The number of lines joined is the number of lines in the range + linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 + // plus one extra line if not at the end of file. + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); + if (joined_lines_count > 1) { + do_join(joined_lines_count, FALSE, TRUE, FALSE, true); + sub_nsubs = joined_lines_count - 1; + sub_nlines = 1; + do_sub_msg(false); + ex_may_print(eap); + } + + if (!cmdmod.keeppatterns) { + save_re_pat(RE_SUBST, pat, p_magic); + } + add_to_history(HIST_SEARCH, pat, TRUE, NUL); + + return true; + } + + return false; +} + +/// Allocate memory to store the replacement text for :substitute. +/// +/// Slightly more memory that is strictly necessary is allocated to reduce the +/// frequency of memory (re)allocation. +/// +/// @param[in,out] new_start pointer to the memory for the replacement text +/// @param[in] needed_len amount of memory needed +/// +/// @returns pointer to the end of the allocated memory +static char_u *sub_grow_buf(char_u **new_start, int needed_len) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET +{ + int new_start_len = 0; + char_u *new_end; + if (*new_start == NULL) { + // Get some space for a temporary buffer to do the + // substitution into (and some extra space to avoid + // too many calls to xmalloc()/free()). + new_start_len = needed_len + 50; + *new_start = xmalloc(new_start_len); + **new_start = NUL; + new_end = *new_start; + } else { + // Check if the temporary buffer is long enough to do the + // substitution into. If not, make it larger (with a bit + // extra to avoid too many calls to xmalloc()/free()). + size_t len = STRLEN(*new_start); + needed_len += len; + if (needed_len > new_start_len) { + new_start_len = needed_len + 50; + *new_start = xrealloc(*new_start, new_start_len); + } + new_end = *new_start + len; + } + + return new_end; +} + +/// Parse cmd string for :substitute's {flags} and update subflags accordingly +/// +/// @param[in] cmd command string +/// @param[in,out] subflags current flags defined for the :substitute command +/// @param[in,out] which_pat pattern type from which to get default search +/// +/// @returns pointer to the end of the flags, which may be the end of the string +static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, + int *which_pat) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + // Find trailing options. When '&' is used, keep old options. + if (*cmd == '&') { + cmd++; + } else { + subflags->do_all = p_gd; + subflags->do_ask = false; + subflags->do_error = true; + subflags->do_print = false; + subflags->do_count = false; + subflags->do_number = false; + subflags->do_ic = kSubHonorOptions; + } + while (*cmd) { + // Note that 'g' and 'c' are always inverted. + // 'r' is never inverted. + if (*cmd == 'g') { + subflags->do_all = !subflags->do_all; + } else if (*cmd == 'c') { + subflags->do_ask = !subflags->do_ask; + } else if (*cmd == 'n') { + subflags->do_count = true; + } else if (*cmd == 'e') { + subflags->do_error = !subflags->do_error; + } else if (*cmd == 'r') { // use last used regexp + *which_pat = RE_LAST; + } else if (*cmd == 'p') { + subflags->do_print = true; + } else if (*cmd == '#') { + subflags->do_print = true; + subflags->do_number = true; + } else if (*cmd == 'l') { + subflags->do_print = true; + subflags->do_list = true; + } else if (*cmd == 'i') { // ignore case + subflags->do_ic = kSubIgnoreCase; + } else if (*cmd == 'I') { // don't ignore case + subflags->do_ic = kSubMatchCase; + } else { + break; + } + cmd++; + } + if (subflags->do_count) { + subflags->do_ask = false; + } + + return cmd; +} + /* do_sub() * * Perform a substitution from line eap->line1 to line eap->line2 using the @@ -2954,9 +3106,8 @@ void do_sub(exarg_T *eap) linenr_T last_line= 0; // below last changed line AFTER the // change linenr_T old_line_count = curbuf->b_ml.ml_line_count; - linenr_T line2; - char_u *sub_firstline; /* allocated copy of first sub line */ - int endcolumn = FALSE; /* cursor in last column when done */ + char_u *sub_firstline; // allocated copy of first sub line + bool endcolumn = false; // cursor in last column when done pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; @@ -3042,94 +3193,11 @@ void do_sub(exarg_T *eap) endcolumn = (curwin->w_curswant == MAXCOL); } - // Recognize ":%s/\n//" and turn it into a join command, which is much - // more efficient. - // TODO: find a generic solution to make line-joining operations more - // efficient, avoid allocating a string that grows in size. - if (pat != NULL - && strcmp((const char *)pat, "\\n") == 0 - && *sub == NUL - && (*cmd == NUL || (cmd[1] == NUL - && (*cmd == 'g' - || *cmd == 'l' - || *cmd == 'p' - || *cmd == '#')))) { - curwin->w_cursor.lnum = eap->line1; - if (*cmd == 'l') { - eap->flags = EXFLAG_LIST; - } else if (*cmd == '#') { - eap->flags = EXFLAG_NR; - } else if (*cmd == 'p') { - eap->flags = EXFLAG_PRINT; - } - - // The number of lines joined is the number of lines in the range - linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 - // plus one extra line if not at the end of file. - + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); - if (joined_lines_count > 1) { - do_join(joined_lines_count, FALSE, TRUE, FALSE, true); - sub_nsubs = joined_lines_count - 1; - sub_nlines = 1; - do_sub_msg(false); - ex_may_print(eap); - } - - if (!cmdmod.keeppatterns) { - save_re_pat(RE_SUBST, pat, p_magic); - } - add_to_history(HIST_SEARCH, pat, TRUE, NUL); - + if (sub_joining_lines(eap, pat, sub, cmd)) { return; } - /* - * Find trailing options. When '&' is used, keep old options. - */ - if (*cmd == '&') { - ++cmd; - } else { - subflags.do_all = p_gd; - subflags.do_ask = false; - subflags.do_error = true; - subflags.do_print = false; - subflags.do_count = false; - subflags.do_number = false; - subflags.do_ic = kSubHonorOptions; - } - while (*cmd) { - // Note that 'g' and 'c' are always inverted. - // 'r' is never inverted. - if (*cmd == 'g') { - subflags.do_all = !subflags.do_all; - } else if (*cmd == 'c') { - subflags.do_ask = !subflags.do_ask; - } else if (*cmd == 'n') { - subflags.do_count = true; - } else if (*cmd == 'e') { - subflags.do_error = !subflags.do_error; - } else if (*cmd == 'r') { // use last used regexp - which_pat = RE_LAST; - } else if (*cmd == 'p') { - subflags.do_print = true; - } else if (*cmd == '#') { - subflags.do_print = true; - subflags.do_number = true; - } else if (*cmd == 'l') { - subflags.do_print = true; - subflags.do_list = true; - } else if (*cmd == 'i') { // ignore case - subflags.do_ic = kSubIgnoreCase; - } else if (*cmd == 'I') { // don't ignore case - subflags.do_ic = kSubMatchCase; - } else { - break; - } - cmd++; - } - if (subflags.do_count) { - subflags.do_ask = false; - } + cmd = sub_parse_flags(cmd, &subflags, &which_pat); bool save_do_all = subflags.do_all; // remember user specified 'g' flag bool save_do_ask = subflags.do_ask; // remember user specified 'c' flag @@ -3194,7 +3262,7 @@ void do_sub(exarg_T *eap) sub = regtilde(sub, p_magic); // Check for a match on each line. - line2 = eap->line2; + linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; lnum <= line2 && !(got_quit || aborting()); lnum++) { @@ -3205,15 +3273,13 @@ void do_sub(exarg_T *eap) colnr_T matchcol; colnr_T prev_matchcol = MAXCOL; char_u *new_end, *new_start = NULL; - unsigned new_start_len = 0; char_u *p1; int did_sub = FALSE; int lastone; - int len, copy_len, needed_len; - long nmatch_tl = 0; /* nr of lines matched below lnum */ - int do_again; /* do it again after joining lines */ - int skip_match = FALSE; - linenr_T sub_firstlnum; /* nr of first sub line */ + long nmatch_tl = 0; // nr of lines matched below lnum + int do_again; // do it again after joining lines + int skip_match = false; + linenr_T sub_firstlnum; // nr of first sub line /* * The new text is build up step by step, to avoid too much @@ -3253,8 +3319,7 @@ void do_sub(exarg_T *eap) * accordingly. * * The new text is built up in new_start[]. It has some extra - * room to avoid using xmalloc()/free() too often. new_start_len is - * the length of the allocated memory at new_start. + * room to avoid using xmalloc()/free() too often. * * Make a copy of the old line, so it won't be taken away when * updating the screen or handling a multi-line match. The "old_" @@ -3562,33 +3627,10 @@ void do_sub(exarg_T *eap) p1 = ml_get(sub_firstlnum + nmatch - 1); nmatch_tl += nmatch - 1; } - copy_len = regmatch.startpos[0].col - copycol; - needed_len = copy_len + ((unsigned)STRLEN(p1) - - regmatch.endpos[0].col) + sublen + 1; - if (new_start == NULL) { - /* - * Get some space for a temporary buffer to do the - * substitution into (and some extra space to avoid - * too many calls to xmalloc()/free()). - */ - new_start_len = needed_len + 50; - new_start = xmalloc(new_start_len); - *new_start = NUL; - new_end = new_start; - } else { - /* - * Check if the temporary buffer is long enough to do the - * substitution into. If not, make it larger (with a bit - * extra to avoid too many calls to xmalloc()/free()). - */ - len = (unsigned)STRLEN(new_start); - needed_len += len; - if (needed_len > (int)new_start_len) { - new_start_len = needed_len + 50; - new_start = xrealloc(new_start, new_start_len); - } - new_end = new_start + len; - } + size_t copy_len = regmatch.startpos[0].col - copycol; + new_end = sub_grow_buf(&new_start, + copy_len + (STRLEN(p1) - regmatch.endpos[0].col) + + sublen + 1); /* * copy the text up to the part that matched @@ -3855,7 +3897,7 @@ skip: // Restore the flag values, they can be used for ":&&". subflags.do_all = save_do_all; subflags.do_ask = save_do_ask; -} +} // NOLINT(readability/fn_size) /* * Give message for number of substitutions. |