aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_cmds.c
diff options
context:
space:
mode:
authorJames McCoy <jamessan@jamessan.com>2016-08-19 21:42:42 -0400
committerJames McCoy <jamessan@jamessan.com>2016-08-20 23:55:26 -0400
commitc317fc20b01e77b22b4b1f2a3b0eceb7ed4f8eba (patch)
treeea2900718c45a8b56450ea3f5ba0e9396eb46ff6 /src/nvim/ex_cmds.c
parentd9ab9160b8b306ca36cb52b57fc95a1c922a42d5 (diff)
downloadrneovim-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.c330
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.