source: trunk/third/nvi/vi/v_scroll.c @ 14302

Revision 14302, 11.9 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *      The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *      Keith Bostic.  All rights reserved.
6 *
7 * See the LICENSE file for redistribution information.
8 */
9
10#include "config.h"
11
12#ifndef lint
13static const char sccsid[] = "@(#)v_scroll.c    10.9 (Berkeley) 4/27/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <errno.h>
22#include <limits.h>
23#include <stdio.h>
24
25#include "../common/common.h"
26#include "vi.h"
27
28static void goto_adjust __P((VICMD *));
29
30/*
31 * The historic vi had a problem in that all movements were by physical
32 * lines, not by logical, or screen lines.  Arguments can be made that this
33 * is the right thing to do.  For example, single line movements, such as
34 * 'j' or 'k', should probably work on physical lines.  Commands like "dj",
35 * or "j.", where '.' is a change command, make more sense for physical lines
36 * than they do for logical lines.
37 *
38 * These arguments, however, don't apply to scrolling commands like ^D and
39 * ^F -- if the window is fairly small, using physical lines can result in
40 * a half-page scroll repainting the entire screen, which is not what the
41 * user wanted.  Second, if the line is larger than the screen, using physical
42 * lines can make it impossible to display parts of the line -- there aren't
43 * any commands that don't display the beginning of the line in historic vi,
44 * and if both the beginning and end of the line can't be on the screen at
45 * the same time, you lose.  This is even worse in the case of the H, L, and
46 * M commands -- for large lines, they may all refer to the same line and
47 * will result in no movement at all.
48 *
49 * Another issue is that page and half-page scrolling commands historically
50 * moved to the first non-blank character in the new line.  If the line is
51 * approximately the same size as the screen, this loses because the cursor
52 * before and after a ^D, may refer to the same location on the screen.  In
53 * this implementation, scrolling commands set the cursor to the first non-
54 * blank character if the line changes because of the scroll.  Otherwise,
55 * the cursor is left alone.
56 *
57 * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the
58 * cursor positioning commands (H, L, M) commands using logical lines, not
59 * physical.
60 */
61
62/*
63 * v_lgoto -- [count]G
64 *      Go to first non-blank character of the line count, the last line
65 *      of the file by default.
66 *
67 * PUBLIC: int v_lgoto __P((SCR *, VICMD *));
68 */
69int
70v_lgoto(sp, vp)
71        SCR *sp;
72        VICMD *vp;
73{
74        recno_t nlines;
75
76        if (F_ISSET(vp, VC_C1SET)) {
77                if (!db_exist(sp, vp->count)) {
78                        /*
79                         * !!!
80                         * Historically, 1G was legal in an empty file.
81                         */
82                        if (vp->count == 1) {
83                                if (db_last(sp, &nlines))
84                                        return (1);
85                                if (nlines == 0)
86                                        return (0);
87                        }
88                        v_eof(sp, &vp->m_start);
89                        return (1);
90                }
91                vp->m_stop.lno = vp->count;
92        } else {
93                if (db_last(sp, &nlines))
94                        return (1);
95                vp->m_stop.lno = nlines ? nlines : 1;
96        }
97        goto_adjust(vp);
98        return (0);
99}
100
101/*
102 * v_home -- [count]H
103 *      Move to the first non-blank character of the logical line
104 *      count - 1 from the top of the screen, 0 by default.
105 *
106 * PUBLIC: int v_home __P((SCR *, VICMD *));
107 */
108int
109v_home(sp, vp)
110        SCR *sp;
111        VICMD *vp;
112{
113        if (vs_sm_position(sp, &vp->m_stop,
114            F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP))
115                return (1);
116        goto_adjust(vp);
117        return (0);
118}
119
120/*
121 * v_middle -- M
122 *      Move to the first non-blank character of the logical line
123 *      in the middle of the screen.
124 *
125 * PUBLIC: int v_middle __P((SCR *, VICMD *));
126 */
127int
128v_middle(sp, vp)
129        SCR *sp;
130        VICMD *vp;
131{
132        /*
133         * Yielding to none in our quest for compatibility with every
134         * historical blemish of vi, no matter how strange it might be,
135         * we permit the user to enter a count and then ignore it.
136         */
137        if (vs_sm_position(sp, &vp->m_stop, 0, P_MIDDLE))
138                return (1);
139        goto_adjust(vp);
140        return (0);
141}
142
143/*
144 * v_bottom -- [count]L
145 *      Move to the first non-blank character of the logical line
146 *      count - 1 from the bottom of the screen, 0 by default.
147 *
148 * PUBLIC: int v_bottom __P((SCR *, VICMD *));
149 */
150int
151v_bottom(sp, vp)
152        SCR *sp;
153        VICMD *vp;
154{
155        if (vs_sm_position(sp, &vp->m_stop,
156            F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM))
157                return (1);
158        goto_adjust(vp);
159        return (0);
160}
161
162static void
163goto_adjust(vp)
164        VICMD *vp;
165{
166        /* Guess that it's the end of the range. */
167        vp->m_final = vp->m_stop;
168
169        /*
170         * Non-motion commands move the cursor to the end of the range, and
171         * then to the NEXT nonblank of the line.  Historic vi always moved
172         * to the first nonblank in the line; since the H, M, and L commands
173         * are logical motions in this implementation, we do the next nonblank
174         * so that it looks approximately the same to the user.  To make this
175         * happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table.
176         *
177         * If it's a motion, it's more complicated.  The best possible solution
178         * is probably to display the first nonblank of the line the cursor
179         * will eventually rest on.  This is tricky, particularly given that if
180         * the associated command is a delete, we don't yet know what line that
181         * will be.  So, we clear the VM_RCM_SETNNB flag, and set the first
182         * nonblank flag (VM_RCM_SETFNB).  Note, if the lines are sufficiently
183         * long, this can cause the cursor to warp out of the screen.  It's too
184         * hard to fix.
185         *
186         * XXX
187         * The G command is always first nonblank, so it's okay to reset it.
188         */
189        if (ISMOTION(vp)) {
190                F_CLR(vp, VM_RCM_MASK);
191                F_SET(vp, VM_RCM_SETFNB);
192        } else
193                return;
194
195        /*
196         * If moving backward in the file, delete and yank move to the end
197         * of the range, unless the line didn't change, in which case yank
198         * doesn't move.  If moving forward in the file, delete and yank
199         * stay at the start of the range.  Ignore others.
200         */
201        if (vp->m_stop.lno < vp->m_start.lno ||
202            vp->m_stop.lno == vp->m_start.lno &&
203            vp->m_stop.cno < vp->m_start.cno) {
204                if (ISCMD(vp->rkp, 'y') && vp->m_stop.lno == vp->m_start.lno)
205                        vp->m_final = vp->m_start;
206        } else
207                vp->m_final = vp->m_start;
208}
209
210/*
211 * v_up -- [count]^P, [count]k, [count]-
212 *      Move up by lines.
213 *
214 * PUBLIC: int v_up __P((SCR *, VICMD *));
215 */
216int
217v_up(sp, vp)
218        SCR *sp;
219        VICMD *vp;
220{
221        recno_t lno;
222
223        lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
224        if (vp->m_start.lno <= lno) {
225                v_sof(sp, &vp->m_start);
226                return (1);
227        }
228        vp->m_stop.lno = vp->m_start.lno - lno;
229        vp->m_final = vp->m_stop;
230        return (0);
231}
232
233/*
234 * v_cr -- [count]^M
235 *      In a script window, send the line to the shell.
236 *      In a regular window, move down by lines.
237 *
238 * PUBLIC: int v_cr __P((SCR *, VICMD *));
239 */
240int
241v_cr(sp, vp)
242        SCR *sp;
243        VICMD *vp;
244{
245        /* If it's a colon command-line edit window, it's an ex command. */
246        if (F_ISSET(sp, SC_COMEDIT))
247                return (v_ecl_exec(sp));
248
249        /* If it's a script window, exec the line. */
250        if (F_ISSET(sp, SC_SCRIPT))
251                return (sscr_exec(sp, vp->m_start.lno));
252
253        /* Otherwise, it's the same as v_down(). */
254        return (v_down(sp, vp));
255}
256
257/*
258 * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
259 *      Move down by lines.
260 *
261 * PUBLIC: int v_down __P((SCR *, VICMD *));
262 */
263int
264v_down(sp, vp)
265        SCR *sp;
266        VICMD *vp;
267{
268        recno_t lno;
269
270        lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
271        if (!db_exist(sp, lno)) {
272                v_eof(sp, &vp->m_start);
273                return (1);
274        }
275        vp->m_stop.lno = lno;
276        vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
277        return (0);
278}
279
280/*
281 * v_hpageup -- [count]^U
282 *      Page up half screens.
283 *
284 * PUBLIC: int v_hpageup __P((SCR *, VICMD *));
285 */
286int
287v_hpageup(sp, vp)
288        SCR *sp;
289        VICMD *vp;
290{
291        /*
292         * Half screens always succeed unless already at SOF.
293         *
294         * !!!
295         * Half screens set the scroll value, even if the command
296         * ultimately failed, in historic vi.  Probably a don't care.
297         */
298        if (F_ISSET(vp, VC_C1SET))
299                sp->defscroll = vp->count;
300        if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_U))
301                return (1);
302        vp->m_final = vp->m_stop;
303        return (0);
304}
305
306/*
307 * v_hpagedown -- [count]^D
308 *      Page down half screens.
309 *
310 * PUBLIC: int v_hpagedown __P((SCR *, VICMD *));
311 */
312int
313v_hpagedown(sp, vp)
314        SCR *sp;
315        VICMD *vp;
316{
317        /*
318         * Half screens always succeed unless already at EOF.
319         *
320         * !!!
321         * Half screens set the scroll value, even if the command
322         * ultimately failed, in historic vi.  Probably a don't care.
323         */
324        if (F_ISSET(vp, VC_C1SET))
325                sp->defscroll = vp->count;
326        if (vs_sm_scroll(sp, &vp->m_stop, sp->defscroll, CNTRL_D))
327                return (1);
328        vp->m_final = vp->m_stop;
329        return (0);
330}
331
332/*
333 * v_pagedown -- [count]^F
334 *      Page down full screens.
335 * !!!
336 * Historic vi did not move to the EOF if the screen couldn't move, i.e.
337 * if EOF was already displayed on the screen.  This implementation does
338 * move to EOF in that case, making ^F more like the the historic ^D.
339 *
340 * PUBLIC: int v_pagedown __P((SCR *, VICMD *));
341 */
342int
343v_pagedown(sp, vp)
344        SCR *sp;
345        VICMD *vp;
346{
347        recno_t offset;
348
349        /*
350         * !!!
351         * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
352         *
353         *      top_line = top_line + count * (window - 2);
354         *
355         * which was historically wrong.  The correct one is:
356         *
357         *      top_line = top_line + count * window - 2;
358         *
359         * i.e. the two line "overlap" was only subtracted once.  Which
360         * makes no sense, but then again, an overlap makes no sense for
361         * any screen but the "next" one anyway.  We do it the historical
362         * way as there's no good reason to change it.
363         *
364         * If the screen has been split, use the smaller of the current
365         * window size and the window option value.
366         *
367         * It possible for this calculation to be less than 1; move at
368         * least one line.
369         */
370        offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
371            MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
372        offset = offset <= 2 ? 1 : offset - 2;
373        if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_F))
374                return (1);
375        vp->m_final = vp->m_stop;
376        return (0);
377}
378
379/*
380 * v_pageup -- [count]^B
381 *      Page up full screens.
382 *
383 * !!!
384 * Historic vi did not move to the SOF if the screen couldn't move, i.e.
385 * if SOF was already displayed on the screen.  This implementation does
386 * move to SOF in that case, making ^B more like the the historic ^U.
387 *
388 * PUBLIC: int v_pageup __P((SCR *, VICMD *));
389 */
390int
391v_pageup(sp, vp)
392        SCR *sp;
393        VICMD *vp;
394{
395        recno_t offset;
396
397        /*
398         * !!!
399         * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
400         *
401         *      top_line = top_line - count * (window - 2);
402         *
403         * which was historically wrong.  The correct one is:
404         *
405         *      top_line = (top_line - count * window) + 2;
406         *
407         * A simpler expression is that, as with ^F, we scroll exactly:
408         *
409         *      count * window - 2
410         *
411         * lines.
412         *
413         * Bizarre.  As with ^F, an overlap makes no sense for anything
414         * but the first screen.  We do it the historical way as there's
415         * no good reason to change it.
416         *
417         * If the screen has been split, use the smaller of the current
418         * window size and the window option value.
419         *
420         * It possible for this calculation to be less than 1; move at
421         * least one line.
422         */
423        offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) * (IS_SPLIT(sp) ?
424            MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW));
425        offset = offset <= 2 ? 1 : offset - 2;
426        if (vs_sm_scroll(sp, &vp->m_stop, offset, CNTRL_B))
427                return (1);
428        vp->m_final = vp->m_stop;
429        return (0);
430}
431
432/*
433 * v_lineup -- [count]^Y
434 *      Page up by lines.
435 *
436 * PUBLIC: int v_lineup __P((SCR *, VICMD *));
437 */
438int
439v_lineup(sp, vp)
440        SCR *sp;
441        VICMD *vp;
442{
443        /*
444         * The cursor moves down, staying with its original line, unless it
445         * reaches the bottom of the screen.
446         */
447        if (vs_sm_scroll(sp,
448            &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_Y))
449                return (1);
450        vp->m_final = vp->m_stop;
451        return (0);
452}
453
454/*
455 * v_linedown -- [count]^E
456 *      Page down by lines.
457 *
458 * PUBLIC: int v_linedown __P((SCR *, VICMD *));
459 */
460int
461v_linedown(sp, vp)
462        SCR *sp;
463        VICMD *vp;
464{
465        /*
466         * The cursor moves up, staying with its original line, unless it
467         * reaches the top of the screen.
468         */
469        if (vs_sm_scroll(sp,
470            &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_E))
471                return (1);
472        vp->m_final = vp->m_stop;
473        return (0);
474}
Note: See TracBrowser for help on using the repository browser.