-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtextobject.c
2004 lines (1858 loc) · 47.9 KB
/
textobject.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* vi:set ts=8 sts=4 sw=4 noet:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
* See README.txt for an overview of the Vim source code.
*/
/*
* textobject.c: functions for text objects
*/
#include "vim.h"
static int cls(void);
static int skip_chars(int, int);
/*
* Find the start of the next sentence, searching in the direction specified
* by the "dir" argument. The cursor is positioned on the start of the next
* sentence when found. If the next sentence is found, return OK. Return FAIL
* otherwise. See ":h sentence" for the precise definition of a "sentence"
* text object.
*/
int
findsent(int dir, long count)
{
pos_T pos, tpos;
pos_T prev_pos;
int c;
int (*func)(pos_T *);
int startlnum;
int noskip = FALSE; // do not skip blanks
int cpo_J;
int found_dot;
pos = curwin->w_cursor;
if (dir == FORWARD)
func = incl;
else
func = decl;
while (count--)
{
prev_pos = pos;
/*
* if on an empty line, skip up to a non-empty line
*/
if (gchar_pos(&pos) == NUL)
{
do
if ((*func)(&pos) == -1)
break;
while (gchar_pos(&pos) == NUL);
if (dir == FORWARD)
goto found;
}
/*
* if on the start of a paragraph or a section and searching forward,
* go to the next line
*/
else if (dir == FORWARD && pos.col == 0 &&
startPS(pos.lnum, NUL, FALSE))
{
if (pos.lnum == curbuf->b_ml.ml_line_count)
return FAIL;
++pos.lnum;
goto found;
}
else if (dir == BACKWARD)
decl(&pos);
// go back to the previous non-white non-punctuation character
found_dot = FALSE;
while (c = gchar_pos(&pos), VIM_ISWHITE(c)
|| vim_strchr((char_u *)".!?)]\"'", c) != NULL)
{
tpos = pos;
if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD))
break;
if (found_dot)
break;
if (vim_strchr((char_u *) ".!?", c) != NULL)
found_dot = TRUE;
if (vim_strchr((char_u *) ")]\"'", c) != NULL
&& vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL)
break;
decl(&pos);
}
// remember the line where the search started
startlnum = pos.lnum;
cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
for (;;) // find end of sentence
{
c = gchar_pos(&pos);
if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
{
if (dir == BACKWARD && pos.lnum != startlnum)
++pos.lnum;
break;
}
if (c == '.' || c == '!' || c == '?')
{
tpos = pos;
do
if ((c = inc(&tpos)) == -1)
break;
while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
!= NULL);
if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
|| (cpo_J && (c == ' ' && inc(&tpos) >= 0
&& gchar_pos(&tpos) == ' ')))
{
pos = tpos;
if (gchar_pos(&pos) == NUL) // skip NUL at EOL
inc(&pos);
break;
}
}
if ((*func)(&pos) == -1)
{
if (count)
return FAIL;
noskip = TRUE;
break;
}
}
found:
// skip white space
while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
if (incl(&pos) == -1)
break;
if (EQUAL_POS(prev_pos, pos))
{
// didn't actually move, advance one character and try again
if ((*func)(&pos) == -1)
{
if (count)
return FAIL;
break;
}
++count;
}
}
setpcmark();
curwin->w_cursor = pos;
return OK;
}
/*
* Find the next paragraph or section in direction 'dir'.
* Paragraphs are currently supposed to be separated by empty lines.
* If 'what' is NUL we go to the next paragraph.
* If 'what' is '{' or '}' we go to the next section.
* If 'both' is TRUE also stop at '}'.
* Return TRUE if the next paragraph or section was found.
*/
int
findpar(
int *pincl, // Return: TRUE if last char is to be included
int dir,
long count,
int what,
int both)
{
linenr_T curr;
int did_skip; // TRUE after separating lines have been skipped
int first; // TRUE on first line
int posix = (vim_strchr(p_cpo, CPO_PARA) != NULL);
#ifdef FEAT_FOLDING
linenr_T fold_first; // first line of a closed fold
linenr_T fold_last; // last line of a closed fold
int fold_skipped; // TRUE if a closed fold was skipped this
// iteration
#endif
curr = curwin->w_cursor.lnum;
while (count--)
{
did_skip = FALSE;
for (first = TRUE; ; first = FALSE)
{
if (*ml_get(curr) != NUL)
did_skip = TRUE;
#ifdef FEAT_FOLDING
// skip folded lines
fold_skipped = FALSE;
if (first && hasFolding(curr, &fold_first, &fold_last))
{
curr = ((dir > 0) ? fold_last : fold_first) + dir;
fold_skipped = TRUE;
}
#endif
// POSIX has its own ideas of what a paragraph boundary is and it
// doesn't match historical Vi: It also stops at a "{" in the
// first column and at an empty line.
if (!first && did_skip && (startPS(curr, what, both)
|| (posix && what == NUL && *ml_get(curr) == '{')))
break;
#ifdef FEAT_FOLDING
if (fold_skipped)
curr -= dir;
#endif
if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
{
if (count)
return FALSE;
curr -= dir;
break;
}
}
}
setpcmark();
if (both && *ml_get(curr) == '}') // include line with '}'
++curr;
curwin->w_cursor.lnum = curr;
if (curr == curbuf->b_ml.ml_line_count && what != '}')
{
char_u *line = ml_get(curr);
// Put the cursor on the last character in the last line and make the
// motion inclusive.
if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0)
{
--curwin->w_cursor.col;
curwin->w_cursor.col -=
(*mb_head_off)(line, line + curwin->w_cursor.col);
*pincl = TRUE;
}
}
else
curwin->w_cursor.col = 0;
return TRUE;
}
/*
* check if the string 's' is a nroff macro that is in option 'opt'
*/
static int
inmacro(char_u *opt, char_u *s)
{
char_u *macro;
for (macro = opt; macro[0]; ++macro)
{
// Accept two characters in the option being equal to two characters
// in the line. A space in the option matches with a space in the
// line or the line having ended.
if ( (macro[0] == s[0]
|| (macro[0] == ' '
&& (s[0] == NUL || s[0] == ' ')))
&& (macro[1] == s[1]
|| ((macro[1] == NUL || macro[1] == ' ')
&& (s[0] == NUL || s[1] == NUL || s[1] == ' '))))
break;
++macro;
if (macro[0] == NUL)
break;
}
return (macro[0] != NUL);
}
/*
* startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
* If 'para' is '{' or '}' only check for sections.
* If 'both' is TRUE also stop at '}'
*/
int
startPS(linenr_T lnum, int para, int both)
{
char_u *s;
s = ml_get(lnum);
if (*s == para || *s == '\f' || (both && *s == '}'))
return TRUE;
if (*s == '.' && (inmacro(p_sections, s + 1) ||
(!para && inmacro(p_para, s + 1))))
return TRUE;
return FALSE;
}
/*
* The following routines do the word searches performed by the 'w', 'W',
* 'b', 'B', 'e', and 'E' commands.
*/
/*
* To perform these searches, characters are placed into one of three
* classes, and transitions between classes determine word boundaries.
*
* The classes are:
*
* 0 - white space
* 1 - punctuation
* 2 or higher - keyword characters (letters, digits and underscore)
*/
static int cls_bigword; // TRUE for "W", "B" or "E"
/*
* cls() - returns the class of character at curwin->w_cursor
*
* If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
* from class 2 and higher are reported as class 1 since only white space
* boundaries are of interest.
*/
static int
cls(void)
{
int c;
c = gchar_cursor();
if (c == ' ' || c == '\t' || c == NUL)
return 0;
if (enc_dbcs != 0 && c > 0xFF)
{
// If cls_bigword, report multi-byte chars as class 1.
if (enc_dbcs == DBCS_KOR && cls_bigword)
return 1;
// process code leading/trailing bytes
return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
}
if (enc_utf8)
{
c = utf_class(c);
if (c != 0 && cls_bigword)
return 1;
return c;
}
// If cls_bigword is TRUE, report all non-blanks as class 1.
if (cls_bigword)
return 1;
if (vim_iswordc(c))
return 2;
return 1;
}
/*
* fwd_word(count, type, eol) - move forward one word
*
* Returns FAIL if the cursor was already at the end of the file.
* If eol is TRUE, last word stops at end of line (for operators).
*/
int
fwd_word(
long count,
int bigword, // "W", "E" or "B"
int eol)
{
int sclass; // starting class
int i;
int last_line;
curwin->w_cursor.coladd = 0;
cls_bigword = bigword;
while (--count >= 0)
{
#ifdef FEAT_FOLDING
// When inside a range of folded lines, move to the last char of the
// last line.
if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
coladvance((colnr_T)MAXCOL);
#endif
sclass = cls();
/*
* We always move at least one character, unless on the last
* character in the buffer.
*/
last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
i = inc_cursor();
if (i == -1 || (i >= 1 && last_line)) // started at last char in file
return FAIL;
if (i >= 1 && eol && count == 0) // started at last char in line
return OK;
/*
* Go one char past end of current word (if any)
*/
if (sclass != 0)
while (cls() == sclass)
{
i = inc_cursor();
if (i == -1 || (i >= 1 && eol && count == 0))
return OK;
}
/*
* go to next non-white
*/
while (cls() == 0)
{
/*
* We'll stop if we land on a blank line
*/
if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
break;
i = inc_cursor();
if (i == -1 || (i >= 1 && eol && count == 0))
return OK;
}
}
return OK;
}
/*
* bck_word() - move backward 'count' words
*
* If stop is TRUE and we are already on the start of a word, move one less.
*
* Returns FAIL if top of the file was reached.
*/
int
bck_word(long count, int bigword, int stop)
{
int sclass; // starting class
curwin->w_cursor.coladd = 0;
cls_bigword = bigword;
while (--count >= 0)
{
#ifdef FEAT_FOLDING
// When inside a range of folded lines, move to the first char of the
// first line.
if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
curwin->w_cursor.col = 0;
#endif
sclass = cls();
if (dec_cursor() == -1) // started at start of file
return FAIL;
if (!stop || sclass == cls() || sclass == 0)
{
/*
* Skip white space before the word.
* Stop on an empty line.
*/
while (cls() == 0)
{
if (curwin->w_cursor.col == 0
&& LINEEMPTY(curwin->w_cursor.lnum))
goto finished;
if (dec_cursor() == -1) // hit start of file, stop here
return OK;
}
/*
* Move backward to start of this word.
*/
if (skip_chars(cls(), BACKWARD))
return OK;
}
inc_cursor(); // overshot - forward one
finished:
stop = FALSE;
}
return OK;
}
/*
* end_word() - move to the end of the word
*
* There is an apparent bug in the 'e' motion of the real vi. At least on the
* System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
* motion crosses blank lines. When the real vi crosses a blank line in an
* 'e' motion, the cursor is placed on the FIRST character of the next
* non-blank line. The 'E' command, however, works correctly. Since this
* appears to be a bug, I have not duplicated it here.
*
* Returns FAIL if end of the file was reached.
*
* If stop is TRUE and we are already on the end of a word, move one less.
* If empty is TRUE stop on an empty line.
*/
int
end_word(
long count,
int bigword,
int stop,
int empty)
{
int sclass; // starting class
curwin->w_cursor.coladd = 0;
cls_bigword = bigword;
while (--count >= 0)
{
#ifdef FEAT_FOLDING
// When inside a range of folded lines, move to the last char of the
// last line.
if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
coladvance((colnr_T)MAXCOL);
#endif
sclass = cls();
if (inc_cursor() == -1)
return FAIL;
/*
* If we're in the middle of a word, we just have to move to the end
* of it.
*/
if (cls() == sclass && sclass != 0)
{
/*
* Move forward to end of the current word
*/
if (skip_chars(sclass, FORWARD))
return FAIL;
}
else if (!stop || sclass == 0)
{
/*
* We were at the end of a word. Go to the end of the next word.
* First skip white space, if 'empty' is TRUE, stop at empty line.
*/
while (cls() == 0)
{
if (empty && curwin->w_cursor.col == 0
&& LINEEMPTY(curwin->w_cursor.lnum))
goto finished;
if (inc_cursor() == -1) // hit end of file, stop here
return FAIL;
}
/*
* Move forward to the end of this word.
*/
if (skip_chars(cls(), FORWARD))
return FAIL;
}
dec_cursor(); // overshot - one char backward
finished:
stop = FALSE; // we move only one word less
}
return OK;
}
/*
* Move back to the end of the word.
*
* Returns FAIL if start of the file was reached.
*/
int
bckend_word(
long count,
int bigword, // TRUE for "B"
int eol) // TRUE: stop at end of line.
{
int sclass; // starting class
int i;
curwin->w_cursor.coladd = 0;
cls_bigword = bigword;
while (--count >= 0)
{
sclass = cls();
if ((i = dec_cursor()) == -1)
return FAIL;
if (eol && i == 1)
return OK;
/*
* Move backward to before the start of this word.
*/
if (sclass != 0)
{
while (cls() == sclass)
if ((i = dec_cursor()) == -1 || (eol && i == 1))
return OK;
}
/*
* Move backward to end of the previous word
*/
while (cls() == 0)
{
if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum))
break;
if ((i = dec_cursor()) == -1 || (eol && i == 1))
return OK;
}
}
return OK;
}
/*
* Skip a row of characters of the same class.
* Return TRUE when end-of-file reached, FALSE otherwise.
*/
static int
skip_chars(int cclass, int dir)
{
while (cls() == cclass)
if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
return TRUE;
return FALSE;
}
/*
* Go back to the start of the word or the start of white space
*/
static void
back_in_line(void)
{
int sclass; // starting class
sclass = cls();
for (;;)
{
if (curwin->w_cursor.col == 0) // stop at start of line
break;
dec_cursor();
if (cls() != sclass) // stop at start of word
{
inc_cursor();
break;
}
}
}
static void
find_first_blank(pos_T *posp)
{
int c;
while (decl(posp) != -1)
{
c = gchar_pos(posp);
if (!VIM_ISWHITE(c))
{
incl(posp);
break;
}
}
}
/*
* Skip count/2 sentences and count/2 separating white spaces.
*/
static void
findsent_forward(
long count,
int at_start_sent) // cursor is at start of sentence
{
while (count--)
{
findsent(FORWARD, 1L);
if (at_start_sent)
find_first_blank(&curwin->w_cursor);
if (count == 0 || at_start_sent)
decl(&curwin->w_cursor);
at_start_sent = !at_start_sent;
}
}
/*
* Find word under cursor, cursor at end.
* Used while an operator is pending, and in Visual mode.
*/
int
current_word(
oparg_T *oap,
long count,
int include, // TRUE: include word and white space
int bigword) // FALSE == word, TRUE == WORD
{
pos_T start_pos;
pos_T pos;
int inclusive = TRUE;
int include_white = FALSE;
cls_bigword = bigword;
CLEAR_POS(&start_pos);
// Correct cursor when 'selection' is exclusive
if (VIsual_active && *p_sel == 'e' && LT_POS(VIsual, curwin->w_cursor))
dec_cursor();
/*
* When Visual mode is not active, or when the VIsual area is only one
* character, select the word and/or white space under the cursor.
*/
if (!VIsual_active || EQUAL_POS(curwin->w_cursor, VIsual))
{
/*
* Go to start of current word or white space.
*/
back_in_line();
start_pos = curwin->w_cursor;
/*
* If the start is on white space, and white space should be included
* (" word"), or start is not on white space, and white space should
* not be included ("word"), find end of word.
*/
if ((cls() == 0) == include)
{
if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
return FAIL;
}
else
{
/*
* If the start is not on white space, and white space should be
* included ("word "), or start is on white space and white
* space should not be included (" "), find start of word.
* If we end up in the first column of the next line (single char
* word) back up to end of the line.
*/
fwd_word(1L, bigword, TRUE);
if (curwin->w_cursor.col == 0)
decl(&curwin->w_cursor);
else
oneleft();
if (include)
include_white = TRUE;
}
if (VIsual_active)
{
// should do something when inclusive == FALSE !
VIsual = start_pos;
redraw_curbuf_later(UPD_INVERTED); // update the inversion
}
else
{
oap->start = start_pos;
oap->motion_type = MCHAR;
}
--count;
}
/*
* When count is still > 0, extend with more objects.
*/
while (count > 0)
{
inclusive = TRUE;
if (VIsual_active && LT_POS(curwin->w_cursor, VIsual))
{
/*
* In Visual mode, with cursor at start: move cursor back.
*/
if (decl(&curwin->w_cursor) == -1)
return FAIL;
if (include != (cls() != 0))
{
if (bck_word(1L, bigword, TRUE) == FAIL)
return FAIL;
}
else
{
if (bckend_word(1L, bigword, TRUE) == FAIL)
return FAIL;
(void)incl(&curwin->w_cursor);
}
}
else
{
/*
* Move cursor forward one word and/or white area.
*/
if (incl(&curwin->w_cursor) == -1)
return FAIL;
if (include != (cls() == 0))
{
if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1)
return FAIL;
/*
* If end is just past a new-line, we don't want to include
* the first character on the line.
* Put cursor on last char of white.
*/
if (oneleft() == FAIL)
inclusive = FALSE;
}
else
{
if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
return FAIL;
}
}
--count;
}
if (include_white && (cls() != 0
|| (curwin->w_cursor.col == 0 && !inclusive)))
{
/*
* If we don't include white space at the end, move the start
* to include some white space there. This makes "daw" work
* better on the last word in a sentence (and "2daw" on last-but-one
* word). Also when "2daw" deletes "word." at the end of the line
* (cursor is at start of next line).
* But don't delete white space at start of line (indent).
*/
pos = curwin->w_cursor; // save cursor position
curwin->w_cursor = start_pos;
if (oneleft() == OK)
{
back_in_line();
if (cls() == 0 && curwin->w_cursor.col > 0)
{
if (VIsual_active)
VIsual = curwin->w_cursor;
else
oap->start = curwin->w_cursor;
}
}
curwin->w_cursor = pos; // put cursor back at end
}
if (VIsual_active)
{
if (*p_sel == 'e' && inclusive && LTOREQ_POS(VIsual, curwin->w_cursor))
inc_cursor();
if (VIsual_mode == 'V')
{
VIsual_mode = 'v';
redraw_cmdline = TRUE; // show mode later
}
}
else
oap->inclusive = inclusive;
return OK;
}
/*
* Find sentence(s) under the cursor, cursor at end.
* When Visual active, extend it by one or more sentences.
*/
int
current_sent(oparg_T *oap, long count, int include)
{
pos_T start_pos;
pos_T pos;
int start_blank;
int c;
int at_start_sent;
long ncount;
start_pos = curwin->w_cursor;
pos = start_pos;
findsent(FORWARD, 1L); // Find start of next sentence.
/*
* When the Visual area is bigger than one character: Extend it.
*/
if (VIsual_active && !EQUAL_POS(start_pos, VIsual))
{
extend:
if (LT_POS(start_pos, VIsual))
{
/*
* Cursor at start of Visual area.
* Find out where we are:
* - in the white space before a sentence
* - in a sentence or just after it
* - at the start of a sentence
*/
at_start_sent = TRUE;
decl(&pos);
while (LT_POS(pos, curwin->w_cursor))
{
c = gchar_pos(&pos);
if (!VIM_ISWHITE(c))
{
at_start_sent = FALSE;
break;
}
incl(&pos);
}
if (!at_start_sent)
{
findsent(BACKWARD, 1L);
if (EQUAL_POS(curwin->w_cursor, start_pos))
at_start_sent = TRUE; // exactly at start of sentence
else
// inside a sentence, go to its end (start of next)
findsent(FORWARD, 1L);
}
if (include) // "as" gets twice as much as "is"
count *= 2;
while (count--)
{
if (at_start_sent)
find_first_blank(&curwin->w_cursor);
c = gchar_cursor();
if (!at_start_sent || (!include && !VIM_ISWHITE(c)))
findsent(BACKWARD, 1L);
at_start_sent = !at_start_sent;
}
}
else
{
/*
* Cursor at end of Visual area.
* Find out where we are:
* - just before a sentence
* - just before or in the white space before a sentence
* - in a sentence
*/
incl(&pos);
at_start_sent = TRUE;
// not just before a sentence
if (!EQUAL_POS(pos, curwin->w_cursor))
{
at_start_sent = FALSE;
while (LT_POS(pos, curwin->w_cursor))
{
c = gchar_pos(&pos);
if (!VIM_ISWHITE(c))
{
at_start_sent = TRUE;
break;
}
incl(&pos);
}
if (at_start_sent) // in the sentence
findsent(BACKWARD, 1L);
else // in/before white before a sentence
curwin->w_cursor = start_pos;
}
if (include) // "as" gets twice as much as "is"
count *= 2;
findsent_forward(count, at_start_sent);
if (*p_sel == 'e')
++curwin->w_cursor.col;
}
return OK;
}
/*
* If the cursor started on a blank, check if it is just before the start
* of the next sentence.
*/
while (c = gchar_pos(&pos), VIM_ISWHITE(c)) // VIM_ISWHITE() is a macro
incl(&pos);
if (EQUAL_POS(pos, curwin->w_cursor))
{
start_blank = TRUE;
find_first_blank(&start_pos); // go back to first blank
}
else
{
start_blank = FALSE;
findsent(BACKWARD, 1L);
start_pos = curwin->w_cursor;
}
if (include)
ncount = count * 2;
else
{
ncount = count;
if (start_blank)
--ncount;
}
if (ncount > 0)
findsent_forward(ncount, TRUE);
else
decl(&curwin->w_cursor);
if (include)
{
/*
* If the blank in front of the sentence is included, exclude the
* blanks at the end of the sentence, go back to the first blank.
* If there are no trailing blanks, try to include leading blanks.
*/
if (start_blank)
{
find_first_blank(&curwin->w_cursor);
c = gchar_pos(&curwin->w_cursor); // VIM_ISWHITE() is a macro
if (VIM_ISWHITE(c))
decl(&curwin->w_cursor);
}
else if (c = gchar_cursor(), !VIM_ISWHITE(c))
find_first_blank(&start_pos);
}