/* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1