⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 getchar.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * getchar.c
 *
 * functions related with getting a character from the user/mapping/redo/...
 *
 * manipulations with redo buffer and stuff buffer
 * mappings and abbreviations
 */

#include "vim.h"

/*
 * structure used to store one block of the stuff/redo/macro buffers
 */
struct bufblock
{
	struct bufblock *b_next;	/* pointer to next bufblock */
	char_u		b_str[1];	/* contents (actually longer) */
};

#define MINIMAL_SIZE 20			/* minimal size for b_str */

/*
 * header used for the stuff buffer and the redo buffer
 */
struct buffheader
{
	struct bufblock bh_first;	/* first (dummy) block of list */
	struct bufblock *bh_curr;	/* bufblock for appending */
	int		bh_index;	/* index for reading */
	int		bh_space;	/* space in bh_curr for appending */
};

static struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader old_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
#if defined(AUTOCMD) || defined(WANT_EVAL) || defined(PROTO)
static struct buffheader save_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
static struct buffheader save_old_redobuff = {{NULL, {NUL}}, NULL, 0, 0};
#endif
static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};

/*
 * when block_redo is TRUE redo buffer will not be changed
 * used by edit() to repeat insertions and 'V' command for redoing
 */
static int	block_redo = FALSE;

/*
 * structure used for mapping
 */
struct mapblock
{
    struct mapblock *m_next;	    /* next mapblock in list */
    char_u	    *m_keys;	    /* mapped from */
    int		     m_keylen;	    /* strlen(m_keys) */
    char_u	    *m_str;	    /* mapped to */
    int		     m_mode;	    /* valid mode */
    int		     m_noremap;	    /* if non-zero no re-mapping for m_str */
};

/*
 * Make a hash value for a mapping.
 * "mode" is the lower 4 bits of the State for the mapping.
 * "c1" is the first character of the "lhs".
 * Returns a value between 0 and 255, index in maphash.
 * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode.
 */
#define MAP_HASH(mode, c1) (((mode) & (NORMAL + VISUAL + OP_PENDING)) ? (c1) : ((c1) ^ 0x80))

/*
 * Each mapping is put in one of the 256 hash lists, to speed up finding it.
 */
static struct mapblock	*(maphash[256]);
static int		maphash_valid = FALSE;

/*
 * List used for abbreviations.
 */
static struct mapblock *first_abbr = NULL; /* first entry in abbrlist */

/*
 * variables used by vgetorpeek() and flush_buffers()
 *
 * typebuf[] contains all characters that are not consumed yet.
 * typebuf[typeoff] is the first valid character in typebuf[].
 * typebuf[typeoff + typelen - 1] is the last valid char.
 * typebuf[typeoff + typelen] must be NUL.
 * The part in front may contain the result of mappings, abbreviations and
 * @a commands. The length of this part is typemaplen.
 * After it are characters that come from the terminal.
 * no_abbr_cnt is the number of characters in typebuf that should not be
 * considered for abbreviations.
 * Some parts of typebuf may not be mapped. These parts are remembered in
 * noremapbuf, which is the same length as typebuf and contains TRUE for the
 * characters that are not to be remapped. noremapbuf[typeoff] is the first
 * valid flag.
 * (typebuf has been put in globals.h, because check_termcode() needs it).
 */
static char_u	*noremapbuf = NULL;	  /* flags for typeahead characters */
/* typebuf has three parts: room in front (for result of mappings), the middle
 * for typeahead and room for new characters (which needs to be 3 * MAXMAPLEN)
 * for the Amiga).
 */
#define TYPELEN_INIT	(5 * (MAXMAPLEN + 3))
static char_u	typebuf_init[TYPELEN_INIT];	    /* initial typebuf */
static char_u	noremapbuf_init[TYPELEN_INIT];	    /* initial noremapbuf */

static int	typemaplen = 0;	    /* nr of mapped characters in typebuf */
static int	no_abbr_cnt = 0;    /* nr of chars without abbrev. in typebuf */
static int	last_recorded_len = 0;	/* number of last recorded chars */

static void	free_buff __ARGS((struct buffheader *));
static char_u	*get_bufcont __ARGS((struct buffheader *, int));
static void	add_buff __ARGS((struct buffheader *, char_u *));
static void	add_num_buff __ARGS((struct buffheader *, long));
static void	add_char_buff __ARGS((struct buffheader *, int));
static int	read_stuff __ARGS((int));
static void	start_stuff __ARGS((void));
static int	read_redo __ARGS((int, int));
static void	copy_redo __ARGS((int));
static void	init_typebuf __ARGS((void));
static void	gotchars __ARGS((char_u *, int));
static void	may_sync_undo __ARGS((void));
static void	closescript __ARGS((void));
static int	vgetorpeek __ARGS((int));
static void	map_free __ARGS((struct mapblock **));
static void	validate_maphash __ARGS((void));
static void	showmap __ARGS((struct mapblock *));

/*
 * free and clear a buffer
 */
    static void
free_buff(buf)
    struct buffheader *buf;
{
	struct bufblock	    *p, *np;

	for (p = buf->bh_first.b_next; p != NULL; p = np)
	{
		np = p->b_next;
		vim_free(p);
	}
	buf->bh_first.b_next = NULL;
}

/*
 * return the contents of a buffer as a single string
 */
    static char_u *
get_bufcont(buffer, dozero)
    struct buffheader	*buffer;
    int			dozero;	    /* count == zero is not an error */
{
    long_u	    count = 0;
    char_u	    *p = NULL;
    char_u	    *p2;
    char_u	    *str;
    struct bufblock *bp;

/* compute the total length of the string */
    for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
	count += STRLEN(bp->b_str);

    if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL)
    {
	p2 = p;
	for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
	    for (str = bp->b_str; *str; )
		*p2++ = *str++;
	*p2 = NUL;
    }
    return (p);
}

/*
 * return the contents of the record buffer as a single string
 *  and clear the record buffer
 */
    char_u *
get_recorded()
{
    char_u *p;
    size_t  len;

    p = get_bufcont(&recordbuff, TRUE);
    free_buff(&recordbuff);
    /*
     * Remove the characters that were added the last time, these must be the
     * (possibly mapped) characters that stopped recording.
     */
    len = STRLEN(p);
    if ((int)len >= last_recorded_len)
	p[len - last_recorded_len] = NUL;
    return (p);
}

/*
 * return the contents of the redo buffer as a single string
 */
    char_u *
get_inserted()
{
    return(get_bufcont(&redobuff, FALSE));
}

/*
 * add string "s" after the current block of buffer "buf"
 */
    static void
add_buff(buf, s)
    struct buffheader	*buf;
    char_u		*s;
{
    struct bufblock *p;
    long_u	    n;
    long_u	    len;

    if ((n = STRLEN(s)) == 0)		    /* don't add empty strings */
	return;

    if (buf->bh_first.b_next == NULL)	    /* first add to list */
    {
	buf->bh_space = 0;
	buf->bh_curr = &(buf->bh_first);
    }
    else if (buf->bh_curr == NULL)	    /* buffer has already been read */
    {
	EMSG("Add to read buffer");
	return;
    }
    else if (buf->bh_index != 0)
	STRCPY(buf->bh_first.b_next->b_str,
				 buf->bh_first.b_next->b_str + buf->bh_index);
    buf->bh_index = 0;

    if (buf->bh_space >= (int)n)
    {
	strcat((char *)buf->bh_curr->b_str, (char *)s);
	buf->bh_space -= n;
    }
    else
    {
	if (n < MINIMAL_SIZE)
	    len = MINIMAL_SIZE;
	else
	    len = n;
	p = (struct bufblock *)lalloc((long_u)(sizeof(struct bufblock) + len),
									TRUE);
	if (p == NULL)
	    return; /* no space, just forget it */
	buf->bh_space = len - n;
	STRCPY(p->b_str, s);

	p->b_next = buf->bh_curr->b_next;
	buf->bh_curr->b_next = p;
	buf->bh_curr = p;
    }
    return;
}

    static void
add_num_buff(buf, n)
    struct buffheader *buf;
    long	      n;
{
	char_u	number[32];

	sprintf((char *)number, "%ld", n);
	add_buff(buf, number);
}

    static void
add_char_buff(buf, c)
    struct buffheader *buf;
    int		      c;
{
    char_u  temp[4];

    /*
     * translate special key code into three byte sequence
     */
    if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL)
    {
	temp[0] = K_SPECIAL;
	temp[1] = K_SECOND(c);
	temp[2] = K_THIRD(c);
	temp[3] = NUL;
    }
    else
    {
	temp[0] = c;
	temp[1] = NUL;
    }
    add_buff(buf, temp);
}

/*
 * get one character from the stuff buffer
 * If advance == TRUE go to the next char.
 */
    static int
read_stuff(advance)
    int		advance;
{
    char_u		c;
    struct bufblock	*curr;


    if (stuffbuff.bh_first.b_next == NULL)  /* buffer is empty */
	return NUL;

    curr = stuffbuff.bh_first.b_next;
    c = curr->b_str[stuffbuff.bh_index];

    if (advance)
    {
	if (curr->b_str[++stuffbuff.bh_index] == NUL)
	{
	    stuffbuff.bh_first.b_next = curr->b_next;
	    vim_free(curr);
	    stuffbuff.bh_index = 0;
	}
    }
    return c;
}

/*
 * prepare stuff buffer for reading (if it contains something)
 */
    static void
start_stuff()
{
    if (stuffbuff.bh_first.b_next != NULL)
    {
	stuffbuff.bh_curr = &(stuffbuff.bh_first);
	stuffbuff.bh_space = 0;
    }
}

/*
 * check if the stuff buffer is empty
 */
    int
stuff_empty()
{
    return (stuffbuff.bh_first.b_next == NULL);
}

/*
 * Remove the contents of the stuff buffer and the mapped characters in the
 * typeahead buffer (used in case of an error). If 'typeahead' is true,
 * flush all typeahead characters (used when interrupted by a CTRL-C).
 */
    void
flush_buffers(typeahead)
    int typeahead;
{
    init_typebuf();

    start_stuff();
    while (read_stuff(TRUE) != NUL)
	;

    if (typeahead)	    /* remove all typeahead */
    {
	/*
	 * We have to get all characters, because we may delete the first part
	 * of an escape sequence.
	 * In an xterm we get one char at a time and we have to get them all.
	 */
	while (inchar(typebuf, typebuflen - 1, 10L))
	    ;
	typeoff = MAXMAPLEN;
	typelen = 0;
    }
    else		    /* remove mapped characters only */
    {
	typeoff += typemaplen;
	typelen -= typemaplen;
    }
    typemaplen = 0;
    no_abbr_cnt = 0;
}

/*
 * The previous contents of the redo buffer is kept in old_redobuffer.
 * This is used for the CTRL-O <.> command in insert mode.
 */
    void
ResetRedobuff()
{
    if (!block_redo)
    {
	free_buff(&old_redobuff);
	old_redobuff = redobuff;
	redobuff.bh_first.b_next = NULL;
    }
}

#if defined(AUTOCMD) || defined(WANT_EVAL) || defined(PROTO)
/*
 * Save redobuff and old_redobuff to save_redobuff and save_old_redobuff.
 * Used before executing autocommands and user functions.
 */
static int save_level = 0;

    void
saveRedobuff()
{
    if (save_level++ == 0)
    {
	save_redobuff = redobuff;
	redobuff.bh_first.b_next = NULL;
	save_old_redobuff = old_redobuff;
	old_redobuff.bh_first.b_next = NULL;
    }
}

/*
 * Restore redobuff and old_redobuff from save_redobuff and save_old_redobuff.
 * Used after executing autocommands and user functions.
 */
    void
restoreRedobuff()
{
    if (--save_level == 0)
    {
	free_buff(&redobuff);
	redobuff = save_redobuff;
	free_buff(&old_redobuff);
	old_redobuff = save_old_redobuff;
    }
}
#endif

    void
AppendToRedobuff(s)
    char_u	   *s;
{
    if (!block_redo)
	add_buff(&redobuff, s);
}

    void
AppendCharToRedobuff(c)
    int		   c;
{
    if (!block_redo)
	add_char_buff(&redobuff, c);
}

    void
AppendNumberToRedobuff(n)
    long	    n;
{
    if (!block_redo)
	add_num_buff(&redobuff, n);
}

    void
stuffReadbuff(s)
    char_u	   *s;
{
    add_buff(&stuffbuff, s);
}

    void
stuffcharReadbuff(c)
    int		   c;
{
    add_char_buff(&stuffbuff, c);
}

    void
stuffnumReadbuff(n)
    long    n;
{
    add_num_buff(&stuffbuff, n);
}

/*
 * Read a character from the redo buffer.
 * The redo buffer is left as it is.
 * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK
 * otherwise
 * if old is TRUE, use old_redobuff instead of redobuff
 */
    static int
read_redo(init, old_redo)
    int		init;
    int		old_redo;
{
    static struct bufblock  *bp;
    static char_u	    *p;
    int			    c;

    if (init)
    {
	if (old_redo)
	    bp = old_redobuff.bh_first.b_next;
	else
	    bp = redobuff.bh_first.b_next;
	if (bp == NULL)
	    return FAIL;
	p = bp->b_str;
	return OK;
    }
    if ((c = *p) != NUL)
    {
	if (c == K_SPECIAL)
	{
	    c = TO_SPECIAL(p[1], p[2]);
	    p += 2;
	}
	if (*++p == NUL && bp->b_next != NULL)
	{
	    bp = bp->b_next;
	    p = bp->b_str;
	}
    }
    return c;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -