Files
vim-im-switch/README.md
2026-07-15 11:04:39 +08:00

287 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 稳定插件 |
| `README.md` | 当前说明文档 |
| `main.ts` | Obsidian 插件入口 |
## 故障排除
### 进入 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