📄 getchar.c
字号:
/* 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 + -