1 |
|
|
/* $OpenBSD: vs_refresh.c,v 1.22 2016/01/30 21:31:08 martijn Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 1992, 1993, 1994 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* Copyright (c) 1992, 1993, 1994, 1995, 1996 |
7 |
|
|
* Keith Bostic. All rights reserved. |
8 |
|
|
* |
9 |
|
|
* See the LICENSE file for redistribution information. |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
#include "config.h" |
13 |
|
|
|
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/queue.h> |
16 |
|
|
#include <sys/time.h> |
17 |
|
|
|
18 |
|
|
#include <bitstring.h> |
19 |
|
|
#include <ctype.h> |
20 |
|
|
#include <libgen.h> |
21 |
|
|
#include <limits.h> |
22 |
|
|
#include <stdio.h> |
23 |
|
|
#include <stdlib.h> |
24 |
|
|
#include <string.h> |
25 |
|
|
|
26 |
|
|
#include "../common/common.h" |
27 |
|
|
#include "vi.h" |
28 |
|
|
|
29 |
|
|
#define UPDATE_CURSOR 0x01 /* Update the cursor. */ |
30 |
|
|
#define UPDATE_SCREEN 0x02 /* Flush to screen. */ |
31 |
|
|
|
32 |
|
|
static void vs_modeline(SCR *); |
33 |
|
|
static int vs_paint(SCR *, u_int); |
34 |
|
|
|
35 |
|
|
/* |
36 |
|
|
* v_repaint -- |
37 |
|
|
* Repaint selected lines from the screen. |
38 |
|
|
* |
39 |
|
|
* PUBLIC: int vs_repaint(SCR *, EVENT *); |
40 |
|
|
*/ |
41 |
|
|
int |
42 |
|
|
vs_repaint(SCR *sp, EVENT *evp) |
43 |
|
|
{ |
44 |
|
|
SMAP *smp; |
45 |
|
|
|
46 |
|
|
for (; evp->e_flno <= evp->e_tlno; ++evp->e_flno) { |
47 |
|
|
smp = HMAP + evp->e_flno - 1; |
48 |
|
|
SMAP_FLUSH(smp); |
49 |
|
|
if (vs_line(sp, smp, NULL, NULL)) |
50 |
|
|
return (1); |
51 |
|
|
} |
52 |
|
|
return (0); |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
/* |
56 |
|
|
* vs_refresh -- |
57 |
|
|
* Refresh all screens. |
58 |
|
|
* |
59 |
|
|
* PUBLIC: int vs_refresh(SCR *, int); |
60 |
|
|
*/ |
61 |
|
|
int |
62 |
|
|
vs_refresh(SCR *sp, int forcepaint) |
63 |
|
|
{ |
64 |
|
|
GS *gp; |
65 |
|
|
SCR *tsp; |
66 |
|
|
int need_refresh; |
67 |
|
|
u_int priv_paint, pub_paint; |
68 |
|
|
|
69 |
|
|
gp = sp->gp; |
70 |
|
|
|
71 |
|
|
/* |
72 |
|
|
* 1: Refresh the screen. |
73 |
|
|
* |
74 |
|
|
* If SC_SCR_REDRAW is set in the current screen, repaint everything |
75 |
|
|
* that we can find, including status lines. |
76 |
|
|
*/ |
77 |
|
|
if (F_ISSET(sp, SC_SCR_REDRAW)) |
78 |
|
|
TAILQ_FOREACH(tsp, &gp->dq, q) |
79 |
|
|
if (tsp != sp) |
80 |
|
|
F_SET(tsp, SC_SCR_REDRAW | SC_STATUS); |
81 |
|
|
|
82 |
|
|
/* |
83 |
|
|
* 2: Related or dirtied screens, or screens with messages. |
84 |
|
|
* |
85 |
|
|
* If related screens share a view into a file, they may have been |
86 |
|
|
* modified as well. Refresh any screens that aren't exiting that |
87 |
|
|
* have paint or dirty bits set. Always update their screens, we |
88 |
|
|
* are not likely to get another chance. Finally, if we refresh any |
89 |
|
|
* screens other than the current one, the cursor will be trashed. |
90 |
|
|
*/ |
91 |
|
|
pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW; |
92 |
|
|
priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH; |
93 |
|
|
if (O_ISSET(sp, O_NUMBER)) |
94 |
|
|
priv_paint |= VIP_N_RENUMBER; |
95 |
|
|
TAILQ_FOREACH(tsp, &gp->dq, q) |
96 |
|
|
if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) && |
97 |
|
|
(F_ISSET(tsp, pub_paint) || |
98 |
|
|
F_ISSET(VIP(tsp), priv_paint))) { |
99 |
|
|
(void)vs_paint(tsp, |
100 |
|
|
(F_ISSET(VIP(tsp), VIP_CUR_INVALID) ? |
101 |
|
|
UPDATE_CURSOR : 0) | UPDATE_SCREEN); |
102 |
|
|
F_SET(VIP(sp), VIP_CUR_INVALID); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
/* |
106 |
|
|
* 3: Refresh the current screen. |
107 |
|
|
* |
108 |
|
|
* Always refresh the current screen, it may be a cursor movement. |
109 |
|
|
* Also, always do it last -- that way, SC_SCR_REDRAW can be set |
110 |
|
|
* in the current screen only, and the screen won't flash. |
111 |
|
|
*/ |
112 |
|
|
if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint && |
113 |
|
|
F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN))) |
114 |
|
|
return (1); |
115 |
|
|
|
116 |
|
|
/* |
117 |
|
|
* 4: Paint any missing status lines. |
118 |
|
|
* |
119 |
|
|
* XXX |
120 |
|
|
* This is fairly evil. Status lines are written using the vi message |
121 |
|
|
* mechanism, since we have no idea how long they are. Since we may be |
122 |
|
|
* painting screens other than the current one, we don't want to make |
123 |
|
|
* the user wait. We depend heavily on there not being any other lines |
124 |
|
|
* currently waiting to be displayed and the message truncation code in |
125 |
|
|
* the msgq_status routine working. |
126 |
|
|
* |
127 |
|
|
* And, finally, if we updated any status lines, make sure the cursor |
128 |
|
|
* gets back to where it belongs. |
129 |
|
|
*/ |
130 |
|
|
need_refresh = 0; |
131 |
|
|
TAILQ_FOREACH(tsp, &gp->dq, q) |
132 |
|
|
if (F_ISSET(tsp, SC_STATUS)) { |
133 |
|
|
need_refresh = 1; |
134 |
|
|
vs_resolve(tsp, sp, 0); |
135 |
|
|
} |
136 |
|
|
if (need_refresh) |
137 |
|
|
(void)gp->scr_refresh(sp, 0); |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* A side-effect of refreshing the screen is that it's now ready |
141 |
|
|
* for everything else, i.e. messages. |
142 |
|
|
*/ |
143 |
|
|
F_SET(sp, SC_SCR_VI); |
144 |
|
|
return (0); |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
/* |
148 |
|
|
* vs_paint -- |
149 |
|
|
* This is the guts of the vi curses screen code. The idea is that |
150 |
|
|
* the SCR structure passed in contains the new coordinates of the |
151 |
|
|
* screen. What makes this hard is that we don't know how big |
152 |
|
|
* characters are, doing input can put the cursor in illegal places, |
153 |
|
|
* and we're frantically trying to avoid repainting unless it's |
154 |
|
|
* absolutely necessary. If you change this code, you'd better know |
155 |
|
|
* what you're doing. It's subtle and quick to anger. |
156 |
|
|
*/ |
157 |
|
|
static int |
158 |
|
|
vs_paint(SCR *sp, u_int flags) |
159 |
|
|
{ |
160 |
|
|
GS *gp; |
161 |
|
|
SMAP *smp, tmp; |
162 |
|
|
VI_PRIVATE *vip; |
163 |
|
|
recno_t lastline, lcnt; |
164 |
|
|
size_t cwtotal, cnt, len, notused, off, y; |
165 |
|
|
int ch = 0, didpaint, isempty, leftright_warp; |
166 |
|
|
char *p; |
167 |
|
|
|
168 |
|
|
#define LNO sp->lno /* Current file line. */ |
169 |
|
|
#define OLNO vip->olno /* Remembered file line. */ |
170 |
|
|
#define CNO sp->cno /* Current file column. */ |
171 |
|
|
#define OCNO vip->ocno /* Remembered file column. */ |
172 |
|
|
#define SCNO vip->sc_col /* Current screen column. */ |
173 |
|
|
|
174 |
|
|
gp = sp->gp; |
175 |
|
|
vip = VIP(sp); |
176 |
|
|
didpaint = leftright_warp = 0; |
177 |
|
|
|
178 |
|
|
/* |
179 |
|
|
* 5: Reformat the lines. |
180 |
|
|
* |
181 |
|
|
* If the lines themselves have changed (:set list, for example), |
182 |
|
|
* fill in the map from scratch. Adjust the screen that's being |
183 |
|
|
* displayed if the leftright flag is set. |
184 |
|
|
*/ |
185 |
|
|
if (F_ISSET(sp, SC_SCR_REFORMAT)) { |
186 |
|
|
/* Invalidate the line size cache. */ |
187 |
|
|
VI_SCR_CFLUSH(vip); |
188 |
|
|
|
189 |
|
|
/* Toss vs_line() cached information. */ |
190 |
|
|
if (F_ISSET(sp, SC_SCR_TOP)) { |
191 |
|
|
if (vs_sm_fill(sp, LNO, P_TOP)) |
192 |
|
|
return (1); |
193 |
|
|
} |
194 |
|
|
else if (F_ISSET(sp, SC_SCR_CENTER)) { |
195 |
|
|
if (vs_sm_fill(sp, LNO, P_MIDDLE)) |
196 |
|
|
return (1); |
197 |
|
|
} else { |
198 |
|
|
if (LNO == HMAP->lno || LNO == TMAP->lno) { |
199 |
|
|
cnt = vs_screens(sp, LNO, &CNO); |
200 |
|
|
if (LNO == HMAP->lno && cnt < HMAP->soff) |
201 |
|
|
HMAP->soff = cnt; |
202 |
|
|
if (LNO == TMAP->lno && cnt > TMAP->soff) |
203 |
|
|
TMAP->soff = cnt; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
if (vs_sm_fill(sp, OOBLNO, P_TOP)) |
207 |
|
|
return (1); |
208 |
|
|
} |
209 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
210 |
|
|
} |
211 |
|
|
|
212 |
|
|
/* |
213 |
|
|
* 6: Line movement. |
214 |
|
|
* |
215 |
|
|
* Line changes can cause the top line to change as well. As |
216 |
|
|
* before, if the movement is large, the screen is repainted. |
217 |
|
|
* |
218 |
|
|
* 6a: Small screens. |
219 |
|
|
* |
220 |
|
|
* Users can use the window, w300, w1200 and w9600 options to make |
221 |
|
|
* the screen artificially small. The behavior of these options |
222 |
|
|
* in the historic vi wasn't all that consistent, and, in fact, it |
223 |
|
|
* was never documented how various screen movements affected the |
224 |
|
|
* screen size. Generally, one of three things would happen: |
225 |
|
|
* 1: The screen would expand in size, showing the line |
226 |
|
|
* 2: The screen would scroll, showing the line |
227 |
|
|
* 3: The screen would compress to its smallest size and |
228 |
|
|
* repaint. |
229 |
|
|
* In general, scrolling didn't cause compression (200^D was handled |
230 |
|
|
* the same as ^D), movement to a specific line would (:N where N |
231 |
|
|
* was 1 line below the screen caused a screen compress), and cursor |
232 |
|
|
* movement would scroll if it was 11 lines or less, and compress if |
233 |
|
|
* it was more than 11 lines. (And, no, I have no idea where the 11 |
234 |
|
|
* comes from.) |
235 |
|
|
* |
236 |
|
|
* What we do is try and figure out if the line is less than half of |
237 |
|
|
* a full screen away. If it is, we expand the screen if there's |
238 |
|
|
* room, and then scroll as necessary. The alternative is to compress |
239 |
|
|
* and repaint. |
240 |
|
|
* |
241 |
|
|
* !!! |
242 |
|
|
* This code is a special case from beginning to end. Unfortunately, |
243 |
|
|
* home modems are still slow enough that it's worth having. |
244 |
|
|
* |
245 |
|
|
* XXX |
246 |
|
|
* If the line a really long one, i.e. part of the line is on the |
247 |
|
|
* screen but the column offset is not, we'll end up in the adjust |
248 |
|
|
* code, when we should probably have compressed the screen. |
249 |
|
|
*/ |
250 |
|
|
if (IS_SMALL(sp)) { |
251 |
|
|
if (LNO < HMAP->lno) { |
252 |
|
|
lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows); |
253 |
|
|
if (lcnt <= HALFSCREEN(sp)) |
254 |
|
|
for (; lcnt && sp->t_rows != sp->t_maxrows; |
255 |
|
|
--lcnt, ++sp->t_rows) { |
256 |
|
|
++TMAP; |
257 |
|
|
if (vs_sm_1down(sp)) |
258 |
|
|
return (1); |
259 |
|
|
} |
260 |
|
|
else |
261 |
|
|
goto small_fill; |
262 |
|
|
} else if (LNO > TMAP->lno) { |
263 |
|
|
lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows); |
264 |
|
|
if (lcnt <= HALFSCREEN(sp)) |
265 |
|
|
for (; lcnt && sp->t_rows != sp->t_maxrows; |
266 |
|
|
--lcnt, ++sp->t_rows) { |
267 |
|
|
if (vs_sm_next(sp, TMAP, TMAP + 1)) |
268 |
|
|
return (1); |
269 |
|
|
++TMAP; |
270 |
|
|
if (vs_line(sp, TMAP, NULL, NULL)) |
271 |
|
|
return (1); |
272 |
|
|
} |
273 |
|
|
else { |
274 |
|
|
small_fill: (void)gp->scr_move(sp, LASTLINE(sp), 0); |
275 |
|
|
(void)gp->scr_clrtoeol(sp); |
276 |
|
|
for (; sp->t_rows > sp->t_minrows; |
277 |
|
|
--sp->t_rows, --TMAP) { |
278 |
|
|
(void)gp->scr_move(sp, TMAP - HMAP, 0); |
279 |
|
|
(void)gp->scr_clrtoeol(sp); |
280 |
|
|
} |
281 |
|
|
if (vs_sm_fill(sp, LNO, P_FILL)) |
282 |
|
|
return (1); |
283 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
284 |
|
|
goto adjust; |
285 |
|
|
} |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
/* |
290 |
|
|
* 6b: Line down, or current screen. |
291 |
|
|
*/ |
292 |
|
|
if (LNO >= HMAP->lno) { |
293 |
|
|
/* Current screen. */ |
294 |
|
|
if (LNO <= TMAP->lno) |
295 |
|
|
goto adjust; |
296 |
|
|
if (F_ISSET(sp, SC_SCR_TOP)) |
297 |
|
|
goto top; |
298 |
|
|
if (F_ISSET(sp, SC_SCR_CENTER)) |
299 |
|
|
goto middle; |
300 |
|
|
|
301 |
|
|
/* |
302 |
|
|
* If less than half a screen above the line, scroll down |
303 |
|
|
* until the line is on the screen. |
304 |
|
|
*/ |
305 |
|
|
lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp)); |
306 |
|
|
if (lcnt < HALFTEXT(sp)) { |
307 |
|
|
while (lcnt--) |
308 |
|
|
if (vs_sm_1up(sp)) |
309 |
|
|
return (1); |
310 |
|
|
goto adjust; |
311 |
|
|
} |
312 |
|
|
goto bottom; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
/* |
316 |
|
|
* 6c: If not on the current screen, may request center or top. |
317 |
|
|
*/ |
318 |
|
|
if (F_ISSET(sp, SC_SCR_TOP)) |
319 |
|
|
goto top; |
320 |
|
|
if (F_ISSET(sp, SC_SCR_CENTER)) |
321 |
|
|
goto middle; |
322 |
|
|
|
323 |
|
|
/* |
324 |
|
|
* 6d: Line up. |
325 |
|
|
*/ |
326 |
|
|
lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp)); |
327 |
|
|
if (lcnt < HALFTEXT(sp)) { |
328 |
|
|
/* |
329 |
|
|
* If less than half a screen below the line, scroll up until |
330 |
|
|
* the line is the first line on the screen. Special check so |
331 |
|
|
* that if the screen has been emptied, we refill it. |
332 |
|
|
*/ |
333 |
|
|
if (db_exist(sp, HMAP->lno)) { |
334 |
|
|
while (lcnt--) |
335 |
|
|
if (vs_sm_1down(sp)) |
336 |
|
|
return (1); |
337 |
|
|
goto adjust; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
/* |
341 |
|
|
* If less than a half screen from the bottom of the file, |
342 |
|
|
* put the last line of the file on the bottom of the screen. |
343 |
|
|
*/ |
344 |
|
|
bottom: if (db_last(sp, &lastline)) |
345 |
|
|
return (1); |
346 |
|
|
tmp.lno = LNO; |
347 |
|
|
tmp.coff = HMAP->coff; |
348 |
|
|
tmp.soff = 1; |
349 |
|
|
lcnt = vs_sm_nlines(sp, &tmp, lastline, sp->t_rows); |
350 |
|
|
if (lcnt < HALFTEXT(sp)) { |
351 |
|
|
if (vs_sm_fill(sp, lastline, P_BOTTOM)) |
352 |
|
|
return (1); |
353 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
354 |
|
|
goto adjust; |
355 |
|
|
} |
356 |
|
|
/* It's not close, just put the line in the middle. */ |
357 |
|
|
goto middle; |
358 |
|
|
} |
359 |
|
|
|
360 |
|
|
/* |
361 |
|
|
* If less than half a screen from the top of the file, put the first |
362 |
|
|
* line of the file at the top of the screen. Otherwise, put the line |
363 |
|
|
* in the middle of the screen. |
364 |
|
|
*/ |
365 |
|
|
tmp.lno = 1; |
366 |
|
|
tmp.coff = HMAP->coff; |
367 |
|
|
tmp.soff = 1; |
368 |
|
|
lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp)); |
369 |
|
|
if (lcnt < HALFTEXT(sp)) { |
370 |
|
|
if (vs_sm_fill(sp, 1, P_TOP)) |
371 |
|
|
return (1); |
372 |
|
|
} else |
373 |
|
|
middle: if (vs_sm_fill(sp, LNO, P_MIDDLE)) |
374 |
|
|
return (1); |
375 |
|
|
if (0) { |
376 |
|
|
top: if (vs_sm_fill(sp, LNO, P_TOP)) |
377 |
|
|
return (1); |
378 |
|
|
} |
379 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
380 |
|
|
|
381 |
|
|
/* |
382 |
|
|
* At this point we know part of the line is on the screen. Since |
383 |
|
|
* scrolling is done using logical lines, not physical, all of the |
384 |
|
|
* line may not be on the screen. While that's not necessarily bad, |
385 |
|
|
* if the part the cursor is on isn't there, we're going to lose. |
386 |
|
|
* This can be tricky; if the line covers the entire screen, lno |
387 |
|
|
* may be the same as both ends of the map, that's why we test BOTH |
388 |
|
|
* the top and the bottom of the map. This isn't a problem for |
389 |
|
|
* left-right scrolling, the cursor movement code handles the problem. |
390 |
|
|
* |
391 |
|
|
* There's a performance issue here if editing *really* long lines. |
392 |
|
|
* This gets to the right spot by scrolling, and, in a binary, by |
393 |
|
|
* scrolling hundreds of lines. If the adjustment looks like it's |
394 |
|
|
* going to be a serious problem, refill the screen and repaint. |
395 |
|
|
*/ |
396 |
|
|
adjust: if (!O_ISSET(sp, O_LEFTRIGHT) && |
397 |
|
|
(LNO == HMAP->lno || LNO == TMAP->lno)) { |
398 |
|
|
cnt = vs_screens(sp, LNO, &CNO); |
399 |
|
|
if (LNO == HMAP->lno && cnt < HMAP->soff) { |
400 |
|
|
if ((HMAP->soff - cnt) > HALFTEXT(sp)) { |
401 |
|
|
HMAP->soff = cnt; |
402 |
|
|
vs_sm_fill(sp, OOBLNO, P_TOP); |
403 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
404 |
|
|
} else |
405 |
|
|
while (cnt < HMAP->soff) |
406 |
|
|
if (vs_sm_1down(sp)) |
407 |
|
|
return (1); |
408 |
|
|
} |
409 |
|
|
if (LNO == TMAP->lno && cnt > TMAP->soff) { |
410 |
|
|
if ((cnt - TMAP->soff) > HALFTEXT(sp)) { |
411 |
|
|
TMAP->soff = cnt; |
412 |
|
|
vs_sm_fill(sp, OOBLNO, P_BOTTOM); |
413 |
|
|
F_SET(sp, SC_SCR_REDRAW); |
414 |
|
|
} else |
415 |
|
|
while (cnt > TMAP->soff) |
416 |
|
|
if (vs_sm_1up(sp)) |
417 |
|
|
return (1); |
418 |
|
|
} |
419 |
|
|
} |
420 |
|
|
|
421 |
|
|
/* |
422 |
|
|
* If the screen needs to be repainted, skip cursor optimization. |
423 |
|
|
* However, in the code above we skipped leftright scrolling on |
424 |
|
|
* the grounds that the cursor code would handle it. Make sure |
425 |
|
|
* the right screen is up. |
426 |
|
|
*/ |
427 |
|
|
if (F_ISSET(sp, SC_SCR_REDRAW)) { |
428 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT)) |
429 |
|
|
goto slow; |
430 |
|
|
goto paint; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
/* |
434 |
|
|
* 7: Cursor movements (current screen only). |
435 |
|
|
*/ |
436 |
|
|
if (!LF_ISSET(UPDATE_CURSOR)) |
437 |
|
|
goto number; |
438 |
|
|
|
439 |
|
|
/* |
440 |
|
|
* Decide cursor position. If the line has changed, the cursor has |
441 |
|
|
* moved over a tab, or don't know where the cursor was, reparse the |
442 |
|
|
* line. Otherwise, we've just moved over fixed-width characters, |
443 |
|
|
* and can calculate the left/right scrolling and cursor movement |
444 |
|
|
* without reparsing the line. Note that we don't know which (if any) |
445 |
|
|
* of the characters between the old and new cursor positions changed. |
446 |
|
|
* |
447 |
|
|
* XXX |
448 |
|
|
* With some work, it should be possible to handle tabs quickly, at |
449 |
|
|
* least in obvious situations, like moving right and encountering |
450 |
|
|
* a tab, without reparsing the whole line. |
451 |
|
|
* |
452 |
|
|
* If the line we're working with has changed, reread it.. |
453 |
|
|
*/ |
454 |
|
|
if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO) |
455 |
|
|
goto slow; |
456 |
|
|
|
457 |
|
|
/* Otherwise, if nothing's changed, ignore the cursor. */ |
458 |
|
|
if (CNO == OCNO) |
459 |
|
|
goto fast; |
460 |
|
|
|
461 |
|
|
/* |
462 |
|
|
* Get the current line. If this fails, we either have an empty |
463 |
|
|
* file and can just repaint, or there's a real problem. This |
464 |
|
|
* isn't a performance issue because there aren't any ways to get |
465 |
|
|
* here repeatedly. |
466 |
|
|
*/ |
467 |
|
|
if (db_eget(sp, LNO, &p, &len, &isempty)) { |
468 |
|
|
if (isempty) |
469 |
|
|
goto slow; |
470 |
|
|
return (1); |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
#ifdef DEBUG |
474 |
|
|
/* Sanity checking. */ |
475 |
|
|
if (CNO >= len && len != 0) { |
476 |
|
|
msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)", |
477 |
|
|
basename(__FILE__), __LINE__, CNO, len); |
478 |
|
|
return (1); |
479 |
|
|
} |
480 |
|
|
#endif |
481 |
|
|
/* |
482 |
|
|
* The basic scheme here is to look at the characters in between |
483 |
|
|
* the old and new positions and decide how big they are on the |
484 |
|
|
* screen, and therefore, how many screen positions to move. |
485 |
|
|
*/ |
486 |
|
|
if (CNO < OCNO) { |
487 |
|
|
/* |
488 |
|
|
* 7a: Cursor moved left. |
489 |
|
|
* |
490 |
|
|
* Point to the old character. The old cursor position can |
491 |
|
|
* be past EOL if, for example, we just deleted the rest of |
492 |
|
|
* the line. In this case, since we don't know the width of |
493 |
|
|
* the characters we traversed, we have to do it slowly. |
494 |
|
|
*/ |
495 |
|
|
p += OCNO; |
496 |
|
|
cnt = (OCNO - CNO) + 1; |
497 |
|
|
if (OCNO >= len) |
498 |
|
|
goto slow; |
499 |
|
|
|
500 |
|
|
/* |
501 |
|
|
* Quick sanity check -- it's hard to figure out exactly when |
502 |
|
|
* we cross a screen boundary as we do in the cursor right |
503 |
|
|
* movement. If cnt is so large that we're going to cross the |
504 |
|
|
* boundary no matter what, stop now. |
505 |
|
|
*/ |
506 |
|
|
if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt) |
507 |
|
|
goto slow; |
508 |
|
|
|
509 |
|
|
/* |
510 |
|
|
* Count up the widths of the characters. If it's a tab |
511 |
|
|
* character, go do it the slow way. |
512 |
|
|
*/ |
513 |
|
|
for (cwtotal = 0; cnt--; cwtotal += KEY_LEN(sp, ch)) |
514 |
|
|
if ((ch = *(u_char *)p--) == '\t') |
515 |
|
|
goto slow; |
516 |
|
|
|
517 |
|
|
/* |
518 |
|
|
* Decrement the screen cursor by the total width of the |
519 |
|
|
* characters minus 1. |
520 |
|
|
*/ |
521 |
|
|
cwtotal -= 1; |
522 |
|
|
|
523 |
|
|
/* |
524 |
|
|
* If we're moving left, and there's a wide character in the |
525 |
|
|
* current position, go to the end of the character. |
526 |
|
|
*/ |
527 |
|
|
if (KEY_LEN(sp, ch) > 1) |
528 |
|
|
cwtotal -= KEY_LEN(sp, ch) - 1; |
529 |
|
|
|
530 |
|
|
/* |
531 |
|
|
* If the new column moved us off of the current logical line, |
532 |
|
|
* calculate a new one. If doing leftright scrolling, we've |
533 |
|
|
* moved off of the current screen, as well. |
534 |
|
|
*/ |
535 |
|
|
if (SCNO < cwtotal) |
536 |
|
|
goto slow; |
537 |
|
|
SCNO -= cwtotal; |
538 |
|
|
} else { |
539 |
|
|
/* |
540 |
|
|
* 7b: Cursor moved right. |
541 |
|
|
* |
542 |
|
|
* Point to the first character to the right. |
543 |
|
|
*/ |
544 |
|
|
p += OCNO + 1; |
545 |
|
|
cnt = CNO - OCNO; |
546 |
|
|
|
547 |
|
|
/* |
548 |
|
|
* Count up the widths of the characters. If it's a tab |
549 |
|
|
* character, go do it the slow way. If we cross a |
550 |
|
|
* screen boundary, we can quit. |
551 |
|
|
*/ |
552 |
|
|
for (cwtotal = SCNO; cnt--;) { |
553 |
|
|
if ((ch = *(u_char *)p++) == '\t') |
554 |
|
|
goto slow; |
555 |
|
|
if ((cwtotal += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp)) |
556 |
|
|
break; |
557 |
|
|
} |
558 |
|
|
|
559 |
|
|
/* |
560 |
|
|
* Increment the screen cursor by the total width of the |
561 |
|
|
* characters. |
562 |
|
|
*/ |
563 |
|
|
SCNO = cwtotal; |
564 |
|
|
|
565 |
|
|
/* See screen change comment in section 6a. */ |
566 |
|
|
if (SCNO >= SCREEN_COLS(sp)) |
567 |
|
|
goto slow; |
568 |
|
|
} |
569 |
|
|
|
570 |
|
|
/* |
571 |
|
|
* 7c: Fast cursor update. |
572 |
|
|
* |
573 |
|
|
* We have the current column, retrieve the current row. |
574 |
|
|
*/ |
575 |
|
|
fast: (void)gp->scr_cursor(sp, &y, ¬used); |
576 |
|
|
goto done_cursor; |
577 |
|
|
|
578 |
|
|
/* |
579 |
|
|
* 7d: Slow cursor update. |
580 |
|
|
* |
581 |
|
|
* Walk through the map and find the current line. |
582 |
|
|
*/ |
583 |
|
|
slow: for (smp = HMAP; smp->lno != LNO; ++smp); |
584 |
|
|
|
585 |
|
|
/* |
586 |
|
|
* 7e: Leftright scrolling adjustment. |
587 |
|
|
* |
588 |
|
|
* If doing left-right scrolling and the cursor movement has changed |
589 |
|
|
* the displayed screen, scroll the screen left or right, unless we're |
590 |
|
|
* updating the info line in which case we just scroll that one line. |
591 |
|
|
* We adjust the offset up or down until we have a window that covers |
592 |
|
|
* the current column, making sure that we adjust differently for the |
593 |
|
|
* first screen as compared to subsequent ones. |
594 |
|
|
*/ |
595 |
|
|
if (O_ISSET(sp, O_LEFTRIGHT)) { |
596 |
|
|
/* |
597 |
|
|
* Get the screen column for this character, and correct |
598 |
|
|
* for the number option offset. |
599 |
|
|
*/ |
600 |
|
|
cnt = vs_columns(sp, NULL, LNO, &CNO, NULL); |
601 |
|
|
if (O_ISSET(sp, O_NUMBER)) |
602 |
|
|
cnt -= O_NUMBER_LENGTH; |
603 |
|
|
|
604 |
|
|
/* Adjust the window towards the beginning of the line. */ |
605 |
|
|
off = smp->coff; |
606 |
|
|
if (off >= cnt) { |
607 |
|
|
do { |
608 |
|
|
if (off >= O_VAL(sp, O_SIDESCROLL)) |
609 |
|
|
off -= O_VAL(sp, O_SIDESCROLL); |
610 |
|
|
else { |
611 |
|
|
off = 0; |
612 |
|
|
break; |
613 |
|
|
} |
614 |
|
|
} while (off >= cnt); |
615 |
|
|
goto shifted; |
616 |
|
|
} |
617 |
|
|
|
618 |
|
|
/* Adjust the window towards the end of the line. */ |
619 |
|
|
if ((off == 0 && off + SCREEN_COLS(sp) < cnt) || |
620 |
|
|
(off != 0 && off + sp->cols < cnt)) { |
621 |
|
|
do { |
622 |
|
|
off += O_VAL(sp, O_SIDESCROLL); |
623 |
|
|
} while (off + sp->cols < cnt); |
624 |
|
|
|
625 |
|
|
shifted: /* Fill in screen map with the new offset. */ |
626 |
|
|
if (F_ISSET(sp, SC_TINPUT_INFO)) |
627 |
|
|
smp->coff = off; |
628 |
|
|
else { |
629 |
|
|
for (smp = HMAP; smp <= TMAP; ++smp) |
630 |
|
|
smp->coff = off; |
631 |
|
|
leftright_warp = 1; |
632 |
|
|
} |
633 |
|
|
goto paint; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
/* |
637 |
|
|
* We may have jumped here to adjust a leftright screen because |
638 |
|
|
* redraw was set. If so, we have to paint the entire screen. |
639 |
|
|
*/ |
640 |
|
|
if (F_ISSET(sp, SC_SCR_REDRAW)) |
641 |
|
|
goto paint; |
642 |
|
|
} |
643 |
|
|
|
644 |
|
|
/* |
645 |
|
|
* Update the screen lines for this particular file line until we |
646 |
|
|
* have a new screen cursor position. |
647 |
|
|
*/ |
648 |
|
|
for (y = -1, |
649 |
|
|
vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) { |
650 |
|
|
if (vs_line(sp, smp, &y, &SCNO)) |
651 |
|
|
return (1); |
652 |
|
|
if (y != -1) { |
653 |
|
|
vip->sc_smap = smp; |
654 |
|
|
break; |
655 |
|
|
} |
656 |
|
|
} |
657 |
|
|
goto done_cursor; |
658 |
|
|
|
659 |
|
|
/* |
660 |
|
|
* 8: Repaint the entire screen. |
661 |
|
|
* |
662 |
|
|
* Lost big, do what you have to do. We flush the cache, since |
663 |
|
|
* SC_SCR_REDRAW gets set when the screen isn't worth fixing, and |
664 |
|
|
* it's simpler to repaint. So, don't trust anything that we |
665 |
|
|
* think we know about it. |
666 |
|
|
*/ |
667 |
|
|
paint: for (smp = HMAP; smp <= TMAP; ++smp) |
668 |
|
|
SMAP_FLUSH(smp); |
669 |
|
|
for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) { |
670 |
|
|
if (vs_line(sp, smp, &y, &SCNO)) |
671 |
|
|
return (1); |
672 |
|
|
if (y != -1 && vip->sc_smap == NULL) |
673 |
|
|
vip->sc_smap = smp; |
674 |
|
|
} |
675 |
|
|
/* |
676 |
|
|
* If it's a small screen and we're redrawing, clear the unused lines, |
677 |
|
|
* ex may have overwritten them. |
678 |
|
|
*/ |
679 |
|
|
if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp)) |
680 |
|
|
for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) { |
681 |
|
|
(void)gp->scr_move(sp, cnt, 0); |
682 |
|
|
(void)gp->scr_clrtoeol(sp); |
683 |
|
|
} |
684 |
|
|
|
685 |
|
|
didpaint = 1; |
686 |
|
|
|
687 |
|
|
done_cursor: |
688 |
|
|
/* |
689 |
|
|
* Sanity checking. When the repainting code messes up, the usual |
690 |
|
|
* result is we don't repaint the cursor and so sc_smap will be |
691 |
|
|
* NULL. If we're debugging, die, otherwise restart from scratch. |
692 |
|
|
*/ |
693 |
|
|
#ifdef DEBUG |
694 |
|
|
if (vip->sc_smap == NULL) |
695 |
|
|
abort(); |
696 |
|
|
#else |
697 |
|
|
if (vip->sc_smap == NULL) { |
698 |
|
|
if (F_ISSET(sp, SC_SCR_REFORMAT)) |
699 |
|
|
return (0); |
700 |
|
|
F_SET(sp, SC_SCR_REFORMAT); |
701 |
|
|
return (vs_paint(sp, flags)); |
702 |
|
|
} |
703 |
|
|
#endif |
704 |
|
|
|
705 |
|
|
/* |
706 |
|
|
* 9: Set the remembered cursor values. |
707 |
|
|
*/ |
708 |
|
|
OCNO = CNO; |
709 |
|
|
OLNO = LNO; |
710 |
|
|
|
711 |
|
|
/* |
712 |
|
|
* 10: Repaint the line numbers. |
713 |
|
|
* |
714 |
|
|
* If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we |
715 |
|
|
* didn't repaint the screen, repaint all of the line numbers, |
716 |
|
|
* they've changed. |
717 |
|
|
*/ |
718 |
|
|
number: if (O_ISSET(sp, O_NUMBER) && |
719 |
|
|
F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp)) |
720 |
|
|
return (1); |
721 |
|
|
|
722 |
|
|
/* |
723 |
|
|
* 11: Update the mode line, position the cursor, and flush changes. |
724 |
|
|
* |
725 |
|
|
* If we warped the screen, we have to refresh everything. |
726 |
|
|
*/ |
727 |
|
|
if (leftright_warp) |
728 |
|
|
LF_SET(UPDATE_CURSOR | UPDATE_SCREEN); |
729 |
|
|
|
730 |
|
|
if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) && |
731 |
|
|
!F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO)) |
732 |
|
|
vs_modeline(sp); |
733 |
|
|
|
734 |
|
|
if (LF_ISSET(UPDATE_CURSOR)) { |
735 |
|
|
(void)gp->scr_move(sp, y, SCNO); |
736 |
|
|
|
737 |
|
|
/* |
738 |
|
|
* XXX |
739 |
|
|
* If the screen shifted, we recalculate the "most favorite" |
740 |
|
|
* cursor position. Vi won't know that we've warped the |
741 |
|
|
* screen, so it's going to have a wrong idea about where the |
742 |
|
|
* cursor should be. This is vi's problem, and fixing it here |
743 |
|
|
* is a gross layering violation. |
744 |
|
|
*/ |
745 |
|
|
if (leftright_warp) |
746 |
|
|
(void)vs_column(sp, &sp->rcm); |
747 |
|
|
} |
748 |
|
|
|
749 |
|
|
if (LF_ISSET(UPDATE_SCREEN)) |
750 |
|
|
(void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT)); |
751 |
|
|
|
752 |
|
|
/* 12: Clear the flags that are handled by this routine. */ |
753 |
|
|
F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP); |
754 |
|
|
F_CLR(vip, VIP_CUR_INVALID | |
755 |
|
|
VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE); |
756 |
|
|
|
757 |
|
|
return (0); |
758 |
|
|
|
759 |
|
|
#undef LNO |
760 |
|
|
#undef OLNO |
761 |
|
|
#undef CNO |
762 |
|
|
#undef OCNO |
763 |
|
|
#undef SCNO |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
/* |
767 |
|
|
* vs_modeline -- |
768 |
|
|
* Update the mode line. |
769 |
|
|
*/ |
770 |
|
|
static void |
771 |
|
|
vs_modeline(SCR *sp) |
772 |
|
|
{ |
773 |
|
|
static char * const modes[] = { |
774 |
|
|
"Append", /* SM_APPEND */ |
775 |
|
|
"Change", /* SM_CHANGE */ |
776 |
|
|
"Command", /* SM_COMMAND */ |
777 |
|
|
"Insert", /* SM_INSERT */ |
778 |
|
|
"Replace", /* SM_REPLACE */ |
779 |
|
|
}; |
780 |
|
|
GS *gp; |
781 |
|
|
size_t cols, curcol, curlen, endpoint, len, midpoint; |
782 |
|
|
const char *t = NULL; |
783 |
|
|
int ellipsis; |
784 |
|
|
char *p, buf[20]; |
785 |
|
|
|
786 |
|
|
/* |
787 |
|
|
* It's possible that this routine will be called after sp->frp |
788 |
|
|
* has been set to NULL by file_end(). We return immediately |
789 |
|
|
* to avoid a SEGV. |
790 |
|
|
*/ |
791 |
|
|
if (sp->frp == NULL) |
792 |
|
|
return; |
793 |
|
|
|
794 |
|
|
gp = sp->gp; |
795 |
|
|
|
796 |
|
|
/* |
797 |
|
|
* We put down the file name, the ruler, the mode and the dirty flag. |
798 |
|
|
* If there's not enough room, there's not enough room, we don't play |
799 |
|
|
* any special games. We try to put the ruler in the middle and the |
800 |
|
|
* mode and dirty flag at the end. |
801 |
|
|
* |
802 |
|
|
* !!! |
803 |
|
|
* Leave the last character blank, in case it's a really dumb terminal |
804 |
|
|
* with hardware scroll. Second, don't paint the last character in the |
805 |
|
|
* screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you. |
806 |
|
|
* |
807 |
|
|
* Move to the last line on the screen. |
808 |
|
|
*/ |
809 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), 0); |
810 |
|
|
|
811 |
|
|
/* If more than one screen in the display, show the file name. */ |
812 |
|
|
curlen = 0; |
813 |
|
|
if (IS_SPLIT(sp)) { |
814 |
|
|
for (p = sp->frp->name; *p != '\0'; ++p); |
815 |
|
|
for (ellipsis = 0, cols = sp->cols / 2; --p > sp->frp->name;) { |
816 |
|
|
if (*p == '/') { |
817 |
|
|
++p; |
818 |
|
|
break; |
819 |
|
|
} |
820 |
|
|
if ((curlen += KEY_LEN(sp, *p)) > cols) { |
821 |
|
|
ellipsis = 3; |
822 |
|
|
curlen += |
823 |
|
|
KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' '); |
824 |
|
|
while (curlen > cols) { |
825 |
|
|
++p; |
826 |
|
|
curlen -= KEY_LEN(sp, *p); |
827 |
|
|
} |
828 |
|
|
break; |
829 |
|
|
} |
830 |
|
|
} |
831 |
|
|
if (ellipsis) { |
832 |
|
|
while (ellipsis--) |
833 |
|
|
(void)gp->scr_addstr(sp, |
834 |
|
|
KEY_NAME(sp, '.'), KEY_LEN(sp, '.')); |
835 |
|
|
(void)gp->scr_addstr(sp, |
836 |
|
|
KEY_NAME(sp, ' '), KEY_LEN(sp, ' ')); |
837 |
|
|
} |
838 |
|
|
for (; *p != '\0'; ++p) |
839 |
|
|
(void)gp->scr_addstr(sp, |
840 |
|
|
KEY_NAME(sp, *p), KEY_LEN(sp, *p)); |
841 |
|
|
} |
842 |
|
|
|
843 |
|
|
/* Clear the rest of the line. */ |
844 |
|
|
(void)gp->scr_clrtoeol(sp); |
845 |
|
|
|
846 |
|
|
/* |
847 |
|
|
* Display the ruler. If we're not at the midpoint yet, move there. |
848 |
|
|
* Otherwise, add in two extra spaces. |
849 |
|
|
* |
850 |
|
|
* Adjust the current column for the fact that the editor uses it as |
851 |
|
|
* a zero-based number. |
852 |
|
|
* |
853 |
|
|
* XXX |
854 |
|
|
* Assume that numbers, commas, and spaces only take up a single |
855 |
|
|
* column on the screen. |
856 |
|
|
*/ |
857 |
|
|
cols = sp->cols - 1; |
858 |
|
|
if (O_ISSET(sp, O_RULER)) { |
859 |
|
|
vs_column(sp, &curcol); |
860 |
|
|
len = snprintf(buf, sizeof(buf), "%lu,%zu", |
861 |
|
|
(ulong)sp->lno, curcol + 1); |
862 |
|
|
|
863 |
|
|
midpoint = (cols - ((len + 1) / 2)) / 2; |
864 |
|
|
if (curlen < midpoint) { |
865 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), midpoint); |
866 |
|
|
curlen += len; |
867 |
|
|
} else if (curlen + 2 + len < cols) { |
868 |
|
|
(void)gp->scr_addstr(sp, " ", 2); |
869 |
|
|
curlen += 2 + len; |
870 |
|
|
} |
871 |
|
|
(void)gp->scr_addstr(sp, buf, len); |
872 |
|
|
} |
873 |
|
|
|
874 |
|
|
/* |
875 |
|
|
* Display the mode and the modified flag, as close to the end of the |
876 |
|
|
* line as possible, but guaranteeing at least two spaces between the |
877 |
|
|
* ruler and the modified flag. |
878 |
|
|
*/ |
879 |
|
|
#define MODESIZE 9 |
880 |
|
|
endpoint = cols; |
881 |
|
|
if (O_ISSET(sp, O_SHOWMODE)) { |
882 |
|
|
if (F_ISSET(sp->ep, F_MODIFIED)) |
883 |
|
|
--endpoint; |
884 |
|
|
t = modes[sp->showmode]; |
885 |
|
|
endpoint -= (len = strlen(t)); |
886 |
|
|
} |
887 |
|
|
|
888 |
|
|
if (endpoint > curlen + 2) { |
889 |
|
|
(void)gp->scr_move(sp, LASTLINE(sp), endpoint); |
890 |
|
|
if (O_ISSET(sp, O_SHOWMODE)) { |
891 |
|
|
if (F_ISSET(sp->ep, F_MODIFIED)) |
892 |
|
|
(void)gp->scr_addstr(sp, |
893 |
|
|
KEY_NAME(sp, '*'), KEY_LEN(sp, '*')); |
894 |
|
|
(void)gp->scr_addstr(sp, t, len); |
895 |
|
|
} |
896 |
|
|
} |
897 |
|
|
} |