On Wednesday, November 20, 2013 7:16:59 PM UTC-5, Daniel "paradigm" Thau wrote:
> On Tuesday, November 19, 2013 2:25:48 PM UTC-5, Daniel "paradigm" Thau wrote:
> > I noticed a common pattern when using Vim:
> > - I want to operate on a text object that is not under/around the cursor
> > - The cursor is not over/in a text object of this type.
> >
> > For example, with the cursor on the "x" and a desire to operate on the
> > conditional part of the if-statement:
> >
> > x = 1;
> > ...
> > if (...) {
> > ...
> > }
> >
> > In these situations I have been moving the cursor to the object then
> > selecting the object with something like "/(<cr>vi(" or "5jf(vi(".
> >
> > However, in that situation the text-object commands don't do anything. We
> > could have Vim do something useful in this context - jump to the desired
> > text object. If the given text-object command is valid - if the cursor is
> > already in a ()-block - then what Vim normally does should happen.
> > However, if Vim is about to "return FAIL" could simply search the buffer
> > for the object and, if it finds it, use that object instead.
> >
> > Moreover, block text-objects (parens, {}, [], <>) have two (or three!) ways
> > to access them. This could be used to indicate direction. "vi(" will
> > select the ()-block under/around the cursor or, if no such block exists in
> > that area, searches forward for a ()-block. "vi)" will do the same except
> > it will search in reverse. "vib" will just act as it always has. Similar
> > actions will be used on {}, [], and <>.
> >
> > Attached is a patch (both in the context and unified formats) to implement
> > this. It seems relatively simple/clean to me, but people looking over and
> > testing would be benficial in case I failed to consider something.
>
> Attached is a newer version of the same patch with reworked code to be a bit
> cleaner. It includes Christian's suggestion for the search direction.
The attachments didn't seem to stick; trying again.
--
--
You received this message from the "vim_dev" 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
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
*** a/runtime/doc/motion.txt
--- b/runtime/doc/motion.txt
***************
*** 662,667 **** i` *v_i`* *i`*
--- 662,675 ----
Special case: With a count of 2 the quotes are
included, but no extra white space as with a"/a'/a`.
+ am{char} *v_am* *am*
+ "a matched {char}". Selects the text from the
+ previous {char} until the next {char}. A count is
+ currently not used.
+
+ im{char} *v_im* *im*
+ Like am{char} but exclude the {char}.
+
When used after an operator:
For non-block objects:
For the "a" commands: The operator applies to the object and the white
*** a/src/normal.c
--- b/src/normal.c
***************
*** 9309,9314 **** nv_object(cap)
--- 9309,9319 ----
flag = current_quote(cap->oap, cap->count1, include,
cap->nchar);
break;
+ case 'm': /* "am{char}" = a matched pair of characters */
+ cap->extra_char = plain_vgetc();
+ flag = current_match(cap->oap, cap->count1, include,
+ cap->extra_char);
+ break;
#if 0 /* TODO */
case 'S': /* "aS" = a section */
case 'f': /* "af" = a filename */
*** a/src/proto/search.pro
--- b/src/proto/search.pro
***************
*** 32,37 **** int current_block __ARGS((oparg_T *oap, long count, int include, int what, int o
--- 32,38 ----
int current_tagblock __ARGS((oparg_T *oap, long count_arg, int include));
int current_par __ARGS((oparg_T *oap, long count, int include, int type));
int current_quote __ARGS((oparg_T *oap, long count, int include, int quotechar));
+ int current_match __ARGS((oparg_T *oap, long count, int include, int matchchar));
int current_search __ARGS((long count, int forward));
int linewhite __ARGS((linenr_T lnum));
void find_pattern_in_path __ARGS((char_u *ptr, int dir, int len, int whole, int skip_comments, int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum));
*** a/src/search.c
--- b/src/search.c
***************
*** 4489,4494 **** current_quote(oap, count, include, quotechar)
--- 4489,4635 ----
return OK;
}
+ /*
+ * Find matching {char} on each side of cursor/visually selected range.
+ * Returns TRUE if found, else FALSE.
+ */
+ int
+ current_match(oap, count, include, matchchar)
+ oparg_T *oap; /* used to set operator range, mode, etc */
+ long count; /* not currently used */
+ int include; /* TRUE == include quote char */
+ int matchchar; /* char to match on both sides */
+ {
+ pos_T old_pos; /* initial cursor position (to restore) */
+ pos_T start_pos; /* left bound */
+ pos_T end_pos; /* right bound */
+ int ret; /* temp holder for return values */
+ #ifdef FEAT_VISUAL
+ int vis_bef_curs; /* store visual mode direction */
+
+ /*
+ * Store visual mode direction so we can be sure it is set the same
+ * direction when we're done.
+ */
+ vis_bef_curs = ltoreq(VIsual, curwin->w_cursor);
+ #endif
+
+ /*
+ * Remember starting position so we can restore if we cannot find the
+ * requested object.
+ */
+ old_pos = curwin->w_cursor;
+
+ /*
+ * If there is a visually selected range, use its bounds as the starting
+ * bounds. Otherwise, have both bounds start under the cursor.
+ */
+ #ifdef FEAT_VISUAL
+ if (VIsual_active && lt(VIsual, curwin->w_cursor))
+ {
+ start_pos = VIsual;
+ end_pos = curwin->w_cursor;
+ }
+ else if (VIsual_active && lt(curwin->w_cursor, VIsual))
+ {
+ start_pos = curwin->w_cursor;
+ end_pos = VIsual;
+ }
+ else
+ #endif
+ {
+ start_pos = curwin->w_cursor;
+ end_pos = curwin->w_cursor;
+ }
+
+ /*
+ * Search for the left bound. It can be under or behind the starting
+ * position of the cursor position/visually selected range.
+ */
+ curwin->w_cursor = start_pos;
+ while (gchar_cursor() != matchchar && dec_cursor() != -1)
+ ;
+ if (gchar_cursor() != matchchar)
+ {
+ /* Failed to find left bound, abort. */
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ start_pos = curwin->w_cursor;
+
+ /*
+ * Search for the right bound. It has to be to the right of the starting
+ * cursor position/visually selected range.
+ */
+ curwin->w_cursor = end_pos;
+ while (inc_cursor() != -1 && gchar_cursor() != matchchar)
+ ;
+ if (gchar_cursor() != matchchar)
+ {
+ /* Failed to find right bound, abort. */
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ end_pos = curwin->w_cursor;
+
+ /*
+ * If not include (i.e.: o_i was used), try to move the bounds inward, but
+ * be sure they don't overlap or switch sides.
+ */
+ if (!include)
+ {
+ /* try moving in right bound */
+ curwin->w_cursor = end_pos;
+ ret = dec_cursor();
+ if (ret == 2)
+ ret = dec_cursor();
+ if (ret != -1 && !ltoreq(curwin->w_cursor, start_pos))
+ {
+ end_pos = curwin->w_cursor;
+ }
+
+ /* try moving in left bound */
+ curwin->w_cursor = start_pos;
+ ret = inc_cursor();
+ if (ret == 2)
+ ret = inc_cursor();
+ if (ret != -1 && !ltoreq(end_pos, curwin->w_cursor))
+ {
+ start_pos = curwin->w_cursor;
+ }
+ }
+
+ /*
+ * We've found the new bounds. Set it in the appropriate places before
+ * returning.
+ */
+ #ifdef FEAT_VISUAL
+ if (VIsual_active)
+ {
+ /* Retain direction we had when starting. */
+ if (vis_bef_curs)
+ {
+ VIsual = start_pos;
+ curwin->w_cursor = end_pos;
+ }
+ else
+ {
+ curwin->w_cursor = start_pos;
+ VIsual = end_pos;
+ }
+ redraw_curbuf_later(INVERTED); /* update the inversion */
+ }
+ else
+ #endif
+ {
+ oap->start = start_pos;
+ curwin->w_cursor = end_pos;
+ oap->motion_type = MCHAR;
+ oap->inclusive = TRUE;
+ }
+ return OK;
+ }
+
#endif /* FEAT_TEXTOBJ */
#if defined(FEAT_VISUAL) || defined(PROTO)
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index d40d825..1cb2287 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -662,6 +662,14 @@ i` *v_i`* *i`*
Special case: With a count of 2 the quotes are
included, but no extra white space as with a"/a'/a`.
+am{char} *v_am* *am*
+ "a matched {char}". Selects the text from the
+ previous {char} until the next {char}. A count is
+ currently not used.
+
+im{char} *v_im* *im*
+ Like am{char} but exclude the {char}.
+
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
index 9349be2..0758ff7 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -9309,6 +9309,11 @@ nv_object(cap)
flag = current_quote(cap->oap, cap->count1, include,
cap->nchar);
break;
+ case 'm': /* "am{char}" = a matched pair of characters */
+ cap->extra_char = plain_vgetc();
+ flag = current_match(cap->oap, cap->count1, include,
+ cap->extra_char);
+ 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
index f94fb69..0cf3be3 100644
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -32,6 +32,7 @@ int current_block __ARGS((oparg_T *oap, long count, int include, int what, int o
int current_tagblock __ARGS((oparg_T *oap, long count_arg, int include));
int current_par __ARGS((oparg_T *oap, long count, int include, int type));
int current_quote __ARGS((oparg_T *oap, long count, int include, int quotechar));
+int current_match __ARGS((oparg_T *oap, long count, int include, int matchchar));
int current_search __ARGS((long count, int forward));
int linewhite __ARGS((linenr_T lnum));
void find_pattern_in_path __ARGS((char_u *ptr, int dir, int len, int whole, int skip_comments, int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum));
diff --git a/src/search.c b/src/search.c
index 2fb7624..5075739 100644
--- a/src/search.c
+++ b/src/search.c
@@ -4489,6 +4489,147 @@ current_quote(oap, count, include, quotechar)
return OK;
}
+ /*
+ * Find matching {char} on each side of cursor/visually selected range.
+ * Returns TRUE if found, else FALSE.
+ */
+ int
+current_match(oap, count, include, matchchar)
+ oparg_T *oap; /* used to set operator range, mode, etc */
+ long count; /* not currently used */
+ int include; /* TRUE == include quote char */
+ int matchchar; /* char to match on both sides */
+{
+ pos_T old_pos; /* initial cursor position (to restore) */
+ pos_T start_pos; /* left bound */
+ pos_T end_pos; /* right bound */
+ int ret; /* temp holder for return values */
+#ifdef FEAT_VISUAL
+ int vis_bef_curs; /* store visual mode direction */
+
+ /*
+ * Store visual mode direction so we can be sure it is set the same
+ * direction when we're done.
+ */
+ vis_bef_curs = ltoreq(VIsual, curwin->w_cursor);
+#endif
+
+ /*
+ * Remember starting position so we can restore if we cannot find the
+ * requested object.
+ */
+ old_pos = curwin->w_cursor;
+
+ /*
+ * If there is a visually selected range, use its bounds as the starting
+ * bounds. Otherwise, have both bounds start under the cursor.
+ */
+#ifdef FEAT_VISUAL
+ if (VIsual_active && lt(VIsual, curwin->w_cursor))
+ {
+ start_pos = VIsual;
+ end_pos = curwin->w_cursor;
+ }
+ else if (VIsual_active && lt(curwin->w_cursor, VIsual))
+ {
+ start_pos = curwin->w_cursor;
+ end_pos = VIsual;
+ }
+ else
+#endif
+ {
+ start_pos = curwin->w_cursor;
+ end_pos = curwin->w_cursor;
+ }
+
+ /*
+ * Search for the left bound. It can be under or behind the starting
+ * position of the cursor position/visually selected range.
+ */
+ curwin->w_cursor = start_pos;
+ while (gchar_cursor() != matchchar && dec_cursor() != -1)
+ ;
+ if (gchar_cursor() != matchchar)
+ {
+ /* Failed to find left bound, abort. */
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ start_pos = curwin->w_cursor;
+
+ /*
+ * Search for the right bound. It has to be to the right of the starting
+ * cursor position/visually selected range.
+ */
+ curwin->w_cursor = end_pos;
+ while (inc_cursor() != -1 && gchar_cursor() != matchchar)
+ ;
+ if (gchar_cursor() != matchchar)
+ {
+ /* Failed to find right bound, abort. */
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ end_pos = curwin->w_cursor;
+
+ /*
+ * If not include (i.e.: o_i was used), try to move the bounds inward, but
+ * be sure they don't overlap or switch sides.
+ */
+ if (!include)
+ {
+ /* try moving in right bound */
+ curwin->w_cursor = end_pos;
+ ret = dec_cursor();
+ if (ret == 2)
+ ret = dec_cursor();
+ if (ret != -1 && !ltoreq(curwin->w_cursor, start_pos))
+ {
+ end_pos = curwin->w_cursor;
+ }
+
+ /* try moving in left bound */
+ curwin->w_cursor = start_pos;
+ ret = inc_cursor();
+ if (ret == 2)
+ ret = inc_cursor();
+ if (ret != -1 && !ltoreq(end_pos, curwin->w_cursor))
+ {
+ start_pos = curwin->w_cursor;
+ }
+ }
+
+ /*
+ * We've found the new bounds. Set it in the appropriate places before
+ * returning.
+ */
+#ifdef FEAT_VISUAL
+ if (VIsual_active)
+ {
+ /* Retain direction we had when starting. */
+ if (vis_bef_curs)
+ {
+ VIsual = start_pos;
+ curwin->w_cursor = end_pos;
+ }
+ else
+ {
+ curwin->w_cursor = start_pos;
+ VIsual = end_pos;
+ }
+ redraw_curbuf_later(INVERTED); /* update the inversion */
+ }
+ else
+#endif
+ {
+ oap->start = start_pos;
+ curwin->w_cursor = end_pos;
+ oap->motion_type = MCHAR;
+ oap->inclusive = TRUE;
+ }
+ return OK;
+}
+
#endif /* FEAT_TEXTOBJ */
#if defined(FEAT_VISUAL) || defined(PROTO)