What is the problem?

As I’ve mentioned in my previous post, I switched to MacOS. There are some things that really bug me and one of those things is how you switch between different language inputs. The default keyboard combination is Ctrl+Space which is really easy to use, but not that useful if you are a programmer (or at least if you are IDEs on your MAC :)).

Why is that you ask? Well, the reason is that Ctrl+Space brings up the suggestions (at least in Visual Studio Code and Visual Studio). Because I come from the Visual Studio’s world, I am really used to Ctrl+Shift+Space for parenthesis hints, so I’ve remapped this shortcut too. So in order not to swallow the Ctrl+Space shortcut in VS Code, I wanted to switch the language input with Ctrl+Shift which unfortunately is not possible out-of-the-box.

How can we solve it?

In order to be able to do that we need to use a 3rd party application, good for us, Karabiner is a free app and it would just do the work! You can think of it as AutoHotkey for MacOS. If you don’t know, I am a big fan of AutoHotkey so I needed an alternative. It’s not 1:1 match, for Karabiner doesn’t have a scripting language, but it supports a lot of different actions and you can pretty much associate any hotkey combination to do any action you want.

Using Karabiner

I am using an external keyboard at work with the MacBook Pro, so I really want a consistent as possible behavior with my 2 keyboard - the integrated one and the external one. In order to do so, I made the so-called “simple modifications”, they are just basic key remapping (e.g. map Option to Command). My final goal is the following - have the keys of both keyboards in the following order - Control, Option and Command, this is the MacBook’s keyboard, you can think of it as our baseline. So, what we want to do is to remap the external keyboard keys in the following way:

  • Option -> Command
  • Command -> Option In Karabiner you really have the Left Option and the Right Option, so you need to remap both the left and the right keys. This is not needed, I am not really sure if I used the right Command button at all, but nevertheless I’ve done it. :)

Complex modifications

There are these modifications, which are not just key remapping, but you can attach different behavior to different shortcuts. You can import such modifications from their site. If you are just testing locally, I would suggest directly editing ~/.config/karabiner/karabiner.json file (make sure you’ve made a backup). The config file is automatically loaded upon saving, so you get really fast feedback loop, very important when trying out new stuff.

SHOW ME THE CODE ALREADY!!!

Okay, okay, here it is.

{
  "title": "Language Toggler",
  "rules": [
    {
  "description": "Switch between BG <-> EN (Left Control + Left Shift)",
  "manipulators": [
    {
      "conditions": [
        {
          "input_sources": [
            {
              "language": "en"
            }
          ],
          "type": "input_source_if"
        }
      ],
      "from": {
        "key_code": "left_shift",
        "modifiers": {
          "mandatory": ["left_control"]
        }
      },
      "to": [
        {
          "key_code": "left_shift",
          "modifiers": ["left_control"]
        }
      ],
      "to_if_alone": [
        {
          "select_input_source": {
            "language": "bg"
          }
        }
      ],
      "type": "basic"
    },
    {
      "conditions": [
        {
          "input_sources": [
            {
              "language": "bg"
            }
          ],
          "type": "input_source_if"
        }
      ],
      "from": {
        "key_code": "left_shift",
        "modifiers": {
          "mandatory": ["left_control"]
        }
      },
      "to": [
        {
          "key_code": "left_shift",
          "modifiers": ["left_control"]
        }
      ],
      "to_if_alone": [
        {
          "select_input_source": {
            "language": "en"
          }
        }
      ],
      "type": "basic"
    }
  ]
}

  ]
}

What is basically done is a 2 state machine, which goes from one state to the other and vice versa. The other interesting thing to notice is how we remap the Ctrl+Shift shortcut. The Ctrl+Shift are remapped to itself, but an additional action is associated with it - to_if_only. This is kinda interesting and a bit hard for me to grasp in the beginning. I want to switch with Ctrl+Shift, but I don’t want to swallow Ctrl+Shift+Tab key press (e.g. traverse tabs in reverse order in Chrome). So what we basically do is the following - if Ctrl+Shift is pressed alone then switch the language, but if any other key is pressed after that - just send it as Ctrl+Shift+<KEY>. That’s why we need to remap Ctrl+Shift to itself.

If you have 3 or more language inputs you need to modify the code snippet accordingly, otherwise just change bg with your desired input language.

If you are not sure your language code or just want a confirmation, you can use Karabiner-EventViewer (it is installed as part of the Karabiner app): IntelliSense Arrow

Hope this was an useful article, till next time.