291 lines
7.2 KiB
Markdown
291 lines
7.2 KiB
Markdown
# Vim 输入法自动切换插件
|
||
|
||
面向 macOS 原生 Vim 的输入法自动切换方案:
|
||
|
||
- Normal 模式固定英文输入法
|
||
- Insert 模式恢复同一 Vim 进程内上次使用的中/英状态
|
||
- macOS 上优先使用 Hammerspoon `hs.keycodes.setLayout()` / `hs.keycodes.setMethod()`
|
||
- `im-select` 仅作为 fallback
|
||
|
||
> 说明:本次 Hammerspoon 方案和行为调整只针对原生 Vim 插件 `vim-im-switch-select.vim`。Obsidian 插件逻辑不变。
|
||
|
||
## Obsidian 插件说明
|
||
|
||
本仓库仍包含 Obsidian 插件入口 `main.ts`,用于在 Obsidian 启用 Vim keymap 时切换输入法。
|
||
|
||
当前 Obsidian 插件逻辑保持原样:
|
||
|
||
- 通过 `fcitx-remote` 执行输入法查询和切换。
|
||
- 监听 Obsidian / CodeMirror 的 Vim 模式变化。
|
||
- Normal / Visual 模式切到英文输入法。
|
||
- Insert / Replace 模式恢复上次 Insert 状态。
|
||
- 配置项仍使用 `manifest.json` / 插件设置页中的 `fcitx-remote` 路径和输入法 ID。
|
||
|
||
本次 Hammerspoon `hs.keycodes.setLayout()` / `hs.keycodes.setMethod()` 方案只应用在原生 Vim 插件 `vim-im-switch-select.vim`,没有改造 Obsidian 插件。
|
||
|
||
## 当前稳定策略
|
||
|
||
### 模式行为
|
||
|
||
| Vim 状态 | 行为 |
|
||
|---|---|
|
||
| Vim 启动后 | 延迟切到英文 `ABC` |
|
||
| 进入 Insert | 恢复本次 Vim 进程内上次 Insert 的中/英状态;首次默认中文 |
|
||
| 离开 Insert / `Esc` | 先记录 Insert 中最后的中/英状态,再切到英文 `ABC` |
|
||
|
||
示例:
|
||
|
||
```text
|
||
第一次进入 Insert -> 中文
|
||
Insert 中手动切到英文 -> Esc -> 再次 Insert -> 英文
|
||
Insert 中手动切回中文 -> Esc -> 再次 Insert -> 中文
|
||
退出 Vim 后重新打开 -> 记忆重置,第一次 Insert 默认中文
|
||
```
|
||
|
||
### 为什么使用 Hammerspoon
|
||
|
||
之前只用 `im-select` 切换 macOS input source ID 时,Apple 拼音可能出现:
|
||
|
||
```text
|
||
当前输入源ID: com.apple.inputmethod.SCIM.ITABC
|
||
当前键盘布局: ABC
|
||
当前输入法Method: Pinyin – Simplified
|
||
```
|
||
|
||
表现为菜单栏显示中文输入法,但实际仍输入英文。
|
||
|
||
Hammerspoon 可以直接设置 layout/method:
|
||
|
||
```lua
|
||
hs.keycodes.setLayout("ABC")
|
||
hs.keycodes.setMethod("Pinyin – Simplified")
|
||
```
|
||
|
||
这能避免只切 source ID 导致的 Apple 拼音内部状态不一致。
|
||
|
||
## 安装要求
|
||
|
||
### 1. Hammerspoon
|
||
|
||
安装并运行 Hammerspoon。
|
||
|
||
在 `~/.hammerspoon/init.lua` 中启用 IPC:
|
||
|
||
```lua
|
||
require("hs.ipc")
|
||
hs.ipc.cliInstall()
|
||
```
|
||
|
||
Reload Hammerspoon config 后,确认命令可用:
|
||
|
||
```bash
|
||
hs -c 'hs.keycodes.setLayout("ABC")'
|
||
hs -c 'hs.keycodes.setMethod("Pinyin – Simplified")'
|
||
```
|
||
|
||
两条命令都应返回:
|
||
|
||
```text
|
||
true
|
||
```
|
||
|
||
如果出现:
|
||
|
||
```text
|
||
can't access Hammerspoon message port Hammerspoon; is it running with the ipc module loaded?
|
||
```
|
||
|
||
说明 Hammerspoon IPC 未启用或配置尚未 reload。
|
||
|
||
### 2. im-select fallback(可选但建议保留)
|
||
|
||
当 `hs` 不可用时,插件会退回使用 `im-select`:
|
||
|
||
```bash
|
||
im-select
|
||
im-select com.apple.keylayout.ABC
|
||
im-select com.apple.inputmethod.SCIM.ITABC
|
||
```
|
||
|
||
常见输入源 ID:
|
||
|
||
| 输入法 | ID |
|
||
|---|---|
|
||
| ABC | `com.apple.keylayout.ABC` |
|
||
| Apple 简体拼音 | `com.apple.inputmethod.SCIM.ITABC` |
|
||
|
||
## 安装 Vim 插件
|
||
|
||
复制插件到 Vim plugin 目录:
|
||
|
||
```bash
|
||
mkdir -p ~/.vim/plugin
|
||
cp vim-im-switch-select.vim ~/.vim/plugin/
|
||
```
|
||
|
||
或在 `.vimrc` 中 source:
|
||
|
||
```vim
|
||
source /path/to/vim-im-switch-select.vim
|
||
```
|
||
|
||
## 配置
|
||
|
||
默认配置适用于 macOS ABC + Apple 简体拼音:
|
||
|
||
```vim
|
||
" Hammerspoon 主路径:英文键盘布局名
|
||
let g:imselect_hs_english_layout = 'ABC'
|
||
|
||
" Hammerspoon 主路径:中文输入法 Method 名
|
||
let g:imselect_hs_chinese_method = 'Pinyin – Simplified'
|
||
|
||
" im-select fallback:英文输入源 ID
|
||
let g:imselect_english_im = 'com.apple.keylayout.ABC'
|
||
|
||
" im-select fallback:中文输入源 ID
|
||
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
|
||
```
|
||
|
||
延迟配置:
|
||
|
||
```vim
|
||
" 进入 Insert 后延迟恢复,默认 80ms
|
||
let g:imselect_restore_delay = 80
|
||
|
||
" 恢复后再次确认目标状态,默认 200ms
|
||
let g:imselect_confirm_delay = 200
|
||
```
|
||
|
||
## 工作原理
|
||
|
||
核心事件:
|
||
|
||
```vim
|
||
InsertLeavePre -> 记录 Insert 中最后的中/英状态
|
||
InsertLeave -> 切到英文 ABC
|
||
InsertEnter -> 延迟恢复上次 Insert 中/英状态
|
||
VimEnter -> 启动后延迟切到英文 ABC
|
||
```
|
||
|
||
关键点:
|
||
|
||
- `InsertLeavePre` 必须早于 `InsertLeave` 保存状态,否则 `InsertLeave` 已经切到英文。
|
||
- timer 回调会检查目标状态,避免快速 `i<Esc>` 后延迟回调把 Normal 模式切回中文。
|
||
- Hammerspoon 是主路径;`im-select` 只在 `hs` 不可用时 fallback。
|
||
- 状态记忆是 script-local 内存,只在当前 Vim 进程内有效。
|
||
|
||
## 手工测试
|
||
|
||
1. 打开 Vim:
|
||
|
||
```bash
|
||
vim test-im-switch.txt
|
||
```
|
||
|
||
2. 第一次按 `i` 进入 Insert:应切到中文。
|
||
3. 输入中文,按 `Esc`:应切到英文 `ABC`。
|
||
4. 再按 `i`:应恢复中文。
|
||
5. Insert 中手动切到英文,输入英文,按 `Esc`。
|
||
6. 再按 `i`:应恢复英文。
|
||
7. Insert 中手动切回中文,按 `Esc`,再按 `i`:应恢复中文。
|
||
8. 退出 Vim 后重新打开:第一次 Insert 默认中文,这是当前已知限制。
|
||
|
||
## 验证命令
|
||
|
||
Vimscript 加载检查:
|
||
|
||
```bash
|
||
vim -Nu NONE -n -es -S "/path/to/vim-im-switch-select.vim" -c 'qa'
|
||
```
|
||
|
||
Hammerspoon 检查:
|
||
|
||
```bash
|
||
hs -c 'hs.keycodes.setLayout("ABC")'
|
||
hs -c 'hs.keycodes.setMethod("Pinyin – Simplified")'
|
||
```
|
||
|
||
## 已知限制
|
||
|
||
### 跨 Vim 进程不持久记忆
|
||
|
||
当前插件只在同一次 Vim 进程内记忆 Insert 中最后的中/英状态。
|
||
|
||
```text
|
||
Insert 中切到英文 -> Esc -> 再 Insert -> 英文
|
||
退出 Vim -> 重新 vi -> 第一次 Insert 默认中文
|
||
```
|
||
|
||
这是当前已知问题,暂不做文件持久化。
|
||
|
||
### 依赖 Hammerspoon 运行
|
||
|
||
稳定路径依赖:
|
||
|
||
- Hammerspoon 正在运行
|
||
- `hs` CLI 可用
|
||
- `hs.ipc` 已启用
|
||
- 输入法名称 `Pinyin – Simplified` 与系统一致
|
||
|
||
如果 Hammerspoon 不可用,插件会 fallback 到 `im-select`,但 fallback 只能切 input source ID,不能完全解决 Apple 拼音内部状态问题。
|
||
|
||
### 不模拟 CapsLock / Ctrl-Space
|
||
|
||
当前实现不模拟 `Caps Lock`、`Ctrl-Space`、`Shift` 等系统快捷键。
|
||
|
||
原因:这些按键可能被 Vim 接收,导致退出 Insert 或产生额外副作用;之前调试也证明模拟 Caps 不稳定。
|
||
|
||
## 主要文件
|
||
|
||
| 文件 | 说明 |
|
||
|---|---|
|
||
| `vim-im-switch-select.vim` | 当前原生 Vim 稳定插件 |
|
||
| `main.ts` | Obsidian 插件入口 |
|
||
| `README.md` | 中文说明文档 |
|
||
| `README_en.md` | English documentation |
|
||
| `CHANGELOG.md` / `CHANGELOG_en.md` | 更新日志 |
|
||
| `COMPARISON.md` | 原生 Vim 插件与 Obsidian 插件对比 |
|
||
| `RELEASE.md` | 发布说明 |
|
||
|
||
## 故障排除
|
||
|
||
### 进入 Insert 仍然输入英文
|
||
|
||
先确认 Hammerspoon method 设置有效:
|
||
|
||
```bash
|
||
hs -c 'hs.keycodes.setMethod("Pinyin – Simplified")'
|
||
```
|
||
|
||
如果返回 `true` 后仍异常,检查 Hammerspoon 日志或系统输入法名称是否不是 `Pinyin – Simplified`。
|
||
|
||
### `hs -c` 报 IPC 错误
|
||
|
||
在 `~/.hammerspoon/init.lua` 加入:
|
||
|
||
```lua
|
||
require("hs.ipc")
|
||
hs.ipc.cliInstall()
|
||
```
|
||
|
||
然后 reload Hammerspoon。
|
||
|
||
### fallback 输入法 ID 不正确
|
||
|
||
切到目标输入法后运行:
|
||
|
||
```bash
|
||
im-select
|
||
```
|
||
|
||
将输出值配置到:
|
||
|
||
```vim
|
||
let g:imselect_chinese_im = '你的输入法ID'
|
||
```
|
||
|
||
## License
|
||
|
||
MIT License
|