update at 2026-07-15 11:04:39

This commit is contained in:
douboer
2026-07-15 11:04:39 +08:00
parent 995d672f4a
commit aeade0f813
7 changed files with 386 additions and 820 deletions

View File

@@ -1,276 +0,0 @@
# vim-im-switch-select.vim
> 使用 `im-select` 而不是 `fcitx-remote` 的 Vim 输入法自动切换插件
## 概述
`vim-im-switch-select.vim``vim-im-switch.vim` 的变体版本,使用 `im-select` 工具而不是 `fcitx-remote` 来控制输入法切换。
## 主要区别
| 特性 | vim-im-switch.vim | vim-im-switch-select.vim |
|------|------------------|-------------------------|
| 输入法工具 | fcitx-remote | im-select |
| 适用平台 | macOS, Linux | 主要是 macOS |
| 获取当前输入法 | `fcitx-remote -n` | `im-select` |
| 切换输入法 | `fcitx-remote -s <IM>` | `im-select <IM>` |
| 全局变量前缀 | `g:fcitx_*` | `g:imselect_*` |
| 函数前缀 | `Fcitx*` | `IMSelect*` |
## 功能特性
**自动切换输入法**:在 Normal 模式和 Insert 模式之间切换时自动切换输入法
**提前保存状态**:使用 `InsertLeavePre` 在离开 Insert 前保存输入法状态
**异步静默处理**:使用 `job_start()` 异步执行,并屏蔽命令输出,避免阻塞和乱码
**macOS 稳定恢复**:进入 Insert 后延迟恢复中文输入源,并进行一次兜底重试
## 安装要求
### 1. 安装 im-select
本项目已经包含了 `im-select` 可执行文件(适用于 macOS ARM64
如果你需要其他版本,可以从这里获取:
- [im-select GitHub](https://github.com/daipeihust/im-select)
或者使用项目中的 `im-select` 文件:
```bash
# 确保 im-select 有执行权限
chmod +x /path/to/vim-im-switch/im-select
# 将 im-select 添加到 PATH可选
sudo cp /path/to/vim-im-switch/im-select /usr/local/bin/
```
### 2. 验证 im-select 安装
```bash
# 如果在项目目录中
./im-select
# 如果在 PATH 中
im-select
```
应该输出当前的输入法名称,例如:
```
com.apple.keylayout.ABC
```
## 安装插件
### 方法 1复制到 Vim 插件目录
```bash
mkdir -p ~/.vim/plugin
cp vim-im-switch-select.vim ~/.vim/plugin/
```
### 方法 2在 .vimrc 中 source
```vim
" 在 .vimrc 中添加
source /Users/gavin/vim-im-switch/vim-im-switch-select.vim
```
### 方法 3使用插件管理器
**vim-plug**:
```vim
Plug 'yourusername/vim-im-switch', { 'rtp': 'vim-im-switch-select.vim' }
```
## 配置
`.vimrc` 中添加配置(可选):
```vim
" 英文输入法 ID默认值
let g:imselect_english_im = 'com.apple.keylayout.ABC'
" 中文输入法 ID默认值系统自带简体拼音
" macOS 更新后建议显式配置,不依赖自动检测
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
```
### 如何获取输入法 ID
```bash
# 切换到你想要的输入法,然后运行:
./im-select
# 或
im-select
```
常见的输入法 ID
- 美国英文:`com.apple.keylayout.ABC`
- 简体拼音:`com.apple.inputmethod.SCIM.ITABC`
- 搜狗输入法:`com.sogou.inputmethod.sogou`
- 百度输入法:`com.baidu.inputmethod.BaiduIM.pinyin`
- 微信输入法:`com.tencent.inputmethod.wetype.pinyin`
## 使用方法
插件安装后会自动工作,无需手动操作:
1. **进入 Insert 模式** (`i`, `a`, `o` 等)
- 延迟切换到中文输入法
- 约 200ms 后再次确认中文输入法,降低 macOS 状态未刷新导致的偶发失败
2. **退出 Insert 模式** (`ESC`)
-`InsertLeavePre` 提前保存当前输入法状态
-`InsertLeave` 自动切换到英文输入法
3. **macOS 行为说明**
- `im-select` 控制的是 macOS 输入源 ID
- 新版 macOS 中文输入法可能存在“图标为中文但内部仍输入英文”的状态
- 当前稳定版不模拟 `Ctrl-Space` / `Caps Lock`,避免快捷键被 Vim 接收导致退出 Insert 模式
## 测试
### 运行自动化测试
```bash
# 测试 im-select 功能
./test-im-select.sh
# 在 Vim 中测试插件
./run-vim-test.sh
```
### 手动测试步骤
1. 启动 Vim 并打开测试文件:
```bash
vim test-im-switch.txt
```
2. 按 `i` 进入 Insert 模式
3. 切换到中文输入法,输入一些中文
4. 按 `ESC` 退出到 Normal 模式
- 观察输入法是否切换到英文
5. 再次按 `i` 进入 Insert 模式
- 观察输入法是否自动恢复到中文
6. 在 Insert 模式下,手动切换到英文输入法,输入一些英文
7. 按 `ESC` 退出到 Normal 模式
8. 再次按 `i` 进入 Insert 模式
- 当前稳定版会恢复到配置的中文输入法
- 如果需要保持英文,请临时禁用插件或调整 `g:imselect_chinese_im` 策略
## 调试
### 查看插件是否加载
```vim
:echo exists('g:imselect_loaded')
" 应该输出 1
```
### 查看保存的输入法状态
```vim
:echo g:imselect_last_insert_im_name
" 输出上次 Insert 模式的输入法名称
```
### 手动测试函数
```vim
" 切换到英文
:call IMSelect2en()
" 恢复输入法
:call IMSelect2zh()
```
### 查看 Vim 消息
```vim
:messages
```
## 故障排除
### 插件没有效果
1. **检查 im-select 是否可执行**
```bash
which im-select
# 或
ls -l ./im-select
```
2. **检查 Vim 版本是否支持 job_start()**
```vim
:echo has('job')
" 应该输出 1
```
3. **检查是否有错误消息**
```vim
:messages
```
### 输入法切换延迟
- 这是正常的,因为进入 Insert 后使用了 `timer_start()` 延迟恢复
- 当前稳定版会在进入 Insert 后约 80ms 切中文,并在约 200ms 后兜底确认
- 如果机器较慢或 macOS 状态刷新较慢,可以在插件中适当调大延迟值
### 输入法图标是中文但实际输入英文
这是新版 macOS 中文输入法可能出现的内部状态问题:`im-select` 已经把输入源切到中文,但中文输入法内部仍处于英文输入状态。
当前稳定版采取的处理方式:
1. 不模拟 `Ctrl-Space`、`Caps Lock`、`Shift` 等按键,避免这些按键被 Vim 接收后影响模式。
2. 进入 Insert 后延迟切换中文输入源。
3. 再进行一次中文输入源兜底确认。
如果仍偶发出现该问题,优先调整延迟时间,不建议重新加入系统快捷键模拟。
### 输入法 ID 不正确
```bash
# 切换到你的中文输入法,然后运行:
./im-select
# 将输出的 ID 配置到 .vimrc
let g:imselect_chinese_im = '你的输入法ID'
```
## 与原版的兼容性
`vim-im-switch-select.vim` 和 `vim-im-switch.vim` 可以共存,但不建议同时使用。它们使用不同的:
- 全局变量名(`g:imselect_*` vs `g:fcitx_*`
- 函数名(`IMSelect*` vs `Fcitx*`
- 自动命令组名(`IMSelect` vs `Fcitx`
## 文件清单
- `vim-im-switch-select.vim` - 插件主文件
- `im-select` - im-select 可执行文件macOS ARM64
- `test-im-select.sh` - 自动化测试脚本
- `run-vim-test.sh` - Vim 交互测试脚本
- `test-im-switch.txt` - 测试文件
- `README-im-select.md` - 本说明文件
## 参考链接
- [im-select GitHub](https://github.com/daipeihust/im-select)
- [原版 vim-im-switch 项目](https://github.com/yourusername/vim-im-switch)
## 许可证
MIT License
---
**如有问题或建议,欢迎提交 Issue**

511
README.md
View File

@@ -1,353 +1,286 @@
# Vim 输入法自动切换插件
[![Version](https://img.shields.io/badge/version-2.0.3-blue.svg)](./CHANGELOG.md)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg)](#安装要求)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE.txt)
面向 macOS 原生 Vim 的输入法自动切换方案:
[English](./README_en.md) | 中文
- Normal 模式固定英文输入法
- Insert 模式恢复同一 Vim 进程内上次使用的中/英状态
- macOS 上优先使用 Hammerspoon `hs.keycodes.setLayout()` / `hs.keycodes.setMethod()`
- `im-select` 仅作为 fallback
[感谢 github 中的 vim-im-switch这个项目](https://github.com/yourusername/vim-im-switch)
这个项目不能满足需求,在这个项目基础上改进。
> 说明:本次 Hammerspoon 方案和行为调整只针对原生 Vim 插件 `vim-im-switch-select.vim`。Obsidian 插件逻辑不变。
作者Gavin Chan
## Obsidian 插件说明
> 🚀 **智能输入法管理工具** - 为 Vim 用户打造的无感知输入法切换体验
本仓库仍包含 Obsidian 插件入口 `main.ts`,用于在 Obsidian 启用 Vim keymap 时切换输入法。
这是为 **原生 Vim****Obsidian 编辑器** 的 Vim 模式设计的智能输入法自动切换插件,包含
- 🎯 **Obsidian 插件**:适用于 Obsidian 编辑器的 Vim 模式
-**Vim 插件**:适用于原生 Vim/NeoVim 编辑器
- 🌍 **跨平台支持**macOS、Linux、Windows 全平台兼容
当前 Obsidian 插件逻辑保持原样
## ✨ 功能亮点
- 通过 `fcitx-remote` 执行输入法查询和切换。
- 监听 Obsidian / CodeMirror 的 Vim 模式变化。
- Normal / Visual 模式切到英文输入法。
- Insert / Replace 模式恢复上次 Insert 状态。
- 配置项仍使用 `manifest.json` / 插件设置页中的 `fcitx-remote` 路径和输入法 ID。
- 🔄 **自动切换输入法**:在 Vim 的 Normal 模式和 Insert 模式之间切换时,自动切换输入法
- 🧠 **智能状态记忆**:记住上次 Insert 模式退出时的输入法状态,下次进入时自动恢复
- 🎭 **无感知体验**:完全静默切换,无 UI 闪烁或延迟
- 🌐 **中英混合友好**:完美支持中英文混合输入场景
-**多重保障机制**:三层检测确保切换的可靠性
本次 Hammerspoon `hs.keycodes.setLayout()` / `hs.keycodes.setMethod()` 方案只应用在原生 Vim 插件 `vim-im-switch-select.vim`,没有改造 Obsidian 插件。
## 核心特性
### 1. 模式切换自动化
- 进入 Normal 模式(按 ESC 或其他命令)→ 自动切换到英文输入法
- 进入 Insert 模式(按 i, a, o 等)→ 自动恢复上次的输入法状态
## 当前稳定策略
### 2. 输入法状态记忆
- 退出 Insert 模式时,自动检测并保存当前的输入法(中文/英文)
- 下次进入 Insert 模式时,自动恢复到上次保存的输入法状态
- 支持中英文混合输入场景
### 模式行为
### 3. 智能检测机制
插件采用**三重检测机制**确保可靠性:
| Vim 状态 | 行为 |
|---|---|
| Vim 启动后 | 延迟切到英文 `ABC` |
| 进入 Insert | 恢复本次 Vim 进程内上次 Insert 的中/英状态;首次默认中文 |
| 离开 Insert / `Esc` | 先记录 Insert 中最后的中/英状态,再切到英文 `ABC` |
| 检测方式 | 优先级 | 说明 |
|---------|--------|------|
| 🎯 **键盘事件监听** | 最高 | 使用事件捕获模式监听 ESC 和 Insert 按键,响应最快 |
| 🔧 **CodeMirror 事件** | 中等 | 监听 vim-mode-change 事件,处理非按键触发的模式切换 |
| 🔄 **定时轮询** | 兜底 | 100ms 轮询检测模式变化,作为最后保障机制 |
示例:
### 4. 技术特性
-**异步处理**:使用 `job_start()` 异步执行,避免 UI 阻塞
- 🛡️ **防抖机制**100ms 防抖避免重复处理
- 🎯 **事件捕获优化**:使用 capture 模式确保最快响应
- 🔧 **自动检测**:智能检测和配置中文输入法
```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 拼音内部状态不一致。
## 安装要求
#### macOS
1. 安装 [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx):
```bash
brew install fcitx-remote-for-osx
### 1. Hammerspoon
安装并运行 Hammerspoon。
`~/.hammerspoon/init.lua` 中启用 IPC
```lua
require("hs.ipc")
hs.ipc.cliInstall()
```
2. 验证安装
Reload Hammerspoon config 后,确认命令可用
```bash
fcitx-remote -n
# 应该输出当前输入法的名称com.apple.keylayout.ABC
hs -c 'hs.keycodes.setLayout("ABC")'
hs -c 'hs.keycodes.setMethod("Pinyin Simplified")'
```
#### Linux
通过你的包管理器安装 `fcitx`
```bash
# Ubuntu/Debian
sudo apt-get install fcitx
两条命令都应返回:
# Fedora
sudo dnf install fcitx
# Arch Linux
sudo pacman -S fcitx
```text
true
```
#### Windows
使用项目内置的 AutoHotkey 脚本:
1. 安装 [AutoHotkey](https://www.autohotkey.com/)
2. 使用项目中的 `fcitx-remote.ahk` 脚本
3. 或下载编译好的版本:[fcitx-remote.exe](https://github.com/yuanotes/obsidian-vim-im-switch-plugin/releases/download/1.0.3/fcitx-remote.exe)
4. 将 exe 文件放到系统 PATH 路径中
如果出现:
## 安装插件
### Obsidian 插件安装
1. 下载插件文件到 Obsidian 插件目录:
```bash
cd /path/to/your/vault/.obsidian/plugins/
git clone https://github.com/yourusername/vim-im-switch.git
```text
can't access Hammerspoon message port Hammerspoon; is it running with the ipc module loaded?
```
2. 在 Obsidian 中启用插件:
- 打开设置 → 社区插件 → 浏览
- 找到 "Vim Input Method Switch"
- 点击启用
说明 Hammerspoon IPC 未启用或配置尚未 reload。
3. 配置输入法(可选):
- 打开插件设置
- 设置英文输入法(默认:`com.apple.keylayout.ABC`
- 设置中文输入法(默认:自动检测)
### 2. im-select fallback可选但建议保留
### Vim 插件安装
`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 目录:
1. 复制插件文件到 Vim 配置目录:
```bash
mkdir -p ~/.vim/plugin
cp vim-im-switch.vim ~/.vim/plugin/
cp vim-im-switch-select.vim ~/.vim/plugin/
```
2. 重启 Vim插件会自动加载
或在 `.vimrc` 中 source
3. 配置输入法(可选):
在 `.vimrc` 中添加:
```vim
" 英文输入法 ID默认值
let g:fcitx_english_im = 'com.apple.keylayout.ABC'
" 中文输入法 ID可选插件会自动检测
" let g:fcitx_chinese_im = 'com.tencent.inputmethod.wetype.pinyin'
source /path/to/vim-im-switch-select.vim
```
#### 使用插件管理器安装
## 配置
默认配置适用于 macOS ABC + Apple 简体拼音:
**vim-plug**:
```vim
Plug 'yourusername/vim-im-switch'
" 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'
```
**Vundle**:
延迟配置:
```vim
Plugin 'yourusername/vim-im-switch'
" 进入 Insert 后延迟恢复,默认 80ms
let g:imselect_restore_delay = 80
" 恢复后再次确认目标状态,默认 200ms
let g:imselect_confirm_delay = 200
```
### 🚀 一键部署(推荐)
使用部署脚本同时安装 Obsidian 插件和 Vim 插件:
```bash
# 克隆项目
git clone https://github.com/yourusername/vim-im-switch.git
cd vim-im-switch
# 构建并部署
npm install
./deploy.sh
```
> 💡 **提示**: v2.0.2 修复了终端 Vim 中的标题闪烁问题,详见 [更新日志](./CHANGELOG.md)
## 使用方法
### 基本使用场景
1. **中文输入**
```
按 i → 进入 Insert 模式 → 输入法切换到中文(如果上次是中文)
输入中文内容
按 ESC → 退出到 Normal 模式 → 输入法切换到英文
```
2. **英文输入**
```
按 i → 进入 Insert 模式 → 输入法保持英文(如果上次是英文)
输入英文内容
按 ESC → 退出到 Normal 模式 → 输入法保持英文
```
3. **中英混合**
```
按 i → 自动恢复上次的输入法
输入中文,然后手动切换到英文继续输入
按 ESC → 保存当前输入法状态(英文)
按 i → 自动恢复英文输入法
```
### 支持的 Vim 命令
- **进入 Insert 模式**`i`, `I`, `a`, `A`, `o`, `O`, `s`, `S`, `c`, `C`
- **退出 Insert 模式**`ESC`, 以及其他触发 Normal 模式的命令
## 工作原理
```mermaid
graph TD
A[Normal 模式<br/>英文输入法] -->|按 i/a/o 等| B[检测模式切换]
B --> C[恢复上次保存的<br/>输入法状态]
C --> D[Insert 模式<br/>自动恢复的输入法:<br/>中文/英文]
D -->|按 ESC| E[保存当前输入法<br/>状态 中/英]
E --> F[切换到英文输入法]
F --> A
核心事件:
style A fill:#e1f5ff,stroke:#01579b,stroke-width:2px
style D fill:#fff9c4,stroke:#f57f17,stroke-width:2px
style E fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style F fill:#e8f5e9,stroke:#1b5e20,stroke-width:2px
```
插件维护一个状态机,跟踪 Vim 模式和输入法状态,在模式转换时自动切换输入法,同时保留用户偏好设置。
## 🔧 故障排除
### 快速诊断
使用内置诊断脚本:
```bash
./diagnose-im.sh
```
### 常见问题
#### 🚫 插件没有效果
1. **检查依赖**:确认 fcitx-remote 是否正确安装
```bash
# macOS
fcitx-remote -n
# Linux
which fcitx-remote
# Windows
fcitx-remote.exe
```
2. **检查日志**
- **Obsidian**: 开发者控制台 (Ctrl+Shift+I)
- **Vim**: `:messages` 命令
3. **验证配置**:确认输入法名称与系统设置匹配
#### ⚠️ 输入法切换不正确
1. **获取正确的输入法名称**
```bash
# 切换到中文输入法后执行
fcitx-remote -n
```
2. **手动测试**
```bash
# 切换到英文
fcitx-remote -s com.apple.keylayout.ABC
# 切换到中文(替换为你的输入法名称)
fcitx-remote -s com.tencent.inputmethod.wetype.pinyin
```
3. **检查冲突**:暂时禁用其他 Vim 插件测试
#### 🐛 其他问题
- **权限问题**:确保 fcitx-remote 有执行权限
- **路径问题**:检查 fcitx-remote 是否在 PATH 中
- **版本兼容**:确认 Vim 版本支持 `job_start()` (Vim 8+)
## 🛠️ 开发指南
### 环境要求
- Node.js 14+
- TypeScript 4.2+
- Rollup (构建工具)
### 构建项目
```bash
# 安装依赖
npm install
# 开发模式(监听文件变化)
npm run dev
# 生产构建
npm run build
```
### 项目结构
```
vim-im-switch/
├── main.ts # Obsidian 插件主文件
├── vim-im-switch.vim # Vim 插件文件
├── fcitx-remote.ahk # Windows 支持脚本
├── fcitx-remote-for-osx/ # macOS 支持工具
├── deploy.sh # 一键部署脚本
├── diagnose-im.sh # 诊断脚本
└── vim-im-switch-plugin/ # 构建输出目录
```
### 调试方法
#### Obsidian 插件调试
插件会在控制台输出关键日志:
```
🚀 [VimIMSwitch] Loading plugin...
🔤 [VimIMSwitch] ESC → English (saved Chinese)
🈳 [VimIMSwitch] → Chinese
❌ [VimIMSwitch] Error: ...
```
#### Vim 插件调试
```vim
" 查看插件消息
:messages
" 检查插件是否加载
:echo exists('g:fcitx_remote')
" 手动测试函数
:call Fcitx2en()
:call Fcitx2zh()
InsertLeavePre -> 记录 Insert 中最后的中/英状态
InsertLeave -> 切到英文 ABC
InsertEnter -> 延迟恢复上次 Insert /英状态
VimEnter -> 启动后延迟切到英文 ABC
```
### 贡献指南
1. Fork 项目
2. 创建功能分支:`git checkout -b feature/amazing-feature`
3. 提交更改:`git commit -m 'Add amazing feature'`
4. 推送分支:`git push origin feature/amazing-feature`
5. 提交 Pull Request
关键点:
## 📋 版本历史
- `InsertLeavePre` 必须早于 `InsertLeave` 保存状态,否则 `InsertLeave` 已经切到英文。
- timer 回调会检查目标状态,避免快速 `i<Esc>` 后延迟回调把 Normal 模式切回中文。
- Hammerspoon 是主路径;`im-select` 只在 `hs` 不可用时 fallback。
- 状态记忆是 script-local 内存,只在当前 Vim 进程内有效。
| 版本 | 日期 | 主要更新 |
|------|------|----------|
| [v2.0.3](./CHANGELOG.md#203---2025-11-09) | 2025-11-09 | 修复 Normal 模式 ESC 键问题 |
| [v2.0.2](./CHANGELOG.md#202---2025-11-04) | 2025-11-04 | 修复终端兼容性问题 |
| [v2.0.0](./CHANGELOG.md#200---2025-11-04) | 2025-11-04 | 新增 Vim 原生插件支持 |
| [v1.0.8](./CHANGELOG.md#108---2025-01-04) | 2025-01-04 | 智能状态记忆功能 |
| [v1.0.0](./CHANGELOG.md#100---2024-06-01) | 2024-06-01 | 首次发布 |
## 手工测试
查看 [完整更新日志](./CHANGELOG.md) 获取详细的版本更新历史。
1. 打开 Vim
## 🔗 相关链接
```bash
vim test-im-switch.txt
```
- 📖 [English Documentation](./README_en.md)
- 📝 [更新日志](./CHANGELOG.md)
- 🛠️ [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx)
- 🐛 [问题反馈](https://github.com/yourusername/vim-im-switch
2. 第一次按 `i` 进入 Insert应切到中文。
3. 输入中文,按 `Esc`:应切到英文 `ABC`。
4. 再按 `i`:应恢复中文。
5. Insert 中手动切到英文,输入英文,按 `Esc`。
6. 再按 `i`:应恢复英文。
7. Insert 中手动切回中文,按 `Esc`,再按 `i`:应恢复中文。
8. 退出 Vim 后重新打开:第一次 Insert 默认中文,这是当前已知限制。
## 🙏 致谢
## 验证命令
感谢以下项目和贡献者
- [fcitx-remote-for-osx](https://github.com/xcodebuild/fcitx-remote-for-osx) - macOS 输入法控制工具
- [Obsidian](https://obsidian.md/) - 强大的知识管理工具
- [Vim](https://www.vim.org/) / [NeoVim](https://neovim.io/) - 经典的文本编辑器
Vimscript 加载检查
---
```bash
vim -Nu NONE -n -es -S "/path/to/vim-im-switch-select.vim" -c 'qa'
```
<div align="center">
Hammerspoon 检查:
**如果这个项目对你有帮助,请给个 ⭐ Star**
```bash
hs -c 'hs.keycodes.setLayout("ABC")'
hs -c 'hs.keycodes.setMethod("Pinyin Simplified")'
```
Made with ❤️ for Vim users
## 已知限制
</div>
### 跨 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

21
t.md Normal file
View File

@@ -0,0 +1,21 @@
jkfsd
及开发京东上看
开接口饭店司机
可激发卡的接口
福建省多孔菌科
及开发水电接口
激发开水大家
空间饭店
jkfd
kjfd
开娇娇饭店
kjfdk
空间发快递
jfdk
kj
激发开水大家看

View File

@@ -1,80 +0,0 @@
#!/bin/bash
# 测试 vim-im-switch-select.vim 插件
echo "=========================================="
echo "测试 vim-im-switch-select.vim 插件"
echo "=========================================="
echo ""
# 检查 im-select 是否可用
echo "1. 检查 im-select 是否存在..."
if [ -x "./im-select" ]; then
echo "✓ 找到 im-select 可执行文件"
IM_SELECT_CMD="./im-select"
elif command -v im-select &> /dev/null; then
echo "✓ 系统中已安装 im-select"
IM_SELECT_CMD="im-select"
else
echo "✗ 未找到 im-select"
exit 1
fi
echo ""
echo "2. 获取当前输入法..."
CURRENT_IM=$($IM_SELECT_CMD)
echo " 当前输入法: $CURRENT_IM"
echo ""
echo "3. 测试切换到英文输入法..."
$IM_SELECT_CMD com.apple.keylayout.ABC
sleep 0.5
NEW_IM=$($IM_SELECT_CMD)
if [ "$NEW_IM" = "com.apple.keylayout.ABC" ]; then
echo "✓ 成功切换到英文: $NEW_IM"
else
echo "✗ 切换失败,当前: $NEW_IM"
fi
echo ""
echo "4. 测试恢复原输入法..."
$IM_SELECT_CMD "$CURRENT_IM"
sleep 0.5
RESTORED_IM=$($IM_SELECT_CMD)
if [ "$RESTORED_IM" = "$CURRENT_IM" ]; then
echo "✓ 成功恢复到原输入法: $RESTORED_IM"
else
echo "✗ 恢复失败,当前: $RESTORED_IM"
fi
echo ""
echo "5. 检查 vim-im-switch-select.vim 文件..."
if [ -f "vim-im-switch-select.vim" ]; then
echo "✓ vim-im-switch-select.vim 文件存在"
echo ""
echo " 文件大小: $(wc -c < vim-im-switch-select.vim) 字节"
echo " 函数统计:"
grep -c "^function!" vim-im-switch-select.vim | xargs echo " 函数数量:"
else
echo "✗ vim-im-switch-select.vim 文件不存在"
exit 1
fi
echo ""
echo "=========================================="
echo "测试完成!"
echo "=========================================="
echo ""
echo "使用方法:"
echo "1. 复制插件到 Vim 配置目录:"
echo " cp vim-im-switch-select.vim ~/.vim/plugin/"
echo ""
echo "2. 或在 .vimrc 中添加:"
echo " source /Users/gavin/vim-im-switch/vim-im-switch-select.vim"
echo ""
echo "3. 可选配置(在 .vimrc 中):"
echo " let g:imselect_english_im = 'com.apple.keylayout.ABC'"
echo " let g:imselect_chinese_im = '$CURRENT_IM'"
echo ""
echo "4. 测试插件:"
echo " vim test-im-switch.txt"
echo ""

View File

@@ -1,11 +0,0 @@
测试输入法切换功能
Test IM Switch Functionality
使用说明:
1. 在 Vim 中加载此文件
2. 按 i 进入 Insert 模式
3. 输入中文内容
4. 按 ESC 退出到 Normal 模式(应该切换到英文输入法)
5. 再次按 i 进入 Insert 模式(应该恢复到中文输入法)
测试记录:

View File

@@ -1,9 +1,8 @@
" vim-im-switch-select.vim - 智能记忆输入法状态 (使用 im-select)
" vim-im-switch-select.vim - macOS Vim 输入法切换
" Author: Gavin Chan
" Modified by: codefalling, enhanced with smart IM state memory
" Version: 2.0.0
" Description: 记住退出 Insert 模式时的输入法状态,下次进入时自动恢复
" 使用 im-select 而不是 fcitx-remote 切换输入法
" Version: 2.3.1
" Description: Normal 模式固定英文Insert 模式恢复同进程内上次中/英状态
" ---------------------------------------------------------------------
" Load Once:
if exists('g:imselect_loaded')
@@ -21,19 +20,18 @@ endif
if exists('$SSH_TTY')
finish
endif
if !executable("im-select")
let s:hs_cmd = executable('hs') ? exepath('hs') : ''
let s:imselect_cmd = executable('im-select') ? exepath('im-select') : ''
if s:hs_cmd ==# '' && s:imselect_cmd ==# ''
finish
endif
let s:imselect_cmd = exepath('im-select')
let s:keepcpo = &cpo
let g:imselect_loaded = 1
set cpo&vim
" ---------------------------------------------------------------------
" 全局变量:记住上次 Insert 模式的输入法名称
let g:imselect_last_insert_im_name = ''
" 英文和中文输入法的 ID可以通过 im-select 获取)
" im-select fallback 使用输入源 IDHammerspoon 主路径使用 layout/method 名称。
if !exists('g:imselect_english_im')
let g:imselect_english_im = 'com.apple.keylayout.ABC'
endif
@@ -42,71 +40,167 @@ if !exists('g:imselect_chinese_im')
let g:imselect_chinese_im = 'com.apple.inputmethod.SCIM.ITABC'
endif
if !exists('g:imselect_hs_english_layout')
let g:imselect_hs_english_layout = 'ABC'
endif
if !exists('g:imselect_hs_chinese_method')
let g:imselect_hs_chinese_method = 'Pinyin Simplified'
endif
if !exists('g:imselect_restore_delay')
let g:imselect_restore_delay = 80
endif
if !exists('g:imselect_confirm_delay')
let g:imselect_confirm_delay = 200
endif
let s:restore_timer = -1
let s:confirm_timer = -1
let s:target = ''
" 只在当前 Vim 进程内记忆;退出 Vim 后会重新默认中文。
let s:last_insert_target = 'chinese'
" ---------------------------------------------------------------------
" Functions:
function! IMSelectCurrentIM() abort
if s:imselect_cmd ==# ''
return ''
endif
return substitute(system(shellescape(s:imselect_cmd) . ' 2>/dev/null'), '[\r\n]', '', 'g')
endfunction
function! s:CancelTimer(timer) abort
if a:timer != -1
silent! call timer_stop(a:timer)
endif
endfunction
function! s:CancelInsertTimers() abort
call s:CancelTimer(s:restore_timer)
call s:CancelTimer(s:confirm_timer)
let s:restore_timer = -1
let s:confirm_timer = -1
endfunction
function! s:RunHammerspoon(lua) abort
if s:hs_cmd ==# ''
return 0
endif
silent! call system(shellescape(s:hs_cmd) . ' -c ' . shellescape(a:lua) . ' >/dev/null 2>&1')
return v:shell_error == 0
endfunction
function! s:HammerspoonOutput(lua) abort
if s:hs_cmd ==# ''
return ''
endif
return substitute(system(shellescape(s:hs_cmd) . ' -c ' . shellescape(a:lua) . ' 2>/dev/null'), '[\r\n]', '', 'g')
endfunction
function! IMSelectSwitch(im) abort
if has('job')
silent! call job_start([s:imselect_cmd, a:im], {'out_io': 'null', 'err_io': 'null'})
if a:im ==# '' || s:imselect_cmd ==# ''
return
endif
silent! call system(shellescape(s:imselect_cmd) . ' ' . shellescape(a:im) . ' >/dev/null 2>&1')
endfunction
function! s:SwitchToEnglish() abort
" Hammerspoon 能直接设置 layout比只切 source ID 更贴近 macOS 实际输入状态。
if s:RunHammerspoon('hs.keycodes.setLayout(' . string(g:imselect_hs_english_layout) . ')')
return
endif
call IMSelectSwitch(g:imselect_english_im)
endfunction
function! s:SwitchToChinese() abort
" Apple 拼音可能出现 source ID 已切中文但 layout 仍为 ABC 的坏状态setMethod 可避免该问题。
if s:RunHammerspoon('hs.keycodes.setMethod(' . string(g:imselect_hs_chinese_method) . ')')
return
endif
call IMSelectSwitch(g:imselect_chinese_im)
endfunction
function! s:SwitchToTarget(target) abort
if a:target ==# 'english'
call s:SwitchToEnglish()
else
silent! call system(shellescape(s:imselect_cmd) . ' ' . shellescape(a:im) . ' >/dev/null 2>&1 &')
call s:SwitchToChinese()
endif
endfunction
function! s:DetectInsertTarget() abort
let layout = s:HammerspoonOutput('print(hs.keycodes.currentLayout() or "")')
let method = s:HammerspoonOutput('print(hs.keycodes.currentMethod() or "")')
if layout ==# g:imselect_hs_english_layout && method ==# ''
let s:last_insert_target = 'english'
elseif method ==# g:imselect_hs_chinese_method || layout ==# g:imselect_hs_chinese_method
let s:last_insert_target = 'chinese'
else
let current_im = IMSelectCurrentIM()
if current_im ==# g:imselect_english_im
let s:last_insert_target = 'english'
elseif current_im ==# g:imselect_chinese_im
let s:last_insert_target = 'chinese'
endif
endif
endfunction
function! IMSelectStart() abort
call IMSelectSwitch(g:imselect_english_im)
let s:target = 'english'
call s:CancelInsertTimers()
call s:SwitchToEnglish()
endfunction
function! IMSelectEnsureTarget(timer) abort
if g:imselect_last_insert_im_name != ''
call IMSelectSwitch(g:imselect_last_insert_im_name)
function! s:ConfirmTarget(timer) abort
let s:confirm_timer = -1
call s:SwitchToTarget(s:target)
endfunction
function! s:RestoreInsertTarget(timer) abort
let s:restore_timer = -1
" 防止快速 i<Esc> 后,延迟 timer 又把 Normal 模式切回中文。
if s:target !=# s:last_insert_target
return
endif
call s:SwitchToTarget(s:last_insert_target)
if g:imselect_confirm_delay > 0
let s:confirm_timer = timer_start(g:imselect_confirm_delay, function('s:ConfirmTarget'))
endif
endfunction
" 离开 Insert 模式前:保存当前输入法名称
" 必须在 InsertLeavePre 保存InsertLeave 中会立即切到英文。
function! IMSelectRemember() abort
let current_im = IMSelectCurrentIM()
if current_im != ''
let g:imselect_last_insert_im_name = current_im
endif
call s:DetectInsertTarget()
endfunction
" 离开 Insert 模式:切换到英文
function! IMSelect2en() abort
call IMSelectSwitch(g:imselect_english_im)
let s:target = 'english'
call s:CancelInsertTimers()
call s:SwitchToEnglish()
endfunction
" 恢复 Insert 模式输入法
function! IMSelectRestore(timer) abort
if g:imselect_last_insert_im_name == ''
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'))
let s:target = s:last_insert_target
call s:CancelInsertTimers()
if g:imselect_restore_delay > 0
let s:restore_timer = timer_start(g:imselect_restore_delay, function('s:RestoreInsertTarget'))
else
call s:RestoreInsertTarget(-1)
endif
endfunction
" ---------------------------------------------------------------------
" Autocmds:
" 绑定自动命令
function! BindIMSelectAu()
augroup IMSelect
autocmd!
" 离开 Insert 模式前:先保存输入法状态
autocmd InsertLeavePre * call IMSelectRemember()
" 离开 Insert 模式:切换到英文
autocmd InsertLeave * call IMSelect2en()
" 进入 Insert 模式:恢复上次的输入法状态
autocmd InsertEnter * call IMSelect2zh()
" Vim 启动后:切换到英文
autocmd VimEnter * call timer_start(100, {-> IMSelectStart()})
augroup END
endfunction
@@ -115,11 +209,11 @@ function! UnBindIMSelectAu()
augroup IMSelect
autocmd!
augroup END
call s:CancelInsertTimers()
endfunction
call BindIMSelectAu()
" 多光标支持:在选择多个光标前后的处理
function! IMSelect_multiple_cursors_before()
call UnBindIMSelectAu()
endfunction

View File

@@ -1,115 +0,0 @@
" fcitx-osx.vim - 智能记忆输入法状态
" Author: Gavin Chan
" Modified by: codefalling, enhanced with smart IM state memory
" Version: 2.0.0
" Description: 记住退出 Insert 模式时的输入法状态,下次进入时自动恢复
" ---------------------------------------------------------------------
" Load Once:
if exists('g:fcitx_remote')
finish
endif
if &ttimeoutlen <= 0 || &ttimeoutlen > 50
set ttimeoutlen=50
endif
if (has("win32") || has("win95") || has("win64") || has("win16"))
" Windows 下不要载入
finish
endif
if exists('$SSH_TTY')
finish
endif
if !executable("fcitx-remote")
finish
endif
let s:keepcpo = &cpo
let g:loaded_fcitx = 1
set cpo&vim
" ---------------------------------------------------------------------
" 全局变量:记住上次 Insert 模式的输入法名称
let g:fcitx_last_insert_im_name = ''
" 英文和中文输入法的 ID可以通过 fcitx-remote -n 获取)
if !exists('g:fcitx_english_im')
let g:fcitx_english_im = 'com.apple.keylayout.ABC'
endif
if !exists('g:fcitx_chinese_im')
let g:fcitx_chinese_im = 'auto-detect' " 将自动检测
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() 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() abort
if g:fcitx_last_insert_im_name == '' || g:fcitx_last_insert_im_name == g:fcitx_english_im
return
endif
call FcitxSwitch(g:fcitx_last_insert_im_name)
endfunction
" ---------------------------------------------------------------------
" Autocmds:
" 绑定自动命令
function! BindAu()
augroup Fcitx
autocmd!
" 离开 Insert 模式:保存输入法状态并切换到英文
autocmd InsertLeave * call Fcitx2en()
" 进入 Insert 模式:恢复上次的输入法状态
autocmd InsertEnter * call Fcitx2zh()
" Vim 启动后:切换到英文
autocmd VimEnter * call timer_start(100, {-> FcitxStart()})
augroup END
endfunction
function! UnBindAu()
augroup Fcitx
autocmd!
augroup END
endfunction
call BindAu()
" 多光标支持:在选择多个光标前后的处理
function! Multiple_cursors_before()
call UnBindAu()
endfunction
function! Multiple_cursors_after()
call Fcitx2en()
call BindAu()
endfunction
" ---------------------------------------------------------------------
" Restoration And Modelines:
let &cpo=s:keepcpo
unlet s:keepcpo
let g:fcitx_remote = 1
" vim:fdm=expr:fde=getline(v\:lnum-1)=~'\\v"\\s*-{20,}'?'>1'\:1