A vim plugin that adds some nice extra's for working with markdown documents
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
after/syntax/markdown Table highlighting stops after 1 blank line below end of table Aug 17, 2018
autoload Handle double quotes inside table content Sep 9, 2018
doc Update CSV to table and back conversion gifs Sep 9, 2018
ftplugin/markdown
test Merge pull request #75 from SidOfc/patch/version-bump Sep 9, 2018
.gitignore ignore all md files starting with test Aug 3, 2018
CHANGELOG.md
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md Mar 24, 2018
CONTRIBUTING.md Update CONTRIBUTING.md Mar 24, 2018
ISSUE_TEMPLATE.md Update issue template Dec 22, 2017
LICENSE.md Add extension to LICENSE Dec 31, 2017
README.md Fix link Sep 9, 2018

README.md

mkdx.vim GitHub tag GitHub issues

          888b     d888 888    d8P  8888888b. Y88b   d88P    888     888 8888888 888b     d888
  , - /|  8888b   d8888 888   d8P   888  "Y88b Y88b d88P     888     888   888   8888b   d8888  |\ - ,
 / = / |  88888b.d88888 888  d8P    888    888  Y88o88P      888     888   888   88888b.d88888  | \ = \
/ = /  |  888Y88888P888 888d88K     888    888   Y888P       Y88b   d88P   888   888Y88888P888  |  \ = \
\ = \  |  888 Y888P 888 8888888b    888    888   d888b        Y88b d88P    888   888 Y888P 888  |  / = /
 \ = \ |  888  Y8P  888 888  Y88b   888    888  d88888b        Y88o88P     888   888  Y8P  888  | / = /
  ' - \|  888   "   888 888   Y88b  888  .d88P d88P Y88b  d8b   Y888P      888   888   "   888  |/ - '
          888       888 888    Y88b 8888888P" d88P   Y88b Y8P    Y8P     8888888 888       888

mkdx.vim is a markdown plugin that aims to reduce the time you spend formatting your markdown documents. It does this by adding some configurable mappings for files with a markdown filetype. Functions are included to handle lists, checkboxes (even lists of checkboxes!), fenced code blocks, shortcuts, headers and links. In addition to that, this plugin provides a mapping to convert a selection of CSV data to a markdown table. And there's a lot more :D Visit :h mkdx or :h mkdx-helptags for more information.

A copy can be found on vim.sourceforge.io. This plugin is also compatible with repeat.vim by Tim Pope. Every normal mode mapping can be repeated with the . command. Below you will find configurable settings and examples with default mappings.

Table of Contents

Click to expand Table of Contents

Install

This plugin is tested using Vader.vim in vim, nvim and mvim.

To install, use a plugin manager of choice like Vundle or Pathogen.

Vundle

Plugin 'SidOfc/mkdx'

:so $MYVIMRC
:PluginInstall

NeoBundle

NeoBundle 'SidOfc/mkdx'

:so $MYVIMRC
:NeoBundleInstall

vim-plug

Plug 'SidOfc/mkdx'

:so $MYVIMRC
:PlugInstall

Pathogen

cd ~/.vim/bundle
git clone https://github.com/SidOfc/mkdx

Quick start

This plugin is extremely customizable, there are a lot of settings and examples to go through. Some of the best settings are disabled because they might not perform on some specific setup or the functionality may interfere with other plugins who are better suited for specific tasks (e.g. highlighting is only a minor 'enhancement' in this plugin).

quick setup

let g:mkdx#settings     = { 'highlight': { 'enable': 1 },
                        \ 'enter': { 'shift': 1 },
                        \ 'links': { 'external': { 'enable': 1 } },
                        \ 'toc': { 'text': 'Table of Contents', 'update_on_write': 1 },
                        \ 'fold': { 'enable': 1 } }
let g:polyglot_disabled = ['markdown'] " for vim-polyglot users, it loads Plasticboy's markdown
                                       " plugin which unfortunately interferes with mkdx list indentation.

The above snippet enables:

  • Folding of inline code blocks and the table of contents
  • Makes dead link detection also check for external links instead of only checking # links
  • Enables shift+enter support for multi line list items (please ensure that it works correctly).
  • Changes the default table of contents text from TOC to Table of Contents
  • Updates the table of contents automatically before saving the file
  • Enables better syntax highlighting for tables, list items, check boxes with certain states, kbd tags and bold+italic markers

There is much more that can be configured, go here for an overview of the defaults or check out the examples below :)

Examples

Folds

mkdx fold table of contents and fenced code-blocks

Folding in mkdx can be enabled by setting g:mkdx#settings.fold.enable to 1. Both the table of contents and fenced code blocks can be folded independently as well, see g:mkdx#settings.fold.components for more information. We also see a tiny bit of auto update in this example, that shows that the new value gets applied instantly.

Insert mode fragment completion

mkdx insert mode fragment completion

When inside a link, typing a url that starts with a # will show a list of autocompletions based on the links of all the headers in the document. This functionality uses Vim's builtin complete (:h complete()) and completefunc (:h completefunc) for autocompletions. Use ctrl+n to select the next entry and ctrl+p to select the previous entry.

When not inside a link, fragment autocompletions will not be triggered and default autocompletion is used instead. To complete a fragment outside of a link, type a # followed by ctrl+n or ctrl+p.

Insert mode autocompletions can be disabled by setting the value of g:mkdx#settings.links.fragment.complete to 0. note: g:mkdx#settings.map.enable must be enabled for ctrl+n and ctrl+p mappings to be bound.

" :h mkdx-mapping-insert-completions
" :h mkdx-function-insert-ctrl-n-handler
" :h mkdx-function-insert-ctrl-p-handler
" :h mkdx-function-complete-link
" :h mkdx-function-complete
" :h mkdx-plug-ctrl-n-compl
" :h mkdx-plug-ctrl-p-compl
" :h mkdx-plug-link-compl

Dead link detection

mkdx dead detection

Often when writing documentation, you'll find the need to point to some other location on the same page. So you go ahead and write that fragment URL and forget about it. Later you come back to this file and you change the header (thus changing the output fragment for that header), but you forgot you even had a link to it in the first place! A new broken link is born :/

Well, no more -- Press <PREFIX>L and Vim's quickfix window will come to your rescue. It will compare all fragment links in the document to those of the headers and show you which ones don't match. Now you can safely change a header, if you want to know if you broke anything you know the mapping :)

External links will also be checked, your (n/m/g)vim must support either Vim's job or Neovim's job-control in order for this to be enabled. curl and git will also be used to send requests or create an absolute path for relative links. See g:mkdx#settings.links.external.enable for more information and other settings.

" :h mkdx-mapping-quickfix-dead-links
" :h mkdx-function-quickfix-dead-links
" :h mkdx-plug-quickfix-links

Insert fenced code block

Backtick Tilde
mkdx fenced codeblock backticks mkdx fenced codeblock tilde

As seen in the gifs, entering either 3 consecutive ` or ~ characters in insert mode will complete the block and put the cursor at the end of the opening fence to allow adding a language. The behaviour is controlled by g:mkdx#settings.map.enable and like other mappings, it is only mapped if no mapping exists.

Fence style can be controlled using g:mkdx#settings.tokens.fence. This allows you to use one style for both ` and ~ blocks.

Note that if you want to copy the {rhs} of this mapping in a mapping in your vimrc, you will need to replace <C-o> with a literal ^o character. In vim, this can be achieved by pressing ctrl+v followed by ctrl+o.

" :h mkdx-mapping-insert-fenced-code-block
  inoremap <buffer><silent><unique> ~~~ ~~~<Enter>~~~<C-o>k<C-o>A
  inoremap <buffer><silent><unique> ``` ```<Enter>```<C-o>k<C-o>A

Insert <kbd></kbd> shortcut

mkdx insert keyboard shortcut

This mapping works in insert mode by pressing <+tab. This mapping is just a regular imap that inserts <kbd></kbd> and puts your cursor in the tag afterwards. The behaviour is controlled by g:mkdx#settings.map.enable and like other mappings, it is only mapped if no mapping exists.

Note that if you want to copy the {rhs} of this mapping in a mapping in your vimrc, you will need to replace <C-o> with a literal ^o character. In vim, this can be achieved by pressing ctrl+v followed by ctrl+o.

" :h mkdx-mapping-insert-kbd-shortcut
imap <buffer><silent><unique> <<Tab> <kbd></kbd><C-o>2h<C-o>cit

Inserting list items

Unordered Numbered
mkdx unordered list mkdx numbered list

When g:mkdx#settings.enter.enable is set (default on), new list tokens will be inserted when editing a markdown list. This happens on any enter in insert mode or o and O in normal mode by default. Additionally, if the list item contains a checkbox ([ ] - any state possible) that will also be appended to the newly inserted item.

Sometimes, you might need to write a multi-line list item, this can be achieved by enabling g:mkdx#settings.enter.shift. Once enabled, pressing shift+enter will place the cursor on a new line, indented to where the text started on the previous line. Press shift+enter while you want to add lines to the current item, a regular enter will create a new list item at the previous indentation level.

" :h mkdx-mapping-list-items
" :h mkdx-setting-enter-enable
" :h mkdx-setting-enter-shift
" :h mkdx-setting-tokens-list
" :h mkdx-function-enter-handler
" :h mkdx-function-shift-enter-handler

Toggling lines from / to task items

mkdx toggle checkbox line

Checkboxes can be toggled using <PREFIX>t. This will cause a checkbox to be prepended before the line if it doesn't exist. The checkbox will be removed instead, if it exists. The initial state can be defined using g:mkdx#settings.checkbox.initial_state.

When toggling a checkbox in a list or checklist, the checkbox will be added / removed accordingly:

- list item           => - [ ] list item
- [ ] checklist item  => - checklist item
* [ ] checklist item  => * checklist item
1. [ ] checklist item => 1. checklist item

Note: the list / checklist support has been added in version 0.4.1. Prior to that, the checkbox would be inserted at the start of the line instead of after the list token.

" :h mkdx-mapping-toggle-checkbox
" :h mkdx-function-toggle-checkbox-task

Toggling lines from / to list items

mkdx toggle list line

Lists can be toggled using <PREFIX>ll. This will cause a list token to be inserted. When present, it will be removed.

When toggling a checkbox or a checklist item, the list token will be added / removed accordingly:

[ ] checkbox item     => - [ ] checkbox item
- [ ] checklist item  => - checklist item
* [ ] checklist item  => * checklist item
1. [ ] checklist item => 1. checklist item

Note: the checklist support has been added in version 0.4.1. Prior to that, tokens other than g:mkdx#settings.tokens.list weren't toggled.

" :h mkdx-mapping-toggle-list
" :h mkdx-function-toggle-list

Toggling lines from / to checklist items

mkdx toggle checklist line

Checklists can be toggled using <PREFIX>lt. This will cause a list token followed by a checkbox to be prepended before the line if it doesn't exist. If it is already present, it will be removed. Like Checkboxes, the initial state of the checkbox can be defined using: g:mkdx#settings.checkbox.initial_state.

If the current line or selection is one or multiple list items, a checkbox with state of g:mkdx#settings.checkbox.initial_state will be added:

- list item  => - [ ] list item
* list item  => * [ ] list item
1. list item => 1. [ ] list item

If the current line or selection is one or multiple checkboxes, a g:mkdx#settings.tokens.list will be added. Any state the checkbox is in will be preserved:

[ ] list item  => - [ ] list item
[x] list item  => - [x] list item

note: the list item / checkbox support has been added in version 0.4.1. Prior to that, toggling checklists only performed a check to see if a checklist item was present or not.

" :h mkdx-mapping-toggle-checklist
" :h mkdx-function-toggle-checklist

Completing Checkboxes / Checklists

Single checkbox: mkdx toggle checkbox

Checkbox in checklist: mkdx update checklist

Checkboxes can be checked using <PREFIX>= and <PREFIX>-. checking a checkbox means going to the previous or next mark in the list of g:mkdx#settings.checkbox.toggles. When checking an item which is nested in a list, the parent and child list items will be updated as well. Automatic updating of checkboxes can be disabled by setting g:mkdx#settings.checkbox.update_tree. All manipulations work fine in visual as well as normal mode.

You can also see that the checkboxes are highlighted differently depending on state. This is an opt-in setting which you must enable explicitly in your vimrc. See g:mkdx#settings.highlight.enable for more information.

A file might not always be indented correctly, the solution to this is g:mkdx#settings.enter.malformed. This setting is enabled by default, it rounds invalid (indentation not divisible by :h shiftwidth) either up or down to the nearest indentation level. In the examples below, the shiftwidth is set to 4. The second item is indented by 3 spaces and the third item is indented by 5 spaces. since 3 is closer to 4 than 0, it will become 4. In the case of 5, it's closer to 4 than 8 and will also become 4.

off on
mkdx toggle checkbox malformed off mkdx toggle checkbox malformed on
" :h mkdx-mapping-toggle-checkbox-forward
" :h mkdx-mapping-toggle-checkbox-backward
" :h mkdx-function-toggle-checkbox

Toggling and promoting / demoting Headers

mkdx toggle header

Increment or decrement a heading with <PREFIX>[ and <PREFIX>]. As can be seen in the gif, headings can be toggled as well as promoted / demoted with these mappings. The header character can be changed using g:mkdx#settings.tokens.header.

" :h mkdx-mapping-increment-header-level
" :h mkdx-mapping-decrement-header-level
" :h mkdx-function-toggle-header

Toggling <kbd /> shortcuts

mkdx toggle kbd shortcuts

Note: does not work with multiline selection

Quickly toggle plain text shortcuts to markdown shortcuts and back with <PREFIX>'.

" :h mkdx-mapping-toggle-kbd-shortcut
" :h mkdx-function-toggle-to-kbd

Toggling Quotes

mkdx toggle quotes

Toggle quotes on the current line or a visual selection with <PREFIX>'.

" :h mkdx-mapping-toggle-quote
" :h mkdx-function-toggle-quote

Wrap as link

mkdx wrap text in link

Wrap the word under the cursor or a visual selection in an empty markdown link with <PREFIX>ln. You'll end up in insert mode with your cursor between the parens, e.g. (|) where the pipe (|) character is the cursor.

If what you're wrapping is an image (only works with visual selections at the moment), an image link will be created instead. To disable this behaviour, see: g:mkdx#settings.image_extension_pattern.

" :h mkdx-mapping-wrap-text-in-link
" :h mkdx-function-wrap-link

Wrap as bold / italic / inline-code / strikethrough

Normal mode mkdx wrap text in bold / italic / inline-code / strikethrough normal

Visual mode mkdx wrap text in bold / italic / inline-code / strikethrough visual

Wrap the word (anywhere) under the cursor or a visual selection using the following mappings:

As with all other mappings, all the normal mode mappings are repeatable.

Convert CSV to table (and back)

mkdx convert csv to table

Convert visually selected CSV rows to a markdown table with <PREFIX>,. The first row will be used as a header. A separator will be inserted below the header. Default and specific column alignment options are also available, see: g:mkdx#settings.table.align for more. The divider (|) as well as the header divider (-) can be changed with g:mkdx#settings.table.divider and g:mkdx#settings.table.header_divider. The above example contains a simple unqouted example but as of version 1.4.2, quoted CSV is also supported.

" :h mkdx-mapping-csv-to-markdown-table
" :h mkdx-setting-table-align
" :h mkdx-setting-table-divider
" :h mkdx-setting-table-header-divider
" :h mkdx-function-tableize

Jump to header

mkdx jump to header

Press <PREFIX>j to jump to a fragment identifier within the document. Every link starting with a # symbol is considered a fragment identifier. both markdown and HTML are supported. Before performing a jump, the current cursor position will be added to the jumplist (:h jumplist). This allows you to jump back and forth between that header and the selected link using ``. This behaviour can be disabled by overriding g:mkdx#settings.links.fragment.jumplist.

" :h mkdx-setting-links-fragment-jumplist
" :h mkdx-plug-jump-to-header
" :h mkdx-function-jump-to-header
" :h mkdx-mapping-jump-to-header

Generate or update TOC

mkdx generate or update table of contents

Press <PREFIX>i to insert a table of contents at cursor position if one does not exist, otherwise updates the existing TOC. the text used in the heading can be changed using g:mkdx#settings.toc.text and the list style can be changed using g:mkdx#settings.toc.list_token. Stuff inside fenced code blocks is excluded too.

If you want to place the TOC always as the [N]th header, see g:mkdx#settings.toc.position.

" :h mkdx-mapping-generate-or-update-toc
" :h mkdx-function-generate-toc
" :h mkdx-function-update-toc
" :h mkdx-function-generate-or-update-toc

Generate or update TOC as <details>

mkdx generate or update table of contents as details

Github supports the <details> and <summary> tags! With these, we can make an expandable table of contents (like this README). Unfortunately though, markdown isn't supported inside the <details> tag, not even with markdown=1 (not from what I've tried anyway, let me know if you do!). So instead the TOC itself will be rendered as HTML nested <ul> tags with <li><a></a></li> tags.

NOTE: This requires you to enable g:mkdx#settings.toc.details.enable!

Click to expand example

Open TOC in quickfix window

mkdx open toc in quickfix

Press <PREFIX>I to load all the markdown headers in a quickfix window. You can jump around using regular quickfix commands afterwards, as shown in the gif using :cn for example.

" :h mkdx-mapping-quickfix-table-of-contents
" :h mkdx-function-quickfix-headers

Open TOC using fzf instead of quickfix window

mkdx open toc using fzf

This is not built-in to the plugin but I just thought "why not, I'd use that". So I started working on a little snippet in my vimrc (included some comments here):

fun! s:MkdxGoToHeader(header)
    " given a line: '  84: # Header'
    " this will match the number 84 and move the cursor to the start of that line
    call cursor(str2nr(get(matchlist(a:header, ' *\([0-9]\+\)'), 1, '')), 1)
endfun

fun! s:MkdxFormatHeader(key, val)
    let text = get(a:val, 'text', '')
    let lnum = get(a:val, 'lnum', '')

    " if the text is empty or no lnum is present, return the empty string
    if (empty(text) || empty(lnum)) | return text | endif

    " We can't jump to it if we dont know the line number so that must be present in the outpt line.
    " We also add extra padding up to 4 digits, so I hope your markdown files don't grow beyond 99.9k lines ;)
    return repeat(' ', 4 - strlen(lnum)) . lnum . ': ' . text
endfun

fun! s:MkdxFzfQuickfixHeaders()
    " passing 0 to mkdx#QuickfixHeaders causes it to return the list instead of opening the quickfix list
    " this allows you to create a 'source' for fzf.
    " first we map each item (formatted for quickfix use) using the function MkdxFormatHeader()
    " then, we strip out any remaining empty headers.
    let headers = filter(map(mkdx#QuickfixHeaders(0), function('<SID>MkdxFormatHeader')), 'v:val != ""')

    " run the fzf function with the formatted data and as a 'sink' (action to execute on selected entry)
    " supply the MkdxGoToHeader() function which will parse the line, extract the line number and move the cursor to it.
    call fzf#run(fzf#wrap(
            \ {'source': headers, 'sink': function('<SID>MkdxGoToHeader') }
          \ ))
endfun

" finally, map it -- in this case, I mapped it to overwrite the default action for toggling quickfix (<PREFIX>I)
nnoremap <silent> <Leader>I :call <SID>MkdxFzfQuickfixHeaders()<Cr>

Using the menu

The plugin comes with builtin menu support if your vim has('menu'). The menu will become available after you've opened a file with a filetype of markdown. Actions can be executed from the menu in MacVim. More information about each mapping can be found in the Vim / Neovim versions.

MacVim NeoVim / Vim
mkdx macvim menu mkdx neovim / vim menu

To view the menu in NeoVim / Vim, run (after opening a markdown file):

" :h mkdx-menu
:menu Plugin.mkdx

g:mkdx#settings

All the settings used in mkdx are defined in a g:mkdx#settings hash. If you still have other g:mkdx# variables in your .vimrc, they should be replaced with an entry in g:mkdx#settings instead. Going forward, no new g:mkdx# variables will be added, only g:mkdx#settings will be extended. To see a mapping of new settings from old variables, see this README.

" :h mkdx-settings
let g:mkdx#settings = {
      \ 'image_extension_pattern': 'a\?png\|jpe\?g\|gif',
      \ 'restore_visual':          1,
      \ 'enter':                   { 'enable': 1, 'malformed': 1, 'o': 1,
      \                              'shifto': 1, 'shift': 0 },
      \ 'map':                     { 'prefix': '<leader>', 'enable': 1 },
      \ 'tokens':                  { 'enter': ['-', '*', '>'],
      \                              'bold': '**', 'italic': '*', 'strike': '',
      \                              'list': '-', 'fence': '',
      \                              'header': '#' },
      \ 'checkbox':                { 'toggles': [' ', '-', 'x'],
      \                              'update_tree': 2,
      \                              'initial_state': ' ' },
      \ 'toc':                     { 'text': "TOC", 'list_token': '-',
      \                              'update_on_write': 0,
      \                              'position': 0,
      \                              'details': {
      \                                 'enable': 0,
      \                                 'summary': 'Click to expand {{toc.text}}'
      \                              }
      \                            },
      \ 'table':                   { 'divider': '|',
      \                              'header_divider': '-',
      \                              'align': {
      \                                 'left':    [],
      \                                 'center':  [],
      \                                 'right':   [],
      \                                 'default': 'center'
      \                              }
      \                            },
      \ 'links':                   { 'external': {
      \                                 'enable': 0, 'timeout': 3, 'host': '', 'relative': 1,
      \                                 'user_agent':  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/9001.0.0000.000 vim-mkdx/1.8.1'
      \                              },
      \                              'fragment': {
      \                                 'jumplist': 1,
      \                                 'complete': 1
      \                              }
      \                            },
      \ 'highlight':               { 'enable': 0 },
      \ 'auto_update':             { 'enable': 0 }
    \ }

To overwrite a setting, write it as seen above in your .vimrc:

" :h mkdx-settings
let g:mkdx#settings = { 'enter': { 'enable': 0 } }

Will disable the g:mkdx#settings.enter.enable setting. For backwards compatibility, g:mkdx# variables are merged into the defaults. This happens before any g:mkdx#settings hash defined in .vimrc is merged with the defaults. So while g:mkdx# variables still work, they are overwritten when you explicitly define them in a g:mkdx#settings variable.

Settings defined in .vimrc are merged with the defaults during initial loading of the plugin. To overwrite a setting while editing:

" :h mkdx-settings
:let g:mkdx#settings.enter.enable = 0

g:mkdx#settings.links.external.enable

Check external links in the background is disabled by default. HTTP requests will be sent using a curl command with the following layout:

curl -L -I -s --no-keepalive -o /dev/null -A [g:mkdx#settings.links.external.user_agent] -m [g:mkdx#settings.links.external.timeout] -w "%{http_code}" [URL]'

As can be seen from the example two additional settings are used in this request:

When a relative URL is encountered, mkdx attempts to convert it to an absolute path. This only works inside a git repository at the moment, git ls-remote output will be used to do the conversion. You can override this behaviour by setting a custom host with g:mkdx#settings.links.external.host. Relative link checking can be disabled all together by setting g:mkdx#settings.links.external.relative to 0;

" :h mkdx-setting-links-external-enable
let g:mkdx#settings = { 'links': { 'external': { 'enable': 0 } } }

g:mkdx#settings.links.external.timeout

This is the maximum time in seconds a request may take. By default it is set to 3. After this time, the request is cancelled and added to the quickfix list.

" :h mkdx-setting-links-external-timeout
let g:mkdx#settings = { 'links': { 'external': { 'timeout': 3 } } }

g:mkdx#settings.links.external.host

When a host is supplied, mkdx will not attempt to do git ls-remote detection and instead, prepends host string provided to any absolute or relative link url. Since these links can either be relative (url) or absolute (/url) some additional checks are executed to prevent double slashes e.g: localhost:3000//url. Therefore you can safely append or remove a trailing / from your host.

As an example, say you have the following markdown link in your README.md file:

See [CONTRIBUTING](/CONTRIBUTING.md)

The git ls-remote command will be executed to construct a base for the relative link. both SSH and HTTP hosts are parsed to a base URL. Additionally, when constructing the base, we need to know the current branch since for example, github appends blob/[BRANCHNAME] to every URL as well. To extract this information, a git branch command is run. The output link will look like this:

See <a href="https://github.com/sidofc/mkdx/blob/master/CONTRIBUTING.md">CONTRIBUTING</a>

If you're running a static site generator and would like to know if every link works, you can set it like so:

:let g:mkdx#settings.links.external.host = 'localhost:5000'

Using the same link as an example, the output link will instead, look like this:

See <a href="http://localhost:5000/CONTRIBUTING.md">CONTRIBUTING</a>

The host can be changed at any given time during runtime, the next time a dead link check is run, the new host will be used instead.

" :h mkdx-setting-links-external-host
let g:mkdx#settings = { 'links': { 'external': { 'host': '' } } }

g:mkdx#settings.links.external.relative

When set to 0, relative links will be ignored while checking the document for dead links. Relative URL's are defined as link paths that do not start with a # (fragment URL) or http (protocol). Therefore both relative (url) and absolute (/url) URL's are "relative" in this context, as no host is provided. When g:mkdx#settings.links.external.host is empty, mkdx attempts to construct an absolute path using git ls-remote output.

" :h mkdx-setting-links-external-relative
let g:mkdx#settings = { 'links': { 'external': { 'relative': 1 } } }

g:mkdx#settings.links.external.user_agent

Specify an actual user agent to prevent being blocked from a website. If you don't like being Chrome/9001 then feel free to change it into anything you like :)

" :h mkdx-setting-links-external-ua
let g:mkdx#settings = { 'links': { 'external': { 'user_agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/9001.0.0000.000 vim-mkdx/1.8.1' } } }

g:mkdx#settings.links.fragment.jumplist

Specify wether or not to add the cursor position before jumping to the jumplist (:h jumplist). This setting is enabled by default, set it to 0 to disable.

" :h mkdx-setting-links-fragment-jumplist
" :h mkdx-function-jump-to-header
" :h mkdx-plug-jump-to-header
let g:mkdx#settings = { 'links': { 'fragment': { 'jumplist': 1 } } }

g:mkdx#settings.links.fragment.complete

Autocomplete fragment links in insert mode. Set to 0 to disable, g:mkdx#settings.map.enable must be enabled for ctrl+n and ctrl+p to be mapped.

This setting is auto updated when available.

" :h mkdx-setting-links-fragment-complete
let g:mkdx#settings = { 'links': { 'fragment': { 'complete': 1 } } }

g:mkdx#settings.image_extension_pattern

Defines the extensions to search for when identifying the type of link that will be generated when wrapping text in a link. Setting it to an empty string disables image wrapping and a regular empty markdown link will be used instead.

" :h mkdx-setting-image-extension-pattern
let g:mkdx#settings = { 'image_extension_pattern': 'a\?png\|jpe\?g\|gif' }

g:mkdx#settings.restore_visual

This setting enables the restoration of the last visual selection after performing an action in visual mode:

" :h mkdx-setting-restore-visual
let g:mkdx#settings = { 'restore_visual': 1 }

g:mkdx#settings.map.prefix

All mappings are prefixed with a single prefix key. If a mapping contains <PREFIX> key, it is the value of this variable. If you do not like the default (<leader>) you can override it:

" :h mkdx-setting-map-prefix
let g:mkdx#settings = { 'map': { 'prefix': '<leader>' } }

g:mkdx#settings.map.enable

If you'd rather full control over what is mapped, you can opt-out all together by setting it to 0. Note: that the plugin checks if a keybind exists before creating it. Mappings defined in your .vimrc will not be overwritten.

" :h mkdx-setting-map-enable
let g:mkdx#settings = { 'map': { 'enable': 1 } }

g:mkdx#settings.checkbox.toggles

Defines the list of states to use when toggling a checkbox. It can be set to a list of your choosing. Special characters must be escaped! Also, the list of toggles must contain at the very least, 2 items!

" :h mkdx-setting-checkbox-toggles
let g:mkdx#settings = { 'checkbox': { 'toggles': [' ', '-', 'x'] } }

" GFM supported list (e.g. on GitHub)
let g:mkdx#settings = { 'checkbox': { 'toggles': [' ', 'x'] } }

g:mkdx#settings.checkbox.update_tree

With this setting on, checkboxes that are toggled within checklists (lists of checkboxes) cause parent and child list items to be updated automatically. The states from g:mkdx#settings.checkbox.toggles are used to check and update the statusses of any parents. Children are force updated to the same token of their parent. To disable this behaviour entirely, set this value to 0. If you do not want children to be updated, set this value to 1 instead.

" :h mkdx-setting-checkbox-update-tree
let g:mkdx#settings = { 'checkbox': { 'update_tree': 2 } }

g:mkdx#settings.checkbox.initial_state

When toggling between checkbox/checklist lines, this defines what the default value of each inserted checkbox should be.

" :h mkdx-setting-checkbox-initial-state
let g:mkdx#settings = { 'checkbox': { 'initial_state': ' ' } }

g:mkdx#settings.tokens.header

If you want to use a different style for markdown headings (h1, h2, etc...).

This setting is auto updated when available.

" :h mkdx-setting-tokens-header
let g:mkdx#settings = { 'tokens': { 'header': '#' } }

g:mkdx#settings.tokens.enter

Used by g:mkdx#settings.enter.enable. This is the list of tokens that are supported by default. Since numbers are handled differently, they are not included in this list but they are supported.

" :h mkdx-setting-tokens-enter
let g:mkdx#settings = { 'tokens': { 'enter': ['-', '*', '>'] } }

g:mkdx#settings.tokens.fence

Defines the fencing style to use when inserting a fenced code block. By default it is set to an empty string, in which case typing tildes will result in a fenced code block using tildes and typing backticks results in a code block using backticks.

This value can be set to a ` or a ~ character. When set, the same style will always be used for fenced code blocks.

This setting is auto updated when available.

" :h mkdx-setting-tokens-fence
let g:mkdx#settings = { 'tokens': { 'fence': '' } }

g:mkdx#settings.tokens.italic

This token is used for italicizing the current word under the cursor or a visual selection of text. See this section for more details.

" :h mkdx-setting-tokens-italic
let g:mkdx#settings = { 'tokens': { 'italic': '*' } }

g:mkdx#settings.tokens.bold

This token is used for bolding the current word under the cursor or a visual selection of text. See this section for more details.

g:mkdx#settings.tokens.strike

This token is used for striking the current word under the cursor or a visual selection of text. See this section for more details.

Note: this setting has been added in version 1.7.0. Prior to that, <strike>...</strike> tags will always be used to create strikethrough text.

" :h mkdx-setting-tokens-bold
let g:mkdx#settings = { 'tokens': { 'bold': '**' } }

g:mkdx#settings.tokens.list

This token defines what list markers should be inserted when toggling list / checklist items.

" :h mkdx-setting-tokens-list
let g:mkdx#settings = { 'tokens': { 'list': '-' } }

g:mkdx#settings.table.header_divider

Change the separator used for table headings in markdown tables.

" :h mkdx-setting-table-header-divider
let g:mkdx#settings = { 'table': { 'header_divider': '-' } }

g:mkdx#settings.table.divider

You can also change the separator used in markdown tables.

" :h mkdx-setting-table-divider
let g:mkdx#settings = { 'table': { 'divider': '|' } }

g:mkdx#settings.table.align

Control the alignment of content within table columns. Its value is a dictionary with keys center, left, right and default. The default value is used when a column name is not found in any of the center, left or right lists.

  {
    \ 'left':    [],
    \ 'center':  [],
    \ 'right':   [],
    \ 'default': 'center'
\ }

If you were to add name to the left list, e.g. { 'left': ['name'] } and the first row of your CSV has a column with the content name, that column will be left aligned (|:----|). Any other column would be centered (|:---:|) because they do not appear in any list so the default value 'center' is used.

" :h mkdx-setting-table-align
let g:mkdx#settings = { 'table': { 'align': {
        \ 'left':    [],
        \ 'center':  [],
        \ 'right':   [],
        \ 'default': 'center' } } }

g:mkdx#settings.enter.shift

When enabled, pressing shift+enter will indent the next line upto the level of the text on the current line. No list tokens will be prepended. This is useful when you want to write a multi-line list item and want to start at the correct indentation level. For example, given this list (cursor indicated by a "|" character):

- [ ] a list item
- [ ] a list item|
- [ ] a list item

Pressing shift+enter will produce:

- [ ] a list item
- [ ] a list item
      |
- [ ] a list item

This works for any list type supported by mkdx. When shift+enter is pressed while not at the end of the line, the text after the cursor position is carried over to the next line at the correct indentation.

Note: this setting is disabled by default due to the sketchy handling of shift+enter in different terminal emulators. Some emulators send the same codes for enter and shift+enter which means (n)vim cannot distinguish one from another. This can usually be fixed by setting them manually for your emulator, included from this SO answer:

I managed to correct my terminal key-code for Shift+Enter by sending the key-code Vim apparently expects. Depending on your terminal, (Adding Ctrl+Enter as a bonus!)

iTerm2, open PreferencesProfilesKeys[+] (Add)

  • Keyboard shortcut: (Hit Shift+Enter)
  • Action: Send Escape Sequence
  • Esc+ [[13;2u Repeat for Ctrl+Enter, with sequence: [[13;5u

urxvt, append to your .Xresources file:

URxvt.keysym.S-Return:     \033[13;2u
URxvt.keysym.C-Return:     \033[13;5u

Alacritty, under key_bindings, add following to your ~/.config/alacritty/alacritty.yml:

- { key: Return,   mods: Shift,   chars: "\x1b\[13;2u" }
- { key: Return,   mods: Control, chars: "\x1b\[13;5u" }
" :h mkdx-setting-enter-shift
" :h mkdx-function-shift-enter-handler
:let g:mkdx#settings = { 'enter': { 'shift': 0 } }

g:mkdx#settings.enter.enable

This setting enables auto-appending list items when you are editing a markdown list. When enter is pressed, a function is executed to detect wether or not to insert a new list item or just do a regular enter. unordered lists and numbered lists are both handled correctly.

NOTE: When this setting is enabled, mkdx will execute a setlocal formatoptions-=r to prevent duplicate list markers from being inserted. This setting is auto updated when available.

" :h mkdx-setting-enter-enable
let g:mkdx#settings = { 'enter': { 'enable': 1 } }

g:mkdx#settings.enter.o

This setting overwrites normal mode o in markdown files and causes o to work like pressing <enter> at the end of the line this means that lists, checklists, checkboxes, quotes etcetera are also inserted when pressing o in normal mode in addition to <enter> in insert mode. Note that g:mkdx#settings.enter.enable must be 1 for this to work.

" :h mkdx-setting-enter-o
let g:mkdx#settings = { 'enter': { 'o': 1 } }

g:mkdx#settings.enter.shifto

This setting enables O in normal mode to prepend list items above the current line, your cursor will be placed after the newly added item. Like g:mkdx#settings.enter.o, checkboxes are also added if they are present on the cursor line. Note that g:mkdx#settings.enter.enable must be 1 for this to work.

" :h mkdx-setting-enter-shifto
let g:mkdx#settings = { 'enter': { 'shifto': 1 } }

g:mkdx#settings.enter.malformed

This setting defines behaviour to use when working with improperly indented markdown lists. At the moment it works for checklist items that do not have an indent() which is divisible by shiftwidth. In which case the indent will be rounded up to the next indent if it is greater than &sw / 2 otherwise it will be rounded down to the previous indent.

" :h mkdx-setting-enter-malformed
let g:mkdx#settings = { 'enter': { 'malformed': 1 } }

g:mkdx#settings.toc.text

Defines the text to use for the table of contents header itself.

This setting is auto updated when available.

" :h mkdx-setting-toc-text
let g:mkdx#settings = { 'toc': { 'text': 'TOC' } }

g:mkdx#settings.toc.list_token

Defines the list token to use in the generated TOC.

" :h mkdx-setting-toc-list-token
let g:mkdx#settings = { 'toc': { 'list_token': '-' } }

g:mkdx#settings.toc.update_on_write

As easy as it is to update the table of contents manually, it is just as easy to forget :) This setting is disabled by default, set it to 1 to enable it and never worry about updating your TOC ever again. When the TOC hasn't yet been generated and g:mkdx#settings.toc.position isn't 0, the TOC will be generated at given position (or as the last header of the document if there aren't enough headers). Otherwise, you will first have to generate the TOC once manually in the desired position and then it will be updated too.

Note: this only works if your vim has('autocmd').

" :h mkdx-setting-toc-update-on-write
let g:mkdx#settings = { 'toc': { 'save_on_write': 0 } }

g:mkdx#settings.toc.position

The position at which to place the TOC, 0 is used for cursor. If a number > 0 is supplied, the TOC will be generated ABOVE that header. e.g. setting it to 1 will cause it to be the first heading of your document.

This setting is auto updated when available.

" :h mkdx-setting-toc-position
let g:mkdx#settings = { 'toc': { 'position': 0 } }

g:mkdx#settings.toc.details.enable

This setting controls wether the generated TOC will be output as a regular markdown list or inside a <details> tag. See: Generate or update TOC as <details> for an example. By default, this option is disabled. To use it, set it's value to 1 instead.

This setting is auto updated when available.

" :h mkdx-setting-toc-details-enable
let g:mkdx#settings = { 'toc': { 'details': { 'enable': 0 } } }

g:mkdx#settings.toc.details.summary

With g:mkdx#settings.toc.details.enable set to 1, a <summary> tag will also be generated inside the resulting <details> tag. This tag contains the text that will be displayed next to the "▶". The default value has a special placeholder {{toc.text}}. This will be replaced with the value of g:mkdx#settings.toc.text upon generation.

This setting is auto updated when available.

" :h mkdx-setting-toc-details-summary
let g:mkdx#settings = { 'toc': { 'details': { 'summary': 'Click to expand {{toc.text}}' } } }

g:mkdx#settings.highlight.enable

This setting enables state-specific highlighting for checkboxes and tables. The highlighting is linked to the gitcommit* family of highlight groups (and Comment for list items), full list: If you want to change the highlighting groups, link them to different groups:

" :h mkdx-highlighting

" these are the defaults, defined by mkdx in after/syntax/markdown/mkdx.vim
highlight default link mkdxListItem Comment
highlight default link mkdxCheckboxEmpty gitcommitUnmergedFile
highlight default link mkdxCheckboxPending gitcommitBranch
highlight default link mkdxCheckboxComplete gitcommitSelectedFile

" to change the color of list items to the "jsOperator" group, one would write this in their vimrc:
highlight link mkdxListItem jsOperator

Note: syntax highlighting is opt-in by default. This means you must explicitly enable this feature to use it. The reason behind this is that this plugin is not a syntax plugin and maybe you are already using one that does such a thing in a way that works better for you. You can see it in action in the Completing checkboxes / checklists examples.

This setting is auto updated when available.

" :h mkdx-setting-highlight-enable
" :h mkdx-highlighting
let g:mkdx#settings = { 'highlight': { 'enable': 0 } }

g:mkdx#settings.auto_update.enable

This setting controls wether or not to update the document after changing certain variables. Currently, this only works if your vim has('*dictwatcheradd').

The following settings are automatically updated:

" :h mkdx-setting-auto-update-enable
let g:mkdx#settings = { 'auto_update': { 'enable': 1 } }

g:mkdx#settings.fold.enable

Controls wether or not to fold fenced (``` / ~~~) code blocks and / or the table of contents (when generated by mkdx). Behaviour can be controlled using g:mkdx#settings.fold.components. Folding is disabled by default, set it to 1 to enable it.

This setting is auto updated when available.

" :h mkdx-setting-fold-enable
:let g:mkdx#settings = { 'fold': { 'enable': 0 } }

g:mkdx#settings.fold.components

Controls which components to fold when g:mkdx#settings.fold.enable is enabled. Currently, two components can be folded, 'toc' and 'fence'. items can be added and removed from the list as needed.

This setting is auto updated when available.

" :h mkdx-setting-fold-components
:let g:mkdx#settings = { 'fold': { 'components': ['toc', 'fence'] } }

Mappings

Mappings can be turned off all together with g:mkdx#settings.map.enable. The plugin checks if a mapping exists before creating it. If it exists, it will not create the mapping. In case a mapping that this plugin provides doesn't work, please check if you have it in your .vimrc.

The below list contains all mappings that mkdx creates by default. To remap functionality: remapping functionality. To prevent mapping of a key from happening, see: unmapping functionality.

Note: replace -{n|v} with just -n or -v when creating your own mappings

description modes mapping Execute
Prev checkbox state normal, visual <PREFIX>- <Plug>(mkdx-checkbox-prev-{n|v})
Next checkbox state normal, visual <PREFIX>= <Plug>(mkdx-checkbox-next-{n|v})
Promote header normal <PREFIX>[ <Plug>(mkdx-promote-header)
Demote header normal <PREFIX>] <Plug>(mkdx-demote-header)
Toggle kbd shortcut normal, visual <PREFIX>k <Plug>(mkdx-toggle-to-kbd-{n|v})
Toggle quote normal, visual <PREFIX>' <Plug>(mkdx-toggle-quote-{n|v})
Toggle checkbox item normal, visual <PREFIX>t <Plug>(mkdx-toggle-checkbox-{n|v})
Toggle checklist item normal, visual <PREFIX>lt <Plug>(mkdx-toggle-checklist-{n|v})
Toggle list item normal, visual <PREFIX>ll <Plug>(mkdx-toggle-list-{n|v})
Wrap link normal, visual <PREFIX>ln <Plug>(mkdx-wrap-link-{n|v})
Italicize text normal, visual <PREFIX>/ <Plug>(mkdx-mkdx-text-italic-{n|v})
Bolden text normal, visual <PREFIX>b <Plug>(mkdx-mkdx-text-bold-{n|v}))
Wrap with inline code normal, visual <PREFIX>` <Plug>(mkdx-mkdx-text-inline-code-{n|v})
Wrap with strikethrough normal, visual <PREFIX>s <Plug>(mkdx-mkdx-text-strike-{n|v})
CSV to table visual <PREFIX>, <Plug>(mkdx-tableize)
Jump to header normal <PREFIX>j <Plug>(mkdx-jump-to-header)
Generate / Update TOC normal <PREFIX>i <Plug>(mkdx-gen-or-upd-toc)
Quickfix TOC normal <PREFIX>I <Plug>(mkdx-quickfix-toc)
Quickfix dead fragment links normal <PREFIX>L <Plug>(mkdx-quickfix-links)
o handler normal o <Plug>(mkdx-o)
O handler normal O <Plug>(mkdx-shift-o)
Insert fenced code block insert ``` <Plug>(mkdx-fence-backtick)
Insert fenced code block insert ~~~ <Plug>(mkdx-fence-tilde)
Insert kbd shortcut insert <tab <Plug>(mkdx-insert-kbd)
enter handler insert enter <Plug>(mkdx-enter)
shift+enter handler insert shift+enter <Plug>(mkdx-shift-enter)
ctrl+n handler insert ctrl+n <Plug>(mkdx-ctrl-n-compl)
ctrl+p handler insert ctrl+p <Plug>(mkdx-ctrl-p-compl)
# handler insert # <Plug>(mkdx-link-compl)

Remapping functionality

<Plug> mappings can easily be remapped to any other key you prefer. When a <Plug>(mkdx-*) mapping is found, mkdx will not create the default mapping for that <Plug>. If you want to disable functionality, see: Unmapping functionality.

" this will remap <leader>q in every filetype, not very handy in most cases
nnoremap <leader>q <Plug>(mkdx-quickfix-toc)

" to keep it limited to markdown files, one can use an "autocommand".
" First, make sure we don't create the default mapping when entering markdown files.
" All plugs can be disabled like this (except insert mode ones, they need "imap" instead of "nmap").
nmap <Plug> <Plug>(mkdx-quickfix-toc)

" then create a function to remap manually
fun! s:MkdxRemap()
    " regular map family can be used since these are buffer local.
    nmap <buffer><silent> <leader>q <Plug>(mkdx-quickfix-toc)
    " other overrides go here
endfun

" finally, add a "FileType" autocommand that calls "s:MkdxRemap()" upon entering markdown filetype
augroup Mkdx
    au!
    au FileType markdown, mkdx call s:MkdxRemap()
augroup END

Unmapping functionality using <Nop>

If you want to unmap specific functionality, you'll have to define a mapping for it. This is required because the plugin maps its keys when opening a markdown file, so if you unmap something, it will still get mapped to other markdown buffers. To disable any map, first find it here or at: :h mkdx-mappings.

Say you want to disable toggling next checkbox state (mapped to <PREFIX>=). In your .vimrc, add the following:

" this will disable toggling checkbox next in normal mode.
nmap <leader>= <Nop>

" this will disable toggling checkbox next in visual mode.
vmap <leader>= <Nop>

The mappings are checked using the value of g:mkdx#settings.map.prefix so you may need to check its value first by running the following: :echo g:mkdx#settings.map.prefix. A better way to prevent mkdx from mapping keys is by remapping <Plug> mappings.

Unmapping functionality using <Plug>

If you don't know what a <Plug> is, it is a builtin tool for plugin authors to provide a more "clear" and user-friendly plugin interface (and to create repeatable mappings with repeat.vim!). All of the functions of mkdx are mapped using <Plug> mappings. To disable a <Plug> mapping, first find it here or at: :h mkdx-plugs.

Say you want to disable the behaviour for toggling the next checkbox state. The corresponding <Plug> is called <Plug>(mkdx-checkbox-next-n). To disable it, add the following to your .vimrc:

map <Plug> <Plug>(mkdx-checkbox-next-n)

Supported grep programs

When your vim has('job') or has('nvim'), mkdx will look for a grep program, the following are recognized:

The listed programs are searched in order, if a program is found, it will be used in various different places. This will prevent your editor from freezing and it'll be blazing fast compared to the builtin Vimscript fallbacks.

Contributing

Found a bug or want to report an issue? Take a look at the CONTRIBUTING file for more information.

Roadmap

  • Improve testing setup
  • Cleanup of code
  • Add more tests for all components

Changelog

The latest changes will be visible in this list. See CHANGELOG.md for older changes.

09-09-2018 VERSION 1.8.1

  • Add: Mkdx can convert tables back to CSV
  • Fix: Only open quickfix when broken links are present(#56)
  • Fix: Table highlighting now stops at the end of a table (#57)
  • Fix: Empty cells in CSV also get a border (#61)
  • Fix: All hasmapto calls are mode specific (#64)
  • Fix: g:mkdx#settings.restore_visual can now be disabled during runtime (#65)
  • Fix: Wrapping at end of line no longer breaks when line ends with non word characters (#67)
  • Fix: No longer pollute register when updating table of contents (#68)
  • Fix: Empty setex-style headers are no longer added to quickfix headers or the table of contents (#69)
  • Fix: Always empty quickfix list before showing dead links (#70)

03-08-2018 VERSION 1.8.0

  • Add: Setting to control updating TOC before writing the buffer (#54)
  • Fix: Unable to position TOC as last header (#53)

30-07-2018 VERSION 1.7.1

  • Add: Pressing enter after an inline list item creates a new list item instead of a blank line