/* vi:ts=4:sw=4 * * VIM - Vi IMproved * * Code Contributions By: Bram Moolenaar mool@oce.nl * Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!watcgl!grwalter */ /* * mark.c: functions for setting marks and jumping to them */ #include "vim.h" #include "globals.h" #include "proto.h" #include "mark.h" #include "ops.h" /* for endop and startop */ /* * This file contains routines to maintain and manipulate marks. */ #define NMARKS 26 /* max. # of named marks */ #define JUMPLISTSIZE 50 /* max. # of marks in jump list */ static struct mark pcmark; /* previous context mark */ static struct mark namedm[NMARKS]; /* original vi marks */ static struct filemark namedfm[NMARKS]; /* new marks with file nr */ static struct filemark jumplist[JUMPLISTSIZE]; /* list of old pcmarks */ static int jumplistlen = 0; static int jumplistidx = 0; static FPOS *mark2pos __ARGS((struct mark *)); #ifdef NEW struct markptr { int mp_ident; /* 'a' - 'z', 'A' - 'Z' or jumplist */ struct filemark mp_fm; } marklist[NMARKS + NMARKS + JUMPLISTSIZE]; int marklistlen = 0; adjustmark(old, new) { max = marklistlen - 1; min = 0; while (max > min) { i = (max + min) / 2; t = marklist[i].mp_fm.ptr; if (t > old) max = i - 1; else if (t < old) min = i + 1; } if (max == min && marklist[i].mp_fm.ptr == old) { } } #endif /* * setmark(c) - set named mark 'c' at current cursor position * * Returns TRUE on success, FALSE if no room for mark or bad name given. */ int setmark(c) int c; { int i; if (islower(c)) { i = c - 'a'; namedm[i].ptr = nr2ptr(Curpos.lnum); namedm[i].col = Curpos.col; return TRUE; } if (isupper(c)) { i = c - 'A'; namedfm[i].mark.ptr = nr2ptr(Curpos.lnum); namedfm[i].mark.col = Curpos.col; namedfm[i].lnum = Curpos.lnum; namedfm[i].fnum = 0; return TRUE; } return FALSE; } /* * setpcmark() - set the previous context mark to the current position * and insert it into the jump list */ void setpcmark() { int i; #ifdef ROTATE struct filemark tempmark; #endif pcmark.ptr = nr2ptr(Curpos.lnum); pcmark.col = Curpos.col; #ifndef ROTATE /* * simply add the new entry at the end of the list */ jumplistidx = jumplistlen; #else /* * If last used entry is not at the top, put it at the top by rotating * the stack until it is (the newer entries will be at the bottom). * Keep one entry (the last used one) at the top. */ if (jumplistidx < jumplistlen) ++jumplistidx; while (jumplistidx < jumplistlen) { tempmark = jumplist[jumplistlen - 1]; for (i = jumplistlen - 1; i > 0; --i) jumplist[i] = jumplist[i - 1]; jumplist[0] = tempmark; ++jumplistidx; } #endif /* only add new entry if it differs from the last one */ if (jumplistlen == 0 || jumplist[jumplistidx - 1].mark.ptr != pcmark.ptr) { /* if jumplist is full: remove oldest entry */ if (++jumplistlen > JUMPLISTSIZE) { jumplistlen = JUMPLISTSIZE; for (i = 1; i < jumplistlen; ++i) jumplist[i - 1] = jumplist[i]; --jumplistidx; } jumplist[jumplistidx].mark = pcmark; jumplist[jumplistidx].lnum = Curpos.lnum; jumplist[jumplistidx].fnum = 0; ++jumplistidx; } } /* * move "count" positions in the jump list (count may be negative) */ FPOS * movemark(count) int count; { FPOS *pos; if (jumplistlen == 0) /* nothing to jump to */ return (FPOS *)NULL; if (jumplistidx + count < 0 || jumplistidx + count >= jumplistlen) return (FPOS *)NULL; /* * if first CTRL-O or CTRL-I command after a jump, add cursor position to list */ if (jumplistidx == jumplistlen) { setpcmark(); --jumplistidx; /* skip the new entry */ } jumplistidx += count; if (jumplist[jumplistidx].mark.ptr == NULL) /* jump to other file */ { if (getaltfile(jumplist[jumplistidx].fnum - 1, jumplist[jumplistidx].lnum, FALSE)) return (FPOS *)NULL; Curpos.col = jumplist[jumplistidx].mark.col; jumplist[jumplistidx].fnum = 0; jumplist[jumplistidx].mark.ptr = nr2ptr(Curpos.lnum); pos = (FPOS *)-1; } else pos = mark2pos(&jumplist[jumplistidx].mark); return pos; } /* * getmark(c) - find mark for char 'c' * * Return pointer to FPOS if found * NULL if no such mark. * -1 if mark is in other file (only if changefile is TRUE) */ FPOS * getmark(c, changefile) int c; int changefile; { FPOS *posp; posp = NULL; if (c == '\'' || c == '`') /* previous context mark */ posp = mark2pos(&pcmark); else if (c == '[') /* to start of previous operator */ { if (startop.lnum > 0 && startop.lnum <= line_count) posp = &startop; } else if (c == ']') /* to end of previous operator */ { if (endop.lnum > 0 && endop.lnum <= line_count) posp = &endop; } else if (islower(c)) /* normal named mark */ posp = mark2pos(&(namedm[c - 'a'])); else if (isupper(c)) /* named file mark */ { c -= 'A'; posp = mark2pos(&(namedfm[c].mark)); if (posp == NULL && namedfm[c].lnum != 0 && (changefile || samealtfile(namedfm[c].fnum - 1))) { if (!getaltfile(namedfm[c].fnum - 1, namedfm[c].lnum, TRUE)) { Curpos.col = namedfm[c].mark.col; namedfm[c].fnum = 0; namedfm[c].mark.ptr = nr2ptr(Curpos.lnum); posp = (FPOS *)-1; } } } return posp; } static FPOS * mark2pos(markp) struct mark *markp; { static FPOS pos; if (markp->ptr != NULL && (pos.lnum = ptr2nr(markp->ptr, (linenr_t)1)) != 0) { pos.col = markp->col; return (&pos); } return (FPOS *)NULL; } /* * clrallmarks() - clear all marks * * Used mainly when trashing the entire buffer during ":e" type commands */ void clrallmarks() { static int i = -1; if (i == -1) /* first call ever: initialize */ for (i = 0; i < NMARKS; i++) namedfm[i].lnum = 0; for (i = 0; i < NMARKS; i++) { namedm[i].ptr = NULL; namedfm[i].mark.ptr = NULL; } pcmark.ptr = NULL; qf_clrallmarks(); for (i = 0; i < jumplistlen; ++i) jumplist[i].mark.ptr = NULL; } /* * increment the file number for all filemarks * called when adding a file to the file stack */ void incrmarks() { int i; for (i = 0; i < NMARKS; i++) ++namedfm[i].fnum; for (i = 0; i < jumplistlen; ++i) { #if 0 /* this would take too much time */ if (jumplist[i].fnum == 0) /* current file */ jumplist[i].lnum = ptr2nr(jumplist[i].mark.ptr, 1); #endif ++jumplist[i].fnum; } } /* * decrement the file number for the filemarks of the current file * called when not adding the current file name to the file stack */ void decrmarks() { int i; for (i = 0; i < NMARKS; i++) if (namedfm[i].fnum == 1) namedfm[i].fnum = 0; for (i = 0; i < jumplistlen; ++i) if (jumplist[i].fnum == 1) jumplist[i].fnum = 0; } /* * adjustmark: set new ptr for a mark * if new == NULL the mark is effectively deleted * (this is slow: we have to check about 100 pointers!) */ void adjustmark(old, new) char *old, *new; { register int i, j; for (i = 0; i < NMARKS; ++i) { if (namedm[i].ptr == old) namedm[i].ptr = new; if (namedfm[i].mark.ptr == old) { namedfm[i].mark.ptr = new; if (new == NULL) namedfm[i].lnum = 0; /* delete this mark */ } } if (pcmark.ptr == old) pcmark.ptr = new; for (i = 0; i < jumplistlen; ++i) if (jumplist[i].mark.ptr == old) { if (new == NULL) /* delete this mark */ { --jumplistlen; if (jumplistidx > jumplistlen) --jumplistidx; for (j = i; j < jumplistlen; ++j) jumplist[j] = jumplist[j + 1]; } else jumplist[i].mark.ptr = new; } qf_adjustmark(old, new); } /* * get name of file from a filemark (use the occasion to update the lnum) */ char * fm_getname(fmark) struct filemark *fmark; { linenr_t nr; char *name; if (fmark->fnum != 0) /* maybe not current file */ { name = getaltfname(fmark->fnum - 1); if (name == NULL) return "-none-"; if (Filename == NULL || fnamecmp(name, Filename) != 0) /* not current file */ return name; fmark->fnum = 0; } if (fmark->mark.ptr == NULL) { if (fmark->lnum <= line_count) /* safety check */ fmark->mark.ptr = nr2ptr(fmark->lnum); /* update ptr */ } else { nr = ptr2nr(fmark->mark.ptr, (linenr_t)1); if (nr != 0) fmark->lnum = nr; /* update lnum */ } return "-current-"; } /* * print the marks (use the occasion to update the line numbers) */ void domarks() { int i; char *name; #ifdef AMIGA settmode(0); /* set cooked mode, so output can be halted */ #endif outstrn("\nmark line file\n"); for (i = 0; i < NMARKS; ++i) { if (namedm[i].ptr != NULL) { sprintf(IObuff, " %c %5ld\n", i + 'a', ptr2nr(namedm[i].ptr, (linenr_t)1)); outstrn(IObuff); } flushbuf(); } for (i = 0; i < NMARKS; ++i) { if (namedfm[i].lnum != 0) { name = fm_getname(&namedfm[i]); if (name == NULL) /* file name not available */ continue; sprintf(IObuff, " %c %5ld %s\n", i + 'A', namedfm[i].lnum, name); outstrn(IObuff); } flushbuf(); } #ifdef AMIGA settmode(1); #endif wait_return(TRUE); } /* * print the jumplist (use the occasion to update the line numbers) */ void dojumps() { int i; char *name; #ifdef AMIGA settmode(0); /* set cooked mode, so output can be halted */ #endif outstrn("\n jump line file\n"); for (i = 0; i < jumplistlen; ++i) { if (jumplist[i].lnum != 0) { name = fm_getname(&jumplist[i]); if (name == NULL) /* file name not available */ continue; sprintf(IObuff, "%c %2d %5ld %s\n", i == jumplistidx ? '>' : ' ', i + 1, jumplist[i].lnum, name); outstrn(IObuff); } flushbuf(); } if (jumplistidx == jumplistlen) outstrn(">\n"); #ifdef AMIGA settmode(1); #endif wait_return(TRUE); }