2

Occasionally, I need to edit text in Chinese, and the input method I use makes it extremely tedious to do so with vim. Each time I want to leave insert mode, I have to <C-]> (to return to normal mode) then <C-Space> (to return to the US-English keyboard layout). Otherwise, vim doesn't recognize any commands (naturally, since each Chinese character involves pressing a sequence of multiple keys).

I see that vim comes with certain input-method-aware features (e.g., :h 'imactivatekey'), but they appear to be compatible only with X11 + GTK. Is there any way to have vim change the input method automatically (or otherwise respond to the state of the system's current input method) on a Mac?

  • Does anything in this thread help? reddit.com/r/vim/comments/6nutcz/… – Rich Jul 28 '17 at 8:43
  • Something in there does, that's for sure. Will report back when I have a working solution. Thanks! – Ryan Lue Jul 28 '17 at 10:22
  • Actually, xkbswitch (the CLI utility for detecting/switching Mac input methods mentioned in that thread) has a funny bug for Chinese, which is the only IME I'm really interested in getting this to work with. So, I probably won't get around to fixing it. :\ – Ryan Lue Jul 28 '17 at 11:24
  • I've figured out a solution, but it introduces a short delay when switching back to the English input method when leaving insert mode. Would that be acceptable, or does your muscle memory require an instant switch? (If the latter, I'll explain in a comment, but won't bother writing it up as a proper answer.) – Rich Jul 28 '17 at 12:28
  • I'd love to hear it! Wouldn't object to it being presented as a proper answer, either ;) – Ryan Lue Jul 30 '17 at 11:44
1

Here's a hacky solution.

You can write a script to change your input mode using AppleScript. Create a file ~/.vim/switch_im.applescript with the following contents:

tell application "System Events"
    key code 49 using command down
end tell

This changes input method by emulating a Command+Space key press. (N.B. I'm using Command because that's the default shortcut. If you have changed this to Control, as indicated in your question, change command to control in the script.)

Then, add the following to your .vimrc (or to file(s) in .vim/after/ftplugin/ if you want to restrict the behaviour to particular file types):

" See note 1
function! SwitchToChinese()
  silent execute "!osascript " . $HOME . "/.vim/switch_im.applescript > /dev/null 2>&1 &"
  let g:chinese_im = 1
  redraw!
endfunction

" See note 2
function! SwitchToEnglish()
  if exists("g:chinese_im") && g:chinese_im
    silent execute "!osascript " . $HOME . "/.vim/switch_im.applescript > /dev/null 2>&1 &"
    let g:chinese_im = 0
    redraw!
  endif
endfunction

" See note 3
inoremap <f5> <c-o>:call SwitchToChinese()<cr>

" See note 4
augroup switchlanguage
  autocmd!
  autocmd InsertLeave * call SwitchToEnglish()
augroup END

" See note 5
set ttimeoutlen=0

The code is comprised of the following sections:

  1. A function to switch from English to Chinese: it works by running the switch input method script with the following command:

    silent execute "!osascript " . $HOME . "/.vim/switch_im.applescript > /dev/null 2>&1 &"

    This acts the same as running the command osascript ~/.vim/switch_im.applescript from the terminal. The rest of the line (the Vim silent and the shell redirection and backgrounding) is just intended to make sure nothing is displayed on the screen when the command is run.

    The function also sets a variable to indicate that the input method is now Chinese.

  2. A function to switch to English if the variable is set to indicate we are currently in Chinese. We don't want to switch if we're already in English!

  3. An insert mode mapping to switch to Chinese by pressing F5. For this code to work, you must always switch to Chinese using this mapping.

  4. An auto command to switch to English when leaving insert mode.

  5. In order to prevent a delay when pressing Escape to exit insert mode, we need to set a very short key-code delay. An alternative solution here would be set noesckeys.

Shortcomings

I said this solution was hacky. So what are the problems with it?

  1. There is a very slight delay when changing the language. If you press another key immediately after exiting insert mode, that keystroke may be lost. I tried compiling the script into a .scpt file with Script Editor, but it didn't make a noticeable difference.

  2. It's not a robust solution: in order to work properly, it needs to know which input method is currently in use. Therefore, you must only change to Chinese by pressing F5 in insert mode, and you must only change back to English by exiting insert mode.

    This fragility can be fixed by replacing the naïve switch_im.applescript script with language-specific scripts. Here's the AppleScript for changing to (British) English:

    tell application "System Events" to tell process "SystemUIServer"
      tell (1st menu bar item of menu bar 1 whose description is "text input") to {click, click (menu 1's menu item "British")}
    end tell
    

Using more specific scripts means you'll never accidentally change in the wrong direction, and removing the g:chinese_im guard too would mean you will always find yourself in English when you leave Insert Mode (and therefore don't need a script, function, or mapping for switching to Chinese).

However, this script runs noticeably more slowly than the key press version. During my testing I found the extra delay more disruptive than the requirements about switching languages, but, depending on your editing style, you may prefer the safer version. If you always stop and think for a second after leaving insert mode anyway, then the delay won't be an issue for you!

Attribution: The AppleScript code in this answer was nabbed from this question on superuser.com.

  • This is great! I'm in the process of writing a plugin to simplify this process, but rather than using AppleScript, it will depend on a third-party binary, which should mostly resolve the latency problem. (I was originally going to go with xkbswitch despite the bug I mentioned earlier, but then found input-source-switcher, which has the same bug but whose dev was faster to respond.) I'll let you know when it's done! – Ryan Lue Aug 3 '17 at 1:39
  • It occurs to me now that osascript can take script lines as command-line arguments, which would improve slightly the simplicity of installing this solution (although not its underlying complexity). The latency is a much bigger issue, though, so hope the dev fixes their bug soon! – Rich Aug 3 '17 at 8:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.