xref: /vim-8.2.3635/src/textobject.c (revision b9115da4)
1ed8ce057SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2ed8ce057SBram Moolenaar  *
3ed8ce057SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4ed8ce057SBram Moolenaar  *
5ed8ce057SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6ed8ce057SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7ed8ce057SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8ed8ce057SBram Moolenaar  */
9ed8ce057SBram Moolenaar 
10ed8ce057SBram Moolenaar /*
11ed8ce057SBram Moolenaar  * textobject.c: functions for text objects
12ed8ce057SBram Moolenaar  */
13ed8ce057SBram Moolenaar #include "vim.h"
14ed8ce057SBram Moolenaar 
15ed8ce057SBram Moolenaar static int cls(void);
16ed8ce057SBram Moolenaar static int skip_chars(int, int);
17ed8ce057SBram Moolenaar 
18ed8ce057SBram Moolenaar /*
19ed8ce057SBram Moolenaar  * Find the start of the next sentence, searching in the direction specified
20ed8ce057SBram Moolenaar  * by the "dir" argument.  The cursor is positioned on the start of the next
21ed8ce057SBram Moolenaar  * sentence when found.  If the next sentence is found, return OK.  Return FAIL
22ed8ce057SBram Moolenaar  * otherwise.  See ":h sentence" for the precise definition of a "sentence"
23ed8ce057SBram Moolenaar  * text object.
24ed8ce057SBram Moolenaar  */
25ed8ce057SBram Moolenaar     int
findsent(int dir,long count)26ed8ce057SBram Moolenaar findsent(int dir, long count)
27ed8ce057SBram Moolenaar {
28ed8ce057SBram Moolenaar     pos_T	pos, tpos;
292f03e5a0SBram Moolenaar     pos_T	prev_pos;
30ed8ce057SBram Moolenaar     int		c;
31ed8ce057SBram Moolenaar     int		(*func)(pos_T *);
32ed8ce057SBram Moolenaar     int		startlnum;
33ed8ce057SBram Moolenaar     int		noskip = FALSE;	    // do not skip blanks
34ed8ce057SBram Moolenaar     int		cpo_J;
35ed8ce057SBram Moolenaar     int		found_dot;
36ed8ce057SBram Moolenaar 
37ed8ce057SBram Moolenaar     pos = curwin->w_cursor;
38ed8ce057SBram Moolenaar     if (dir == FORWARD)
39ed8ce057SBram Moolenaar 	func = incl;
40ed8ce057SBram Moolenaar     else
41ed8ce057SBram Moolenaar 	func = decl;
42ed8ce057SBram Moolenaar 
43ed8ce057SBram Moolenaar     while (count--)
44ed8ce057SBram Moolenaar     {
452f03e5a0SBram Moolenaar 	prev_pos = pos;
462f03e5a0SBram Moolenaar 
47ed8ce057SBram Moolenaar 	/*
48ed8ce057SBram Moolenaar 	 * if on an empty line, skip up to a non-empty line
49ed8ce057SBram Moolenaar 	 */
50ed8ce057SBram Moolenaar 	if (gchar_pos(&pos) == NUL)
51ed8ce057SBram Moolenaar 	{
52ed8ce057SBram Moolenaar 	    do
53ed8ce057SBram Moolenaar 		if ((*func)(&pos) == -1)
54ed8ce057SBram Moolenaar 		    break;
55ed8ce057SBram Moolenaar 	    while (gchar_pos(&pos) == NUL);
56ed8ce057SBram Moolenaar 	    if (dir == FORWARD)
57ed8ce057SBram Moolenaar 		goto found;
58ed8ce057SBram Moolenaar 	}
59ed8ce057SBram Moolenaar 	/*
60ed8ce057SBram Moolenaar 	 * if on the start of a paragraph or a section and searching forward,
61ed8ce057SBram Moolenaar 	 * go to the next line
62ed8ce057SBram Moolenaar 	 */
63ed8ce057SBram Moolenaar 	else if (dir == FORWARD && pos.col == 0 &&
64ed8ce057SBram Moolenaar 						startPS(pos.lnum, NUL, FALSE))
65ed8ce057SBram Moolenaar 	{
66ed8ce057SBram Moolenaar 	    if (pos.lnum == curbuf->b_ml.ml_line_count)
67ed8ce057SBram Moolenaar 		return FAIL;
68ed8ce057SBram Moolenaar 	    ++pos.lnum;
69ed8ce057SBram Moolenaar 	    goto found;
70ed8ce057SBram Moolenaar 	}
71ed8ce057SBram Moolenaar 	else if (dir == BACKWARD)
72ed8ce057SBram Moolenaar 	    decl(&pos);
73ed8ce057SBram Moolenaar 
74ed8ce057SBram Moolenaar 	// go back to the previous non-white non-punctuation character
75ed8ce057SBram Moolenaar 	found_dot = FALSE;
76ed8ce057SBram Moolenaar 	while (c = gchar_pos(&pos), VIM_ISWHITE(c)
77ed8ce057SBram Moolenaar 				|| vim_strchr((char_u *)".!?)]\"'", c) != NULL)
78ed8ce057SBram Moolenaar 	{
79ed8ce057SBram Moolenaar 	    tpos = pos;
80ed8ce057SBram Moolenaar 	    if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD))
81ed8ce057SBram Moolenaar 		break;
82ed8ce057SBram Moolenaar 
83ed8ce057SBram Moolenaar 	    if (found_dot)
84ed8ce057SBram Moolenaar 		break;
85ed8ce057SBram Moolenaar 	    if (vim_strchr((char_u *) ".!?", c) != NULL)
86ed8ce057SBram Moolenaar 		found_dot = TRUE;
87ed8ce057SBram Moolenaar 
88ed8ce057SBram Moolenaar 	    if (vim_strchr((char_u *) ")]\"'", c) != NULL
89ed8ce057SBram Moolenaar 		&& vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL)
90ed8ce057SBram Moolenaar 		break;
91ed8ce057SBram Moolenaar 
92ed8ce057SBram Moolenaar 	    decl(&pos);
93ed8ce057SBram Moolenaar 	}
94ed8ce057SBram Moolenaar 
95ed8ce057SBram Moolenaar 	// remember the line where the search started
96ed8ce057SBram Moolenaar 	startlnum = pos.lnum;
97ed8ce057SBram Moolenaar 	cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
98ed8ce057SBram Moolenaar 
99ed8ce057SBram Moolenaar 	for (;;)		// find end of sentence
100ed8ce057SBram Moolenaar 	{
101ed8ce057SBram Moolenaar 	    c = gchar_pos(&pos);
102ed8ce057SBram Moolenaar 	    if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
103ed8ce057SBram Moolenaar 	    {
104ed8ce057SBram Moolenaar 		if (dir == BACKWARD && pos.lnum != startlnum)
105ed8ce057SBram Moolenaar 		    ++pos.lnum;
106ed8ce057SBram Moolenaar 		break;
107ed8ce057SBram Moolenaar 	    }
108ed8ce057SBram Moolenaar 	    if (c == '.' || c == '!' || c == '?')
109ed8ce057SBram Moolenaar 	    {
110ed8ce057SBram Moolenaar 		tpos = pos;
111ed8ce057SBram Moolenaar 		do
112ed8ce057SBram Moolenaar 		    if ((c = inc(&tpos)) == -1)
113ed8ce057SBram Moolenaar 			break;
114ed8ce057SBram Moolenaar 		while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
115ed8ce057SBram Moolenaar 			!= NULL);
116ed8ce057SBram Moolenaar 		if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
117ed8ce057SBram Moolenaar 		    || (cpo_J && (c == ' ' && inc(&tpos) >= 0
118ed8ce057SBram Moolenaar 			      && gchar_pos(&tpos) == ' ')))
119ed8ce057SBram Moolenaar 		{
120ed8ce057SBram Moolenaar 		    pos = tpos;
121ed8ce057SBram Moolenaar 		    if (gchar_pos(&pos) == NUL) // skip NUL at EOL
122ed8ce057SBram Moolenaar 			inc(&pos);
123ed8ce057SBram Moolenaar 		    break;
124ed8ce057SBram Moolenaar 		}
125ed8ce057SBram Moolenaar 	    }
126ed8ce057SBram Moolenaar 	    if ((*func)(&pos) == -1)
127ed8ce057SBram Moolenaar 	    {
128ed8ce057SBram Moolenaar 		if (count)
129ed8ce057SBram Moolenaar 		    return FAIL;
130ed8ce057SBram Moolenaar 		noskip = TRUE;
131ed8ce057SBram Moolenaar 		break;
132ed8ce057SBram Moolenaar 	    }
133ed8ce057SBram Moolenaar 	}
134ed8ce057SBram Moolenaar found:
135ed8ce057SBram Moolenaar 	    // skip white space
136ed8ce057SBram Moolenaar 	while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
137ed8ce057SBram Moolenaar 	    if (incl(&pos) == -1)
138ed8ce057SBram Moolenaar 		break;
1392f03e5a0SBram Moolenaar 
1402f03e5a0SBram Moolenaar 	if (EQUAL_POS(prev_pos, pos))
1412f03e5a0SBram Moolenaar 	{
1422f03e5a0SBram Moolenaar 	    // didn't actually move, advance one character and try again
1432f03e5a0SBram Moolenaar 	    if ((*func)(&pos) == -1)
1442f03e5a0SBram Moolenaar 	    {
1452f03e5a0SBram Moolenaar 		if (count)
1462f03e5a0SBram Moolenaar 		    return FAIL;
1472f03e5a0SBram Moolenaar 		break;
1482f03e5a0SBram Moolenaar 	    }
1492f03e5a0SBram Moolenaar 	    ++count;
1502f03e5a0SBram Moolenaar 	}
151ed8ce057SBram Moolenaar     }
152ed8ce057SBram Moolenaar 
153ed8ce057SBram Moolenaar     setpcmark();
154ed8ce057SBram Moolenaar     curwin->w_cursor = pos;
155ed8ce057SBram Moolenaar     return OK;
156ed8ce057SBram Moolenaar }
157ed8ce057SBram Moolenaar 
158ed8ce057SBram Moolenaar /*
159ed8ce057SBram Moolenaar  * Find the next paragraph or section in direction 'dir'.
160ed8ce057SBram Moolenaar  * Paragraphs are currently supposed to be separated by empty lines.
161ed8ce057SBram Moolenaar  * If 'what' is NUL we go to the next paragraph.
162ed8ce057SBram Moolenaar  * If 'what' is '{' or '}' we go to the next section.
163ed8ce057SBram Moolenaar  * If 'both' is TRUE also stop at '}'.
164ed8ce057SBram Moolenaar  * Return TRUE if the next paragraph or section was found.
165ed8ce057SBram Moolenaar  */
166ed8ce057SBram Moolenaar     int
findpar(int * pincl,int dir,long count,int what,int both)167ed8ce057SBram Moolenaar findpar(
168ed8ce057SBram Moolenaar     int		*pincl,	    // Return: TRUE if last char is to be included
169ed8ce057SBram Moolenaar     int		dir,
170ed8ce057SBram Moolenaar     long	count,
171ed8ce057SBram Moolenaar     int		what,
172ed8ce057SBram Moolenaar     int		both)
173ed8ce057SBram Moolenaar {
174ed8ce057SBram Moolenaar     linenr_T	curr;
175ed8ce057SBram Moolenaar     int		did_skip;   // TRUE after separating lines have been skipped
176ed8ce057SBram Moolenaar     int		first;	    // TRUE on first line
177ed8ce057SBram Moolenaar     int		posix = (vim_strchr(p_cpo, CPO_PARA) != NULL);
178ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
179ed8ce057SBram Moolenaar     linenr_T	fold_first;	// first line of a closed fold
180ed8ce057SBram Moolenaar     linenr_T	fold_last;	// last line of a closed fold
181ed8ce057SBram Moolenaar     int		fold_skipped;	// TRUE if a closed fold was skipped this
182ed8ce057SBram Moolenaar 				// iteration
183ed8ce057SBram Moolenaar #endif
184ed8ce057SBram Moolenaar 
185ed8ce057SBram Moolenaar     curr = curwin->w_cursor.lnum;
186ed8ce057SBram Moolenaar 
187ed8ce057SBram Moolenaar     while (count--)
188ed8ce057SBram Moolenaar     {
189ed8ce057SBram Moolenaar 	did_skip = FALSE;
190ed8ce057SBram Moolenaar 	for (first = TRUE; ; first = FALSE)
191ed8ce057SBram Moolenaar 	{
192ed8ce057SBram Moolenaar 	    if (*ml_get(curr) != NUL)
193ed8ce057SBram Moolenaar 		did_skip = TRUE;
194ed8ce057SBram Moolenaar 
195ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
196ed8ce057SBram Moolenaar 	    // skip folded lines
197ed8ce057SBram Moolenaar 	    fold_skipped = FALSE;
198ed8ce057SBram Moolenaar 	    if (first && hasFolding(curr, &fold_first, &fold_last))
199ed8ce057SBram Moolenaar 	    {
200ed8ce057SBram Moolenaar 		curr = ((dir > 0) ? fold_last : fold_first) + dir;
201ed8ce057SBram Moolenaar 		fold_skipped = TRUE;
202ed8ce057SBram Moolenaar 	    }
203ed8ce057SBram Moolenaar #endif
204ed8ce057SBram Moolenaar 
205ed8ce057SBram Moolenaar 	    // POSIX has its own ideas of what a paragraph boundary is and it
206ed8ce057SBram Moolenaar 	    // doesn't match historical Vi: It also stops at a "{" in the
207ed8ce057SBram Moolenaar 	    // first column and at an empty line.
208ed8ce057SBram Moolenaar 	    if (!first && did_skip && (startPS(curr, what, both)
209ed8ce057SBram Moolenaar 			   || (posix && what == NUL && *ml_get(curr) == '{')))
210ed8ce057SBram Moolenaar 		break;
211ed8ce057SBram Moolenaar 
212ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
213ed8ce057SBram Moolenaar 	    if (fold_skipped)
214ed8ce057SBram Moolenaar 		curr -= dir;
215ed8ce057SBram Moolenaar #endif
216ed8ce057SBram Moolenaar 	    if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
217ed8ce057SBram Moolenaar 	    {
218ed8ce057SBram Moolenaar 		if (count)
219ed8ce057SBram Moolenaar 		    return FALSE;
220ed8ce057SBram Moolenaar 		curr -= dir;
221ed8ce057SBram Moolenaar 		break;
222ed8ce057SBram Moolenaar 	    }
223ed8ce057SBram Moolenaar 	}
224ed8ce057SBram Moolenaar     }
225ed8ce057SBram Moolenaar     setpcmark();
226ed8ce057SBram Moolenaar     if (both && *ml_get(curr) == '}')	// include line with '}'
227ed8ce057SBram Moolenaar 	++curr;
228ed8ce057SBram Moolenaar     curwin->w_cursor.lnum = curr;
229ed8ce057SBram Moolenaar     if (curr == curbuf->b_ml.ml_line_count && what != '}')
230ed8ce057SBram Moolenaar     {
231ed8ce057SBram Moolenaar 	char_u *line = ml_get(curr);
232ed8ce057SBram Moolenaar 
233ed8ce057SBram Moolenaar 	// Put the cursor on the last character in the last line and make the
234ed8ce057SBram Moolenaar 	// motion inclusive.
235ed8ce057SBram Moolenaar 	if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0)
236ed8ce057SBram Moolenaar 	{
237ed8ce057SBram Moolenaar 	    --curwin->w_cursor.col;
238ed8ce057SBram Moolenaar 	    curwin->w_cursor.col -=
239ed8ce057SBram Moolenaar 			     (*mb_head_off)(line, line + curwin->w_cursor.col);
240ed8ce057SBram Moolenaar 	    *pincl = TRUE;
241ed8ce057SBram Moolenaar 	}
242ed8ce057SBram Moolenaar     }
243ed8ce057SBram Moolenaar     else
244ed8ce057SBram Moolenaar 	curwin->w_cursor.col = 0;
245ed8ce057SBram Moolenaar     return TRUE;
246ed8ce057SBram Moolenaar }
247ed8ce057SBram Moolenaar 
248ed8ce057SBram Moolenaar /*
249ed8ce057SBram Moolenaar  * check if the string 's' is a nroff macro that is in option 'opt'
250ed8ce057SBram Moolenaar  */
251ed8ce057SBram Moolenaar     static int
inmacro(char_u * opt,char_u * s)252ed8ce057SBram Moolenaar inmacro(char_u *opt, char_u *s)
253ed8ce057SBram Moolenaar {
254ed8ce057SBram Moolenaar     char_u	*macro;
255ed8ce057SBram Moolenaar 
256ed8ce057SBram Moolenaar     for (macro = opt; macro[0]; ++macro)
257ed8ce057SBram Moolenaar     {
258ed8ce057SBram Moolenaar 	// Accept two characters in the option being equal to two characters
259ed8ce057SBram Moolenaar 	// in the line.  A space in the option matches with a space in the
260ed8ce057SBram Moolenaar 	// line or the line having ended.
261ed8ce057SBram Moolenaar 	if (       (macro[0] == s[0]
262ed8ce057SBram Moolenaar 		    || (macro[0] == ' '
263ed8ce057SBram Moolenaar 			&& (s[0] == NUL || s[0] == ' ')))
264ed8ce057SBram Moolenaar 		&& (macro[1] == s[1]
265ed8ce057SBram Moolenaar 		    || ((macro[1] == NUL || macro[1] == ' ')
266ed8ce057SBram Moolenaar 			&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
267ed8ce057SBram Moolenaar 	    break;
268ed8ce057SBram Moolenaar 	++macro;
269ed8ce057SBram Moolenaar 	if (macro[0] == NUL)
270ed8ce057SBram Moolenaar 	    break;
271ed8ce057SBram Moolenaar     }
272ed8ce057SBram Moolenaar     return (macro[0] != NUL);
273ed8ce057SBram Moolenaar }
274ed8ce057SBram Moolenaar 
275ed8ce057SBram Moolenaar /*
276ed8ce057SBram Moolenaar  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
277ed8ce057SBram Moolenaar  * If 'para' is '{' or '}' only check for sections.
278ed8ce057SBram Moolenaar  * If 'both' is TRUE also stop at '}'
279ed8ce057SBram Moolenaar  */
280ed8ce057SBram Moolenaar     int
startPS(linenr_T lnum,int para,int both)281ed8ce057SBram Moolenaar startPS(linenr_T lnum, int para, int both)
282ed8ce057SBram Moolenaar {
283ed8ce057SBram Moolenaar     char_u	*s;
284ed8ce057SBram Moolenaar 
285ed8ce057SBram Moolenaar     s = ml_get(lnum);
286ed8ce057SBram Moolenaar     if (*s == para || *s == '\f' || (both && *s == '}'))
287ed8ce057SBram Moolenaar 	return TRUE;
288ed8ce057SBram Moolenaar     if (*s == '.' && (inmacro(p_sections, s + 1) ||
289ed8ce057SBram Moolenaar 					   (!para && inmacro(p_para, s + 1))))
290ed8ce057SBram Moolenaar 	return TRUE;
291ed8ce057SBram Moolenaar     return FALSE;
292ed8ce057SBram Moolenaar }
293ed8ce057SBram Moolenaar 
294ed8ce057SBram Moolenaar /*
295ed8ce057SBram Moolenaar  * The following routines do the word searches performed by the 'w', 'W',
296ed8ce057SBram Moolenaar  * 'b', 'B', 'e', and 'E' commands.
297ed8ce057SBram Moolenaar  */
298ed8ce057SBram Moolenaar 
299ed8ce057SBram Moolenaar /*
300ed8ce057SBram Moolenaar  * To perform these searches, characters are placed into one of three
301ed8ce057SBram Moolenaar  * classes, and transitions between classes determine word boundaries.
302ed8ce057SBram Moolenaar  *
303ed8ce057SBram Moolenaar  * The classes are:
304ed8ce057SBram Moolenaar  *
305ed8ce057SBram Moolenaar  * 0 - white space
306ed8ce057SBram Moolenaar  * 1 - punctuation
307ed8ce057SBram Moolenaar  * 2 or higher - keyword characters (letters, digits and underscore)
308ed8ce057SBram Moolenaar  */
309ed8ce057SBram Moolenaar 
310ed8ce057SBram Moolenaar static int	cls_bigword;	// TRUE for "W", "B" or "E"
311ed8ce057SBram Moolenaar 
312ed8ce057SBram Moolenaar /*
313ed8ce057SBram Moolenaar  * cls() - returns the class of character at curwin->w_cursor
314ed8ce057SBram Moolenaar  *
315ed8ce057SBram Moolenaar  * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
316ed8ce057SBram Moolenaar  * from class 2 and higher are reported as class 1 since only white space
317ed8ce057SBram Moolenaar  * boundaries are of interest.
318ed8ce057SBram Moolenaar  */
319ed8ce057SBram Moolenaar     static int
cls(void)320ed8ce057SBram Moolenaar cls(void)
321ed8ce057SBram Moolenaar {
322ed8ce057SBram Moolenaar     int	    c;
323ed8ce057SBram Moolenaar 
324ed8ce057SBram Moolenaar     c = gchar_cursor();
325ed8ce057SBram Moolenaar     if (c == ' ' || c == '\t' || c == NUL)
326ed8ce057SBram Moolenaar 	return 0;
327ed8ce057SBram Moolenaar     if (enc_dbcs != 0 && c > 0xFF)
328ed8ce057SBram Moolenaar     {
329ed8ce057SBram Moolenaar 	// If cls_bigword, report multi-byte chars as class 1.
330ed8ce057SBram Moolenaar 	if (enc_dbcs == DBCS_KOR && cls_bigword)
331ed8ce057SBram Moolenaar 	    return 1;
332ed8ce057SBram Moolenaar 
333ed8ce057SBram Moolenaar 	// process code leading/trailing bytes
334ed8ce057SBram Moolenaar 	return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
335ed8ce057SBram Moolenaar     }
336ed8ce057SBram Moolenaar     if (enc_utf8)
337ed8ce057SBram Moolenaar     {
338ed8ce057SBram Moolenaar 	c = utf_class(c);
339ed8ce057SBram Moolenaar 	if (c != 0 && cls_bigword)
340ed8ce057SBram Moolenaar 	    return 1;
341ed8ce057SBram Moolenaar 	return c;
342ed8ce057SBram Moolenaar     }
343ed8ce057SBram Moolenaar 
344ed8ce057SBram Moolenaar     // If cls_bigword is TRUE, report all non-blanks as class 1.
345ed8ce057SBram Moolenaar     if (cls_bigword)
346ed8ce057SBram Moolenaar 	return 1;
347ed8ce057SBram Moolenaar 
348ed8ce057SBram Moolenaar     if (vim_iswordc(c))
349ed8ce057SBram Moolenaar 	return 2;
350ed8ce057SBram Moolenaar     return 1;
351ed8ce057SBram Moolenaar }
352ed8ce057SBram Moolenaar 
353ed8ce057SBram Moolenaar 
354ed8ce057SBram Moolenaar /*
355ed8ce057SBram Moolenaar  * fwd_word(count, type, eol) - move forward one word
356ed8ce057SBram Moolenaar  *
357ed8ce057SBram Moolenaar  * Returns FAIL if the cursor was already at the end of the file.
358ed8ce057SBram Moolenaar  * If eol is TRUE, last word stops at end of line (for operators).
359ed8ce057SBram Moolenaar  */
360ed8ce057SBram Moolenaar     int
fwd_word(long count,int bigword,int eol)361ed8ce057SBram Moolenaar fwd_word(
362ed8ce057SBram Moolenaar     long	count,
363ed8ce057SBram Moolenaar     int		bigword,    // "W", "E" or "B"
364ed8ce057SBram Moolenaar     int		eol)
365ed8ce057SBram Moolenaar {
366ed8ce057SBram Moolenaar     int		sclass;	    // starting class
367ed8ce057SBram Moolenaar     int		i;
368ed8ce057SBram Moolenaar     int		last_line;
369ed8ce057SBram Moolenaar 
370ed8ce057SBram Moolenaar     curwin->w_cursor.coladd = 0;
371ed8ce057SBram Moolenaar     cls_bigword = bigword;
372ed8ce057SBram Moolenaar     while (--count >= 0)
373ed8ce057SBram Moolenaar     {
374ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
375ed8ce057SBram Moolenaar 	// When inside a range of folded lines, move to the last char of the
376ed8ce057SBram Moolenaar 	// last line.
377ed8ce057SBram Moolenaar 	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
378ed8ce057SBram Moolenaar 	    coladvance((colnr_T)MAXCOL);
379ed8ce057SBram Moolenaar #endif
380ed8ce057SBram Moolenaar 	sclass = cls();
381ed8ce057SBram Moolenaar 
382ed8ce057SBram Moolenaar 	/*
383ed8ce057SBram Moolenaar 	 * We always move at least one character, unless on the last
384ed8ce057SBram Moolenaar 	 * character in the buffer.
385ed8ce057SBram Moolenaar 	 */
386ed8ce057SBram Moolenaar 	last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
387ed8ce057SBram Moolenaar 	i = inc_cursor();
388ed8ce057SBram Moolenaar 	if (i == -1 || (i >= 1 && last_line)) // started at last char in file
389ed8ce057SBram Moolenaar 	    return FAIL;
390ed8ce057SBram Moolenaar 	if (i >= 1 && eol && count == 0)      // started at last char in line
391ed8ce057SBram Moolenaar 	    return OK;
392ed8ce057SBram Moolenaar 
393ed8ce057SBram Moolenaar 	/*
394ed8ce057SBram Moolenaar 	 * Go one char past end of current word (if any)
395ed8ce057SBram Moolenaar 	 */
396ed8ce057SBram Moolenaar 	if (sclass != 0)
397ed8ce057SBram Moolenaar 	    while (cls() == sclass)
398ed8ce057SBram Moolenaar 	    {
399ed8ce057SBram Moolenaar 		i = inc_cursor();
400ed8ce057SBram Moolenaar 		if (i == -1 || (i >= 1 && eol && count == 0))
401ed8ce057SBram Moolenaar 		    return OK;
402ed8ce057SBram Moolenaar 	    }
403ed8ce057SBram Moolenaar 
404ed8ce057SBram Moolenaar 	/*
405ed8ce057SBram Moolenaar 	 * go to next non-white
406ed8ce057SBram Moolenaar 	 */
407ed8ce057SBram Moolenaar 	while (cls() == 0)
408ed8ce057SBram Moolenaar 	{
409ed8ce057SBram Moolenaar 	    /*
410ed8ce057SBram Moolenaar 	     * We'll stop if we land on a blank line
411ed8ce057SBram Moolenaar 	     */
412ed8ce057SBram Moolenaar 	    if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
413ed8ce057SBram Moolenaar 		break;
414ed8ce057SBram Moolenaar 
415ed8ce057SBram Moolenaar 	    i = inc_cursor();
416ed8ce057SBram Moolenaar 	    if (i == -1 || (i >= 1 && eol && count == 0))
417ed8ce057SBram Moolenaar 		return OK;
418ed8ce057SBram Moolenaar 	}
419ed8ce057SBram Moolenaar     }
420ed8ce057SBram Moolenaar     return OK;
421ed8ce057SBram Moolenaar }
422ed8ce057SBram Moolenaar 
423ed8ce057SBram Moolenaar /*
424ed8ce057SBram Moolenaar  * bck_word() - move backward 'count' words
425ed8ce057SBram Moolenaar  *
426ed8ce057SBram Moolenaar  * If stop is TRUE and we are already on the start of a word, move one less.
427ed8ce057SBram Moolenaar  *
428ed8ce057SBram Moolenaar  * Returns FAIL if top of the file was reached.
429ed8ce057SBram Moolenaar  */
430ed8ce057SBram Moolenaar     int
bck_word(long count,int bigword,int stop)431ed8ce057SBram Moolenaar bck_word(long count, int bigword, int stop)
432ed8ce057SBram Moolenaar {
433ed8ce057SBram Moolenaar     int		sclass;	    // starting class
434ed8ce057SBram Moolenaar 
435ed8ce057SBram Moolenaar     curwin->w_cursor.coladd = 0;
436ed8ce057SBram Moolenaar     cls_bigword = bigword;
437ed8ce057SBram Moolenaar     while (--count >= 0)
438ed8ce057SBram Moolenaar     {
439ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
440ed8ce057SBram Moolenaar 	// When inside a range of folded lines, move to the first char of the
441ed8ce057SBram Moolenaar 	// first line.
442ed8ce057SBram Moolenaar 	if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
443ed8ce057SBram Moolenaar 	    curwin->w_cursor.col = 0;
444ed8ce057SBram Moolenaar #endif
445ed8ce057SBram Moolenaar 	sclass = cls();
446ed8ce057SBram Moolenaar 	if (dec_cursor() == -1)		// started at start of file
447ed8ce057SBram Moolenaar 	    return FAIL;
448ed8ce057SBram Moolenaar 
449ed8ce057SBram Moolenaar 	if (!stop || sclass == cls() || sclass == 0)
450ed8ce057SBram Moolenaar 	{
451ed8ce057SBram Moolenaar 	    /*
452ed8ce057SBram Moolenaar 	     * Skip white space before the word.
453ed8ce057SBram Moolenaar 	     * Stop on an empty line.
454ed8ce057SBram Moolenaar 	     */
455ed8ce057SBram Moolenaar 	    while (cls() == 0)
456ed8ce057SBram Moolenaar 	    {
457ed8ce057SBram Moolenaar 		if (curwin->w_cursor.col == 0
458ed8ce057SBram Moolenaar 				      && LINEEMPTY(curwin->w_cursor.lnum))
459ed8ce057SBram Moolenaar 		    goto finished;
460ed8ce057SBram Moolenaar 		if (dec_cursor() == -1) // hit start of file, stop here
461ed8ce057SBram Moolenaar 		    return OK;
462ed8ce057SBram Moolenaar 	    }
463ed8ce057SBram Moolenaar 
464ed8ce057SBram Moolenaar 	    /*
465ed8ce057SBram Moolenaar 	     * Move backward to start of this word.
466ed8ce057SBram Moolenaar 	     */
467ed8ce057SBram Moolenaar 	    if (skip_chars(cls(), BACKWARD))
468ed8ce057SBram Moolenaar 		return OK;
469ed8ce057SBram Moolenaar 	}
470ed8ce057SBram Moolenaar 
471ed8ce057SBram Moolenaar 	inc_cursor();			// overshot - forward one
472ed8ce057SBram Moolenaar finished:
473ed8ce057SBram Moolenaar 	stop = FALSE;
474ed8ce057SBram Moolenaar     }
475ed8ce057SBram Moolenaar     return OK;
476ed8ce057SBram Moolenaar }
477ed8ce057SBram Moolenaar 
478ed8ce057SBram Moolenaar /*
479ed8ce057SBram Moolenaar  * end_word() - move to the end of the word
480ed8ce057SBram Moolenaar  *
481ed8ce057SBram Moolenaar  * There is an apparent bug in the 'e' motion of the real vi. At least on the
482ed8ce057SBram Moolenaar  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
483ed8ce057SBram Moolenaar  * motion crosses blank lines. When the real vi crosses a blank line in an
484ed8ce057SBram Moolenaar  * 'e' motion, the cursor is placed on the FIRST character of the next
485ed8ce057SBram Moolenaar  * non-blank line. The 'E' command, however, works correctly. Since this
486ed8ce057SBram Moolenaar  * appears to be a bug, I have not duplicated it here.
487ed8ce057SBram Moolenaar  *
488ed8ce057SBram Moolenaar  * Returns FAIL if end of the file was reached.
489ed8ce057SBram Moolenaar  *
490ed8ce057SBram Moolenaar  * If stop is TRUE and we are already on the end of a word, move one less.
491ed8ce057SBram Moolenaar  * If empty is TRUE stop on an empty line.
492ed8ce057SBram Moolenaar  */
493ed8ce057SBram Moolenaar     int
end_word(long count,int bigword,int stop,int empty)494ed8ce057SBram Moolenaar end_word(
495ed8ce057SBram Moolenaar     long	count,
496ed8ce057SBram Moolenaar     int		bigword,
497ed8ce057SBram Moolenaar     int		stop,
498ed8ce057SBram Moolenaar     int		empty)
499ed8ce057SBram Moolenaar {
500ed8ce057SBram Moolenaar     int		sclass;	    // starting class
501ed8ce057SBram Moolenaar 
502ed8ce057SBram Moolenaar     curwin->w_cursor.coladd = 0;
503ed8ce057SBram Moolenaar     cls_bigword = bigword;
504ed8ce057SBram Moolenaar     while (--count >= 0)
505ed8ce057SBram Moolenaar     {
506ed8ce057SBram Moolenaar #ifdef FEAT_FOLDING
507ed8ce057SBram Moolenaar 	// When inside a range of folded lines, move to the last char of the
508ed8ce057SBram Moolenaar 	// last line.
509ed8ce057SBram Moolenaar 	if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
510ed8ce057SBram Moolenaar 	    coladvance((colnr_T)MAXCOL);
511ed8ce057SBram Moolenaar #endif
512ed8ce057SBram Moolenaar 	sclass = cls();
513ed8ce057SBram Moolenaar 	if (inc_cursor() == -1)
514ed8ce057SBram Moolenaar 	    return FAIL;
515ed8ce057SBram Moolenaar 
516ed8ce057SBram Moolenaar 	/*
517ed8ce057SBram Moolenaar 	 * If we're in the middle of a word, we just have to move to the end
518ed8ce057SBram Moolenaar 	 * of it.
519ed8ce057SBram Moolenaar 	 */
520ed8ce057SBram Moolenaar 	if (cls() == sclass && sclass != 0)
521ed8ce057SBram Moolenaar 	{
522ed8ce057SBram Moolenaar 	    /*
523ed8ce057SBram Moolenaar 	     * Move forward to end of the current word
524ed8ce057SBram Moolenaar 	     */
525ed8ce057SBram Moolenaar 	    if (skip_chars(sclass, FORWARD))
526ed8ce057SBram Moolenaar 		return FAIL;
527ed8ce057SBram Moolenaar 	}
528ed8ce057SBram Moolenaar 	else if (!stop || sclass == 0)
529ed8ce057SBram Moolenaar 	{
530ed8ce057SBram Moolenaar 	    /*
531ed8ce057SBram Moolenaar 	     * We were at the end of a word. Go to the end of the next word.
532ed8ce057SBram Moolenaar 	     * First skip white space, if 'empty' is TRUE, stop at empty line.
533ed8ce057SBram Moolenaar 	     */
534ed8ce057SBram Moolenaar 	    while (cls() == 0)
535ed8ce057SBram Moolenaar 	    {
536ed8ce057SBram Moolenaar 		if (empty && curwin->w_cursor.col == 0
537ed8ce057SBram Moolenaar 					  && LINEEMPTY(curwin->w_cursor.lnum))
538ed8ce057SBram Moolenaar 		    goto finished;
539ed8ce057SBram Moolenaar 		if (inc_cursor() == -1)	    // hit end of file, stop here
540ed8ce057SBram Moolenaar 		    return FAIL;
541ed8ce057SBram Moolenaar 	    }
542ed8ce057SBram Moolenaar 
543ed8ce057SBram Moolenaar 	    /*
544ed8ce057SBram Moolenaar 	     * Move forward to the end of this word.
545ed8ce057SBram Moolenaar 	     */
546ed8ce057SBram Moolenaar 	    if (skip_chars(cls(), FORWARD))
547ed8ce057SBram Moolenaar 		return FAIL;
548ed8ce057SBram Moolenaar 	}
549ed8ce057SBram Moolenaar 	dec_cursor();			// overshot - one char backward
550ed8ce057SBram Moolenaar finished:
551ed8ce057SBram Moolenaar 	stop = FALSE;			// we move only one word less
552ed8ce057SBram Moolenaar     }
553ed8ce057SBram Moolenaar     return OK;
554ed8ce057SBram Moolenaar }
555ed8ce057SBram Moolenaar 
556ed8ce057SBram Moolenaar /*
557ed8ce057SBram Moolenaar  * Move back to the end of the word.
558ed8ce057SBram Moolenaar  *
559ed8ce057SBram Moolenaar  * Returns FAIL if start of the file was reached.
560ed8ce057SBram Moolenaar  */
561ed8ce057SBram Moolenaar     int
bckend_word(long count,int bigword,int eol)562ed8ce057SBram Moolenaar bckend_word(
563ed8ce057SBram Moolenaar     long	count,
564ed8ce057SBram Moolenaar     int		bigword,    // TRUE for "B"
565ed8ce057SBram Moolenaar     int		eol)	    // TRUE: stop at end of line.
566ed8ce057SBram Moolenaar {
567ed8ce057SBram Moolenaar     int		sclass;	    // starting class
568ed8ce057SBram Moolenaar     int		i;
569ed8ce057SBram Moolenaar 
570ed8ce057SBram Moolenaar     curwin->w_cursor.coladd = 0;
571ed8ce057SBram Moolenaar     cls_bigword = bigword;
572ed8ce057SBram Moolenaar     while (--count >= 0)
573ed8ce057SBram Moolenaar     {
574ed8ce057SBram Moolenaar 	sclass = cls();
575ed8ce057SBram Moolenaar 	if ((i = dec_cursor()) == -1)
576ed8ce057SBram Moolenaar 	    return FAIL;
577ed8ce057SBram Moolenaar 	if (eol && i == 1)
578ed8ce057SBram Moolenaar 	    return OK;
579ed8ce057SBram Moolenaar 
580ed8ce057SBram Moolenaar 	/*
581ed8ce057SBram Moolenaar 	 * Move backward to before the start of this word.
582ed8ce057SBram Moolenaar 	 */
583ed8ce057SBram Moolenaar 	if (sclass != 0)
584ed8ce057SBram Moolenaar 	{
585ed8ce057SBram Moolenaar 	    while (cls() == sclass)
586ed8ce057SBram Moolenaar 		if ((i = dec_cursor()) == -1 || (eol && i == 1))
587ed8ce057SBram Moolenaar 		    return OK;
588ed8ce057SBram Moolenaar 	}
589ed8ce057SBram Moolenaar 
590ed8ce057SBram Moolenaar 	/*
591ed8ce057SBram Moolenaar 	 * Move backward to end of the previous word
592ed8ce057SBram Moolenaar 	 */
593ed8ce057SBram Moolenaar 	while (cls() == 0)
594ed8ce057SBram Moolenaar 	{
595ed8ce057SBram Moolenaar 	    if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum))
596ed8ce057SBram Moolenaar 		break;
597ed8ce057SBram Moolenaar 	    if ((i = dec_cursor()) == -1 || (eol && i == 1))
598ed8ce057SBram Moolenaar 		return OK;
599ed8ce057SBram Moolenaar 	}
600ed8ce057SBram Moolenaar     }
601ed8ce057SBram Moolenaar     return OK;
602ed8ce057SBram Moolenaar }
603ed8ce057SBram Moolenaar 
604ed8ce057SBram Moolenaar /*
605ed8ce057SBram Moolenaar  * Skip a row of characters of the same class.
606ed8ce057SBram Moolenaar  * Return TRUE when end-of-file reached, FALSE otherwise.
607ed8ce057SBram Moolenaar  */
608ed8ce057SBram Moolenaar     static int
skip_chars(int cclass,int dir)609ed8ce057SBram Moolenaar skip_chars(int cclass, int dir)
610ed8ce057SBram Moolenaar {
611ed8ce057SBram Moolenaar     while (cls() == cclass)
612ed8ce057SBram Moolenaar 	if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
613ed8ce057SBram Moolenaar 	    return TRUE;
614ed8ce057SBram Moolenaar     return FALSE;
615ed8ce057SBram Moolenaar }
616ed8ce057SBram Moolenaar 
617ed8ce057SBram Moolenaar #if defined(FEAT_TEXTOBJ) || defined(PROTO)
618ed8ce057SBram Moolenaar /*
619ed8ce057SBram Moolenaar  * Go back to the start of the word or the start of white space
620ed8ce057SBram Moolenaar  */
621ed8ce057SBram Moolenaar     static void
back_in_line(void)622ed8ce057SBram Moolenaar back_in_line(void)
623ed8ce057SBram Moolenaar {
624ed8ce057SBram Moolenaar     int		sclass;		    // starting class
625ed8ce057SBram Moolenaar 
626ed8ce057SBram Moolenaar     sclass = cls();
627ed8ce057SBram Moolenaar     for (;;)
628ed8ce057SBram Moolenaar     {
629ed8ce057SBram Moolenaar 	if (curwin->w_cursor.col == 0)	    // stop at start of line
630ed8ce057SBram Moolenaar 	    break;
631ed8ce057SBram Moolenaar 	dec_cursor();
632ed8ce057SBram Moolenaar 	if (cls() != sclass)		    // stop at start of word
633ed8ce057SBram Moolenaar 	{
634ed8ce057SBram Moolenaar 	    inc_cursor();
635ed8ce057SBram Moolenaar 	    break;
636ed8ce057SBram Moolenaar 	}
637ed8ce057SBram Moolenaar     }
638ed8ce057SBram Moolenaar }
639ed8ce057SBram Moolenaar 
640ed8ce057SBram Moolenaar     static void
find_first_blank(pos_T * posp)641ed8ce057SBram Moolenaar find_first_blank(pos_T *posp)
642ed8ce057SBram Moolenaar {
643ed8ce057SBram Moolenaar     int	    c;
644ed8ce057SBram Moolenaar 
645ed8ce057SBram Moolenaar     while (decl(posp) != -1)
646ed8ce057SBram Moolenaar     {
647ed8ce057SBram Moolenaar 	c = gchar_pos(posp);
648ed8ce057SBram Moolenaar 	if (!VIM_ISWHITE(c))
649ed8ce057SBram Moolenaar 	{
650ed8ce057SBram Moolenaar 	    incl(posp);
651ed8ce057SBram Moolenaar 	    break;
652ed8ce057SBram Moolenaar 	}
653ed8ce057SBram Moolenaar     }
654ed8ce057SBram Moolenaar }
655ed8ce057SBram Moolenaar 
656ed8ce057SBram Moolenaar /*
657ed8ce057SBram Moolenaar  * Skip count/2 sentences and count/2 separating white spaces.
658ed8ce057SBram Moolenaar  */
659ed8ce057SBram Moolenaar     static void
findsent_forward(long count,int at_start_sent)660ed8ce057SBram Moolenaar findsent_forward(
661ed8ce057SBram Moolenaar     long    count,
662ed8ce057SBram Moolenaar     int	    at_start_sent)	// cursor is at start of sentence
663ed8ce057SBram Moolenaar {
664ed8ce057SBram Moolenaar     while (count--)
665ed8ce057SBram Moolenaar     {
666ed8ce057SBram Moolenaar 	findsent(FORWARD, 1L);
667ed8ce057SBram Moolenaar 	if (at_start_sent)
668ed8ce057SBram Moolenaar 	    find_first_blank(&curwin->w_cursor);
669ed8ce057SBram Moolenaar 	if (count == 0 || at_start_sent)
670ed8ce057SBram Moolenaar 	    decl(&curwin->w_cursor);
671ed8ce057SBram Moolenaar 	at_start_sent = !at_start_sent;
672ed8ce057SBram Moolenaar     }
673ed8ce057SBram Moolenaar }
674ed8ce057SBram Moolenaar 
675ed8ce057SBram Moolenaar /*
676ed8ce057SBram Moolenaar  * Find word under cursor, cursor at end.
677ed8ce057SBram Moolenaar  * Used while an operator is pending, and in Visual mode.
678ed8ce057SBram Moolenaar  */
679ed8ce057SBram Moolenaar     int
current_word(oparg_T * oap,long count,int include,int bigword)680ed8ce057SBram Moolenaar current_word(
681ed8ce057SBram Moolenaar     oparg_T	*oap,
682ed8ce057SBram Moolenaar     long	count,
683ed8ce057SBram Moolenaar     int		include,	// TRUE: include word and white space
684ed8ce057SBram Moolenaar     int		bigword)	// FALSE == word, TRUE == WORD
685ed8ce057SBram Moolenaar {
686ed8ce057SBram Moolenaar     pos_T	start_pos;
687ed8ce057SBram Moolenaar     pos_T	pos;
688ed8ce057SBram Moolenaar     int		inclusive = TRUE;
689ed8ce057SBram Moolenaar     int		include_white = FALSE;
690ed8ce057SBram Moolenaar 
691ed8ce057SBram Moolenaar     cls_bigword = bigword;
692ed8ce057SBram Moolenaar     CLEAR_POS(&start_pos);
693ed8ce057SBram Moolenaar 
694ed8ce057SBram Moolenaar     // Correct cursor when 'selection' is exclusive
695ed8ce057SBram Moolenaar     if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
696ed8ce057SBram Moolenaar 	dec_cursor();
697ed8ce057SBram Moolenaar 
698ed8ce057SBram Moolenaar     /*
699ed8ce057SBram Moolenaar      * When Visual mode is not active, or when the VIsual area is only one
700ed8ce057SBram Moolenaar      * character, select the word and/or white space under the cursor.
701ed8ce057SBram Moolenaar      */
702ed8ce057SBram Moolenaar     if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual))
703ed8ce057SBram Moolenaar     {
704ed8ce057SBram Moolenaar 	/*
705ed8ce057SBram Moolenaar 	 * Go to start of current word or white space.
706ed8ce057SBram Moolenaar 	 */
707ed8ce057SBram Moolenaar 	back_in_line();
708ed8ce057SBram Moolenaar 	start_pos = curwin->w_cursor;
709ed8ce057SBram Moolenaar 
710ed8ce057SBram Moolenaar 	/*
711ed8ce057SBram Moolenaar 	 * If the start is on white space, and white space should be included
712ed8ce057SBram Moolenaar 	 * ("	word"), or start is not on white space, and white space should
713ed8ce057SBram Moolenaar 	 * not be included ("word"), find end of word.
714ed8ce057SBram Moolenaar 	 */
715ed8ce057SBram Moolenaar 	if ((cls() == 0) == include)
716ed8ce057SBram Moolenaar 	{
717ed8ce057SBram Moolenaar 	    if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
718ed8ce057SBram Moolenaar 		return FAIL;
719ed8ce057SBram Moolenaar 	}
720ed8ce057SBram Moolenaar 	else
721ed8ce057SBram Moolenaar 	{
722ed8ce057SBram Moolenaar 	    /*
723ed8ce057SBram Moolenaar 	     * If the start is not on white space, and white space should be
724ed8ce057SBram Moolenaar 	     * included ("word	 "), or start is on white space and white
725ed8ce057SBram Moolenaar 	     * space should not be included ("	 "), find start of word.
726ed8ce057SBram Moolenaar 	     * If we end up in the first column of the next line (single char
727ed8ce057SBram Moolenaar 	     * word) back up to end of the line.
728ed8ce057SBram Moolenaar 	     */
729ed8ce057SBram Moolenaar 	    fwd_word(1L, bigword, TRUE);
730ed8ce057SBram Moolenaar 	    if (curwin->w_cursor.col == 0)
731ed8ce057SBram Moolenaar 		decl(&curwin->w_cursor);
732ed8ce057SBram Moolenaar 	    else
733ed8ce057SBram Moolenaar 		oneleft();
734ed8ce057SBram Moolenaar 
735ed8ce057SBram Moolenaar 	    if (include)
736ed8ce057SBram Moolenaar 		include_white = TRUE;
737ed8ce057SBram Moolenaar 	}
738ed8ce057SBram Moolenaar 
739ed8ce057SBram Moolenaar 	if (VIsual_active)
740ed8ce057SBram Moolenaar 	{
741ed8ce057SBram Moolenaar 	    // should do something when inclusive == FALSE !
742ed8ce057SBram Moolenaar 	    VIsual = start_pos;
743ed8ce057SBram Moolenaar 	    redraw_curbuf_later(INVERTED);	// update the inversion
744ed8ce057SBram Moolenaar 	}
745ed8ce057SBram Moolenaar 	else
746ed8ce057SBram Moolenaar 	{
747ed8ce057SBram Moolenaar 	    oap->start = start_pos;
748ed8ce057SBram Moolenaar 	    oap->motion_type = MCHAR;
749ed8ce057SBram Moolenaar 	}
750ed8ce057SBram Moolenaar 	--count;
751ed8ce057SBram Moolenaar     }
752ed8ce057SBram Moolenaar 
753ed8ce057SBram Moolenaar     /*
754ed8ce057SBram Moolenaar      * When count is still > 0, extend with more objects.
755ed8ce057SBram Moolenaar      */
756ed8ce057SBram Moolenaar     while (count > 0)
757ed8ce057SBram Moolenaar     {
758ed8ce057SBram Moolenaar 	inclusive = TRUE;
759ed8ce057SBram Moolenaar 	if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
760ed8ce057SBram Moolenaar 	{
761ed8ce057SBram Moolenaar 	    /*
762ed8ce057SBram Moolenaar 	     * In Visual mode, with cursor at start: move cursor back.
763ed8ce057SBram Moolenaar 	     */
764ed8ce057SBram Moolenaar 	    if (decl(&curwin->w_cursor) == -1)
765ed8ce057SBram Moolenaar 		return FAIL;
766ed8ce057SBram Moolenaar 	    if (include != (cls() != 0))
767ed8ce057SBram Moolenaar 	    {
768ed8ce057SBram Moolenaar 		if (bck_word(1L, bigword, TRUE) == FAIL)
769ed8ce057SBram Moolenaar 		    return FAIL;
770ed8ce057SBram Moolenaar 	    }
771ed8ce057SBram Moolenaar 	    else
772ed8ce057SBram Moolenaar 	    {
773ed8ce057SBram Moolenaar 		if (bckend_word(1L, bigword, TRUE) == FAIL)
774ed8ce057SBram Moolenaar 		    return FAIL;
775ed8ce057SBram Moolenaar 		(void)incl(&curwin->w_cursor);
776ed8ce057SBram Moolenaar 	    }
777ed8ce057SBram Moolenaar 	}
778ed8ce057SBram Moolenaar 	else
779ed8ce057SBram Moolenaar 	{
780ed8ce057SBram Moolenaar 	    /*
781ed8ce057SBram Moolenaar 	     * Move cursor forward one word and/or white area.
782ed8ce057SBram Moolenaar 	     */
783ed8ce057SBram Moolenaar 	    if (incl(&curwin->w_cursor) == -1)
784ed8ce057SBram Moolenaar 		return FAIL;
785ed8ce057SBram Moolenaar 	    if (include != (cls() == 0))
786ed8ce057SBram Moolenaar 	    {
787ed8ce057SBram Moolenaar 		if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1)
788ed8ce057SBram Moolenaar 		    return FAIL;
789ed8ce057SBram Moolenaar 		/*
790ed8ce057SBram Moolenaar 		 * If end is just past a new-line, we don't want to include
791ed8ce057SBram Moolenaar 		 * the first character on the line.
792ed8ce057SBram Moolenaar 		 * Put cursor on last char of white.
793ed8ce057SBram Moolenaar 		 */
794ed8ce057SBram Moolenaar 		if (oneleft() == FAIL)
795ed8ce057SBram Moolenaar 		    inclusive = FALSE;
796ed8ce057SBram Moolenaar 	    }
797ed8ce057SBram Moolenaar 	    else
798ed8ce057SBram Moolenaar 	    {
799ed8ce057SBram Moolenaar 		if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
800ed8ce057SBram Moolenaar 		    return FAIL;
801ed8ce057SBram Moolenaar 	    }
802ed8ce057SBram Moolenaar 	}
803ed8ce057SBram Moolenaar 	--count;
804ed8ce057SBram Moolenaar     }
805ed8ce057SBram Moolenaar 
806ed8ce057SBram Moolenaar     if (include_white && (cls() != 0
807ed8ce057SBram Moolenaar 		 || (curwin->w_cursor.col == 0 && !inclusive)))
808ed8ce057SBram Moolenaar     {
809ed8ce057SBram Moolenaar 	/*
810ed8ce057SBram Moolenaar 	 * If we don't include white space at the end, move the start
811ed8ce057SBram Moolenaar 	 * to include some white space there. This makes "daw" work
812ed8ce057SBram Moolenaar 	 * better on the last word in a sentence (and "2daw" on last-but-one
813ed8ce057SBram Moolenaar 	 * word).  Also when "2daw" deletes "word." at the end of the line
814ed8ce057SBram Moolenaar 	 * (cursor is at start of next line).
815ed8ce057SBram Moolenaar 	 * But don't delete white space at start of line (indent).
816ed8ce057SBram Moolenaar 	 */
817ed8ce057SBram Moolenaar 	pos = curwin->w_cursor;	// save cursor position
818ed8ce057SBram Moolenaar 	curwin->w_cursor = start_pos;
819ed8ce057SBram Moolenaar 	if (oneleft() == OK)
820ed8ce057SBram Moolenaar 	{
821ed8ce057SBram Moolenaar 	    back_in_line();
822ed8ce057SBram Moolenaar 	    if (cls() == 0 && curwin->w_cursor.col > 0)
823ed8ce057SBram Moolenaar 	    {
824ed8ce057SBram Moolenaar 		if (VIsual_active)
825ed8ce057SBram Moolenaar 		    VIsual = curwin->w_cursor;
826ed8ce057SBram Moolenaar 		else
827ed8ce057SBram Moolenaar 		    oap->start = curwin->w_cursor;
828ed8ce057SBram Moolenaar 	    }
829ed8ce057SBram Moolenaar 	}
830ed8ce057SBram Moolenaar 	curwin->w_cursor = pos;	// put cursor back at end
831ed8ce057SBram Moolenaar     }
832ed8ce057SBram Moolenaar 
833ed8ce057SBram Moolenaar     if (VIsual_active)
834ed8ce057SBram Moolenaar     {
835ed8ce057SBram Moolenaar 	if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor))
836ed8ce057SBram Moolenaar 	    inc_cursor();
837ed8ce057SBram Moolenaar 	if (VIsual_mode == 'V')
838ed8ce057SBram Moolenaar 	{
839ed8ce057SBram Moolenaar 	    VIsual_mode = 'v';
840ed8ce057SBram Moolenaar 	    redraw_cmdline = TRUE;		// show mode later
841ed8ce057SBram Moolenaar 	}
842ed8ce057SBram Moolenaar     }
843ed8ce057SBram Moolenaar     else
844ed8ce057SBram Moolenaar 	oap->inclusive = inclusive;
845ed8ce057SBram Moolenaar 
846ed8ce057SBram Moolenaar     return OK;
847ed8ce057SBram Moolenaar }
848ed8ce057SBram Moolenaar 
849ed8ce057SBram Moolenaar /*
850ed8ce057SBram Moolenaar  * Find sentence(s) under the cursor, cursor at end.
851ed8ce057SBram Moolenaar  * When Visual active, extend it by one or more sentences.
852ed8ce057SBram Moolenaar  */
853ed8ce057SBram Moolenaar     int
current_sent(oparg_T * oap,long count,int include)854ed8ce057SBram Moolenaar current_sent(oparg_T *oap, long count, int include)
855ed8ce057SBram Moolenaar {
856ed8ce057SBram Moolenaar     pos_T	start_pos;
857ed8ce057SBram Moolenaar     pos_T	pos;
858ed8ce057SBram Moolenaar     int		start_blank;
859ed8ce057SBram Moolenaar     int		c;
860ed8ce057SBram Moolenaar     int		at_start_sent;
861ed8ce057SBram Moolenaar     long	ncount;
862ed8ce057SBram Moolenaar 
863ed8ce057SBram Moolenaar     start_pos = curwin->w_cursor;
864ed8ce057SBram Moolenaar     pos = start_pos;
865ed8ce057SBram Moolenaar     findsent(FORWARD, 1L);	// Find start of next sentence.
866ed8ce057SBram Moolenaar 
867ed8ce057SBram Moolenaar     /*
868ed8ce057SBram Moolenaar      * When the Visual area is bigger than one character: Extend it.
869ed8ce057SBram Moolenaar      */
870ed8ce057SBram Moolenaar     if (VIsual_active && !EQUAL_POS(start_pos, VIsual))
871ed8ce057SBram Moolenaar     {
872ed8ce057SBram Moolenaar extend:
873ed8ce057SBram Moolenaar 	if (LT_POS(start_pos, VIsual))
874ed8ce057SBram Moolenaar 	{
875ed8ce057SBram Moolenaar 	    /*
876ed8ce057SBram Moolenaar 	     * Cursor at start of Visual area.
877ed8ce057SBram Moolenaar 	     * Find out where we are:
878ed8ce057SBram Moolenaar 	     * - in the white space before a sentence
879ed8ce057SBram Moolenaar 	     * - in a sentence or just after it
880ed8ce057SBram Moolenaar 	     * - at the start of a sentence
881ed8ce057SBram Moolenaar 	     */
882ed8ce057SBram Moolenaar 	    at_start_sent = TRUE;
883ed8ce057SBram Moolenaar 	    decl(&pos);
884ed8ce057SBram Moolenaar 	    while (LT_POS(pos, curwin->w_cursor))
885ed8ce057SBram Moolenaar 	    {
886ed8ce057SBram Moolenaar 		c = gchar_pos(&pos);
887ed8ce057SBram Moolenaar 		if (!VIM_ISWHITE(c))
888ed8ce057SBram Moolenaar 		{
889ed8ce057SBram Moolenaar 		    at_start_sent = FALSE;
890ed8ce057SBram Moolenaar 		    break;
891ed8ce057SBram Moolenaar 		}
892ed8ce057SBram Moolenaar 		incl(&pos);
893ed8ce057SBram Moolenaar 	    }
894ed8ce057SBram Moolenaar 	    if (!at_start_sent)
895ed8ce057SBram Moolenaar 	    {
896ed8ce057SBram Moolenaar 		findsent(BACKWARD, 1L);
897ed8ce057SBram Moolenaar 		if (EQUAL_POS(curwin->w_cursor, start_pos))
898ed8ce057SBram Moolenaar 		    at_start_sent = TRUE;  // exactly at start of sentence
899ed8ce057SBram Moolenaar 		else
900ed8ce057SBram Moolenaar 		    // inside a sentence, go to its end (start of next)
901ed8ce057SBram Moolenaar 		    findsent(FORWARD, 1L);
902ed8ce057SBram Moolenaar 	    }
903ed8ce057SBram Moolenaar 	    if (include)	// "as" gets twice as much as "is"
904ed8ce057SBram Moolenaar 		count *= 2;
905ed8ce057SBram Moolenaar 	    while (count--)
906ed8ce057SBram Moolenaar 	    {
907ed8ce057SBram Moolenaar 		if (at_start_sent)
908ed8ce057SBram Moolenaar 		    find_first_blank(&curwin->w_cursor);
909ed8ce057SBram Moolenaar 		c = gchar_cursor();
910ed8ce057SBram Moolenaar 		if (!at_start_sent || (!include && !VIM_ISWHITE(c)))
911ed8ce057SBram Moolenaar 		    findsent(BACKWARD, 1L);
912ed8ce057SBram Moolenaar 		at_start_sent = !at_start_sent;
913ed8ce057SBram Moolenaar 	    }
914ed8ce057SBram Moolenaar 	}
915ed8ce057SBram Moolenaar 	else
916ed8ce057SBram Moolenaar 	{
917ed8ce057SBram Moolenaar 	    /*
918ed8ce057SBram Moolenaar 	     * Cursor at end of Visual area.
919ed8ce057SBram Moolenaar 	     * Find out where we are:
920ed8ce057SBram Moolenaar 	     * - just before a sentence
921ed8ce057SBram Moolenaar 	     * - just before or in the white space before a sentence
922ed8ce057SBram Moolenaar 	     * - in a sentence
923ed8ce057SBram Moolenaar 	     */
924ed8ce057SBram Moolenaar 	    incl(&pos);
925ed8ce057SBram Moolenaar 	    at_start_sent = TRUE;
926ed8ce057SBram Moolenaar 	    // not just before a sentence
927ed8ce057SBram Moolenaar 	    if (!EQUAL_POS(pos, curwin->w_cursor))
928ed8ce057SBram Moolenaar 	    {
929ed8ce057SBram Moolenaar 		at_start_sent = FALSE;
930ed8ce057SBram Moolenaar 		while (LT_POS(pos, curwin->w_cursor))
931ed8ce057SBram Moolenaar 		{
932ed8ce057SBram Moolenaar 		    c = gchar_pos(&pos);
933ed8ce057SBram Moolenaar 		    if (!VIM_ISWHITE(c))
934ed8ce057SBram Moolenaar 		    {
935ed8ce057SBram Moolenaar 			at_start_sent = TRUE;
936ed8ce057SBram Moolenaar 			break;
937ed8ce057SBram Moolenaar 		    }
938ed8ce057SBram Moolenaar 		    incl(&pos);
939ed8ce057SBram Moolenaar 		}
940ed8ce057SBram Moolenaar 		if (at_start_sent)	// in the sentence
941ed8ce057SBram Moolenaar 		    findsent(BACKWARD, 1L);
942ed8ce057SBram Moolenaar 		else		// in/before white before a sentence
943ed8ce057SBram Moolenaar 		    curwin->w_cursor = start_pos;
944ed8ce057SBram Moolenaar 	    }
945ed8ce057SBram Moolenaar 
946ed8ce057SBram Moolenaar 	    if (include)	// "as" gets twice as much as "is"
947ed8ce057SBram Moolenaar 		count *= 2;
948ed8ce057SBram Moolenaar 	    findsent_forward(count, at_start_sent);
949ed8ce057SBram Moolenaar 	    if (*p_sel == 'e')
950ed8ce057SBram Moolenaar 		++curwin->w_cursor.col;
951ed8ce057SBram Moolenaar 	}
952ed8ce057SBram Moolenaar 	return OK;
953ed8ce057SBram Moolenaar     }
954ed8ce057SBram Moolenaar 
955ed8ce057SBram Moolenaar     /*
956ed8ce057SBram Moolenaar      * If the cursor started on a blank, check if it is just before the start
957ed8ce057SBram Moolenaar      * of the next sentence.
958ed8ce057SBram Moolenaar      */
959ed8ce057SBram Moolenaar     while (c = gchar_pos(&pos), VIM_ISWHITE(c))	// VIM_ISWHITE() is a macro
960ed8ce057SBram Moolenaar 	incl(&pos);
961ed8ce057SBram Moolenaar     if (EQUAL_POS(pos, curwin->w_cursor))
962ed8ce057SBram Moolenaar     {
963ed8ce057SBram Moolenaar 	start_blank = TRUE;
964ed8ce057SBram Moolenaar 	find_first_blank(&start_pos);	// go back to first blank
965ed8ce057SBram Moolenaar     }
966ed8ce057SBram Moolenaar     else
967ed8ce057SBram Moolenaar     {
968ed8ce057SBram Moolenaar 	start_blank = FALSE;
969ed8ce057SBram Moolenaar 	findsent(BACKWARD, 1L);
970ed8ce057SBram Moolenaar 	start_pos = curwin->w_cursor;
971ed8ce057SBram Moolenaar     }
972ed8ce057SBram Moolenaar     if (include)
973ed8ce057SBram Moolenaar 	ncount = count * 2;
974ed8ce057SBram Moolenaar     else
975ed8ce057SBram Moolenaar     {
976ed8ce057SBram Moolenaar 	ncount = count;
977ed8ce057SBram Moolenaar 	if (start_blank)
978ed8ce057SBram Moolenaar 	    --ncount;
979ed8ce057SBram Moolenaar     }
980ed8ce057SBram Moolenaar     if (ncount > 0)
981ed8ce057SBram Moolenaar 	findsent_forward(ncount, TRUE);
982ed8ce057SBram Moolenaar     else
983ed8ce057SBram Moolenaar 	decl(&curwin->w_cursor);
984ed8ce057SBram Moolenaar 
985ed8ce057SBram Moolenaar     if (include)
986ed8ce057SBram Moolenaar     {
987ed8ce057SBram Moolenaar 	/*
988ed8ce057SBram Moolenaar 	 * If the blank in front of the sentence is included, exclude the
989ed8ce057SBram Moolenaar 	 * blanks at the end of the sentence, go back to the first blank.
990ed8ce057SBram Moolenaar 	 * If there are no trailing blanks, try to include leading blanks.
991ed8ce057SBram Moolenaar 	 */
992ed8ce057SBram Moolenaar 	if (start_blank)
993ed8ce057SBram Moolenaar 	{
994ed8ce057SBram Moolenaar 	    find_first_blank(&curwin->w_cursor);
995ed8ce057SBram Moolenaar 	    c = gchar_pos(&curwin->w_cursor);	// VIM_ISWHITE() is a macro
996ed8ce057SBram Moolenaar 	    if (VIM_ISWHITE(c))
997ed8ce057SBram Moolenaar 		decl(&curwin->w_cursor);
998ed8ce057SBram Moolenaar 	}
999ed8ce057SBram Moolenaar 	else if (c = gchar_cursor(), !VIM_ISWHITE(c))
1000ed8ce057SBram Moolenaar 	    find_first_blank(&start_pos);
1001ed8ce057SBram Moolenaar     }
1002ed8ce057SBram Moolenaar 
1003ed8ce057SBram Moolenaar     if (VIsual_active)
1004ed8ce057SBram Moolenaar     {
1005ed8ce057SBram Moolenaar 	// Avoid getting stuck with "is" on a single space before a sentence.
1006ed8ce057SBram Moolenaar 	if (EQUAL_POS(start_pos, curwin->w_cursor))
1007ed8ce057SBram Moolenaar 	    goto extend;
1008ed8ce057SBram Moolenaar 	if (*p_sel == 'e')
1009ed8ce057SBram Moolenaar 	    ++curwin->w_cursor.col;
1010ed8ce057SBram Moolenaar 	VIsual = start_pos;
1011ed8ce057SBram Moolenaar 	VIsual_mode = 'v';
1012ed8ce057SBram Moolenaar 	redraw_cmdline = TRUE;		// show mode later
1013ed8ce057SBram Moolenaar 	redraw_curbuf_later(INVERTED);	// update the inversion
1014ed8ce057SBram Moolenaar     }
1015ed8ce057SBram Moolenaar     else
1016ed8ce057SBram Moolenaar     {
1017ed8ce057SBram Moolenaar 	// include a newline after the sentence, if there is one
1018ed8ce057SBram Moolenaar 	if (incl(&curwin->w_cursor) == -1)
1019ed8ce057SBram Moolenaar 	    oap->inclusive = TRUE;
1020ed8ce057SBram Moolenaar 	else
1021ed8ce057SBram Moolenaar 	    oap->inclusive = FALSE;
1022ed8ce057SBram Moolenaar 	oap->start = start_pos;
1023ed8ce057SBram Moolenaar 	oap->motion_type = MCHAR;
1024ed8ce057SBram Moolenaar     }
1025ed8ce057SBram Moolenaar     return OK;
1026ed8ce057SBram Moolenaar }
1027ed8ce057SBram Moolenaar 
1028ed8ce057SBram Moolenaar /*
1029ed8ce057SBram Moolenaar  * Find block under the cursor, cursor at end.
1030ed8ce057SBram Moolenaar  * "what" and "other" are two matching parenthesis/brace/etc.
1031ed8ce057SBram Moolenaar  */
1032ed8ce057SBram Moolenaar     int
current_block(oparg_T * oap,long count,int include,int what,int other)1033ed8ce057SBram Moolenaar current_block(
1034ed8ce057SBram Moolenaar     oparg_T	*oap,
1035ed8ce057SBram Moolenaar     long	count,
1036ed8ce057SBram Moolenaar     int		include,	// TRUE == include white space
1037ed8ce057SBram Moolenaar     int		what,		// '(', '{', etc.
1038ed8ce057SBram Moolenaar     int		other)		// ')', '}', etc.
1039ed8ce057SBram Moolenaar {
1040ed8ce057SBram Moolenaar     pos_T	old_pos;
1041ed8ce057SBram Moolenaar     pos_T	*pos = NULL;
1042ed8ce057SBram Moolenaar     pos_T	start_pos;
1043ed8ce057SBram Moolenaar     pos_T	*end_pos;
1044ed8ce057SBram Moolenaar     pos_T	old_start, old_end;
1045ed8ce057SBram Moolenaar     char_u	*save_cpo;
1046ed8ce057SBram Moolenaar     int		sol = FALSE;		// '{' at start of line
1047ed8ce057SBram Moolenaar 
1048ed8ce057SBram Moolenaar     old_pos = curwin->w_cursor;
1049ed8ce057SBram Moolenaar     old_end = curwin->w_cursor;		// remember where we started
1050ed8ce057SBram Moolenaar     old_start = old_end;
1051ed8ce057SBram Moolenaar 
1052ed8ce057SBram Moolenaar     /*
1053ed8ce057SBram Moolenaar      * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
1054ed8ce057SBram Moolenaar      */
1055ed8ce057SBram Moolenaar     if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
1056ed8ce057SBram Moolenaar     {
1057ed8ce057SBram Moolenaar 	setpcmark();
1058ed8ce057SBram Moolenaar 	if (what == '{')		// ignore indent
1059ed8ce057SBram Moolenaar 	    while (inindent(1))
1060ed8ce057SBram Moolenaar 		if (inc_cursor() != 0)
1061ed8ce057SBram Moolenaar 		    break;
1062ed8ce057SBram Moolenaar 	if (gchar_cursor() == what)
1063ed8ce057SBram Moolenaar 	    // cursor on '(' or '{', move cursor just after it
1064ed8ce057SBram Moolenaar 	    ++curwin->w_cursor.col;
1065ed8ce057SBram Moolenaar     }
1066ed8ce057SBram Moolenaar     else if (LT_POS(VIsual, curwin->w_cursor))
1067ed8ce057SBram Moolenaar     {
1068ed8ce057SBram Moolenaar 	old_start = VIsual;
1069ed8ce057SBram Moolenaar 	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
1070ed8ce057SBram Moolenaar     }
1071ed8ce057SBram Moolenaar     else
1072ed8ce057SBram Moolenaar 	old_end = VIsual;
1073ed8ce057SBram Moolenaar 
1074ed8ce057SBram Moolenaar     /*
1075ed8ce057SBram Moolenaar      * Search backwards for unclosed '(', '{', etc..
1076ed8ce057SBram Moolenaar      * Put this position in start_pos.
1077ed8ce057SBram Moolenaar      * Ignore quotes here.  Keep the "M" flag in 'cpo', as that is what the
1078ed8ce057SBram Moolenaar      * user wants.
1079ed8ce057SBram Moolenaar      */
1080ed8ce057SBram Moolenaar     save_cpo = p_cpo;
1081ed8ce057SBram Moolenaar     p_cpo = (char_u *)(vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%");
1082*b9115da4SConnor Lane Smith     if ((pos = findmatch(NULL, what)) != NULL)
1083*b9115da4SConnor Lane Smith     {
1084ed8ce057SBram Moolenaar 	while (count-- > 0)
1085ed8ce057SBram Moolenaar 	{
1086ed8ce057SBram Moolenaar 	    if ((pos = findmatch(NULL, what)) == NULL)
1087ed8ce057SBram Moolenaar 		break;
1088ed8ce057SBram Moolenaar 	    curwin->w_cursor = *pos;
1089ed8ce057SBram Moolenaar 	    start_pos = *pos;   // the findmatch for end_pos will overwrite *pos
1090ed8ce057SBram Moolenaar 	}
1091*b9115da4SConnor Lane Smith     }
1092*b9115da4SConnor Lane Smith     else
1093*b9115da4SConnor Lane Smith     {
1094*b9115da4SConnor Lane Smith 	while (count-- > 0)
1095*b9115da4SConnor Lane Smith 	{
1096*b9115da4SConnor Lane Smith 	    if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL)
1097*b9115da4SConnor Lane Smith 		break;
1098*b9115da4SConnor Lane Smith 	    curwin->w_cursor = *pos;
1099*b9115da4SConnor Lane Smith 	    start_pos = *pos;   // the findmatch for end_pos will overwrite *pos
1100*b9115da4SConnor Lane Smith 	}
1101*b9115da4SConnor Lane Smith     }
1102ed8ce057SBram Moolenaar     p_cpo = save_cpo;
1103ed8ce057SBram Moolenaar 
1104ed8ce057SBram Moolenaar     /*
1105ed8ce057SBram Moolenaar      * Search for matching ')', '}', etc.
1106ed8ce057SBram Moolenaar      * Put this position in curwin->w_cursor.
1107ed8ce057SBram Moolenaar      */
1108ed8ce057SBram Moolenaar     if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
1109ed8ce057SBram Moolenaar     {
1110ed8ce057SBram Moolenaar 	curwin->w_cursor = old_pos;
1111ed8ce057SBram Moolenaar 	return FAIL;
1112ed8ce057SBram Moolenaar     }
1113ed8ce057SBram Moolenaar     curwin->w_cursor = *end_pos;
1114ed8ce057SBram Moolenaar 
1115ed8ce057SBram Moolenaar     /*
1116ed8ce057SBram Moolenaar      * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
1117ed8ce057SBram Moolenaar      * If the ending '}', ')' or ']' is only preceded by indent, skip that
1118ed8ce057SBram Moolenaar      * indent.  But only if the resulting area is not smaller than what we
1119ed8ce057SBram Moolenaar      * started with.
1120ed8ce057SBram Moolenaar      */
1121ed8ce057SBram Moolenaar     while (!include)
1122ed8ce057SBram Moolenaar     {
1123ed8ce057SBram Moolenaar 	incl(&start_pos);
1124ed8ce057SBram Moolenaar 	sol = (curwin->w_cursor.col == 0);
1125ed8ce057SBram Moolenaar 	decl(&curwin->w_cursor);
1126ed8ce057SBram Moolenaar 	while (inindent(1))
1127ed8ce057SBram Moolenaar 	{
1128ed8ce057SBram Moolenaar 	    sol = TRUE;
1129ed8ce057SBram Moolenaar 	    if (decl(&curwin->w_cursor) != 0)
1130ed8ce057SBram Moolenaar 		break;
1131ed8ce057SBram Moolenaar 	}
1132ed8ce057SBram Moolenaar 
1133ed8ce057SBram Moolenaar 	/*
1134ed8ce057SBram Moolenaar 	 * In Visual mode, when the resulting area is not bigger than what we
1135ed8ce057SBram Moolenaar 	 * started with, extend it to the next block, and then exclude again.
1136ed8ce057SBram Moolenaar 	 */
1137ed8ce057SBram Moolenaar 	if (!LT_POS(start_pos, old_start) && !LT_POS(old_end, curwin->w_cursor)
1138ed8ce057SBram Moolenaar 		&& VIsual_active)
1139ed8ce057SBram Moolenaar 	{
1140ed8ce057SBram Moolenaar 	    curwin->w_cursor = old_start;
1141ed8ce057SBram Moolenaar 	    decl(&curwin->w_cursor);
1142ed8ce057SBram Moolenaar 	    if ((pos = findmatch(NULL, what)) == NULL)
1143ed8ce057SBram Moolenaar 	    {
1144ed8ce057SBram Moolenaar 		curwin->w_cursor = old_pos;
1145ed8ce057SBram Moolenaar 		return FAIL;
1146ed8ce057SBram Moolenaar 	    }
1147ed8ce057SBram Moolenaar 	    start_pos = *pos;
1148ed8ce057SBram Moolenaar 	    curwin->w_cursor = *pos;
1149ed8ce057SBram Moolenaar 	    if ((end_pos = findmatch(NULL, other)) == NULL)
1150ed8ce057SBram Moolenaar 	    {
1151ed8ce057SBram Moolenaar 		curwin->w_cursor = old_pos;
1152ed8ce057SBram Moolenaar 		return FAIL;
1153ed8ce057SBram Moolenaar 	    }
1154ed8ce057SBram Moolenaar 	    curwin->w_cursor = *end_pos;
1155ed8ce057SBram Moolenaar 	}
1156ed8ce057SBram Moolenaar 	else
1157ed8ce057SBram Moolenaar 	    break;
1158ed8ce057SBram Moolenaar     }
1159ed8ce057SBram Moolenaar 
1160ed8ce057SBram Moolenaar     if (VIsual_active)
1161ed8ce057SBram Moolenaar     {
1162ed8ce057SBram Moolenaar 	if (*p_sel == 'e')
1163ed8ce057SBram Moolenaar 	    inc(&curwin->w_cursor);
1164ed8ce057SBram Moolenaar 	if (sol && gchar_cursor() != NUL)
1165ed8ce057SBram Moolenaar 	    inc(&curwin->w_cursor);	// include the line break
1166ed8ce057SBram Moolenaar 	VIsual = start_pos;
1167ed8ce057SBram Moolenaar 	VIsual_mode = 'v';
1168ed8ce057SBram Moolenaar 	redraw_curbuf_later(INVERTED);	// update the inversion
1169ed8ce057SBram Moolenaar 	showmode();
1170ed8ce057SBram Moolenaar     }
1171ed8ce057SBram Moolenaar     else
1172ed8ce057SBram Moolenaar     {
1173ed8ce057SBram Moolenaar 	oap->start = start_pos;
1174ed8ce057SBram Moolenaar 	oap->motion_type = MCHAR;
1175ed8ce057SBram Moolenaar 	oap->inclusive = FALSE;
1176ed8ce057SBram Moolenaar 	if (sol)
1177ed8ce057SBram Moolenaar 	    incl(&curwin->w_cursor);
1178ed8ce057SBram Moolenaar 	else if (LTOREQ_POS(start_pos, curwin->w_cursor))
1179ed8ce057SBram Moolenaar 	    // Include the character under the cursor.
1180ed8ce057SBram Moolenaar 	    oap->inclusive = TRUE;
1181ed8ce057SBram Moolenaar 	else
1182ed8ce057SBram Moolenaar 	    // End is before the start (no text in between <>, [], etc.): don't
1183ed8ce057SBram Moolenaar 	    // operate on any text.
1184ed8ce057SBram Moolenaar 	    curwin->w_cursor = start_pos;
1185ed8ce057SBram Moolenaar     }
1186ed8ce057SBram Moolenaar 
1187ed8ce057SBram Moolenaar     return OK;
1188ed8ce057SBram Moolenaar }
1189ed8ce057SBram Moolenaar 
1190ed8ce057SBram Moolenaar /*
1191ed8ce057SBram Moolenaar  * Return TRUE if the cursor is on a "<aaa>" tag.  Ignore "<aaa/>".
1192ed8ce057SBram Moolenaar  * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>".
1193ed8ce057SBram Moolenaar  */
1194ed8ce057SBram Moolenaar     static int
in_html_tag(int end_tag)1195ed8ce057SBram Moolenaar in_html_tag(
1196ed8ce057SBram Moolenaar     int		end_tag)
1197ed8ce057SBram Moolenaar {
1198ed8ce057SBram Moolenaar     char_u	*line = ml_get_curline();
1199ed8ce057SBram Moolenaar     char_u	*p;
1200ed8ce057SBram Moolenaar     int		c;
1201ed8ce057SBram Moolenaar     int		lc = NUL;
1202ed8ce057SBram Moolenaar     pos_T	pos;
1203ed8ce057SBram Moolenaar 
1204ed8ce057SBram Moolenaar     if (enc_dbcs)
1205ed8ce057SBram Moolenaar     {
1206ed8ce057SBram Moolenaar 	char_u	*lp = NULL;
1207ed8ce057SBram Moolenaar 
1208ed8ce057SBram Moolenaar 	// We search forward until the cursor, because searching backwards is
1209ed8ce057SBram Moolenaar 	// very slow for DBCS encodings.
1210ed8ce057SBram Moolenaar 	for (p = line; p < line + curwin->w_cursor.col; MB_PTR_ADV(p))
1211ed8ce057SBram Moolenaar 	    if (*p == '>' || *p == '<')
1212ed8ce057SBram Moolenaar 	    {
1213ed8ce057SBram Moolenaar 		lc = *p;
1214ed8ce057SBram Moolenaar 		lp = p;
1215ed8ce057SBram Moolenaar 	    }
1216ed8ce057SBram Moolenaar 	if (*p != '<')	    // check for '<' under cursor
1217ed8ce057SBram Moolenaar 	{
1218ed8ce057SBram Moolenaar 	    if (lc != '<')
1219ed8ce057SBram Moolenaar 		return FALSE;
1220ed8ce057SBram Moolenaar 	    p = lp;
1221ed8ce057SBram Moolenaar 	}
1222ed8ce057SBram Moolenaar     }
1223ed8ce057SBram Moolenaar     else
1224ed8ce057SBram Moolenaar     {
1225ed8ce057SBram Moolenaar 	for (p = line + curwin->w_cursor.col; p > line; )
1226ed8ce057SBram Moolenaar 	{
1227ed8ce057SBram Moolenaar 	    if (*p == '<')	// find '<' under/before cursor
1228ed8ce057SBram Moolenaar 		break;
1229ed8ce057SBram Moolenaar 	    MB_PTR_BACK(line, p);
1230ed8ce057SBram Moolenaar 	    if (*p == '>')	// find '>' before cursor
1231ed8ce057SBram Moolenaar 		break;
1232ed8ce057SBram Moolenaar 	}
1233ed8ce057SBram Moolenaar 	if (*p != '<')
1234ed8ce057SBram Moolenaar 	    return FALSE;
1235ed8ce057SBram Moolenaar     }
1236ed8ce057SBram Moolenaar 
1237ed8ce057SBram Moolenaar     pos.lnum = curwin->w_cursor.lnum;
1238ed8ce057SBram Moolenaar     pos.col = (colnr_T)(p - line);
1239ed8ce057SBram Moolenaar 
1240ed8ce057SBram Moolenaar     MB_PTR_ADV(p);
1241ed8ce057SBram Moolenaar     if (end_tag)
1242ed8ce057SBram Moolenaar 	// check that there is a '/' after the '<'
1243ed8ce057SBram Moolenaar 	return *p == '/';
1244ed8ce057SBram Moolenaar 
1245ed8ce057SBram Moolenaar     // check that there is no '/' after the '<'
1246ed8ce057SBram Moolenaar     if (*p == '/')
1247ed8ce057SBram Moolenaar 	return FALSE;
1248ed8ce057SBram Moolenaar 
1249ed8ce057SBram Moolenaar     // check that the matching '>' is not preceded by '/'
1250ed8ce057SBram Moolenaar     for (;;)
1251ed8ce057SBram Moolenaar     {
1252ed8ce057SBram Moolenaar 	if (inc(&pos) < 0)
1253ed8ce057SBram Moolenaar 	    return FALSE;
1254ed8ce057SBram Moolenaar 	c = *ml_get_pos(&pos);
1255ed8ce057SBram Moolenaar 	if (c == '>')
1256ed8ce057SBram Moolenaar 	    break;
1257ed8ce057SBram Moolenaar 	lc = c;
1258ed8ce057SBram Moolenaar     }
1259ed8ce057SBram Moolenaar     return lc != '/';
1260ed8ce057SBram Moolenaar }
1261ed8ce057SBram Moolenaar 
1262ed8ce057SBram Moolenaar /*
1263ed8ce057SBram Moolenaar  * Find tag block under the cursor, cursor at end.
1264ed8ce057SBram Moolenaar  */
1265ed8ce057SBram Moolenaar     int
current_tagblock(oparg_T * oap,long count_arg,int include)1266ed8ce057SBram Moolenaar current_tagblock(
1267ed8ce057SBram Moolenaar     oparg_T	*oap,
1268ed8ce057SBram Moolenaar     long	count_arg,
1269ed8ce057SBram Moolenaar     int		include)	// TRUE == include white space
1270ed8ce057SBram Moolenaar {
1271ed8ce057SBram Moolenaar     long	count = count_arg;
1272ed8ce057SBram Moolenaar     long	n;
1273ed8ce057SBram Moolenaar     pos_T	old_pos;
1274ed8ce057SBram Moolenaar     pos_T	start_pos;
1275ed8ce057SBram Moolenaar     pos_T	end_pos;
1276ed8ce057SBram Moolenaar     pos_T	old_start, old_end;
1277ed8ce057SBram Moolenaar     char_u	*spat, *epat;
1278ed8ce057SBram Moolenaar     char_u	*p;
1279ed8ce057SBram Moolenaar     char_u	*cp;
1280ed8ce057SBram Moolenaar     int		len;
1281ed8ce057SBram Moolenaar     int		r;
1282ed8ce057SBram Moolenaar     int		do_include = include;
1283ed8ce057SBram Moolenaar     int		save_p_ws = p_ws;
1284ed8ce057SBram Moolenaar     int		retval = FAIL;
1285ed8ce057SBram Moolenaar     int		is_inclusive = TRUE;
1286ed8ce057SBram Moolenaar 
1287ed8ce057SBram Moolenaar     p_ws = FALSE;
1288ed8ce057SBram Moolenaar 
1289ed8ce057SBram Moolenaar     old_pos = curwin->w_cursor;
1290ed8ce057SBram Moolenaar     old_end = curwin->w_cursor;		    // remember where we started
1291ed8ce057SBram Moolenaar     old_start = old_end;
1292ed8ce057SBram Moolenaar     if (!VIsual_active || *p_sel == 'e')
1293ed8ce057SBram Moolenaar 	decl(&old_end);			    // old_end is inclusive
1294ed8ce057SBram Moolenaar 
1295ed8ce057SBram Moolenaar     /*
1296ed8ce057SBram Moolenaar      * If we start on "<aaa>" select that block.
1297ed8ce057SBram Moolenaar      */
1298ed8ce057SBram Moolenaar     if (!VIsual_active || EQUAL_POS(VIsual, curwin->w_cursor))
1299ed8ce057SBram Moolenaar     {
1300ed8ce057SBram Moolenaar 	setpcmark();
1301ed8ce057SBram Moolenaar 
1302ed8ce057SBram Moolenaar 	// ignore indent
1303ed8ce057SBram Moolenaar 	while (inindent(1))
1304ed8ce057SBram Moolenaar 	    if (inc_cursor() != 0)
1305ed8ce057SBram Moolenaar 		break;
1306ed8ce057SBram Moolenaar 
1307ed8ce057SBram Moolenaar 	if (in_html_tag(FALSE))
1308ed8ce057SBram Moolenaar 	{
1309ed8ce057SBram Moolenaar 	    // cursor on start tag, move to its '>'
1310ed8ce057SBram Moolenaar 	    while (*ml_get_cursor() != '>')
1311ed8ce057SBram Moolenaar 		if (inc_cursor() < 0)
1312ed8ce057SBram Moolenaar 		    break;
1313ed8ce057SBram Moolenaar 	}
1314ed8ce057SBram Moolenaar 	else if (in_html_tag(TRUE))
1315ed8ce057SBram Moolenaar 	{
1316ed8ce057SBram Moolenaar 	    // cursor on end tag, move to just before it
1317ed8ce057SBram Moolenaar 	    while (*ml_get_cursor() != '<')
1318ed8ce057SBram Moolenaar 		if (dec_cursor() < 0)
1319ed8ce057SBram Moolenaar 		    break;
1320ed8ce057SBram Moolenaar 	    dec_cursor();
1321ed8ce057SBram Moolenaar 	    old_end = curwin->w_cursor;
1322ed8ce057SBram Moolenaar 	}
1323ed8ce057SBram Moolenaar     }
1324ed8ce057SBram Moolenaar     else if (LT_POS(VIsual, curwin->w_cursor))
1325ed8ce057SBram Moolenaar     {
1326ed8ce057SBram Moolenaar 	old_start = VIsual;
1327ed8ce057SBram Moolenaar 	curwin->w_cursor = VIsual;	    // cursor at low end of Visual
1328ed8ce057SBram Moolenaar     }
1329ed8ce057SBram Moolenaar     else
1330ed8ce057SBram Moolenaar 	old_end = VIsual;
1331ed8ce057SBram Moolenaar 
1332ed8ce057SBram Moolenaar again:
1333ed8ce057SBram Moolenaar     /*
1334ed8ce057SBram Moolenaar      * Search backwards for unclosed "<aaa>".
1335ed8ce057SBram Moolenaar      * Put this position in start_pos.
1336ed8ce057SBram Moolenaar      */
1337ed8ce057SBram Moolenaar     for (n = 0; n < count; ++n)
1338ed8ce057SBram Moolenaar     {
1339ed8ce057SBram Moolenaar 	if (do_searchpair((char_u *)"<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
1340ed8ce057SBram Moolenaar 		    (char_u *)"",
1341ed8ce057SBram Moolenaar 		    (char_u *)"</[^>]*>", BACKWARD, NULL, 0,
1342ed8ce057SBram Moolenaar 						  NULL, (linenr_T)0, 0L) <= 0)
1343ed8ce057SBram Moolenaar 	{
1344ed8ce057SBram Moolenaar 	    curwin->w_cursor = old_pos;
1345ed8ce057SBram Moolenaar 	    goto theend;
1346ed8ce057SBram Moolenaar 	}
1347ed8ce057SBram Moolenaar     }
1348ed8ce057SBram Moolenaar     start_pos = curwin->w_cursor;
1349ed8ce057SBram Moolenaar 
1350ed8ce057SBram Moolenaar     /*
1351ed8ce057SBram Moolenaar      * Search for matching "</aaa>".  First isolate the "aaa".
1352ed8ce057SBram Moolenaar      */
1353ed8ce057SBram Moolenaar     inc_cursor();
1354ed8ce057SBram Moolenaar     p = ml_get_cursor();
1355ed8ce057SBram Moolenaar     for (cp = p; *cp != NUL && *cp != '>' && !VIM_ISWHITE(*cp); MB_PTR_ADV(cp))
1356ed8ce057SBram Moolenaar 	;
1357ed8ce057SBram Moolenaar     len = (int)(cp - p);
1358ed8ce057SBram Moolenaar     if (len == 0)
1359ed8ce057SBram Moolenaar     {
1360ed8ce057SBram Moolenaar 	curwin->w_cursor = old_pos;
1361ed8ce057SBram Moolenaar 	goto theend;
1362ed8ce057SBram Moolenaar     }
1363a604ccc9SBram Moolenaar     spat = alloc(len + 39);
1364ed8ce057SBram Moolenaar     epat = alloc(len + 9);
1365ed8ce057SBram Moolenaar     if (spat == NULL || epat == NULL)
1366ed8ce057SBram Moolenaar     {
1367ed8ce057SBram Moolenaar 	vim_free(spat);
1368ed8ce057SBram Moolenaar 	vim_free(epat);
1369ed8ce057SBram Moolenaar 	curwin->w_cursor = old_pos;
1370ed8ce057SBram Moolenaar 	goto theend;
1371ed8ce057SBram Moolenaar     }
1372a604ccc9SBram Moolenaar     sprintf((char *)spat, "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p);
1373ed8ce057SBram Moolenaar     sprintf((char *)epat, "</%.*s>\\c", len, p);
1374ed8ce057SBram Moolenaar 
1375ed8ce057SBram Moolenaar     r = do_searchpair(spat, (char_u *)"", epat, FORWARD, NULL,
1376ed8ce057SBram Moolenaar 						    0, NULL, (linenr_T)0, 0L);
1377ed8ce057SBram Moolenaar 
1378ed8ce057SBram Moolenaar     vim_free(spat);
1379ed8ce057SBram Moolenaar     vim_free(epat);
1380ed8ce057SBram Moolenaar 
1381ed8ce057SBram Moolenaar     if (r < 1 || LT_POS(curwin->w_cursor, old_end))
1382ed8ce057SBram Moolenaar     {
1383ed8ce057SBram Moolenaar 	// Can't find other end or it's before the previous end.  Could be a
1384ed8ce057SBram Moolenaar 	// HTML tag that doesn't have a matching end.  Search backwards for
1385ed8ce057SBram Moolenaar 	// another starting tag.
1386ed8ce057SBram Moolenaar 	count = 1;
1387ed8ce057SBram Moolenaar 	curwin->w_cursor = start_pos;
1388ed8ce057SBram Moolenaar 	goto again;
1389ed8ce057SBram Moolenaar     }
1390ed8ce057SBram Moolenaar 
1391ed8ce057SBram Moolenaar     if (do_include)
1392ed8ce057SBram Moolenaar     {
1393ed8ce057SBram Moolenaar 	// Include up to the '>'.
1394ed8ce057SBram Moolenaar 	while (*ml_get_cursor() != '>')
1395ed8ce057SBram Moolenaar 	    if (inc_cursor() < 0)
1396ed8ce057SBram Moolenaar 		break;
1397ed8ce057SBram Moolenaar     }
1398ed8ce057SBram Moolenaar     else
1399ed8ce057SBram Moolenaar     {
1400ed8ce057SBram Moolenaar 	char_u *c = ml_get_cursor();
1401ed8ce057SBram Moolenaar 
1402ed8ce057SBram Moolenaar 	// Exclude the '<' of the end tag.
1403ed8ce057SBram Moolenaar 	// If the closing tag is on new line, do not decrement cursor, but
1404ed8ce057SBram Moolenaar 	// make operation exclusive, so that the linefeed will be selected
1405ed8ce057SBram Moolenaar 	if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0)
1406ed8ce057SBram Moolenaar 	    // do not decrement cursor
1407ed8ce057SBram Moolenaar 	    is_inclusive = FALSE;
1408ed8ce057SBram Moolenaar 	else if (*c == '<')
1409ed8ce057SBram Moolenaar 	    dec_cursor();
1410ed8ce057SBram Moolenaar     }
1411ed8ce057SBram Moolenaar     end_pos = curwin->w_cursor;
1412ed8ce057SBram Moolenaar 
1413ed8ce057SBram Moolenaar     if (!do_include)
1414ed8ce057SBram Moolenaar     {
1415ed8ce057SBram Moolenaar 	// Exclude the start tag.
1416ed8ce057SBram Moolenaar 	curwin->w_cursor = start_pos;
1417ed8ce057SBram Moolenaar 	while (inc_cursor() >= 0)
1418ed8ce057SBram Moolenaar 	    if (*ml_get_cursor() == '>')
1419ed8ce057SBram Moolenaar 	    {
1420ed8ce057SBram Moolenaar 		inc_cursor();
1421ed8ce057SBram Moolenaar 		start_pos = curwin->w_cursor;
1422ed8ce057SBram Moolenaar 		break;
1423ed8ce057SBram Moolenaar 	    }
1424ed8ce057SBram Moolenaar 	curwin->w_cursor = end_pos;
1425ed8ce057SBram Moolenaar 
1426ed8ce057SBram Moolenaar 	// If we are in Visual mode and now have the same text as before set
1427ed8ce057SBram Moolenaar 	// "do_include" and try again.
1428ed8ce057SBram Moolenaar 	if (VIsual_active && EQUAL_POS(start_pos, old_start)
1429ed8ce057SBram Moolenaar 						&& EQUAL_POS(end_pos, old_end))
1430ed8ce057SBram Moolenaar 	{
1431ed8ce057SBram Moolenaar 	    do_include = TRUE;
1432ed8ce057SBram Moolenaar 	    curwin->w_cursor = old_start;
1433ed8ce057SBram Moolenaar 	    count = count_arg;
1434ed8ce057SBram Moolenaar 	    goto again;
1435ed8ce057SBram Moolenaar 	}
1436ed8ce057SBram Moolenaar     }
1437ed8ce057SBram Moolenaar 
1438ed8ce057SBram Moolenaar     if (VIsual_active)
1439ed8ce057SBram Moolenaar     {
1440ed8ce057SBram Moolenaar 	// If the end is before the start there is no text between tags, select
1441ed8ce057SBram Moolenaar 	// the char under the cursor.
1442ed8ce057SBram Moolenaar 	if (LT_POS(end_pos, start_pos))
1443ed8ce057SBram Moolenaar 	    curwin->w_cursor = start_pos;
1444ed8ce057SBram Moolenaar 	else if (*p_sel == 'e')
1445ed8ce057SBram Moolenaar 	    inc_cursor();
1446ed8ce057SBram Moolenaar 	VIsual = start_pos;
1447ed8ce057SBram Moolenaar 	VIsual_mode = 'v';
1448ed8ce057SBram Moolenaar 	redraw_curbuf_later(INVERTED);	// update the inversion
1449ed8ce057SBram Moolenaar 	showmode();
1450ed8ce057SBram Moolenaar     }
1451ed8ce057SBram Moolenaar     else
1452ed8ce057SBram Moolenaar     {
1453ed8ce057SBram Moolenaar 	oap->start = start_pos;
1454ed8ce057SBram Moolenaar 	oap->motion_type = MCHAR;
1455ed8ce057SBram Moolenaar 	if (LT_POS(end_pos, start_pos))
1456ed8ce057SBram Moolenaar 	{
1457ed8ce057SBram Moolenaar 	    // End is before the start: there is no text between tags; operate
1458ed8ce057SBram Moolenaar 	    // on an empty area.
1459ed8ce057SBram Moolenaar 	    curwin->w_cursor = start_pos;
1460ed8ce057SBram Moolenaar 	    oap->inclusive = FALSE;
1461ed8ce057SBram Moolenaar 	}
1462ed8ce057SBram Moolenaar 	else
1463ed8ce057SBram Moolenaar 	    oap->inclusive = is_inclusive;
1464ed8ce057SBram Moolenaar     }
1465ed8ce057SBram Moolenaar     retval = OK;
1466ed8ce057SBram Moolenaar 
1467ed8ce057SBram Moolenaar theend:
1468ed8ce057SBram Moolenaar     p_ws = save_p_ws;
1469ed8ce057SBram Moolenaar     return retval;
1470ed8ce057SBram Moolenaar }
1471ed8ce057SBram Moolenaar 
1472ed8ce057SBram Moolenaar     int
current_par(oparg_T * oap,long count,int include,int type)1473ed8ce057SBram Moolenaar current_par(
1474ed8ce057SBram Moolenaar     oparg_T	*oap,
1475ed8ce057SBram Moolenaar     long	count,
1476ed8ce057SBram Moolenaar     int		include,	// TRUE == include white space
1477ed8ce057SBram Moolenaar     int		type)		// 'p' for paragraph, 'S' for section
1478ed8ce057SBram Moolenaar {
1479ed8ce057SBram Moolenaar     linenr_T	start_lnum;
1480ed8ce057SBram Moolenaar     linenr_T	end_lnum;
1481ed8ce057SBram Moolenaar     int		white_in_front;
1482ed8ce057SBram Moolenaar     int		dir;
1483ed8ce057SBram Moolenaar     int		start_is_white;
1484ed8ce057SBram Moolenaar     int		prev_start_is_white;
1485ed8ce057SBram Moolenaar     int		retval = OK;
1486ed8ce057SBram Moolenaar     int		do_white = FALSE;
1487ed8ce057SBram Moolenaar     int		t;
1488ed8ce057SBram Moolenaar     int		i;
1489ed8ce057SBram Moolenaar 
1490ed8ce057SBram Moolenaar     if (type == 'S')	    // not implemented yet
1491ed8ce057SBram Moolenaar 	return FAIL;
1492ed8ce057SBram Moolenaar 
1493ed8ce057SBram Moolenaar     start_lnum = curwin->w_cursor.lnum;
1494ed8ce057SBram Moolenaar 
1495ed8ce057SBram Moolenaar     /*
1496ed8ce057SBram Moolenaar      * When visual area is more than one line: extend it.
1497ed8ce057SBram Moolenaar      */
1498ed8ce057SBram Moolenaar     if (VIsual_active && start_lnum != VIsual.lnum)
1499ed8ce057SBram Moolenaar     {
1500ed8ce057SBram Moolenaar extend:
1501ed8ce057SBram Moolenaar 	if (start_lnum < VIsual.lnum)
1502ed8ce057SBram Moolenaar 	    dir = BACKWARD;
1503ed8ce057SBram Moolenaar 	else
1504ed8ce057SBram Moolenaar 	    dir = FORWARD;
1505ed8ce057SBram Moolenaar 	for (i = count; --i >= 0; )
1506ed8ce057SBram Moolenaar 	{
1507ed8ce057SBram Moolenaar 	    if (start_lnum ==
1508ed8ce057SBram Moolenaar 			   (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
1509ed8ce057SBram Moolenaar 	    {
1510ed8ce057SBram Moolenaar 		retval = FAIL;
1511ed8ce057SBram Moolenaar 		break;
1512ed8ce057SBram Moolenaar 	    }
1513ed8ce057SBram Moolenaar 
1514ed8ce057SBram Moolenaar 	    prev_start_is_white = -1;
1515ed8ce057SBram Moolenaar 	    for (t = 0; t < 2; ++t)
1516ed8ce057SBram Moolenaar 	    {
1517ed8ce057SBram Moolenaar 		start_lnum += dir;
1518ed8ce057SBram Moolenaar 		start_is_white = linewhite(start_lnum);
1519ed8ce057SBram Moolenaar 		if (prev_start_is_white == start_is_white)
1520ed8ce057SBram Moolenaar 		{
1521ed8ce057SBram Moolenaar 		    start_lnum -= dir;
1522ed8ce057SBram Moolenaar 		    break;
1523ed8ce057SBram Moolenaar 		}
1524ed8ce057SBram Moolenaar 		for (;;)
1525ed8ce057SBram Moolenaar 		{
1526ed8ce057SBram Moolenaar 		    if (start_lnum == (dir == BACKWARD
1527ed8ce057SBram Moolenaar 					    ? 1 : curbuf->b_ml.ml_line_count))
1528ed8ce057SBram Moolenaar 			break;
1529ed8ce057SBram Moolenaar 		    if (start_is_white != linewhite(start_lnum + dir)
1530ed8ce057SBram Moolenaar 			    || (!start_is_white
1531ed8ce057SBram Moolenaar 				    && startPS(start_lnum + (dir > 0
1532ed8ce057SBram Moolenaar 							     ? 1 : 0), 0, 0)))
1533ed8ce057SBram Moolenaar 			break;
1534ed8ce057SBram Moolenaar 		    start_lnum += dir;
1535ed8ce057SBram Moolenaar 		}
1536ed8ce057SBram Moolenaar 		if (!include)
1537ed8ce057SBram Moolenaar 		    break;
1538ed8ce057SBram Moolenaar 		if (start_lnum == (dir == BACKWARD
1539ed8ce057SBram Moolenaar 					    ? 1 : curbuf->b_ml.ml_line_count))
1540ed8ce057SBram Moolenaar 		    break;
1541ed8ce057SBram Moolenaar 		prev_start_is_white = start_is_white;
1542ed8ce057SBram Moolenaar 	    }
1543ed8ce057SBram Moolenaar 	}
1544ed8ce057SBram Moolenaar 	curwin->w_cursor.lnum = start_lnum;
1545ed8ce057SBram Moolenaar 	curwin->w_cursor.col = 0;
1546ed8ce057SBram Moolenaar 	return retval;
1547ed8ce057SBram Moolenaar     }
1548ed8ce057SBram Moolenaar 
1549ed8ce057SBram Moolenaar     /*
1550ed8ce057SBram Moolenaar      * First move back to the start_lnum of the paragraph or white lines
1551ed8ce057SBram Moolenaar      */
1552ed8ce057SBram Moolenaar     white_in_front = linewhite(start_lnum);
1553ed8ce057SBram Moolenaar     while (start_lnum > 1)
1554ed8ce057SBram Moolenaar     {
1555ed8ce057SBram Moolenaar 	if (white_in_front)	    // stop at first white line
1556ed8ce057SBram Moolenaar 	{
1557ed8ce057SBram Moolenaar 	    if (!linewhite(start_lnum - 1))
1558ed8ce057SBram Moolenaar 		break;
1559ed8ce057SBram Moolenaar 	}
1560ed8ce057SBram Moolenaar 	else		// stop at first non-white line of start of paragraph
1561ed8ce057SBram Moolenaar 	{
1562ed8ce057SBram Moolenaar 	    if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
1563ed8ce057SBram Moolenaar 		break;
1564ed8ce057SBram Moolenaar 	}
1565ed8ce057SBram Moolenaar 	--start_lnum;
1566ed8ce057SBram Moolenaar     }
1567ed8ce057SBram Moolenaar 
1568ed8ce057SBram Moolenaar     /*
1569ed8ce057SBram Moolenaar      * Move past the end of any white lines.
1570ed8ce057SBram Moolenaar      */
1571ed8ce057SBram Moolenaar     end_lnum = start_lnum;
1572ed8ce057SBram Moolenaar     while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum))
1573ed8ce057SBram Moolenaar 	++end_lnum;
1574ed8ce057SBram Moolenaar 
1575ed8ce057SBram Moolenaar     --end_lnum;
1576ed8ce057SBram Moolenaar     i = count;
1577ed8ce057SBram Moolenaar     if (!include && white_in_front)
1578ed8ce057SBram Moolenaar 	--i;
1579ed8ce057SBram Moolenaar     while (i--)
1580ed8ce057SBram Moolenaar     {
1581ed8ce057SBram Moolenaar 	if (end_lnum == curbuf->b_ml.ml_line_count)
1582ed8ce057SBram Moolenaar 	    return FAIL;
1583ed8ce057SBram Moolenaar 
1584ed8ce057SBram Moolenaar 	if (!include)
1585ed8ce057SBram Moolenaar 	    do_white = linewhite(end_lnum + 1);
1586ed8ce057SBram Moolenaar 
1587ed8ce057SBram Moolenaar 	if (include || !do_white)
1588ed8ce057SBram Moolenaar 	{
1589ed8ce057SBram Moolenaar 	    ++end_lnum;
1590ed8ce057SBram Moolenaar 	    /*
1591ed8ce057SBram Moolenaar 	     * skip to end of paragraph
1592ed8ce057SBram Moolenaar 	     */
1593ed8ce057SBram Moolenaar 	    while (end_lnum < curbuf->b_ml.ml_line_count
1594ed8ce057SBram Moolenaar 		    && !linewhite(end_lnum + 1)
1595ed8ce057SBram Moolenaar 		    && !startPS(end_lnum + 1, 0, 0))
1596ed8ce057SBram Moolenaar 		++end_lnum;
1597ed8ce057SBram Moolenaar 	}
1598ed8ce057SBram Moolenaar 
1599ed8ce057SBram Moolenaar 	if (i == 0 && white_in_front && include)
1600ed8ce057SBram Moolenaar 	    break;
1601ed8ce057SBram Moolenaar 
1602ed8ce057SBram Moolenaar 	/*
1603ed8ce057SBram Moolenaar 	 * skip to end of white lines after paragraph
1604ed8ce057SBram Moolenaar 	 */
1605ed8ce057SBram Moolenaar 	if (include || do_white)
1606ed8ce057SBram Moolenaar 	    while (end_lnum < curbuf->b_ml.ml_line_count
1607ed8ce057SBram Moolenaar 						   && linewhite(end_lnum + 1))
1608ed8ce057SBram Moolenaar 		++end_lnum;
1609ed8ce057SBram Moolenaar     }
1610ed8ce057SBram Moolenaar 
1611ed8ce057SBram Moolenaar     /*
1612ed8ce057SBram Moolenaar      * If there are no empty lines at the end, try to find some empty lines at
1613ed8ce057SBram Moolenaar      * the start (unless that has been done already).
1614ed8ce057SBram Moolenaar      */
1615ed8ce057SBram Moolenaar     if (!white_in_front && !linewhite(end_lnum) && include)
1616ed8ce057SBram Moolenaar 	while (start_lnum > 1 && linewhite(start_lnum - 1))
1617ed8ce057SBram Moolenaar 	    --start_lnum;
1618ed8ce057SBram Moolenaar 
1619ed8ce057SBram Moolenaar     if (VIsual_active)
1620ed8ce057SBram Moolenaar     {
1621ed8ce057SBram Moolenaar 	// Problem: when doing "Vipipip" nothing happens in a single white
1622ed8ce057SBram Moolenaar 	// line, we get stuck there.  Trap this here.
1623ed8ce057SBram Moolenaar 	if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
1624ed8ce057SBram Moolenaar 	    goto extend;
1625ed8ce057SBram Moolenaar 	if (VIsual.lnum != start_lnum)
1626ed8ce057SBram Moolenaar 	{
1627ed8ce057SBram Moolenaar 	    VIsual.lnum = start_lnum;
1628ed8ce057SBram Moolenaar 	    VIsual.col = 0;
1629ed8ce057SBram Moolenaar 	}
1630ed8ce057SBram Moolenaar 	VIsual_mode = 'V';
1631ed8ce057SBram Moolenaar 	redraw_curbuf_later(INVERTED);	// update the inversion
1632ed8ce057SBram Moolenaar 	showmode();
1633ed8ce057SBram Moolenaar     }
1634ed8ce057SBram Moolenaar     else
1635ed8ce057SBram Moolenaar     {
1636ed8ce057SBram Moolenaar 	oap->start.lnum = start_lnum;
1637ed8ce057SBram Moolenaar 	oap->start.col = 0;
1638ed8ce057SBram Moolenaar 	oap->motion_type = MLINE;
1639ed8ce057SBram Moolenaar     }
1640ed8ce057SBram Moolenaar     curwin->w_cursor.lnum = end_lnum;
1641ed8ce057SBram Moolenaar     curwin->w_cursor.col = 0;
1642ed8ce057SBram Moolenaar 
1643ed8ce057SBram Moolenaar     return OK;
1644ed8ce057SBram Moolenaar }
1645ed8ce057SBram Moolenaar 
1646ed8ce057SBram Moolenaar /*
1647ed8ce057SBram Moolenaar  * Search quote char from string line[col].
1648ed8ce057SBram Moolenaar  * Quote character escaped by one of the characters in "escape" is not counted
1649ed8ce057SBram Moolenaar  * as a quote.
1650ed8ce057SBram Moolenaar  * Returns column number of "quotechar" or -1 when not found.
1651ed8ce057SBram Moolenaar  */
1652ed8ce057SBram Moolenaar     static int
find_next_quote(char_u * line,int col,int quotechar,char_u * escape)1653ed8ce057SBram Moolenaar find_next_quote(
1654ed8ce057SBram Moolenaar     char_u	*line,
1655ed8ce057SBram Moolenaar     int		col,
1656ed8ce057SBram Moolenaar     int		quotechar,
1657ed8ce057SBram Moolenaar     char_u	*escape)	// escape characters, can be NULL
1658ed8ce057SBram Moolenaar {
1659ed8ce057SBram Moolenaar     int		c;
1660ed8ce057SBram Moolenaar 
1661ed8ce057SBram Moolenaar     for (;;)
1662ed8ce057SBram Moolenaar     {
1663ed8ce057SBram Moolenaar 	c = line[col];
1664ed8ce057SBram Moolenaar 	if (c == NUL)
1665ed8ce057SBram Moolenaar 	    return -1;
1666ed8ce057SBram Moolenaar 	else if (escape != NULL && vim_strchr(escape, c))
1667ed8ce057SBram Moolenaar 	    ++col;
1668ed8ce057SBram Moolenaar 	else if (c == quotechar)
1669ed8ce057SBram Moolenaar 	    break;
1670ed8ce057SBram Moolenaar 	if (has_mbyte)
1671ed8ce057SBram Moolenaar 	    col += (*mb_ptr2len)(line + col);
1672ed8ce057SBram Moolenaar 	else
1673ed8ce057SBram Moolenaar 	    ++col;
1674ed8ce057SBram Moolenaar     }
1675ed8ce057SBram Moolenaar     return col;
1676ed8ce057SBram Moolenaar }
1677ed8ce057SBram Moolenaar 
1678ed8ce057SBram Moolenaar /*
1679ed8ce057SBram Moolenaar  * Search backwards in "line" from column "col_start" to find "quotechar".
1680ed8ce057SBram Moolenaar  * Quote character escaped by one of the characters in "escape" is not counted
1681ed8ce057SBram Moolenaar  * as a quote.
1682ed8ce057SBram Moolenaar  * Return the found column or zero.
1683ed8ce057SBram Moolenaar  */
1684ed8ce057SBram Moolenaar     static int
find_prev_quote(char_u * line,int col_start,int quotechar,char_u * escape)1685ed8ce057SBram Moolenaar find_prev_quote(
1686ed8ce057SBram Moolenaar     char_u	*line,
1687ed8ce057SBram Moolenaar     int		col_start,
1688ed8ce057SBram Moolenaar     int		quotechar,
1689ed8ce057SBram Moolenaar     char_u	*escape)	// escape characters, can be NULL
1690ed8ce057SBram Moolenaar {
1691ed8ce057SBram Moolenaar     int		n;
1692ed8ce057SBram Moolenaar 
1693ed8ce057SBram Moolenaar     while (col_start > 0)
1694ed8ce057SBram Moolenaar     {
1695ed8ce057SBram Moolenaar 	--col_start;
1696ed8ce057SBram Moolenaar 	col_start -= (*mb_head_off)(line, line + col_start);
1697ed8ce057SBram Moolenaar 	n = 0;
1698ed8ce057SBram Moolenaar 	if (escape != NULL)
1699ed8ce057SBram Moolenaar 	    while (col_start - n > 0 && vim_strchr(escape,
1700ed8ce057SBram Moolenaar 					     line[col_start - n - 1]) != NULL)
1701ed8ce057SBram Moolenaar 	    ++n;
1702ed8ce057SBram Moolenaar 	if (n & 1)
1703ed8ce057SBram Moolenaar 	    col_start -= n;	// uneven number of escape chars, skip it
1704ed8ce057SBram Moolenaar 	else if (line[col_start] == quotechar)
1705ed8ce057SBram Moolenaar 	    break;
1706ed8ce057SBram Moolenaar     }
1707ed8ce057SBram Moolenaar     return col_start;
1708ed8ce057SBram Moolenaar }
1709ed8ce057SBram Moolenaar 
1710ed8ce057SBram Moolenaar /*
1711ed8ce057SBram Moolenaar  * Find quote under the cursor, cursor at end.
1712ed8ce057SBram Moolenaar  * Returns TRUE if found, else FALSE.
1713ed8ce057SBram Moolenaar  */
1714ed8ce057SBram Moolenaar     int
current_quote(oparg_T * oap,long count,int include,int quotechar)1715ed8ce057SBram Moolenaar current_quote(
1716ed8ce057SBram Moolenaar     oparg_T	*oap,
1717ed8ce057SBram Moolenaar     long	count,
1718ed8ce057SBram Moolenaar     int		include,	// TRUE == include quote char
1719ed8ce057SBram Moolenaar     int		quotechar)	// Quote character
1720ed8ce057SBram Moolenaar {
1721ed8ce057SBram Moolenaar     char_u	*line = ml_get_curline();
1722ed8ce057SBram Moolenaar     int		col_end;
1723ed8ce057SBram Moolenaar     int		col_start = curwin->w_cursor.col;
1724ed8ce057SBram Moolenaar     int		inclusive = FALSE;
1725ed8ce057SBram Moolenaar     int		vis_empty = TRUE;	// Visual selection <= 1 char
1726ed8ce057SBram Moolenaar     int		vis_bef_curs = FALSE;	// Visual starts before cursor
1727ed8ce057SBram Moolenaar     int		did_exclusive_adj = FALSE;  // adjusted pos for 'selection'
1728ed8ce057SBram Moolenaar     int		inside_quotes = FALSE;	// Looks like "i'" done before
1729ed8ce057SBram Moolenaar     int		selected_quote = FALSE;	// Has quote inside selection
1730ed8ce057SBram Moolenaar     int		i;
1731ed8ce057SBram Moolenaar     int		restore_vis_bef = FALSE; // restore VIsual on abort
1732ed8ce057SBram Moolenaar 
1733ed8ce057SBram Moolenaar     // When 'selection' is "exclusive" move the cursor to where it would be
1734ed8ce057SBram Moolenaar     // with 'selection' "inclusive", so that the logic is the same for both.
1735ed8ce057SBram Moolenaar     // The cursor then is moved forward after adjusting the area.
1736ed8ce057SBram Moolenaar     if (VIsual_active)
1737ed8ce057SBram Moolenaar     {
1738ed8ce057SBram Moolenaar 	// this only works within one line
1739ed8ce057SBram Moolenaar 	if (VIsual.lnum != curwin->w_cursor.lnum)
1740ed8ce057SBram Moolenaar 	    return FALSE;
1741ed8ce057SBram Moolenaar 
1742ed8ce057SBram Moolenaar 	vis_bef_curs = LT_POS(VIsual, curwin->w_cursor);
1743ed8ce057SBram Moolenaar 	vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
1744ed8ce057SBram Moolenaar 	if (*p_sel == 'e')
1745ed8ce057SBram Moolenaar 	{
1746ed8ce057SBram Moolenaar 	    if (vis_bef_curs)
1747ed8ce057SBram Moolenaar 	    {
1748ed8ce057SBram Moolenaar 		dec_cursor();
1749ed8ce057SBram Moolenaar 		did_exclusive_adj = TRUE;
1750ed8ce057SBram Moolenaar 	    }
1751ed8ce057SBram Moolenaar 	    else if (!vis_empty)
1752ed8ce057SBram Moolenaar 	    {
1753ed8ce057SBram Moolenaar 		dec(&VIsual);
1754ed8ce057SBram Moolenaar 		did_exclusive_adj = TRUE;
1755ed8ce057SBram Moolenaar 	    }
1756ed8ce057SBram Moolenaar 	    vis_empty = EQUAL_POS(VIsual, curwin->w_cursor);
1757ed8ce057SBram Moolenaar 	    if (!vis_bef_curs && !vis_empty)
1758ed8ce057SBram Moolenaar 	    {
1759ed8ce057SBram Moolenaar 		// VIsual needs to be the start of Visual selection.
1760ed8ce057SBram Moolenaar 		pos_T t = curwin->w_cursor;
1761ed8ce057SBram Moolenaar 
1762ed8ce057SBram Moolenaar 		curwin->w_cursor = VIsual;
1763ed8ce057SBram Moolenaar 		VIsual = t;
1764ed8ce057SBram Moolenaar 		vis_bef_curs = TRUE;
1765ed8ce057SBram Moolenaar 		restore_vis_bef = TRUE;
1766ed8ce057SBram Moolenaar 	    }
1767ed8ce057SBram Moolenaar 	}
1768ed8ce057SBram Moolenaar     }
1769ed8ce057SBram Moolenaar 
1770ed8ce057SBram Moolenaar     if (!vis_empty)
1771ed8ce057SBram Moolenaar     {
1772ed8ce057SBram Moolenaar 	// Check if the existing selection exactly spans the text inside
1773ed8ce057SBram Moolenaar 	// quotes.
1774ed8ce057SBram Moolenaar 	if (vis_bef_curs)
1775ed8ce057SBram Moolenaar 	{
1776ed8ce057SBram Moolenaar 	    inside_quotes = VIsual.col > 0
1777ed8ce057SBram Moolenaar 			&& line[VIsual.col - 1] == quotechar
1778ed8ce057SBram Moolenaar 			&& line[curwin->w_cursor.col] != NUL
1779ed8ce057SBram Moolenaar 			&& line[curwin->w_cursor.col + 1] == quotechar;
1780ed8ce057SBram Moolenaar 	    i = VIsual.col;
1781ed8ce057SBram Moolenaar 	    col_end = curwin->w_cursor.col;
1782ed8ce057SBram Moolenaar 	}
1783ed8ce057SBram Moolenaar 	else
1784ed8ce057SBram Moolenaar 	{
1785ed8ce057SBram Moolenaar 	    inside_quotes = curwin->w_cursor.col > 0
1786ed8ce057SBram Moolenaar 			&& line[curwin->w_cursor.col - 1] == quotechar
1787ed8ce057SBram Moolenaar 			&& line[VIsual.col] != NUL
1788ed8ce057SBram Moolenaar 			&& line[VIsual.col + 1] == quotechar;
1789ed8ce057SBram Moolenaar 	    i = curwin->w_cursor.col;
1790ed8ce057SBram Moolenaar 	    col_end = VIsual.col;
1791ed8ce057SBram Moolenaar 	}
1792ed8ce057SBram Moolenaar 
1793ed8ce057SBram Moolenaar 	// Find out if we have a quote in the selection.
1794ed8ce057SBram Moolenaar 	while (i <= col_end)
1795ed8ce057SBram Moolenaar 	    if (line[i++] == quotechar)
1796ed8ce057SBram Moolenaar 	    {
1797ed8ce057SBram Moolenaar 		selected_quote = TRUE;
1798ed8ce057SBram Moolenaar 		break;
1799ed8ce057SBram Moolenaar 	    }
1800ed8ce057SBram Moolenaar     }
1801ed8ce057SBram Moolenaar 
1802ed8ce057SBram Moolenaar     if (!vis_empty && line[col_start] == quotechar)
1803ed8ce057SBram Moolenaar     {
1804ed8ce057SBram Moolenaar 	// Already selecting something and on a quote character.  Find the
1805ed8ce057SBram Moolenaar 	// next quoted string.
1806ed8ce057SBram Moolenaar 	if (vis_bef_curs)
1807ed8ce057SBram Moolenaar 	{
1808ed8ce057SBram Moolenaar 	    // Assume we are on a closing quote: move to after the next
1809ed8ce057SBram Moolenaar 	    // opening quote.
1810ed8ce057SBram Moolenaar 	    col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
1811ed8ce057SBram Moolenaar 	    if (col_start < 0)
1812ed8ce057SBram Moolenaar 		goto abort_search;
1813ed8ce057SBram Moolenaar 	    col_end = find_next_quote(line, col_start + 1, quotechar,
1814ed8ce057SBram Moolenaar 							      curbuf->b_p_qe);
1815ed8ce057SBram Moolenaar 	    if (col_end < 0)
1816ed8ce057SBram Moolenaar 	    {
1817ed8ce057SBram Moolenaar 		// We were on a starting quote perhaps?
1818ed8ce057SBram Moolenaar 		col_end = col_start;
1819ed8ce057SBram Moolenaar 		col_start = curwin->w_cursor.col;
1820ed8ce057SBram Moolenaar 	    }
1821ed8ce057SBram Moolenaar 	}
1822ed8ce057SBram Moolenaar 	else
1823ed8ce057SBram Moolenaar 	{
1824ed8ce057SBram Moolenaar 	    col_end = find_prev_quote(line, col_start, quotechar, NULL);
1825ed8ce057SBram Moolenaar 	    if (line[col_end] != quotechar)
1826ed8ce057SBram Moolenaar 		goto abort_search;
1827ed8ce057SBram Moolenaar 	    col_start = find_prev_quote(line, col_end, quotechar,
1828ed8ce057SBram Moolenaar 							      curbuf->b_p_qe);
1829ed8ce057SBram Moolenaar 	    if (line[col_start] != quotechar)
1830ed8ce057SBram Moolenaar 	    {
1831ed8ce057SBram Moolenaar 		// We were on an ending quote perhaps?
1832ed8ce057SBram Moolenaar 		col_start = col_end;
1833ed8ce057SBram Moolenaar 		col_end = curwin->w_cursor.col;
1834ed8ce057SBram Moolenaar 	    }
1835ed8ce057SBram Moolenaar 	}
1836ed8ce057SBram Moolenaar     }
1837ed8ce057SBram Moolenaar     else
1838ed8ce057SBram Moolenaar 
1839ed8ce057SBram Moolenaar     if (line[col_start] == quotechar || !vis_empty)
1840ed8ce057SBram Moolenaar     {
1841ed8ce057SBram Moolenaar 	int	first_col = col_start;
1842ed8ce057SBram Moolenaar 
1843ed8ce057SBram Moolenaar 	if (!vis_empty)
1844ed8ce057SBram Moolenaar 	{
1845ed8ce057SBram Moolenaar 	    if (vis_bef_curs)
1846ed8ce057SBram Moolenaar 		first_col = find_next_quote(line, col_start, quotechar, NULL);
1847ed8ce057SBram Moolenaar 	    else
1848ed8ce057SBram Moolenaar 		first_col = find_prev_quote(line, col_start, quotechar, NULL);
1849ed8ce057SBram Moolenaar 	}
1850ed8ce057SBram Moolenaar 
1851ed8ce057SBram Moolenaar 	// The cursor is on a quote, we don't know if it's the opening or
1852ed8ce057SBram Moolenaar 	// closing quote.  Search from the start of the line to find out.
1853ed8ce057SBram Moolenaar 	// Also do this when there is a Visual area, a' may leave the cursor
1854ed8ce057SBram Moolenaar 	// in between two strings.
1855ed8ce057SBram Moolenaar 	col_start = 0;
1856ed8ce057SBram Moolenaar 	for (;;)
1857ed8ce057SBram Moolenaar 	{
1858ed8ce057SBram Moolenaar 	    // Find open quote character.
1859ed8ce057SBram Moolenaar 	    col_start = find_next_quote(line, col_start, quotechar, NULL);
1860ed8ce057SBram Moolenaar 	    if (col_start < 0 || col_start > first_col)
1861ed8ce057SBram Moolenaar 		goto abort_search;
1862ed8ce057SBram Moolenaar 	    // Find close quote character.
1863ed8ce057SBram Moolenaar 	    col_end = find_next_quote(line, col_start + 1, quotechar,
1864ed8ce057SBram Moolenaar 							      curbuf->b_p_qe);
1865ed8ce057SBram Moolenaar 	    if (col_end < 0)
1866ed8ce057SBram Moolenaar 		goto abort_search;
1867ed8ce057SBram Moolenaar 	    // If is cursor between start and end quote character, it is
1868ed8ce057SBram Moolenaar 	    // target text object.
1869ed8ce057SBram Moolenaar 	    if (col_start <= first_col && first_col <= col_end)
1870ed8ce057SBram Moolenaar 		break;
1871ed8ce057SBram Moolenaar 	    col_start = col_end + 1;
1872ed8ce057SBram Moolenaar 	}
1873ed8ce057SBram Moolenaar     }
1874ed8ce057SBram Moolenaar     else
1875ed8ce057SBram Moolenaar     {
1876ed8ce057SBram Moolenaar 	// Search backward for a starting quote.
1877ed8ce057SBram Moolenaar 	col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
1878ed8ce057SBram Moolenaar 	if (line[col_start] != quotechar)
1879ed8ce057SBram Moolenaar 	{
1880ed8ce057SBram Moolenaar 	    // No quote before the cursor, look after the cursor.
1881ed8ce057SBram Moolenaar 	    col_start = find_next_quote(line, col_start, quotechar, NULL);
1882ed8ce057SBram Moolenaar 	    if (col_start < 0)
1883ed8ce057SBram Moolenaar 		goto abort_search;
1884ed8ce057SBram Moolenaar 	}
1885ed8ce057SBram Moolenaar 
1886ed8ce057SBram Moolenaar 	// Find close quote character.
1887ed8ce057SBram Moolenaar 	col_end = find_next_quote(line, col_start + 1, quotechar,
1888ed8ce057SBram Moolenaar 							      curbuf->b_p_qe);
1889ed8ce057SBram Moolenaar 	if (col_end < 0)
1890ed8ce057SBram Moolenaar 	    goto abort_search;
1891ed8ce057SBram Moolenaar     }
1892ed8ce057SBram Moolenaar 
1893ed8ce057SBram Moolenaar     // When "include" is TRUE, include spaces after closing quote or before
1894ed8ce057SBram Moolenaar     // the starting quote.
1895ed8ce057SBram Moolenaar     if (include)
1896ed8ce057SBram Moolenaar     {
1897ed8ce057SBram Moolenaar 	if (VIM_ISWHITE(line[col_end + 1]))
1898ed8ce057SBram Moolenaar 	    while (VIM_ISWHITE(line[col_end + 1]))
1899ed8ce057SBram Moolenaar 		++col_end;
1900ed8ce057SBram Moolenaar 	else
1901ed8ce057SBram Moolenaar 	    while (col_start > 0 && VIM_ISWHITE(line[col_start - 1]))
1902ed8ce057SBram Moolenaar 		--col_start;
1903ed8ce057SBram Moolenaar     }
1904ed8ce057SBram Moolenaar 
1905ed8ce057SBram Moolenaar     // Set start position.  After vi" another i" must include the ".
1906ed8ce057SBram Moolenaar     // For v2i" include the quotes.
1907ed8ce057SBram Moolenaar     if (!include && count < 2 && (vis_empty || !inside_quotes))
1908ed8ce057SBram Moolenaar 	++col_start;
1909ed8ce057SBram Moolenaar     curwin->w_cursor.col = col_start;
1910ed8ce057SBram Moolenaar     if (VIsual_active)
1911ed8ce057SBram Moolenaar     {
1912ed8ce057SBram Moolenaar 	// Set the start of the Visual area when the Visual area was empty, we
1913ed8ce057SBram Moolenaar 	// were just inside quotes or the Visual area didn't start at a quote
1914ed8ce057SBram Moolenaar 	// and didn't include a quote.
1915ed8ce057SBram Moolenaar 	if (vis_empty
1916ed8ce057SBram Moolenaar 		|| (vis_bef_curs
1917ed8ce057SBram Moolenaar 		    && !selected_quote
1918ed8ce057SBram Moolenaar 		    && (inside_quotes
1919ed8ce057SBram Moolenaar 			|| (line[VIsual.col] != quotechar
1920ed8ce057SBram Moolenaar 			    && (VIsual.col == 0
1921ed8ce057SBram Moolenaar 				|| line[VIsual.col - 1] != quotechar)))))
1922ed8ce057SBram Moolenaar 	{
1923ed8ce057SBram Moolenaar 	    VIsual = curwin->w_cursor;
1924ed8ce057SBram Moolenaar 	    redraw_curbuf_later(INVERTED);
1925ed8ce057SBram Moolenaar 	}
1926ed8ce057SBram Moolenaar     }
1927ed8ce057SBram Moolenaar     else
1928ed8ce057SBram Moolenaar     {
1929ed8ce057SBram Moolenaar 	oap->start = curwin->w_cursor;
1930ed8ce057SBram Moolenaar 	oap->motion_type = MCHAR;
1931ed8ce057SBram Moolenaar     }
1932ed8ce057SBram Moolenaar 
1933ed8ce057SBram Moolenaar     // Set end position.
1934ed8ce057SBram Moolenaar     curwin->w_cursor.col = col_end;
1935ed8ce057SBram Moolenaar     if ((include || count > 1 // After vi" another i" must include the ".
1936ed8ce057SBram Moolenaar 		|| (!vis_empty && inside_quotes)
1937ed8ce057SBram Moolenaar 	) && inc_cursor() == 2)
1938ed8ce057SBram Moolenaar 	inclusive = TRUE;
1939ed8ce057SBram Moolenaar     if (VIsual_active)
1940ed8ce057SBram Moolenaar     {
1941ed8ce057SBram Moolenaar 	if (vis_empty || vis_bef_curs)
1942ed8ce057SBram Moolenaar 	{
1943ed8ce057SBram Moolenaar 	    // decrement cursor when 'selection' is not exclusive
1944ed8ce057SBram Moolenaar 	    if (*p_sel != 'e')
1945ed8ce057SBram Moolenaar 		dec_cursor();
1946ed8ce057SBram Moolenaar 	}
1947ed8ce057SBram Moolenaar 	else
1948ed8ce057SBram Moolenaar 	{
1949ed8ce057SBram Moolenaar 	    // Cursor is at start of Visual area.  Set the end of the Visual
1950ed8ce057SBram Moolenaar 	    // area when it was just inside quotes or it didn't end at a
1951ed8ce057SBram Moolenaar 	    // quote.
1952ed8ce057SBram Moolenaar 	    if (inside_quotes
1953ed8ce057SBram Moolenaar 		    || (!selected_quote
1954ed8ce057SBram Moolenaar 			&& line[VIsual.col] != quotechar
1955ed8ce057SBram Moolenaar 			&& (line[VIsual.col] == NUL
1956ed8ce057SBram Moolenaar 			    || line[VIsual.col + 1] != quotechar)))
1957ed8ce057SBram Moolenaar 	    {
1958ed8ce057SBram Moolenaar 		dec_cursor();
1959ed8ce057SBram Moolenaar 		VIsual = curwin->w_cursor;
1960ed8ce057SBram Moolenaar 	    }
1961ed8ce057SBram Moolenaar 	    curwin->w_cursor.col = col_start;
1962ed8ce057SBram Moolenaar 	}
1963ed8ce057SBram Moolenaar 	if (VIsual_mode == 'V')
1964ed8ce057SBram Moolenaar 	{
1965ed8ce057SBram Moolenaar 	    VIsual_mode = 'v';
1966ed8ce057SBram Moolenaar 	    redraw_cmdline = TRUE;		// show mode later
1967ed8ce057SBram Moolenaar 	}
1968ed8ce057SBram Moolenaar     }
1969ed8ce057SBram Moolenaar     else
1970ed8ce057SBram Moolenaar     {
1971ed8ce057SBram Moolenaar 	// Set inclusive and other oap's flags.
1972ed8ce057SBram Moolenaar 	oap->inclusive = inclusive;
1973ed8ce057SBram Moolenaar     }
1974ed8ce057SBram Moolenaar 
1975ed8ce057SBram Moolenaar     return OK;
1976ed8ce057SBram Moolenaar 
1977ed8ce057SBram Moolenaar abort_search:
1978ed8ce057SBram Moolenaar     if (VIsual_active && *p_sel == 'e')
1979ed8ce057SBram Moolenaar     {
1980ed8ce057SBram Moolenaar 	if (did_exclusive_adj)
1981ed8ce057SBram Moolenaar 	    inc_cursor();
1982ed8ce057SBram Moolenaar 	if (restore_vis_bef)
1983ed8ce057SBram Moolenaar 	{
1984ed8ce057SBram Moolenaar 	    pos_T t = curwin->w_cursor;
1985ed8ce057SBram Moolenaar 
1986ed8ce057SBram Moolenaar 	    curwin->w_cursor = VIsual;
1987ed8ce057SBram Moolenaar 	    VIsual = t;
1988ed8ce057SBram Moolenaar 	}
1989ed8ce057SBram Moolenaar     }
1990ed8ce057SBram Moolenaar     return FALSE;
1991ed8ce057SBram Moolenaar }
1992ed8ce057SBram Moolenaar 
1993ed8ce057SBram Moolenaar #endif // FEAT_TEXTOBJ
1994