Monday, July 16, 2012

Re: yank matched portion of a selection of lines

diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -666,6 +666,8 @@
[n] Report the number of matches, do not actually substitute. The [c]
flag is ignored. The matches are reported as if 'report' is zero.
Useful to |count-items|.
+ If |sub-replace-expression| is used, the expression will be evaluated
+ in the |sandbox|.

[p] Print the line containing the last substitute.

diff --git a/src/ex_cmds.c b/src/ex_cmds.c
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -4264,6 +4264,7 @@
int endcolumn = FALSE; /* cursor in last column when done */
pos_T old_cursor = curwin->w_cursor;
int start_nsubs;
+ int save_ma;

cmd = eap->arg;
if (!global_busy)
@@ -4668,7 +4669,8 @@
}
sub_nsubs++;
did_sub = TRUE;
- goto skip;
+ if (!(sub[0] == '\\' && sub[1] == '='))
+ goto skip;
}

if (do_ask)
@@ -4840,10 +4842,24 @@
/*
* 3. substitute the string.
*/
+ if (do_count)
+ {
+ /* prevent accidently changing the buffer
+ * by a function */
+ save_ma = curbuf->b_p_ma;
+ curbuf->b_p_ma = FALSE;
+ sandbox++;
+ }
/* get length of substitution part */
sublen = vim_regsub_multi(&regmatch,
sub_firstlnum - regmatch.startpos[0].lnum,
sub, sub_firstline, FALSE, p_magic, TRUE);
+ if (do_count)
+ {
+ curbuf->b_p_ma = save_ma;
+ sandbox--;
+ goto skip;
+ }

/* When the match included the "$" of the last line it may
* go beyond the last line of the buffer. */
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -656,6 +656,22 @@
Special case: With a count of 2 the quotes are
included, but no extra white space as with a"/a'/a`.

+a/ *v_aslash* *aslash*
+a? *v_a/* *a/* *a?* *v_a?*
+ "a search match". Selects text, that matches the most
+ recent performed search.
+ If the cursor is not on a match, it will be moved to
+ the next match in forward direction and select that
+ match.
+ / moves always forward, ? moves backwards.
+ Any trailing white space is included, unless there is
+ none, then leading white space is included.
+
+i/ *v_islash* *islash*
+i? *v_i/* *i?* *v_i?* *i/*
+ Like a/ and a? but exclude trailing or leading white
+ space.
+
When used after an operator:
For non-block objects:
For the "a" commands: The operator applies to the object and the white
diff --git a/src/normal.c b/src/normal.c
--- a/src/normal.c
+++ b/src/normal.c
@@ -9211,6 +9211,11 @@
flag = current_quote(cap->oap, cap->count1, include,
cap->nchar);
break;
+ case '/': /* "a/" = a search match */
+ case '?': /* "a?" = a search match */
+ flag = current_search(cap->oap, cap->count1, include,
+ cap->nchar == '/');
+ break;
#if 0 /* TODO */
case 'S': /* "aS" = a section */
case 'f': /* "af" = a filename */
diff --git a/src/proto/search.pro b/src/proto/search.pro
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -27,6 +27,7 @@
int end_word __ARGS((long count, int bigword, int stop, int empty));
int bckend_word __ARGS((long count, int bigword, int eol));
int current_word __ARGS((oparg_T *oap, long count, int include, int bigword));
+int current_search __ARGS((oparg_T *oap, long count, int include, int forward));
int current_sent __ARGS((oparg_T *oap, long count, int include));
int current_block __ARGS((oparg_T *oap, long count, int include, int what, int other));
int current_tagblock __ARGS((oparg_T *oap, long count_arg, int include));
diff --git a/src/search.c b/src/search.c
--- a/src/search.c
+++ b/src/search.c
@@ -3398,6 +3398,374 @@
}

/*
+ * Find next search match under cursor, cursor at end.
+ * Used while an operator is pending, and in Visual mode.
+ */
+ int
+current_search(oap, count, include, forward)
+ oparg_T *oap;
+ long count;
+ int include; /* TRUE: include white space */
+ int forward; /* move forward or backwards */
+{
+ pos_T start_pos; /* position before the pattern */
+ pos_T orig_pos; /* position of the cursor at beginning */
+ pos_T pos; /* position after the pattern */
+ int i;
+ int result; /* result of various function calls */
+ char_u old_p_ws = p_ws;
+ int visual_active = FALSE;
+ pos_T t;
+ int c;
+ int t_ws = FALSE; /* extend by trailing whitespace */
+ int flags = 0;
+#ifdef FEAT_VISUAL
+ pos_T save_VIsual;
+#endif
+
+ /* wrapping should not occur */
+ p_ws = FALSE;
+
+#ifdef FEAT_VISUAL
+ /* Correct cursor when 'selection' is exclusive */
+ if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))
+ dec_cursor();
+
+ if (VIsual_active)
+ {
+ orig_pos = curwin->w_cursor;
+ save_VIsual = VIsual;
+ visual_active = TRUE;
+
+ /* just started visual selection */
+ if (equalpos(VIsual, curwin->w_cursor))
+ visual_active = FALSE;
+
+ pos = curwin->w_cursor;
+ start_pos = VIsual;
+ /* Switch search direction */
+ if (lt(curwin->w_cursor, VIsual))
+ {
+ /* skip directions */
+ forward = FALSE;
+ start_pos = curwin->w_cursor;
+ curwin->w_cursor = VIsual;
+ pos = VIsual = orig_pos;
+ }
+ /* make sure, searching further will extend the match */
+ if (visual_active)
+ {
+ if (forward)
+ incl(&pos);
+ else
+ decl(&pos);
+ }
+ }
+ else
+#endif
+ orig_pos = pos = start_pos = curwin->w_cursor;
+
+ /* move one char back, so that it will match, even so the cursor
+ * is on the last char of the match */
+ if (!visual_active)
+ {
+ if (forward)
+ dec(&pos);
+ else
+ inc(&pos);
+ }
+
+ if (include)
+ flags = (forward ? SEARCH_END : 0); /* skip over the match */
+
+ /* The trick is to first search backwards and then search forward again,
+ * so that a match at the current cursor position will be correctly
+ * captured.
+ */
+ for (i = 0; i < 2; i++)
+ {
+ if (forward)
+ result = searchit(curwin, curbuf, &pos, (!i ? BACKWARD : FORWARD),
+ spats[last_idx].pat, 0L, SEARCH_KEEP | SEARCH_START |
+ (!i ? SEARCH_END : 0), RE_SEARCH, 0, NULL);
+ else
+ result = searchit(curwin, curbuf, &pos, (!i ? FORWARD : BACKWARD),
+ spats[last_idx].pat, 0L, SEARCH_KEEP | SEARCH_START |
+ (i ? SEARCH_END : 0), RE_SEARCH, 0, NULL);
+
+ /* first search may fail, but then start searching from the
+ * beginning of the file (cursor might be on the search match)
+ * except when visual mode is active, so that extending the visual
+ * selection works */
+ if (!result && i) /* not found, abort */
+ {
+ curwin->w_cursor = orig_pos;
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ VIsual = save_VIsual;
+#endif
+ p_ws = old_p_ws;
+ return FAIL;
+ }
+ else if (!i && !result && !visual_active)
+ {
+ if (forward) /* try again from start of buffer */
+ {
+ clearpos(&pos);
+ }
+ else /* try again from end of buffer */
+ {
+ /* searching backwards, so set pos to last line and col */
+ pos.lnum = curwin->w_buffer->b_ml.ml_line_count;
+ pos.col = STRLEN(ml_get(
+ curwin->w_buffer->b_ml.ml_line_count));
+ }
+ }
+
+ if (i) /* successful search */
+ {
+ t = pos;
+ /* check whether we should move to end of match or stop in front of it */
+ if (forward)
+ {
+ decl(&pos);
+ if ((!include && ltoreq(pos, curwin->w_cursor))
+#ifdef FEAT_VISUAL
+ || !VIsual_active
+#endif
+ )
+ flags = SEARCH_END;
+ }
+ else
+ {
+ incl(&pos);
+ if ((!include && lt(pos, start_pos))
+#ifdef FEAT_VISUAL
+ && VIsual_active
+#endif
+ )
+ flags = SEARCH_END;
+ }
+ if (!visual_active)
+ pos = t;
+ }
+
+ }
+
+ count--;
+ start_pos = pos;
+
+ /* move to first match */
+ result = searchit(curwin, curbuf, &pos,
+ (forward ? FORWARD : BACKWARD), spats[last_idx].pat, 1L,
+ flags | SEARCH_KEEP | SEARCH_START, RE_SEARCH, 0, NULL);
+
+ if (!flags && forward) /* stop in front of the match */
+ decl(&pos);
+ else if (flags && !forward)
+ incl(&pos);
+
+
+ /*
+ * When count is still > 0, extend with more objects.
+ */
+ if (count > 0)
+ {
+ if (!include)
+ {
+ /* for the i-objects, check where to stop */
+ if (count%2)
+ {
+ if (!flags)
+ {
+ flags = SEARCH_END;
+ count = count/2 + 1;
+ }
+ else
+ {
+ flags = 0;
+ count = count/2 + 1;
+ }
+ }
+ else
+ {
+ if (!flags)
+ {
+ flags = 0;
+ count = count/2 + forward;
+ }
+ else
+ {
+ flags = SEARCH_END;
+ count = count/2 + !forward;
+ }
+ }
+ }
+
+ result = searchit(curwin, curbuf, &pos, (forward ? FORWARD : BACKWARD),
+ spats[last_idx].pat, (long) count,
+ SEARCH_KEEP | flags, RE_SEARCH, 0, NULL);
+
+ if (forward && !flags) /* stop in front of the match */
+ decl(&pos);
+ else if (!forward && flags)
+ incl(&pos);
+
+ if (!result)
+ {
+ curwin->w_cursor = orig_pos;
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ VIsual = save_VIsual;
+#endif
+ p_ws = old_p_ws;
+ return FAIL;
+ }
+ }
+
+ if (!visual_active && include)
+ {
+ /* only whitespace between orig_pos and start_pos */
+ /* put cursor in front of match */
+ /* deactivated for now */
+ t = orig_pos;
+ c = gchar_pos(&t);
+ while ((vim_iswhite(c) || c == NUL) && (lt(t,start_pos)))
+ {
+ incl(&t);
+ c = gchar_pos(&t);
+ if (equalpos(t,start_pos))
+ {
+ decl(&t);
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ include = FALSE;
+#endif
+ }
+ }
+ /* include trailing white space? */
+ if (forward && include)
+ {
+ t = pos;
+ incl(&t);
+ c = gchar_pos(&t);
+
+ if (vim_iswhite(c))
+ {
+ incl(&t);
+ while (vim_iswhite(gchar_pos(&t)))
+ {
+ if (incl(&t) == -1)
+ break;
+ }
+ t_ws = TRUE;
+ decl(&t);
+ }
+ else /* try to include leading whitespace */
+ {
+ t = start_pos;
+ decl(&t);
+ c = gchar_pos(&t);
+ if (vim_iswhite(c))
+ {
+ decl(&t);
+ while (vim_iswhite(gchar_pos(&t)))
+ {
+ if (decl(&t) == -1)
+ break;
+ }
+ incl(&t);
+ start_pos = t;
+ }
+ }
+ }
+ else if (!forward && include)
+ {
+ t = orig_pos;
+ decl(&t);
+ c = gchar_pos(&t);
+ if (vim_iswhite(c))
+ {
+
+ decl(&t);
+ while (vim_iswhite(gchar_pos(&t)))
+ {
+ if (decl(&t) == -1)
+ break;
+ }
+ t_ws = TRUE;
+ incl(&t);
+ }
+ else /* try to include leading whitespace */
+ {
+ t = start_pos;
+ decl(&t);
+ c = gchar_pos(&t);
+ if (vim_iswhite(c))
+ {
+ decl(&t);
+ while (vim_iswhite(gchar_pos(&t)))
+ {
+ if (decl(&t) == -1)
+ break;
+ }
+ incl(&t);
+ start_pos = t;
+ }
+ }
+ }
+ if (t_ws)
+ pos = t;
+ }
+
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ {
+ if (forward)
+ {
+ if (lt(start_pos, VIsual))
+ VIsual = start_pos;
+ }
+ else
+ {
+ if (lt(start_pos, curwin->w_cursor))
+ VIsual = curwin->w_cursor;
+ else
+ VIsual = start_pos;
+ }
+ }
+ else
+#endif
+ {
+ oap->start = start_pos;
+ oap->motion_type = MCHAR;
+ }
+
+
+ p_ws = old_p_ws;
+ curwin->w_cursor = pos;
+
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ {
+ redraw_curbuf_later(INVERTED); /* update the inversion */
+ if (*p_sel == 'e' && ltoreq(VIsual, curwin->w_cursor))
+ inc_cursor();
+ }
+ else
+#endif
+ oap->inclusive = TRUE;
+
+#ifdef FEAT_FOLDING
+ if (fdo_flags & FDO_SEARCH && KeyTyped)
+ foldOpenCursor();
+#endif
+ check_cursor();
+
+ return OK;
+}
+
+/*
* Find sentence(s) under the cursor, cursor at end.
* When Visual active, extend it by one or more sentences.
*/
diff --git a/src/testdir/test53.in b/src/testdir/test53.in
--- a/src/testdir/test53.in
+++ b/src/testdir/test53.in
@@ -28,6 +28,11 @@
:put =matchstr(\"abcd\", \".\", 0, -1) " a
:put =match(\"abcd\", \".\", 0, 5) " -1
:put =match(\"abcd\", \".\", 0, -1) " 0
+/^foobar
+ci/searchmatch/one\_s*two
+:1 di/
+/[a]bcdx
+:1 d3i/
:/^start:/,/^end:/wq! test.out
ENDTEST

@@ -45,4 +50,9 @@
-<b>asdf<i>Xasdf</i>asdf</b>-
-<b>asdX<i>as<b />df</i>asdf</b>-
</begin>
+SEARCH:
+foobar
+one
+two
+abcdx abcdx abcdx
end:
diff --git a/src/testdir/test53.ok b/src/testdir/test53.ok
--- a/src/testdir/test53.ok
+++ b/src/testdir/test53.ok
@@ -18,4 +18,7 @@
a
-1
0
+SEARCH:
+searchmatch
+ abcdx
end:
Bram,

On Sa, 07 Jul 2012, Tim Chase wrote:

> On 07/07/12 06:35, Christian Brabandt wrote:
> >> I'd favor a solution with
> >> :%s/{pattern}/\=CollectMatch(submatch(0))/gn
> >>
> >> but '\=' and the "n" flag don't work together.
> >> What about a todo item? Add another flag?
>
> I must say I'm surprised that the \= and "n" flags don't play well
> together. Though it would toggle 'modified', you could have
> CollectMatch just return its argument for a noop replacement...

Attached s_eval_expr_n-flag.diff allows to execute functions inside the
substitution part of an :s-command. The function is executed inside the
sandbox, which should be good enough. Here is why I'd like such a
function:

I have a plugin Colorizer.vim, that highlights color codes and names by
their values. I found, that the fastest way to do this, is to
issue an :s/.../\=Highlight(submatch)/ Unfortunately, this creates a new
undo-branch, although my plugin does not change the buffer.

The alternative is to manually loop over each line, match against a
pattern and for each pattern found, call the function. This works, but is
way to slow. For my use-case, using the sandbox is good enough.

> > Would a textobject like i/ work?
>
> Two parts of me conflict on this: one thinks "wow, that's a cool
> idea", the other thinks "what twisted sicko thought up that one?!"
> :-) I suspect there are a bunch of odd edge-cases such as
> search-offset modifiers, zero-width assertions of what can
> precede/follow a given pattern, use of the \zs and \ze modifiers, etc.

Attached patch search_textobj.diff implements a match text-object. This
was just a fun way to, to see if this is possible. I don't know, if this
is really useful, so I like the idea. I tried to mimic the behaviour of
existing textobjects as good as possible and so far, it seems to do what
it should.

The inner object jump to the next search-object and in visual-mode on
further jumps stop in front of the next match. The 'a' object jump from
match to match, including trailing whitespace if possible (or leading
whitespace, if no trailing whitespace is possible).

Use i/ for inner-forward search i? for inner-backward object, a/ and a?
for the "a" forward/backwards motions.

BTW: This here looks suspicious:

#define RE_SEARCH 0 /* save/use pat in/from search_pattern */
#define RE_SUBST 1 /* save/use pat in/from subst_pattern */
#define RE_BOTH 2 /* save pat in both patterns */
#define RE_LAST 2 /* use last used pattern if "pat" is NULL */


regards,
Christian
--

--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

No comments:

Post a Comment