]> granicus.if.org Git - vim/commitdiff
patch 8.2.0660: the search.c file is a bit big v8.2.0660
authorBram Moolenaar <Bram@vim.org>
Wed, 29 Apr 2020 19:04:15 +0000 (21:04 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 29 Apr 2020 19:04:15 +0000 (21:04 +0200)
Problem:    The search.c file is a bit big.
Solution:   Split off the text object code to a separate file. (Yegappan
            Lakshmanan, closes #6007)

13 files changed:
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 [new file with mode: 0644]
src/search.c
src/textobject.c [new file with mode: 0644]
src/version.c

index 46f6774e766e61f6c32008807c872191405b1022..ef65b8da240ed4090456f478b81c771d36f3e40a 100644 (file)
--- 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 \
index 750e940e067fc0d6f4b396551a9cba06249546c1..7964b0a1f2065e9688a9fc2174bb7c03620d421e 100644 (file)
@@ -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 \
index 1830d85b50050affdd15c115ded34044dde80f33..d326174cd2b9d1613191e639d7f32bcc19bb29dd 100644 (file)
@@ -103,6 +103,7 @@ SRC =       arabic.c                                                \
        tag.c                                                   \
        term.c                                                  \
        testing.c                                               \
+       textobject.c                                            \
        textprop.c                                              \
        time.c                                                  \
        ui.c                                                    \
index ebc059f8623fea0e891d276320ac918e6df05a56..c873ca0840f63cf33dc386590f84f43fc88dff87 100644 (file)
@@ -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 @@ $(OUTDIR)/term.obj:       $(OUTDIR) term.c  $(INCL)
 
 $(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 \
index fdd7ba7c1e48782ec9505cbe7eed20c3675d9956..e04020172a3ae609a95d86c812b32b63bb4fc9f0 100644 (file)
@@ -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]config.h feature.h os_unix.h   \
 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
index 19453c7421b5d0d8e63f145a109195fe3d2de362..c8e0f087fed53102a0dc28388d7a942edc2ab2fb 100644 (file)
@@ -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_DEPS)
 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 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/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 \
index 54be08284f0c1fb1b6ade058466a58e39dec548c..765313da7eaae25a73cabb68f72f842154da7236 100644 (file)
@@ -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
index 0115c68d1991a0300043f0e03b304c6604445a2f..d54936c751f3f1ed90c7cacdaab3fb93d0a5a530 100644 (file)
@@ -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"
index 018c33a4f2c4b704c71e8aab4852111e597353e4..1e15a8828e861b22cbcbc95dfd369f808216c613 100644 (file)
@@ -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);
diff --git a/src/proto/textobject.pro b/src/proto/textobject.pro
new file mode 100644 (file)
index 0000000..7931979
--- /dev/null
@@ -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 : */
+
index 8c335ffd3ab10ee90060ffda63aaddc7c8e01625..0bf49629eb206daf8315b4a09bd571a3f3e65827 100644 (file)
@@ -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);
@@ -2837,1955 +2835,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
diff --git a/src/textobject.c b/src/textobject.c
new file mode 100644 (file)
index 0000000..e876d52
--- /dev/null
@@ -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
index d3a1b0b7fee38a60ee8cf7b734985d1368430137..ad2e1c3d76f56f59a5f52791e5ec7d189891f58b 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    660,
 /**/
     659,
 /**/