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