diff --git a/bears/markdown/MarkdownBear.py b/bears/markdown/MarkdownBear.py
index 4cd376fa62..5fc1658baa 100644
--- a/bears/markdown/MarkdownBear.py
+++ b/bears/markdown/MarkdownBear.py
@@ -66,7 +66,27 @@ def create_arguments(filename, file, config_file,
horizontal_rule_spaces: bool=False,
horizontal_rule_repeat: int=3,
max_line_length: int=None,
- check_links: bool=False):
+ check_links: bool=True,
+ blockquote_indentation: int=2,
+ enforce_checkbox_content_indentation: bool=True,
+ code_block_style: str='consistent',
+ enforce_labels_at_eof: bool=True,
+ first_heading_level: int=None,
+ enforce_heading_level_increment: bool=False,
+ max_heading_length: int=60,
+ prohibit_duplicate_definitions: bool=True,
+ prohibit_duplicate_headings_in_section: bool=True,
+ prohibit_duplicate_headings: bool=True,
+ prohibit_empty_url: bool=True,
+ prohibit_irregular_chars_filename:
+ str='\\.a-zA-Z0-9-_',
+ prohibit_punctuations_in_heading: str='.,;:!?',
+ prohibit_html: bool=True,
+ prohibit_shortcut_reference_image: bool=True,
+ prohibit_shortcut_reference_link: bool=True,
+ use_spaces: bool=True,
+ check_undefined_references: bool=True,
+ check_unused_definition: bool=True):
"""
:param bullets:
Character to use for bullets in lists. Can be "-", "*" or "+".
@@ -111,6 +131,121 @@ def create_arguments(filename, file, config_file,
The maximum line length allowed.
:param check_links:
Checks if links to headings and files in markdown are valid.
+ :param blockquote_indentation:
+ Warn when blockquotes are either indented too much or too little.
+ :param enforce_checkbox_content_indentation:
+ Warn when list item checkboxes are followed by unnecessary
+ whitespace.
+ :param code_block_style:
+ Warn when code-blocks do not adhere to a given style. Can be
+ ``consistent``, ``fenced``, or ``indented``. The default value,
+ ``consistent``, detects the first used code-block style, and will
+ warn when a subsequent code-block uses a different style.
+ :param enforce_labels_at_eof:
+ Warn when definitions are not placed at the end of the file.
+ For example: If set to ``True``, this will throw a warning::
+
+ Paragraph.
+ [example]: http://example.com "Example Domain"
+ Another paragraph.
+
+ :param first_heading_level:
+ Warn when the first heading has a level other than a specified
+ value.
+ For example: If set to ``2``, this will throw a warning::
+
+ # Bravo
+ Paragraph.
+
+ :param enforce_heading_level_increment:
+ Warn when headings increment with more than 1 level at a time.
+ For example: If set to ``True``, prefer this::
+
+ # Alpha
+ ## Bravo
+
+ over this::
+
+ # Alpha
+ ### Bravo
+
+ :param max_heading_length:
+ The maximum heading length allowed. Ignores markdown syntax, only
+ checks the plain text content.
+ :param prohibit_duplicate_definitions:
+ Warn when duplicate definitions are found.
+ For example: If set to ``True``, this will throw a warning::
+
+ [foo]: bar
+ [foo]: qux
+
+ :param prohibit_duplicate_headings_in_section:
+ Warn when duplicate headings are found, but only when on the same
+ level, “in” the same section.
+ For example: If set to ``True``, this will throw a warning::
+
+ ## Foxtrot
+ ### Golf
+ ### Golf
+
+ :param prohibit_duplicate_headings:
+ Warn when duplicate headings are found.
+ For example: If set to ``True``, this will throw a warning::
+
+ # Foo
+ ## Foo
+ ## [Foo](http://foo.com/bar)
+
+ :param prohibit_empty_url:
+ Warn for empty URLs in links and images.
+ For example: If set to ``True``, this will throw a warning::
+
+ [golf]().
+ ![hotel]().
+
+ :param prohibit_irregular_chars_filename:
+ Warn when file names contain irregular characters: characters other
+ than alpha-numericals, dashes, dots (full-stops) and underscores.
+ Can take ``RegExp`` or ``string``. Any match by the given
+ expression triggers a warning.
+ :param prohibit_punctuations_in_heading:
+ Warn when a heading ends with a group of characters. Can take a
+ ``string`` that contains the group of characters.
+ :param prohibit_html:
+ Warn when HTML elements are used. Ignores comments, because they
+ are used remark, because markdown doesn’t have native comments.
+ For example: If set to ``True``, this will throw a warning::
+
+
Hello
+
+ :param prohibit_shortcut_reference_image:
+ Warn when shortcut reference images are used.
+ For example: If set to ``True``, this will throw a warning::
+
+ ![foo]
+ [foo]: http://foo.bar/baz.png
+
+ :param prohibit_shortcut_reference_link:
+ Warn when shortcut reference links are used.
+ For example: If set to ``True``, this will throw a warning::
+
+ [foo]
+ [foo]: http://foo.bar/baz
+
+ :param use_spaces:
+ Warn when tabs are used instead of spaces.
+ :param check_undefined_references:
+ Warn when references to undefined definitions are found.
+ For example: If set to ``True``, this will throw a warning::
+
+ [bar][]
+
+ :param check_unused_definition:
+ Warn when unused definitions are found.
+ For example: If set to ``True``, this will throw a warning::
+
+ [bar]: https://example.com
+
"""
remark_configs = {
'bullet': bullets, # - or *
@@ -130,7 +265,28 @@ def create_arguments(filename, file, config_file,
'ruleSpaces': horizontal_rule_spaces, # Bool
'ruleRepetition': horizontal_rule_repeat, # int
}
- remark_lint_configs = {}
+ remark_lint_configs = {
+ 'blockquoteIndentation': blockquote_indentation,
+ 'checkboxContentIndent': enforce_checkbox_content_indentation,
+ 'codeBlockStyle': code_block_style,
+ 'finalDefinition': enforce_labels_at_eof,
+ 'firstHeadingLevel': first_heading_level,
+ 'headingIncrement': enforce_heading_level_increment,
+ 'maximumHeadingLength': max_heading_length,
+ 'noDuplicateDefinitions': prohibit_duplicate_definitions,
+ 'noDuplicateHeadingsInSection':
+ prohibit_duplicate_headings_in_section,
+ 'noDuplicateHeadings': prohibit_duplicate_headings,
+ 'noEmptyURL': prohibit_empty_url,
+ 'noFileNameIrregularCharacters': prohibit_irregular_chars_filename,
+ 'noHeadingPunctuation': prohibit_punctuations_in_heading,
+ 'noHTML': prohibit_html,
+ 'noShortcutReferenceImage': prohibit_shortcut_reference_image,
+ 'noShortcutReferenceLink': prohibit_shortcut_reference_link,
+ 'noTabs': use_spaces,
+ 'noUndefinedReferences': check_undefined_references,
+ 'noUnusedDefinitions': check_unused_definition,
+ }
if max_line_length:
remark_lint_configs['maximumLineLength'] = max_line_length
diff --git a/tests/markdown/MarkdownBearTest.py b/tests/markdown/MarkdownBearTest.py
index 40c0005306..ecde659339 100644
--- a/tests/markdown/MarkdownBearTest.py
+++ b/tests/markdown/MarkdownBearTest.py
@@ -32,6 +32,55 @@
Read more [This link exists](#world).
"""
+blockquote_indentation_file = """> Hello
+
+Paragraph.
+"""
+
+checkbox_content_indentation_file = """Some line.
+
+- [x] List item
+"""
+
+labels_at_eof_unused_definition_file = """Paragraph.
+
+[example]: http://example.com "Example Domain"
+
+Another paragraph.
+"""
+
+first_heading_level_file = """# Bravo
+
+Paragraph.
+"""
+
+heading_level_increment_file = """# Charlie
+
+### Delta
+"""
+
+empty_url_file = """[golf](<>).
+
+![hotel](<>).
+"""
+
+duplicate_heading_file = """## Foxtrot
+
+### Golf
+
+### Golf
+"""
+
+punctuation_in_heading_file = """# Hello:
+
+# Hello?
+
+# Hello!
+"""
+
+html_file = """Hello
+"""
+
MarkdownBearTest = verify_local_bear(MarkdownBear,
valid_files=(test_file2,),
invalid_files=(test_file1,))
@@ -80,3 +129,94 @@ def test_valid_link(self):
with prepare_file(content, None) as (file, fname):
with execute_bear(self.uut, fname, file) as results:
self.assertEqual(results, [])
+
+ def test_blockquote_indentation(self):
+ content = blockquote_indentation_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Remove 1 space between blockquote and '
+ 'content')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_checkbox_content_indentation(self):
+ content = checkbox_content_indentation_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Checkboxes should be followed by a single '
+ 'character')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_codeblock_style_unused_definition(self):
+ content = labels_at_eof_unused_definition_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Move definitions to the end of the file '
+ '(after the node at line `5`)')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+ self.assertEqual(results[1].message,
+ 'Found unused definition')
+ self.assertEqual(results[1].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_first_heading_level(self):
+ content = first_heading_level_file.splitlines()
+ self.section.append(Setting('first_heading_level', 2))
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'First heading level should be `2`')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_heading_level_increment(self):
+ content = heading_level_increment_file.splitlines()
+ self.section.append(Setting('enforce_heading_level_increment', True))
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Heading levels should increment by one '
+ 'level at a time')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_empty_url(self):
+ content = empty_url_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Don’t use links without URL')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+ self.assertEqual(results[1].message,
+ 'Don’t use images without URL')
+ self.assertEqual(results[1].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_duplicate_headings(self):
+ content = duplicate_heading_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Do not use headings with similar content '
+ 'per section (3:1)')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_punctuations_in_heading(self):
+ content = punctuation_in_heading_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Don’t add a trailing `:` to headings')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)
+ self.assertEqual(results[1].message,
+ 'Don’t add a trailing `?` to headings')
+ self.assertEqual(results[1].severity, RESULT_SEVERITY.NORMAL)
+ self.assertEqual(results[2].message,
+ 'Don’t add a trailing `!` to headings')
+ self.assertEqual(results[2].severity, RESULT_SEVERITY.NORMAL)
+
+ def test_html_in_markdown(self):
+ content = html_file.splitlines()
+ with prepare_file(content, None) as (file, fname):
+ with execute_bear(self.uut, fname, file) as results:
+ self.assertEqual(results[0].message,
+ 'Do not use HTML in markdown')
+ self.assertEqual(results[0].severity, RESULT_SEVERITY.NORMAL)