update at 2026-07-06 14:20:29
This commit is contained in:
@@ -20,9 +20,9 @@
|
||||
## 功能特性
|
||||
|
||||
✅ **自动切换输入法**:在 Normal 模式和 Insert 模式之间切换时自动切换输入法
|
||||
✅ **智能状态记忆**:记住上次 Insert 模式的输入法状态,下次自动恢复
|
||||
✅ **异步处理**:使用 `job_start()` 异步执行,避免 UI 阻塞
|
||||
✅ **中英混合友好**:完美支持中英文混合输入场景
|
||||
✅ **提前保存状态**:使用 `InsertLeavePre` 在离开 Insert 前保存输入法状态
|
||||
✅ **异步静默处理**:使用 `job_start()` 异步执行,并屏蔽命令输出,避免阻塞和乱码
|
||||
✅ **macOS 稳定恢复**:进入 Insert 后延迟恢复中文输入源,并进行一次兜底重试
|
||||
|
||||
## 安装要求
|
||||
|
||||
@@ -89,11 +89,9 @@ Plug 'yourusername/vim-im-switch', { 'rtp': 'vim-im-switch-select.vim' }
|
||||
" 英文输入法 ID(默认值)
|
||||
let g:imselect_english_im = 'com.apple.keylayout.ABC'
|
||||
|
||||
" 中文输入法 ID(可选,插件会自动检测)
|
||||
" 例如:搜狗输入法
|
||||
" let g:imselect_chinese_im = 'com.sogou.inputmethod.sogou'
|
||||
" 例如:系统自带简体拼音
|
||||
" let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
|
||||
" 中文输入法 ID(默认值:系统自带简体拼音)
|
||||
" macOS 更新后建议显式配置,不依赖自动检测
|
||||
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
|
||||
```
|
||||
|
||||
### 如何获取输入法 ID
|
||||
@@ -117,15 +115,17 @@ im-select
|
||||
插件安装后会自动工作,无需手动操作:
|
||||
|
||||
1. **进入 Insert 模式** (`i`, `a`, `o` 等)
|
||||
- 自动恢复上次保存的输入法状态
|
||||
- 延迟切换到中文输入法
|
||||
- 约 200ms 后再次确认中文输入法,降低 macOS 状态未刷新导致的偶发失败
|
||||
|
||||
2. **退出 Insert 模式** (`ESC`)
|
||||
- 保存当前输入法状态
|
||||
- 自动切换到英文输入法
|
||||
- 在 `InsertLeavePre` 提前保存当前输入法状态
|
||||
- 在 `InsertLeave` 自动切换到英文输入法
|
||||
|
||||
3. **中英文混合输入**
|
||||
- 插件会记住你每次退出 Insert 模式时的输入法
|
||||
- 下次进入时自动恢复
|
||||
3. **macOS 行为说明**
|
||||
- `im-select` 控制的是 macOS 输入源 ID
|
||||
- 新版 macOS 中文输入法可能存在“图标为中文但内部仍输入英文”的状态
|
||||
- 当前稳定版不模拟 `Ctrl-Space` / `Caps Lock`,避免快捷键被 Vim 接收导致退出 Insert 模式
|
||||
|
||||
## 测试
|
||||
|
||||
@@ -161,7 +161,8 @@ im-select
|
||||
7. 按 `ESC` 退出到 Normal 模式
|
||||
|
||||
8. 再次按 `i` 进入 Insert 模式
|
||||
- 观察输入法是否保持英文
|
||||
- 当前稳定版会恢复到配置的中文输入法
|
||||
- 如果需要保持英文,请临时禁用插件或调整 `g:imselect_chinese_im` 策略
|
||||
|
||||
## 调试
|
||||
|
||||
@@ -219,8 +220,21 @@ im-select
|
||||
|
||||
### 输入法切换延迟
|
||||
|
||||
- 这是正常的,因为使用了异步处理
|
||||
- 通常延迟小于 100ms,不影响使用
|
||||
- 这是正常的,因为进入 Insert 后使用了 `timer_start()` 延迟恢复
|
||||
- 当前稳定版会在进入 Insert 后约 80ms 切中文,并在约 200ms 后兜底确认
|
||||
- 如果机器较慢或 macOS 状态刷新较慢,可以在插件中适当调大延迟值
|
||||
|
||||
### 输入法图标是中文但实际输入英文
|
||||
|
||||
这是新版 macOS 中文输入法可能出现的内部状态问题:`im-select` 已经把输入源切到中文,但中文输入法内部仍处于英文输入状态。
|
||||
|
||||
当前稳定版采取的处理方式:
|
||||
|
||||
1. 不模拟 `Ctrl-Space`、`Caps Lock`、`Shift` 等按键,避免这些按键被 Vim 接收后影响模式。
|
||||
2. 进入 Insert 后延迟切换中文输入源。
|
||||
3. 再进行一次中文输入源兜底确认。
|
||||
|
||||
如果仍偶发出现该问题,优先调整延迟时间,不建议重新加入系统快捷键模拟。
|
||||
|
||||
### 输入法 ID 不正确
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ endif
|
||||
if !executable("im-select")
|
||||
finish
|
||||
endif
|
||||
let s:imselect_cmd = exepath('im-select')
|
||||
let s:keepcpo = &cpo
|
||||
let g:imselect_loaded = 1
|
||||
set cpo&vim
|
||||
@@ -38,96 +39,89 @@ if !exists('g:imselect_english_im')
|
||||
endif
|
||||
|
||||
if !exists('g:imselect_chinese_im')
|
||||
let g:imselect_chinese_im = 'auto-detect' " 将自动检测
|
||||
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
|
||||
endif
|
||||
|
||||
" ---------------------------------------------------------------------
|
||||
" Functions:
|
||||
|
||||
" 离开 Insert 模式:保存当前输入法名称,然后切换到英文
|
||||
function! IMSelect2en()
|
||||
" 保存当前输入法名称
|
||||
let current_im = substitute(system("im-select 2>/dev/null"), '\n', '', 'g')
|
||||
let g:imselect_last_insert_im_name = current_im
|
||||
|
||||
" 切换到英文输入法(使用后台任务并重定向所有输出)
|
||||
function! IMSelectCurrentIM() abort
|
||||
return substitute(system(shellescape(s:imselect_cmd) . ' 2>/dev/null'), '[\r\n]', '', 'g')
|
||||
endfunction
|
||||
|
||||
function! IMSelectSwitch(im) abort
|
||||
if has('job')
|
||||
" Use nohup in a shell backgrounded process. Some Vim builds don't accept
|
||||
" the 'detach' option; calling via sh -lc with nohup/& keeps process
|
||||
" disassociated while avoiding unsupported job_start options.
|
||||
call job_start(['sh', '-lc', 'nohup im-select ' . shellescape(g:imselect_english_im) . ' >/dev/null 2>&1 &'])
|
||||
silent! call job_start([s:imselect_cmd, a:im], {'out_io': 'null', 'err_io': 'null'})
|
||||
else
|
||||
" Fallback: nohup in background
|
||||
call system('nohup im-select ' . shellescape(g:imselect_english_im) . ' >/dev/null 2>&1 &')
|
||||
silent! call system(shellescape(s:imselect_cmd) . ' ' . shellescape(a:im) . ' >/dev/null 2>&1 &')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" 进入 Insert 模式:根据保存的输入法名称恢复
|
||||
function! IMSelect2zh()
|
||||
" 如果上次保存的输入法名称为空,说明是第一次进入,保持英文
|
||||
function! IMSelectStart() abort
|
||||
call IMSelectSwitch(g:imselect_english_im)
|
||||
endfunction
|
||||
|
||||
function! IMSelectEnsureTarget(timer) abort
|
||||
if g:imselect_last_insert_im_name != ''
|
||||
call IMSelectSwitch(g:imselect_last_insert_im_name)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" 离开 Insert 模式前:保存当前输入法名称
|
||||
function! IMSelectRemember() abort
|
||||
let current_im = IMSelectCurrentIM()
|
||||
if current_im != ''
|
||||
let g:imselect_last_insert_im_name = current_im
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" 离开 Insert 模式:切换到英文
|
||||
function! IMSelect2en() abort
|
||||
call IMSelectSwitch(g:imselect_english_im)
|
||||
endfunction
|
||||
|
||||
" 恢复 Insert 模式输入法
|
||||
function! IMSelectRestore(timer) abort
|
||||
if g:imselect_last_insert_im_name == ''
|
||||
return
|
||||
endif
|
||||
|
||||
" 如果上次是英文输入法,保持英文(不需要做任何事)
|
||||
if g:imselect_last_insert_im_name == g:imselect_english_im
|
||||
return
|
||||
endif
|
||||
|
||||
" 上次是中文输入法,恢复到那个输入法(使用后台任务并重定向所有输出)
|
||||
if has('job')
|
||||
call job_start(['sh', '-lc', 'nohup im-select ' . shellescape(g:imselect_last_insert_im_name) . ' >/dev/null 2>&1 &'])
|
||||
else
|
||||
call system('nohup im-select ' . shellescape(g:imselect_last_insert_im_name) . ' >/dev/null 2>&1 &')
|
||||
let g:imselect_last_insert_im_name = g:imselect_chinese_im
|
||||
endif
|
||||
call IMSelectSwitch(g:imselect_last_insert_im_name)
|
||||
call timer_start(200, function('IMSelectEnsureTarget'))
|
||||
endfunction
|
||||
|
||||
" 进入 Insert 模式:延迟恢复保存的输入法
|
||||
function! IMSelect2zh() abort
|
||||
call timer_start(80, function('IMSelectRestore'))
|
||||
endfunction
|
||||
" ---------------------------------------------------------------------
|
||||
" Autocmds:
|
||||
|
||||
" 进入 Insert 模式一次后的处理
|
||||
function! IMSelect2zhOnce()
|
||||
call IMSelect2zh()
|
||||
call UnBindIMSelectAu()
|
||||
endfunction
|
||||
|
||||
function! BindIMSelectAu2zhOnce()
|
||||
augroup IMSelect
|
||||
au InsertEnter * call IMSelect2zhOnce()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
" 绑定自动命令
|
||||
function! BindIMSelectAu()
|
||||
augroup IMSelect
|
||||
" 离开 Insert 模式:保存输入法状态并切换到英文
|
||||
au InsertLeave * call IMSelect2en()
|
||||
autocmd!
|
||||
" 离开 Insert 模式前:先保存输入法状态
|
||||
autocmd InsertLeavePre * call IMSelectRemember()
|
||||
" 离开 Insert 模式:切换到英文
|
||||
autocmd InsertLeave * call IMSelect2en()
|
||||
" 进入 Insert 模式:恢复上次的输入法状态
|
||||
au InsertEnter * call IMSelect2zh()
|
||||
" Vim 启动时:切换到英文
|
||||
au VimEnter * call IMSelect2en()
|
||||
autocmd InsertEnter * call IMSelect2zh()
|
||||
" Vim 启动后:切换到英文
|
||||
autocmd VimEnter * call timer_start(100, {-> IMSelectStart()})
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! UnBindIMSelectAu()
|
||||
au! IMSelect InsertLeave *
|
||||
au! IMSelect InsertEnter *
|
||||
augroup IMSelect
|
||||
autocmd!
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
" 延迟初始化:首次进入 Insert 模式时才绑定自动命令
|
||||
let g:imselect_called_bind = 0
|
||||
function! IMSelectEchoBind()
|
||||
if (g:imselect_called_bind==0)
|
||||
call BindIMSelectAu()
|
||||
endif
|
||||
let g:imselect_called_bind = 1
|
||||
endfunction
|
||||
|
||||
autocmd InsertEnter * call IMSelectEchoBind()
|
||||
call BindIMSelectAu()
|
||||
|
||||
" 多光标支持:在选择多个光标前后的处理
|
||||
function! IMSelect_multiple_cursors_before()
|
||||
call UnBindIMSelectAu()
|
||||
call BindIMSelectAu2zhOnce()
|
||||
endfunction
|
||||
|
||||
function! IMSelect_multiple_cursors_after()
|
||||
|
||||
@@ -43,90 +43,61 @@ endif
|
||||
" ---------------------------------------------------------------------
|
||||
" Functions:
|
||||
|
||||
function! FcitxCurrentIM() abort
|
||||
return substitute(system('fcitx-remote -n 2>/dev/null'), '[\r\n]', '', 'g')
|
||||
endfunction
|
||||
|
||||
function! FcitxSwitch(im) abort
|
||||
silent! call system('fcitx-remote -s ' . shellescape(a:im) . ' >/dev/null 2>&1')
|
||||
endfunction
|
||||
|
||||
function! FcitxStart() abort
|
||||
call FcitxSwitch(g:fcitx_english_im)
|
||||
endfunction
|
||||
|
||||
" 离开 Insert 模式:保存当前输入法名称,然后切换到英文
|
||||
function! Fcitx2en()
|
||||
" 保存当前输入法名称
|
||||
let current_im = substitute(system("fcitx-remote -n 2>/dev/null"), '\n', '', 'g')
|
||||
let g:fcitx_last_insert_im_name = current_im
|
||||
|
||||
" 切换到英文输入法(使用后台任务并重定向所有输出)
|
||||
if has('job')
|
||||
" Use nohup in a shell backgrounded process. Some Vim builds don't accept
|
||||
" the 'detach' option; calling via sh -lc with nohup/& keeps process
|
||||
" disassociated while avoiding unsupported job_start options.
|
||||
call job_start(['sh', '-lc', 'nohup fcitx-remote -s ' . shellescape(g:fcitx_english_im) . ' >/dev/null 2>&1 &'])
|
||||
else
|
||||
" Fallback: nohup in background
|
||||
call system('nohup fcitx-remote -s ' . shellescape(g:fcitx_english_im) . ' >/dev/null 2>&1 &')
|
||||
function! Fcitx2en() abort
|
||||
let current_im = FcitxCurrentIM()
|
||||
if current_im != ''
|
||||
let g:fcitx_last_insert_im_name = current_im
|
||||
endif
|
||||
call FcitxSwitch(g:fcitx_english_im)
|
||||
endfunction
|
||||
|
||||
" 进入 Insert 模式:根据保存的输入法名称恢复
|
||||
function! Fcitx2zh()
|
||||
" 如果上次保存的输入法名称为空,说明是第一次进入,保持英文
|
||||
if g:fcitx_last_insert_im_name == ''
|
||||
function! Fcitx2zh() abort
|
||||
if g:fcitx_last_insert_im_name == '' || g:fcitx_last_insert_im_name == g:fcitx_english_im
|
||||
return
|
||||
endif
|
||||
|
||||
" 如果上次是英文输入法,保持英文(不需要做任何事)
|
||||
if g:fcitx_last_insert_im_name == g:fcitx_english_im
|
||||
return
|
||||
endif
|
||||
|
||||
" 上次是中文输入法,恢复到那个输入法(使用后台任务并重定向所有输出)
|
||||
if has('job')
|
||||
call job_start(['sh', '-lc', 'nohup fcitx-remote -s ' . shellescape(g:fcitx_last_insert_im_name) . ' >/dev/null 2>&1 &'])
|
||||
else
|
||||
call system('nohup fcitx-remote -s ' . shellescape(g:fcitx_last_insert_im_name) . ' >/dev/null 2>&1 &')
|
||||
endif
|
||||
call FcitxSwitch(g:fcitx_last_insert_im_name)
|
||||
endfunction
|
||||
" ---------------------------------------------------------------------
|
||||
" Autocmds:
|
||||
|
||||
" 进入 Insert 模式一次后的处理
|
||||
function! Fcitx2zhOnce()
|
||||
call Fcitx2zh()
|
||||
call UnBindAu()
|
||||
endfunction
|
||||
|
||||
function! BindAu2zhOnce()
|
||||
augroup Fcitx
|
||||
au InsertEnter * call Fcitx2zhOnce()
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
" 绑定自动命令
|
||||
function! BindAu()
|
||||
augroup Fcitx
|
||||
autocmd!
|
||||
" 离开 Insert 模式:保存输入法状态并切换到英文
|
||||
au InsertLeave * call Fcitx2en()
|
||||
autocmd InsertLeave * call Fcitx2en()
|
||||
" 进入 Insert 模式:恢复上次的输入法状态
|
||||
au InsertEnter * call Fcitx2zh()
|
||||
" Vim 启动时:切换到英文
|
||||
au VimEnter * call Fcitx2en()
|
||||
autocmd InsertEnter * call Fcitx2zh()
|
||||
" Vim 启动后:切换到英文
|
||||
autocmd VimEnter * call timer_start(100, {-> FcitxStart()})
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! UnBindAu()
|
||||
au! Fcitx InsertLeave *
|
||||
au! Fcitx InsertEnter *
|
||||
augroup Fcitx
|
||||
autocmd!
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
" 延迟初始化:首次进入 Insert 模式时才绑定自动命令
|
||||
let g:called_bind = 0
|
||||
function! EchoBind()
|
||||
if (g:called_bind==0)
|
||||
call BindAu()
|
||||
endif
|
||||
let g:called_bind = 1
|
||||
endfunction
|
||||
|
||||
autocmd InsertEnter * call EchoBind()
|
||||
call BindAu()
|
||||
|
||||
" 多光标支持:在选择多个光标前后的处理
|
||||
function! Multiple_cursors_before()
|
||||
call UnBindAu()
|
||||
call BindAu2zhOnce()
|
||||
endfunction
|
||||
|
||||
function! Multiple_cursors_after()
|
||||
Reference in New Issue
Block a user