1 /* $NetBSD: v_sentence.c,v 1.2 2013/11/22 15:52:06 christos Exp $ */
3 * Copyright (c) 1992, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 * Copyright (c) 1992, 1993, 1994, 1995, 1996
6 * Keith Bostic. All rights reserved.
8 * See the LICENSE file for redistribution information.
14 static const char sccsid
[] = "Id: v_sentence.c,v 10.9 2001/06/25 15:19:35 skimo Exp (Berkeley) Date: 2001/06/25 15:19:35 ";
17 #include <sys/types.h>
18 #include <sys/queue.h>
21 #include <bitstring.h>
26 #include "../common/common.h"
31 * In historic vi, a sentence was delimited by a '.', '?' or '!' character
32 * followed by TWO spaces or a newline. One or more empty lines was also
33 * treated as a separate sentence. The Berkeley documentation for historical
34 * vi states that any number of ')', ']', '"' and '\'' characters can be
35 * between the delimiter character and the spaces or end of line, however,
36 * the historical implementation did not handle additional '"' characters.
37 * We follow the documentation here, not the implementation.
39 * Once again, historical vi didn't do sentence movements associated with
40 * counts consistently, mostly in the presence of lines containing only
41 * white-space characters.
43 * This implementation also permits a single tab to delimit sentences, and
44 * treats lines containing only white-space characters as empty lines.
45 * Finally, tabs are eaten (along with spaces) when skipping to the start
46 * of the text following a "sentence".
50 * v_sentencef -- [count])
51 * Move forward count sentences.
53 * PUBLIC: int v_sentencef __P((SCR *, VICMD *));
56 v_sentencef(SCR
*sp
, VICMD
*vp
)
58 enum { BLANK
, NONE
, PERIOD
} state
;
63 cs
.cs_lno
= vp
->m_start
.lno
;
64 cs
.cs_cno
= vp
->m_start
.cno
;
68 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
72 * If in white-space, the next start of sentence counts as one.
73 * This may not handle " . " correctly, but it's real unclear
74 * what correctly means in that case.
76 if (cs
.cs_flags
== CS_EMP
|| (cs
.cs_flags
== 0 && ISBLANK2(cs
.cs_ch
))) {
77 if (cs_fblank(sp
, &cs
))
80 if (vp
->m_start
.lno
!= cs
.cs_lno
||
81 vp
->m_start
.cno
!= cs
.cs_cno
)
87 for (state
= NONE
;;) {
90 if (cs
.cs_flags
== CS_EOF
)
92 if (cs
.cs_flags
== CS_EOL
) {
93 if ((state
== PERIOD
|| state
== BLANK
) && --cnt
== 0) {
96 if (cs
.cs_flags
== 0 &&
97 ISBLANK2(cs
.cs_ch
) && cs_fblank(sp
, &cs
))
104 if (cs
.cs_flags
== CS_EMP
) { /* An EMP is two sentences. */
107 if (cs_fblank(sp
, &cs
))
132 if (state
== PERIOD
) {
136 if (state
== BLANK
&& --cnt
== 0) {
137 if (cs_fblank(sp
, &cs
))
148 /* EOF is a movement sink, but it's an error not to have moved. */
149 if (vp
->m_start
.lno
== cs
.cs_lno
&& vp
->m_start
.cno
== cs
.cs_cno
) {
154 okret
: vp
->m_stop
.lno
= cs
.cs_lno
;
155 vp
->m_stop
.cno
= cs
.cs_cno
;
159 * Historic, uh, features, yeah, that's right, call 'em features.
160 * If the starting and ending cursor positions are at the first
161 * column in their lines, i.e. the movement is cutting entire lines,
162 * the buffer is in line mode, and the ending position is the last
163 * character of the previous line. Note check to make sure that
164 * it's not within a single line.
166 * Non-motion commands move to the end of the range. Delete and
167 * yank stay at the start. Ignore others. Adjust the end of the
168 * range for motion commands.
171 if (vp
->m_start
.cno
== 0 &&
172 (cs
.cs_flags
!= 0 || vp
->m_stop
.cno
== 0)) {
173 if (vp
->m_start
.lno
< vp
->m_stop
.lno
) {
175 --vp
->m_stop
.lno
, DBG_FATAL
, NULL
, &len
))
177 vp
->m_stop
.cno
= len
? len
- 1 : 0;
182 vp
->m_final
= vp
->m_start
;
184 vp
->m_final
= vp
->m_stop
;
189 * v_sentenceb -- [count](
190 * Move backward count sentences.
192 * PUBLIC: int v_sentenceb __P((SCR *, VICMD *));
195 v_sentenceb(SCR
*sp
, VICMD
*vp
)
205 * Historic vi permitted the user to hit SOF repeatedly.
207 if (vp
->m_start
.lno
== 1 && vp
->m_start
.cno
== 0)
210 cs
.cs_lno
= vp
->m_start
.lno
;
211 cs
.cs_cno
= vp
->m_start
.cno
;
212 if (cs_init(sp
, &cs
))
215 cnt
= F_ISSET(vp
, VC_C1SET
) ? vp
->count
: 1;
219 * In empty lines, skip to the previous non-white-space character.
220 * If in text, skip to the prevous white-space character. Believe
221 * it or not, in the paragraph:
224 * if the cursor is on the 'A' or 'B', ( moves to the 'a'. If it
225 * is on the ' ', 'C' or 'D', it moves to the 'A'. Yes, Virginia,
226 * Berkeley was once a major center of drug activity.
228 if (cs
.cs_flags
== CS_EMP
) {
229 if (cs_bblank(sp
, &cs
))
232 if (cs_prev(sp
, &cs
))
234 if (cs
.cs_flags
!= CS_EOL
)
237 } else if (cs
.cs_flags
== 0 && !ISBLANK2(cs
.cs_ch
))
239 if (cs_prev(sp
, &cs
))
241 if (cs
.cs_flags
!= 0 || ISBLANK2(cs
.cs_ch
))
246 if (cs_prev(sp
, &cs
))
248 if (cs
.cs_flags
== CS_SOF
) /* SOF is a movement sink. */
250 if (cs
.cs_flags
== CS_EOL
) {
254 if (cs
.cs_flags
== CS_EMP
) {
257 if (cs_bblank(sp
, &cs
))
266 if (!last
|| --cnt
!= 0) {
271 ret
: slno
= cs
.cs_lno
;
275 * Move to the start of the sentence, skipping blanks
276 * and special characters.
279 if (cs_next(sp
, &cs
))
281 } while (!cs
.cs_flags
&&
282 (cs
.cs_ch
== ')' || cs
.cs_ch
== ']' ||
283 cs
.cs_ch
== '"' || cs
.cs_ch
== '\''));
284 if ((cs
.cs_flags
|| ISBLANK2(cs
.cs_ch
)) &&
289 * If it was ". xyz", with the cursor on the 'x', or
290 * "end. ", with the cursor in the spaces, or the
291 * beginning of a sentence preceded by an empty line,
292 * we can end up where we started. Fix it.
294 if (vp
->m_start
.lno
!= cs
.cs_lno
||
295 vp
->m_start
.cno
!= cs
.cs_cno
)
299 * Well, if an empty line preceded possible blanks
300 * and the sentence, it could be a real sentence.
303 if (cs_prev(sp
, &cs
))
305 if (cs
.cs_flags
== CS_EOL
)
307 if (cs
.cs_flags
== 0 && ISBLANK2(cs
.cs_ch
))
311 if (cs
.cs_flags
== CS_EMP
)
314 /* But it wasn't; try again. */
325 cs
.cs_flags
== CS_EOL
|| ISBLANK2(cs
.cs_ch
) ||
326 cs
.cs_ch
== ')' || cs
.cs_ch
== ']' ||
327 cs
.cs_ch
== '"' || cs
.cs_ch
== '\'' ? 1 : 0;
331 okret
: vp
->m_stop
.lno
= cs
.cs_lno
;
332 vp
->m_stop
.cno
= cs
.cs_cno
;
336 * If the starting and stopping cursor positions are at the first
337 * columns in the line, i.e. the movement is cutting an entire line,
338 * the buffer is in line mode, and the starting position is the last
339 * character of the previous line.
341 * All commands move to the end of the range. Adjust the start of
342 * the range for motion commands.
345 if (vp
->m_start
.cno
== 0 &&
346 (cs
.cs_flags
!= 0 || vp
->m_stop
.cno
== 0)) {
348 --vp
->m_start
.lno
, DBG_FATAL
, NULL
, &len
))
350 vp
->m_start
.cno
= len
? len
- 1 : 0;
355 vp
->m_final
= vp
->m_stop
;