When I write, I want proper curly quotes, with the correct opening and closing ‘single’ and “double” quote marks. However, the standard QWERTY keyboard doesn’t have these keys, having only straight 'single' and "double" quotes. Microsoft Office and Apple Pages “Smart Quotes” will automatically fix this, but since I write this blog in Visual Studio Code, here is how I setup my keyboard.

macOS has a plethora of keyboard shortcuts, but alas, these are missing on Windows 10:

Keystroke Output
Option ⌥+[
Shift+Option ⌥+[
Option ⌥+]
Shift+Option ⌥+]

Fortunately, this is easy to setup in Visual Studio Code, simply by editing the key bindings configuration file:

  • On Windows, File > Preferences > Keyboard Shortcuts
  • On macOS, Code > Preferences > Keyboard Shortcuts
  • Next to the Keyboard Shortcuts tab, hit this button to “Open Keyboard Shortcuts (JSON)”.
  • Edit and save keybindings.json file, then restart Visual Studio Code.

For VS Code in Windows to behave exactly the same way as macOS:

Keystroke Output
Alt+[
Shift+Alt+[
Alt+]
Shift+Alt+]
[{
    "key": "alt+[",
    "command": "type",
    "args": {"text": "“"},
    "when": "editorTextFocus"
},{
    "key": "alt+]",
    "command": "type",
    "args": {"text": "‘"},
    "when": "editorTextFocus"
},{
    "key": "shift+alt+[",
    "command": "type",
    "args": {"text": "”"},
    "when": "editorTextFocus"
},{
    "key": "shift+alt+]",
    "command": "type",
    "args": {"text": "’"},
    "when": "editorTextFocus"
}]

But I want something even better:

  • If there is text selected the keystroke should enclose the text in the desired single or double curly quote.
  • In addition, since the single closing quote is often used (e.g. don’t, won’t), I also want to map it to another key, so that:
Keystroke Output
Alt+'

Luckily, VS Code is very flexible, the trick is to use the insertSnippet command instead of type. I got the idea from issue #26820 on the VS Code GitHub:

[{
    "key": "alt+[",
    "command": "editor.action.insertSnippet",
    "args": {"snippet": "“$TM_SELECTED_TEXT$0”"},
    "when": "editorTextFocus&&editorHasSelection"
},{
    "key": "alt+[",
    "command": "type",
    "args": {"text": "“"},
    "when": "editorTextFocus&&!editorHasSelection"
},{
    "key": "alt+]",
    "command": "editor.action.insertSnippet",
    "args": {"snippet": "‘$TM_SELECTED_TEXT$0’"},
    "when": "editorTextFocus&&editorHasSelection"
},{
    "key": "alt+]",
    "command": "type",
    "args": {"text": "‘"},
    "when": "editorTextFocus&&!editorHasSelection"
},{
    "key": "shift+alt+[",
    "command": "type",
    "args": {"text": "”"},
    "when": "editorTextFocus"
},{
    "key": "shift+alt+]",
    "command": "type",
    "args": {"text": "’"},
    "when": "editorTextFocus"
},{
    "key": "alt+'",
    "command": "type",
    "args": {"text": "’"},
    "when": "editorTextFocus"
}]

A few of notes:

  • I don’t mind that these keys apply for all file types, but it’s also possible to limit it to only MarkDown files in VS Code. Just append &&editorLangId==markdown to all the "when" conditions.
  • On macOS, I actually apply similar settings too, specifically for Alt+[, ] and ' (the first and third and last lines)!
  • Be aware that on macOS, the last line would overwrite the standard behaviour, but that’s ok since I never use æ.

Helpful?

Updated 16 July 2024:

I changed the keybindings some time ago, because I kept forgetting the keybinding! Now the ' is used for the closing curly quote, and the neighbouring ; key is used for the opening curly quotes, so that:

  • Cmd+; and +' adds curly single quotes ‘ ’,
  • Shift+Cmd+; and +' adds curly double quotes “ ”.

As above, either use the View > Command Palette menu or hit Shift+Cmd+P, type Preferences: Open Keyboard Shortcuts (JSON) to open the keybindings.json file, then add these keybindings at the appropriate location:

[ 
...
    {
        "key": "cmd+;",
        "command": "type",
        "args": {"text": "‘"},
        "when": "editorTextFocus&&!editorHasSelection"
    },{
        "key": "cmd+'",
        "command": "type",
        "args": {"text": "’"},
        "when": "editorTextFocus&&!editorHasSelection"
    },{
        "key": "cmd+shift+;",
        "command": "type",
        "args": {"text": "“"},
        "when": "editorTextFocus&&!editorHasSelection"
    },{
        "key": "cmd+shift+'",
        "command": "type",
        "args": {"text": "”"},
        "when": "editorTextFocus&&!editorHasSelection"
    },{
        "key": "cmd+;",
        "command": "editor.action.insertSnippet",
        "args": {"text": "‘"},
        "args": {"snippet": "‘$TM_SELECTED_TEXT$0’"},
        "when": "editorTextFocus&&editorHasSelection"
    },{
        "key": "cmd+'",
        "command": "editor.action.insertSnippet",
        "args": {"snippet": "“$TM_SELECTED_TEXT$0”"},
        "when": "editorTextFocus&&editorHasSelection"
    }
]

On Windows, I change cmd to ctrl.