/************************************************************************
 * This program is Copyright (C) 1986 by Jonathan Payne.  JOVE is       *
 * provided to you without charge, and with no warranty.  You may give  *
 * away copies of JOVE, including sources, provided that this notice is *
 * included in all the files.                                           *
 ************************************************************************/
/*
 * Modified: October 1988 by T.R.Hageman
 */
#include "jove.h"

RCS("$Id: move.c,v 14.30.0.10 1994/05/09 09:04:17 tom Exp tom $")

#include "ctype.h"
#include "re.h"

/* this kludge to determine whether ForChar was invoked directly */
#define DIRECT_CMD	(LastCmd->Type & ARG(0x80))

/* Move over characters. Bidirectional. */

DEF_CMD( "backward-character", ForChar, NEGATE|ARG(0x80) );
DEF_CMD( "forward-character", ForChar, ARG(0x80) )
{
	register Buffer	*cb = curbuf;
	register int	num;

	if ((num = exp) <= 0) {		/* backward move is simple. */
		while ((cb->b_char += num) < 0) {
			num = cb->b_char + 1;
			cb->b_char = 0;
			/* Go to the previous Line */
			if (firstp(cb, cb->b_dot))
				break;
			/* ...except when we're in overwrite mode */
			/* AND invoked directly by the dispatcher */
			if (BufMinorMode(cb, OverWrite) &&
			    DIRECT_CMD && exp_p != YES_NODIGIT)
				break;
			SetLine(cb->b_dot->l_prev);
			cb->b_char = strlen(linebuf);
		}
	} else {			/* forward move is more complicated. */
		register char	*cp = &linebuf[cb->b_char];

		do {
			if (*cp++)
				continue;

			/* Go to the next Line */
			--cp;
			/* ...except when we're in overwrite mode */
			/* AND invoked directly by the dispatcher */
			if (BufMinorMode(cb, OverWrite) &&
			    DIRECT_CMD && exp_p != YES_NODIGIT) {
				ins_c(' ', cp, 0, num,
				      (int)(&linebuf[LBSIZE] - cp));
				cp += num;
				break;
			}
			if (Asking) {
				/* This avoids losing the line when
				   we're editing minibuf. */
				break;
			}
			if (lastp(cb, cb->b_dot))
				break;

			SetLine(cb->b_dot->l_next);
			cp = linebuf;

		} while (--num);

		cb->b_char = cp - linebuf;
	}
}

DEF_CMD( "beginning-of-line", Bol, NO )
{
	curchar = 0;
}

DEF_CMD( "end-of-line", Eol, NO )
{
	curchar = strlen(linebuf);
}

DEF_CMD( "end-of-file", Eof, NO )
{
	PushPntp(curbuf->b_last);
	ToLast();
}

DEF_CMD( "beginning-of-file", Bof, NO )
{
	PushPntp(curbuf->b_first);
	ToFirst();
}

/* moves NUM lines down (up if NUM < 0) */

void
line_move(num)
{
	register Buffer	*cb = curbuf;
	register Line	*line;

	line = next_line(cb->b_dot, num);
	if (line == cb->b_dot)
		if (num <= 0)
			cb->b_char = 0;	/* Bol(); */
		else
			Eol();
	else
		SetLine(line);		/* curline is in linebuf now */
}

/*
 * Move over lines. Bidirectional.
 * (we don't use the above `line_move' since we want to line up with the
 * column of the current line, and some other funny stuff in Overwrite mode)
 */
DEF_CMD( "previous-line", NextLine, NEGATE );
DEF_CMD( "next-line", NextLine, NO )
{
	extern void	AskNextLine __(( void ));
	static int	line_pos;
	register Buffer	*cb = curbuf;
	register Line	*line;
	register int	num;

	if (Asking) {
		AskNextLine();
		return;
	}

	if ((num = exp) == 0)
		return;

	this_cmd = LINECMD;
	if (last_cmd != LINECMD)
		line_pos = calc_pos(linebuf, cb->b_char);

	line = next_line(cb->b_dot, num);

	if (line == cb->b_dot) {
		if (num < 0) {
			if (cb->b_char == 0)
				complain((char *)0);
			cb->b_char = 0;	/* Bol(); */
			return;
		}
		if (!BufMinorMode(cb, OverWrite)) {
			if (eolp(cb))
				complain((char *)0);
			Eol();
			return;
		}
		/* append empty lines to buffer in Overwrite mode */
		do {
			line = listput(cb, line);
			SavLine(line, NullStr);
		} while (--num);
	}
	DotTo(line, how_far(lcontents(line), line_pos));
}

#ifdef SKIPTABS
DEF_INT( "skip-partial-tabs", SkipTabs, V_BOOL ) _IF(def SKIPTABS)_IF(def PRIVATE) ZERO;
#endif

/* returns what cur_char should be for that position col.
   If in OverWrite mode, we allow moving ``beyond end-of-line''.
   (These are handled in DotTo.) */
int
how_far(base, col)
const char	*base;
{
	register const char
			*lp = base;
	register int	pos = 0,
			c;

	/* position to start of tab if between tab stops [TRH] 5-Sep-88 */

	while (pos < col && (c = *lp++))
		UpdVisPos(pos, c);

	if (pos != col) {
#ifdef SKIPTABS
		if (pos < col || False(SkipTabs))
#endif
			lp--;
		if (pos < col && MinorMode(OverWrite))
			lp += col - pos;
	}
	return lp - base;
}

/* Move forward (if dir > 0) or backward (if dir < 0) a word. */

private void to_word __(( int _(dir) ));
private void
to_word(dir)
int	dir;
{
	register Buffer	*cb = curbuf;
	register char	*cp = &linebuf[cb->b_char];
	register Line	*lp = cb->b_dot;

	if (dir > 0) for (;;) {
		if (*cp) {
			if (isword(*cp++)) {
				--cp;
				break;
			}
			continue;
		}
		if ((lp = lp->l_next) == NULL)	/* eobp() */
			break;
		SetLine(lp);
		cp = linebuf;
	} else for (;;) {
		if (cp > linebuf) {
			if (isword(*--cp)) {
				++cp;
				break;
			}
			continue;
		}
		if ((lp = lp->l_prev) == NULL)	/* bobp() */
			break;
		SetLine(lp);
		while (*cp++) ;			/* skip to end of line */
	}
	cb->b_char = cp - linebuf;
}

/* Move over words. Bidirectional */

DEF_CMD( "backward-word", ForWord, NEGATE );
DEF_CMD( "forward-word", ForWord, NO )
{
	register Buffer	*cb = curbuf;
	register int	num;
	register char	*cp;

	this_cmd = 0;	/* Semi kludge to stop some unfavorable behavior */

	if ((num = exp) == 0)
		return;

	if (num < 0) {
		do {
			to_word(BACKWARD);

			/* Now, move to start of word. */
			cp = &linebuf[cb->b_char];
			while (cb->b_char > 0 && isword(*--cp))
				cb->b_char--;
			if (bobp(cb))
				break;
		} while (++num);
	} else {
		do {
			to_word(FORWARD);

			/* Now, move to end of word. */
			cp = &linebuf[cb->b_char];
			while (*cp != '\0' && isword(*cp++))
				cb->b_char++;

			if (eobp(cb))
				break;
		} while (--num);
	}
}

/* Move forward (if dir > 0) or backward (if dir < 0) a sentence.  Deals
   with all the kludgery involved with paragraphs, and moving backwards
   is particularly yucky. */

DEF_STR( "sentence-end", SentEndRE, 80, V_REGEXP ) =
	"^[ \t]*$\\|[.?!:][]'\")}]*";	/* How to find the end of a sentence. */
DEF_STR( "sentence-sep", SentSepRE, 40, V_REGEXP ) =
	"\\{\\{  ,\t\\}[\t ]*,[\t ]*$\\}";	/* What should follow eos. */

private Bufpos *to_sent __(( int _(dir) ));
private Bufpos *
to_sent(dir)
register int	dir;
{
	register Buffer	*cb = curbuf;
	register Bufpos	*new;
	Bufpos		old;

    for (;;) {
	DOTsave(&old);

	/* [TRH] Sentence search string is now a user-variable.
	   Also moved end-of-sentence check to the front in order
	   to make backward- and forward scans consistent. */

	if ((new = dosearch(SentEndRE, dir, YES)) == NULL) {
		if (dir < 0)
			ToFirst();
		else
			ToLast();
		break;
	}
	SetDot(new);

	/* Consider match at beginning of line a paragraph boundary, so
	   certainly (probably, hopefully) a sentence boundary.  Otherwise
	   punctuation *must* be followed by whitespace (or end-of-line)
	   to qualify as a true end-of-sentence. */

	if (REbom > 0 && !LookingAt(SentSepRE, linebuf, REeom))
		continue;

	/* Make sure we don't move in the wrong direction. */

	if (dir < 0) /* BACKWARD */{
		cb->b_char = REeom;	/* Avoid problems in Fundamental mode */
		to_word(FORWARD);
		if ((old.p_line == cb->b_dot && old.p_char <= cb->b_char) ||
		    (inorder(new->p_line, new->p_char, old.p_line, old.p_char) &&
		     inorder(old.p_line, old.p_char, cb->b_dot, cb->b_char))) {
			SetDot(new);
			continue;
		}
	}
	else /* FORWARD */ if (REbom == 0) {	/* Assume paragraph boundary */
		line_move(BACKWARD);
		Eol();
		if (old.p_line == cb->b_dot && old.p_char >= cb->b_char) {
			to_word(FORWARD);	/* Oh brother this is painful */
			continue;
		}
	}
	break;
    }
	return new;
}

/* Move over sentences. Bidirectional. */

DEF_CMD( "backward-sentence", Eos, NEGATE );
DEF_CMD( "forward-sentence", Eos, NO )
{
	register Buffer	*cb = curbuf;
	register int	num;
	register int	dir = FORWARD;

	if ((num = exp) == 0)
		return;
	if (num < 0)
		dir = -dir;

	do {
		if (!to_sent(dir))
			break;
	} while (num -= dir);
}

/*======================================================================
 * $Log: move.c,v $
 * Revision 14.30.0.10  1994/05/09  09:04:17  tom
 * ("sentence-end","sentence-sep"): new variables, replace
 *  "sentence-format-string"; (to_sent): use them; (Eos): space-optimization.
 *
 * Revision 14.30.0.5  1993/09/26  23:44:36  tom
 * (ForChar): fix botch in OverWrite mode beyond EOL, introduced in 14.22.
 *
 * Revision 14.30  1993/01/27  01:33:21  tom
 * cleanup whitespace; parenthize || && expr to avoid compiler warnings.
 *
 * Revision 14.26  1992/08/26  23:56:56  tom
 * some optimizations; PRIVATE-ized some Variable defs; add RCS directives.
 *
 */
