On Tuesday, November 19, 2013 4:42:32 PM UTC-5, Bram Moolenaar wrote:
> Daniel Thau wrote:
>
>
>
> > Attached is a patch (in both the unified and context formats) to make
>
> > the discussed "am/im" text-object.
>
>
>
> > *** a/runtime/doc/motion.txt
>
> > --- b/runtime/doc/motion.txt
>
> > ***************
>
> > *** 662,667 **** i` *v_i`*
> > *i`*
>
> > --- 662,687 ----
>
> > 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}.
>
> > + Only works within one line.
>
> > + When the cursor starts on a {char}, Vim will figure
>
> > + out which {char} pairs are desired by searching from
>
> > + the start of the line.
>
> > + Any trailing white space is included, unless there is
>
> > + none, then leading white space is included.
>
> > + When used in Visual mode it is made characterwise.
>
> > + Repeating this object in Visual mode another
>
> > + {char}..{char} range is included. A count is
>
> > + currently not used.
>
> > +
>
> > + im{char} *v_im* *im*
>
> > + Like am{char} but exclude the {char} and
>
> > + repeating won't extend the Visual selection.
>
> > + Special case: With a count of 2 the quotes are
>
> > + included, but no extra white space as with am{char}.
>
> > +
>
>
>
> I'm sure users will ask about a multi-line implementation. This
>
> requires defining how Vim decides whether a matching {char} is the start
>
> or the end, but otherwise it should work.
>
>
>
> --
>
> BEDEVERE: And that, my lord, is how we know the Earth to be banana-shaped.
>
> "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
>
>
>
> /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
>
> /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
>
> \\\ an exciting new programming language -- http://www.Zimbu.org ///
>
> \\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Attached is a patch to have the im/am object support multi-line. I figured
you'd probably not want the pre-existing i"/a" object to do multi-line, so
instead of continuing to piggyback off of it I made a new function for the
im/am object. It does not yet support doing a iv to select a larger range - I
can get to that later if this strategy seems acceptable.
--
--
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)