3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: vs_smap.c,v 8.29 1993/11/30 09:27:44 bostic Exp $ (Berkeley) $Date: 1993/11/30 09:27:44 $";
12 #include <sys/types.h>
20 #include "svi_screen.h"
22 static int svi_deleteln
__P((SCR
*, int));
23 static int svi_insertln
__P((SCR
*, int));
24 static int svi_sm_delete
__P((SCR
*, EXF
*, recno_t
));
25 static int svi_sm_insert
__P((SCR
*, EXF
*, recno_t
));
26 static int svi_sm_reset
__P((SCR
*, EXF
*, recno_t
));
30 * Make a change to the screen.
33 svi_change(sp
, ep
, lno
, op
)
42 /* Appending is the same as inserting, if the line is incremented. */
43 if (op
== LINE_APPEND
) {
48 /* Ignore the change if the line is after the map. */
53 * If the line is before the map, and it's a decrement, decrement
54 * the map. If it's an increment, increment the map. Otherwise,
57 if (lno
< HMAP
->lno
) {
63 for (p
= HMAP
; p
<= TMAP
; ++p
)
67 F_SET(sp
, S_RENUMBER
);
70 for (p
= HMAP
; p
<= TMAP
; ++p
)
74 F_SET(sp
, S_RENUMBER
);
82 F_SET(SVP(sp
), SVI_SCREENDIRTY
);
84 /* Flush cached information from svi_screens(). */
85 SVP(sp
)->ss_lno
= OOBLNO
;
87 /* Invalidate the cursor, if it's on this line. */
89 F_SET(SVP(sp
), SVI_CUR_INVALID
);
91 getyx(stdscr
, oldy
, oldx
);
95 if (svi_sm_delete(sp
, ep
, lno
))
97 F_SET(sp
, S_RENUMBER
);
100 if (svi_sm_insert(sp
, ep
, lno
))
102 F_SET(sp
, S_RENUMBER
);
105 if (svi_sm_reset(sp
, ep
, lno
))
112 MOVEA(sp
, oldy
, oldx
);
119 * Fill in the screen map, placing the specified line at the
120 * right position. There isn't any way to tell if an SMAP
121 * entry has been filled in, so this routine had better be
122 * called with P_FILL set before anything else is done.
125 * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
126 * slot is already filled in, P_BOTTOM means that the TMAP slot is
127 * already filled in, and we just finish up the job.
130 svi_sm_fill(sp
, ep
, lno
, pos
)
138 /* Flush all cached information from the SMAP. */
139 for (p
= HMAP
; p
<= TMAP
; ++p
)
147 /* See if less than half a screen from the top. */
148 if (svi_sm_nlines(sp
, ep
,
149 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
154 /* See if less than half a screen from the bottom. */
155 if (file_lline(sp
, ep
, &tmp
.lno
))
157 tmp
.off
= svi_screens(sp
, ep
, tmp
.lno
, NULL
);
158 if (svi_sm_nlines(sp
, ep
,
159 &tmp
, lno
, HALFTEXT(sp
)) <= HALFTEXT(sp
)) {
167 top
: HMAP
->lno
= lno
;
170 /* If we fail, just punt. */
171 for (p
= HMAP
; p
< TMAP
; ++p
)
172 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
176 /* If we fail, guess that the file is too small. */
177 middle
: p
= HMAP
+ (TMAP
- HMAP
) / 2;
178 for (p
->lno
= lno
, p
->off
= 1; p
> HMAP
; --p
)
179 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
184 /* If we fail, just punt. */
185 p
= HMAP
+ (TMAP
- HMAP
) / 2;
186 for (; p
< TMAP
; ++p
)
187 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
193 TMAP
->off
= svi_screens(sp
, ep
, lno
, NULL
);
195 /* If we fail, guess that the file is too small. */
196 bottom
: for (p
= TMAP
; p
> HMAP
; --p
)
197 if (svi_sm_prev(sp
, ep
, p
, p
- 1)) {
206 * Try and put *something* on the screen. If this fails,
207 * we have a serious hard error.
211 for (p
= HMAP
; p
< TMAP
; ++p
)
212 if (svi_sm_next(sp
, ep
, p
, p
+ 1))
218 * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
219 * screen only contains one line, or, if the line is the entire screen, this
220 * gets fairly exciting. Skip the fun and simply return if there's only one
221 * line in the screen, or just call fill. Fill may not be entirely accurate,
222 * i.e. we may be painting the screen with something not even close to the
223 * cursor, but it's not like we're into serious performance issues here, and
224 * the refresh routine will fix it for us.
226 #define TOO_WEIRD { \
227 if (cnt_orig >= sp->t_rows) { \
230 if (file_gline(sp, ep, lno, NULL) == NULL) \
231 if (file_lline(sp, ep, &lno)) \
233 F_SET(sp, S_REDRAW); \
234 return (svi_sm_fill(sp, ep, lno, P_TOP)); \
240 * Delete a line out of the SMAP.
243 svi_sm_delete(sp
, ep
, lno
)
252 * Find the line in the map, and count the number of screen lines
253 * which display any part of the deleted line.
255 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
256 for (cnt_orig
= 1, t
= p
+ 1;
257 t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
261 /* Delete that many lines from the screen. */
262 MOVE(sp
, p
- HMAP
, 0);
263 if (svi_deleteln(sp
, cnt_orig
))
266 /* Shift the screen map up. */
267 memmove(p
, p
+ cnt_orig
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
269 /* Decrement the line numbers for the rest of the map. */
270 for (t
= TMAP
- cnt_orig
; p
<= t
; ++p
)
273 /* Display the new lines. */
274 for (p
= TMAP
- cnt_orig
;;) {
275 if (p
< TMAP
&& svi_sm_next(sp
, ep
, p
, p
+ 1))
277 /* svi_sm_next() flushed the cache. */
278 if (svi_line(sp
, ep
, ++p
, NULL
, NULL
))
288 * Insert a line into the SMAP.
291 svi_sm_insert(sp
, ep
, lno
)
297 size_t cnt_orig
, cnt
;
300 * Find the line in the map, find out how many screen lines
301 * needed to display the line.
303 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
304 cnt_orig
= svi_screens(sp
, ep
, lno
, NULL
);
309 * The lines left in the screen override the number of screen
310 * lines in the inserted line.
312 cnt
= (TMAP
- p
) + 1;
316 /* Push down that many lines. */
317 MOVE(sp
, p
- HMAP
, 0);
318 if (svi_insertln(sp
, cnt_orig
))
321 /* Shift the screen map down. */
322 memmove(p
+ cnt_orig
, p
, (((TMAP
- p
) - cnt_orig
) + 1) * sizeof(SMAP
));
324 /* Increment the line numbers for the rest of the map. */
325 for (t
= p
+ cnt_orig
; t
<= TMAP
; ++t
)
328 /* Fill in the SMAP for the new lines, and display. */
329 for (cnt
= 1, t
= p
; cnt
<= cnt_orig
; ++t
, ++cnt
) {
333 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
341 * Reset a line in the SMAP.
344 svi_sm_reset(sp
, ep
, lno
)
350 size_t cnt_orig
, cnt_new
, cnt
, diff
;
353 * See if the number of on-screen rows taken up by the old display
354 * for the line is the same as the number needed for the new one.
355 * If so, repaint, otherwise do it the hard way.
357 for (p
= HMAP
; p
->lno
!= lno
; ++p
);
358 for (cnt_orig
= 0, t
= p
; t
<= TMAP
&& t
->lno
== lno
; ++cnt_orig
, ++t
);
359 cnt_new
= svi_screens(sp
, ep
, lno
, NULL
);
363 if (cnt_orig
== cnt_new
) {
366 if (svi_line(sp
, ep
, p
, NULL
, NULL
))
372 if (cnt_orig
< cnt_new
) {
373 /* Get the difference. */
374 diff
= cnt_new
- cnt_orig
;
377 * The lines left in the screen override the number of screen
378 * lines in the inserted line.
380 cnt
= (TMAP
- p
) + 1;
384 /* Push down the extra lines. */
385 MOVE(sp
, p
- HMAP
, 0);
386 if (svi_insertln(sp
, diff
))
389 /* Shift the screen map down. */
390 memmove(p
+ diff
, p
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
392 /* Fill in the SMAP for the replaced line, and display. */
393 for (cnt
= 1, t
= p
; cnt_new
-- && t
<= TMAP
; ++t
, ++cnt
) {
397 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
401 /* Get the difference. */
402 diff
= cnt_orig
- cnt_new
;
404 /* Delete that many lines from the screen. */
405 MOVE(sp
, p
- HMAP
, 0);
406 if (svi_deleteln(sp
, diff
))
409 /* Shift the screen map up. */
410 memmove(p
, p
+ diff
, (((TMAP
- p
) - diff
) + 1) * sizeof(SMAP
));
412 /* Fill in the SMAP for the replaced line, and display. */
413 for (cnt
= 1, t
= p
; cnt_new
--; ++t
, ++cnt
) {
417 if (svi_line(sp
, ep
, t
, NULL
, NULL
))
421 /* Display the new lines at the bottom of the screen. */
422 for (t
= TMAP
- diff
;;) {
423 if (t
< TMAP
&& svi_sm_next(sp
, ep
, t
, t
+ 1))
425 /* svi_sm_next() flushed the cache. */
426 if (svi_line(sp
, ep
, ++t
, NULL
, NULL
))
437 * Scroll the SMAP up count logical lines.
440 svi_sm_up(sp
, ep
, rp
, count
, cursor_move
)
450 /* Set the default return position. */
455 * Invalidate the cursor. The line is probably going to change,
456 * but if cursor_move isn't set it may not. In any case, this
457 * routine moves the cursor to draw things.
459 F_SET(SVP(sp
), SVI_CUR_INVALID
);
462 * There are two forms of this command, one where the cursor tries to
463 * follow the line, and one where it doesn't. In the latter, we try
464 * and keep the cursor at the same position on the screen, but, if the
465 * screen is small enough and the line length large enough, the cursor
466 * can end up in very strange places. Probably not worth fixing.
468 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
471 if (svi_sm_cursor(sp
, ep
, &p
))
481 * Check to see if movement is possible. Lots of checks...
483 * Find out if it's possible to move past the end of the map. If
484 * that's okay because we think that we can move the cursor down
485 * in the map, check to make sure that the map isn't mostly empty.
487 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
489 if (tmp
.lno
> TMAP
->lno
&&
490 !file_gline(sp
, ep
, tmp
.lno
, NULL
) ||
491 tmp
.off
> svi_screens(sp
, ep
, tmp
.lno
, NULL
)) {
492 if (!cursor_move
|| ignore_cursor
|| p
== TMAP
) {
496 if (svi_sm_next(sp
, ep
, p
, &tmp
))
498 if (!file_gline(sp
, ep
, tmp
.lno
, NULL
) ||
499 tmp
.off
> svi_screens(sp
, ep
, tmp
.lno
, NULL
)) {
506 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
508 * If it's a small screen, and the movement is small, open up the
509 * screen. Otherwise, compress and repaint. If we compress, we
510 * ignore the cursor, the movement is too large to care.
512 if (ISSMALLSCREEN(sp
))
513 if (count
<= HALFTEXT(sp
)) {
514 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
;
515 --count
, ++sp
->t_rows
) {
516 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
518 if (TMAP
->lno
!= tmp
.lno
&&
519 !file_gline(sp
, ep
, tmp
.lno
, NULL
))
522 /* svi_sm_next() flushed the cache. */
523 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
529 MOVE(sp
, INFOLINE(sp
), 0);
532 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
533 MOVE(sp
, TMAP
- HMAP
, 0);
539 for (; count
; --count
) {
540 /* Decide what would show up on the screen. */
541 if (svi_sm_next(sp
, ep
, TMAP
, &tmp
))
544 /* If the line doesn't exist, we're done. */
545 if (TMAP
->lno
!= tmp
.lno
&& !file_gline(sp
, ep
, tmp
.lno
, NULL
))
548 /* Scroll the screen cursor up one logical line. */
549 if (svi_sm_1up(sp
, ep
))
551 if (!cursor_move
&& !ignore_cursor
&& p
> HMAP
)
555 /* If ignoring the cursor, we're done. */
561 * If we didn't move enough, head toward EOF. Check to make
562 * sure the lines actually, if the file is smaller than the
563 * screen they may not.
565 for (; count
; --count
, ++p
)
566 if (p
== TMAP
|| !file_gline(sp
, ep
, p
[1].lno
, NULL
))
570 * If the line itself moved, invalidate the cursor, because
571 * the comparison with the old line/new line won't be right
573 F_SET(SVP(sp
), SVI_CUR_INVALID
);
575 /* If didn't move enough, it's an error. */
581 /* If the cursor moved off the screen, move it to the top. */
582 if (sp
->lno
< HMAP
->lno
)
586 * On a logical movement, we try and keep the cursor as close as
587 * possible to the last position, but also set it up so that the
588 * next "real" movement will return the cursor to the closest position
589 * to the last real movement.
591 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
593 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
600 * Scroll the SMAP up one.
608 * Delete the top line of the screen. Shift the screen map up.
609 * Display a new line at the bottom of the screen.
612 if (svi_deleteln(sp
, 1))
615 /* One-line screens can fail. */
617 if (svi_sm_next(sp
, ep
, TMAP
, TMAP
))
620 memmove(HMAP
, HMAP
+ 1, (sp
->rows
- 1) * sizeof(SMAP
));
621 if (svi_sm_next(sp
, ep
, TMAP
- 1, TMAP
))
624 /* svi_sm_next() flushed the cache. */
625 if (svi_line(sp
, ep
, TMAP
, NULL
, NULL
))
632 * Delete a line a la curses, make sure to put the information
633 * line and other screens back.
636 svi_deleteln(sp
, cnt
)
642 getyx(stdscr
, oldy
, oldx
);
645 MOVE(sp
, INFOLINE(sp
) - 1, 0);
647 MOVEA(sp
, oldy
, oldx
);
654 * Scroll the SMAP down count logical lines.
657 svi_sm_down(sp
, ep
, rp
, count
, cursor_move
)
667 /* Set the default return position. */
672 * Invalidate the cursor. The line is probably going to change,
673 * but if cursor_move isn't set it may not. In any case, this
674 * routine moves the cursor to draw things.
676 F_SET(SVP(sp
), SVI_CUR_INVALID
);
679 * There are two forms of this command, one where the cursor tries to
680 * follow the line, and one where it doesn't. In the latter, we try
681 * and keep the cursor at the same position on the screen, but, if the
682 * screen is small enough and the line length large enough, the cursor
683 * can end up in very strange places. Probably not worth fixing.
685 * Find the line in the SMAP -- ignore the cursor if it wasn't on the
688 if (svi_sm_cursor(sp
, ep
, &p
))
697 /* Check to see if movement is possible. */
698 if (HMAP
->lno
== 1 && HMAP
->off
== 1 &&
699 (!cursor_move
|| ignore_cursor
|| p
== HMAP
)) {
705 * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
707 * If it's a small screen, and the movement is small, open up the
708 * screen. Otherwise, compress and repaint. If we compress, we
709 * ignore the cursor, the movement is too large to care.
711 if (ISSMALLSCREEN(sp
))
712 if (count
<= HALFTEXT(sp
)) {
713 for (; count
&& sp
->t_rows
!= sp
->t_maxrows
&&
714 (HMAP
->lno
> 1 || HMAP
->off
> 1);
715 --count
, ++sp
->t_rows
) {
717 if (svi_sm_1down(sp
, ep
))
725 MOVE(sp
, INFOLINE(sp
), 0);
728 sp
->t_rows
> sp
->t_minrows
; --sp
->t_rows
, --TMAP
) {
729 MOVE(sp
, TMAP
- HMAP
, 0);
735 for (; count
; --count
) {
736 /* If the line doesn't exist, we're done. */
737 if (HMAP
->lno
== 1 && HMAP
->off
== 1)
740 /* Scroll the screen and cursor down one logical line. */
741 if (svi_sm_1down(sp
, ep
))
743 if (!cursor_move
&& !ignore_cursor
&& p
< TMAP
)
747 /* If ignoring the cursor, we're done. */
752 /* If we didn't move enough, move to SOF. */
757 * If the line itself moved, invalidate the cursor, because
758 * the comparison with the old line/new line won't be right.
760 F_SET(SVP(sp
), SVI_CUR_INVALID
);
762 /* If didn't move enough, it's an error. */
768 /* If the cursor moved off the screen, move it to the bottom. */
769 if (sp
->lno
> TMAP
->lno
)
774 * On a logical movement, we try and keep the cursor as close as
775 * possible to the last position, but also set it up so that the
776 * next "real" movement will return the cursor to the closest position
777 * to the last real movement.
779 if (p
->lno
!= svmap
.lno
|| p
->off
!= svmap
.off
) {
781 rp
->cno
= svi_lrelative(sp
, ep
, p
->lno
, p
->off
);
788 * Scroll the SMAP down one.
796 * Clear the bottom line of the screen, insert a line at the top
797 * of the screen. Shift the screen map down, display a new line
798 * at the top of the screen.
800 MOVE(sp
, sp
->t_rows
, 0);
803 if (svi_insertln(sp
, 1))
805 memmove(HMAP
+ 1, HMAP
, (sp
->rows
- 1) * sizeof(SMAP
));
806 if (svi_sm_prev(sp
, ep
, HMAP
+ 1, HMAP
))
808 /* svi_sm_prev() flushed the cache. */
809 if (svi_line(sp
, ep
, HMAP
, NULL
, NULL
))
816 * Insert a line a la curses, make sure to put the information
817 * line and other screens back.
820 svi_insertln(sp
, cnt
)
826 getyx(stdscr
, oldy
, oldx
);
828 MOVE(sp
, INFOLINE(sp
) - 1, 0);
830 MOVEA(sp
, oldy
, oldx
);
838 * Fill in the next entry in the SMAP.
841 svi_sm_next(sp
, ep
, p
, t
)
849 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
853 lcnt
= svi_screens(sp
, ep
, p
->lno
, NULL
);
854 if (lcnt
== p
->off
) {
867 * Fill in the previous entry in the SMAP.
870 svi_sm_prev(sp
, ep
, p
, t
)
876 if (O_ISSET(sp
, O_LEFTRIGHT
)) {
879 } else if (p
->off
!= 1) {
884 t
->off
= svi_screens(sp
, ep
, t
->lno
, NULL
);
886 return (t
->lno
== 0);
891 * Return the SMAP entry referenced by the cursor.
894 svi_sm_cursor(sp
, ep
, smp
)
901 /* See if the cursor is not in the map. */
902 if (sp
->lno
< HMAP
->lno
|| sp
->lno
> TMAP
->lno
) {
907 /* Find the first occurence of the line. */
908 for (p
= HMAP
; p
->lno
!= sp
->lno
; ++p
);
910 /* Fill in the map information until we find the right line. */
911 for (; p
<= TMAP
; ++p
) {
912 /* Short lines are common and easy to detect. */
913 if (p
!= TMAP
&& (p
+ 1)->lno
!= p
->lno
) {
917 if (!SMAP_CACHE(p
) && svi_line(sp
, ep
, p
, NULL
, NULL
))
919 if (p
->c_eboff
>= sp
->cno
) {
925 /* It was past the end of the map after all. */
932 * Return the line/column of the top, middle or last line on the screen.
933 * (The vi H, M and L commands.) Here because only the screen routines
934 * know what's really out there.
937 svi_sm_position(sp
, ep
, rp
, cnt
, pos
)
949 if (cnt
> TMAP
- HMAP
)
954 if (cnt
> (TMAP
- HMAP
) / 2)
956 smp
= (HMAP
+ (TMAP
- HMAP
) / 2) + cnt
;
959 if (cnt
> TMAP
- HMAP
) {
960 err
: msgq(sp
, M_BERR
, "Movement past the end-of-screen.");
964 eof
: if (file_gline(sp
, ep
, smp
->lno
, NULL
) == NULL
) {
965 if (file_lline(sp
, ep
, &last
))
967 for (; smp
->lno
> last
&& smp
> HMAP
; --smp
);
974 if (!SMAP_CACHE(smp
) && svi_line(sp
, ep
, smp
, NULL
, NULL
))
977 rp
->cno
= smp
->c_sboff
;
984 * Return the number of screen lines from an SMAP entry to the
985 * start of some file line, less than a maximum value.
988 svi_sm_nlines(sp
, ep
, from_sp
, to_lno
, max
)
997 if (O_ISSET(sp
, O_LEFTRIGHT
))
998 return (from_sp
->lno
> to_lno
?
999 from_sp
->lno
- to_lno
: to_lno
- from_sp
->lno
);
1001 if (from_sp
->lno
== to_lno
)
1002 return (from_sp
->off
- 1);
1004 if (from_sp
->lno
> to_lno
) {
1005 lcnt
= from_sp
->off
- 1; /* Correct for off-by-one. */
1006 for (lno
= from_sp
->lno
; --lno
>= to_lno
&& lcnt
<= max
;)
1007 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);
1010 lcnt
= (svi_screens(sp
, ep
, lno
, NULL
) - from_sp
->off
) + 1;
1011 for (; ++lno
< to_lno
&& lcnt
<= max
;)
1012 lcnt
+= svi_screens(sp
, ep
, lno
, NULL
);