update at 2026-07-06 14:20:29

This commit is contained in:
douboer
2026-07-06 14:20:29 +08:00
parent 58179424cf
commit 995d672f4a
3 changed files with 114 additions and 135 deletions

View File

@@ -20,9 +20,9 @@
## 功能特性 ## 功能特性
**自动切换输入法**:在 Normal 模式和 Insert 模式之间切换时自动切换输入法 **自动切换输入法**:在 Normal 模式和 Insert 模式之间切换时自动切换输入法
**智能状态记忆**:记住上次 Insert 模式的输入法状态,下次自动恢复 **提前保存状态**:使用 `InsertLeavePre` 在离开 Insert 前保存输入法状态
**异步处理**:使用 `job_start()` 异步执行,避免 UI 阻塞 **异步静默处理**:使用 `job_start()` 异步执行,并屏蔽命令输出,避免阻塞和乱码
**中英混合友好**:完美支持中英文混合输入场景 **macOS 稳定恢复**:进入 Insert 后延迟恢复中文输入源,并进行一次兜底重试
## 安装要求 ## 安装要求
@@ -89,11 +89,9 @@ Plug 'yourusername/vim-im-switch', { 'rtp': 'vim-im-switch-select.vim' }
" 英文输入法 ID默认值 " 英文输入法 ID默认值
let g:imselect_english_im = 'com.apple.keylayout.ABC' let g:imselect_english_im = 'com.apple.keylayout.ABC'
" 中文输入法 ID可选,插件会自动检测 " 中文输入法 ID默认值:系统自带简体拼音
" 例如:搜狗输入法 " macOS 更新后建议显式配置,不依赖自动检测
" let g:imselect_chinese_im = 'com.sogou.inputmethod.sogou' let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
" 例如:系统自带简体拼音
" let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
``` ```
### 如何获取输入法 ID ### 如何获取输入法 ID
@@ -117,15 +115,17 @@ im-select
插件安装后会自动工作,无需手动操作: 插件安装后会自动工作,无需手动操作:
1. **进入 Insert 模式** (`i`, `a`, `o` 等) 1. **进入 Insert 模式** (`i`, `a`, `o` 等)
- 自动恢复上次保存的输入法状态 - 延迟切换到中文输入法
- 约 200ms 后再次确认中文输入法,降低 macOS 状态未刷新导致的偶发失败
2. **退出 Insert 模式** (`ESC`) 2. **退出 Insert 模式** (`ESC`)
- 保存当前输入法状态 - `InsertLeavePre` 提前保存当前输入法状态
- 自动切换到英文输入法 -`InsertLeave` 自动切换到英文输入法
3. **中英文混合输入** 3. **macOS 行为说明**
- 插件会记住你每次退出 Insert 模式时的输入法 - `im-select` 控制的是 macOS 输入源 ID
- 下次进入时自动恢复 - 新版 macOS 中文输入法可能存在“图标为中文但内部仍输入英文”的状态
- 当前稳定版不模拟 `Ctrl-Space` / `Caps Lock`,避免快捷键被 Vim 接收导致退出 Insert 模式
## 测试 ## 测试
@@ -161,7 +161,8 @@ im-select
7. 按 `ESC` 退出到 Normal 模式 7. 按 `ESC` 退出到 Normal 模式
8. 再次按 `i` 进入 Insert 模式 8. 再次按 `i` 进入 Insert 模式
- 观察输入法是否保持英文 - 当前稳定版会恢复到配置的中文输入法
- 如果需要保持英文,请临时禁用插件或调整 `g:imselect_chinese_im` 策略
## 调试 ## 调试
@@ -219,8 +220,21 @@ im-select
### 输入法切换延迟 ### 输入法切换延迟
- 这是正常的,因为使用了异步处理 - 这是正常的,因为进入 Insert 后使用了 `timer_start()` 延迟恢复
- 通常延迟小于 100ms不影响使用 - 当前稳定版会在进入 Insert 后约 80ms 切中文,并在约 200ms 后兜底确认
- 如果机器较慢或 macOS 状态刷新较慢,可以在插件中适当调大延迟值
### 输入法图标是中文但实际输入英文
这是新版 macOS 中文输入法可能出现的内部状态问题:`im-select` 已经把输入源切到中文,但中文输入法内部仍处于英文输入状态。
当前稳定版采取的处理方式:
1. 不模拟 `Ctrl-Space`、`Caps Lock`、`Shift` 等按键,避免这些按键被 Vim 接收后影响模式。
2. 进入 Insert 后延迟切换中文输入源。
3. 再进行一次中文输入源兜底确认。
如果仍偶发出现该问题,优先调整延迟时间,不建议重新加入系统快捷键模拟。
### 输入法 ID 不正确 ### 输入法 ID 不正确

View File

@@ -24,6 +24,7 @@ endif
if !executable("im-select") if !executable("im-select")
finish finish
endif endif
let s:imselect_cmd = exepath('im-select')
let s:keepcpo = &cpo let s:keepcpo = &cpo
let g:imselect_loaded = 1 let g:imselect_loaded = 1
set cpo&vim set cpo&vim
@@ -38,96 +39,89 @@ if !exists('g:imselect_english_im')
endif endif
if !exists('g:imselect_chinese_im') if !exists('g:imselect_chinese_im')
let g:imselect_chinese_im = 'auto-detect' " 将自动检测 let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
endif endif
" --------------------------------------------------------------------- " ---------------------------------------------------------------------
" Functions: " Functions:
" 离开 Insert 模式:保存当前输入法名称,然后切换到英文 function! IMSelectCurrentIM() abort
function! IMSelect2en() return substitute(system(shellescape(s:imselect_cmd) . ' 2>/dev/null'), '[\r\n]', '', 'g')
" 保存当前输入法名称 endfunction
let current_im = substitute(system("im-select 2>/dev/null"), '\n', '', 'g')
let g:imselect_last_insert_im_name = current_im
" 切换到英文输入法(使用后台任务并重定向所有输出) function! IMSelectSwitch(im) abort
if has('job') if has('job')
" Use nohup in a shell backgrounded process. Some Vim builds don't accept silent! call job_start([s:imselect_cmd, a:im], {'out_io': 'null', 'err_io': 'null'})
" 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 &'])
else else
" Fallback: nohup in background silent! call system(shellescape(s:imselect_cmd) . ' ' . shellescape(a:im) . ' >/dev/null 2>&1 &')
call system('nohup im-select ' . shellescape(g:imselect_english_im) . ' >/dev/null 2>&1 &')
endif endif
endfunction endfunction
" 进入 Insert 模式:根据保存的输入法名称恢复 function! IMSelectStart() abort
function! IMSelect2zh() 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 == '' if g:imselect_last_insert_im_name == ''
return let g:imselect_last_insert_im_name = g:imselect_chinese_im
endif endif
call IMSelectSwitch(g:imselect_last_insert_im_name)
call timer_start(200, function('IMSelectEnsureTarget'))
endfunction
" 如果上次是英文输入法,保持英文(不需要做任何事) " 进入 Insert 模式:延迟恢复保存的输入法
if g:imselect_last_insert_im_name == g:imselect_english_im function! IMSelect2zh() abort
return call timer_start(80, function('IMSelectRestore'))
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 &')
endif
endfunction endfunction
" --------------------------------------------------------------------- " ---------------------------------------------------------------------
" Autocmds: " Autocmds:
" 进入 Insert 模式一次后的处理
function! IMSelect2zhOnce()
call IMSelect2zh()
call UnBindIMSelectAu()
endfunction
function! BindIMSelectAu2zhOnce()
augroup IMSelect
au InsertEnter * call IMSelect2zhOnce()
augroup END
endfunction
" 绑定自动命令 " 绑定自动命令
function! BindIMSelectAu() function! BindIMSelectAu()
augroup IMSelect augroup IMSelect
" 离开 Insert 模式:保存输入法状态并切换到英文 autocmd!
au InsertLeave * call IMSelect2en() " 离开 Insert 模式前:先保存输入法状态
autocmd InsertLeavePre * call IMSelectRemember()
" 离开 Insert 模式:切换到英文
autocmd InsertLeave * call IMSelect2en()
" 进入 Insert 模式:恢复上次的输入法状态 " 进入 Insert 模式:恢复上次的输入法状态
au InsertEnter * call IMSelect2zh() autocmd InsertEnter * call IMSelect2zh()
" Vim 启动:切换到英文 " Vim 启动:切换到英文
au VimEnter * call IMSelect2en() autocmd VimEnter * call timer_start(100, {-> IMSelectStart()})
augroup END augroup END
endfunction endfunction
function! UnBindIMSelectAu() function! UnBindIMSelectAu()
au! IMSelect InsertLeave * augroup IMSelect
au! IMSelect InsertEnter * autocmd!
augroup END
endfunction endfunction
" 延迟初始化:首次进入 Insert 模式时才绑定自动命令 call BindIMSelectAu()
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()
" 多光标支持:在选择多个光标前后的处理 " 多光标支持:在选择多个光标前后的处理
function! IMSelect_multiple_cursors_before() function! IMSelect_multiple_cursors_before()
call UnBindIMSelectAu() call UnBindIMSelectAu()
call BindIMSelectAu2zhOnce()
endfunction endfunction
function! IMSelect_multiple_cursors_after() function! IMSelect_multiple_cursors_after()

View File

@@ -43,90 +43,61 @@ endif
" --------------------------------------------------------------------- " ---------------------------------------------------------------------
" Functions: " Functions:
" 离开 Insert 模式:保存当前输入法名称,然后切换到英文 function! FcitxCurrentIM() abort
function! Fcitx2en() return substitute(system('fcitx-remote -n 2>/dev/null'), '[\r\n]', '', 'g')
" 保存当前输入法名称 endfunction
let current_im = substitute(system("fcitx-remote -n 2>/dev/null"), '\n', '', 'g')
let g:fcitx_last_insert_im_name = current_im
" 切换到英文输入法(使用后台任务并重定向所有输出) function! FcitxSwitch(im) abort
if has('job') silent! call system('fcitx-remote -s ' . shellescape(a:im) . ' >/dev/null 2>&1')
" Use nohup in a shell backgrounded process. Some Vim builds don't accept endfunction
" the 'detach' option; calling via sh -lc with nohup/& keeps process
" disassociated while avoiding unsupported job_start options. function! FcitxStart() abort
call job_start(['sh', '-lc', 'nohup fcitx-remote -s ' . shellescape(g:fcitx_english_im) . ' >/dev/null 2>&1 &']) call FcitxSwitch(g:fcitx_english_im)
else endfunction
" Fallback: nohup in background
call system('nohup fcitx-remote -s ' . shellescape(g:fcitx_english_im) . ' >/dev/null 2>&1 &') " 离开 Insert 模式:保存当前输入法名称,然后切换到英文
function! Fcitx2en() abort
let current_im = FcitxCurrentIM()
if current_im != ''
let g:fcitx_last_insert_im_name = current_im
endif endif
call FcitxSwitch(g:fcitx_english_im)
endfunction endfunction
" 进入 Insert 模式:根据保存的输入法名称恢复 " 进入 Insert 模式:根据保存的输入法名称恢复
function! Fcitx2zh() function! Fcitx2zh() abort
" 如果上次保存的输入法名称为空,说明是第一次进入,保持英文 if g:fcitx_last_insert_im_name == '' || g:fcitx_last_insert_im_name == g:fcitx_english_im
if g:fcitx_last_insert_im_name == ''
return return
endif endif
call FcitxSwitch(g:fcitx_last_insert_im_name)
" 如果上次是英文输入法,保持英文(不需要做任何事)
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
endfunction endfunction
" --------------------------------------------------------------------- " ---------------------------------------------------------------------
" Autocmds: " Autocmds:
" 进入 Insert 模式一次后的处理
function! Fcitx2zhOnce()
call Fcitx2zh()
call UnBindAu()
endfunction
function! BindAu2zhOnce()
augroup Fcitx
au InsertEnter * call Fcitx2zhOnce()
augroup END
endfunction
" 绑定自动命令 " 绑定自动命令
function! BindAu() function! BindAu()
augroup Fcitx augroup Fcitx
autocmd!
" 离开 Insert 模式:保存输入法状态并切换到英文 " 离开 Insert 模式:保存输入法状态并切换到英文
au InsertLeave * call Fcitx2en() autocmd InsertLeave * call Fcitx2en()
" 进入 Insert 模式:恢复上次的输入法状态 " 进入 Insert 模式:恢复上次的输入法状态
au InsertEnter * call Fcitx2zh() autocmd InsertEnter * call Fcitx2zh()
" Vim 启动:切换到英文 " Vim 启动:切换到英文
au VimEnter * call Fcitx2en() autocmd VimEnter * call timer_start(100, {-> FcitxStart()})
augroup END augroup END
endfunction endfunction
function! UnBindAu() function! UnBindAu()
au! Fcitx InsertLeave * augroup Fcitx
au! Fcitx InsertEnter * autocmd!
augroup END
endfunction endfunction
" 延迟初始化:首次进入 Insert 模式时才绑定自动命令 call BindAu()
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()
" 多光标支持:在选择多个光标前后的处理 " 多光标支持:在选择多个光标前后的处理
function! Multiple_cursors_before() function! Multiple_cursors_before()
call UnBindAu() call UnBindAu()
call BindAu2zhOnce()
endfunction endfunction
function! Multiple_cursors_after() function! Multiple_cursors_after()