# 解决 Windows 中文输入法下 Shift + Space 快捷键的困扰
**Status**:: #x
**Zettel**:: #zettel/permanent
**Created**:: [[2024-04-10]]
**URL**:: [blog.iany.me](https://blog.iany.me/2024/04/resolve-the-annoying-shift-space-shortcut-in-windows-chinese-input-methods/)
最近我把主要的工作环境转移到了Windows系统。之前 Windows 最困扰我的是 <kbd>Shift</kbd> + <kbd>Space</kbd> 的快捷键。在中文输入法状态下稍微打字快一点,就会误触发切换半角全角的功能,打乱输入的节奏。既然我决定长期使用 Windows 系统,就花了一些时间全面解决了这个问题。
<!--more-->
## TL;DR
安装 [AutoHotkey](https://www.autohotkey.com/) v2 版。将以下代码加入到一个 `.ahk` 为后缀的文件中,比如“文档”目录中的 `main.ahk` 文件:
```
<+Space::Send (A_PriorKey = "LShift" ? "+{Space}" : "{Blind}{Shift up}{Space}{Shift down}{LWin}")
>+Space::Send (A_PriorKey = "RShift" ? "+{Space}" : "{Blind}{Shift up}{Space}{Shift down}{LWin}")
```
双击文件 `main.ahk` 即可生效,如果要启动时自动生效,可以为这个文件创建一个快捷方式,然后将快捷方式移到到“启动”目录中。可以通过以下办法快速打开“启动”目录。
- 使用快捷键 <kbd>Win</kbd> + <kbd>r</kbd> 打开运行窗口
- 输入 `shell:startup` 并使用 <kbd>Enter</kbd> 确认
## 思路说明
<kbd>Shift</kbd> + <kbd>Space</kbd> 烦人的地方在于它会吞掉本想输入的空格。输入需要 <kbd>Shift</kbd> 组合键的符号时,通常在前或者在后要加上空格,比如 `:(){}` 这些符号。如果输入速度很快,很可能会在 <kbd>Shift</kbd> 尚未完全松开时按下 <kbd>Space</kbd> ,这时候没有输入空格反而是切换了半角全角,如果当前需要使用半角,还得再按一次切换回来。
基于上述分析,我想实现在按下 <kbd>Shift</kbd> 键后,如果在按下空格键之前按下了其他键,便视为 <kbd>Shift</kbd> 键没有及时松开,应该输入一个空格符号。如果在按下 <kbd>Shift</kbd> 键后立即按下空格键,应保留原来的半角全角切换功能。这样的配置不但能避免误触发,还可以被利用来快速输入 `) {` 这样的序列,因为全程 <kbd>Shift</kbd> 不用松开了。
上面的 AutoHotkey 代码即实现了这里描述的功能。
## 代码说明
代码有两行,分别对应的左右手 <kbd>Shift</kbd> 触发的情况。我尝试过合并成一条但失败了,具体原因我也不清楚,也不知道为什么分开就能工作了。
下面以左手的第一行为例说明:
```
<+Space::Send (A_PriorKey = "LShift" ? "+{Space}" : "{Blind}{Shift up}{Space}{Shift down}{LWin}")
```
| AutoHotkey 代码 | 说明 |
| -------------------------------------------- | -------------------------------------------------------------------------- |
| `<+Space::` | 为 <kbd>Shift</kbd> + <kbd>Space</kbd> 设置命令,并只有使用左手的 <kbd>Shift</kbd> 才能触发。 |
| `Send` | 发送指定的按键序列。 |
| `(A_PriorKey = "LShift" ? X : Y)` | 如果变量 `A_PriorKey` 的值等于 `LShift` 时返回 X 否则返回 Y。 |
| `+{Space}` | 发送组合按键 <kbd>Shift</kbd> + <kbd>Space</kbd> 以触发其原本的功能,即切换半角全角。 |
| `{Blind}{Shift up}{Space}{Shift down}{LWin}` | 这么一大串其实只是为了实现在 AutoHotkey 里在 <kbd>Shfit</kbd> 按下的情况下输入一个空格符号。 |
第一个比较重要的点是自动变量 [A_PriorHotkey](https://www.autohotkey.com/docs/v2/Variables.htm#PriorHotkey)。它记录的是组合键所有键被按下之前上一个按键状态有变化的键。因为组合键最后一个按下的一定是非修饰键,所以如果按下 <kbd>Shift</kbd> 后立即按下 <kbd>Space</kbd>,`A_PriorHotkey` 就是 <kbd>LShift</kbd> 或者 <kbd>RShift</kbd>。相反,如果 `A_PriorHotkey` 等于其它值就表示按下 <kbd>Shift</kbd> 到按下 <kbd>Space</kbd> 之间有按过其它按键。
第二个点是用来模拟输入空格的一长串 `{Blind}{Shift up}{Space}{Shift down}{LWin}`
- `{Blind}` 的作用是让 `{Shift up}` 强制修改 <kbd>Shift</kbd> 的状态改为松开保证后面 `{Space}` 输入一个空格符号。
- `{Shift down}` 是恢复 <kbd>Shift</kbd> 按下的状态,这样后面可以继续输入还 <kbd>Shift</kbd> 的组合键。
- `{LWin}` 不会触发任何操作。它的作用是如果在中文输入法里启用了点按 <kbd>Shift</kbd> 来切换中英文,在下面的按键序列中会正确输入 `: ` (冒号+空格) 并且不会误触发中英文切换:
- 按下 <kbd>Shift</kbd>
- 按下 <kbd>:</kbd>
- 松开 <kbd>:</kbd>
- 按下 <kbd>Space</kbd>
- 松开 <kbd>Space</kbd>
- 松开 <kbd>Shift</kbd>