On Wednesday, January 29, 2014 7:45:23 PM UTC-5, Daniel "paradigm" Thau wrote:
> Apologies for the delay.
> 
> Review for those who have forgotten and/or don't care to backread:
> 
> This patch adds a new text object, "m", which will take one more character as 
> input.  That character will be used as bounds to the left and right for the 
> object.  For example, "cim$" will change between dollar signs.  This supports 
> multi-line objects, so one could do "cim'" which, unlike "ci'", will search 
> across lines; this way users have both.
> 
> I've been using it quite happily for the last two months or so, but more eyes 
> and testing would not be a bad idea.
> 
> Attached is the patch, in both unified and context format, including a test.

Added wrong file/filename for the test results.  Fixed in attachment.

-- 
-- 
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 vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
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/runtime/doc/tags b/runtime/doc/tags
index dce548f..eb258d5 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4760,6 +4760,7 @@ aleph	options.txt	/*aleph*
 alt	intro.txt	/*alt*
 alt-input	debugger.txt	/*alt-input*
 alternate-file	editing.txt	/*alternate-file*
+am	motion.txt	/*am*
 amiga-window	starting.txt	/*amiga-window*
 and()	eval.txt	/*and()*
 anonymous-function	eval.txt	/*anonymous-function*
@@ -6532,6 +6533,7 @@ if_ruby.txt	if_ruby.txt	/*if_ruby.txt*
 if_sniff.txt	if_sniff.txt	/*if_sniff.txt*
 if_tcl.txt	if_tcl.txt	/*if_tcl.txt*
 ignore-errors	eval.txt	/*ignore-errors*
+im	motion.txt	/*im*
 improved-autocmds-5.4	version5.txt	/*improved-autocmds-5.4*
 improved-quickfix	version5.txt	/*improved-quickfix*
 improved-sessions	version5.txt	/*improved-sessions*
@@ -8457,6 +8459,7 @@ v_a[	motion.txt	/*v_a[*
 v_a]	motion.txt	/*v_a]*
 v_a`	motion.txt	/*v_a`*
 v_ab	motion.txt	/*v_ab*
+v_am	motion.txt	/*v_am*
 v_ap	motion.txt	/*v_ap*
 v_aquote	motion.txt	/*v_aquote*
 v_as	motion.txt	/*v_as*
@@ -8504,6 +8507,7 @@ v_i[	motion.txt	/*v_i[*
 v_i]	motion.txt	/*v_i]*
 v_i`	motion.txt	/*v_i`*
 v_ib	motion.txt	/*v_ib*
+v_im	motion.txt	/*v_im*
 v_ip	motion.txt	/*v_ip*
 v_iquote	motion.txt	/*v_iquote*
 v_is	motion.txt	/*v_is*
diff --git a/src/normal.c b/src/normal.c
index 66a5b7a..09d9d0e 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -9310,6 +9310,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 0341a5e..58db91e 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 bounding 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/src/testdir/Makefile b/src/testdir/Makefile
index bae4f44..bd29836 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -30,7 +30,8 @@ SCRIPTS = test1.out test2.out test3.out test4.out test5.out test6.out \
 		test84.out test85.out test86.out test87.out test88.out \
 		test89.out test90.out test91.out test92.out test93.out \
 		test94.out test95.out test96.out test97.out test98.out \
-		test99.out test100.out test101.out test102.out test103.out
+		test99.out test100.out test101.out test102.out test103.out \
+		test104.out
 
 SCRIPTS_GUI = test16.out
 
diff --git a/src/testdir/test104.in b/src/testdir/test104.in
new file mode 100644
index 0000000..70df33d
--- /dev/null
+++ b/src/testdir/test104.in
@@ -0,0 +1,59 @@
+Test for im/am text object
+
+Tests every combination of the following:
+- every non-whitespace printable ASCII character (bang/33 to tilde/126)
+- im (i) or am (a)
+- in-line (l) or entire-line (L)
+- single line (s) or multiple line (S)
+
+STARTTEST
+:so small.vim
+:enew
+:
+:" Generate lines with im/am objects in them
+:
+:for nr in range(33, 126)
+:	let char = nr2char(nr)
+:	let diffchar = nr2char((nr - 33 + 1) % (126 - 32) + 33)
+:	let diffchars = diffchar . diffchar . diffchar
+:	for ia in ["i", "a"]
+:	for lL in ["l", "L"]
+:	for sS in ["s", "S"]
+:		if sS == "s"
+:			let lines = diffchars
+:		else
+:			let lines = diffchars . "\<cr>" . diffchars
+:		endif
+:		execute "normal o" . char . ia . lL . sS . "X" . char . lines . char . "X\<esc>"
+:	endfor
+:	endfor
+:	endfor
+:endfor
+:
+:" Apply im/am to every created object
+:
+:for nr in range(33, 126)
+:	let char = nr2char(nr)
+:	let diffchar = nr2char((nr - 33 + 1) % (126 - 32) + 33)
+:	let diffchars = diffchar . diffchar . diffchar
+:	for ia in ["i", "a"]
+:	for lL in ["l", "L"]
+:	for sS in ["s", "S"]
+:		execute "normal /^\\V" . escape(char,"\\") . ia . lL . sS . "\<cr>4x"
+:		if lL == "L"
+:			if sS == "s"
+:				execute "normal $x0x"
+:			else
+:				execute "normal j$xk0x"
+:			endif
+:		endif
+:		execute "normal lc" . ia . "m" . char . "OK\<esc>"
+:	endfor
+:	endfor
+:	endfor
+:endfor
+:
+:w! test.out
+:qa!
+ENDTEST
+
diff --git a/src/testdir/test104.ok b/src/testdir/test104.ok
new file mode 100644
index 0000000..3e095db
--- /dev/null
+++ b/src/testdir/test104.ok
@@ -0,0 +1,753 @@
+
+X!OK!X
+X!OK!X
+!OK!
+!OK!
+XOKX
+XOKX
+OK
+OK
+X"OK"X
+X"OK"X
+"OK"
+"OK"
+XOKX
+XOKX
+OK
+OK
+X#OK#X
+X#OK#X
+#OK#
+#OK#
+XOKX
+XOKX
+OK
+OK
+X$OK$X
+X$OK$X
+$OK$
+$OK$
+XOKX
+XOKX
+OK
+OK
+X%OK%X
+X%OK%X
+%OK%
+%OK%
+XOKX
+XOKX
+OK
+OK
+X&OK&X
+X&OK&X
+&OK&
+&OK&
+XOKX
+XOKX
+OK
+OK
+X'OK'X
+X'OK'X
+'OK'
+'OK'
+XOKX
+XOKX
+OK
+OK
+X(OK(X
+X(OK(X
+(OK(
+(OK(
+XOKX
+XOKX
+OK
+OK
+X)OK)X
+X)OK)X
+)OK)
+)OK)
+XOKX
+XOKX
+OK
+OK
+X*OK*X
+X*OK*X
+*OK*
+*OK*
+XOKX
+XOKX
+OK
+OK
+X+OK+X
+X+OK+X
++OK+
++OK+
+XOKX
+XOKX
+OK
+OK
+X,OK,X
+X,OK,X
+,OK,
+,OK,
+XOKX
+XOKX
+OK
+OK
+X-OK-X
+X-OK-X
+-OK-
+-OK-
+XOKX
+XOKX
+OK
+OK
+X.OK.X
+X.OK.X
+.OK.
+.OK.
+XOKX
+XOKX
+OK
+OK
+X/OK/X
+X/OK/X
+/OK/
+/OK/
+XOKX
+XOKX
+OK
+OK
+X0OK0X
+X0OK0X
+0OK0
+0OK0
+XOKX
+XOKX
+OK
+OK
+X1OK1X
+X1OK1X
+1OK1
+1OK1
+XOKX
+XOKX
+OK
+OK
+X2OK2X
+X2OK2X
+2OK2
+2OK2
+XOKX
+XOKX
+OK
+OK
+X3OK3X
+X3OK3X
+3OK3
+3OK3
+XOKX
+XOKX
+OK
+OK
+X4OK4X
+X4OK4X
+4OK4
+4OK4
+XOKX
+XOKX
+OK
+OK
+X5OK5X
+X5OK5X
+5OK5
+5OK5
+XOKX
+XOKX
+OK
+OK
+X6OK6X
+X6OK6X
+6OK6
+6OK6
+XOKX
+XOKX
+OK
+OK
+X7OK7X
+X7OK7X
+7OK7
+7OK7
+XOKX
+XOKX
+OK
+OK
+X8OK8X
+X8OK8X
+8OK8
+8OK8
+XOKX
+XOKX
+OK
+OK
+X9OK9X
+X9OK9X
+9OK9
+9OK9
+XOKX
+XOKX
+OK
+OK
+X:OK:X
+X:OK:X
+:OK:
+:OK:
+XOKX
+XOKX
+OK
+OK
+X;OK;X
+X;OK;X
+;OK;
+;OK;
+XOKX
+XOKX
+OK
+OK
+X<OK<X
+X<OK<X
+<OK<
+<OK<
+XOKX
+XOKX
+OK
+OK
+X=OK=X
+X=OK=X
+=OK=
+=OK=
+XOKX
+XOKX
+OK
+OK
+X>OK>X
+X>OK>X
+>OK>
+>OK>
+XOKX
+XOKX
+OK
+OK
+X?OK?X
+X?OK?X
+?OK?
+?OK?
+XOKX
+XOKX
+OK
+OK
+X@OK@X
+X@OK@X
+@OK@
+@OK@
+XOKX
+XOKX
+OK
+OK
+XAOKAX
+XAOKAX
+AOKA
+AOKA
+XOKX
+XOKX
+OK
+OK
+XBOKBX
+XBOKBX
+BOKB
+BOKB
+XOKX
+XOKX
+OK
+OK
+XCOKCX
+XCOKCX
+COKC
+COKC
+XOKX
+XOKX
+OK
+OK
+XDOKDX
+XDOKDX
+DOKD
+DOKD
+XOKX
+XOKX
+OK
+OK
+XEOKEX
+XEOKEX
+EOKE
+EOKE
+XOKX
+XOKX
+OK
+OK
+XFOKFX
+XFOKFX
+FOKF
+FOKF
+XOKX
+XOKX
+OK
+OK
+XGOKGX
+XGOKGX
+GOKG
+GOKG
+XOKX
+XOKX
+OK
+OK
+XHOKHX
+XHOKHX
+HOKH
+HOKH
+XOKX
+XOKX
+OK
+OK
+XIOKIX
+XIOKIX
+IOKI
+IOKI
+XOKX
+XOKX
+OK
+OK
+XJOKJX
+XJOKJX
+JOKJ
+JOKJ
+XOKX
+XOKX
+OK
+OK
+XKOKKX
+XKOKKX
+KOKK
+KOKK
+XOKX
+XOKX
+OK
+OK
+XLOKLX
+XLOKLX
+LOKL
+LOKL
+XOKX
+XOKX
+OK
+OK
+XMOKMX
+XMOKMX
+MOKM
+MOKM
+XOKX
+XOKX
+OK
+OK
+XNOKNX
+XNOKNX
+NOKN
+NOKN
+XOKX
+XOKX
+OK
+OK
+XOOKOX
+XOOKOX
+OOKO
+OOKO
+XOKX
+XOKX
+OK
+OK
+XPOKPX
+XPOKPX
+POKP
+POKP
+XOKX
+XOKX
+OK
+OK
+XQOKQX
+XQOKQX
+QOKQ
+QOKQ
+XOKX
+XOKX
+OK
+OK
+XROKRX
+XROKRX
+ROKR
+ROKR
+XOKX
+XOKX
+OK
+OK
+XSOKSX
+XSOKSX
+SOKS
+SOKS
+XOKX
+XOKX
+OK
+OK
+XTOKTX
+XTOKTX
+TOKT
+TOKT
+XOKX
+XOKX
+OK
+OK
+XUOKUX
+XUOKUX
+UOKU
+UOKU
+XOKX
+XOKX
+OK
+OK
+XVOKVX
+XVOKVX
+VOKV
+VOKV
+XOKX
+XOKX
+OK
+OK
+XWOKWX
+XWOKWX
+WOKW
+WOKW
+XOKX
+XOKX
+OK
+OK
+XXOKXX
+XXOKXX
+XOKX
+XOKX
+XOKX
+XOKX
+OK
+OK
+XYOKYX
+XYOKYX
+YOKY
+YOKY
+XOKX
+XOKX
+OK
+OK
+XZOKZX
+XZOKZX
+ZOKZ
+ZOKZ
+XOKX
+XOKX
+OK
+OK
+X[OK[X
+X[OK[X
+[OK[
+[OK[
+XOKX
+XOKX
+OK
+OK
+X\OK\X
+X\OK\X
+\OK\
+\OK\
+XOKX
+XOKX
+OK
+OK
+X]OK]X
+X]OK]X
+]OK]
+]OK]
+XOKX
+XOKX
+OK
+OK
+X^OK^X
+X^OK^X
+^OK^
+^OK^
+XOKX
+XOKX
+OK
+OK
+X_OK_X
+X_OK_X
+_OK_
+_OK_
+XOKX
+XOKX
+OK
+OK
+X`OK`X
+X`OK`X
+`OK`
+`OK`
+XOKX
+XOKX
+OK
+OK
+XaOKaX
+XaOKaX
+aOKa
+aOKa
+XOKX
+XOKX
+OK
+OK
+XbOKbX
+XbOKbX
+bOKb
+bOKb
+XOKX
+XOKX
+OK
+OK
+XcOKcX
+XcOKcX
+cOKc
+cOKc
+XOKX
+XOKX
+OK
+OK
+XdOKdX
+XdOKdX
+dOKd
+dOKd
+XOKX
+XOKX
+OK
+OK
+XeOKeX
+XeOKeX
+eOKe
+eOKe
+XOKX
+XOKX
+OK
+OK
+XfOKfX
+XfOKfX
+fOKf
+fOKf
+XOKX
+XOKX
+OK
+OK
+XgOKgX
+XgOKgX
+gOKg
+gOKg
+XOKX
+XOKX
+OK
+OK
+XhOKhX
+XhOKhX
+hOKh
+hOKh
+XOKX
+XOKX
+OK
+OK
+XiOKiX
+XiOKiX
+iOKi
+iOKi
+XOKX
+XOKX
+OK
+OK
+XjOKjX
+XjOKjX
+jOKj
+jOKj
+XOKX
+XOKX
+OK
+OK
+XkOKkX
+XkOKkX
+kOKk
+kOKk
+XOKX
+XOKX
+OK
+OK
+XlOKlX
+XlOKlX
+lOKl
+lOKl
+XOKX
+XOKX
+OK
+OK
+XmOKmX
+XmOKmX
+mOKm
+mOKm
+XOKX
+XOKX
+OK
+OK
+XnOKnX
+XnOKnX
+nOKn
+nOKn
+XOKX
+XOKX
+OK
+OK
+XoOKoX
+XoOKoX
+oOKo
+oOKo
+XOKX
+XOKX
+OK
+OK
+XpOKpX
+XpOKpX
+pOKp
+pOKp
+XOKX
+XOKX
+OK
+OK
+XqOKqX
+XqOKqX
+qOKq
+qOKq
+XOKX
+XOKX
+OK
+OK
+XrOKrX
+XrOKrX
+rOKr
+rOKr
+XOKX
+XOKX
+OK
+OK
+XsOKsX
+XsOKsX
+sOKs
+sOKs
+XOKX
+XOKX
+OK
+OK
+XtOKtX
+XtOKtX
+tOKt
+tOKt
+XOKX
+XOKX
+OK
+OK
+XuOKuX
+XuOKuX
+uOKu
+uOKu
+XOKX
+XOKX
+OK
+OK
+XvOKvX
+XvOKvX
+vOKv
+vOKv
+XOKX
+XOKX
+OK
+OK
+XwOKwX
+XwOKwX
+wOKw
+wOKw
+XOKX
+XOKX
+OK
+OK
+XxOKxX
+XxOKxX
+xOKx
+xOKx
+XOKX
+XOKX
+OK
+OK
+XyOKyX
+XyOKyX
+yOKy
+yOKy
+XOKX
+XOKX
+OK
+OK
+XzOKzX
+XzOKzX
+zOKz
+zOKz
+XOKX
+XOKX
+OK
+OK
+X{OK{X
+X{OK{X
+{OK{
+{OK{
+XOKX
+XOKX
+OK
+OK
+X|OK|X
+X|OK|X
+|OK|
+|OK|
+XOKX
+XOKX
+OK
+OK
+X}OK}X
+X}OK}X
+}OK}
+}OK}
+XOKX
+XOKX
+OK
+OK
+X~OK~X
+X~OK~X
+~OK~
+~OK~
+XOKX
+XOKX
+OK
+OK
*** 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/runtime/doc/tags
--- b/runtime/doc/tags
***************
*** 4760,4765 **** aleph	options.txt	/*aleph*
--- 4760,4766 ----
  alt	intro.txt	/*alt*
  alt-input	debugger.txt	/*alt-input*
  alternate-file	editing.txt	/*alternate-file*
+ am	motion.txt	/*am*
  amiga-window	starting.txt	/*amiga-window*
  and()	eval.txt	/*and()*
  anonymous-function	eval.txt	/*anonymous-function*
***************
*** 6532,6537 **** if_ruby.txt	if_ruby.txt	/*if_ruby.txt*
--- 6533,6539 ----
  if_sniff.txt	if_sniff.txt	/*if_sniff.txt*
  if_tcl.txt	if_tcl.txt	/*if_tcl.txt*
  ignore-errors	eval.txt	/*ignore-errors*
+ im	motion.txt	/*im*
  improved-autocmds-5.4	version5.txt	/*improved-autocmds-5.4*
  improved-quickfix	version5.txt	/*improved-quickfix*
  improved-sessions	version5.txt	/*improved-sessions*
***************
*** 8457,8462 **** v_a[	motion.txt	/*v_a[*
--- 8459,8465 ----
  v_a]	motion.txt	/*v_a]*
  v_a`	motion.txt	/*v_a`*
  v_ab	motion.txt	/*v_ab*
+ v_am	motion.txt	/*v_am*
  v_ap	motion.txt	/*v_ap*
  v_aquote	motion.txt	/*v_aquote*
  v_as	motion.txt	/*v_as*
***************
*** 8504,8509 **** v_i[	motion.txt	/*v_i[*
--- 8507,8513 ----
  v_i]	motion.txt	/*v_i]*
  v_i`	motion.txt	/*v_i`*
  v_ib	motion.txt	/*v_ib*
+ v_im	motion.txt	/*v_im*
  v_ip	motion.txt	/*v_ip*
  v_iquote	motion.txt	/*v_iquote*
  v_is	motion.txt	/*v_is*
*** a/src/normal.c
--- b/src/normal.c
***************
*** 9310,9315 **** nv_object(cap)
--- 9310,9320 ----
  		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 bounding 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)
*** a/src/testdir/Makefile
--- b/src/testdir/Makefile
***************
*** 30,36 **** SCRIPTS = test1.out test2.out test3.out test4.out test5.out test6.out \
  		test84.out test85.out test86.out test87.out test88.out \
  		test89.out test90.out test91.out test92.out test93.out \
  		test94.out test95.out test96.out test97.out test98.out \
! 		test99.out test100.out test101.out test102.out test103.out
  
  SCRIPTS_GUI = test16.out
  
--- 30,37 ----
  		test84.out test85.out test86.out test87.out test88.out \
  		test89.out test90.out test91.out test92.out test93.out \
  		test94.out test95.out test96.out test97.out test98.out \
! 		test99.out test100.out test101.out test102.out test103.out \
! 		test104.out
  
  SCRIPTS_GUI = test16.out
  
*** /dev/null
--- b/src/testdir/test104.in
***************
*** 0 ****
--- 1,59 ----
+ Test for im/am text object
+ 
+ Tests every combination of the following:
+ - every non-whitespace printable ASCII character (bang/33 to tilde/126)
+ - im (i) or am (a)
+ - in-line (l) or entire-line (L)
+ - single line (s) or multiple line (S)
+ 
+ STARTTEST
+ :so small.vim
+ :enew
+ :
+ :" Generate lines with im/am objects in them
+ :
+ :for nr in range(33, 126)
+ :	let char = nr2char(nr)
+ :	let diffchar = nr2char((nr - 33 + 1) % (126 - 32) + 33)
+ :	let diffchars = diffchar . diffchar . diffchar
+ :	for ia in ["i", "a"]
+ :	for lL in ["l", "L"]
+ :	for sS in ["s", "S"]
+ :		if sS == "s"
+ :			let lines = diffchars
+ :		else
+ :			let lines = diffchars . "\<cr>" . diffchars
+ :		endif
+ :		execute "normal o" . char . ia . lL . sS . "X" . char . lines . char . "X\<esc>"
+ :	endfor
+ :	endfor
+ :	endfor
+ :endfor
+ :
+ :" Apply im/am to every created object
+ :
+ :for nr in range(33, 126)
+ :	let char = nr2char(nr)
+ :	let diffchar = nr2char((nr - 33 + 1) % (126 - 32) + 33)
+ :	let diffchars = diffchar . diffchar . diffchar
+ :	for ia in ["i", "a"]
+ :	for lL in ["l", "L"]
+ :	for sS in ["s", "S"]
+ :		execute "normal /^\\V" . escape(char,"\\") . ia . lL . sS . "\<cr>4x"
+ :		if lL == "L"
+ :			if sS == "s"
+ :				execute "normal $x0x"
+ :			else
+ :				execute "normal j$xk0x"
+ :			endif
+ :		endif
+ :		execute "normal lc" . ia . "m" . char . "OK\<esc>"
+ :	endfor
+ :	endfor
+ :	endfor
+ :endfor
+ :
+ :w! test.out
+ :qa!
+ ENDTEST
+ 
*** /dev/null
--- b/src/testdir/test104.ok
***************
*** 0 ****
--- 1,753 ----
+ 
+ X!OK!X
+ X!OK!X
+ !OK!
+ !OK!
+ XOKX
+ XOKX
+ OK
+ OK
+ X"OK"X
+ X"OK"X
+ "OK"
+ "OK"
+ XOKX
+ XOKX
+ OK
+ OK
+ X#OK#X
+ X#OK#X
+ #OK#
+ #OK#
+ XOKX
+ XOKX
+ OK
+ OK
+ X$OK$X
+ X$OK$X
+ $OK$
+ $OK$
+ XOKX
+ XOKX
+ OK
+ OK
+ X%OK%X
+ X%OK%X
+ %OK%
+ %OK%
+ XOKX
+ XOKX
+ OK
+ OK
+ X&OK&X
+ X&OK&X
+ &OK&
+ &OK&
+ XOKX
+ XOKX
+ OK
+ OK
+ X'OK'X
+ X'OK'X
+ 'OK'
+ 'OK'
+ XOKX
+ XOKX
+ OK
+ OK
+ X(OK(X
+ X(OK(X
+ (OK(
+ (OK(
+ XOKX
+ XOKX
+ OK
+ OK
+ X)OK)X
+ X)OK)X
+ )OK)
+ )OK)
+ XOKX
+ XOKX
+ OK
+ OK
+ X*OK*X
+ X*OK*X
+ *OK*
+ *OK*
+ XOKX
+ XOKX
+ OK
+ OK
+ X+OK+X
+ X+OK+X
+ +OK+
+ +OK+
+ XOKX
+ XOKX
+ OK
+ OK
+ X,OK,X
+ X,OK,X
+ ,OK,
+ ,OK,
+ XOKX
+ XOKX
+ OK
+ OK
+ X-OK-X
+ X-OK-X
+ -OK-
+ -OK-
+ XOKX
+ XOKX
+ OK
+ OK
+ X.OK.X
+ X.OK.X
+ .OK.
+ .OK.
+ XOKX
+ XOKX
+ OK
+ OK
+ X/OK/X
+ X/OK/X
+ /OK/
+ /OK/
+ XOKX
+ XOKX
+ OK
+ OK
+ X0OK0X
+ X0OK0X
+ 0OK0
+ 0OK0
+ XOKX
+ XOKX
+ OK
+ OK
+ X1OK1X
+ X1OK1X
+ 1OK1
+ 1OK1
+ XOKX
+ XOKX
+ OK
+ OK
+ X2OK2X
+ X2OK2X
+ 2OK2
+ 2OK2
+ XOKX
+ XOKX
+ OK
+ OK
+ X3OK3X
+ X3OK3X
+ 3OK3
+ 3OK3
+ XOKX
+ XOKX
+ OK
+ OK
+ X4OK4X
+ X4OK4X
+ 4OK4
+ 4OK4
+ XOKX
+ XOKX
+ OK
+ OK
+ X5OK5X
+ X5OK5X
+ 5OK5
+ 5OK5
+ XOKX
+ XOKX
+ OK
+ OK
+ X6OK6X
+ X6OK6X
+ 6OK6
+ 6OK6
+ XOKX
+ XOKX
+ OK
+ OK
+ X7OK7X
+ X7OK7X
+ 7OK7
+ 7OK7
+ XOKX
+ XOKX
+ OK
+ OK
+ X8OK8X
+ X8OK8X
+ 8OK8
+ 8OK8
+ XOKX
+ XOKX
+ OK
+ OK
+ X9OK9X
+ X9OK9X
+ 9OK9
+ 9OK9
+ XOKX
+ XOKX
+ OK
+ OK
+ X:OK:X
+ X:OK:X
+ :OK:
+ :OK:
+ XOKX
+ XOKX
+ OK
+ OK
+ X;OK;X
+ X;OK;X
+ ;OK;
+ ;OK;
+ XOKX
+ XOKX
+ OK
+ OK
+ X<OK<X
+ X<OK<X
+ <OK<
+ <OK<
+ XOKX
+ XOKX
+ OK
+ OK
+ X=OK=X
+ X=OK=X
+ =OK=
+ =OK=
+ XOKX
+ XOKX
+ OK
+ OK
+ X>OK>X
+ X>OK>X
+ >OK>
+ >OK>
+ XOKX
+ XOKX
+ OK
+ OK
+ X?OK?X
+ X?OK?X
+ ?OK?
+ ?OK?
+ XOKX
+ XOKX
+ OK
+ OK
+ X@OK@X
+ X@OK@X
+ @OK@
+ @OK@
+ XOKX
+ XOKX
+ OK
+ OK
+ XAOKAX
+ XAOKAX
+ AOKA
+ AOKA
+ XOKX
+ XOKX
+ OK
+ OK
+ XBOKBX
+ XBOKBX
+ BOKB
+ BOKB
+ XOKX
+ XOKX
+ OK
+ OK
+ XCOKCX
+ XCOKCX
+ COKC
+ COKC
+ XOKX
+ XOKX
+ OK
+ OK
+ XDOKDX
+ XDOKDX
+ DOKD
+ DOKD
+ XOKX
+ XOKX
+ OK
+ OK
+ XEOKEX
+ XEOKEX
+ EOKE
+ EOKE
+ XOKX
+ XOKX
+ OK
+ OK
+ XFOKFX
+ XFOKFX
+ FOKF
+ FOKF
+ XOKX
+ XOKX
+ OK
+ OK
+ XGOKGX
+ XGOKGX
+ GOKG
+ GOKG
+ XOKX
+ XOKX
+ OK
+ OK
+ XHOKHX
+ XHOKHX
+ HOKH
+ HOKH
+ XOKX
+ XOKX
+ OK
+ OK
+ XIOKIX
+ XIOKIX
+ IOKI
+ IOKI
+ XOKX
+ XOKX
+ OK
+ OK
+ XJOKJX
+ XJOKJX
+ JOKJ
+ JOKJ
+ XOKX
+ XOKX
+ OK
+ OK
+ XKOKKX
+ XKOKKX
+ KOKK
+ KOKK
+ XOKX
+ XOKX
+ OK
+ OK
+ XLOKLX
+ XLOKLX
+ LOKL
+ LOKL
+ XOKX
+ XOKX
+ OK
+ OK
+ XMOKMX
+ XMOKMX
+ MOKM
+ MOKM
+ XOKX
+ XOKX
+ OK
+ OK
+ XNOKNX
+ XNOKNX
+ NOKN
+ NOKN
+ XOKX
+ XOKX
+ OK
+ OK
+ XOOKOX
+ XOOKOX
+ OOKO
+ OOKO
+ XOKX
+ XOKX
+ OK
+ OK
+ XPOKPX
+ XPOKPX
+ POKP
+ POKP
+ XOKX
+ XOKX
+ OK
+ OK
+ XQOKQX
+ XQOKQX
+ QOKQ
+ QOKQ
+ XOKX
+ XOKX
+ OK
+ OK
+ XROKRX
+ XROKRX
+ ROKR
+ ROKR
+ XOKX
+ XOKX
+ OK
+ OK
+ XSOKSX
+ XSOKSX
+ SOKS
+ SOKS
+ XOKX
+ XOKX
+ OK
+ OK
+ XTOKTX
+ XTOKTX
+ TOKT
+ TOKT
+ XOKX
+ XOKX
+ OK
+ OK
+ XUOKUX
+ XUOKUX
+ UOKU
+ UOKU
+ XOKX
+ XOKX
+ OK
+ OK
+ XVOKVX
+ XVOKVX
+ VOKV
+ VOKV
+ XOKX
+ XOKX
+ OK
+ OK
+ XWOKWX
+ XWOKWX
+ WOKW
+ WOKW
+ XOKX
+ XOKX
+ OK
+ OK
+ XXOKXX
+ XXOKXX
+ XOKX
+ XOKX
+ XOKX
+ XOKX
+ OK
+ OK
+ XYOKYX
+ XYOKYX
+ YOKY
+ YOKY
+ XOKX
+ XOKX
+ OK
+ OK
+ XZOKZX
+ XZOKZX
+ ZOKZ
+ ZOKZ
+ XOKX
+ XOKX
+ OK
+ OK
+ X[OK[X
+ X[OK[X
+ [OK[
+ [OK[
+ XOKX
+ XOKX
+ OK
+ OK
+ X\OK\X
+ X\OK\X
+ \OK\
+ \OK\
+ XOKX
+ XOKX
+ OK
+ OK
+ X]OK]X
+ X]OK]X
+ ]OK]
+ ]OK]
+ XOKX
+ XOKX
+ OK
+ OK
+ X^OK^X
+ X^OK^X
+ ^OK^
+ ^OK^
+ XOKX
+ XOKX
+ OK
+ OK
+ X_OK_X
+ X_OK_X
+ _OK_
+ _OK_
+ XOKX
+ XOKX
+ OK
+ OK
+ X`OK`X
+ X`OK`X
+ `OK`
+ `OK`
+ XOKX
+ XOKX
+ OK
+ OK
+ XaOKaX
+ XaOKaX
+ aOKa
+ aOKa
+ XOKX
+ XOKX
+ OK
+ OK
+ XbOKbX
+ XbOKbX
+ bOKb
+ bOKb
+ XOKX
+ XOKX
+ OK
+ OK
+ XcOKcX
+ XcOKcX
+ cOKc
+ cOKc
+ XOKX
+ XOKX
+ OK
+ OK
+ XdOKdX
+ XdOKdX
+ dOKd
+ dOKd
+ XOKX
+ XOKX
+ OK
+ OK
+ XeOKeX
+ XeOKeX
+ eOKe
+ eOKe
+ XOKX
+ XOKX
+ OK
+ OK
+ XfOKfX
+ XfOKfX
+ fOKf
+ fOKf
+ XOKX
+ XOKX
+ OK
+ OK
+ XgOKgX
+ XgOKgX
+ gOKg
+ gOKg
+ XOKX
+ XOKX
+ OK
+ OK
+ XhOKhX
+ XhOKhX
+ hOKh
+ hOKh
+ XOKX
+ XOKX
+ OK
+ OK
+ XiOKiX
+ XiOKiX
+ iOKi
+ iOKi
+ XOKX
+ XOKX
+ OK
+ OK
+ XjOKjX
+ XjOKjX
+ jOKj
+ jOKj
+ XOKX
+ XOKX
+ OK
+ OK
+ XkOKkX
+ XkOKkX
+ kOKk
+ kOKk
+ XOKX
+ XOKX
+ OK
+ OK
+ XlOKlX
+ XlOKlX
+ lOKl
+ lOKl
+ XOKX
+ XOKX
+ OK
+ OK
+ XmOKmX
+ XmOKmX
+ mOKm
+ mOKm
+ XOKX
+ XOKX
+ OK
+ OK
+ XnOKnX
+ XnOKnX
+ nOKn
+ nOKn
+ XOKX
+ XOKX
+ OK
+ OK
+ XoOKoX
+ XoOKoX
+ oOKo
+ oOKo
+ XOKX
+ XOKX
+ OK
+ OK
+ XpOKpX
+ XpOKpX
+ pOKp
+ pOKp
+ XOKX
+ XOKX
+ OK
+ OK
+ XqOKqX
+ XqOKqX
+ qOKq
+ qOKq
+ XOKX
+ XOKX
+ OK
+ OK
+ XrOKrX
+ XrOKrX
+ rOKr
+ rOKr
+ XOKX
+ XOKX
+ OK
+ OK
+ XsOKsX
+ XsOKsX
+ sOKs
+ sOKs
+ XOKX
+ XOKX
+ OK
+ OK
+ XtOKtX
+ XtOKtX
+ tOKt
+ tOKt
+ XOKX
+ XOKX
+ OK
+ OK
+ XuOKuX
+ XuOKuX
+ uOKu
+ uOKu
+ XOKX
+ XOKX
+ OK
+ OK
+ XvOKvX
+ XvOKvX
+ vOKv
+ vOKv
+ XOKX
+ XOKX
+ OK
+ OK
+ XwOKwX
+ XwOKwX
+ wOKw
+ wOKw
+ XOKX
+ XOKX
+ OK
+ OK
+ XxOKxX
+ XxOKxX
+ xOKx
+ xOKx
+ XOKX
+ XOKX
+ OK
+ OK
+ XyOKyX
+ XyOKyX
+ yOKy
+ yOKy
+ XOKX
+ XOKX
+ OK
+ OK
+ XzOKzX
+ XzOKzX
+ zOKz
+ zOKz
+ XOKX
+ XOKX
+ OK
+ OK
+ X{OK{X
+ X{OK{X
+ {OK{
+ {OK{
+ XOKX
+ XOKX
+ OK
+ OK
+ X|OK|X
+ X|OK|X
+ |OK|
+ |OK|
+ XOKX
+ XOKX
+ OK
+ OK
+ X}OK}X
+ X}OK}X
+ }OK}
+ }OK}
+ XOKX
+ XOKX
+ OK
+ OK
+ X~OK~X
+ X~OK~X
+ ~OK~
+ ~OK~
+ XOKX
+ XOKX
+ OK
+ OK

Raspunde prin e-mail lui