Customizing VS Code for Markdown note-taking

Posted

I’ve been using Visual Studio Code for all my notes. I have used Typora, Obsidian, and Zetlr, but I keep coming back to VS Code, because of its extensibility, customizability and integrated terminal. Here is how I set it up for note-taking.

This post complements my previous “how-to” Override VS Code’s Terminal Color Theme.

Here are the extensions that I use for notes (and only notes), in no particular order:

There is a fourth extension I use - a totally customized TouchBar extension for macOS, built with zero code!

Markdown Extended Extension

Here is a short summary of capabilities added by the Markdown Extended extension:

  • Keyboard shortcut keys, like Ctrl+B to toggle bold,
  • Table editing features,
  • Extended syntax that can be Previewed, e.g.

Markdown Extended extension for VS Code

MarkdownDescription
[ ] and [x]Unchecked and checked checkboxes
^text^ and ~text~Enclosed text is superscript and subscript respectively, e.g. 1st and H2O
_text_Enclosed text is Underlined with <u> (instead of italics!)
~~text~~Strikethrough enclosed text
[[Ctrl]]Enclosed text is rendered in a <kbd> tag like this Ctrl, but...
[[TOC]]The keyword TOC renders a table of contents based on # headings
:emoji:Emojis like :P and :smile: get rendered as 😛 and 😄
[title](#heading)Link to headings in same document (anchors), replacing spaces with dashes, e.g. if the heading is ## Sub Heading, the link would be to (#sub-heading).
[^f1]: textFootnotes, which appear at the bottom of the whole page (which must be unique), and linked to via...
[^f1]Renders a superscript link to footnotes like this [1] (footnotes are sequentially numbered and the name fn1 is not shown)
*[abbr]: textAbbreviations with tooltips, where ever abbr appears in the document
==highlight==highlights (“marks”) the enclosed text
!!! note textShows an “admonition” notice, where note is the icon to be displayed. More on this below...

Digression: Workspace Settings

In subsequent sections, I make a lot of changes to Settings. You can access settings:

  • From the menu, Code > Preferences... > Settings,
  • From the menu, View > Command Palette..., and search for Preferences: Open Settings (JSON),
  • Or via the Cmd+P shortcut, and type settings.json.

Also, I want to point out that if you are using Workspaces, you can place your settings in a workspace settings file. Generally the way I work is:

  • “A "workspace" is the collection of one or more folders that are opened in a VS Code window (instance).” - I use one Workspace for all my notes, e.g. ~Desktop/Notes.
  • Therefore I can have workspace-specific settings configured in ~Desktop/Notes/.vscode/settings.json

For example, in this screenshot, I know when I am taking notes, I’ll not be debugging anything. So from the settings UI, I can turn off anything to do with debugging in the touchbar, action bar, etc. for this workspace:

VS Code Workspace Settings UI

My workspace .vscode/settings.json file will contain these lines to override the default user setting (plus, I can add more to buttons to Nasc VSCode Touchbar ):

{
    "keyboard.touchbar.ignored": [
        "workbench.action.debug.start",
        "workbench.action.debug.run"
    ],
}

Disabling Markdown Extended plugins

MarkDown Extended can be configured to disable some features. For example, I might want to turn off the following:

  • admonition - because I use !!! differently when editing this blog (since the first word is expected to specify an icon and will not be displayed).
  • container - because then Preview needs to include Bootstrap CSS styles, which adds to bloat.
  • html5-embed - because this sometimes breaks links that are wrongly considered audio/video (e.g. .ts extensions) and as a result, the entire Preview page will not render.

So:

"markdownExtended.disabledPlugins": "admonition,container,html5-embed",

BTW “Admonition” takes this:

!!! note Important stuff here
!!! danger
    Quicksand ahead!

And renders it as:

Markdown Extended Admonition

The first word denotes the icon, which can be, e.g.:

  • note, summary, question, hint
  • todo, done, fail
  • warning, danger

Overriding Keyboard Shortcuts

Markdown Extended enables key bindings for Ctrl+B and Ctrl+I to toggle bold and italics.

On macOS, I’d rather override them to use Cmd+B and Cmd+I because of muscle memory I can live without the default bindings for these keystrokes.

  • View > Command Palette..., and search for Preferences: Open Keyboard Shortcuts (JSON) (or Cmd+P and type keybindings.json)
  • Add something like this:
{
    "key": "cmd+b",
    "command": "markdownExtended.toggleBold",
    "when": "editorTextFocus&&editorLangId==markdown"
},{
    "key": "cmd+i",
    "command": "markdownExtended.toggleItalics",
    "when": "editorTextFocus&&editorLangId==markdown"
}

However, Markdown Extended has a small quirk in that if it cannot select a word, then it does not apply the style.

If you don’t like this behaviour, or would rather not toggle the style, or don’t enable the extension, then this binding simply inserts the required characters:

{
    "key": "cmd+b",
    "command": "editor.action.insertSnippet",
    "args": {"snippet":"**$TM_SELECTED_TEXT$0**"},
    "when": "editorTextFocus&&editorLangId==markdown"
},{
    "key": "cmd+i",
    "command": "editor.action.insertSnippet",
    "args": {"snippet":"*$TM_SELECTED_TEXT$0*"},
    "when": "editorTextFocus&&editorLangId==markdown"
}

Highlight Extension

While Markdown Extended can render things like admonitions, highlight, etc. in Markdown Preview mode, it does not change the editor text styles. Enter the Highlight extension.

Highlight applies CSS text decorations to sections of text that match a given regular expression. Styles include:

  • color, opacity, backgroundColor - foreground (text) color, foreground opacity and background color
  • border, borderColor, borderRadius, borderSpacing, borderStyle, borderWidth
  • outline, outlineColor, outlineStyle, outlineWidth
  • overviewRulerColor and overviewRulerLane (1=left, 2=center, 4=right, 7=full)
  • fontStyle (italic), fontWeight, textDecoration (i.e. underline or over-line), letterSpacing
  • isWholeLine (set to true to apply decoration on the whitespace to the end of the line)

Customizing

I configure it to:

  1. ==text== to be actually highlighted with a yellow background,
  2. Strikethrough ~~text~~,
  3. Show end-of-line new line marks (two or more spaces) as a red block,
  4. Mark where “admonition” (notes) !!! blocks are used.
  5. Highlight Headings 1-4 with a background color, and make them appear in the overview ruler (those dots on the right)

Highlight don’t always work as expected when editing. Also, you have to customize the colors to match your own theme!

Markdown Extended and Highlight extensions for VS Code

To configure:

  • Edit settings.json,
  • And add something like this:
"highlight.decorations": { "highlight.rangeBehavior": 1 },
"highlight.regexes": {
    "(==.*?==)": {
        "filterLanguageRegex": "markdown",
        "decorations": [ {
            "backgroundColor": "yellow" }
        ]
    },
    "(~~.*?~~)": {
        "filterLanguageRegex": "markdown",
        "decorations": [ {
            "textDecoration": "line-through" }
        ]
    },
    "(  +)$": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "gm",
        "decorations": [ {
            "borderRadius": "4px",
            "backgroundColor": "rgba(155,0,0,0.2)" }
        ]
    },
    "(!!! \\w*)": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "g",
        "decorations": [ {
            "backgroundColor": "rgb(205,240,234,0.5)" }
        ]
    },
    "^(# )": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "gm",
        "decorations": [ {
            "isWholeLine": "true",
            "letterSpacing": "2px",
            "backgroundColor": "rgba(190,174,226,0.4)",
            "overviewRulerColor": "rgba(190,174,226,0.5)" }
        ]
    },
    "^(## )": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "gm",
        "decorations": [ {
            "isWholeLine": "true",
            "letterSpacing": "2px",
            "backgroundColor": "rgba(190,174,226,0.3)",
            "overviewRulerColor": "rgba(190,174,226,0.4)" }
        ]
    },
    "^(### )": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "gm",
        "decorations": [ {
            "isWholeLine": "true",
            "letterSpacing": "1px",
            "backgroundColor": "rgba(190,174,226,0.2)",
            "overviewRulerColor": "rgba(190,174,226,0.3)" }
        ]
    },
    "^(#### )": {
        "filterLanguageRegex": "markdown",
        "regexFlags": "gm",
        "decorations": [ {
            "isWholeLine": "true",
            "letterSpacing": "1px",
            "backgroundColor": "rgba(190,174,226,0.1)",
            "overviewRulerColor": "rgba(190,174,226,0.2)" }
        ]
    }
}

You might we wondering why an Extension, as opposed to Themes with Semantic highlighting - alas, that does not seem to allow a background color to be set.

A note about new lines: the original Markdown specification uses one or more blank lines to denote paragraphs <p>, and two spaces at the end of a line for a line break <br>.

If you want to use new lines in this way, edit this setting, to keep end-of-line whitespaces:

"[markdown]": {
     "files.trimTrailingWhitespace": false
}

Preview with styles.css

So now the text editor is customized, how about applying custom styles to Markdown Preview too? Certainly, VS Code allows custom CSS to be applied too!

I am just going to make one small change, so that Markdown Preview will highlight Headings 1-4 with the same background color I used in the editor.

To do that:

  • you’ll need a file called style.css in your Workspace folder:
h1 {
  background-color: rgba(190, 174, 226, 0.4);
}
h2 {
  background-color: rgba(190, 174, 226, 0.3);
}
h3 {
  background-color: rgba(190, 174, 226, 0.2);
}
h4 {
  background-color: rgba(190, 174, 226, 0.1);
}
  • then, edit (I assume your workspace settings) .vscode/settings.json and add:
"markdown.styles": [ "style.css" ],

This file is always relative to the current workspace. Absolute file paths are not supported for security. If VS Code cannot find style.css, you will see a toast message with an error. Unsaved Markdown files and saved files that are not in this workspace will also prompt the same error. Personally, put my style.css file in the .vscode folder of my notes workspace, and live with the errors.

Coloring Lines

Markdown Extended incorporates markdown-it-attrs to “add classes, identifiers and attributes to your markdown with {.class #identifier attr=value attr2="spaced value"} curly brackets.”

That means, to color a word or line, in Markdown, we can do something like this to have the Preview rendered with colored text:

  • color the whole line red{style="color:red"}
  • color a single **word**{style="color:blue"} blue

This is a lot to type, so one simplification is to define styles first by putting this block at the start of the Markdown document (for fun I've chosen desaturated colors, but you can also use background-color:red or anything else), i.e.

<style>
.r { background-color: rgb(249,38,114) }
.g { background-color: rgb(166,226,46) }
.b { background-color: rgb(129,154,255) }
.c { background-color: rgb(102,217,239) }
.m { background-color: rgb(174,129,255) }
.y { background-color: rgb(226,226,46) }
</style>

But that’s a lot to type too... so why not just include it in styles.css? That will work too! Do remove the <style> tags of course.

Now, the Markdown can be something like this:

  • color the whole line red{.r}
  • color a single **word**{.b} blue

Editor Highlighting

Highlight extension for VS Code

Now to configure the Highlight extension to color the line (alas, I cannot color a single word or block of text):

"(.*{style=\\\"color:(.*)\\\"}.*)": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "color": "$2"
    } ]
},
"({\\.r})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(249,38,114)"
    } ]
},
"({\\.g})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(166,226,46)"
    } ]
},
"({\\.b})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(129,154,255)"
    } ]
},
"({\\.c})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(102,217,239)"
    } ]
},
"({\\.m})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(174,129,255)"
    } ]
},
"({\\.y})": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "isWholeLine": "true",
        "color": "rgb(226,226,46)"
    } ]
}

Bonus! You may prefer not to change the font color, but underline the line where the style= is used. In settings.json (I’m being sneaky here by adding a space):

"(.*{style=\\\"color:(.*)\\\" }.*)": {
    "filterLanguageRegex": "markdown",
    "decorations":[ {
        "border": "1px $2",
        "borderStyle": "none none solid none"
    } ]
}

Exporting PDF

Markdown Extended can save Markdown as PDF exactly as seen in Preview, including any output rendered by other extensions (like Mermaid diagrams below) plus the custom style.css above!

This is remarkably simple to setup, but very poorly documented. So:

  • Make sure Google Chrome or the open source Chromium browser installed.
  • Edit settings.json
  • Add or edit this line to point to the browser executable - Chrome on macOS will be:
"markdownExtended.puppeteerExecutable": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
  • Then from the Command Palette..., run Markdown: Export to File > PDF File
  • The PDF will be saved in a folder called out in the Workspace folder.

Speaking of previews, I also use this extension so I don’t have to leave VS Code to open PDFs:

More Extensions

Other

Markdown Preview Renderers

These extensions extend Markdown Preview to support:

This interesting extension renders your Markdown note as a mind map:

UNotes

I sometimes enable UNOTES - Markdown Notes WYSIWYG, as it provides a toolbar and WYSIWYG mode!

However, this extension totally takes over the editor and provides its own Preview, as opposed to working with the default editor and Preview. Therefore, no other extensions will work with it.

VS Code UNOTES extension

There a some small customizations you can make with UNotes by editingsettings.json, e.g.:

"workbench.colorCustomizations": {
    "unotes.wysH1": "#fd3571",
    "unotes.wysH2": "#fc7b65",
    "unotes.wysH3H4": "#30e3ca",
    "unotes.wysH5H6": "#364f6b"
}

I’m not 100% certain but if I recall correctly, UNotes also did not bind keyboard shortcuts in a manner friendly to other extensions. I had to edit keybindings.json to remove UNotes bindings, like so:

{
    "key": "ctrl+b",
    "command": "-unotes.bold"
},
{
    "key": "ctrl+i",
    "command": "-unotes.italic"
},

Conclusion

Much words! Much customization! Much time wasted... :P

Credits: Some colors based off this ColorHunt.co palette and the Monokai Theme