微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么vim编辑模式下ctrl-w可以前向删除单词及按键映射的展开

一、问题

在vim的编辑模式下,我之前一直以为只能进行字符的插入操作,但是意外看到可以在编辑模式下通过ctrl-w来前向删除一个单词,并且可以通过ctrl-h来前向删除一个字符。根据通常的ASCII码内置控制方法,通过ctrl-h对应的是ASCII码的BS(backspace)字符,所以通过ctrl-h前向删除单个字符在逻辑上是可以理解的。但是,ctrl-w对应的ASCII码却是ETB (end of trans. blk),这个明显不是标准的前向删除一个单词的意思,所以它是vim内部完成的一个功能。那么vim具体是通过什么方式来完成的呢?是否是内置的标准用法,还是通过编辑器定制的方法

二、vim读到终端输入是什么

这一点很容易确认,通过strace可以看到,当在vim下按下ctrl-w按键时,此时vim通过read读到的就是转换之后的23这个输入。
select(1, [0], [], [0], {0, 0}) = 0 (Timeout)
select(1, [0], [], [0], NULL) = 1 (in [0])
read(0, "\27", 4096) = 1

三、vim对终端的配置格式

通过对比可以看到,vim使用的终端比标准终端添加了下面几个选项
-icrnl
-onlcr
-isig -icanon -iexten -echo -echoe
为了便于查看,将这些字段的意义整理罗列下
[-]icrnl
translate carriage return to newline
* [-]onlcr
translate newline to carriage return-newline
[-]isig
enable interrupt, quit, and suspend special characters
[-]icanon
enable erase, kill, werase, and rprnt special characters
[-]iexten
enable non-POSIX special characters
[-]echo
echo input characters
[-]echoe
same as [-]crterase
[-]crterase
echo erase characters as backspace-space-backspace
这里比较关键的就是其实是关闭了icanon选项,所以终端认的erase、kill、werase内置功能是被禁用的,也就是说,通过ctrl-w删除单词不是终端完成而是有vim自己完成的。

#下面是标准终端的配置
tsecer@harry: stty
speed 38400 baud; line = 0;
-brkint -imaxbel
#下面是vim使用的终端的配置
tsecer@harry: stty -F /dev/pts/3
speed 38400 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl -imaxbel
-onlcr
-isig -icanon -iexten -echo -echoe

四、vim如何完成这个功能

通过查看Vim的源代码,可以知道这个功能是vim的内置功能
edit.c
int
edit(
int cmdchar,
int startln, /* if set, insert at start of line */
long count)
{
……
case Ctrl_W: /* delete word before the cursor */
#ifdef FEAT_JOB_CHANNEL
if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0)
{
// In a prompt window CTRL-W is used for window commands.
// Use Shift-CTRL-W to delete a word.
stuffcharReadbuff(Ctrl_W);
restart_edit = 'A';
nomove = TRUE;
count = 0;
goto doESCkey;
}
#endif
did_backspace = ins_bs(c, BACKSPACE_WORD, &inserted_space);
auto_format(FALSE, TRUE);
break;

case Ctrl_U: /* delete all inserted text in current line */
# ifdef FEAT_COMPL_FUNC
/* CTRL-X CTRL-U completes with 'completefunc'. */
if (ctrl_x_mode == CTRL_X_FUNCTION)
goto docomplete;
# endif
did_backspace = ins_bs(c, BACKSPACE_LINE, &inserted_space);
auto_format(FALSE, TRUE);
inserted_space = FALSE;
break;
……
}

vim中可以通过help index查看所有的内置(按键)功能。其中可以找到关于这个ctrl-w的说明
i_CTRL-U CTRL-U delete all entered characters in the current
line
i_CTRL-V CTRL-V {char} insert next non-digit literally
i_CTRL-V_digit CTRL-V {number} insert three digit decimal number as a single
byte.
i_CTRL-W CTRL-W delete word before the cursor

五、map功能

除了这些内置功能之外,vim和包含了可以定制的键盘映射功能。可以通过imap、vmap、nmap来查看在isert、visual、normal模式下的键盘映射。
在这些map中,可能需要有一些特定的按键,最为常见的就是配合Ctrl按键。所有这些内容可以通过在vim中执行help keycode命令查看:

<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9*

<S-...> shift-key *shift* *<S-*

<C-...> control-key *control* *ctrl* *<C-*

<M-...> alt-key or meta-key *Meta* *alt* *<M-*

<A-...> same as <M-...> *<A-*

<D-...> command-key (Macintosh only) *<D-*
<t_xx> key with "xx" entry in termcap
这里看到,对于修饰符都是单个字符开始的,也就是Ctrl通过C-表示(而不是Ctrl-)表示,通常的Ctrl-W是vim文档中使用的表示方法,它通常也表示在键盘上按下Ctrl的同时按下W键。

六、vim中map的展开

src\getchar.c
static int
vgetorpeek(int advance)
{
……

/*
* Handle ":map <expr>": evaluate the {rhs} as an
* expression. Also save and restore the command line
* for "normal :".
*/
if (mp->m_expr)
{
int save_vgetc_busy = vgetc_busy;

vgetc_busy = 0;
save_m_keys = vim_strsave(mp->m_keys);
save_m_str = vim_strsave(mp->m_str);
s = eval_map_expr(save_m_str, NUL);
vgetc_busy = save_vgetc_busy;
}
else
#endif
s = mp->m_str;
……
i = ins_typebuf(s, noremap,
0, TRUE, cmd_silent || save_m_silent);
……
}
结构的定义
structs.h
/*
* Structure used for mappings and abbreviations.
*/
typedef struct mapblock mapblock_T;
struct mapblock
{
mapblock_T *m_next; /* next mapblock in list */
char_u *m_keys; /* mapped from, lhs */
char_u *m_str; /* mapped to, rhs */
char_u *m_orig_str; /* rhs as entered by the user */
int m_keylen; /* strlen(m_keys) */
int m_mode; /* valid mode */
int m_noremap; /* if non-zero no re-mapping for m_str */
char m_silent; /* <silent> used, don't echo commands */
char m_Nowait; /* <Nowait> used */
#ifdef FEAT_EVAL
char m_expr; /* <expr> used, m_str is an expression */
scid_T m_script_ID; /* ID of script where map was defined */
#endif
};

 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐