Add fenced code block style rule (#224) (fixes #223).

This commit is contained in:
Marius Lichtblau 2019-10-08 21:10:02 -07:00 committed by David Anson
parent fd06a50ee5
commit e9b3cc4c18
17 changed files with 232 additions and 25 deletions

View file

@ -90,6 +90,7 @@ playground for learning and exploring.
* **[MD045](doc/Rules.md#md045)** *no-alt-text* - Images should have alternate text (alt text) * **[MD045](doc/Rules.md#md045)** *no-alt-text* - Images should have alternate text (alt text)
* **[MD046](doc/Rules.md#md046)** *code-block-style* - Code block style * **[MD046](doc/Rules.md#md046)** *code-block-style* - Code block style
* **[MD047](doc/Rules.md#md047)** *single-trailing-newline* - Files should end with a single newline character * **[MD047](doc/Rules.md#md047)** *single-trailing-newline* - Files should end with a single newline character
* **[MD048](doc/Rules.md#md048)** *code-fence-style* - Code fence style
See [Rules.md](doc/Rules.md) for more details. See [Rules.md](doc/Rules.md) for more details.
@ -109,7 +110,7 @@ Tags group related rules and can be used to enable/disable multiple rules at onc
* **blank_lines** - MD012, MD022, MD031, MD032, MD047 * **blank_lines** - MD012, MD022, MD031, MD032, MD047
* **blockquote** - MD027, MD028 * **blockquote** - MD027, MD028
* **bullet** - MD004, MD005, MD006, MD007, MD032 * **bullet** - MD004, MD005, MD006, MD007, MD032
* **code** - MD014, MD031, MD038, MD040, MD046 * **code** - MD014, MD031, MD038, MD040, MD046, MD048
* **emphasis** - MD036, MD037 * **emphasis** - MD036, MD037
* **hard_tab** - MD010 * **hard_tab** - MD010
* **headers** - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, * **headers** - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023,

View file

@ -1601,3 +1601,41 @@ To fix the violation, add a newline character to the end of the file:
This file ends with a newline. This file ends with a newline.
[EOF] [EOF]
``` ```
<a name="md048"></a>
## MD048 - Code fence style
Tags: code
Aliases: code-fence-style
Parameters: style ("consistent", "tilde", "backtick"; default "consistent")
This rule is triggered when the symbols used in the document for fenced code blocks do not match the configured code fence style:
````markdown
```ruby
# Fenced code
```
~~~ruby
# Fenced code
~~~
````
To fix this issue, use the configured code fence style throughout the
document:
````markdown
```ruby
# Fenced code
```
```ruby
# Fenced code
```
````
The configured list style can be a specific symbol to use (backtick, tilde), or
can require that usage be consistent within the document.

38
lib/md048.js Normal file
View file

@ -0,0 +1,38 @@
// @ts-check
"use strict";
const { addErrorDetailIf } = require("../helpers");
function fencedCodeBlockStyleFor(markup) {
switch (markup[0]) {
case "~":
return "tilde";
default:
return "backtick";
}
}
module.exports = {
"names": [ "MD048", "code-fence-style" ],
"description": "Code fence style",
"tags": [ "code" ],
"function": function MD048(params, onError) {
const style = params.config.style || "consistent";
let expectedStyle = style;
params.tokens
.filter((token) => token.type === "fence")
.forEach((fenceToken) => {
const { lineNumber, markup } = fenceToken;
if (expectedStyle === "consistent") {
expectedStyle = fencedCodeBlockStyleFor(markup);
}
addErrorDetailIf(
onError,
lineNumber,
expectedStyle,
fencedCodeBlockStyleFor(markup)
);
});
}
};

View file

@ -50,7 +50,8 @@ const rules = [
require("./md044"), require("./md044"),
require("./md045"), require("./md045"),
require("./md046"), require("./md046"),
require("./md047") require("./md047"),
require("./md048")
]; ];
rules.forEach((rule) => { rules.forEach((rule) => {
const name = rule.names[0].toLowerCase(); const name = rule.names[0].toLowerCase();

View file

@ -334,6 +334,20 @@ rules.forEach(function forRule(rule) {
} }
}; };
break; break;
case "MD048":
scheme.properties = {
"style": {
"description": "Code fence syle",
"type": "string",
"enum": [
"consistent",
"backtick",
"tilde"
],
"default": "consistent"
}
};
break;
default: default:
custom = false; custom = false;
break; break;

View file

@ -1339,6 +1339,48 @@
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },
"MD048": {
"description": "MD048/code-fence-style - Code fence style",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"style": {
"description": "Code fence syle",
"type": "string",
"enum": [
"consistent",
"backtick",
"tilde"
],
"default": "consistent"
}
},
"additionalProperties": false
},
"code-fence-style": {
"description": "MD048/code-fence-style - Code fence style",
"type": [
"boolean",
"object"
],
"default": true,
"properties": {
"style": {
"description": "Code fence syle",
"type": "string",
"enum": [
"consistent",
"backtick",
"tilde"
],
"default": "consistent"
}
},
"additionalProperties": false
},
"headings": { "headings": {
"description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043", "description": "headings - MD001, MD002, MD003, MD018, MD019, MD020, MD021, MD022, MD023, MD024, MD025, MD026, MD036, MD041, MD043",
"type": "boolean", "type": "boolean",
@ -1390,7 +1432,7 @@
"default": true "default": true
}, },
"code": { "code": {
"description": "code - MD014, MD031, MD038, MD040, MD046", "description": "code - MD014, MD031, MD038, MD040, MD046, MD048",
"type": "boolean", "type": "boolean",
"default": true "default": true
}, },

View file

@ -74,6 +74,10 @@ Code `with ` space {MD038}
code fence without language {MD040:73} {MD046:73} code fence without language {MD040:73} {MD046:73}
``` ```
~~~js
code fence with different style {MD048:77} {MD046:77}
~~~
[empty link]() {MD042} [empty link]() {MD042}
markdownLint {MD044} markdownLint {MD044}

View file

@ -0,0 +1,6 @@
{
"default": true,
"MD048": {
"style": "backtick"
}
}

View file

@ -0,0 +1,19 @@
```text
This is a code block
```
~~~text
This is {MD048:5} a code block
~~~
```text
~~~
This is fine
~~~
```
~~~text
```
This is not {MD048:15}
```
~~~

View file

@ -0,0 +1,6 @@
{
"default": true,
"MD048": {
"style": "tilde"
}
}

View file

@ -0,0 +1,19 @@
```text
This is {MD048:1} a code block
```
~~~text
This is a code block
~~~
```text
~~~
This is not fine {MD048:9}
~~~
```
~~~text
```
This is
```
~~~

View file

@ -24,4 +24,8 @@ Fenced code
Indented code Indented code
~~~text
Fenced code
~~~
Missing newline character Missing newline character

View file

@ -24,4 +24,8 @@ Fenced code
Indented code Indented code
~~~text
Fenced code
~~~
Missing newline character Missing newline character

View file

@ -45,7 +45,7 @@
"errorRange": [25, 13] "errorRange": [25, 13]
}, },
{ {
"lineNumber": 27, "lineNumber": 31,
"ruleNames": [ "MD043", "required-headings", "required-headers" ], "ruleNames": [ "MD043", "required-headings", "required-headers" ],
"ruleDescription": "Required heading structure", "ruleDescription": "Required heading structure",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md043", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md043",
@ -99,12 +99,21 @@
"errorRange": null "errorRange": null
}, },
{ {
"lineNumber": 27, "lineNumber": 31,
"ruleNames": [ "MD047", "single-trailing-newline" ], "ruleNames": [ "MD047", "single-trailing-newline" ],
"ruleDescription": "Files should end with a single newline character", "ruleDescription": "Files should end with a single newline character",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md047", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md047",
"errorDetail": null, "errorDetail": null,
"errorContext": null, "errorContext": null,
"errorRange": [ 25, 1 ] "errorRange": [ 25, 1 ]
},
{
"lineNumber": 27,
"ruleNames": [ "MD048", "code-fence-style" ],
"ruleDescription": "Code fence style",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/Rules.md#md048",
"errorDetail": "Expected: backtick; Actual: tilde",
"errorContext": null,
"errorRange": null
} }
] ]

View file

@ -24,4 +24,4 @@ None of the above should trigger any heading related rules.
Code block without a language specifier Code block without a language specifier
``` ```
{MD040:23} {MD040:23} {MD048:13}

View file

@ -4,7 +4,7 @@ text {MD031:4}
```fence ```fence
code {MD031:6} code {MD031:6}
``` ```
text {MD031:8} text {MD031:8} {MD048:8}
~~~fence ~~~fence
code code
~~~ ~~~
@ -14,7 +14,7 @@ text {MD031:10} {MD031:12}
code code
~~~ ~~~
``` ```
text {MD031:16} {MD031:18} text {MD031:16} {MD031:18} {MD048:18}
~~~fence ~~~fence
```fence ```fence
code code
@ -28,7 +28,7 @@ code
~~~ ~~~
``` ```
text {MD031:30} {MD031:32} text {MD031:30} {MD031:32} {MD048:32}
~~~fence ~~~fence
```fence ```fence
@ -41,7 +41,7 @@ text {MD031:38} {MD031:40}
code code
~~~ ~~~
``` ```
text {MD031:43} {MD031:45} text {MD031:43} {MD031:45} {MD048:45}
~~~fence ~~~fence
code code
``` ```
@ -52,7 +52,7 @@ text {MD031:48} {MD031:50}
code code
``` ```
```` ````
text {MD031:54} {MD031:56} text {MD031:54} {MD031:56} {MD048:56}
~~~~fence ~~~~fence
~~~fence ~~~fence
code code
@ -64,7 +64,7 @@ text {MD031:60} {MD031:62}
code code
``` ```
````` `````
text {MD031:66} {MD031:68} text {MD031:66} {MD031:68} {MD048:68}
~~~~fence ~~~~fence
~~~fence ~~~fence
code code

View file

@ -1124,7 +1124,7 @@ module.exports.styleAll = function styleAll(test) {
"MD019": [ 27 ], "MD019": [ 27 ],
"MD020": [ 29 ], "MD020": [ 29 ],
"MD021": [ 31 ], "MD021": [ 31 ],
"MD022": [ 82 ], "MD022": [ 86 ],
"MD023": [ 40 ], "MD023": [ 40 ],
"MD024": [ 35 ], "MD024": [ 35 ],
"MD026": [ 40 ], "MD026": [ 40 ],
@ -1143,10 +1143,11 @@ module.exports.styleAll = function styleAll(test) {
"MD039": [ 71 ], "MD039": [ 71 ],
"MD040": [ 73 ], "MD040": [ 73 ],
"MD041": [ 1 ], "MD041": [ 1 ],
"MD042": [ 77 ], "MD042": [ 81 ],
"MD045": [ 81 ], "MD045": [ 85 ],
"MD046": [ 49, 73 ], "MD046": [ 49, 73, 77 ],
"MD047": [ 84 ] "MD047": [ 88 ],
"MD048": [ 77 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -1175,7 +1176,7 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD019": [ 27 ], "MD019": [ 27 ],
"MD020": [ 29 ], "MD020": [ 29 ],
"MD021": [ 31 ], "MD021": [ 31 ],
"MD022": [ 82 ], "MD022": [ 86 ],
"MD023": [ 40 ], "MD023": [ 40 ],
"MD024": [ 35 ], "MD024": [ 35 ],
"MD026": [ 40 ], "MD026": [ 40 ],
@ -1184,10 +1185,11 @@ module.exports.styleRelaxed = function styleRelaxed(test) {
"MD032": [ 7, 8, 51 ], "MD032": [ 7, 8, 51 ],
"MD035": [ 61 ], "MD035": [ 61 ],
"MD036": [ 65 ], "MD036": [ 65 ],
"MD042": [ 77 ], "MD042": [ 81 ],
"MD045": [ 81 ], "MD045": [ 85 ],
"MD046": [ 49, 73 ], "MD046": [ 49, 73, 77 ],
"MD047": [ 84 ] "MD047": [ 88 ],
"MD048": [ 77 ]
} }
}; };
test.deepEqual(actualResult, expectedResult, "Undetected issues."); test.deepEqual(actualResult, expectedResult, "Undetected issues.");
@ -1430,7 +1432,7 @@ module.exports.missingStringValue = function missingStringValue(test) {
}; };
module.exports.readme = function readme(test) { module.exports.readme = function readme(test) {
test.expect(113); test.expect(115);
const tagToRules = {}; const tagToRules = {};
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
rule.tags.forEach(function forTag(tag) { rule.tags.forEach(function forTag(tag) {
@ -1499,7 +1501,7 @@ module.exports.readme = function readme(test) {
}; };
module.exports.doc = function doc(test) { module.exports.doc = function doc(test) {
test.expect(328); test.expect(336);
fs.readFile("doc/Rules.md", helpers.utf8Encoding, fs.readFile("doc/Rules.md", helpers.utf8Encoding,
function readFile(err, contents) { function readFile(err, contents) {
test.ifError(err); test.ifError(err);
@ -2787,7 +2789,7 @@ module.exports.configBadHybridSync = function configBadHybridSync(test) {
module.exports.allBuiltInRulesHaveValidUrl = module.exports.allBuiltInRulesHaveValidUrl =
function allBuiltInRulesHaveValidUrl(test) { function allBuiltInRulesHaveValidUrl(test) {
test.expect(129); test.expect(132);
rules.forEach(function forRule(rule) { rules.forEach(function forRule(rule) {
test.ok(rule.information); test.ok(rule.information);
test.ok(Object.getPrototypeOf(rule.information) === URL.prototype); test.ok(Object.getPrototypeOf(rule.information) === URL.prototype);