2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: v_sentence.c,v 8.7 1993/08/26 12:14:09 bostic Exp $ (Berkeley) $Date: 1993/08/26 12:14:09 $";
12 #include <sys/types.h>
20 * In historic vi, a sentence was delimited by a '.', '?' or '!' character
21 * followed by TWO spaces or a newline. One or more empty lines was also
22 * treated as a separate sentence. The Berkeley documentation for historical
23 * vi states that any number of ')', ']', '"' and '\'' characters can be
24 * between the delimiter character and the spaces or end of line, however,
25 * the historical implementation did not handle additional '"' characters.
26 * We follow the documentation here, not the implementation.
28 * Once again, historical vi didn't do sentence movements associated with
29 * counts consistently, mostly in the presence of lines containing only
30 * white-space characters.
32 * This implementation also permits a single tab to delimit sentences, and
33 * treats lines containing only white-space characters as empty lines.
34 * And, tabs are eaten (along with spaces) when skipping to the start of the
35 * text follow a "sentence".
39 * v_sentencef -- [count])
40 * Move forward count sentences.
43 v_sentencef(sp
, ep
, vp
, fm
, tm
, rp
)
49 enum { BLANK
, NONE
, PERIOD
} state
;
55 if (cs_init(sp
, ep
, &cs
))
58 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
61 * If in white-space, the next start of sentence counts as one.
62 * This may not handle " . " correctly, but it's real unclear
63 * what correctly means in that case.
65 if (cs
.cs_flags
== CS_EMP
|| cs
.cs_flags
== 0 && isblank(cs
.cs_ch
)) {
66 if (cs_fblank(sp
, ep
, &cs
))
69 if (fm
->lno
!= cs
.cs_lno
|| fm
->cno
!= cs
.cs_cno
)
74 for (state
= NONE
;;) {
75 if (cs_next(sp
, ep
, &cs
))
77 if (cs
.cs_flags
== CS_EOF
)
79 if (cs
.cs_flags
== CS_EOL
) {
80 if ((state
== PERIOD
|| state
== BLANK
) && --cnt
== 0) {
81 if (cs_next(sp
, ep
, &cs
))
83 if (cs
.cs_flags
== 0 &&
84 isblank(cs
.cs_ch
) && cs_fblank(sp
, ep
, &cs
))
91 if (cs
.cs_flags
== CS_EMP
) { /* An EMP is two sentences. */
94 if (cs_fblank(sp
, ep
, &cs
))
119 if (state
== PERIOD
) {
123 if (state
== BLANK
&& --cnt
== 0) {
124 if (cs_fblank(sp
, ep
, &cs
))
135 /* EOF is a movement sink. */
136 if (fm
->lno
!= cs
.cs_lno
|| fm
->cno
!= cs
.cs_cno
)
141 okret
: rp
->lno
= cs
.cs_lno
;
145 * Historic, uh, features, yeah, that's right, call 'em features.
146 * If the sentence movement is cutting an entire line, the buffer
147 * is in line mode. Reach up into the caller's VICMDARG structure,
148 * and whack the flags.
150 if (F_ISSET(vp
, VC_C
| VC_D
| VC_Y
) &&
151 fm
->cno
== 0 && (rp
->cno
== 0 || cs
.cs_flags
!= 0)) {
160 * v_sentenceb -- [count](
161 * Move backward count sentences.
164 v_sentenceb(sp
, ep
, vp
, fm
, tm
, rp
)
176 if (fm
->lno
== 1 && fm
->cno
== 0) {
183 if (cs_init(sp
, ep
, &cs
))
186 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
189 * If on an empty line, skip to the next previous
190 * non-white-space character.
192 if (cs
.cs_flags
== CS_EMP
) {
193 if (cs_bblank(sp
, ep
, &cs
))
196 if (cs_next(sp
, ep
, &cs
))
198 if (cs
.cs_flags
!= CS_EOL
)
203 for (last1
= last2
= 0;;) {
204 if (cs_prev(sp
, ep
, &cs
))
206 if (cs
.cs_flags
== CS_SOF
) /* SOF is a movement sink. */
208 if (cs
.cs_flags
== CS_EOL
) {
213 if (cs
.cs_flags
== CS_EMP
) {
216 if (cs_bblank(sp
, ep
, &cs
))
225 if (!last1
|| --cnt
!= 0) {
230 ret
: slno
= cs
.cs_lno
;
234 * Move to the start of the sentence, skipping blanks
235 * and special characters.
238 if (cs_next(sp
, ep
, &cs
))
240 } while (!cs
.cs_flags
&&
241 (cs
.cs_ch
== ')' || cs
.cs_ch
== ']' ||
242 cs
.cs_ch
== '"' || cs
.cs_ch
== '\''));
243 if ((cs
.cs_flags
|| isblank(cs
.cs_ch
)) &&
244 cs_fblank(sp
, ep
, &cs
))
248 * If it was ". xyz", with the cursor on the 'x', or
249 * "end. ", with the cursor in the spaces, or the
250 * beginning of a sentence preceded by an empty line,
251 * we can end up where we started. Fix it.
253 if (fm
->lno
!= cs
.cs_lno
|| fm
->cno
!= cs
.cs_cno
)
257 * Well, if an empty line preceded possible blanks
258 * and the sentence, it could be a real sentence.
261 if (cs_prev(sp
, ep
, &cs
))
263 if (cs
.cs_flags
== CS_EOL
)
265 if (cs
.cs_flags
== 0 && isblank(cs
.cs_ch
))
269 if (cs
.cs_flags
== CS_EMP
)
272 /* But it wasn't; try again. */
284 cs
.cs_flags
== CS_EOL
|| isblank(cs
.cs_ch
) ||
285 cs
.cs_ch
== ')' || cs
.cs_ch
== ']' ||
286 cs
.cs_ch
== '"' || cs
.cs_ch
== '\'' ? 1 : 0;
290 okret
: rp
->lno
= cs
.cs_lno
;
294 * See comment in v_sentencef(). Ignore errors, they should
295 * never occur, and they'll get caught later.
297 if (F_ISSET(vp
, VC_C
| VC_D
| VC_Y
) && rp
->cno
== 0 &&
298 file_gline(sp
, ep
, fm
->lno
, &len
) != NULL
&& (len
== 0 ||