changeset 20209:6ca6a372fef6 v8.2.0660

patch 8.2.0660: the search.c file is a bit big Commit: https://github.com/vim/vim/commit/ed8ce057b7a2fcd89b5f55680ae8f85d62a992a5 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Apr 29 21:04:15 2020 +0200 patch 8.2.0660: the search.c file is a bit big Problem: The search.c file is a bit big. Solution: Split off the text object code to a separate file. (Yegappan Lakshmanan, closes #6007)
author Bram Moolenaar <Bram@vim.org>
date Wed, 29 Apr 2020 21:15:05 +0200
parents 061dfda170cd
children 94eacaea0709
files Filelist src/Make_cyg_ming.mak src/Make_morph.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/README.md src/proto.h src/proto/search.pro src/proto/textobject.pro src/search.c src/textobject.c src/version.c
diffstat 13 files changed, 2020 insertions(+), 1976 deletions(-) [+]
line wrap: on
line diff
--- a/Filelist
+++ b/Filelist
@@ -128,6 +128,7 @@ SRC_ALL =	\
 		src/term.h \
 		src/termlib.c \
 		src/testing.c \
+		src/textobject.c \
 		src/textprop.c \
 		src/time.c \
 		src/ui.c \
@@ -279,6 +280,7 @@ SRC_ALL =	\
 		src/proto/terminal.pro \
 		src/proto/termlib.pro \
 		src/proto/testing.pro \
+		src/proto/textobject.pro \
 		src/proto/textprop.pro \
 		src/proto/time.pro \
 		src/proto/ui.pro \
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -787,6 +787,7 @@ OBJ = \
 	$(OUTDIR)/tag.o \
 	$(OUTDIR)/term.o \
 	$(OUTDIR)/testing.o \
+	$(OUTDIR)/textobject.o \
 	$(OUTDIR)/textprop.o \
 	$(OUTDIR)/time.o \
 	$(OUTDIR)/ui.o \
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -103,6 +103,7 @@ SRC =	arabic.c						\
 	tag.c							\
 	term.c							\
 	testing.c						\
+	textobject.c						\
 	textprop.c						\
 	time.c							\
 	ui.c							\
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -806,6 +806,7 @@ OBJ = \
 	$(OUTDIR)\tag.obj \
 	$(OUTDIR)\term.obj \
 	$(OUTDIR)\testing.obj \
+	$(OUTDIR)\textobject.obj \
 	$(OUTDIR)\textprop.obj \
 	$(OUTDIR)\time.obj \
 	$(OUTDIR)\ui.obj \
@@ -1744,6 +1745,8 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l
 
 $(OUTDIR)/term.obj:	$(OUTDIR) testing.c  $(INCL)
 
+$(OUTDIR)/textobject.obj:	$(OUTDIR) textobject.c  $(INCL)
+
 $(OUTDIR)/textprop.obj:	$(OUTDIR) textprop.c  $(INCL)
 
 $(OUTDIR)/time.obj:	$(OUTDIR) time.c  $(INCL)
@@ -1942,6 +1945,7 @@ proto.h: \
 	proto/tag.pro \
 	proto/term.pro \
 	proto/testing.pro \
+	proto/textobject.pro \
 	proto/textprop.pro \
 	proto/time.pro \
 	proto/ui.pro \
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -382,6 +382,7 @@ SRC = \
 	term.c \
 	termlib.c \
 	testing.c \
+	textobject.c \
 	textprop.c \
 	time.c \
 	ui.c \
@@ -491,6 +492,7 @@ OBJ = \
 	term.obj \
 	termlib.obj \
 	testing.obj \
+	textobject.obj \
 	textprop.obj \
 	time.obj \
 	ui.obj \
@@ -989,6 +991,9 @@ termlib.obj : termlib.c vim.h [.auto]con
 testing.obj : testing.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
+textobject.obj : textobject.c vim.h [.auto]config.h feature.h os_unix.h   \
+ ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
+ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
 textprop.obj : textprop.c vim.h [.auto]config.h feature.h os_unix.h   \
  ascii.h keymap.h term.h macros.h structs.h regexp.h gui.h beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h
--- a/src/Makefile
+++ b/src/Makefile
@@ -404,7 +404,7 @@ CClink = $(CC)
 # Use --with-luajit if you want to use LuaJIT instead of Lua.
 # Set PATH environment variable to find lua or luajit executable.
 # This requires at least "normal" features, "tiny" and "small" don't work.
-#CONF_OPT_LUA = --enable-luainterp
+CONF_OPT_LUA = --enable-luainterp
 #CONF_OPT_LUA = --enable-luainterp=dynamic
 #CONF_OPT_LUA = --enable-luainterp --with-luajit
 #CONF_OPT_LUA = --enable-luainterp=dynamic --with-luajit
@@ -433,7 +433,7 @@ CClink = $(CC)
 # When you get an error for a missing "perl.exp" file, try creating an empty
 # one: "touch perl.exp".
 # This requires at least "normal" features, "tiny" and "small" don't work.
-#CONF_OPT_PERL = --enable-perlinterp
+CONF_OPT_PERL = --enable-perlinterp
 #CONF_OPT_PERL = --enable-perlinterp=dynamic
 
 # PYTHON
@@ -447,10 +447,10 @@ CClink = $(CC)
 # dlopen(), dlsym(), dlclose(), i.e. pythonX.Y.so must be available
 # However, this may still cause problems, such as "import termios" failing.
 # Build two separate versions of Vim in that case.
-#CONF_OPT_PYTHON = --enable-pythoninterp
+CONF_OPT_PYTHON = --enable-pythoninterp
 #CONF_OPT_PYTHON = --enable-pythoninterp --with-python-command=python2.7
 #CONF_OPT_PYTHON = --enable-pythoninterp=dynamic
-#CONF_OPT_PYTHON3 = --enable-python3interp
+CONF_OPT_PYTHON3 = --enable-python3interp
 #CONF_OPT_PYTHON3 = --enable-python3interp --with-python3-command=python3.6
 #CONF_OPT_PYTHON3 = --enable-python3interp=dynamic
 
@@ -460,19 +460,19 @@ CClink = $(CC)
 # Note: you need the development package (e.g., ruby1.9.1-dev on Ubuntu).
 # This requires at least "normal" features, "tiny" and "small" don't work.
 #CONF_OPT_RUBY = --enable-rubyinterp
-#CONF_OPT_RUBY = --enable-rubyinterp=dynamic
+CONF_OPT_RUBY = --enable-rubyinterp=dynamic
 #CONF_OPT_RUBY = --enable-rubyinterp --with-ruby-command=ruby1.9.1
 
 # TCL
 # Uncomment this when you want to include the Tcl interface.
 # First one is for static linking, second one for dynamic loading.
 #CONF_OPT_TCL = --enable-tclinterp
-#CONF_OPT_TCL = --enable-tclinterp=dynamic
+CONF_OPT_TCL = --enable-tclinterp=dynamic
 #CONF_OPT_TCL = --enable-tclinterp --with-tclsh=tclsh8.4
 
 # CSCOPE
 # Uncomment this when you want to include the Cscope interface.
-#CONF_OPT_CSCOPE = --enable-cscope
+CONF_OPT_CSCOPE = --enable-cscope
 
 # NETBEANS - NetBeans interface. Only works with Motif, GTK, and gnome.
 # Motif version must have XPM libraries (see |netbeans-xpm|).
@@ -540,7 +540,7 @@ CClink = $(CC)
 #CONF_OPT_FEAT = --with-features=small
 #CONF_OPT_FEAT = --with-features=normal
 #CONF_OPT_FEAT = --with-features=big
-#CONF_OPT_FEAT = --with-features=huge
+CONF_OPT_FEAT = --with-features=huge
 
 # COMPILED BY - For including a specific e-mail address for ":version".
 #CONF_OPT_COMPBY = "--with-compiledby=John Doe <JohnDoe@yahoo.com>"
@@ -614,11 +614,11 @@ CClink = $(CC)
 # Use this with GCC to check for mistakes, unused arguments, etc.
 # Note: If you use -Wextra and get warnings in GTK code about function
 #       parameters, you can add -Wno-cast-function-type
-#CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wunreachable-code -Wno-cast-function-type -Wno-deprecated-declarations -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
+CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wunreachable-code -Wno-cast-function-type -Wno-deprecated-declarations -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
 # Add -Wpedantic to find // comments and other C99 constructs.
 # Better disable Perl and Python to avoid a lot of warnings.
 #CFLAGS = -g -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -Wunused-result -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
-#CFLAGS = -g -O2 -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -Wunused-result -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
+#CFLAGS = -g -O2 -Wall -Wextra -Wshadow -Wmissing-prototypes -Wpedantic -Wunreachable-code -Wno-cast-function-type -Wunused-result -Wno-deprecated-declarations -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
 #PYTHON_CFLAGS_EXTRA = -Wno-missing-field-initializers
 #MZSCHEME_CFLAGS_EXTRA = -Wno-unreachable-code -Wno-unused-parameter
 
@@ -707,12 +707,12 @@ SANITIZER_LIBS = $(SANITIZER_CFLAGS)
 # Configuration is in the .ccmalloc or ~/.ccmalloc file.
 # Doesn't work very well, since memory linked to from global variables
 # (in libraries) is also marked as leaked memory.
-#LEAK_CFLAGS = -DEXITFREE
+LEAK_CFLAGS = -DEXITFREE
 #LEAK_LIBS = -lccmalloc
 
 # Uncomment this line to have Vim call abort() when an internal error is
 # detected.  Useful when using a tool to find errors.
-#ABORT_CFLAGS = -DABORT_ON_INTERNAL_ERROR
+ABORT_CFLAGS = -DABORT_ON_INTERNAL_ERROR
 
 #####################################################
 ###  Specific systems, check if yours is listed!  ### {{{
@@ -1680,6 +1680,7 @@ BASIC_SRC = \
 	term.c \
 	terminal.c \
 	testing.c \
+	textobject.c \
 	textprop.c \
 	time.c \
 	ui.c \
@@ -1822,6 +1823,7 @@ OBJ_COMMON = \
 	objects/term.o \
 	objects/terminal.o \
 	objects/testing.o \
+	objects/textobject.o \
 	objects/textprop.o \
 	objects/time.o \
 	objects/ui.o \
@@ -1996,6 +1998,7 @@ PRO_AUTO = \
 	terminal.pro \
 	termlib.pro \
 	testing.pro \
+	textobject.pro \
 	textprop.pro \
 	time.pro \
 	ui.pro \
@@ -3469,6 +3472,9 @@ objects/terminal.o: terminal.c $(TERM_DE
 objects/testing.o: testing.c
 	$(CCC) -o $@ testing.c
 
+objects/textobject.o: textobject.c
+	$(CCC) -o $@ textobject.c
+
 objects/textprop.o: textprop.c
 	$(CCC) -o $@ textprop.c
 
@@ -4060,6 +4066,10 @@ objects/testing.o: testing.c vim.h proto
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
  proto.h globals.h
+objects/textobject.o: textobject.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+ proto.h globals.h
 objects/textprop.o: textprop.c vim.h protodef.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
--- a/src/README.md
+++ b/src/README.md
@@ -80,6 +80,7 @@ syntax.c	| syntax and other highlighting
 tag.c		| tags
 term.c		| terminal handling, termcap codes
 testing.c	| testing: assert and test functions
+textobject.c	| text objects
 textprop.c	| text properties
 time.c		| time and timer functions
 undo.c		| undo and redo
--- a/src/proto.h
+++ b/src/proto.h
@@ -223,6 +223,7 @@ void mbyte_im_set_active(int active_arg)
 #  include "textprop.pro"
 # endif
 # include "testing.pro"
+# include "textobject.pro"
 # include "time.pro"
 # include "ui.pro"
 # include "undo.pro"
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -30,19 +30,6 @@ int searchc(cmdarg_T *cap, int t_cmd);
 pos_T *findmatch(oparg_T *oap, int initc);
 pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int maxtravel);
 void showmatch(int c);
-int findsent(int dir, long count);
-int findpar(int *pincl, int dir, long count, int what, int both);
-int startPS(linenr_T lnum, int para, int both);
-int fwd_word(long count, int bigword, int eol);
-int bck_word(long count, int bigword, int stop);
-int end_word(long count, int bigword, int stop, int empty);
-int bckend_word(long count, int bigword, int eol);
-int current_word(oparg_T *oap, long count, int include, int bigword);
-int current_sent(oparg_T *oap, long count, int include);
-int current_block(oparg_T *oap, long count, int include, int what, int other);
-int current_tagblock(oparg_T *oap, long count_arg, int include);
-int current_par(oparg_T *oap, long count, int include, int type);
-int current_quote(oparg_T *oap, long count, int include, int quotechar);
 int current_search(long count, int forward);
 int linewhite(linenr_T lnum);
 void find_pattern_in_path(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);
new file mode 100644
--- /dev/null
+++ b/src/proto/textobject.pro
@@ -0,0 +1,16 @@
+/* textobject.c */
+int findsent(int dir, long count);
+int findpar(int *pincl, int dir, long count, int what, int both);
+int startPS(linenr_T lnum, int para, int both);
+int fwd_word(long count, int bigword, int eol);
+int bck_word(long count, int bigword, int stop);
+int end_word(long count, int bigword, int stop, int empty);
+int bckend_word(long count, int bigword, int eol);
+int current_word(oparg_T *oap, long count, int include, int bigword);
+int current_sent(oparg_T *oap, long count, int include);
+int current_block(oparg_T *oap, long count, int include, int what, int other);
+int current_tagblock(oparg_T *oap, long count_arg, int include);
+int current_par(oparg_T *oap, long count, int include, int type);
+int current_quote(oparg_T *oap, long count, int include, int quotechar);
+/* vim: set ft=c : */
+
--- a/src/search.c
+++ b/src/search.c
@@ -17,8 +17,6 @@ static void set_vv_searchforward(void);
 static int first_submatch(regmmatch_T *rp);
 #endif
 static int check_linecomment(char_u *line);
-static int cls(void);
-static int skip_chars(int, int);
 #ifdef FEAT_FIND_ID
 static void show_pat_in_path(char_u *, int,
 					 int, int, FILE *, linenr_T *, long);
@@ -2838,1955 +2836,6 @@ showmatch(
 }
 
 /*
- * Find the start of the next sentence, searching in the direction specified
- * by the "dir" argument.  The cursor is positioned on the start of the next
- * sentence when found.  If the next sentence is found, return OK.  Return FAIL
- * otherwise.  See ":h sentence" for the precise definition of a "sentence"
- * text object.
- */
-    int
-findsent(int dir, long count)
-{
-    pos_T	pos, tpos;
-    int		c;
-    int		(*func)(pos_T *);
-    int		startlnum;
-    int		noskip = FALSE;	    // do not skip blanks
-    int		cpo_J;
-    int		found_dot;
-
-    pos = curwin->w_cursor;
-    if (dir == FORWARD)
-	func = incl;
-    else
-	func = decl;
-
-    while (count--)
-    {
-	/*
-	 * if on an empty line, skip up to a non-empty line
-	 */
-	if (gchar_pos(&pos) == NUL)
-	{
-	    do
-		if ((*func)(&pos) == -1)
-		    break;
-	    while (gchar_pos(&pos) == NUL);
-	    if (dir == FORWARD)
-		goto found;
-	}
-	/*
-	 * if on the start of a paragraph or a section and searching forward,
-	 * go to the next line
-	 */
-	else if (dir == FORWARD && pos.col == 0 &&
-						startPS(pos.lnum, NUL, FALSE))
-	{
-	    if (pos.lnum == curbuf->b_ml.ml_line_count)
-		return FAIL;
-	    ++pos.lnum;
-	    goto found;
-	}
-	else if (dir == BACKWARD)
-	    decl(&pos);
-
-	// go back to the previous non-white non-punctuation character
-	found_dot = FALSE;
-	while (c = gchar_pos(&pos), VIM_ISWHITE(c)
-				|| vim_strchr((char_u *)".!?)]\"'", c) != NULL)
-	{
-	    tpos = pos;
-	    if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD))
-		break;
-
-	    if (found_dot)
-		break;
-	    if (vim_strchr((char_u *) ".!?", c) != NULL)
-		found_dot = TRUE;
-
-	    if (vim_strchr((char_u *) ")]\"'", c) != NULL
-		&& vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL)
-		break;
-
-	    decl(&pos);
-	}
-
-	// remember the line where the search started
-	startlnum = pos.lnum;
-	cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
-
-	for (;;)		// find end of sentence
-	{
-	    c = gchar_pos(&pos);
-	    if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
-	    {
-		if (dir == BACKWARD && pos.lnum != startlnum)
-		    ++pos.lnum;
-		break;
-	    }
-	    if (c == '.' || c == '!' || c == '?')
-	    {
-		tpos = pos;
-		do
-		    if ((c = inc(&tpos)) == -1)
-			break;
-		while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
-			!= NULL);
-		if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
-		    || (cpo_J && (c == ' ' && inc(&tpos) >= 0
-			      && gchar_pos(&tpos) == ' ')))
-		{
-		    pos = tpos;
-		    if (gchar_pos(&pos) == NUL) // skip NUL at EOL
-			inc(&pos);
-		    break;
-		}
-	    }
-	    if ((*func)(&pos) == -1)
-	    {
-		if (count)
-		    return FAIL;
-		noskip = TRUE;
-		break;
-	    }
-	}
-found:
-	    // skip white space
-	while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
-	    if (incl(&pos) == -1)
-		break;
-    }
-
-    setpcmark();
-    curwin->w_cursor = pos;
-    return OK;
-}
-
-/*
- * Find the next paragraph or section in direction 'dir'.
- * Paragraphs are currently supposed to be separated by empty lines.
- * If 'what' is NUL we go to the next paragraph.
- * If 'what' is '{' or '}' we go to the next section.
- * If 'both' is TRUE also stop at '}'.
- * Return TRUE if the next paragraph or section was found.
- */
-    int
-findpar(
-    int		*pincl,	    // Return: TRUE if last char is to be included
-    int		dir,
-    long	count,
-    int		what,
-    int		both)
-{
-    linenr_T	curr;
-    int		did_skip;   // TRUE after separating lines have been skipped
-    int		first;	    // TRUE on first line
-    int		posix = (vim_strchr(p_cpo, CPO_PARA) != NULL);
-#ifdef FEAT_FOLDING
-    linenr_T	fold_first;	// first line of a closed fold
-    linenr_T	fold_last;	// last line of a closed fold
-    int		fold_skipped;	// TRUE if a closed fold was skipped this
-				// iteration
-#endif
-
-    curr = curwin->w_cursor.lnum;
-
-    while (count--)
-    {
-	did_skip = FALSE;
-	for (first = TRUE; ; first = FALSE)
-	{
-	    if (*ml_get(curr) != NUL)
-		did_skip = TRUE;
-
-#ifdef FEAT_FOLDING
-	    // skip folded lines
-	    fold_skipped = FALSE;
-	    if (first && hasFolding(curr, &fold_first, &fold_last))
-	    {
-		curr = ((dir > 0) ? fold_last : fold_first) + dir;
-		fold_skipped = TRUE;
-	    }
-#endif
-
-	    // POSIX has its own ideas of what a paragraph boundary is and it
-	    // doesn't match historical Vi: It also stops at a "{" in the
-	    // first column and at an empty line.
-	    if (!first && did_skip && (startPS(curr, what, both)
-			   || (posix && what == NUL && *ml_get(curr) == '{')))
-		break;
-
-#ifdef FEAT_FOLDING
-	    if (fold_skipped)
-		curr -= dir;
-#endif
-	    if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
-	    {
-		if (count)
-		    return FALSE;
-		curr -= dir;
-		break;
-	    }
-	}
-    }
-    setpcmark();
-    if (both && *ml_get(curr) == '}')	// include line with '}'
-	++curr;
-    curwin->w_cursor.lnum = curr;
-    if (curr == curbuf->b_ml.ml_line_count && what != '}')
-    {
-	char_u *line = ml_get(curr);
-
-	// Put the cursor on the last character in the last line and make the
-	// motion inclusive.
-	if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0)
-	{
-	    --curwin->w_cursor.col;
-	    curwin->w_cursor.col -=
-			     (*mb_head_off)(line, line + curwin->w_cursor.col);
-	    *pincl = TRUE;
-	}
-    }
-    else
-	curwin->w_cursor.col = 0;
-    return TRUE;
-}
-
-/*
- * check if the string 's' is a nroff macro that is in option 'opt'
- */
-    static int
-inmacro(char_u *opt, char_u *s)
-{
-    char_u	*macro;
-
-    for (macro = opt; macro[0]; ++macro)
-    {
-	// Accept two characters in the option being equal to two characters
-	// in the line.  A space in the option matches with a space in the
-	// line or the line having ended.
-	if (       (macro[0] == s[0]
-		    || (macro[0] == ' '
-			&& (s[0] == NUL || s[0] == ' ')))
-		&& (macro[1] == s[1]
-		    || ((macro[1] == NUL || macro[1] == ' ')
-			&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
-	    break;
-	++macro;
-	if (macro[0] == NUL)
-	    break;
-    }
-    return (macro[0] != NUL);
-}
-
-/*
- * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
- * If 'para' is '{' or '}' only check for sections.
- * If 'both' is TRUE also stop at '}'
- */
-    int
-startPS(linenr_T lnum, int para, int both)
-{
-    char_u	*s;
-
-    s = ml_get(lnum);
-    if (*s == para || *s == '\f' || (both && *s == '}'))
-	return TRUE;
-    if (*s == '.' && (inmacro(p_sections, s + 1) ||
-					   (!para && inmacro(p_para, s + 1))))
-	return TRUE;
-    return FALSE;
-}
-
-/*
- * The following routines do the word searches performed by the 'w', 'W',
- * 'b', 'B', 'e', and 'E' commands.
- */
-
-/*
- * To perform these searches, characters are placed into one of three
- * classes, and transitions between classes determine word boundaries.
- *
- * The classes are:
- *
- * 0 - white space
- * 1 - punctuation
- * 2 or higher - keyword characters (letters, digits and underscore)
- */
-
-static int	cls_bigword;	// TRUE for "W", "B" or "E"
-
-/*
- * cls() - returns the class of character at curwin->w_cursor
- *
- * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
- * from class 2 and higher are reported as class 1 since only white space
- * boundaries are of interest.
- */
-    static int
-cls(void)
-{
-    int	    c;
-
-    c = gchar_cursor();
-    if (c == ' ' || c == '\t' || c == NUL)
-	return 0;
-    if (enc_dbcs != 0 && c > 0xFF)
-    {
-	// If cls_bigword, report multi-byte chars as class 1.
-	if (enc_dbcs == DBCS_KOR && cls_bigword)
-	    return 1;
-
-	// process code leading/trailing bytes
-	return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
-    }
-    if (enc_utf8)
-    {
-	c = utf_class(c);
-	if (c != 0 && cls_bigword)
-	    return 1;
-	return c;
-    }
-
-    // If cls_bigword is TRUE, report all non-blanks as class 1.
-    if (cls_bigword)
-	return 1;
-
-    if (vim_iswordc(c))
-	return 2;
-    return 1;
-}
-
-
-/*
- * fwd_word(count, type, eol) - move forward one word
- *
- * Returns FAIL if the cursor was already at the end of the file.
- * If eol is TRUE, last word stops at end of line (for operators).
- */
-    int
-fwd_word(
-    long	count,
-    int		bigword,    // "W", "E" or "B"
-    int		eol)
-{
-    int		sclass;	    // starting class
-    int		i;
-    int		last_line;
-
-    curwin->w_cursor.coladd = 0;
-    cls_bigword = bigword;
-    while (--count >= 0)
-    {
-#ifdef FEAT_FOLDING
-	// When inside a range of folded lines, move to the last char of the
-	// last line.
-	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
-	    coladvance((colnr_T)MAXCOL);
-#endif
-	sclass = cls();
-
-	/*
-	 * We always move at least one character, unless on the last
-	 * character in the buffer.
-	 */
-	last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
-	i = inc_cursor();
-	if (i == -1 || (i >= 1 && last_line)) // started at last char in file
-	    return FAIL;
-	if (i >= 1 && eol && count == 0)      // started at last char in line
-	    return OK;
-
-	/*
-	 * Go one char past end of current word (if any)
-	 */
-	if (sclass != 0)
-	    while (cls() == sclass)
-	    {
-		i = inc_cursor();
-		if (i == -1 || (i >= 1 && eol && count == 0))
-		    return OK;
-	    }
-
-	/*
-	 * go to next non-white
-	 */
-	while (cls() == 0)
-	{
-	    /*
-	     * We'll stop if we land on a blank line
-	     */
-	    if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
-		break;
-
-	    i = inc_cursor();
-	    if (i == -1 || (i >= 1 && eol && count == 0))
-		return OK;
-	}
-    }
-    return OK;
-}
-
-/*
- * bck_word() - move backward 'count' words
- *
- * If stop is TRUE and we are already on the start of a word, move one less.
- *
- * Returns FAIL if top of the file was reached.
- */
-    int
-bck_word(long count, int bigword, int stop)
-{
-    int		sclass;	    // starting class
-
-    curwin->w_cursor.coladd = 0;
-    cls_bigword = bigword;
-    while (--count >= 0)
-    {
-#ifdef FEAT_FOLDING
-	// When inside a range of folded lines, move to the first char of the
-	// first line.
-	if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
-	    curwin->w_cursor.col = 0;
-#endif
-	sclass = cls();
-	if (dec_cursor() == -1)		// started at start of file
-	    return FAIL;
-
-	if (!stop || sclass == cls() || sclass == 0)
-	{
-	    /*
-	     * Skip white space before the word.
-	     * Stop on an empty line.
-	     */
-	    while (cls() == 0)
-	    {
-		if (curwin->w_cursor.col == 0
-				      && LINEEMPTY(curwin->w_cursor.lnum))
-		    goto finished;
-		if (dec_cursor() == -1) // hit start of file, stop here
-		    return OK;
-	    }
-
-	    /*
-	     * Move backward to start of this word.
-	     */
-	    if (skip_chars(cls(), BACKWARD))
-		return OK;
-	}
-
-	inc_cursor();			// overshot - forward one
-finished:
-	stop = FALSE;
-    }
-    return OK;
-}
-
-/*
- * end_word() - move to the end of the word
- *
- * There is an apparent bug in the 'e' motion of the real vi. At least on the
- * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
- * motion crosses blank lines. When the real vi crosses a blank line in an
- * 'e' motion, the cursor is placed on the FIRST character of the next
- * non-blank line. The 'E' command, however, works correctly. Since this
- * appears to be a bug, I have not duplicated it here.
- *
- * Returns FAIL if end of the file was reached.
- *
- * If stop is TRUE and we are already on the end of a word, move one less.
- * If empty is TRUE stop on an empty line.
- */
-    int
-end_word(
-    long	count,
-    int		bigword,
-    int		stop,
-    int		empty)
-{
-    int		sclass;	    // starting class
-
-    curwin->w_cursor.coladd = 0;
-    cls_bigword = bigword;
-    while (--count >= 0)
-    {
-#ifdef FEAT_FOLDING
-	// When inside a range of folded lines, move to the last char of the
-	// last line.
-	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
-	    coladvance((colnr_T)MAXCOL);
-#endif
-	sclass = cls();
-	if (inc_cursor() == -1)
-	    return FAIL;
-
-	/*
-	 * If we're in the middle of a word, we just have to move to the end
-	 * of it.
-	 */
-	if (cls() == sclass && sclass != 0)
-	{
-	    /*
-	     * Move forward to end of the current word
-	     */
-	    if (skip_chars(sclass, FORWARD))
-		return FAIL;
-	}
-	else if (!stop || sclass == 0)
-	{
-	    /*
-	     * We were at the end of a word. Go to the end of the next word.
-	     * First skip white space, if 'empty' is TRUE, stop at empty line.
-	     */
-	    while (cls() == 0)
-	    {
-		if (empty && curwin->w_cursor.col == 0
-					  && LINEEMPTY(curwin->w_cursor.lnum))
-		    goto finished;
-		if (inc_cursor() == -1)	    // hit end of file, stop here
-		    return FAIL;
-	    }
-
-	    /*
-	     * Move forward to the end of this word.
-	     */
-	    if (skip_chars(cls(), FORWARD))
-		return FAIL;
-	}
-	dec_cursor();			// overshot - one char backward
-finished:
-	stop = FALSE;			// we move only one word less
-    }
-    return OK;
-}
-
-/*
- * Move back to the end of the word.
- *
- * Returns FAIL if start of the file was reached.
- */
-    int
-bckend_word(
-    long	count,
-    int		bigword,    // TRUE for "B"
-    int		eol)	    // TRUE: stop at end of line.
-{
-    int		sclass;	    // starting class
-    int		i;
-
-    curwin->w_cursor.coladd = 0;
-    cls_bigword = bigword;
-    while (--count >= 0)
-    {
-	sclass = cls();
-	if ((i = dec_cursor()) == -1)
-	    return FAIL;
-	if (eol && i == 1)
-	    return OK;
-
-	/*
-	 * Move backward to before the start of this word.
-	 */
-	if (sclass != 0)
-	{
-	    while (cls() == sclass)
-		if ((i = dec_cursor()) == -1 || (eol && i == 1))
-		    return OK;
-	}
-
-	/*
-	 * Move backward to end of the previous word
-	 */
-	while (cls() == 0)
-	{
-	    if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum))
-		break;
-	    if ((i = dec_cursor()) == -1 || (eol && i == 1))
-		return OK;
-	}
-    }
-    return OK;
-}
-
-/*
- * Skip a row of characters of the same class.
- * Return TRUE when end-of-file reached, FALSE otherwise.
- */
-    static int
-skip_chars(int cclass, int dir)
-{
-    while (cls() == cclass)
-	if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
-	    return TRUE;
-    return FALSE;
-}
-
-#ifdef FEAT_TEXTOBJ
-/*
- * Go back to the start of the word or the start of white space
- */
-    static void
-back_in_line(void)
-{
-    int		sclass;		    // starting class
-
-    sclass = cls();
-    for (;;)
-    {
-	if (curwin->w_cursor.col == 0)	    // stop at start of line
-	    break;
-	dec_cursor();
-	if (cls() != sclass)		    // stop at start of word
-	{
-	    inc_cursor();
-	    break;
-	}
-    }
-}
-
-    static void
-find_first_blank(pos_T *posp)
-{
-    int	    c;
-
-    while (decl(posp) != -1)
-    {
-	c = gchar_pos(posp);
-	if (!VIM_ISWHITE(c))
-	{
-	    incl(posp);
-	    break;
-	}
-    }
-}
-
-/*
- * Skip count/2 sentences and count/2 separating white spaces.
- */
-    static void
-findsent_forward(
-    long    count,
-    int	    at_start_sent)	// cursor is at start of sentence
-{
-    while (count--)
-    {
-	findsent(FORWARD, 1L);
-	if (at_start_sent)
-	    find_first_blank(&curwin->w_cursor);
-	if (count == 0 || at_start_sent)
-	    decl(&curwin->w_cursor);
-	at_start_sent = !at_start_sent;
-    }
-}
-
-/*
- * Find word under cursor, cursor at end.
- * Used while an operator is pending, and in Visual mode.
- */
-    int
-current_word(
-    oparg_T	*oap,
-    long	count,
-    int		include,	// TRUE: include word and white space
-    int		bigword)	// FALSE == word, TRUE == WORD
-{
-    pos_T	start_pos;
-    pos_T	pos;
-    int		inclusive = TRUE;
-    int		include_white = FALSE;
-
-    cls_bigword = bigword;
-    CLEAR_POS(&start_pos);
-
-    // Correct cursor when 'selection' is exclusive
-    if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
-	dec_cursor();
-
-    /*
-     * When Visual mode is not active, or when the VIsual area is only one
-     * character, select the word and/or white space under the cursor.
-     */
-    if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual))
-    {
-	/*
-	 * Go to start of current word or white space.
-	 */
-	back_in_line();
-	start_pos = curwin->w_cursor;
-
-	/*
-	 * If the start is on white space, and white space should be included
-	 * ("	word"), or start is not on white space, and white space should
-	 * not be included ("word"), find end of word.
-	 */
-	if ((cls() == 0) == include)
-	{
-	    if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
-		return FAIL;
-	}
-	else
-	{
-	    /*
-	     * If the start is not on white space, and white space should be
-	     * included ("word	 "), or start is on white space and white
-	     * space should not be included ("	 "), find start of word.
-	     * If we end up in the first column of the next line (single char
-	     * word) back up to end of the line.
-	     */
-	    fwd_word(1L, bigword, TRUE);
-	    if (curwin->w_cursor.col == 0)
-		decl(&curwin->w_cursor);
-	    else
-		oneleft();
-
-	    if (include)
-		include_white = TRUE;
-	}
-
-	if (VIsual_active)
-	{
-	    // should do something when inclusive == FALSE !
-	    VIsual = start_pos;
-	    redraw_curbuf_later(INVERTED);	// update the inversion
-	}
-	else
-	{
-	    oap->start = start_pos;
-	    oap->motion_type = MCHAR;
-	}
-	--count;
-    }
-
-    /*
-     * When count is still > 0, extend with more objects.
-     */
-    while (count > 0)
-    {
-	inclusive = TRUE;
-	if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
-	{
-	    /*
-	     * In Visual mode, with cursor at start: move cursor back.
-	     */
-	    if (decl(&curwin->w_cursor) == -1)
-		return FAIL;
-	    if (include != (cls() != 0))
-	    {
-		if (bck_word(1L, bigword, TRUE) == FAIL)
-		    return FAIL;
-	    }
-	    else
-	    {
-		if (bckend_word(1L, bigword, TRUE) == FAIL)
-		    return FAIL;
-		(void)incl(&curwin->w_cursor);
-	    }
-	}
-	else
-	{
-	    /*
-	     * Move cursor forward one word and/or white area.
-	     */
-	    if (incl(&curwin->w_cursor) == -1)
-		return FAIL;
-	    if (include != (cls() == 0))
-	    {
-		if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1)
-		    return FAIL;
-		/*
-		 * If end is just past a new-line, we don't want to include
-		 * the first character on the line.
-		 * Put cursor on last char of white.
-		 */
-		if (oneleft() == FAIL)
-		    inclusive = FALSE;
-	    }
-	    else
-	    {
-		if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
-		    return FAIL;
-	    }
-	}
-	--count;
-    }
-
-    if (include_white && (cls() != 0
-		 || (curwin->w_cursor.col == 0 && !inclusive)))
-    {
-	/*
-	 * If we don't include white space at the end, move the start
-	 * to include some white space there. This makes "daw" work
-	 * better on the last word in a sentence (and "2daw" on last-but-one
-	 * word).  Also when "2daw" deletes "word." at the end of the line
-	 * (cursor is at start of next line).
-	 * But don't delete white space at start of line (indent).
-	 */
-	pos = curwin->w_cursor;	// save cursor position
-	curwin->w_cursor = start_pos;
-	if (oneleft() == OK)
-	{
-	    back_in_line();
-	    if (cls() == 0 && curwin->w_cursor.col > 0)
-	    {
-		if (VIsual_active)
-		    VIsual = curwin->w_cursor;
-		else
-		    oap->start = curwin->w_cursor;
-	    }
-	}
-	curwin->w_cursor = pos;	// put cursor back at end
-    }
-
-    if (VIsual_active)
-    {
-	if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor))
-	    inc_cursor();
-	if (VIsual_mode == 'V')
-	{
-	    VIsual_mode = 'v';
-	    redraw_cmdline = TRUE;		// show mode later
-	}
-    }
-    else
-	oap->inclusive = inclusive;
-
-    return OK;
-}
-
-/*
- * Find sentence(s) under the cursor, cursor at end.
- * When Visual active, extend it by one or more sentences.
- */
-    int
-current_sent(oparg_T *oap, long count, int include)
-{
-    pos_T	start_pos;
-    pos_T	pos;
-    int		start_blank;
-    int		c;
-    int		at_start_sent;
-    long	ncount;
-
-    start_pos = curwin->w_cursor;
-    pos = start_pos;
-    findsent(FORWARD, 1L);	// Find start of next sentence.
-
-    /*
-     * When the Visual area is bigger than one character: Extend it.
-     */
-    if (VIsual_active && !EQUAL_POS(start_pos, VIsual))
-    {
-extend:
-	if (LT_POS(start_pos, VIsual))
-	{
-	    /*
-	     * Cursor at start of Visual area.
-	     * Find out where we are:
-	     * - in the white space before a sentence
-	     * - in a sentence or just after it
-	     * - at the start of a sentence
-	     */
-	    at_start_sent = TRUE;
-	    decl(&pos);
-	    while (LT_POS(pos, curwin->w_cursor))
-	    {
-		c = gchar_pos(&pos);
-		if (!VIM_ISWHITE(c))
-		{
-		    at_start_sent = FALSE;
-		    break;
-		}
-		incl(&pos);
-	    }
-	    if (!at_start_sent)
-	    {
-		findsent(BACKWARD, 1L);
-		if (EQUAL_POS(curwin->w_cursor, start_pos))
-		    at_start_sent = TRUE;  // exactly at start of sentence
-		else
-		    // inside a sentence, go to its end (start of next)
-		    findsent(FORWARD, 1L);
-	    }
-	    if (include)	// "as" gets twice as much as "is"
-		count *= 2;
-	    while (count--)
-	    {
-		if (at_start_sent)
-		    find_first_blank(&curwin->w_cursor);
-		c = gchar_cursor();
-		if (!at_start_sent || (!include && !VIM_ISWHITE(c)))
-		    findsent(BACKWARD, 1L);
-		at_start_sent = !at_start_sent;
-	    }
-	}
-	else
-	{
-	    /*
-	     * Cursor at end of Visual area.
-	     * Find out where we are:
-	     * - just before a sentence
-	     * - just before or in the white space before a sentence
-	     * - in a sentence
-	     */
-	    incl(&pos);
-	    at_start_sent = TRUE;
-	    // not just before a sentence
-	    if (!EQUAL_POS(pos, curwin->w_cursor))
-	    {
-		at_start_sent = FALSE;
-		while (LT_POS(pos, curwin->w_cursor))
-		{
-		    c = gchar_pos(&pos);
-		    if (!VIM_ISWHITE(c))
-		    {
-			at_start_sent = TRUE;
-			break;
-		    }
-		    incl(&pos);
-		}
-		if (at_start_sent)	// in the sentence
-		    findsent(BACKWARD, 1L);
-		else		// in/before white before a sentence
-		    curwin->w_cursor = start_pos;
-	    }
-
-	    if (include)	// "as" gets twice as much as "is"
-		count *= 2;
-	    findsent_forward(count, at_start_sent);
-	    if (*p_sel == 'e')
-		++curwin->w_cursor.col;
-	}
-	return OK;
-    }
-
-    /*
-     * If the cursor started on a blank, check if it is just before the start
-     * of the next sentence.
-     */
-    while (c = gchar_pos(&pos), VIM_ISWHITE(c))	// VIM_ISWHITE() is a macro
-	incl(&pos);
-    if (EQUAL_POS(pos, curwin->w_cursor))
-    {
-	start_blank = TRUE;
-	find_first_blank(&start_pos);	// go back to first blank
-    }
-    else
-    {
-	start_blank = FALSE;
-	findsent(BACKWARD, 1L);
-	start_pos = curwin->w_cursor;
-    }
-    if (include)
-	ncount = count * 2;
-    else
-    {
-	ncount = count;
-	if (start_blank)
-	    --ncount;
-    }
-    if (ncount > 0)
-	findsent_forward(ncount, TRUE);
-    else
-	decl(&curwin->w_cursor);
-
-    if (include)
-    {
-	/*
-	 * If the blank in front of the sentence is included, exclude the
-	 * blanks at the end of the sentence, go back to the first blank.
-	 * If there are no trailing blanks, try to include leading blanks.
-	 */
-	if (start_blank)
-	{
-	    find_first_blank(&curwin->w_cursor);
-	    c = gchar_pos(&curwin->w_cursor);	// VIM_ISWHITE() is a macro
-	    if (VIM_ISWHITE(c))
-		decl(&curwin->w_cursor);
-	}
-	else if (c = gchar_cursor(), !VIM_ISWHITE(c))
-	    find_first_blank(&start_pos);
-    }
-
-    if (VIsual_active)
-    {
-	// Avoid getting stuck with "is" on a single space before a sentence.
-	if (EQUAL_POS(start_pos, curwin->w_cursor))
-	    goto extend;
-	if (*p_sel == 'e')
-	    ++curwin->w_cursor.col;
-	VIsual = start_pos;
-	VIsual_mode = 'v';
-	redraw_cmdline = TRUE;		// show mode later
-	redraw_curbuf_later(INVERTED);	// update the inversion
-    }
-    else
-    {
-	// include a newline after the sentence, if there is one
-	if (incl(&curwin->w_cursor) == -1)
-	    oap->inclusive = TRUE;
-	else
-	    oap->inclusive = FALSE;
-	oap->start = start_pos;
-	oap->motion_type = MCHAR;
-    }
-    return OK;
-}
-
-/*
- * Find block under the cursor, cursor at end.
- * "what" and "other" are two matching parenthesis/brace/etc.
- */
-    int
-current_block(
-    oparg_T	*oap,
-    long	count,
-    int		include,	// TRUE == include white space
-    int		what,		// '(', '{', etc.
-    int		other)		// ')', '}', etc.
-{
-    pos_T	old_pos;
-    pos_T	*pos = NULL;
-    pos_T	start_pos;
-    pos_T	*end_pos;
-    pos_T	old_start, old_end;
-    char_u	*save_cpo;
-    int		sol = FALSE;		// '{' at start of line
-
-    old_pos = curwin->w_cursor;
-    old_end = curwin->w_cursor;		// remember where we started
-    old_start = old_end;
-
-    /*
-     * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
-     */
-    if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
-    {
-	setpcmark();
-	if (what == '{')		// ignore indent
-	    while (inindent(1))
-		if (inc_cursor() != 0)
-		    break;
-	if (gchar_cursor() == what)
-	    // cursor on '(' or '{', move cursor just after it
-	    ++curwin->w_cursor.col;
-    }
-    else if (LT_POS(VIsual, curwin->w_cursor))
-    {
-	old_start = VIsual;
-	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
-    }
-    else
-	old_end = VIsual;
-
-    /*
-     * Search backwards for unclosed '(', '{', etc..
-     * Put this position in start_pos.
-     * Ignore quotes here.  Keep the "M" flag in 'cpo', as that is what the
-     * user wants.
-     */
-    save_cpo = p_cpo;
-    p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
-    while (count-- > 0)
-    {
-	if ((pos = findmatch(NULL, what)) == NULL)
-	    break;
-	curwin->w_cursor = *pos;
-	start_pos = *pos;   // the findmatch for end_pos will overwrite *pos
-    }
-    p_cpo = save_cpo;
-
-    /*
-     * Search for matching ')', '}', etc.
-     * Put this position in curwin->w_cursor.
-     */
-    if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
-    {
-	curwin->w_cursor = old_pos;
-	return FAIL;
-    }
-    curwin->w_cursor = *end_pos;
-
-    /*
-     * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
-     * If the ending '}', ')' or ']' is only preceded by indent, skip that
-     * indent.  But only if the resulting area is not smaller than what we
-     * started with.
-     */
-    while (!include)
-    {
-	incl(&start_pos);
-	sol = (curwin->w_cursor.col == 0);
-	decl(&curwin->w_cursor);
-	while (inindent(1))
-	{
-	    sol = TRUE;
-	    if (decl(&curwin->w_cursor) != 0)
-		break;
-	}
-
-	/*
-	 * In Visual mode, when the resulting area is not bigger than what we
-	 * started with, extend it to the next block, and then exclude again.
-	 */
-	if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor)
-		&& VIsual_active)
-	{
-	    curwin->w_cursor = old_start;
-	    decl(&curwin->w_cursor);
-	    if ((pos = findmatch(NULL, what)) == NULL)
-	    {
-		curwin->w_cursor = old_pos;
-		return FAIL;
-	    }
-	    start_pos = *pos;
-	    curwin->w_cursor = *pos;
-	    if ((end_pos = findmatch(NULL, other)) == NULL)
-	    {
-		curwin->w_cursor = old_pos;
-		return FAIL;
-	    }
-	    curwin->w_cursor = *end_pos;
-	}
-	else
-	    break;
-    }
-
-    if (VIsual_active)
-    {
-	if (*p_sel == 'e')
-	    inc(&curwin->w_cursor);
-	if (sol && gchar_cursor() != NUL)
-	    inc(&curwin->w_cursor);	// include the line break
-	VIsual = start_pos;
-	VIsual_mode = 'v';
-	redraw_curbuf_later(INVERTED);	// update the inversion
-	showmode();
-    }
-    else
-    {
-	oap->start = start_pos;
-	oap->motion_type = MCHAR;
-	oap->inclusive = FALSE;
-	if (sol)
-	    incl(&curwin->w_cursor);
-	else if (LTOREQ_POS(start_pos, curwin->w_cursor))
-	    // Include the character under the cursor.
-	    oap->inclusive = TRUE;
-	else
-	    // End is before the start (no text in between <>, [], etc.): don't
-	    // operate on any text.
-	    curwin->w_cursor = start_pos;
-    }
-
-    return OK;
-}
-
-/*
- * Return TRUE if the cursor is on a "<aaa>" tag.  Ignore "<aaa/>".
- * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>".
- */
-    static int
-in_html_tag(
-    int		end_tag)
-{
-    char_u	*line = ml_get_curline();
-    char_u	*p;
-    int		c;
-    int		lc = NUL;
-    pos_T	pos;
-
-    if (enc_dbcs)
-    {
-	char_u	*lp = NULL;
-
-	// We search forward until the cursor, because searching backwards is
-	// very slow for DBCS encodings.
-	for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p))
-	    if (*p == '>' || *p == '<')
-	    {
-		lc = *p;
-		lp = p;
-	    }
-	if (*p != '<')	    // check for '<' under cursor
-	{
-	    if (lc != '<')
-		return FALSE;
-	    p = lp;
-	}
-    }
-    else
-    {
-	for (p = line + curwin->w_cursor.col; p > line; )
-	{
-	    if (*p == '<')	// find '<' under/before cursor
-		break;
-	    MB_PTR_BACK(line, p);
-	    if (*p == '>')	// find '>' before cursor
-		break;
-	}
-	if (*p != '<')
-	    return FALSE;
-    }
-
-    pos.lnum = curwin->w_cursor.lnum;
-    pos.col = (colnr_T)(p - line);
-
-    MB_PTR_ADV(p);
-    if (end_tag)
-	// check that there is a '/' after the '<'
-	return *p == '/';
-
-    // check that there is no '/' after the '<'
-    if (*p == '/')
-	return FALSE;
-
-    // check that the matching '>' is not preceded by '/'
-    for (;;)
-    {
-	if (inc(&pos) < 0)
-	    return FALSE;
-	c = *ml_get_pos(&pos);
-	if (c == '>')
-	    break;
-	lc = c;
-    }
-    return lc != '/';
-}
-
-/*
- * Find tag block under the cursor, cursor at end.
- */
-    int
-current_tagblock(
-    oparg_T	*oap,
-    long	count_arg,
-    int		include)	// TRUE == include white space
-{
-    long	count = count_arg;
-    long	n;
-    pos_T	old_pos;
-    pos_T	start_pos;
-    pos_T	end_pos;
-    pos_T	old_start, old_end;
-    char_u	*spat, *epat;
-    char_u	*p;
-    char_u	*cp;
-    int		len;
-    int		r;
-    int		do_include = include;
-    int		save_p_ws = p_ws;
-    int		retval = FAIL;
-    int		is_inclusive = TRUE;
-
-    p_ws = FALSE;
-
-    old_pos = curwin->w_cursor;
-    old_end = curwin->w_cursor;		    // remember where we started
-    old_start = old_end;
-    if (!VIsual_active || *p_sel == 'e')
-	decl(&old_end);			    // old_end is inclusive
-
-    /*
-     * If we start on "<aaa>" select that block.
-     */
-    if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
-    {
-	setpcmark();
-
-	// ignore indent
-	while (inindent(1))
-	    if (inc_cursor() != 0)
-		break;
-
-	if (in_html_tag(FALSE))
-	{
-	    // cursor on start tag, move to its '>'
-	    while (*ml_get_cursor() != '>')
-		if (inc_cursor() < 0)
-		    break;
-	}
-	else if (in_html_tag(TRUE))
-	{
-	    // cursor on end tag, move to just before it
-	    while (*ml_get_cursor() != '<')
-		if (dec_cursor() < 0)
-		    break;
-	    dec_cursor();
-	    old_end = curwin->w_cursor;
-	}
-    }
-    else if (LT_POS(VIsual, curwin->w_cursor))
-    {
-	old_start = VIsual;
-	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
-    }
-    else
-	old_end = VIsual;
-
-again:
-    /*
-     * Search backwards for unclosed "<aaa>".
-     * Put this position in start_pos.
-     */
-    for (n = 0; n < count; ++n)
-    {
-	if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
-		    (char_u *)"",
-		    (char_u *)"</[^>]*>", BACKWARD, NULL, 0,
-						  NULL, (linenr_T)0, 0L) <= 0)
-	{
-	    curwin->w_cursor = old_pos;
-	    goto theend;
-	}
-    }
-    start_pos = curwin->w_cursor;
-
-    /*
-     * Search for matching "</aaa>".  First isolate the "aaa".
-     */
-    inc_cursor();
-    p = ml_get_cursor();
-    for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp))
-	;
-    len = (int)(cp - p);
-    if (len == 0)
-    {
-	curwin->w_cursor = old_pos;
-	goto theend;
-    }
-    spat = alloc(len + 31);
-    epat = alloc(len + 9);
-    if (spat == NULL || epat == NULL)
-    {
-	vim_free(spat);
-	vim_free(epat);
-	curwin->w_cursor = old_pos;
-	goto theend;
-    }
-    sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p);
-    sprintf((char *)epat, "</%.*s>\\c", len, p);
-
-    r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL,
-						    0, NULL, (linenr_T)0, 0L);
-
-    vim_free(spat);
-    vim_free(epat);
-
-    if (r < 1 || LT_POS(curwin->w_cursor, old_end))
-    {
-	// Can't find other end or it's before the previous end.  Could be a
-	// HTML tag that doesn't have a matching end.  Search backwards for
-	// another starting tag.
-	count = 1;
-	curwin->w_cursor = start_pos;
-	goto again;
-    }
-
-    if (do_include)
-    {
-	// Include up to the '>'.
-	while (*ml_get_cursor() != '>')
-	    if (inc_cursor() < 0)
-		break;
-    }
-    else
-    {
-	char_u *c = ml_get_cursor();
-
-	// Exclude the '<' of the end tag.
-	// If the closing tag is on new line, do not decrement cursor, but
-	// make operation exclusive, so that the linefeed will be selected
-	if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0)
-	    // do not decrement cursor
-	    is_inclusive = FALSE;
-	else if (*c == '<')
-	    dec_cursor();
-    }
-    end_pos = curwin->w_cursor;
-
-    if (!do_include)
-    {
-	// Exclude the start tag.
-	curwin->w_cursor = start_pos;
-	while (inc_cursor() >= 0)
-	    if (*ml_get_cursor() == '>')
-	    {
-		inc_cursor();
-		start_pos = curwin->w_cursor;
-		break;
-	    }
-	curwin->w_cursor = end_pos;
-
-	// If we are in Visual mode and now have the same text as before set
-	// "do_include" and try again.
-	if (VIsual_active && EQUAL_POS(start_pos, old_start)
-						&& EQUAL_POS(end_pos, old_end))
-	{
-	    do_include = TRUE;
-	    curwin->w_cursor = old_start;
-	    count = count_arg;
-	    goto again;
-	}
-    }
-
-    if (VIsual_active)
-    {
-	// If the end is before the start there is no text between tags, select
-	// the char under the cursor.
-	if (LT_POS(end_pos, start_pos))
-	    curwin->w_cursor = start_pos;
-	else if (*p_sel == 'e')
-	    inc_cursor();
-	VIsual = start_pos;
-	VIsual_mode = 'v';
-	redraw_curbuf_later(INVERTED);	// update the inversion
-	showmode();
-    }
-    else
-    {
-	oap->start = start_pos;
-	oap->motion_type = MCHAR;
-	if (LT_POS(end_pos, start_pos))
-	{
-	    // End is before the start: there is no text between tags; operate
-	    // on an empty area.
-	    curwin->w_cursor = start_pos;
-	    oap->inclusive = FALSE;
-	}
-	else
-	    oap->inclusive = is_inclusive;
-    }
-    retval = OK;
-
-theend:
-    p_ws = save_p_ws;
-    return retval;
-}
-
-    int
-current_par(
-    oparg_T	*oap,
-    long	count,
-    int		include,	// TRUE == include white space
-    int		type)		// 'p' for paragraph, 'S' for section
-{
-    linenr_T	start_lnum;
-    linenr_T	end_lnum;
-    int		white_in_front;
-    int		dir;
-    int		start_is_white;
-    int		prev_start_is_white;
-    int		retval = OK;
-    int		do_white = FALSE;
-    int		t;
-    int		i;
-
-    if (type == 'S')	    // not implemented yet
-	return FAIL;
-
-    start_lnum = curwin->w_cursor.lnum;
-
-    /*
-     * When visual area is more than one line: extend it.
-     */
-    if (VIsual_active && start_lnum != VIsual.lnum)
-    {
-extend:
-	if (start_lnum < VIsual.lnum)
-	    dir = BACKWARD;
-	else
-	    dir = FORWARD;
-	for (i = count; --i >= 0; )
-	{
-	    if (start_lnum ==
-			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
-	    {
-		retval = FAIL;
-		break;
-	    }
-
-	    prev_start_is_white = -1;
-	    for (t = 0; t < 2; ++t)
-	    {
-		start_lnum += dir;
-		start_is_white = linewhite(start_lnum);
-		if (prev_start_is_white == start_is_white)
-		{
-		    start_lnum -= dir;
-		    break;
-		}
-		for (;;)
-		{
-		    if (start_lnum == (dir == BACKWARD
-					    ? 1 : curbuf->b_ml.ml_line_count))
-			break;
-		    if (start_is_white != linewhite(start_lnum + dir)
-			    || (!start_is_white
-				    && startPS(start_lnum + (dir > 0
-							     ? 1 : 0), 0, 0)))
-			break;
-		    start_lnum += dir;
-		}
-		if (!include)
-		    break;
-		if (start_lnum == (dir == BACKWARD
-					    ? 1 : curbuf->b_ml.ml_line_count))
-		    break;
-		prev_start_is_white = start_is_white;
-	    }
-	}
-	curwin->w_cursor.lnum = start_lnum;
-	curwin->w_cursor.col = 0;
-	return retval;
-    }
-
-    /*
-     * First move back to the start_lnum of the paragraph or white lines
-     */
-    white_in_front = linewhite(start_lnum);
-    while (start_lnum > 1)
-    {
-	if (white_in_front)	    // stop at first white line
-	{
-	    if (!linewhite(start_lnum - 1))
-		break;
-	}
-	else		// stop at first non-white line of start of paragraph
-	{
-	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
-		break;
-	}
-	--start_lnum;
-    }
-
-    /*
-     * Move past the end of any white lines.
-     */
-    end_lnum = start_lnum;
-    while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum))
-	++end_lnum;
-
-    --end_lnum;
-    i = count;
-    if (!include && white_in_front)
-	--i;
-    while (i--)
-    {
-	if (end_lnum == curbuf->b_ml.ml_line_count)
-	    return FAIL;
-
-	if (!include)
-	    do_white = linewhite(end_lnum + 1);
-
-	if (include || !do_white)
-	{
-	    ++end_lnum;
-	    /*
-	     * skip to end of paragraph
-	     */
-	    while (end_lnum < curbuf->b_ml.ml_line_count
-		    && !linewhite(end_lnum + 1)
-		    && !startPS(end_lnum + 1, 0, 0))
-		++end_lnum;
-	}
-
-	if (i == 0 && white_in_front && include)
-	    break;
-
-	/*
-	 * skip to end of white lines after paragraph
-	 */
-	if (include || do_white)
-	    while (end_lnum < curbuf->b_ml.ml_line_count
-						   && linewhite(end_lnum + 1))
-		++end_lnum;
-    }
-
-    /*
-     * If there are no empty lines at the end, try to find some empty lines at
-     * the start (unless that has been done already).
-     */
-    if (!white_in_front && !linewhite(end_lnum) && include)
-	while (start_lnum > 1 && linewhite(start_lnum - 1))
-	    --start_lnum;
-
-    if (VIsual_active)
-    {
-	// Problem: when doing "Vipipip" nothing happens in a single white
-	// line, we get stuck there.  Trap this here.
-	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
-	    goto extend;
-	if (VIsual.lnum != start_lnum)
-	{
-	    VIsual.lnum = start_lnum;
-	    VIsual.col = 0;
-	}
-	VIsual_mode = 'V';
-	redraw_curbuf_later(INVERTED);	// update the inversion
-	showmode();
-    }
-    else
-    {
-	oap->start.lnum = start_lnum;
-	oap->start.col = 0;
-	oap->motion_type = MLINE;
-    }
-    curwin->w_cursor.lnum = end_lnum;
-    curwin->w_cursor.col = 0;
-
-    return OK;
-}
-
-/*
- * Search quote char from string line[col].
- * Quote character escaped by one of the characters in "escape" is not counted
- * as a quote.
- * Returns column number of "quotechar" or -1 when not found.
- */
-    static int
-find_next_quote(
-    char_u	*line,
-    int		col,
-    int		quotechar,
-    char_u	*escape)	// escape characters, can be NULL
-{
-    int		c;
-
-    for (;;)
-    {
-	c = line[col];
-	if (c == NUL)
-	    return -1;
-	else if (escape != NULL && vim_strchr(escape, c))
-	    ++col;
-	else if (c == quotechar)
-	    break;
-	if (has_mbyte)
-	    col += (*mb_ptr2len)(line + col);
-	else
-	    ++col;
-    }
-    return col;
-}
-
-/*
- * Search backwards in "line" from column "col_start" to find "quotechar".
- * Quote character escaped by one of the characters in "escape" is not counted
- * as a quote.
- * Return the found column or zero.
- */
-    static int
-find_prev_quote(
-    char_u	*line,
-    int		col_start,
-    int		quotechar,
-    char_u	*escape)	// escape characters, can be NULL
-{
-    int		n;
-
-    while (col_start > 0)
-    {
-	--col_start;
-	col_start -= (*mb_head_off)(line, line + col_start);
-	n = 0;
-	if (escape != NULL)
-	    while (col_start - n > 0 && vim_strchr(escape,
-					     line[col_start - n - 1]) != NULL)
-	    ++n;
-	if (n & 1)
-	    col_start -= n;	// uneven number of escape chars, skip it
-	else if (line[col_start] == quotechar)
-	    break;
-    }
-    return col_start;
-}
-
-/*
- * Find quote under the cursor, cursor at end.
- * Returns TRUE if found, else FALSE.
- */
-    int
-current_quote(
-    oparg_T	*oap,
-    long	count,
-    int		include,	// TRUE == include quote char
-    int		quotechar)	// Quote character
-{
-    char_u	*line = ml_get_curline();
-    int		col_end;
-    int		col_start = curwin->w_cursor.col;
-    int		inclusive = FALSE;
-    int		vis_empty = TRUE;	// Visual selection <= 1 char
-    int		vis_bef_curs = FALSE;	// Visual starts before cursor
-    int		did_exclusive_adj = FALSE;  // adjusted pos for 'selection'
-    int		inside_quotes = FALSE;	// Looks like "i'" done before
-    int		selected_quote = FALSE;	// Has quote inside selection
-    int		i;
-    int		restore_vis_bef = FALSE; // restore VIsual on abort
-
-    // When 'selection' is "exclusive" move the cursor to where it would be
-    // with 'selection' "inclusive", so that the logic is the same for both.
-    // The cursor then is moved forward after adjusting the area.
-    if (VIsual_active)
-    {
-	// this only works within one line
-	if (VIsual.lnum != curwin->w_cursor.lnum)
-	    return FALSE;
-
-	vis_bef_curs = LT_POS(VIsual, curwin->w_cursor);
-	vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
-	if (*p_sel == 'e')
-	{
-	    if (vis_bef_curs)
-	    {
-		dec_cursor();
-		did_exclusive_adj = TRUE;
-	    }
-	    else if (!vis_empty)
-	    {
-		dec(&VIsual);
-		did_exclusive_adj = TRUE;
-	    }
-	    vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
-	    if (!vis_bef_curs && !vis_empty)
-	    {
-		// VIsual needs to be the start of Visual selection.
-		pos_T t = curwin->w_cursor;
-
-		curwin->w_cursor = VIsual;
-		VIsual = t;
-		vis_bef_curs = TRUE;
-		restore_vis_bef = TRUE;
-	    }
-	}
-    }
-
-    if (!vis_empty)
-    {
-	// Check if the existing selection exactly spans the text inside
-	// quotes.
-	if (vis_bef_curs)
-	{
-	    inside_quotes = VIsual.col > 0
-			&& line[VIsual.col - 1] == quotechar
-			&& line[curwin->w_cursor.col] != NUL
-			&& line[curwin->w_cursor.col + 1] == quotechar;
-	    i = VIsual.col;
-	    col_end = curwin->w_cursor.col;
-	}
-	else
-	{
-	    inside_quotes = curwin->w_cursor.col > 0
-			&& line[curwin->w_cursor.col - 1] == quotechar
-			&& line[VIsual.col] != NUL
-			&& line[VIsual.col + 1] == quotechar;
-	    i = curwin->w_cursor.col;
-	    col_end = VIsual.col;
-	}
-
-	// Find out if we have a quote in the selection.
-	while (i <= col_end)
-	    if (line[i++] == quotechar)
-	    {
-		selected_quote = TRUE;
-		break;
-	    }
-    }
-
-    if (!vis_empty && line[col_start] == quotechar)
-    {
-	// Already selecting something and on a quote character.  Find the
-	// next quoted string.
-	if (vis_bef_curs)
-	{
-	    // Assume we are on a closing quote: move to after the next
-	    // opening quote.
-	    col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
-	    if (col_start < 0)
-		goto abort_search;
-	    col_end = find_next_quote(line, col_start + 1, quotechar,
-							      curbuf->b_p_qe);
-	    if (col_end < 0)
-	    {
-		// We were on a starting quote perhaps?
-		col_end = col_start;
-		col_start = curwin->w_cursor.col;
-	    }
-	}
-	else
-	{
-	    col_end = find_prev_quote(line, col_start, quotechar, NULL);
-	    if (line[col_end] != quotechar)
-		goto abort_search;
-	    col_start = find_prev_quote(line, col_end, quotechar,
-							      curbuf->b_p_qe);
-	    if (line[col_start] != quotechar)
-	    {
-		// We were on an ending quote perhaps?
-		col_start = col_end;
-		col_end = curwin->w_cursor.col;
-	    }
-	}
-    }
-    else
-
-    if (line[col_start] == quotechar || !vis_empty)
-    {
-	int	first_col = col_start;
-
-	if (!vis_empty)
-	{
-	    if (vis_bef_curs)
-		first_col = find_next_quote(line, col_start, quotechar, NULL);
-	    else
-		first_col = find_prev_quote(line, col_start, quotechar, NULL);
-	}
-
-	// The cursor is on a quote, we don't know if it's the opening or
-	// closing quote.  Search from the start of the line to find out.
-	// Also do this when there is a Visual area, a' may leave the cursor
-	// in between two strings.
-	col_start = 0;
-	for (;;)
-	{
-	    // Find open quote character.
-	    col_start = find_next_quote(line, col_start, quotechar, NULL);
-	    if (col_start < 0 || col_start > first_col)
-		goto abort_search;
-	    // Find close quote character.
-	    col_end = find_next_quote(line, col_start + 1, quotechar,
-							      curbuf->b_p_qe);
-	    if (col_end < 0)
-		goto abort_search;
-	    // If is cursor between start and end quote character, it is
-	    // target text object.
-	    if (col_start <= first_col && first_col <= col_end)
-		break;
-	    col_start = col_end + 1;
-	}
-    }
-    else
-    {
-	// Search backward for a starting quote.
-	col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
-	if (line[col_start] != quotechar)
-	{
-	    // No quote before the cursor, look after the cursor.
-	    col_start = find_next_quote(line, col_start, quotechar, NULL);
-	    if (col_start < 0)
-		goto abort_search;
-	}
-
-	// Find close quote character.
-	col_end = find_next_quote(line, col_start + 1, quotechar,
-							      curbuf->b_p_qe);
-	if (col_end < 0)
-	    goto abort_search;
-    }
-
-    // When "include" is TRUE, include spaces after closing quote or before
-    // the starting quote.
-    if (include)
-    {
-	if (VIM_ISWHITE(line[col_end + 1]))
-	    while (VIM_ISWHITE(line[col_end + 1]))
-		++col_end;
-	else
-	    while (col_start > 0 && VIM_ISWHITE(line[col_start - 1]))
-		--col_start;
-    }
-
-    // Set start position.  After vi" another i" must include the ".
-    // For v2i" include the quotes.
-    if (!include && count < 2 && (vis_empty || !inside_quotes))
-	++col_start;
-    curwin->w_cursor.col = col_start;
-    if (VIsual_active)
-    {
-	// Set the start of the Visual area when the Visual area was empty, we
-	// were just inside quotes or the Visual area didn't start at a quote
-	// and didn't include a quote.
-	if (vis_empty
-		|| (vis_bef_curs
-		    && !selected_quote
-		    && (inside_quotes
-			|| (line[VIsual.col] != quotechar
-			    && (VIsual.col == 0
-				|| line[VIsual.col - 1] != quotechar)))))
-	{
-	    VIsual = curwin->w_cursor;
-	    redraw_curbuf_later(INVERTED);
-	}
-    }
-    else
-    {
-	oap->start = curwin->w_cursor;
-	oap->motion_type = MCHAR;
-    }
-
-    // Set end position.
-    curwin->w_cursor.col = col_end;
-    if ((include || count > 1 // After vi" another i" must include the ".
-		|| (!vis_empty && inside_quotes)
-	) && inc_cursor() == 2)
-	inclusive = TRUE;
-    if (VIsual_active)
-    {
-	if (vis_empty || vis_bef_curs)
-	{
-	    // decrement cursor when 'selection' is not exclusive
-	    if (*p_sel != 'e')
-		dec_cursor();
-	}
-	else
-	{
-	    // Cursor is at start of Visual area.  Set the end of the Visual
-	    // area when it was just inside quotes or it didn't end at a
-	    // quote.
-	    if (inside_quotes
-		    || (!selected_quote
-			&& line[VIsual.col] != quotechar
-			&& (line[VIsual.col] == NUL
-			    || line[VIsual.col + 1] != quotechar)))
-	    {
-		dec_cursor();
-		VIsual = curwin->w_cursor;
-	    }
-	    curwin->w_cursor.col = col_start;
-	}
-	if (VIsual_mode == 'V')
-	{
-	    VIsual_mode = 'v';
-	    redraw_cmdline = TRUE;		// show mode later
-	}
-    }
-    else
-    {
-	// Set inclusive and other oap's flags.
-	oap->inclusive = inclusive;
-    }
-
-    return OK;
-
-abort_search:
-    if (VIsual_active && *p_sel == 'e')
-    {
-	if (did_exclusive_adj)
-	    inc_cursor();
-	if (restore_vis_bef)
-	{
-	    pos_T t = curwin->w_cursor;
-
-	    curwin->w_cursor = VIsual;
-	    VIsual = t;
-	}
-    }
-    return FALSE;
-}
-
-#endif // FEAT_TEXTOBJ
-
-/*
  * Check if the pattern is zero-width.
  * If move is TRUE, check from the beginning of the buffer, else from position
  * "cur".
new file mode 100644
--- /dev/null
+++ b/src/textobject.c
@@ -0,0 +1,1965 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * textobject.c: functions for text objects
+ */
+#include "vim.h"
+
+static int cls(void);
+static int skip_chars(int, int);
+
+/*
+ * Find the start of the next sentence, searching in the direction specified
+ * by the "dir" argument.  The cursor is positioned on the start of the next
+ * sentence when found.  If the next sentence is found, return OK.  Return FAIL
+ * otherwise.  See ":h sentence" for the precise definition of a "sentence"
+ * text object.
+ */
+    int
+findsent(int dir, long count)
+{
+    pos_T	pos, tpos;
+    int		c;
+    int		(*func)(pos_T *);
+    int		startlnum;
+    int		noskip = FALSE;	    // do not skip blanks
+    int		cpo_J;
+    int		found_dot;
+
+    pos = curwin->w_cursor;
+    if (dir == FORWARD)
+	func = incl;
+    else
+	func = decl;
+
+    while (count--)
+    {
+	/*
+	 * if on an empty line, skip up to a non-empty line
+	 */
+	if (gchar_pos(&pos) == NUL)
+	{
+	    do
+		if ((*func)(&pos) == -1)
+		    break;
+	    while (gchar_pos(&pos) == NUL);
+	    if (dir == FORWARD)
+		goto found;
+	}
+	/*
+	 * if on the start of a paragraph or a section and searching forward,
+	 * go to the next line
+	 */
+	else if (dir == FORWARD && pos.col == 0 &&
+						startPS(pos.lnum, NUL, FALSE))
+	{
+	    if (pos.lnum == curbuf->b_ml.ml_line_count)
+		return FAIL;
+	    ++pos.lnum;
+	    goto found;
+	}
+	else if (dir == BACKWARD)
+	    decl(&pos);
+
+	// go back to the previous non-white non-punctuation character
+	found_dot = FALSE;
+	while (c = gchar_pos(&pos), VIM_ISWHITE(c)
+				|| vim_strchr((char_u *)".!?)]\"'", c) != NULL)
+	{
+	    tpos = pos;
+	    if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD))
+		break;
+
+	    if (found_dot)
+		break;
+	    if (vim_strchr((char_u *) ".!?", c) != NULL)
+		found_dot = TRUE;
+
+	    if (vim_strchr((char_u *) ")]\"'", c) != NULL
+		&& vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL)
+		break;
+
+	    decl(&pos);
+	}
+
+	// remember the line where the search started
+	startlnum = pos.lnum;
+	cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
+
+	for (;;)		// find end of sentence
+	{
+	    c = gchar_pos(&pos);
+	    if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
+	    {
+		if (dir == BACKWARD && pos.lnum != startlnum)
+		    ++pos.lnum;
+		break;
+	    }
+	    if (c == '.' || c == '!' || c == '?')
+	    {
+		tpos = pos;
+		do
+		    if ((c = inc(&tpos)) == -1)
+			break;
+		while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
+			!= NULL);
+		if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
+		    || (cpo_J && (c == ' ' && inc(&tpos) >= 0
+			      && gchar_pos(&tpos) == ' ')))
+		{
+		    pos = tpos;
+		    if (gchar_pos(&pos) == NUL) // skip NUL at EOL
+			inc(&pos);
+		    break;
+		}
+	    }
+	    if ((*func)(&pos) == -1)
+	    {
+		if (count)
+		    return FAIL;
+		noskip = TRUE;
+		break;
+	    }
+	}
+found:
+	    // skip white space
+	while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
+	    if (incl(&pos) == -1)
+		break;
+    }
+
+    setpcmark();
+    curwin->w_cursor = pos;
+    return OK;
+}
+
+/*
+ * Find the next paragraph or section in direction 'dir'.
+ * Paragraphs are currently supposed to be separated by empty lines.
+ * If 'what' is NUL we go to the next paragraph.
+ * If 'what' is '{' or '}' we go to the next section.
+ * If 'both' is TRUE also stop at '}'.
+ * Return TRUE if the next paragraph or section was found.
+ */
+    int
+findpar(
+    int		*pincl,	    // Return: TRUE if last char is to be included
+    int		dir,
+    long	count,
+    int		what,
+    int		both)
+{
+    linenr_T	curr;
+    int		did_skip;   // TRUE after separating lines have been skipped
+    int		first;	    // TRUE on first line
+    int		posix = (vim_strchr(p_cpo, CPO_PARA) != NULL);
+#ifdef FEAT_FOLDING
+    linenr_T	fold_first;	// first line of a closed fold
+    linenr_T	fold_last;	// last line of a closed fold
+    int		fold_skipped;	// TRUE if a closed fold was skipped this
+				// iteration
+#endif
+
+    curr = curwin->w_cursor.lnum;
+
+    while (count--)
+    {
+	did_skip = FALSE;
+	for (first = TRUE; ; first = FALSE)
+	{
+	    if (*ml_get(curr) != NUL)
+		did_skip = TRUE;
+
+#ifdef FEAT_FOLDING
+	    // skip folded lines
+	    fold_skipped = FALSE;
+	    if (first && hasFolding(curr, &fold_first, &fold_last))
+	    {
+		curr = ((dir > 0) ? fold_last : fold_first) + dir;
+		fold_skipped = TRUE;
+	    }
+#endif
+
+	    // POSIX has its own ideas of what a paragraph boundary is and it
+	    // doesn't match historical Vi: It also stops at a "{" in the
+	    // first column and at an empty line.
+	    if (!first && did_skip && (startPS(curr, what, both)
+			   || (posix && what == NUL && *ml_get(curr) == '{')))
+		break;
+
+#ifdef FEAT_FOLDING
+	    if (fold_skipped)
+		curr -= dir;
+#endif
+	    if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
+	    {
+		if (count)
+		    return FALSE;
+		curr -= dir;
+		break;
+	    }
+	}
+    }
+    setpcmark();
+    if (both && *ml_get(curr) == '}')	// include line with '}'
+	++curr;
+    curwin->w_cursor.lnum = curr;
+    if (curr == curbuf->b_ml.ml_line_count && what != '}')
+    {
+	char_u *line = ml_get(curr);
+
+	// Put the cursor on the last character in the last line and make the
+	// motion inclusive.
+	if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0)
+	{
+	    --curwin->w_cursor.col;
+	    curwin->w_cursor.col -=
+			     (*mb_head_off)(line, line + curwin->w_cursor.col);
+	    *pincl = TRUE;
+	}
+    }
+    else
+	curwin->w_cursor.col = 0;
+    return TRUE;
+}
+
+/*
+ * check if the string 's' is a nroff macro that is in option 'opt'
+ */
+    static int
+inmacro(char_u *opt, char_u *s)
+{
+    char_u	*macro;
+
+    for (macro = opt; macro[0]; ++macro)
+    {
+	// Accept two characters in the option being equal to two characters
+	// in the line.  A space in the option matches with a space in the
+	// line or the line having ended.
+	if (       (macro[0] == s[0]
+		    || (macro[0] == ' '
+			&& (s[0] == NUL || s[0] == ' ')))
+		&& (macro[1] == s[1]
+		    || ((macro[1] == NUL || macro[1] == ' ')
+			&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
+	    break;
+	++macro;
+	if (macro[0] == NUL)
+	    break;
+    }
+    return (macro[0] != NUL);
+}
+
+/*
+ * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
+ * If 'para' is '{' or '}' only check for sections.
+ * If 'both' is TRUE also stop at '}'
+ */
+    int
+startPS(linenr_T lnum, int para, int both)
+{
+    char_u	*s;
+
+    s = ml_get(lnum);
+    if (*s == para || *s == '\f' || (both && *s == '}'))
+	return TRUE;
+    if (*s == '.' && (inmacro(p_sections, s + 1) ||
+					   (!para && inmacro(p_para, s + 1))))
+	return TRUE;
+    return FALSE;
+}
+
+/*
+ * The following routines do the word searches performed by the 'w', 'W',
+ * 'b', 'B', 'e', and 'E' commands.
+ */
+
+/*
+ * To perform these searches, characters are placed into one of three
+ * classes, and transitions between classes determine word boundaries.
+ *
+ * The classes are:
+ *
+ * 0 - white space
+ * 1 - punctuation
+ * 2 or higher - keyword characters (letters, digits and underscore)
+ */
+
+static int	cls_bigword;	// TRUE for "W", "B" or "E"
+
+/*
+ * cls() - returns the class of character at curwin->w_cursor
+ *
+ * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
+ * from class 2 and higher are reported as class 1 since only white space
+ * boundaries are of interest.
+ */
+    static int
+cls(void)
+{
+    int	    c;
+
+    c = gchar_cursor();
+    if (c == ' ' || c == '\t' || c == NUL)
+	return 0;
+    if (enc_dbcs != 0 && c > 0xFF)
+    {
+	// If cls_bigword, report multi-byte chars as class 1.
+	if (enc_dbcs == DBCS_KOR && cls_bigword)
+	    return 1;
+
+	// process code leading/trailing bytes
+	return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
+    }
+    if (enc_utf8)
+    {
+	c = utf_class(c);
+	if (c != 0 && cls_bigword)
+	    return 1;
+	return c;
+    }
+
+    // If cls_bigword is TRUE, report all non-blanks as class 1.
+    if (cls_bigword)
+	return 1;
+
+    if (vim_iswordc(c))
+	return 2;
+    return 1;
+}
+
+
+/*
+ * fwd_word(count, type, eol) - move forward one word
+ *
+ * Returns FAIL if the cursor was already at the end of the file.
+ * If eol is TRUE, last word stops at end of line (for operators).
+ */
+    int
+fwd_word(
+    long	count,
+    int		bigword,    // "W", "E" or "B"
+    int		eol)
+{
+    int		sclass;	    // starting class
+    int		i;
+    int		last_line;
+
+    curwin->w_cursor.coladd = 0;
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	// When inside a range of folded lines, move to the last char of the
+	// last line.
+	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
+	    coladvance((colnr_T)MAXCOL);
+#endif
+	sclass = cls();
+
+	/*
+	 * We always move at least one character, unless on the last
+	 * character in the buffer.
+	 */
+	last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
+	i = inc_cursor();
+	if (i == -1 || (i >= 1 && last_line)) // started at last char in file
+	    return FAIL;
+	if (i >= 1 && eol && count == 0)      // started at last char in line
+	    return OK;
+
+	/*
+	 * Go one char past end of current word (if any)
+	 */
+	if (sclass != 0)
+	    while (cls() == sclass)
+	    {
+		i = inc_cursor();
+		if (i == -1 || (i >= 1 && eol && count == 0))
+		    return OK;
+	    }
+
+	/*
+	 * go to next non-white
+	 */
+	while (cls() == 0)
+	{
+	    /*
+	     * We'll stop if we land on a blank line
+	     */
+	    if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
+		break;
+
+	    i = inc_cursor();
+	    if (i == -1 || (i >= 1 && eol && count == 0))
+		return OK;
+	}
+    }
+    return OK;
+}
+
+/*
+ * bck_word() - move backward 'count' words
+ *
+ * If stop is TRUE and we are already on the start of a word, move one less.
+ *
+ * Returns FAIL if top of the file was reached.
+ */
+    int
+bck_word(long count, int bigword, int stop)
+{
+    int		sclass;	    // starting class
+
+    curwin->w_cursor.coladd = 0;
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	// When inside a range of folded lines, move to the first char of the
+	// first line.
+	if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
+	    curwin->w_cursor.col = 0;
+#endif
+	sclass = cls();
+	if (dec_cursor() == -1)		// started at start of file
+	    return FAIL;
+
+	if (!stop || sclass == cls() || sclass == 0)
+	{
+	    /*
+	     * Skip white space before the word.
+	     * Stop on an empty line.
+	     */
+	    while (cls() == 0)
+	    {
+		if (curwin->w_cursor.col == 0
+				      && LINEEMPTY(curwin->w_cursor.lnum))
+		    goto finished;
+		if (dec_cursor() == -1) // hit start of file, stop here
+		    return OK;
+	    }
+
+	    /*
+	     * Move backward to start of this word.
+	     */
+	    if (skip_chars(cls(), BACKWARD))
+		return OK;
+	}
+
+	inc_cursor();			// overshot - forward one
+finished:
+	stop = FALSE;
+    }
+    return OK;
+}
+
+/*
+ * end_word() - move to the end of the word
+ *
+ * There is an apparent bug in the 'e' motion of the real vi. At least on the
+ * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
+ * motion crosses blank lines. When the real vi crosses a blank line in an
+ * 'e' motion, the cursor is placed on the FIRST character of the next
+ * non-blank line. The 'E' command, however, works correctly. Since this
+ * appears to be a bug, I have not duplicated it here.
+ *
+ * Returns FAIL if end of the file was reached.
+ *
+ * If stop is TRUE and we are already on the end of a word, move one less.
+ * If empty is TRUE stop on an empty line.
+ */
+    int
+end_word(
+    long	count,
+    int		bigword,
+    int		stop,
+    int		empty)
+{
+    int		sclass;	    // starting class
+
+    curwin->w_cursor.coladd = 0;
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+#ifdef FEAT_FOLDING
+	// When inside a range of folded lines, move to the last char of the
+	// last line.
+	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
+	    coladvance((colnr_T)MAXCOL);
+#endif
+	sclass = cls();
+	if (inc_cursor() == -1)
+	    return FAIL;
+
+	/*
+	 * If we're in the middle of a word, we just have to move to the end
+	 * of it.
+	 */
+	if (cls() == sclass && sclass != 0)
+	{
+	    /*
+	     * Move forward to end of the current word
+	     */
+	    if (skip_chars(sclass, FORWARD))
+		return FAIL;
+	}
+	else if (!stop || sclass == 0)
+	{
+	    /*
+	     * We were at the end of a word. Go to the end of the next word.
+	     * First skip white space, if 'empty' is TRUE, stop at empty line.
+	     */
+	    while (cls() == 0)
+	    {
+		if (empty && curwin->w_cursor.col == 0
+					  && LINEEMPTY(curwin->w_cursor.lnum))
+		    goto finished;
+		if (inc_cursor() == -1)	    // hit end of file, stop here
+		    return FAIL;
+	    }
+
+	    /*
+	     * Move forward to the end of this word.
+	     */
+	    if (skip_chars(cls(), FORWARD))
+		return FAIL;
+	}
+	dec_cursor();			// overshot - one char backward
+finished:
+	stop = FALSE;			// we move only one word less
+    }
+    return OK;
+}
+
+/*
+ * Move back to the end of the word.
+ *
+ * Returns FAIL if start of the file was reached.
+ */
+    int
+bckend_word(
+    long	count,
+    int		bigword,    // TRUE for "B"
+    int		eol)	    // TRUE: stop at end of line.
+{
+    int		sclass;	    // starting class
+    int		i;
+
+    curwin->w_cursor.coladd = 0;
+    cls_bigword = bigword;
+    while (--count >= 0)
+    {
+	sclass = cls();
+	if ((i = dec_cursor()) == -1)
+	    return FAIL;
+	if (eol && i == 1)
+	    return OK;
+
+	/*
+	 * Move backward to before the start of this word.
+	 */
+	if (sclass != 0)
+	{
+	    while (cls() == sclass)
+		if ((i = dec_cursor()) == -1 || (eol && i == 1))
+		    return OK;
+	}
+
+	/*
+	 * Move backward to end of the previous word
+	 */
+	while (cls() == 0)
+	{
+	    if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum))
+		break;
+	    if ((i = dec_cursor()) == -1 || (eol && i == 1))
+		return OK;
+	}
+    }
+    return OK;
+}
+
+/*
+ * Skip a row of characters of the same class.
+ * Return TRUE when end-of-file reached, FALSE otherwise.
+ */
+    static int
+skip_chars(int cclass, int dir)
+{
+    while (cls() == cclass)
+	if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
+	    return TRUE;
+    return FALSE;
+}
+
+#if defined(FEAT_TEXTOBJ) || defined(PROTO)
+/*
+ * Go back to the start of the word or the start of white space
+ */
+    static void
+back_in_line(void)
+{
+    int		sclass;		    // starting class
+
+    sclass = cls();
+    for (;;)
+    {
+	if (curwin->w_cursor.col == 0)	    // stop at start of line
+	    break;
+	dec_cursor();
+	if (cls() != sclass)		    // stop at start of word
+	{
+	    inc_cursor();
+	    break;
+	}
+    }
+}
+
+    static void
+find_first_blank(pos_T *posp)
+{
+    int	    c;
+
+    while (decl(posp) != -1)
+    {
+	c = gchar_pos(posp);
+	if (!VIM_ISWHITE(c))
+	{
+	    incl(posp);
+	    break;
+	}
+    }
+}
+
+/*
+ * Skip count/2 sentences and count/2 separating white spaces.
+ */
+    static void
+findsent_forward(
+    long    count,
+    int	    at_start_sent)	// cursor is at start of sentence
+{
+    while (count--)
+    {
+	findsent(FORWARD, 1L);
+	if (at_start_sent)
+	    find_first_blank(&curwin->w_cursor);
+	if (count == 0 || at_start_sent)
+	    decl(&curwin->w_cursor);
+	at_start_sent = !at_start_sent;
+    }
+}
+
+/*
+ * Find word under cursor, cursor at end.
+ * Used while an operator is pending, and in Visual mode.
+ */
+    int
+current_word(
+    oparg_T	*oap,
+    long	count,
+    int		include,	// TRUE: include word and white space
+    int		bigword)	// FALSE == word, TRUE == WORD
+{
+    pos_T	start_pos;
+    pos_T	pos;
+    int		inclusive = TRUE;
+    int		include_white = FALSE;
+
+    cls_bigword = bigword;
+    CLEAR_POS(&start_pos);
+
+    // Correct cursor when 'selection' is exclusive
+    if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
+	dec_cursor();
+
+    /*
+     * When Visual mode is not active, or when the VIsual area is only one
+     * character, select the word and/or white space under the cursor.
+     */
+    if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual))
+    {
+	/*
+	 * Go to start of current word or white space.
+	 */
+	back_in_line();
+	start_pos = curwin->w_cursor;
+
+	/*
+	 * If the start is on white space, and white space should be included
+	 * ("	word"), or start is not on white space, and white space should
+	 * not be included ("word"), find end of word.
+	 */
+	if ((cls() == 0) == include)
+	{
+	    if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
+		return FAIL;
+	}
+	else
+	{
+	    /*
+	     * If the start is not on white space, and white space should be
+	     * included ("word	 "), or start is on white space and white
+	     * space should not be included ("	 "), find start of word.
+	     * If we end up in the first column of the next line (single char
+	     * word) back up to end of the line.
+	     */
+	    fwd_word(1L, bigword, TRUE);
+	    if (curwin->w_cursor.col == 0)
+		decl(&curwin->w_cursor);
+	    else
+		oneleft();
+
+	    if (include)
+		include_white = TRUE;
+	}
+
+	if (VIsual_active)
+	{
+	    // should do something when inclusive == FALSE !
+	    VIsual = start_pos;
+	    redraw_curbuf_later(INVERTED);	// update the inversion
+	}
+	else
+	{
+	    oap->start = start_pos;
+	    oap->motion_type = MCHAR;
+	}
+	--count;
+    }
+
+    /*
+     * When count is still > 0, extend with more objects.
+     */
+    while (count > 0)
+    {
+	inclusive = TRUE;
+	if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
+	{
+	    /*
+	     * In Visual mode, with cursor at start: move cursor back.
+	     */
+	    if (decl(&curwin->w_cursor) == -1)
+		return FAIL;
+	    if (include != (cls() != 0))
+	    {
+		if (bck_word(1L, bigword, TRUE) == FAIL)
+		    return FAIL;
+	    }
+	    else
+	    {
+		if (bckend_word(1L, bigword, TRUE) == FAIL)
+		    return FAIL;
+		(void)incl(&curwin->w_cursor);
+	    }
+	}
+	else
+	{
+	    /*
+	     * Move cursor forward one word and/or white area.
+	     */
+	    if (incl(&curwin->w_cursor) == -1)
+		return FAIL;
+	    if (include != (cls() == 0))
+	    {
+		if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1)
+		    return FAIL;
+		/*
+		 * If end is just past a new-line, we don't want to include
+		 * the first character on the line.
+		 * Put cursor on last char of white.
+		 */
+		if (oneleft() == FAIL)
+		    inclusive = FALSE;
+	    }
+	    else
+	    {
+		if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
+		    return FAIL;
+	    }
+	}
+	--count;
+    }
+
+    if (include_white && (cls() != 0
+		 || (curwin->w_cursor.col == 0 && !inclusive)))
+    {
+	/*
+	 * If we don't include white space at the end, move the start
+	 * to include some white space there. This makes "daw" work
+	 * better on the last word in a sentence (and "2daw" on last-but-one
+	 * word).  Also when "2daw" deletes "word." at the end of the line
+	 * (cursor is at start of next line).
+	 * But don't delete white space at start of line (indent).
+	 */
+	pos = curwin->w_cursor;	// save cursor position
+	curwin->w_cursor = start_pos;
+	if (oneleft() == OK)
+	{
+	    back_in_line();
+	    if (cls() == 0 && curwin->w_cursor.col > 0)
+	    {
+		if (VIsual_active)
+		    VIsual = curwin->w_cursor;
+		else
+		    oap->start = curwin->w_cursor;
+	    }
+	}
+	curwin->w_cursor = pos;	// put cursor back at end
+    }
+
+    if (VIsual_active)
+    {
+	if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor))
+	    inc_cursor();
+	if (VIsual_mode == 'V')
+	{
+	    VIsual_mode = 'v';
+	    redraw_cmdline = TRUE;		// show mode later
+	}
+    }
+    else
+	oap->inclusive = inclusive;
+
+    return OK;
+}
+
+/*
+ * Find sentence(s) under the cursor, cursor at end.
+ * When Visual active, extend it by one or more sentences.
+ */
+    int
+current_sent(oparg_T *oap, long count, int include)
+{
+    pos_T	start_pos;
+    pos_T	pos;
+    int		start_blank;
+    int		c;
+    int		at_start_sent;
+    long	ncount;
+
+    start_pos = curwin->w_cursor;
+    pos = start_pos;
+    findsent(FORWARD, 1L);	// Find start of next sentence.
+
+    /*
+     * When the Visual area is bigger than one character: Extend it.
+     */
+    if (VIsual_active && !EQUAL_POS(start_pos, VIsual))
+    {
+extend:
+	if (LT_POS(start_pos, VIsual))
+	{
+	    /*
+	     * Cursor at start of Visual area.
+	     * Find out where we are:
+	     * - in the white space before a sentence
+	     * - in a sentence or just after it
+	     * - at the start of a sentence
+	     */
+	    at_start_sent = TRUE;
+	    decl(&pos);
+	    while (LT_POS(pos, curwin->w_cursor))
+	    {
+		c = gchar_pos(&pos);
+		if (!VIM_ISWHITE(c))
+		{
+		    at_start_sent = FALSE;
+		    break;
+		}
+		incl(&pos);
+	    }
+	    if (!at_start_sent)
+	    {
+		findsent(BACKWARD, 1L);
+		if (EQUAL_POS(curwin->w_cursor, start_pos))
+		    at_start_sent = TRUE;  // exactly at start of sentence
+		else
+		    // inside a sentence, go to its end (start of next)
+		    findsent(FORWARD, 1L);
+	    }
+	    if (include)	// "as" gets twice as much as "is"
+		count *= 2;
+	    while (count--)
+	    {
+		if (at_start_sent)
+		    find_first_blank(&curwin->w_cursor);
+		c = gchar_cursor();
+		if (!at_start_sent || (!include && !VIM_ISWHITE(c)))
+		    findsent(BACKWARD, 1L);
+		at_start_sent = !at_start_sent;
+	    }
+	}
+	else
+	{
+	    /*
+	     * Cursor at end of Visual area.
+	     * Find out where we are:
+	     * - just before a sentence
+	     * - just before or in the white space before a sentence
+	     * - in a sentence
+	     */
+	    incl(&pos);
+	    at_start_sent = TRUE;
+	    // not just before a sentence
+	    if (!EQUAL_POS(pos, curwin->w_cursor))
+	    {
+		at_start_sent = FALSE;
+		while (LT_POS(pos, curwin->w_cursor))
+		{
+		    c = gchar_pos(&pos);
+		    if (!VIM_ISWHITE(c))
+		    {
+			at_start_sent = TRUE;
+			break;
+		    }
+		    incl(&pos);
+		}
+		if (at_start_sent)	// in the sentence
+		    findsent(BACKWARD, 1L);
+		else		// in/before white before a sentence
+		    curwin->w_cursor = start_pos;
+	    }
+
+	    if (include)	// "as" gets twice as much as "is"
+		count *= 2;
+	    findsent_forward(count, at_start_sent);
+	    if (*p_sel == 'e')
+		++curwin->w_cursor.col;
+	}
+	return OK;
+    }
+
+    /*
+     * If the cursor started on a blank, check if it is just before the start
+     * of the next sentence.
+     */
+    while (c = gchar_pos(&pos), VIM_ISWHITE(c))	// VIM_ISWHITE() is a macro
+	incl(&pos);
+    if (EQUAL_POS(pos, curwin->w_cursor))
+    {
+	start_blank = TRUE;
+	find_first_blank(&start_pos);	// go back to first blank
+    }
+    else
+    {
+	start_blank = FALSE;
+	findsent(BACKWARD, 1L);
+	start_pos = curwin->w_cursor;
+    }
+    if (include)
+	ncount = count * 2;
+    else
+    {
+	ncount = count;
+	if (start_blank)
+	    --ncount;
+    }
+    if (ncount > 0)
+	findsent_forward(ncount, TRUE);
+    else
+	decl(&curwin->w_cursor);
+
+    if (include)
+    {
+	/*
+	 * If the blank in front of the sentence is included, exclude the
+	 * blanks at the end of the sentence, go back to the first blank.
+	 * If there are no trailing blanks, try to include leading blanks.
+	 */
+	if (start_blank)
+	{
+	    find_first_blank(&curwin->w_cursor);
+	    c = gchar_pos(&curwin->w_cursor);	// VIM_ISWHITE() is a macro
+	    if (VIM_ISWHITE(c))
+		decl(&curwin->w_cursor);
+	}
+	else if (c = gchar_cursor(), !VIM_ISWHITE(c))
+	    find_first_blank(&start_pos);
+    }
+
+    if (VIsual_active)
+    {
+	// Avoid getting stuck with "is" on a single space before a sentence.
+	if (EQUAL_POS(start_pos, curwin->w_cursor))
+	    goto extend;
+	if (*p_sel == 'e')
+	    ++curwin->w_cursor.col;
+	VIsual = start_pos;
+	VIsual_mode = 'v';
+	redraw_cmdline = TRUE;		// show mode later
+	redraw_curbuf_later(INVERTED);	// update the inversion
+    }
+    else
+    {
+	// include a newline after the sentence, if there is one
+	if (incl(&curwin->w_cursor) == -1)
+	    oap->inclusive = TRUE;
+	else
+	    oap->inclusive = FALSE;
+	oap->start = start_pos;
+	oap->motion_type = MCHAR;
+    }
+    return OK;
+}
+
+/*
+ * Find block under the cursor, cursor at end.
+ * "what" and "other" are two matching parenthesis/brace/etc.
+ */
+    int
+current_block(
+    oparg_T	*oap,
+    long	count,
+    int		include,	// TRUE == include white space
+    int		what,		// '(', '{', etc.
+    int		other)		// ')', '}', etc.
+{
+    pos_T	old_pos;
+    pos_T	*pos = NULL;
+    pos_T	start_pos;
+    pos_T	*end_pos;
+    pos_T	old_start, old_end;
+    char_u	*save_cpo;
+    int		sol = FALSE;		// '{' at start of line
+
+    old_pos = curwin->w_cursor;
+    old_end = curwin->w_cursor;		// remember where we started
+    old_start = old_end;
+
+    /*
+     * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
+     */
+    if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
+    {
+	setpcmark();
+	if (what == '{')		// ignore indent
+	    while (inindent(1))
+		if (inc_cursor() != 0)
+		    break;
+	if (gchar_cursor() == what)
+	    // cursor on '(' or '{', move cursor just after it
+	    ++curwin->w_cursor.col;
+    }
+    else if (LT_POS(VIsual, curwin->w_cursor))
+    {
+	old_start = VIsual;
+	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
+    }
+    else
+	old_end = VIsual;
+
+    /*
+     * Search backwards for unclosed '(', '{', etc..
+     * Put this position in start_pos.
+     * Ignore quotes here.  Keep the "M" flag in 'cpo', as that is what the
+     * user wants.
+     */
+    save_cpo = p_cpo;
+    p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
+    while (count-- > 0)
+    {
+	if ((pos = findmatch(NULL, what)) == NULL)
+	    break;
+	curwin->w_cursor = *pos;
+	start_pos = *pos;   // the findmatch for end_pos will overwrite *pos
+    }
+    p_cpo = save_cpo;
+
+    /*
+     * Search for matching ')', '}', etc.
+     * Put this position in curwin->w_cursor.
+     */
+    if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
+    {
+	curwin->w_cursor = old_pos;
+	return FAIL;
+    }
+    curwin->w_cursor = *end_pos;
+
+    /*
+     * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
+     * If the ending '}', ')' or ']' is only preceded by indent, skip that
+     * indent.  But only if the resulting area is not smaller than what we
+     * started with.
+     */
+    while (!include)
+    {
+	incl(&start_pos);
+	sol = (curwin->w_cursor.col == 0);
+	decl(&curwin->w_cursor);
+	while (inindent(1))
+	{
+	    sol = TRUE;
+	    if (decl(&curwin->w_cursor) != 0)
+		break;
+	}
+
+	/*
+	 * In Visual mode, when the resulting area is not bigger than what we
+	 * started with, extend it to the next block, and then exclude again.
+	 */
+	if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor)
+		&& VIsual_active)
+	{
+	    curwin->w_cursor = old_start;
+	    decl(&curwin->w_cursor);
+	    if ((pos = findmatch(NULL, what)) == NULL)
+	    {
+		curwin->w_cursor = old_pos;
+		return FAIL;
+	    }
+	    start_pos = *pos;
+	    curwin->w_cursor = *pos;
+	    if ((end_pos = findmatch(NULL, other)) == NULL)
+	    {
+		curwin->w_cursor = old_pos;
+		return FAIL;
+	    }
+	    curwin->w_cursor = *end_pos;
+	}
+	else
+	    break;
+    }
+
+    if (VIsual_active)
+    {
+	if (*p_sel == 'e')
+	    inc(&curwin->w_cursor);
+	if (sol && gchar_cursor() != NUL)
+	    inc(&curwin->w_cursor);	// include the line break
+	VIsual = start_pos;
+	VIsual_mode = 'v';
+	redraw_curbuf_later(INVERTED);	// update the inversion
+	showmode();
+    }
+    else
+    {
+	oap->start = start_pos;
+	oap->motion_type = MCHAR;
+	oap->inclusive = FALSE;
+	if (sol)
+	    incl(&curwin->w_cursor);
+	else if (LTOREQ_POS(start_pos, curwin->w_cursor))
+	    // Include the character under the cursor.
+	    oap->inclusive = TRUE;
+	else
+	    // End is before the start (no text in between <>, [], etc.): don't
+	    // operate on any text.
+	    curwin->w_cursor = start_pos;
+    }
+
+    return OK;
+}
+
+/*
+ * Return TRUE if the cursor is on a "<aaa>" tag.  Ignore "<aaa/>".
+ * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>".
+ */
+    static int
+in_html_tag(
+    int		end_tag)
+{
+    char_u	*line = ml_get_curline();
+    char_u	*p;
+    int		c;
+    int		lc = NUL;
+    pos_T	pos;
+
+    if (enc_dbcs)
+    {
+	char_u	*lp = NULL;
+
+	// We search forward until the cursor, because searching backwards is
+	// very slow for DBCS encodings.
+	for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p))
+	    if (*p == '>' || *p == '<')
+	    {
+		lc = *p;
+		lp = p;
+	    }
+	if (*p != '<')	    // check for '<' under cursor
+	{
+	    if (lc != '<')
+		return FALSE;
+	    p = lp;
+	}
+    }
+    else
+    {
+	for (p = line + curwin->w_cursor.col; p > line; )
+	{
+	    if (*p == '<')	// find '<' under/before cursor
+		break;
+	    MB_PTR_BACK(line, p);
+	    if (*p == '>')	// find '>' before cursor
+		break;
+	}
+	if (*p != '<')
+	    return FALSE;
+    }
+
+    pos.lnum = curwin->w_cursor.lnum;
+    pos.col = (colnr_T)(p - line);
+
+    MB_PTR_ADV(p);
+    if (end_tag)
+	// check that there is a '/' after the '<'
+	return *p == '/';
+
+    // check that there is no '/' after the '<'
+    if (*p == '/')
+	return FALSE;
+
+    // check that the matching '>' is not preceded by '/'
+    for (;;)
+    {
+	if (inc(&pos) < 0)
+	    return FALSE;
+	c = *ml_get_pos(&pos);
+	if (c == '>')
+	    break;
+	lc = c;
+    }
+    return lc != '/';
+}
+
+/*
+ * Find tag block under the cursor, cursor at end.
+ */
+    int
+current_tagblock(
+    oparg_T	*oap,
+    long	count_arg,
+    int		include)	// TRUE == include white space
+{
+    long	count = count_arg;
+    long	n;
+    pos_T	old_pos;
+    pos_T	start_pos;
+    pos_T	end_pos;
+    pos_T	old_start, old_end;
+    char_u	*spat, *epat;
+    char_u	*p;
+    char_u	*cp;
+    int		len;
+    int		r;
+    int		do_include = include;
+    int		save_p_ws = p_ws;
+    int		retval = FAIL;
+    int		is_inclusive = TRUE;
+
+    p_ws = FALSE;
+
+    old_pos = curwin->w_cursor;
+    old_end = curwin->w_cursor;		    // remember where we started
+    old_start = old_end;
+    if (!VIsual_active || *p_sel == 'e')
+	decl(&old_end);			    // old_end is inclusive
+
+    /*
+     * If we start on "<aaa>" select that block.
+     */
+    if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
+    {
+	setpcmark();
+
+	// ignore indent
+	while (inindent(1))
+	    if (inc_cursor() != 0)
+		break;
+
+	if (in_html_tag(FALSE))
+	{
+	    // cursor on start tag, move to its '>'
+	    while (*ml_get_cursor() != '>')
+		if (inc_cursor() < 0)
+		    break;
+	}
+	else if (in_html_tag(TRUE))
+	{
+	    // cursor on end tag, move to just before it
+	    while (*ml_get_cursor() != '<')
+		if (dec_cursor() < 0)
+		    break;
+	    dec_cursor();
+	    old_end = curwin->w_cursor;
+	}
+    }
+    else if (LT_POS(VIsual, curwin->w_cursor))
+    {
+	old_start = VIsual;
+	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
+    }
+    else
+	old_end = VIsual;
+
+again:
+    /*
+     * Search backwards for unclosed "<aaa>".
+     * Put this position in start_pos.
+     */
+    for (n = 0; n < count; ++n)
+    {
+	if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
+		    (char_u *)"",
+		    (char_u *)"</[^>]*>", BACKWARD, NULL, 0,
+						  NULL, (linenr_T)0, 0L) <= 0)
+	{
+	    curwin->w_cursor = old_pos;
+	    goto theend;
+	}
+    }
+    start_pos = curwin->w_cursor;
+
+    /*
+     * Search for matching "</aaa>".  First isolate the "aaa".
+     */
+    inc_cursor();
+    p = ml_get_cursor();
+    for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp))
+	;
+    len = (int)(cp - p);
+    if (len == 0)
+    {
+	curwin->w_cursor = old_pos;
+	goto theend;
+    }
+    spat = alloc(len + 31);
+    epat = alloc(len + 9);
+    if (spat == NULL || epat == NULL)
+    {
+	vim_free(spat);
+	vim_free(epat);
+	curwin->w_cursor = old_pos;
+	goto theend;
+    }
+    sprintf((char *)spat, "<%.*s\\>\\%%(\\s\\_[^>]\\{-}[^/]>\\|>\\)\\c", len, p);
+    sprintf((char *)epat, "</%.*s>\\c", len, p);
+
+    r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL,
+						    0, NULL, (linenr_T)0, 0L);
+
+    vim_free(spat);
+    vim_free(epat);
+
+    if (r < 1 || LT_POS(curwin->w_cursor, old_end))
+    {
+	// Can't find other end or it's before the previous end.  Could be a
+	// HTML tag that doesn't have a matching end.  Search backwards for
+	// another starting tag.
+	count = 1;
+	curwin->w_cursor = start_pos;
+	goto again;
+    }
+
+    if (do_include)
+    {
+	// Include up to the '>'.
+	while (*ml_get_cursor() != '>')
+	    if (inc_cursor() < 0)
+		break;
+    }
+    else
+    {
+	char_u *c = ml_get_cursor();
+
+	// Exclude the '<' of the end tag.
+	// If the closing tag is on new line, do not decrement cursor, but
+	// make operation exclusive, so that the linefeed will be selected
+	if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0)
+	    // do not decrement cursor
+	    is_inclusive = FALSE;
+	else if (*c == '<')
+	    dec_cursor();
+    }
+    end_pos = curwin->w_cursor;
+
+    if (!do_include)
+    {
+	// Exclude the start tag.
+	curwin->w_cursor = start_pos;
+	while (inc_cursor() >= 0)
+	    if (*ml_get_cursor() == '>')
+	    {
+		inc_cursor();
+		start_pos = curwin->w_cursor;
+		break;
+	    }
+	curwin->w_cursor = end_pos;
+
+	// If we are in Visual mode and now have the same text as before set
+	// "do_include" and try again.
+	if (VIsual_active && EQUAL_POS(start_pos, old_start)
+						&& EQUAL_POS(end_pos, old_end))
+	{
+	    do_include = TRUE;
+	    curwin->w_cursor = old_start;
+	    count = count_arg;
+	    goto again;
+	}
+    }
+
+    if (VIsual_active)
+    {
+	// If the end is before the start there is no text between tags, select
+	// the char under the cursor.
+	if (LT_POS(end_pos, start_pos))
+	    curwin->w_cursor = start_pos;
+	else if (*p_sel == 'e')
+	    inc_cursor();
+	VIsual = start_pos;
+	VIsual_mode = 'v';
+	redraw_curbuf_later(INVERTED);	// update the inversion
+	showmode();
+    }
+    else
+    {
+	oap->start = start_pos;
+	oap->motion_type = MCHAR;
+	if (LT_POS(end_pos, start_pos))
+	{
+	    // End is before the start: there is no text between tags; operate
+	    // on an empty area.
+	    curwin->w_cursor = start_pos;
+	    oap->inclusive = FALSE;
+	}
+	else
+	    oap->inclusive = is_inclusive;
+    }
+    retval = OK;
+
+theend:
+    p_ws = save_p_ws;
+    return retval;
+}
+
+    int
+current_par(
+    oparg_T	*oap,
+    long	count,
+    int		include,	// TRUE == include white space
+    int		type)		// 'p' for paragraph, 'S' for section
+{
+    linenr_T	start_lnum;
+    linenr_T	end_lnum;
+    int		white_in_front;
+    int		dir;
+    int		start_is_white;
+    int		prev_start_is_white;
+    int		retval = OK;
+    int		do_white = FALSE;
+    int		t;
+    int		i;
+
+    if (type == 'S')	    // not implemented yet
+	return FAIL;
+
+    start_lnum = curwin->w_cursor.lnum;
+
+    /*
+     * When visual area is more than one line: extend it.
+     */
+    if (VIsual_active && start_lnum != VIsual.lnum)
+    {
+extend:
+	if (start_lnum < VIsual.lnum)
+	    dir = BACKWARD;
+	else
+	    dir = FORWARD;
+	for (i = count; --i >= 0; )
+	{
+	    if (start_lnum ==
+			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
+	    {
+		retval = FAIL;
+		break;
+	    }
+
+	    prev_start_is_white = -1;
+	    for (t = 0; t < 2; ++t)
+	    {
+		start_lnum += dir;
+		start_is_white = linewhite(start_lnum);
+		if (prev_start_is_white == start_is_white)
+		{
+		    start_lnum -= dir;
+		    break;
+		}
+		for (;;)
+		{
+		    if (start_lnum == (dir == BACKWARD
+					    ? 1 : curbuf->b_ml.ml_line_count))
+			break;
+		    if (start_is_white != linewhite(start_lnum + dir)
+			    || (!start_is_white
+				    && startPS(start_lnum + (dir > 0
+							     ? 1 : 0), 0, 0)))
+			break;
+		    start_lnum += dir;
+		}
+		if (!include)
+		    break;
+		if (start_lnum == (dir == BACKWARD
+					    ? 1 : curbuf->b_ml.ml_line_count))
+		    break;
+		prev_start_is_white = start_is_white;
+	    }
+	}
+	curwin->w_cursor.lnum = start_lnum;
+	curwin->w_cursor.col = 0;
+	return retval;
+    }
+
+    /*
+     * First move back to the start_lnum of the paragraph or white lines
+     */
+    white_in_front = linewhite(start_lnum);
+    while (start_lnum > 1)
+    {
+	if (white_in_front)	    // stop at first white line
+	{
+	    if (!linewhite(start_lnum - 1))
+		break;
+	}
+	else		// stop at first non-white line of start of paragraph
+	{
+	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
+		break;
+	}
+	--start_lnum;
+    }
+
+    /*
+     * Move past the end of any white lines.
+     */
+    end_lnum = start_lnum;
+    while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum))
+	++end_lnum;
+
+    --end_lnum;
+    i = count;
+    if (!include && white_in_front)
+	--i;
+    while (i--)
+    {
+	if (end_lnum == curbuf->b_ml.ml_line_count)
+	    return FAIL;
+
+	if (!include)
+	    do_white = linewhite(end_lnum + 1);
+
+	if (include || !do_white)
+	{
+	    ++end_lnum;
+	    /*
+	     * skip to end of paragraph
+	     */
+	    while (end_lnum < curbuf->b_ml.ml_line_count
+		    && !linewhite(end_lnum + 1)
+		    && !startPS(end_lnum + 1, 0, 0))
+		++end_lnum;
+	}
+
+	if (i == 0 && white_in_front && include)
+	    break;
+
+	/*
+	 * skip to end of white lines after paragraph
+	 */
+	if (include || do_white)
+	    while (end_lnum < curbuf->b_ml.ml_line_count
+						   && linewhite(end_lnum + 1))
+		++end_lnum;
+    }
+
+    /*
+     * If there are no empty lines at the end, try to find some empty lines at
+     * the start (unless that has been done already).
+     */
+    if (!white_in_front && !linewhite(end_lnum) && include)
+	while (start_lnum > 1 && linewhite(start_lnum - 1))
+	    --start_lnum;
+
+    if (VIsual_active)
+    {
+	// Problem: when doing "Vipipip" nothing happens in a single white
+	// line, we get stuck there.  Trap this here.
+	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
+	    goto extend;
+	if (VIsual.lnum != start_lnum)
+	{
+	    VIsual.lnum = start_lnum;
+	    VIsual.col = 0;
+	}
+	VIsual_mode = 'V';
+	redraw_curbuf_later(INVERTED);	// update the inversion
+	showmode();
+    }
+    else
+    {
+	oap->start.lnum = start_lnum;
+	oap->start.col = 0;
+	oap->motion_type = MLINE;
+    }
+    curwin->w_cursor.lnum = end_lnum;
+    curwin->w_cursor.col = 0;
+
+    return OK;
+}
+
+/*
+ * Search quote char from string line[col].
+ * Quote character escaped by one of the characters in "escape" is not counted
+ * as a quote.
+ * Returns column number of "quotechar" or -1 when not found.
+ */
+    static int
+find_next_quote(
+    char_u	*line,
+    int		col,
+    int		quotechar,
+    char_u	*escape)	// escape characters, can be NULL
+{
+    int		c;
+
+    for (;;)
+    {
+	c = line[col];
+	if (c == NUL)
+	    return -1;
+	else if (escape != NULL && vim_strchr(escape, c))
+	    ++col;
+	else if (c == quotechar)
+	    break;
+	if (has_mbyte)
+	    col += (*mb_ptr2len)(line + col);
+	else
+	    ++col;
+    }
+    return col;
+}
+
+/*
+ * Search backwards in "line" from column "col_start" to find "quotechar".
+ * Quote character escaped by one of the characters in "escape" is not counted
+ * as a quote.
+ * Return the found column or zero.
+ */
+    static int
+find_prev_quote(
+    char_u	*line,
+    int		col_start,
+    int		quotechar,
+    char_u	*escape)	// escape characters, can be NULL
+{
+    int		n;
+
+    while (col_start > 0)
+    {
+	--col_start;
+	col_start -= (*mb_head_off)(line, line + col_start);
+	n = 0;
+	if (escape != NULL)
+	    while (col_start - n > 0 && vim_strchr(escape,
+					     line[col_start - n - 1]) != NULL)
+	    ++n;
+	if (n & 1)
+	    col_start -= n;	// uneven number of escape chars, skip it
+	else if (line[col_start] == quotechar)
+	    break;
+    }
+    return col_start;
+}
+
+/*
+ * Find quote under the cursor, cursor at end.
+ * Returns TRUE if found, else FALSE.
+ */
+    int
+current_quote(
+    oparg_T	*oap,
+    long	count,
+    int		include,	// TRUE == include quote char
+    int		quotechar)	// Quote character
+{
+    char_u	*line = ml_get_curline();
+    int		col_end;
+    int		col_start = curwin->w_cursor.col;
+    int		inclusive = FALSE;
+    int		vis_empty = TRUE;	// Visual selection <= 1 char
+    int		vis_bef_curs = FALSE;	// Visual starts before cursor
+    int		did_exclusive_adj = FALSE;  // adjusted pos for 'selection'
+    int		inside_quotes = FALSE;	// Looks like "i'" done before
+    int		selected_quote = FALSE;	// Has quote inside selection
+    int		i;
+    int		restore_vis_bef = FALSE; // restore VIsual on abort
+
+    // When 'selection' is "exclusive" move the cursor to where it would be
+    // with 'selection' "inclusive", so that the logic is the same for both.
+    // The cursor then is moved forward after adjusting the area.
+    if (VIsual_active)
+    {
+	// this only works within one line
+	if (VIsual.lnum != curwin->w_cursor.lnum)
+	    return FALSE;
+
+	vis_bef_curs = LT_POS(VIsual, curwin->w_cursor);
+	vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
+	if (*p_sel == 'e')
+	{
+	    if (vis_bef_curs)
+	    {
+		dec_cursor();
+		did_exclusive_adj = TRUE;
+	    }
+	    else if (!vis_empty)
+	    {
+		dec(&VIsual);
+		did_exclusive_adj = TRUE;
+	    }
+	    vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
+	    if (!vis_bef_curs && !vis_empty)
+	    {
+		// VIsual needs to be the start of Visual selection.
+		pos_T t = curwin->w_cursor;
+
+		curwin->w_cursor = VIsual;
+		VIsual = t;
+		vis_bef_curs = TRUE;
+		restore_vis_bef = TRUE;
+	    }
+	}
+    }
+
+    if (!vis_empty)
+    {
+	// Check if the existing selection exactly spans the text inside
+	// quotes.
+	if (vis_bef_curs)
+	{
+	    inside_quotes = VIsual.col > 0
+			&& line[VIsual.col - 1] == quotechar
+			&& line[curwin->w_cursor.col] != NUL
+			&& line[curwin->w_cursor.col + 1] == quotechar;
+	    i = VIsual.col;
+	    col_end = curwin->w_cursor.col;
+	}
+	else
+	{
+	    inside_quotes = curwin->w_cursor.col > 0
+			&& line[curwin->w_cursor.col - 1] == quotechar
+			&& line[VIsual.col] != NUL
+			&& line[VIsual.col + 1] == quotechar;
+	    i = curwin->w_cursor.col;
+	    col_end = VIsual.col;
+	}
+
+	// Find out if we have a quote in the selection.
+	while (i <= col_end)
+	    if (line[i++] == quotechar)
+	    {
+		selected_quote = TRUE;
+		break;
+	    }
+    }
+
+    if (!vis_empty && line[col_start] == quotechar)
+    {
+	// Already selecting something and on a quote character.  Find the
+	// next quoted string.
+	if (vis_bef_curs)
+	{
+	    // Assume we are on a closing quote: move to after the next
+	    // opening quote.
+	    col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
+	    if (col_start < 0)
+		goto abort_search;
+	    col_end = find_next_quote(line, col_start + 1, quotechar,
+							      curbuf->b_p_qe);
+	    if (col_end < 0)
+	    {
+		// We were on a starting quote perhaps?
+		col_end = col_start;
+		col_start = curwin->w_cursor.col;
+	    }
+	}
+	else
+	{
+	    col_end = find_prev_quote(line, col_start, quotechar, NULL);
+	    if (line[col_end] != quotechar)
+		goto abort_search;
+	    col_start = find_prev_quote(line, col_end, quotechar,
+							      curbuf->b_p_qe);
+	    if (line[col_start] != quotechar)
+	    {
+		// We were on an ending quote perhaps?
+		col_start = col_end;
+		col_end = curwin->w_cursor.col;
+	    }
+	}
+    }
+    else
+
+    if (line[col_start] == quotechar || !vis_empty)
+    {
+	int	first_col = col_start;
+
+	if (!vis_empty)
+	{
+	    if (vis_bef_curs)
+		first_col = find_next_quote(line, col_start, quotechar, NULL);
+	    else
+		first_col = find_prev_quote(line, col_start, quotechar, NULL);
+	}
+
+	// The cursor is on a quote, we don't know if it's the opening or
+	// closing quote.  Search from the start of the line to find out.
+	// Also do this when there is a Visual area, a' may leave the cursor
+	// in between two strings.
+	col_start = 0;
+	for (;;)
+	{
+	    // Find open quote character.
+	    col_start = find_next_quote(line, col_start, quotechar, NULL);
+	    if (col_start < 0 || col_start > first_col)
+		goto abort_search;
+	    // Find close quote character.
+	    col_end = find_next_quote(line, col_start + 1, quotechar,
+							      curbuf->b_p_qe);
+	    if (col_end < 0)
+		goto abort_search;
+	    // If is cursor between start and end quote character, it is
+	    // target text object.
+	    if (col_start <= first_col && first_col <= col_end)
+		break;
+	    col_start = col_end + 1;
+	}
+    }
+    else
+    {
+	// Search backward for a starting quote.
+	col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
+	if (line[col_start] != quotechar)
+	{
+	    // No quote before the cursor, look after the cursor.
+	    col_start = find_next_quote(line, col_start, quotechar, NULL);
+	    if (col_start < 0)
+		goto abort_search;
+	}
+
+	// Find close quote character.
+	col_end = find_next_quote(line, col_start + 1, quotechar,
+							      curbuf->b_p_qe);
+	if (col_end < 0)
+	    goto abort_search;
+    }
+
+    // When "include" is TRUE, include spaces after closing quote or before
+    // the starting quote.
+    if (include)
+    {
+	if (VIM_ISWHITE(line[col_end + 1]))
+	    while (VIM_ISWHITE(line[col_end + 1]))
+		++col_end;
+	else
+	    while (col_start > 0 && VIM_ISWHITE(line[col_start - 1]))
+		--col_start;
+    }
+
+    // Set start position.  After vi" another i" must include the ".
+    // For v2i" include the quotes.
+    if (!include && count < 2 && (vis_empty || !inside_quotes))
+	++col_start;
+    curwin->w_cursor.col = col_start;
+    if (VIsual_active)
+    {
+	// Set the start of the Visual area when the Visual area was empty, we
+	// were just inside quotes or the Visual area didn't start at a quote
+	// and didn't include a quote.
+	if (vis_empty
+		|| (vis_bef_curs
+		    && !selected_quote
+		    && (inside_quotes
+			|| (line[VIsual.col] != quotechar
+			    && (VIsual.col == 0
+				|| line[VIsual.col - 1] != quotechar)))))
+	{
+	    VIsual = curwin->w_cursor;
+	    redraw_curbuf_later(INVERTED);
+	}
+    }
+    else
+    {
+	oap->start = curwin->w_cursor;
+	oap->motion_type = MCHAR;
+    }
+
+    // Set end position.
+    curwin->w_cursor.col = col_end;
+    if ((include || count > 1 // After vi" another i" must include the ".
+		|| (!vis_empty && inside_quotes)
+	) && inc_cursor() == 2)
+	inclusive = TRUE;
+    if (VIsual_active)
+    {
+	if (vis_empty || vis_bef_curs)
+	{
+	    // decrement cursor when 'selection' is not exclusive
+	    if (*p_sel != 'e')
+		dec_cursor();
+	}
+	else
+	{
+	    // Cursor is at start of Visual area.  Set the end of the Visual
+	    // area when it was just inside quotes or it didn't end at a
+	    // quote.
+	    if (inside_quotes
+		    || (!selected_quote
+			&& line[VIsual.col] != quotechar
+			&& (line[VIsual.col] == NUL
+			    || line[VIsual.col + 1] != quotechar)))
+	    {
+		dec_cursor();
+		VIsual = curwin->w_cursor;
+	    }
+	    curwin->w_cursor.col = col_start;
+	}
+	if (VIsual_mode == 'V')
+	{
+	    VIsual_mode = 'v';
+	    redraw_cmdline = TRUE;		// show mode later
+	}
+    }
+    else
+    {
+	// Set inclusive and other oap's flags.
+	oap->inclusive = inclusive;
+    }
+
+    return OK;
+
+abort_search:
+    if (VIsual_active && *p_sel == 'e')
+    {
+	if (did_exclusive_adj)
+	    inc_cursor();
+	if (restore_vis_bef)
+	{
+	    pos_T t = curwin->w_cursor;
+
+	    curwin->w_cursor = VIsual;
+	    VIsual = t;
+	}
+    }
+    return FALSE;
+}
+
+#endif // FEAT_TEXTOBJ
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    660,
+/**/
     659,
 /**/
     658,