discard the old line before updating the screen so that the line
[nvi.git] / vi / v_sentence.c
blobe735dc4a5e5fa91fe2a53622a05c0cdc5f3e7667
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
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 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <ctype.h>
16 #include "vi.h"
17 #include "vcmd.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.
42 int
43 v_sentencef(sp, ep, vp, fm, tm, rp)
44 SCR *sp;
45 EXF *ep;
46 VICMDARG *vp;
47 MARK *fm, *tm, *rp;
49 enum { BLANK, NONE, PERIOD } state;
50 VCS cs;
51 u_long cnt;
53 cs.cs_lno = fm->lno;
54 cs.cs_cno = fm->cno;
55 if (cs_init(sp, ep, &cs))
56 return (1);
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))
67 return (1);
68 if (--cnt == 0) {
69 if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
70 goto okret;
71 return (1);
74 for (state = NONE;;) {
75 if (cs_next(sp, ep, &cs))
76 return (1);
77 if (cs.cs_flags == CS_EOF)
78 break;
79 if (cs.cs_flags == CS_EOL) {
80 if ((state == PERIOD || state == BLANK) && --cnt == 0) {
81 if (cs_next(sp, ep, &cs))
82 return (1);
83 if (cs.cs_flags == 0 &&
84 isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs))
85 return (1);
86 goto okret;
88 state = NONE;
89 continue;
91 if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */
92 if (--cnt == 0)
93 goto okret;
94 if (cs_fblank(sp, ep, &cs))
95 return (1);
96 if (--cnt == 0)
97 goto okret;
98 state = NONE;
99 continue;
101 switch (cs.cs_ch) {
102 case '.':
103 case '?':
104 case '!':
105 state = PERIOD;
106 break;
107 case ')':
108 case ']':
109 case '"':
110 case '\'':
111 if (state != PERIOD)
112 state = NONE;
113 break;
114 case '\t':
115 if (state == PERIOD)
116 state = BLANK;
117 /* FALLTHROUGH */
118 case ' ':
119 if (state == PERIOD) {
120 state = BLANK;
121 break;
123 if (state == BLANK && --cnt == 0) {
124 if (cs_fblank(sp, ep, &cs))
125 return (1);
126 goto okret;
128 /* FALLTHROUGH */
129 default:
130 state = NONE;
131 break;
135 /* EOF is a movement sink. */
136 if (fm->lno != cs.cs_lno || fm->cno != cs.cs_cno)
137 goto okret;
138 v_eof(sp, ep, NULL);
139 return (1);
141 okret: rp->lno = cs.cs_lno;
142 rp->cno = cs.cs_cno;
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)) {
152 if (rp->cno == 0)
153 --rp->lno;
154 F_SET(vp, VC_LMODE);
156 return (0);
160 * v_sentenceb -- [count](
161 * Move backward count sentences.
164 v_sentenceb(sp, ep, vp, fm, tm, rp)
165 SCR *sp;
166 EXF *ep;
167 VICMDARG *vp;
168 MARK *fm, *tm, *rp;
170 VCS cs;
171 recno_t slno;
172 size_t len, scno;
173 u_long cnt;
174 int last1, last2;
176 if (fm->lno == 1 && fm->cno == 0) {
177 v_sof(sp, NULL);
178 return (1);
181 cs.cs_lno = fm->lno;
182 cs.cs_cno = fm->cno;
183 if (cs_init(sp, ep, &cs))
184 return (1);
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))
194 return (1);
195 for (;;) {
196 if (cs_next(sp, ep, &cs))
197 return (1);
198 if (cs.cs_flags != CS_EOL)
199 break;
203 for (last1 = last2 = 0;;) {
204 if (cs_prev(sp, ep, &cs))
205 return (1);
206 if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */
207 break;
208 if (cs.cs_flags == CS_EOL) {
209 last2 = last1;
210 last1 = 1;
211 continue;
213 if (cs.cs_flags == CS_EMP) {
214 if (--cnt == 0)
215 goto ret;
216 if (cs_bblank(sp, ep, &cs))
217 return (1);
218 last1 = last2 = 0;
219 continue;
221 switch (cs.cs_ch) {
222 case '.':
223 case '?':
224 case '!':
225 if (!last1 || --cnt != 0) {
226 last2 = last1 = 0;
227 continue;
230 ret: slno = cs.cs_lno;
231 scno = cs.cs_cno;
234 * Move to the start of the sentence, skipping blanks
235 * and special characters.
237 do {
238 if (cs_next(sp, ep, &cs))
239 return (1);
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))
245 return (1);
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)
254 goto okret;
257 * Well, if an empty line preceded possible blanks
258 * and the sentence, it could be a real sentence.
260 for (;;) {
261 if (cs_prev(sp, ep, &cs))
262 return (1);
263 if (cs.cs_flags == CS_EOL)
264 continue;
265 if (cs.cs_flags == 0 && isblank(cs.cs_ch))
266 continue;
267 break;
269 if (cs.cs_flags == CS_EMP)
270 goto okret;
272 /* But it wasn't; try again. */
273 ++cnt;
274 cs.cs_lno = slno;
275 cs.cs_cno = scno;
276 last2 = last1 = 0;
277 break;
278 case '\t':
279 last1 = last2 = 1;
280 break;
281 default:
282 last2 = last1;
283 last1 =
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;
291 rp->cno = cs.cs_cno;
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 ||
299 fm->cno == len - 1))
300 F_SET(vp, VC_LMODE);
301 return (0);