vim无法打中文_vim 中文输入解决方案

本文介绍了如何利用smartim插件和neoclide-client改进Vim的用户体验,实现在保持中文输入法的同时无缝切换到插入模式,以及通过系统接口监控输入法状态。作者还分享了针对Mac的输入法管理和模式监听的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

做为一个 vimer,想必你也有有过这样的遭遇:在 normal 模式下输入 a 想进入插入模式,结果却发现当前输入法处于中文输入状态,于是进入了尴尬的组合输入状态:

停顿半秒之后你敲了下 delete 然后切回英文输入按下 a 进入插入模块,再切换回中文状态开始正常的中文输入。

难道说 vim 不能更聪明些吗?

当然可以,首先要介绍下 smartim 这个插件,它可以在离开插入状态记录同时切换到默认输入法,然后在下次进入插入模式后自动切换为上次插入状态使用的输入法。我们还可以做的更好一些:按键时判定输入状态,如果是 normal 模块并且处于输入法状态,则将按键值直接发送给 vim 同时阻止原来的输入法生效,这样我们就能做到保持中文输入法同时直接进入插入模式。

我使用了基于 electron 和 neovim 提供的 RPC 调用实现的 neoclide-client 这个模块来实现这个功能。

监控当前系统输入法

进行系统调用获取输入法是有时间消耗的,如果每次 normal 模式按键都去获取则必然导致输入的延迟,首先想到的做法是监听 input 元素的 compositionstart 和 compositionend 来判定输入法状态,然而这种办法并不可行,因为 compositionstart 事件实在 keyDown 之后才会触发,此时输入法已经开始起作用了,而我们必须在 keyDown 时获取到当前的输入法状态。所以需要系统提供对应的接口,keyboard-layout 这个模块为我们提供了一个监听接口,只需要简单调用就可以做到同步输入法:

let keyboardLayout = ''

KeyboardLayout.observeCurrentKeyboardLayout(layout => {

keyboardLayout = layout

const ev = new CustomEvent('layoutChange', {

detail: layout

})

window.dispatchEvent(ev) // 便于其它模块监听})

export function imeRunning() {

return keyboardLayout && keyboardLayout !== 'com.apple.keylayout.US'

}

仅做了针对 Mac 的处理

设置系统输入法

因为没找到可用的 node 模块,所以我做了 imselect 这个使用了一点 Object-C 的 node 原生模块。

export function defaultIM() {

if (keyboardLayout && keyboardLayout !== 'com.apple.keylayout.US') {

imselect.selectMethod()

return true

}

return false

}

在 onKeyDown 事件使用的代码:

if (mode == 'normal' && imeRunning() &&

!ctrlKey &&

!metaKey &&

!altKey) {

event.preventDefault()

if (['a', 'A', 'i', 'I', 'o', 'O'].indexOf(event.key) === -1) {

setImmediate(() => defaultIM())

}

this.inputToNeovim(event.key, event)

return

}

监听 vim 模式变化

我们希望 vim 在 normal 模式下总是自动切换到系统的输入法,然而 vim 仅提供了 InsertLeave,并没有的 CmdlineLeave 事件让我们监听,譬如说我们使用 / 搜索中文,回到 normal 模式还会是中文输入法状态。neovim 提供的 RPC 接口也不会给我们返回 cmdline 这个状态,所以我暂时只能对 neovim 的源码做一点修改:

diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.cindex 56b41f1..9178538 100644--- a/src/nvim/api/ui.c+++ b/src/nvim/api/ui.c@@ -271,6 +271,8 @@ static void remote_ui_mode_change(UI *ui, int mode) ADD(args, STRING_OBJ(cstr_to_string("insert")));

} else if (mode == REPLACE) {

ADD(args, STRING_OBJ(cstr_to_string("replace")));

+ } else if (mode == CMDLINE) {+ ADD(args, STRING_OBJ(cstr_to_string("cmdline"))); } else {

assert(mode == NORMAL);

ADD(args, STRING_OBJ(cstr_to_string("normal")));

d然iff --git a/src/nvim/ui.c b/src/nvim/ui.c

index eb50041..3e31b90 100644--- a/src/nvim/ui.c+++ b/src/nvim/ui.c@@ -537,6 +537,8 @@ static void ui_mode_change(void) mode = REPLACE;

else if (State & INSERT)

mode = INSERT;

+ else if (State & CMDLINE)+ mode = CMDLINE; else

mode = NORMAL;

UI_CALL(mode_change, mode);

然后监听 RPC 传来的 mode_change 事件即可。

更新: 这部分代码已经合并到 neovim 的 master 分支上了

Focus 事件监听

Mac 提供了针对应用的输入法记录功能,可以自动还原 app 之前的输入法状态,建议开启。

记录搜索模式输入法

通过在 onKeyDown 中我们判定 event.key (这是个比较新的 API, 很多浏览器并不支持) 为 ‘?’ 或者 ‘/’ 同时模式为 normal 可以判定 vim 即将进入搜索模式,监听 mode_change 可在模式变为其它模式时保存当前输入法,下次进入后自动复原。

p.on('mode_change', mode => {

const {searching} = proxy

const curMode = proxy.mode

if (mode != 'cmdline' && searching) {

util.saveCommandIm()

store.dispatch(A.toggleSearch(false))

}

if (curMode != 'insert' && mode == 'normal') {

// works with smartim util.defaultIM()

}

if (mode == 'cmdline' && searching) {

util.selectCommandIm()

}

store.dispatch(A.changeMode(mode))

})

醒目颜色提醒

我们还需要更加便捷的知道当前所处的输入法状态,所以我使用了贴心的黄色鼠标背景:

neoclide-client 这个模块尽管已经完成,但它实际只是为了neoclide 提供编辑的模块,更多的主要功能还在开发中 :p

Happy vimming!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值