Markdown/CommonMark linting and style checking for Visual Studio Code
The Markdown markup language is designed to be easy to read, write, and understand. It succeeds - and its flexibility is both a benefit and a drawback. Many styles are possible, so formatting can be inconsistent. Some constructs don't work well in all parsers and should be avoided. For example, here are some common/troublesome Markdown constructs.
markdownlint is a Visual Studio Code extension that includes a library of rules to encourage standards and consistency for Markdown files. It is powered by markdownlint for Node.js which is based on markdownlint for Ruby.
- Open Visual Studio Code
- Press
Ctrl+P
to open the Quick Open dialog - Type
ext install markdownlint
to find the extension - Click the
Install
button, then theEnable
button
OR
- Press
Ctrl+Shift+X
to open the Extensions tab - Type
markdownlint
to find the extension - Click the
Install
button, then theEnable
button
OR
- Open a command-line prompt
- Run
code --install-extension DavidAnson.vscode-markdownlint
When editing a Markdown file in Code with markdownlint installed, any lines that violate one of markdownlint's rules (see below) will trigger a Warning in the editor. Warnings are indicated by a wavy green underline and can also be seen by pressing Ctrl+Shift+M
to open the Errors and Warnings dialog. Hover the mouse pointer over a green line to see the warning or press F8
and Shift+F8
to cycle through all the warnings (markdownlint warnings all begin with MD###
). For more information about a markdownlint warning, place the cursor on a line and click the light bulb icon or press Ctrl+.
to open the code action dialog. Clicking one of the warnings in the dialog will display that rule's help entry in the default web browser.
For a tutorial, please see Build an Amazing Markdown Editor Using Visual Studio Code and Pandoc by Dave Johnson.
- MD001 heading-increment/header-increment - Heading levels should only increment by one level at a time
- MD002 first-heading-h1/first-header-h1 - First heading should be a top level heading
- MD003 heading-style/header-style - Heading style
- MD004 ul-style - Unordered list style
- MD005 list-indent - Inconsistent indentation for list items at the same level
- MD006 ul-start-left - Consider starting bulleted lists at the beginning of the line
- MD007 ul-indent - Unordered list indentation
- MD009 no-trailing-spaces - Trailing spaces
- MD010 no-hard-tabs - Hard tabs
- MD011 no-reversed-links - Reversed link syntax
- MD012 no-multiple-blanks - Multiple consecutive blank lines
- MD013 line-length - Line length
- MD014 commands-show-output - Dollar signs used before commands without showing output
- MD018 no-missing-space-atx - No space after hash on atx style heading
- MD019 no-multiple-space-atx - Multiple spaces after hash on atx style heading
- MD020 no-missing-space-closed-atx - No space inside hashes on closed atx style heading
- MD021 no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading
- MD022 blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
- MD023 heading-start-left/header-start-left - Headings must start at the beginning of the line
- MD024 no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
- MD025 single-title/single-h1 - Multiple top level headings in the same document
- MD026 no-trailing-punctuation - Trailing punctuation in heading
- MD027 no-multiple-space-blockquote - Multiple spaces after blockquote symbol
- MD028 no-blanks-blockquote - Blank line inside blockquote
- MD029 ol-prefix - Ordered list item prefix
- MD030 list-marker-space - Spaces after list markers
- MD031 blanks-around-fences - Fenced code blocks should be surrounded by blank lines
- MD032 blanks-around-lists - Lists should be surrounded by blank lines
- MD033 no-inline-html - Inline HTML
- MD034 no-bare-urls - Bare URL used
- MD035 hr-style - Horizontal rule style
- MD036 no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading
- MD037 no-space-in-emphasis - Spaces inside emphasis markers
- MD038 no-space-in-code - Spaces inside code span elements
- MD039 no-space-in-links - Spaces inside link text
- MD040 fenced-code-language - Fenced code blocks should have a language specified
- MD041 first-line-heading/first-line-h1 - First line in file should be a top level heading
- MD042 no-empty-links - No empty links
- MD043 required-headings/required-headers - Required heading structure
- MD044 proper-names - Proper names should have the correct capitalization
- MD045 no-alt-text - Images should have alternate text (alt text)
- MD046 code-block-style - Code block style
- MD047 single-trailing-newline - Files should end with a single newline character
See markdownlint's Rules.md file for more details.
The following rules can be automatically fixed by moving the cursor to a rule violation (green underlined text) and typing Ctrl+.
or clicking the light bulb icon.
- MD006 ul-start-left
- MD009 no-trailing-spaces
- MD010 no-hard-tabs
- MD011 no-reversed-links
- MD018 no-missing-space-atx
- MD019 no-multiple-space-atx
- MD020 no-missing-space-closed-atx
- MD021 no-multiple-space-closed-atx
- MD023 heading-start-left
- MD027 no-multiple-space-blockquote
- MD028 no-blanks-blockquote
- MD034 no-bare-urls
- MD037 no-space-in-emphasis
- MD038 no-space-in-code
- MD039 no-space-in-links
- MD047 single-trailing-newline
Fixes can be reverted by Edit|Undo
or Ctrl+Z
.
The default rule configuration disables MD013
/line-length
because many files include lines longer than the conventional 80 character limit:
{
"MD013": false
}
Note:
MD002
/first-heading-h1
is disabled by default because it has been deprecated in themarkdownlint
library.
Rules can be enabled, disabled, and customized by creating a JSON file named .markdownlint.json
or a YAML file named .markdownlint.yaml
(or .markdownlint.yml
) in any directory of a project. The rules defined by .markdownlint.{json,yaml,yml}
apply to Markdown files in the same directory and any sub-directories without their own .markdownlint.{json,yaml,yml}
.
Note:
.markdownlint.{json,yaml,yml}
is used only if a project has been opened. When no folder is open or a file is not part of the current project, normal user and workspace settings apply (see below). If multiple of these files are present in the same directory,.json
will be used instead of.yaml
or.yml
;.yaml
will be used instead of.yml
.
A custom configuration is often defined by a .markdownlint.json
file in the root of the project:
{
"default": true,
"MD003": { "style": "atx_closed" },
"MD007": { "indent": 4 },
"no-hard-tabs": false
}
To extend another configuration file, any configuration file can use the extends
property to provide a relative path:
{
"extends": "../.markdownlint.json",
"no-hard-tabs": true
}
Files referenced via extends
do not need to be part of the current project (but usually are).
Rules can also be configured using Code's support for user and workspace settings.
The earlier configuration might look like the following in Code's user settings:
{
"editor.someSetting": true,
"markdownlint.config": {
"default": true,
"MD003": { "style": "atx_closed" },
"MD007": { "indent": 4 },
"no-hard-tabs": false
}
}
File paths referenced by extends
from user settings are resolved relative to the user's home directory (ex: %USERPROFILE%
on Windows or $HOME
on macOS/Linux).
Configuration locations have the following precedence (in decreasing order):
.markdownlint.{json,yaml,yml}
file in the same directory.markdownlint.{json,yaml,yml}
file in a parent directory.markdownlint.{json,yaml,yml}
file in the root of the project- Visual Studio Code user/workspace settings
- Default configuration (see above)
Once a configuration is found, lower-precedence locations are ignored. Changes saved to any location take effect immediately. Files referenced via extends
are not monitored for changes. Only the last two locations apply to files outside a project.
See markdownlint's options.config section for more information about rule configuration.
If a workspace contains generated content or other Markdown files that trigger warnings but cannot be fixed, it may be helpful to ignore (skip) those files when linting. This can be done by updating the user/workspace configuration with a glob expression matching the relevant file names.
An example from Code's workspace settings might be:
{
"editor.someSetting": true,
"markdownlint.ignore": [
"ignore.md",
"directory/ignore.md",
"**/ignore.md",
"**/*.md"
]
}
The globbing library used for matching file names is minimatch with the dot
and nocomment
options enabled. Matching is case-sensitive and paths are resolved relative to the root of the workspace. The directory separator is /
, even on Windows.
By default, linting is performed as you type or edit a document. Linting is fast and efficient and should not interfere with typical workflows.
If you find this distracting, linting can be configured to run only when the document is saved. This looks like the following in Code's user settings:
{
"editor.someSetting": true,
"markdownlint.run": "onSave"
}
Note: When configured to run
onSave
, the list of reported issues will become outdated while the document is edited and will update when the document is saved.
Custom rules can be specified in Code's user/workspace configuration to apply additional linting beyond the default set of rules. Custom rules are specified by the path to a JavaScript file or the path to an npm package exporting one rule or an array of rules.
Paths are normally relative to the root of the current workspace (or the Code install directory when no folder is open). Paths can also be absolute. When adding custom rules to a workspace, consider committing those rules under the .vscode
directory where they will be separate from other workspace content and available to everyone who clones the repository.
Paths of the form {extension}/path
are relative to the base directory of the Code extension named extension
(which must already be installed). This syntax allows custom rules to be included within another extension's package and shared across multiple workspaces.
An example of Code's workspace settings for custom rules might look like the following:
{
"editor.someSetting": true,
"markdownlint.customRules": [
".vscode/my-custom-rule.js",
".vscode/my-custom-rule-array.js",
".vscode/npm-package-for-custom-rule",
"c:\\absolute\\path\\to\\custom\\rule.js",
"{publisher.extension-name}/custom-rule.js",
"{publisher.extension-name}/npm/rule/package"
]
}
To troubleshoot issues loading or running custom rules, please refer to diagnostic messages from the extension in Code's Output window.
For information about authoring custom rules, see markdownlint/CustomRules.
A list of workspace paths for which the user's response to the custom rule prompt was "Always allow". This setting is updated automatically by the extension, but can be modified to reset the prompt for a workspace.
Note: This setting is only valid as a user setting, not as a workspace setting (where it could be set by a malicious workspace).
Individual warnings can be suppressed with inline comments:
<!-- markdownlint-disable MD037 -->
deliberate space * in * emphasis
<!-- markdownlint-enable MD037 -->
The following snippets are available to help (press Ctrl+Space
for IntelliSense suggestions):
markdownlint-disable
markdownlint-enable
markdownlint-capture
markdownlint-restore
See markdownlint's configuration section for more details.
See CHANGELOG.md.