258 lines
6.8 KiB
Markdown
258 lines
6.8 KiB
Markdown
# Vim Input Method Switch Plugin
|
||
|
||
English | [中文](./README.md)
|
||
|
||
This repository contains two related but currently separate implementations:
|
||
|
||
- **Native Vim plugin**: `vim-im-switch-select.vim`, currently the stable macOS path.
|
||
- **Obsidian plugin**: `main.ts`, unchanged by the recent Hammerspoon work.
|
||
|
||
## Native Vim plugin
|
||
|
||
The native Vim plugin is designed for macOS Vim and uses this stable strategy:
|
||
|
||
- Normal mode always switches to English `ABC`.
|
||
- Insert mode restores the last Chinese/English state used in the same Vim process.
|
||
- Hammerspoon is preferred via `hs.keycodes.setLayout()` / `hs.keycodes.setMethod()`.
|
||
- `im-select` is kept only as fallback.
|
||
|
||
> Note: The Hammerspoon behavior described here applies only to the native Vim plugin `vim-im-switch-select.vim`. It does not change the Obsidian plugin.
|
||
|
||
### Mode behavior
|
||
|
||
| Vim state | Behavior |
|
||
|---|---|
|
||
| After Vim starts | Delayed switch to English `ABC` |
|
||
| Enter Insert | Restore the last Insert-mode Chinese/English state in this Vim process; first entry defaults to Chinese |
|
||
| Leave Insert / `Esc` | Save the current Insert-mode language state, then switch to English `ABC` |
|
||
|
||
Example:
|
||
|
||
```text
|
||
First Insert -> Chinese
|
||
Manually switch to English in Insert -> Esc -> next Insert -> English
|
||
Manually switch back to Chinese in Insert -> Esc -> next Insert -> Chinese
|
||
Quit Vim and open again -> memory resets; first Insert defaults to Chinese
|
||
```
|
||
|
||
### Why Hammerspoon
|
||
|
||
Using only `im-select` can leave Apple Pinyin in an inconsistent macOS state:
|
||
|
||
```text
|
||
sourceID = com.apple.inputmethod.SCIM.ITABC
|
||
layout = ABC
|
||
method = Pinyin – Simplified
|
||
```
|
||
|
||
The menu bar may show Chinese input, but actual typing remains English.
|
||
|
||
Hammerspoon can directly set layout and method:
|
||
|
||
```lua
|
||
hs.keycodes.setLayout("ABC")
|
||
hs.keycodes.setMethod("Pinyin – Simplified")
|
||
```
|
||
|
||
This avoids relying only on the input source ID.
|
||
|
||
### Hammerspoon setup
|
||
|
||
Install and run Hammerspoon. Enable IPC in `~/.hammerspoon/init.lua`:
|
||
|
||
```lua
|
||
require("hs.ipc")
|
||
hs.ipc.cliInstall()
|
||
```
|
||
|
||
Reload Hammerspoon config, then verify:
|
||
|
||
```bash
|
||
hs -c 'hs.keycodes.setLayout("ABC")'
|
||
hs -c 'hs.keycodes.setMethod("Pinyin – Simplified")'
|
||
```
|
||
|
||
Both commands should return:
|
||
|
||
```text
|
||
true
|
||
```
|
||
|
||
### im-select fallback
|
||
|
||
If `hs` is unavailable, the Vim plugin falls back to `im-select`:
|
||
|
||
```bash
|
||
im-select
|
||
im-select com.apple.keylayout.ABC
|
||
im-select com.apple.inputmethod.SCIM.ITABC
|
||
```
|
||
|
||
Common input source IDs:
|
||
|
||
| Input method | ID |
|
||
|---|---|
|
||
| ABC | `com.apple.keylayout.ABC` |
|
||
| Apple Simplified Pinyin | `com.apple.inputmethod.SCIM.ITABC` |
|
||
|
||
### Install native Vim plugin
|
||
|
||
Copy the plugin to Vim's plugin directory:
|
||
|
||
```bash
|
||
mkdir -p ~/.vim/plugin
|
||
cp vim-im-switch-select.vim ~/.vim/plugin/
|
||
```
|
||
|
||
Or source it from `.vimrc`:
|
||
|
||
```vim
|
||
source /path/to/vim-im-switch-select.vim
|
||
```
|
||
|
||
### Native Vim configuration
|
||
|
||
Defaults are for macOS ABC + Apple Simplified Pinyin:
|
||
|
||
```vim
|
||
" Hammerspoon path: English keyboard layout name
|
||
let g:imselect_hs_english_layout = 'ABC'
|
||
|
||
" Hammerspoon path: Chinese input method name
|
||
let g:imselect_hs_chinese_method = 'Pinyin – Simplified'
|
||
|
||
" im-select fallback: English input source ID
|
||
let g:imselect_english_im = 'com.apple.keylayout.ABC'
|
||
|
||
" im-select fallback: Chinese input source ID
|
||
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
|
||
|
||
" Delay before restoring Insert state, default 80ms
|
||
let g:imselect_restore_delay = 80
|
||
|
||
" Confirm target state after restore, default 200ms
|
||
let g:imselect_confirm_delay = 200
|
||
```
|
||
|
||
### Native Vim implementation notes
|
||
|
||
```vim
|
||
InsertLeavePre -> Save the last Insert-mode Chinese/English state
|
||
InsertLeave -> Switch to English ABC
|
||
InsertEnter -> Delayed restore of the last Insert-mode state
|
||
VimEnter -> Delayed switch to English ABC on startup
|
||
```
|
||
|
||
Important details:
|
||
|
||
- `InsertLeavePre` must save state before `InsertLeave` switches to English.
|
||
- Timer callbacks check the target state so fast `i<Esc>` does not switch Normal mode back to Chinese.
|
||
- Hammerspoon is the primary path; `im-select` is fallback only.
|
||
- State memory is script-local and only valid within the current Vim process.
|
||
|
||
### Native Vim manual test
|
||
|
||
1. Open Vim:
|
||
|
||
```bash
|
||
vim test-im-switch.txt
|
||
```
|
||
|
||
2. First `i`: should switch to Chinese.
|
||
3. Type Chinese, press `Esc`: should switch to English `ABC`.
|
||
4. Press `i` again: should restore Chinese.
|
||
5. In Insert, manually switch to English, type English, press `Esc`.
|
||
6. Press `i` again: should restore English.
|
||
7. In Insert, manually switch back to Chinese, press `Esc`, then `i`: should restore Chinese.
|
||
8. Quit and reopen Vim: first Insert defaults to Chinese; this is a known limitation.
|
||
|
||
### Native Vim verification
|
||
|
||
```bash
|
||
vim -Nu NONE -n -es -S "/path/to/vim-im-switch-select.vim" -c 'qa'
|
||
```
|
||
|
||
```bash
|
||
hs -c 'hs.keycodes.setLayout("ABC")'
|
||
hs -c 'hs.keycodes.setMethod("Pinyin – Simplified")'
|
||
```
|
||
|
||
## Obsidian plugin
|
||
|
||
The Obsidian plugin remains in this repository and is implemented by `main.ts`.
|
||
|
||
Current Obsidian plugin behavior is unchanged:
|
||
|
||
- Uses `fcitx-remote` to query and switch input methods.
|
||
- Listens to Obsidian / CodeMirror Vim mode changes.
|
||
- Normal / Visual mode switches to English.
|
||
- Insert / Replace mode restores the last Insert-mode state.
|
||
- Settings still use the plugin settings page and the configured `fcitx-remote` paths / input method IDs.
|
||
|
||
### Obsidian requirements
|
||
|
||
The plugin expects a working `fcitx-remote` command for the current platform.
|
||
|
||
Default macOS path:
|
||
|
||
```text
|
||
/usr/local/bin/fcitx-remote
|
||
```
|
||
|
||
Default input methods:
|
||
|
||
```text
|
||
English: com.apple.keylayout.ABC
|
||
Chinese: auto-detect, fallback com.apple.inputmethod.SCIM.ITABC
|
||
```
|
||
|
||
### Obsidian development
|
||
|
||
```bash
|
||
npm install
|
||
npm run build
|
||
```
|
||
|
||
## Known limitations
|
||
|
||
### Native Vim Insert memory is not persisted across Vim processes
|
||
|
||
The native Vim plugin only remembers the last Insert-mode state within the current Vim process.
|
||
|
||
```text
|
||
Switch to English in Insert -> Esc -> next Insert -> English
|
||
Quit Vim -> reopen Vim -> first Insert defaults to Chinese
|
||
```
|
||
|
||
This is intentional for now; no file-based persistence is implemented.
|
||
|
||
### Native Vim depends on Hammerspoon for the stable macOS path
|
||
|
||
The stable path requires:
|
||
|
||
- Hammerspoon running
|
||
- `hs` CLI available
|
||
- `hs.ipc` enabled
|
||
- Input method name matching `Pinyin – Simplified`
|
||
|
||
If Hammerspoon is unavailable, the plugin falls back to `im-select`, but fallback cannot fully fix Apple Pinyin layout/method inconsistency.
|
||
|
||
### No simulated CapsLock / Ctrl-Space
|
||
|
||
The native Vim plugin does not simulate `Caps Lock`, `Ctrl-Space`, `Shift`, or other system shortcuts. Those keys may be received by Vim and cause side effects.
|
||
|
||
## Main files
|
||
|
||
| File | Description |
|
||
|---|---|
|
||
| `vim-im-switch-select.vim` | Current stable native Vim plugin |
|
||
| `main.ts` | Obsidian plugin entry |
|
||
| `README.md` | Chinese documentation |
|
||
| `README_en.md` | English documentation |
|
||
| `CHANGELOG.md` / `CHANGELOG_en.md` | Change history |
|
||
| `RELEASE.md` | Release notes |
|
||
|
||
## License
|
||
|
||
MIT License
|