📄 svi_smap.c
字号:
/*- * Copyright (c) 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)svi_smap.c 8.39 (Berkeley) 4/13/94";#endif /* not lint */#include <sys/types.h>#include <sys/queue.h>#include <sys/time.h>#include <bitstring.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include "compat.h"#include <curses.h>#include <db.h>#include <regex.h>#include "vi.h"#include "vcmd.h"#include "svi_screen.h"static int svi_deleteln __P((SCR *, int));static int svi_insertln __P((SCR *, int));static int svi_sm_delete __P((SCR *, EXF *, recno_t));static int svi_sm_insert __P((SCR *, EXF *, recno_t));static int svi_sm_reset __P((SCR *, EXF *, recno_t));/* * svi_change -- * Make a change to the screen. */intsvi_change(sp, ep, lno, op) SCR *sp; EXF *ep; recno_t lno; enum operation op;{ SMAP *p; size_t oldy, oldx; /* Appending is the same as inserting, if the line is incremented. */ if (op == LINE_APPEND) { ++lno; op = LINE_INSERT; } /* Ignore the change if the line is after the map. */ if (lno > TMAP->lno) return (0); /* * If the line is before the map, and it's a decrement, decrement * the map. If it's an increment, increment the map. Otherwise, * ignore it. */ if (lno < HMAP->lno) { switch (op) { case LINE_APPEND: abort(); /* NOTREACHED */ case LINE_DELETE: for (p = HMAP; p <= TMAP; ++p) --p->lno; if (sp->lno >= lno) --sp->lno; F_SET(sp, S_RENUMBER); break; case LINE_INSERT: for (p = HMAP; p <= TMAP; ++p) ++p->lno; if (sp->lno >= lno) ++sp->lno; F_SET(sp, S_RENUMBER); break; case LINE_RESET: break; } return (0); } F_SET(SVP(sp), SVI_SCREENDIRTY); /* Invalidate the cursor, if it's on this line. */ if (sp->lno == lno) F_SET(SVP(sp), SVI_CUR_INVALID); /* Invalidate the line size cache. */ SVI_SCR_CFLUSH(SVP(sp)); getyx(stdscr, oldy, oldx); switch (op) { case LINE_DELETE: if (svi_sm_delete(sp, ep, lno)) return (1); F_SET(sp, S_RENUMBER); break; case LINE_INSERT: if (svi_sm_insert(sp, ep, lno)) return (1); F_SET(sp, S_RENUMBER); break; case LINE_RESET: if (svi_sm_reset(sp, ep, lno)) return (1); break; default: abort(); } MOVEA(sp, oldy, oldx); return (0);}/* * svi_sm_fill -- * Fill in the screen map, placing the specified line at the * right position. There isn't any way to tell if an SMAP * entry has been filled in, so this routine had better be * called with P_FILL set before anything else is done. * * !!! * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP * slot is already filled in, P_BOTTOM means that the TMAP slot is * already filled in, and we just finish up the job. */intsvi_sm_fill(sp, ep, lno, pos) SCR *sp; EXF *ep; recno_t lno; enum position pos;{ SMAP *p, tmp; /* Flush all cached information from the SMAP. */ for (p = HMAP; p <= TMAP; ++p) SMAP_FLUSH(p); switch (pos) { case P_FILL: tmp.lno = 1; tmp.off = 1; /* See if less than half a screen from the top. */ if (svi_sm_nlines(sp, ep, &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { lno = 1; goto top; } /* See if less than half a screen from the bottom. */ if (file_lline(sp, ep, &tmp.lno)) return (1); if (!O_ISSET(sp, O_LEFTRIGHT)) tmp.off = svi_opt_screens(sp, ep, tmp.lno, NULL); if (svi_sm_nlines(sp, ep, &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) { TMAP->lno = tmp.lno; if (!O_ISSET(sp, O_LEFTRIGHT)) TMAP->off = tmp.off; goto bottom; } goto middle; case P_TOP: if (lno != OOBLNO) {top: HMAP->lno = lno; HMAP->off = 1; } /* If we fail, just punt. */ for (p = HMAP; p < TMAP; ++p) if (svi_sm_next(sp, ep, p, p + 1)) goto err; break; case P_MIDDLE: /* If we fail, guess that the file is too small. */middle: p = HMAP + (TMAP - HMAP) / 2; for (p->lno = lno, p->off = 1; p > HMAP; --p) if (svi_sm_prev(sp, ep, p, p - 1)) { lno = 1; goto top; } /* If we fail, just punt. */ p = HMAP + (TMAP - HMAP) / 2; for (; p < TMAP; ++p) if (svi_sm_next(sp, ep, p, p + 1)) goto err; break; case P_BOTTOM: if (lno != OOBLNO) { TMAP->lno = lno; if (!O_ISSET(sp, O_LEFTRIGHT)) TMAP->off = svi_opt_screens(sp, ep, lno, NULL); } /* If we fail, guess that the file is too small. */bottom: for (p = TMAP; p > HMAP; --p) if (svi_sm_prev(sp, ep, p, p - 1)) { lno = 1; goto top; } break; } return (0); /* * Try and put *something* on the screen. If this fails, * we have a serious hard error. */err: HMAP->lno = 1; HMAP->off = 1; for (p = HMAP; p < TMAP; ++p) if (svi_sm_next(sp, ep, p, p + 1)) return (1); return (0);}/* * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the * screen only contains one line, or, if the line is the entire screen, this * gets fairly exciting. Skip the fun and simply return if there's only one * line in the screen, or just call fill. Fill may not be entirely accurate, * i.e. we may be painting the screen with something not even close to the * cursor, but it's not like we're into serious performance issues here, and * the refresh routine will fix it for us. */#define TOO_WEIRD { \ if (cnt_orig >= sp->t_rows) { \ if (cnt_orig == 1) \ return (0); \ if (file_gline(sp, ep, lno, NULL) == NULL) \ if (file_lline(sp, ep, &lno)) \ return (1); \ F_SET(sp, S_REDRAW); \ return (svi_sm_fill(sp, ep, lno, P_TOP)); \ } \}/* * svi_sm_delete -- * Delete a line out of the SMAP. */static intsvi_sm_delete(sp, ep, lno) SCR *sp; EXF *ep; recno_t lno;{ SMAP *p, *t; size_t cnt_orig; /* * Find the line in the map, and count the number of screen lines * which display any part of the deleted line. */ for (p = HMAP; p->lno != lno; ++p); if (O_ISSET(sp, O_LEFTRIGHT)) cnt_orig = 1; else for (cnt_orig = 1, t = p + 1; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); TOO_WEIRD; /* Delete that many lines from the screen. */ MOVE(sp, p - HMAP, 0); if (svi_deleteln(sp, cnt_orig)) return (1); /* Shift the screen map up. */ memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); /* Decrement the line numbers for the rest of the map. */ for (t = TMAP - cnt_orig; p <= t; ++p) --p->lno; /* Display the new lines. */ for (p = TMAP - cnt_orig;;) { if (p < TMAP && svi_sm_next(sp, ep, p, p + 1)) return (1); /* svi_sm_next() flushed the cache. */ if (svi_line(sp, ep, ++p, NULL, NULL)) return (1); if (p == TMAP) break; } return (0);}/* * svi_sm_insert -- * Insert a line into the SMAP. */static intsvi_sm_insert(sp, ep, lno) SCR *sp; EXF *ep; recno_t lno;{ SMAP *p, *t; size_t cnt_orig, cnt; /* * Find the line in the map, find out how many screen lines * needed to display the line. */ for (p = HMAP; p->lno != lno; ++p); if (O_ISSET(sp, O_LEFTRIGHT)) cnt_orig = 1; else cnt_orig = svi_opt_screens(sp, ep, lno, NULL); TOO_WEIRD; /* * The lines left in the screen override the number of screen * lines in the inserted line. */ cnt = (TMAP - p) + 1; if (cnt_orig > cnt) cnt_orig = cnt; /* Push down that many lines. */ MOVE(sp, p - HMAP, 0); if (svi_insertln(sp, cnt_orig)) return (1); /* Shift the screen map down. */ memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP)); /* Increment the line numbers for the rest of the map. */ for (t = p + cnt_orig; t <= TMAP; ++t) ++t->lno; /* Fill in the SMAP for the new lines, and display. */ for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) { t->lno = lno; t->off = cnt; SMAP_FLUSH(t); if (svi_line(sp, ep, t, NULL, NULL)) return (1); } return (0);}/* * svi_sm_reset -- * Reset a line in the SMAP. */static intsvi_sm_reset(sp, ep, lno) SCR *sp; EXF *ep; recno_t lno;{ SMAP *p, *t; size_t cnt_orig, cnt_new, cnt, diff; /* * See if the number of on-screen rows taken up by the old display * for the line is the same as the number needed for the new one. * If so, repaint, otherwise do it the hard way. */ for (p = HMAP; p->lno != lno; ++p); if (O_ISSET(sp, O_LEFTRIGHT)) { t = p; cnt_orig = cnt_new = 1; } else { for (cnt_orig = 0, t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t); cnt_new = svi_opt_screens(sp, ep, lno, NULL); } TOO_WEIRD; if (cnt_orig == cnt_new) { do { SMAP_FLUSH(p); if (svi_line(sp, ep, p, NULL, NULL)) return (1); } while (++p < t); return (0); } if (cnt_orig < cnt_new) { /* Get the difference. */ diff = cnt_new - cnt_orig; /* * The lines left in the screen override the number of screen * lines in the inserted line. */ cnt = (TMAP - p) + 1; if (diff > cnt) diff = cnt; /* Push down the extra lines. */ MOVE(sp, p - HMAP, 0); if (svi_insertln(sp, diff)) return (1); /* Shift the screen map down. */ memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); /* Fill in the SMAP for the replaced line, and display. */ for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) { t->lno = lno; t->off = cnt; SMAP_FLUSH(t); if (svi_line(sp, ep, t, NULL, NULL)) return (1); } } else { /* Get the difference. */ diff = cnt_orig - cnt_new; /* Delete that many lines from the screen. */ MOVE(sp, p - HMAP, 0); if (svi_deleteln(sp, diff)) return (1); /* Shift the screen map up. */ memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP)); /* Fill in the SMAP for the replaced line, and display. */ for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) { t->lno = lno; t->off = cnt; SMAP_FLUSH(t); if (svi_line(sp, ep, t, NULL, NULL)) return (1); } /* Display the new lines at the bottom of the screen. */ for (t = TMAP - diff;;) { if (t < TMAP && svi_sm_next(sp, ep, t, t + 1)) return (1); /* svi_sm_next() flushed the cache. */ if (svi_line(sp, ep, ++t, NULL, NULL)) return (1); if (t == TMAP) break; } } return (0);}/* * svi_sm_up -- * Scroll the SMAP up count logical lines. */intsvi_sm_up(sp, ep, rp, count, cursor_move) SCR *sp; EXF *ep; MARK *rp; recno_t count; int cursor_move;{ SMAP *p, svmap, tmp; int ignore_cursor; /* Set the default return position. */ rp->lno = sp->lno; rp->cno = sp->cno; /* * Invalidate the cursor. The line is probably going to change, * but if cursor_move isn't set it may not. In any case, this * routine moves the cursor to draw things. */ F_SET(SVP(sp), SVI_CUR_INVALID); /* * There are two forms of this command, one where the cursor tries to * follow the line, and one where it doesn't. In the latter, we try * and keep the cursor at the same position on the screen, but, if the * screen is small enough and the line length large enough, the cursor * can end up in very strange places. Probably not worth fixing. * * Find the line in the SMAP -- ignore the cursor if it wasn't on the * screen. */ if (svi_sm_cursor(sp, ep, &p)) return (1); if (p == NULL) ignore_cursor = 1; else { svmap = *p; ignore_cursor = 0; } /* * Check to see if movement is possible. Lots of checks... * * Find out if it's possible to move past the end of the map. If * that's okay because we think that we can move the cursor down * in the map, check to make sure that the map isn't mostly empty. */ if (svi_sm_next(sp, ep, TMAP, &tmp)) return (1); if (tmp.lno > TMAP->lno && !file_gline(sp, ep, tmp.lno, NULL) || !O_ISSET(sp, O_LEFTRIGHT) && tmp.off > svi_opt_screens(sp, ep, tmp.lno, NULL)) { if (!cursor_move || ignore_cursor || p == TMAP) { v_eof(sp, ep, NULL); return (1); } if (svi_sm_next(sp, ep, p, &tmp)) return (1); if (!file_gline(sp, ep, tmp.lno, NULL) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -