Skip to content

sleepymalc/VSCode-LaTeX-Inkscape

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VSCode-LaTeX-Inkscape

A way to integrate LaTeX, VS Code, and Inkscape in macOS.

Table of Content

Abstract

I use $\LaTeX$ heavily for both academic work and professional work, and I think I'm quite proficient in terms of typing things out in $\LaTeX$. But when I see the mind-blowing blog posts from Gilles Castel (RIP)-How I'm able to take notes in mathematics lectures using LaTeX and Vim and also How I draw figures for my mathematical lecture notes using Inkscape, I realize that I'm still far from fast, so I decided to adapt the whole setup from Linux-Vim to macOS-VS Code.

This setup is universal for VS Code users indeed. The only part that'll be macOS-specific is the Inkscape part (Inkscape-figures and Inkscape-shortcut-manager). While the first part can be replaced by super-figure (while I still prefer my setup, you can still try it out even if you're in macOS), and you can certainly achieve a similar result in Windows as in my Notes, the drawing speed will be slower without the shortcut manager. Just keep that in mind.

If you still don't know what to expect, please check out my Notes taken in this setup. Also, due to the VS Code recent update (1.76.1), we have the profile functionality available. Specifically, this is my current minimal profile for $\LaTeX$ I'm currently using, but since some configurations are not included in the profile, you should still read through everything.

Available: My website

Disclaimer

Please look through the two blog posts above by Gilles Castel! They are incredible and worth spending your time to understand how all things work, and what's the motivation behind all these. I'm only mimicking his workflow, with a little patience to set up the whole thing in my environment. Show respect to the original author!

Before we start anything serious, just copy the keybindings.json and settings.json into your own keybindings.json and settings.json. Don't worry, I'll explain what they do later.

// Place your key bindings in this file to override the defaultsauto[]i
[
// Sympy setup
{
"key": "shift+e",
"command": "latex-sympy-calculator.equal",
"when": "editorHasSelection && editorTextFocus && resourceLangId == 'latex' || editorHasSelection && editorTextFocus && resourceLangId == 'markdown'"
},
{
"key": "shift+alt+cmd+e",
"command": "-latex-sympy-calculator.equal",
"when": "editorHasSelection && editorTextFocus && resourceLangId == 'latex' || editorHasSelection && editorTextFocus && resourceLangId == 'markdown'"
},
{
"key": "shift+r",
"command": "latex-sympy-calculator.replace",
"when": "editorHasSelection && editorTextFocus && resourceLangId == 'latex' || editorHasSelection && editorTextFocus && resourceLangId == 'markdown'"
},
{
"key": "shift+alt+cmd+r",
"command": "-latex-sympy-calculator.replace",
"when": "editorHasSelection && editorTextFocus && resourceLangId == 'latex' || editorHasSelection && editorTextFocus && resourceLangId == 'markdown'"
},
// Auto Correction setup
{
"key": "cmd+l",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"cSpell.goToPreviousSpellingIssue",
{
"command": "editor.action.codeAction",
"args": {
"kind": "quickfix",
"apply": "first"
}
},
"cursorUndo",
]
}
},
{
"key": "cmd+k",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"editor.action.marker.prev",
{
"command": "editor.action.codeAction",
"args": {
"kind": "quickfix",
"apply": "first"
}
},
"cursorUndo",
]
}
},
// Inkscape setup
{
"key": "ctrl+f",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"editor.action.clipboardCopyAction",
"editor.action.insertLineAfter",
"cursorUp",
"editor.action.deleteLines",
{
"command": "editor.action.insertSnippet",
"args": {
"name": "incfig"
}
},
{
"command": "command-runner.run",
"args": {
"command": "inkscapeCreate",
},
"terminal": {
"name": "runCommand",
"shellArgs": [],
"autoClear": true,
"autoFocus": false
}
},
]
},
"when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Insert'"
},
{
"key": "ctrl+f",
"command": "command-runner.run",
"args": {
"command": "inkscapeEdit",
"terminal": {
"name": "runCommand",
"shellArgs": [],
"autoClear": true,
"autoFocus": false
}
},
"when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Normal'"
},
{
"key": "ctrl+f",
"command": "command-runner.run",
"args": {
"command": "inkscapeStart",
"terminal": {
"name": "runCommand",
"shellArgs": [],
"autoClear": true,
"autoFocus": false
}
},
"when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Visual'"
},
{
"key": "ctrl+c",
"command": "command-runner.run",
"args": {
"command": "quiver",
"terminal": {
"name": "runCommand",
"shellArgs": [],
"autoClear": true,
"autoFocus": false
}
},
"when": "editorTextFocus"
}
]

{
// Auto Correction setup
"cSpell.diagnosticLevel": "Warning",
// Inkscape setup
"command-runner.terminal.name": "runCommand",
"command-runner.terminal.autoClear": true,
"command-runner.terminal.autoFocus": false,
"command-runner.commands": {
"inkscapeCreate": "inkscape-figures create ${selectedText} ${fileDirname}/Figures/",
"inkscapeEdit": "inkscape-figures edit ${fileDirname}/Figures/",
"inkscapeStart": "inkscape-figures watch",
"quiver": "open -na 'Google Chrome' --args --new-window <path-to-quiver>/quiver/src/index.html"
},
// vsc-conceal
"conceal.revealOn": "active-line",
"conceal.substitutions": [
{
"language": [
"latex",
{
"pattern": "**/*.{tex}"
}
],
"substitutions": [
{
"ugly": "\\\\iiint",
"pretty": ""
},
{
"ugly": "\\\\iint",
"pretty": ""
},
{
"ugly": "\\\\int",
"pretty": ""
},
{
"ugly": "\\\\forall",
"pretty": ""
},
{
"ugly": "\\\\exists",
"pretty": ""
},
{
"ugly": "\\\\nexists",
"pretty": ""
},
{
"ugly": "\\\\leq",
"pretty": ""
},
{
"ugly": "\\\\geq",
"pretty": ""
},
{
"ugly": "\\\\gg",
"pretty": ""
},
{
"ugly": "\\\\ll",
"pretty": ""
},
{
"ugly": "\\\\notin",
"pretty": "",
"post": " "
},
{
"ugly": "\\\\in",
"pretty": "",
"post": " "
},
{
"ugly": "\\\\sim",
"pretty": ""
},
{
"ugly": "\\\\coloneqq",
"pretty": ""
},
{
"ugly": "\\\\eqqcolon",
"pretty": ""
},
{
"ugly": "\\\\subseteq",
"pretty": ""
},
{
"ugly": "\\\\subsetneq",
"pretty": ""
},
{
"ugly": "\\\\nsubseteq",
"pretty": ""
},
{
"ugly": "\\\\subset",
"pretty": ""
},
{
"ugly": "\\\\supseteq",
"pretty": ""
},
{
"ugly": "\\\\supsetneq",
"pretty": ""
},
{
"ugly": "\\\\nsupseteq",
"pretty": ""
},
{
"ugly": "\\\\supset",
"pretty": ""
},
{
"ugly": "\\\\sqcup",
"pretty": ""
},
{
"ugly": "\\\\cup",
"pretty": ""
},
{
"ugly": "\\\\cap",
"pretty": ""
},
{
"ugly": "\\\\times",
"pretty": "×"
},
{
"ugly": "\\\\sqrt",
"pretty": ""
},
{
"ugly": "\\\\infty",
"pretty": ""
},
{
"ugly": "\\\\bot",
"pretty": ""
},
{
"ugly": "\\\\prep",
"pretty": ""
},
{
"ugly": "\\\\top",
"pretty": ""
},
{
"ugly": "\\\\land",
"pretty": ""
},
{
"ugly": "\\\\lor",
"pretty": ""
},
{
"ugly": "\\\\lnot",
"pretty": "¬"
},
{
"ugly": "\\\\setminus",
"pretty": ""
},
{
"ugly": "\\\\equiv",
"pretty": ""
},
{
"ugly": "\\\\circ",
"pretty": ""
},
{
"ugly": "\\\\mid",
"pretty": "|"
},
{
"ugly": "\\\\colon",
"pretty": ":"
},
{
"ugly": "\\\\partial",
"pretty": ""
},
{
"ugly": "\\\\oplus",
"pretty": ""
},
{
"ugly": "\\\\otimes",
"pretty": ""
},
{
"ugly": "\\\\leftarrow",
"pretty": ""
},
{
"ugly": "\\\\longleftarrow",
"pretty": ""
},
{
"ugly": "\\\\Leftarrow",
"pretty": ""
},
{
"ugly": "\\\\impliedby",
"pretty": ""
},
{
"ugly": "\\\\Longleftarrow",
"pretty": ""
},
{
"ugly": "\\\\rightarrow",
"pretty": ""
},
{
"ugly": "\\\\longrightarrow",
"pretty": ""
},
{
"ugly": "\\\\Rightarrow",
"pretty": ""
},
{
"ugly": "\\\\implies",
"pretty": ""
},
{
"ugly": "\\\\Longrightarrow",
"pretty": ""
},
{
"ugly": "\\\\iff",
"pretty": ""
},
{
"ugly": "\\\\leftrightarrow",
"pretty": ""
},
{
"ugly": "\\\\uparrow",
"pretty": ""
},
{
"ugly": "\\\\downarrow",
"pretty": ""
},
{
"ugly": "\\\\to",
"pretty": "",
"post": " "
},
{
"ugly": "\\\\gets",
"pretty": ""
},
{
"ugly": "\\\\Uparrow",
"pretty": ""
},
{
"ugly": "\\\\Downarrow",
"pretty": ""
},
{
"ugly": "\\\\approx",
"pretty": ""
},
{
"ugly": "\\\\cdot",
"pretty": "·"
},
{
"ugly": "\\\\left",
"pretty": "\\l"
},
{
"ugly": "\\\\right",
"pretty": "\\r"
},
{
"ugly": "\\\\langle",
"pretty": ""
},
{
"ugly": "\\\\rangle",
"pretty": ""
},
{
"ugly": "\\\\vert",
"pretty": "|"
},
{
"ugly": "\\\\lVert",
"pretty": ""
},
{
"ugly": "\\\\rVert",
"pretty": ""
},
{
"ugly": "\\\\lfloor",
"pretty": ""
},
{
"ugly": "\\\\rfloor",
"pretty": ""
},
{
"ugly": "\\\\lceil",
"pretty": ""
},
{
"ugly": "\\\\rceil",
"pretty": ""
},
{
"ugly": "\\^\\{\\\\ast\\}",
"pretty": "^*"
},
{
"ugly": "\\\\ast",
"pretty": "*"
},
{
"ugly": "\\\\star",
"pretty": ""
},
{
"ugly": "\\^\\{\\\\prime\\}",
"pretty": "'"
},
{
"ugly": "\\\\prime",
"pretty": "'"
},
{
"ugly": "\\\\varnothing",
"pretty": ""
},
{
"ugly": "\\\\alpha",
"pretty": "α"
},
{
"ugly": "\\\\beta",
"pretty": "β"
},
{
"ugly": "\\\\gamma",
"pretty": "γ"
},
{
"ugly": "\\\\Gamma",
"pretty": "Γ"
},
{
"ugly": "\\\\zeta",
"pretty": "ζ"
},
{
"ugly": "\\\\delta",
"pretty": "δ"
},
{
"ugly": "\\\\Delta",
"pretty": "Δ"
},
{
"ugly": "\\\\eta",
"pretty": "η"
},
{
"ugly": "\\\\varepsilon",
"pretty": "ε"
},
{
"ugly": "\\\\epsilon",
"pretty": "ϵ"
},
{
"ugly": "\\\\rho",
"pretty": "ρ"
},
{
"ugly": "\\\\sigma",
"pretty": "σ"
},
{
"ugly": "\\\\Sigma",
"pretty": "Σ"
},
{
"ugly": "\\\\sum",
"pretty": "𝚺"
},
{
"ugly": "\\\\theta",
"pretty": "θ"
},
{
"ugly": "\\\\Theta",
"pretty": "Θ"
},
{
"ugly": "\\\\tau",
"pretty": "τ"
},
{
"ugly": "\\\\iota",
"pretty": "ɩ"
},
{
"ugly": "\\\\phi",
"pretty": "ϕ"
},
{
"ugly": "\\\\varphi",
"pretty": "φ"
},
{
"ugly": "\\\\Phi",
"pretty": "Φ"
},
{
"ugly": "\\\\psi",
"pretty": "ψ"
},
{
"ugly": "\\\\Psi",
"pretty": "Ψ"
},
{
"ugly": "\\\\lambda",
"pretty": "λ"
},
{
"ugly": "\\\\Lambda",
"pretty": "Λ"
},
{
"ugly": "\\\\kappa",
"pretty": "κ"
},
{
"ugly": "\\\\nabla",
"pretty": ""
},
{
"ugly": "\\\\mu",
"pretty": "μ"
},
{
"ugly": "\\\\psi",
"pretty": "ψ"
},
{
"ugly": "\\\\pi",
"pretty": "π"
},
{
"ugly": "\\\\Pi",
"pretty": "Π"
},
{
"ugly": "\\\\omega",
"pretty": "ω"
},
{
"ugly": "\\\\Omega",
"pretty": "Ω"
},
{
"ugly": "\\\\chi",
"pretty": "χ"
},
{
"ugly": "\\\\xi",
"pretty": "ξ"
},
{
"ugly": "\\\\Xi",
"pretty": "Ξ"
},
{
"ugly": "\\\\ell",
"pretty": ""
},
{
"ugly": "\\\\mathbb{A}",
"pretty": "𝔸"
},
{
"ugly": "\\\\mathbb{B}",
"pretty": "𝔹"
},
{
"ugly": "\\\\mathbb{C}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{D}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{E}",
"pretty": "𝔼"
},
{
"ugly": "\\\\mathbb{F}",
"pretty": "𝔽"
},
{
"ugly": "\\\\mathbb{G}",
"pretty": "𝔾"
},
{
"ugly": "\\\\mathbb{H}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{I}",
"pretty": "𝕀"
},
{
"ugly": "\\\\mathbb{J}",
"pretty": "𝕁"
},
{
"ugly": "\\\\mathbb{K}",
"pretty": "𝕂"
},
{
"ugly": "\\\\mathbb{L}",
"pretty": "𝕃"
},
{
"ugly": "\\\\mathbb{M}",
"pretty": "𝕄"
},
{
"ugly": "\\\\mathbb{N}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{O}",
"pretty": "𝕆"
},
{
"ugly": "\\\\mathbb{P}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{Q}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{R}",
"pretty": ""
},
{
"ugly": "\\\\mathbb{S}",
"pretty": "𝕊"
},
{
"ugly": "\\\\mathbb{T}",
"pretty": "𝕋"
},
{
"ugly": "\\\\mathbb{U}",
"pretty": "𝕌"
},
{
"ugly": "\\\\mathbb{V}",
"pretty": "𝕍"
},
{
"ugly": "\\\\mathbb{W}",
"pretty": "𝕎"
},
{
"ugly": "\\\\mathbb{X}",
"pretty": "𝕏"
},
{
"ugly": "\\\\mathbb{Y}",
"pretty": "𝕐"
},
{
"ugly": "\\\\mathbb{Z}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{A}",
"pretty": "𝒜"
},
{
"ugly": "\\\\mathcal{B}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{C}",
"pretty": "𝒞"
},
{
"ugly": "\\\\mathcal{D}",
"pretty": "𝒟"
},
{
"ugly": "\\\\mathcal{E}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{F}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{G}",
"pretty": "𝒢"
},
{
"ugly": "\\\\mathcal{H}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{I}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{J}",
"pretty": "𝒥"
},
{
"ugly": "\\\\mathcal{K}",
"pretty": "𝒦"
},
{
"ugly": "\\\\mathcal{L}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{M}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{N}",
"pretty": "𝒩"
},
{
"ugly": "\\\\mathcal{O}",
"pretty": "𝒪"
},
{
"ugly": "\\\\mathcal{P}",
"pretty": "𝒫"
},
{
"ugly": "\\\\mathcal{Q}",
"pretty": "𝒬"
},
{
"ugly": "\\\\mathcal{R}",
"pretty": ""
},
{
"ugly": "\\\\mathcal{S}",
"pretty": "𝒮"
},
{
"ugly": "\\\\mathcal{T}",
"pretty": "𝒯"
},
{
"ugly": "\\\\mathcal{U}",
"pretty": "𝒰"
},
{
"ugly": "\\\\mathcal{V}",
"pretty": "𝒱"
},
{
"ugly": "\\\\mathcal{W}",
"pretty": "𝒲"
},
{
"ugly": "\\\\mathcal{X}",
"pretty": "𝒳"
},
{
"ugly": "\\\\mathcal{Y}",
"pretty": "𝒴"
},
{
"ugly": "\\\\mathcal{Z}",
"pretty": "𝒵"
},
{
"ugly": "\\\\mathscr{A}",
"pretty": "𝓐"
},
{
"ugly": "\\\\mathscr{B}",
"pretty": "𝓑"
},
{
"ugly": "\\\\mathscr{C}",
"pretty": "𝓒"
},
{
"ugly": "\\\\mathscr{D}",
"pretty": "𝓓"
},
{
"ugly": "\\\\mathscr{E}",
"pretty": "𝓔"
},
{
"ugly": "\\\\mathscr{F}",
"pretty": "𝓕"
},
{
"ugly": "\\\\mathscr{G}",
"pretty": "𝓖"
},
{
"ugly": "\\\\mathscr{H}",
"pretty": "𝓗"
},
{
"ugly": "\\\\mathscr{I}",
"pretty": "𝓘"
},
{
"ugly": "\\\\mathscr{J}",
"pretty": "𝓙"
},
{
"ugly": "\\\\mathscr{K}",
"pretty": "𝓚"
},
{
"ugly": "\\\\mathscr{L}",
"pretty": "𝓛"
},
{
"ugly": "\\\\mathscr{M}",
"pretty": "𝓜"
},
{
"ugly": "\\\\mathscr{N}",
"pretty": "𝓝"
},
{
"ugly": "\\\\mathscr{O}",
"pretty": "𝓞"
},
{
"ugly": "\\\\mathscr{P}",
"pretty": "𝓟"
},
{
"ugly": "\\\\mathscr{Q}",
"pretty": "𝓠"
},
{
"ugly": "\\\\mathscr{R}",
"pretty": "𝓡"
},
{
"ugly": "\\\\mathscr{S}",
"pretty": "𝓢"
},
{
"ugly": "\\\\mathscr{T}",
"pretty": "𝓣"
},
{
"ugly": "\\\\mathscr{U}",
"pretty": "𝓤"
},
{
"ugly": "\\\\mathscr{V}",
"pretty": "𝓥"
},
{
"ugly": "\\\\mathscr{W}",
"pretty": "𝓦"
},
{
"ugly": "\\\\mathscr{X}",
"pretty": "𝓧"
},
{
"ugly": "\\\\mathscr{Y}",
"pretty": "𝓨"
},
{
"ugly": "\\\\mathscr{Z}",
"pretty": "𝓩"
},
{
"ugly": "\\\\mathfrak{A}",
"pretty": "𝔄"
},
{
"ugly": "\\\\mathfrak{B}",
"pretty": "𝔅"
},
{
"ugly": "\\\\mathfrak{C}",
"pretty": ""
},
{
"ugly": "\\\\mathfrak{D}",
"pretty": "𝔇"
},
{
"ugly": "\\\\mathfrak{E}",
"pretty": "𝔈"
},
{
"ugly": "\\\\mathfrak{F}",
"pretty": "𝔉"
},
{
"ugly": "\\\\mathfrak{G}",
"pretty": "𝔊"
},
{
"ugly": "\\\\mathfrak{H}",
"pretty": ""
},
{
"ugly": "\\\\mathfrak{I}",
"pretty": ""
},
{
"ugly": "\\\\mathfrak{J}",
"pretty": "𝔍"
},
{
"ugly": "\\\\mathfrak{K}",
"pretty": "𝔎"
},
{
"ugly": "\\\\mathfrak{L}",
"pretty": "𝔏"
},
{
"ugly": "\\\\mathfrak{M}",
"pretty": "𝔐"
},
{
"ugly": "\\\\mathfrak{N}",
"pretty": "𝔑"
},
{
"ugly": "\\\\mathfrak{O}",
"pretty": "𝔒"
},
{
"ugly": "\\\\mathfrak{P}",
"pretty": "𝔓"
},
{
"ugly": "\\\\mathfrak{Q}",
"pretty": "𝔔"
},
{
"ugly": "\\\\mathfrak{R}",
"pretty": ""
},
{
"ugly": "\\\\mathfrak{S}",
"pretty": "𝔖"
},
{
"ugly": "\\\\mathfrak{T}",
"pretty": "𝔗"
},
{
"ugly": "\\\\mathfrak{U}",
"pretty": "𝔘"
},
{
"ugly": "\\\\mathfrak{V}",
"pretty": "𝔙"
},
{
"ugly": "\\\\mathfrak{W}",
"pretty": "𝔚"
},
{
"ugly": "\\\\mathfrak{X}",
"pretty": "𝔛"
},
{
"ugly": "\\\\mathfrak{Y}",
"pretty": "𝔜"
},
{
"ugly": "\\\\mathfrak{Z}",
"pretty": ""
},
]
}
],
}

Also, create a snippet file for $\LaTeX$ in the following steps:

  1. Press shift+cmd+p to open the VS Code command.

  2. Type snippets, and choose Snippets: Configure Snippets.

  3. Choose New Global Snippets file....

  4. Enter latex to create a new file.

  5. Paste the latex.json into that file.

    {
    "incfig": {
    "prefix": "incfig",
    "body": [
    "\\begin{figure}[H]",
    "\t\\centering",
    "\t\\incfig{${1:$CLIPBOARD}}",
    "\t\\caption{${2:title}}",
    "\t\\label{fig:${1:$CLIPBOARD}}",
    "\\end{figure}",
    ],
    "description": "Inserts mathematical diagram"
    }
    }

Setup For Typing Blasting Fast

First thing first, please set up your VS Code with $\LaTeX$ properly with LaTeX Workshop, there are lots of tutorials online, just check them out and set them up properly. Basically, it can be done in the following steps:

  1. Download MacTex. This can be replaced by something more lightweight, but in my opinion, this doesn't really help much in terms of speed or wasting your disk. But if you want something like this, check out TeXLive.

  2. Download LaTeX Workshop

  3. Copy-pasting the following configuration file into your settings.json

    "latex-workshop.latex.autoBuild.run": "onSave"

    This will save your time by compiling your $\LaTeX$ project whenever you save your file by cmd+s.

Now, we go through things one by one following Gilles Castel's blog post.

Tex Conceal

To achieve a similar result as in Gilles Castel's setup, there is an extension called vsc-conceal for VS Code. All the setup is in the setting.json, and since this setup is quite straightforward, I'll just give a snapshot to show how it looks in practice.

Note that I set the "conceal.revealOn" to "active-line", which is why you will see the source code in line 51. There are other options you can choose, see the original repo for details.

HyperSnips

If you look around in the VS Code extension marketplace to find UltiSnips' equivalence, you probably will find Vsnips. But I'm not sure why this is the case, I can't figure out how to set it up properly. Hence, I find another alternative, which is HyperSnips. Please first download HyperSnips and just follow the instructions, copy latex.hsnips into $HOME/Library/Application Support/Code/User/globalStorage/draivin.hsnips/hsnips/, and you're good to go!

To modify this file, you can either go to this file in your finder or use the VS Code built-in command function. For commands function,

  1. Press shift+cmd+space to type in some commands to VS Code.
  2. Type >HyperSnips: Open Snippet File
  3. Choose latex.hsnips

After doing this, you're all set. But a big question is, what exactly is a snippet?

Snippets

A snippet is a short reusable piece of text that can be triggered by some other text. For example, when I type dm (stands for display math), the word dm will be expanded to a display math environment:

If you are a math guy, you may need to type some inline math like \(\), which is kind of painful. But with snippets, you can have

See? You just type fm (not the best choice here, but since im is a common prefix, so can't really use that as our snippet 🥲), and then your snippet not only automatically types \(\) for you, but it also sends your cursor between \(\)! With this, you can type something really fast:

Note that in the above demo, I use a very common snippet, qs for ^{2}.

As you can imagine, this can be quite complex. For example, you can even have something like this:

or this:

For the first snippet, I type table2 5, and then it generates a table with 2 rows and 5 columns. For the second one, I type pmat for matrix, and then type 2 5 to indicate that I want a 2 by 5 matrix, then boom! My snippets do that for me in an instant!

My snippet file includes commonly used snippets as suggested in the original posts, you can look into it to better understand how it works. And maybe you can create your snippets also! Here are some useful snippets for you.

Math Environment

In the recent update of HyperSnips, the context functionality is implemented, which is very useful, and you should understand how it works. If you look at the top of the snippet file, you will see

global
function math(context) {
    return  context.scopes.findLastIndex(s => s.startsWith("meta.math")) > context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex"));
}
endglobal

And for some snippets, you will see context math(context) in front of which, e.g., the greater or equal snippet:

context math(context)
snippet `>=|(?<!\\)geq` "greater or equal to" A
\geq $0
endsnippet

while some do not, e.g., the display math snippet:

snippet dm "display Math" bA
\[
	${1}
\]$0
endsnippet

Basically, the context specifies when the snippet will be triggered, and I define the function math(context) to determine when we are in the math environment. This is quite important since the official math(context) function for detecting math scope for $\LaTeX$ is given by

global
function math(context) {
    return context.scopes.some(s => s.startsWith("meta.math")) && !context.scopes.some(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex"));
}
endglobal

However, this leads to some problems. For example, sometimes, in the equation, I want to write

\[
    x_n = x \text{ for some \(n\) large enough},
\]

Such a case is fine since the math I want to write in the text scope is simple, just \(n\). However, in the current (and perhaps most popular) scope function, it happens that \(\text{ \( not in the math mode \) }\).

To overcome this, I write the following more generic version:

global
function math(context) {
    return  context.scopes.findLastIndex(s => s.startsWith("meta.math")) > context.scopes.findLastIndex(s => s.startsWith("comment") || s.startsWith("meta.text.normal.tex"));
}
endglobal

So, since the nested environment is ordered in the scope, this will always return the correct mode. Even better, there will be no undefined behavior since if the .findLastIndex can't find either, it will return -1 instead of something undefined, so everything is handled.

Sympy and Mathematica

Unlike Gilles Castel's approach, there is an available extension out there for you to simplify your math calculation already! Please go check out Latex SYMPY Calculator. It works like follows:

Magic right? Let's set it up! First, please look at the installation document provided by Latex Sympy Calculator. After your installation, you can set up the keybinding for calculating the math expression. I use shift+e, where e stands for evaluating, to calculate so that it will append an equal sign and the answer right after your formula, just like above. If you want to avoid showing the intermediate steps of your calculation, you can use shift+r, where r stands for replacing, to directly replace the whole formula and give me the answer only. See the demo below:

This plugin is indeed more powerful than just this, see the documentation for detail.

Let's go to the last thing covered in Gilles Castel's post, correcting spelling mistakes.

Correcting Spelling Mistakes on the Fly

Although my typing speed is quite high, I have typos all the time. So this is a must for me. And surprisingly, this is the hardest thing until now for me to set it upright. Let's see how we can configure this functionality in VS Code! There are three plugins we need:

  1. multi-command: This is a very powerful extension, which allows you to do a sequence of actions in one shortcut. We will use this later on also, and that's the place it shines.

  2. Code Spell Checker: This is a popular spelling checker out there that meets our needs.

  3. LTeX: If you are bad at grammar like me, you definitely want to install to check some simple grammar mistakes for you. Although it's not as powerful as Grammarly, not even comparable, it's still a good reference for you to keep your eyes on some simple mistakes you may overlook.

    There is an unofficial API for Grammarly, and the plugin can be found here. Though it's quite slow...

Here is a quick demo of how it works when typing:

Additionally, if you also want to correct your grammar error, I use the shortcut cmd+k to trigger a quick-fix for a general error.

Detail Explanation

You can skip this part if you don't want to know the working mechanism. But if you're interested, please follow! The following code snippet in settings.json is responsible for correcting your spelling mistakes by just clicking cmd+l.

{
    "key": "cmd+l",
    "command": "extension.multiCommand.execute",
    "args": {
        "sequence": [
	    "cSpell.goToPreviousSpellingIssue",
	    {
	        "command": "editor.action.codeAction",
	        "args": {
	            "kind": "quickfix",
		    "apply": "first"
                }
	    },
	    "cursorUndo",
        ]
    }
},

Make sure that the curly braces above have a trailing comma, otherwise, VS Code will complain about it.

The working mechanism is as follows. When you press cmd+l, the multi-command will do the following:

  1. Use one of the default function from cSpell's: goToPreviousSpellingIssue, which jump your cursor on that spelling error word
  2. Triggered a default editor action, with the argument being quickfix to open a quick fix drop-down list, and choose the first suggestion
  3. Move your cursor back by cursorUndo

And likewise, the following code snippet is responsible for correcting grammar mistakes.

{
    "key": "cmd+k",
    "command": "extension.multiCommand.execute",
    "args": {
        "sequence": [
            "editor.action.marker.prev",
            {
                "command": "editor.action.codeAction",
                "args": {
                    "kind": "quickfix",
                    "apply": "first"
                }
            },
            "cursorUndo",
        ]
    }
 },

Now, the first part is over. Let's go to the next truly beautiful, elegant, and exciting world, drawing with Inkscape.

Drawing Like a Pro - With Inkscape

For more examples, check out the original blog. Or for more figures I draw, you can check out Note.

One last thing is that I'll assume you have already installed VS Code Vim. While this is not required, if you don't want to use it, then you'll need to assign different keybinding. Anyway, you'll see what I mean until then!

Inkscape

A big question is, why Inkscape? In the original blog, he had already explained it. One reason is that although $\texttt{TikZ}$ can do the job of drawing vector figures in $\LaTeX$ with original support, it's too slow to set all diagrams right. This is so true since my experience with $\texttt{TikZ}$ is nice looking and intuitive but also slow and bulky. Also, the $\texttt{TikZ}$ code tends to be long. A large file will take latexindent and pdfLaTeX a minute to compile for one save. That's not efficient at all, especially when you want some instant feedback for some small changes.

Download Inkscape

You need to install Inkscape first. I recommend you install this in a terminal. I assume that you have your homebrew installed. Then, just type the following into your terminal:

> brew install --cask inkscape

Set up the Environment in LaTeX

First thing first, include the following in your preamble

\usepackage{import}
\usepackage{xifthen}
\usepackage{pdfpages}
\usepackage{transparent}

\newcommand{\incfig}[1]{%
    \def\svgwidth{\columnwidth}
    \import{./Figures/}{#1.pdf_tex}
}

And to use it in your code, it's like the following:

\begin{figure}[H]
    \centering
    \incfig{figure's name}
    \caption{Your caption}
    \label{fig:label}
\end{figure}

And then you're done! Also, the compilation time for this is shorter than you can ever expect. Let's get started then!

This assumes that your $\LaTeX$ project's home directory looks like this:

LaTeX_project
    ├── main.tex
    ├── main.pdf
    ├── Figures
    │    ├── fig.pdf
    │    ├── fig.pdf_tex
    │    ├── fig.svg
    │    .
    .

Now, let's get into the fun part, i.e., setting up the shortcut for this.

Inkscape Figure Manager

This is a figure manager developed by Gilles Castel, and here is the repo. I recommend you follow the installation instructions there. Here are just some guidelines for you.

  1. Install choose (specifically for macOS, rofi for Linux instead):

    > brew install choose-gui
  2. Install fswatch:

    > brew install fswatch
  3. Install the Inkscape figure manager:

    > pip3 install inkscape-figures

    After installing it, type inkscape-figures in your terminal to make sure you have corrected install it.

If you're using Linux and Vim, then you are done already. But since you're using macOS and VS Code, please follow me, there are some more things for you to configure.

If you're using Windows, then check out super-figure. It implements similar functionalities but in a more chunky way. Even if you're using macOS, you can try it too, although I prefer my setup.

Set up Inkscape Figure Manager

Firstly, install the Command Runner. This will allow you to send commands into a terminal with the shortcut. The configuration is in settings.json, and we'll see how it works later. Now, this is a tricky part: you need to find the source code of the inkscape-figures manager. In my case, it's in /Users/pbb/opt/anaconda3/lib/python3.8/site-packages/inkscapefigures.

Using global finding may be helpful...

Open this directory by VS Code, there is something for you to modify. Ok, I know you probably don't have that much patience now, so I have a modified version available here. Just replace the whole directory with mine, and you're good to go.

Notice that the directory in this repo is named Inkscape-figure-manager, while in your system, it should be inkscapefigures.

Detail Explanation

In Gilles Castel's approach, he uses the shortcut ctrl+f to trigger this script, which will copy the whole line's content depending on the cursor's position, and the script will send the snippets by the function

def latex_template(name, title):
    return '\n'.join((r"\begin{figure}[ht]",
                      r"    This is a custom LaTeX template!",
                      r"    \centering",
                      rf"    \incfig[1]{{{name}}}",
                      rf"    \caption{{{title}}}",
                      rf"    \label{{fig:{name}}}",
                      r"\end{figure}"))

to stdout, and then create a figure by the name, which is the content of the line.

But this in VS Code is impossible, hence we don't need this, we'll use command line. And if we leave this function as it was, then it will send all these snippets into our terminal, which is quite annoying. So the modified version just removes this snippet completely.

But let me explain it to you, in case you want to modify it to meet your need later on. First thing first, we see that in the given code in keybindings.json and settings.json, we're using Command Runner, so let me tell you how to set this up first.

We're now prepared to see a detailed explanation of commands provided in Inkscape figure manager. There are three different commands in the Inkscape figure manager. We break it down one by one.

Watch

Since Inkscape by default does not save the file in pdf+latex, we need Inkscape figure manager to help us. We need to first open the file watcher to watch the file for any changes. If there is any, then the file watcher will tell Inkscape to save the file in pdf+latex format.

To open the file watcher, you can type inkscape-figures watch in the terminal. But remember the Command Runner we just installed? We can assign this command with a keybinding! In my case, since I don't want to introduce more than one keybinding for Inkscape-figures manager, I use mode provided by vim to help us. In VISUAL mode (enter by v in NORMAL mode), press ctrl+f.

You should trigger this at the beginning. i.e., use this after you open your project folder. To check whether watch is triggered correctly, you can simply open the terminal and see what's the output when you press ctrl+f: If it's already triggered, then it'll show

> inkscape-figures watch
Unable to lock on the pidfile.

Otherwise it'll simply show nothing. (Remember to select the terminal corresponds to runCommand!)

Detail Explanation

In keybindings.json, we have

{
    "key": "ctrl+f",
    "command": "command-runner.run",
    "args": {
        "command": "inkscapeStart",
        "terminal": {
            "name": "runCommand",
            "shellArgs": [],
            "autoClear": true,
            "autoFocus": false
        }
    },
    "when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Visual'"
}

for starting the Inkscape figure manager. And the command is defined in settings.json:

"command-runner.commands": {
    "inkscapeStart": "inkscape-figures watch"
}

In detail, we just use Command Runner to run the command we defined in settings.json, in this case, I explicitly tell the keybinding ctrl+f will trigger inkscapeStart when I'm in VISUAL mode in Vim, which is just inkscape-figures watcher as defined above.

Notice that we set the autoFocus=false for the terminal Command Runner uses since we don't want a pop-up terminal to distract us. If you want to see whether the command is triggered correctly every time, you can set it to true.

Create

Same as above, we also use ctrl+f to trigger inkscape-figures create command. But in this case, we use INSERT for creating a new Inkscape figure. Specifically, we first type out the image's name we want our image to be called, then, in this case, we're already in INSERT mode, we just press ctrl+f to create this image after naming.

Detail Explanation

We set up our keybindings.json as

{
    "key": "ctrl+f",
    "command": "extension.multiCommand.execute",
    "args": {
        "sequence": [
	    "editor.action.clipboardCopyAction",
	    "editor.action.insertLineAfter",
	    "cursorUp",
	    "editor.action.deleteLines",
	    {
	        "command": "editor.action.insertSnippet",
		"args": {
		    "name": "incfig"
		}
            },
            {
	        "command": "command-runner.run",
		"args": {
		    "command": "inkscapeCreate",
		},
		"terminal": {
		    "name": "runCommand",
		    "shellArgs": [],
		    "autoClear": true,
		    "autoFocus": false
		}
            },
	]
    },
    "when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Insert'"
},

and also in settings.json:

"command-runner.commands": {
    "inkscapeCreate": "inkscape-figures create ${selectedText} ${fileDirname}/Figures/"
}

We break down what ctrl+f do in INSERT mode exactly step by step. We see that when we press ctrl+f in INSERT mode, we trigger multiCommand.execute to execute a sequence of instructions, which are

  1. Copy the content into your clipboard of the line your cursor at
  2. Insert a blank line after since we need to insert a snippet, and that will delete an additional line. You can try to delete this and the next instruction, and see what happens.
  3. Move back our cursor after inserting that new line.
  4. Delete that copied content by removing this line.
  5. Insert a snippet defined in latex.json. Notice that this is the default snippet functionality built-in VS Code, not HyperSnips we have used before. I'll explain where to copy this file in a minute.
  6. Lastly, we send a command in a terminal by Command Runner, with the command inkscapeCreate we defined in settings.json.

In the fifth instruction, the snippet we used is

{
"incfig": {
"prefix": "incfig",
"body": [
"\\begin{figure}[H]",
"\t\\centering",
"\t\\incfig{${1:$CLIPBOARD}}",
"\t\\caption{${2:title}}",
"\t\\label{fig:${1:$CLIPBOARD}}",
"\\end{figure}",
],
"description": "Inserts mathematical diagram"
}
}

which is just the snippet we remove from Inkscape figure manager's source code! It's back again, in a different approach.

Let me break it down for you. Firstly, I changed into INSERT mode in VS Code Vim and typed my new figure's name figure-test. Then, I press ctrl+f to trigger the keybinding, which will automatically create an Inkscape figure named figure-test for me and open it.

The three files will be created along the way: figure-test.pdf, figure-test.pdf_tex and figure-test.svg. Unfortunately, to rename a file, you'll need to manually rename three of them.

Edit

Again, we also use ctrl+f to trigger inkscape-figures edit command, but this time in NOMAL mode. Here, choose comes into play. After you select the image you want to edit in Inkscape, you simply press enter and it'll open that image for you to edit.

You can modify the styling of choose. For example, in picker.py, we have the following:

def get_picker_cmd(picker_args=None, fuzzy=True):
    """
    Create the shell command that will be run to start the picker.
    """
    if SYSTEM_NAME == "Darwin":
        args = ["choose"]
    #   args = ["choose", "-u", "-n", "15", "-c", "BB33B7", "-b", "BF44C8"]

We see that we don't have any additional argument for choose, but if you want, you can replace this line by the next line, which modify the style of choose. For detail information, type choose -h to see all the options.

Detail Explanation

The corresponding keybinding in 'keybindings.json' is:

{
    "key": "ctrl+f",
    "command": "command-runner.run",
    "args": {
        "command": "inkscapeEdit",
        "terminal": {
            "name": "runCommand",
            "shellArgs": [],
            "autoClear": true,
            "autoFocus": false
        }
    },
    "when": "editorTextFocus && vim.active && vim.use<C-f> && !inDebugRepl && vim.mode == 'Normal'"
},

and also in settings.json:

"command-runner.commands": {
    "inkscapeEdit": "inkscape-figures edit ${fileDirname}/Figures/"
}

I think now it's clear enough how all these work together to trigger the corresponding command. When you press ctrl+f in NORMAL mode, you'll trigger the inkscape-figures edit command, and it'll look into your Figures/ subfolder to see what figures you have and pop out a window for you to choose, which is the functionality provided by choose.

In the following demo, I create another figure named figure-test2, then modify it a little, and compile it again.

Inkscape Shortcut Manager

In this section, we'll set up a very efficient shortcut manager to help you draw any mathematical figures faster than you can ever imagine! Notice that this setup is quite complicated, but the result is quite good. It depends on

Please download the above two apps.

This section is contributed purely by @kiryph in #1.

Karabiner Elements

We'll need Karabiner Elements' Complex Modifications to help us. The steps are the following (adapted from ️⌨ How to type?).

  1. Open Karabiner-Elements, go to Misc and click on Export & Import.
  2. Copy Inkscape.json into .config/karabiner/assets/complex_modifications.
  3. Again open Karabiner-Elements, go to Complex Modifications and click on Add rule.
  4. Enable it.

If you're interested in how Inkscape.json is created, see the following.

Detail Explanation

The Inkscape.json is created by using a jsonnet file. The file can be found here,

local arr = [
[x, y, z] // 24x
for x in ['s', 'd', 'e'] // solid, dotted, dashed
for y in ['f', 'w', 'a', 'x'] // gray, white, arrow, double-arrow
for z in ['g', 'v'] // thick, very thick
] +
[
[x, y] // 18x
for x in ['s', 'd', 'e'] // solid, dotted, dashed
for y in ['f', 'w', 'g', 'v', 'a', 'x']
] +
[
['spacebar', x] // 6x
for x in ['s', 'd', 'e', 'f', 'b', 'w'] // solid, dotted, dashed, gray, black, white
];
{
title: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)',
rules: [
{
description: 'Apply quickly essential shape or line styles in Inkscape using hammerspoon (Gilles Castel, 2019)',
manipulators: [
{
local str = std.join('+', el),
type: 'basic',
from: {
simultaneous: [{ key_code: i } for i in el],
simultaneous_options: {
key_up_when: 'all',
},
},
to: [{ shell_command: "/usr/local/bin/hs -c 'hs.alert(\"" + str + '");create_svg_and_paste({"' + std.join('","', el) + "\"});'" }],
conditions: [{ type: 'frontmost_application_if', bundle_identifiers: ['org.inkscape.Inkscape'] }],
}
for el in arr
],
},
],
}

and the jsonnet tool can be installed via > brew install jsonnet.

Converting the jsonnet file into the json file for Karabiner Elements can be done as follows

> jsonnet karabiner-Inkscape.jsonnet > ~/.config/karabiner/assets/complex_modifications/karabiner-Inkscape.json

Hammerspoon

Firstly, open the Hammerspoon console and run hs.ipc.cliInstall() to install the cli command hs. Then, just add the following code to your ~/.hammerspoon/init.lua.

-- Make cli command `hs` available:
-- After an update of hammerspoon run following two commmands once in the hammerspoon console
-- hs.ipc.cliUninstall(); hs.ipc.cliInstall()
require("hs.ipc")
local function intersect(m,n)
local r={}
for i,v1 in ipairs(m) do
for k,v2 in pairs(n) do
if (v1==v2) then
return true
end
end
end
return false
end
local function has_value (tab, val)
for index, value in ipairs(tab) do
if value == val then
return true
end
end
return false
end
function create_svg_and_paste(keys)
pt = 1.327 -- pixels
w = 2 * pt
thick_width = 4 * pt
very_thick_width = 8 * pt
style = {}
style["stroke-opacity"] = 1
if intersect({"s", "a", "d", "g", "h", "x", "e"}, keys)
then
style["stroke"] = "black"
style["stroke-width"] = w
style["marker-end"] = "none"
style["marker-start"] = "none"
style["stroke-dasharray"] = "none"
else
style["stroke"] = "none"
end
if has_value(keys, "g")
then
w = thick_width
style["stroke-width"] = w
end
if has_value(keys, "v")
then
w = very_thick_width
style["stroke-width"] = w
end
if has_value(keys, "a")
then
style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
end
if has_value(keys, "x")
then
style['marker-start'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
end
if has_value(keys, "d")
then
style['stroke-dasharray'] = tostring(w) .. ',' .. tostring(2*pt)
end
if has_value(keys, "e")
then
style['stroke-dasharray'] = tostring(3*pt) .. ',' .. tostring(3*pt)
end
if has_value(keys, "f")
then
style['fill'] = 'black'
style['fill-opacity'] = 0.12
end
if has_value(keys, "b")
then
style['fill'] = 'black'
style['fill-opacity'] = 1
end
if has_value(keys, "w")
then
style['fill'] = 'white'
style['fill-opacity'] = 1
end
if intersect(keys, {"f", "b", "w"})
then
style['marker-end'] = 'none'
style['marker-start'] = 'none'
end
if not intersect(keys, {"f", "b", "w"})
then
style['fill'] = 'none'
style['fill-opacity'] = 1
end
svg = [[
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg>
]]
if (style['marker-end'] ~= nil and style['marker-end'] ~= 'none') or
(style['marker-start'] ~= nil and style['marker-start'] ~= 'none')
then
svgtemp = [[
<defs id="marker-defs">
<marker
]]
svgtemp = svgtemp .. 'id="marker-arrow-' .. tostring(w) .. "\"\n"
svgtemp = svgtemp .. 'orient="auto-start-reverse"' .. "\n"
svgtemp = svgtemp .. 'refY="0" refX="0"' .. "\n"
svgtemp = svgtemp .. 'markerHeight="3" markerWidth="2">' .. "\n"
svgtemp = svgtemp .. ' <g transform="scale('.. tostring((2.40 * w + 3.87)/(4.5*w)) .. ')">' .. "\n"
svg = svg .. svgtemp
svgtemp = [[
<path
d="M -1.55415,2.0722 C -1.42464,1.29512 0,0.1295 0.38852,0 0,-0.1295 -1.42464,-1.29512 -1.55415,-2.0722"
style="fill:none;stroke:#000000;stroke-width:{0.6};stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1
inkscape:connector-curvature="0" />
</g>
</marker>
</defs>
]]
svg = svg .. svgtemp
end
style_string = ''
for key, value in pairs(style) do
-- skips nil?
style_string = style_string .. key .. ":" .. " " .. value .. ";"
end
svg = svg .. '<inkscape:clipboard style="' .. style_string .. '" />' .. "\n</svg>"
hs.pasteboard.writeDataForUTI("dyn.ah62d4rv4gu80w5pbq7ww88brrf1g065dqf2gnppxs3xu", svg)
-- get UTI via https://github.com/sindresorhus/Pasteboard-Viewer
hs.eventtap.keyStroke({"shift", "cmd"}, "v")
end

Reference Card for Key Chords

As a reference for the key chords, I added the original picture from the original blog but with the key chords included in the picture.

Missing Key Chords

I did not add the ergonomic rebinding x, w, f, and shift+z. This should be possible in Inkscape itself. This setup also misses the bindings t, shift+t, a, shift+a, s, and shift+s. Since I encountered issues I did not pursue these.

Summary

This is the whole setup I have, and let's wrap this up since I know this may be quite overwhelming.

  1. Before you start your project, enter the VISUAL mode by pressing v in NORMAL mode. And then press ctrl+f. This will set up the file watcher.
  2. When you want to create a new figure, go into a new line, type the name of your figure in INSERT mode, then press ctrl+f. This will create a new figure with the name you typed, and open it in Inkscape for you.
  3. When you have drawn your figure, as long as you press cmd+s in Inkscape, it will automatically save the figure in pdf+latex for you, then you can close Inkscape.
  4. When you want to edit one of your figures, you press ctrl+f in NORMAL mode, it will pop out a window for you to choose the figure you want to edit. And the rest is the same as 3.

Updates

About Inkscape Shortcut Manager (09.27.21)

After some research, although there is a way to let the original script in inkscape-shortcut-manager run correctly since it depends on xlib, which is no longer used by macOS for almost every application(including Inkscape, as expected), hence the only thing I can do now is to give up. In a perceivable future, if I have time to find an alternative way to interrupt the window activity in macOS, I'll try to configure it for macOS.

Now the Inkscape Shortcut Manager is fully functional, see here.

Quiver - For commutative diagram (01.24.22)

I have been working on Category Theory for a while, and I found out that quiver is quite appealing, hence I integrate it into my workflow. You can also pull it to your local environment, configure the VS Code Task, and combine it with a hotkey to use it locally. Specifically, I added the following code to my keybindings.json:

{
    "key": "ctrl+c",
    "command": "command-runner.run",
    "args": {
        "command": "quiver",
        "terminal": {
            "name": "runCommand",
            "shellArgs": [],
            "autoClear": true,
            "autoFocus": false
        }
    },
    "when": "editorTextFocus"
},

and also, define the command quiver as

"command-runner.commands": {
    "quiver": "open -na 'Google Chrome' --args --new-window <path-to-quiver>/quiver/src/index.html"
},

Notice that you'll need to build it first if you want to use it offline! Please follow the tutorial here. Otherwise, it's totally fine to use "quiver": "open -na 'Google Chrome' --args --new-window https://q.uiver.app/" as your command.

This is what the workflow looks like.

To use the package tikz-cd, you need to include the following in your header:

% quiver style
\usepackage{tikz-cd}
% `calc` is necessary to draw curved arrows.
\usetikzlibrary{calc}
% `pathmorphing` is necessary to draw squiggly arrows.
\usetikzlibrary{decorations.pathmorphing}

% A TikZ style for curved arrows of a fixed height, due to AndréC.
\tikzset{curve/.style={settings={#1},to path={(\tikztostart)
					.. controls ($(\tikztostart)!\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$)
					and ($(\tikztostart)!1-\pv{pos}!(\tikztotarget)!\pv{height}!270:(\tikztotarget)$)
					.. (\tikztotarget)\tikztonodes}},
	settings/.code={\tikzset{quiver/.cd,#1}
			\def\pv##1{\pgfkeysvalueof{/tikz/quiver/##1}}},
	quiver/.cd,pos/.initial=0.35,height/.initial=0}

% TikZ arrowhead/tail styles.
\tikzset{tail reversed/.code={\pgfsetarrowsstart{tikzcd to}}}
\tikzset{2tail/.code={\pgfsetarrowsstart{Implies[reversed]}}}
\tikzset{2tail reversed/.code={\pgfsetarrowsstart{Implies}}}
% TikZ arrow styles.
\tikzset{no body/.style={/tikz/dash pattern=on 0 off 1mm}}

You can certainly follow my Template, which already includes all the requirement headers for you.

Migrate to HyperSnips (02.18.22)

Now, instead of using HyperSnips for Math, we're using HyperSnips, namely the original one! Since I just found out that we can trigger the snippets only in math mode by using the special keyword called context, I migrated to the original one. To migrate, you just need to uninstall HyperSnips for Math, install HyperSnips with the updated latex.hsnips I prepared for you, and then enjoy!

Documenting Inkscape Shortcut Manager (07.30.22)

I finally have time to document the configuration of the Inkscape shortcut manager and make some changes to make this document more readable. Personally, I have used this workflow for more than half of a year, so I think this is stable and will not be changed shortly.

Credits

Again, thanks to Gilles Castel, this workflow fits my style. Although it originally worked in Linux+Vim only, the idea is the most important thing. Without his wonderful post, I can't even imagine this is possible. But now it is! Go to his original post to show him some love.

Related Project

  1. LaTeX-Template
  2. Notes
  3. gillescastel/inkscape-figures
  4. gillescastel/inkscape-shortcut-manager
  5. chipsenkbeil/choose
  6. varkor/quiver

Star History