mdl 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +7 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +79 -0
- data/Rakefile +8 -0
- data/bin/mdl +10 -0
- data/docs/RULES.md +609 -0
- data/docs/creating_rules.md +83 -0
- data/docs/creating_styles.md +47 -0
- data/example/markdown_spec.md +897 -0
- data/lib/mdl.rb +72 -0
- data/lib/mdl/cli.rb +89 -0
- data/lib/mdl/config.rb +9 -0
- data/lib/mdl/doc.rb +252 -0
- data/lib/mdl/kramdown_parser.rb +29 -0
- data/lib/mdl/rules.rb +393 -0
- data/lib/mdl/ruleset.rb +47 -0
- data/lib/mdl/style.rb +50 -0
- data/lib/mdl/styles/all.rb +1 -0
- data/lib/mdl/styles/cirosantilli.rb +6 -0
- data/lib/mdl/styles/default.rb +1 -0
- data/lib/mdl/styles/relaxed.rb +6 -0
- data/lib/mdl/version.rb +3 -0
- data/mdl.gemspec +31 -0
- data/test/rule_tests/atx_closed_header_spacing.md +17 -0
- data/test/rule_tests/atx_header_spacing.md +5 -0
- data/test/rule_tests/blockquote_blank_lines.md +31 -0
- data/test/rule_tests/blockquote_spaces.md +21 -0
- data/test/rule_tests/bulleted_list_2_space_indent.md +6 -0
- data/test/rule_tests/bulleted_list_2_space_indent_style.rb +2 -0
- data/test/rule_tests/bulleted_list_4_space_indent.md +3 -0
- data/test/rule_tests/bulleted_list_not_at_beginning_of_line.md +14 -0
- data/test/rule_tests/code_block_dollar.md +22 -0
- data/test/rule_tests/consecutive_blank_lines.md +11 -0
- data/test/rule_tests/consistent_bullet_styles_asterisk.md +3 -0
- data/test/rule_tests/consistent_bullet_styles_dash.md +3 -0
- data/test/rule_tests/consistent_bullet_styles_plus.md +3 -0
- data/test/rule_tests/empty_doc.md +0 -0
- data/test/rule_tests/fenced_code_blocks.md +21 -0
- data/test/rule_tests/first_header_bad_atx.md +1 -0
- data/test/rule_tests/first_header_bad_setext.md +2 -0
- data/test/rule_tests/first_header_good_atx.md +1 -0
- data/test/rule_tests/first_header_good_setext.md +2 -0
- data/test/rule_tests/header_duplicate_content.md +11 -0
- data/test/rule_tests/header_multiple_toplevel.md +3 -0
- data/test/rule_tests/header_mutliple_h1_no_toplevel.md +5 -0
- data/test/rule_tests/header_trailing_punctuation.md +11 -0
- data/test/rule_tests/header_trailing_punctuation_customized.md +14 -0
- data/test/rule_tests/header_trailing_punctuation_customized_style.rb +2 -0
- data/test/rule_tests/headers_bad.md +7 -0
- data/test/rule_tests/headers_good.md +5 -0
- data/test/rule_tests/headers_surrounding_space_atx.md +9 -0
- data/test/rule_tests/headers_surrounding_space_setext.md +15 -0
- data/test/rule_tests/headers_with_spaces_at_the_beginning.md +9 -0
- data/test/rule_tests/inconsistent_bullet_indent_same_level.md +4 -0
- data/test/rule_tests/inconsistent_bullet_styles_asterisk.md +3 -0
- data/test/rule_tests/inconsistent_bullet_styles_dash.md +3 -0
- data/test/rule_tests/inconsistent_bullet_styles_plus.md +3 -0
- data/test/rule_tests/incorrect_bullet_style_asterisk.md +3 -0
- data/test/rule_tests/incorrect_bullet_style_asterisk_style.rb +2 -0
- data/test/rule_tests/incorrect_bullet_style_dash.md +3 -0
- data/test/rule_tests/incorrect_bullet_style_dash_style.rb +2 -0
- data/test/rule_tests/incorrect_bullet_style_plus.md +3 -0
- data/test/rule_tests/incorrect_bullet_style_plus_style.rb +2 -0
- data/test/rule_tests/incorrect_header_atx.md +6 -0
- data/test/rule_tests/incorrect_header_atx_closed.md +6 -0
- data/test/rule_tests/incorrect_header_atx_closed_style.rb +2 -0
- data/test/rule_tests/incorrect_header_atx_style.rb +2 -0
- data/test/rule_tests/incorrect_header_setext.md +6 -0
- data/test/rule_tests/incorrect_header_setext_style.rb +2 -0
- data/test/rule_tests/long_lines.md +3 -0
- data/test/rule_tests/long_lines_100.md +7 -0
- data/test/rule_tests/long_lines_100_style.rb +2 -0
- data/test/rule_tests/mixed_header_types_atx.md +6 -0
- data/test/rule_tests/mixed_header_types_atx_closed.md +6 -0
- data/test/rule_tests/mixed_header_types_setext.md +6 -0
- data/test/rule_tests/ordered_list_item_prefix.md +13 -0
- data/test/rule_tests/ordered_list_item_prefix_ordered.md +13 -0
- data/test/rule_tests/ordered_list_item_prefix_ordered_style.rb +2 -0
- data/test/rule_tests/reversed_link.md +7 -0
- data/test/rule_tests/spaces_after_list_marker.md +74 -0
- data/test/rule_tests/spaces_after_list_marker_style.rb +3 -0
- data/test/rule_tests/whitespace issues.md +3 -0
- data/test/setup_tests.rb +5 -0
- data/test/test_ruledocs.rb +45 -0
- data/test/test_rules.rb +56 -0
- data/tools/README.md +3 -0
- data/tools/test_location.rb +20 -0
- data/tools/view_markdown.rb +11 -0
- metadata +314 -0
data/lib/mdl.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'mdl/cli'
|
2
|
+
require 'mdl/config'
|
3
|
+
require 'mdl/doc'
|
4
|
+
require 'mdl/kramdown_parser'
|
5
|
+
require 'mdl/ruleset'
|
6
|
+
require 'mdl/style'
|
7
|
+
require 'mdl/version'
|
8
|
+
|
9
|
+
require 'kramdown'
|
10
|
+
|
11
|
+
module MarkdownLint
|
12
|
+
def self.run
|
13
|
+
cli = MarkdownLint::CLI.new
|
14
|
+
cli.run
|
15
|
+
rules = RuleSet.load_default
|
16
|
+
style = Style.load(Config[:style], rules)
|
17
|
+
# Rule option filter
|
18
|
+
rules.select! {|r| Config[:rules].include?(r) } if Config[:rules]
|
19
|
+
# Tag option filter
|
20
|
+
rules.select! {|r, v| not (v.tags & Config[:tags]).empty? } if Config[:tags]
|
21
|
+
|
22
|
+
if Config[:list_rules]
|
23
|
+
puts "Enabled rules:"
|
24
|
+
rules.each do |id, rule|
|
25
|
+
if Config[:verbose]
|
26
|
+
puts "#{id} (#{rule.tags.join(', ')}) - #{rule.description}"
|
27
|
+
else
|
28
|
+
puts "#{id} - #{rule.description}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
exit 0
|
32
|
+
end
|
33
|
+
|
34
|
+
# Recurse into directories
|
35
|
+
cli.cli_arguments.each_with_index do |filename, i|
|
36
|
+
if Dir.exist?(filename)
|
37
|
+
pattern = "#{filename}/**/*.md" # This works for both Dir and ls-files
|
38
|
+
if Config[:git_recurse]
|
39
|
+
Dir.chdir(filename) do
|
40
|
+
cli.cli_arguments[i] = %x(git ls-files '*.md').split("\n")
|
41
|
+
end
|
42
|
+
else
|
43
|
+
cli.cli_arguments[i] = Dir["#{filename}/**/*.md"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
cli.cli_arguments.flatten!
|
48
|
+
|
49
|
+
status = 0
|
50
|
+
cli.cli_arguments.each do |filename|
|
51
|
+
puts "Checking #{filename}..." if Config[:verbose]
|
52
|
+
doc = Doc.new_from_file(filename)
|
53
|
+
filename = '(stdin)' if filename == "-"
|
54
|
+
if Config[:show_kramdown_warnings]
|
55
|
+
status = 2 if not doc.parsed.warnings.empty?
|
56
|
+
doc.parsed.warnings.each do |w|
|
57
|
+
puts "#{filename}: Kramdown Warning: #{w}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rules.sort.each do |id, rule|
|
61
|
+
puts "Processing rule #{id}" if Config[:verbose]
|
62
|
+
error_lines = rule.check.call(doc)
|
63
|
+
next if error_lines.nil? or error_lines.empty?
|
64
|
+
status = 1
|
65
|
+
error_lines.each do |line|
|
66
|
+
puts "#{filename}:#{line}: #{id} #{rule.description}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
exit status
|
71
|
+
end
|
72
|
+
end
|
data/lib/mdl/cli.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
|
3
|
+
module MarkdownLint
|
4
|
+
class CLI
|
5
|
+
include Mixlib::CLI
|
6
|
+
|
7
|
+
banner "Usage: #{File.basename($0)} [options] [FILE.md|DIR ...]"
|
8
|
+
|
9
|
+
option :config_file,
|
10
|
+
:short => '-c',
|
11
|
+
:long => '--config FILE',
|
12
|
+
:description => 'The configuration file to use',
|
13
|
+
:default => '~/.mdlrc'
|
14
|
+
|
15
|
+
option :verbose,
|
16
|
+
:short => '-v',
|
17
|
+
:long => '--[no-]verbose',
|
18
|
+
:description => 'Increase verbosity',
|
19
|
+
:boolean => true
|
20
|
+
|
21
|
+
option :show_kramdown_warnings,
|
22
|
+
:short => '-w',
|
23
|
+
:long => '--[no-]warnings',
|
24
|
+
:description => 'Show kramdown warnings',
|
25
|
+
:boolean => true
|
26
|
+
|
27
|
+
option :tags,
|
28
|
+
:short => '-t',
|
29
|
+
:long => '--tags TAG1,TAG2',
|
30
|
+
:description => 'Only process rules with these tags',
|
31
|
+
:proc => Proc.new { |v| v.split(',').map { |t| t.to_sym } }
|
32
|
+
|
33
|
+
option :rules,
|
34
|
+
:short => '-r',
|
35
|
+
:long => '--rules RULE1,RULE2',
|
36
|
+
:description => 'Only process these rules',
|
37
|
+
:proc => Proc.new { |v| v.split(',') }
|
38
|
+
|
39
|
+
option :style,
|
40
|
+
:short => '-s',
|
41
|
+
:long => '--style STYLE',
|
42
|
+
:description => "Load the given style"
|
43
|
+
|
44
|
+
option :list_rules,
|
45
|
+
:short => '-l',
|
46
|
+
:long => '--list-rules',
|
47
|
+
:boolean => true,
|
48
|
+
:description => "Don't process any files, just list enabled rules"
|
49
|
+
|
50
|
+
option :git_recurse,
|
51
|
+
:short => '-g',
|
52
|
+
:long => '--git-recurse',
|
53
|
+
:boolean => true,
|
54
|
+
:description => "Only process files known to git when given a directory"
|
55
|
+
|
56
|
+
option :help,
|
57
|
+
:on => :tail,
|
58
|
+
:short => '-h',
|
59
|
+
:long => '--help',
|
60
|
+
:description => 'Show this message',
|
61
|
+
:boolean => true,
|
62
|
+
:show_options => true,
|
63
|
+
:exit => 0
|
64
|
+
|
65
|
+
option :version,
|
66
|
+
:on => :tail,
|
67
|
+
:short => "-V",
|
68
|
+
:long => "--version",
|
69
|
+
:description => "Show version",
|
70
|
+
:boolean => true,
|
71
|
+
:proc => Proc.new { puts MarkdownLint::VERSION },
|
72
|
+
:exit => 0
|
73
|
+
|
74
|
+
def run(argv=ARGV)
|
75
|
+
parse_options(argv)
|
76
|
+
# Load the config file if it's present
|
77
|
+
filename = File.expand_path(config[:config_file])
|
78
|
+
MarkdownLint::Config.from_file(filename) if File.exists?(filename)
|
79
|
+
|
80
|
+
# Put values in the config file
|
81
|
+
MarkdownLint::Config.merge!(config)
|
82
|
+
|
83
|
+
# Read from stdin if we didn't provide a filename
|
84
|
+
if cli_arguments.empty? and not config[:list_rules]
|
85
|
+
cli_arguments << "-"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/mdl/config.rb
ADDED
data/lib/mdl/doc.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
require 'mdl/kramdown_parser'
|
3
|
+
|
4
|
+
module MarkdownLint
|
5
|
+
##
|
6
|
+
# Representation of the markdown document passed to rule checks
|
7
|
+
|
8
|
+
class Doc
|
9
|
+
##
|
10
|
+
# A list of raw markdown source lines. Note that the list is 0-indexed,
|
11
|
+
# while line numbers in the parsed source are 1-indexed, so you need to
|
12
|
+
# subtract 1 from a line number to get the correct line. The element_line*
|
13
|
+
# methods take care of this for you.
|
14
|
+
|
15
|
+
attr_reader :lines
|
16
|
+
|
17
|
+
##
|
18
|
+
# A Kramdown::Document object containing the parsed markdown document.
|
19
|
+
|
20
|
+
attr_reader :parsed
|
21
|
+
|
22
|
+
##
|
23
|
+
# A list of top level Kramdown::Element objects from the parsed document.
|
24
|
+
|
25
|
+
attr_reader :elements
|
26
|
+
|
27
|
+
##
|
28
|
+
# Create a new document given a string containing the markdown source
|
29
|
+
|
30
|
+
def initialize(text)
|
31
|
+
# Workaround for the following two issues:
|
32
|
+
# https://github.com/mivok/markdownlint/issues/52
|
33
|
+
# https://github.com/gettalong/kramdown/issues/158
|
34
|
+
# Unfortunately this forces all input text back into ascii, which may
|
35
|
+
# be problematic for any rules that make use of non-ascii characters, so
|
36
|
+
# we should remove this if it no longer becomes necessary to do so.
|
37
|
+
text.encode!("ASCII", invalid: :replace, undef: :replace, replace: '')
|
38
|
+
|
39
|
+
@lines = text.split("\n")
|
40
|
+
@parsed = Kramdown::Document.new(text, :input => 'MarkdownLint')
|
41
|
+
@elements = @parsed.root.children
|
42
|
+
add_levels(@elements)
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Alternate 'constructor' passing in a filename
|
47
|
+
|
48
|
+
def self.new_from_file(filename)
|
49
|
+
if filename == "-"
|
50
|
+
self.new(STDIN.read)
|
51
|
+
else
|
52
|
+
self.new(File.read(filename))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Find all elements of a given type, returning their options hash. The
|
58
|
+
# options hash has most of the useful data about an element and often you
|
59
|
+
# can just use this in your rules.
|
60
|
+
#
|
61
|
+
# # Returns [ { :location => 1, :element_level => 2 }, ... ]
|
62
|
+
# elements = find_type(:li)
|
63
|
+
#
|
64
|
+
# If +nested+ is set to false, this returns only top level elements of a
|
65
|
+
# given type.
|
66
|
+
|
67
|
+
def find_type(type, nested=true)
|
68
|
+
find_type_elements(type, nested).map { |e| e.options }
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Find all elements of a given type, returning a list of the element
|
73
|
+
# objects themselves.
|
74
|
+
#
|
75
|
+
# Instead of a single type, a list of types can be provided instead to
|
76
|
+
# find all types.
|
77
|
+
#
|
78
|
+
# If +nested+ is set to false, this returns only top level elements of a
|
79
|
+
# given type.
|
80
|
+
|
81
|
+
def find_type_elements(type, nested=true, elements=@elements)
|
82
|
+
results = []
|
83
|
+
if type.class == Symbol
|
84
|
+
type = [type]
|
85
|
+
end
|
86
|
+
elements.each do |e|
|
87
|
+
results.push(e) if type.include?(e.type)
|
88
|
+
if nested and not e.children.empty?
|
89
|
+
results.concat(find_type_elements(type, nested, e.children))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
results
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Returns the line number a given element is located on in the source
|
97
|
+
# file. You can pass in either an element object or an options hash here.
|
98
|
+
|
99
|
+
def element_linenumber(element)
|
100
|
+
element = element.options if element.is_a?(Kramdown::Element)
|
101
|
+
element[:location]
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Returns the actual source line for a given element. You can pass in an
|
106
|
+
# element object or an options hash here. This is useful if you need to
|
107
|
+
# examine the source line directly for your rule to make use of
|
108
|
+
# information that isn't present in the parsed document.
|
109
|
+
|
110
|
+
def element_line(element)
|
111
|
+
@lines[element_linenumber(element) - 1]
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Returns a list of line numbers for all elements passed in. You can pass
|
116
|
+
# in a list of element objects or a list of options hashes here.
|
117
|
+
|
118
|
+
def element_linenumbers(elements)
|
119
|
+
elements.map { |e| element_linenumber(e) }
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Returns the actual source lines for a list of elements. You can pass in
|
124
|
+
# a list of elements objects or a list of options hashes here.
|
125
|
+
|
126
|
+
def element_lines(elements)
|
127
|
+
elements.map { |e| element_line(e) }
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Returns the header 'style' - :atx (hashes at the beginning), :atx_closed
|
132
|
+
# (atx header style, but with hashes at the end of the line also), :setext
|
133
|
+
# (underlined). You can pass in the element object or an options hash
|
134
|
+
# here.
|
135
|
+
|
136
|
+
def header_style(header)
|
137
|
+
if header.type != :header
|
138
|
+
raise "header_style called with non-header element"
|
139
|
+
end
|
140
|
+
line = element_line(header)
|
141
|
+
if line.start_with?("#")
|
142
|
+
if line.strip.end_with?("#")
|
143
|
+
:atx_closed
|
144
|
+
else
|
145
|
+
:atx
|
146
|
+
end
|
147
|
+
else
|
148
|
+
:setext
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
##
|
153
|
+
# Returns the list style for a list: :asterisk, :plus, :dash, :ordered or
|
154
|
+
# :ordered_paren depending on which symbol is used to denote the list
|
155
|
+
# item. You can pass in either the element itself or an options hash here.
|
156
|
+
|
157
|
+
def list_style(item)
|
158
|
+
if item.type != :li
|
159
|
+
raise "list_style called with non-list element"
|
160
|
+
end
|
161
|
+
line = element_line(item).strip
|
162
|
+
if line.start_with?("*")
|
163
|
+
:asterisk
|
164
|
+
elsif line.start_with?("+")
|
165
|
+
:plus
|
166
|
+
elsif line.start_with?("-")
|
167
|
+
:dash
|
168
|
+
elsif line.match("[0-9]+\.")
|
169
|
+
:ordered
|
170
|
+
elsif line.match("[0-9]+\)")
|
171
|
+
:ordered_paren
|
172
|
+
else
|
173
|
+
:unknown
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Returns how much a given line is indented. Hard tabs are treated as an
|
179
|
+
# indent of 8 spaces. You need to pass in the raw string here.
|
180
|
+
|
181
|
+
def indent_for(line)
|
182
|
+
return line.match(/^\s*/)[0].gsub("\t", " " * 8).length
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Returns line numbers for lines that match the given regular expression
|
187
|
+
|
188
|
+
def matching_lines(re)
|
189
|
+
@lines.each_with_index.select{|text, linenum| re.match(text)}.map{
|
190
|
+
|i| i[1]+1}
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# Returns line numbers for lines that match the given regular expression.
|
195
|
+
# Only considers text inside of 'text' elements (i.e. regular markdown
|
196
|
+
# text and not code/links or other elements).
|
197
|
+
def matching_text_element_lines(re)
|
198
|
+
matches = []
|
199
|
+
find_type_elements(:text).each do |e|
|
200
|
+
first_line = e.options[:location]
|
201
|
+
lines = e.value.split("\n")
|
202
|
+
lines.each_with_index do |l, i|
|
203
|
+
matches << first_line + i if re.match(l)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
matches
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# Extracts the text from an element whose children consist of text
|
211
|
+
# elements and other things
|
212
|
+
|
213
|
+
def extract_text(element, prefix="")
|
214
|
+
quotes = {
|
215
|
+
:rdquo => '"',
|
216
|
+
:ldquo => '"',
|
217
|
+
:lsquo => "'",
|
218
|
+
:rsquo => "'"
|
219
|
+
}
|
220
|
+
# If anything goes amiss here, e.g. unknown type, then nil will be
|
221
|
+
# returned and we'll just not catch that part of the text, which seems
|
222
|
+
# like a sensible failure mode.
|
223
|
+
lines = element.children.map { |e|
|
224
|
+
if e.type == :text
|
225
|
+
e.value
|
226
|
+
elsif [:strong, :em, :p].include?(e.type)
|
227
|
+
extract_text(e, prefix).join("\n")
|
228
|
+
elsif e.type == :smart_quote
|
229
|
+
quotes[e.value]
|
230
|
+
end
|
231
|
+
}.join.split("\n")
|
232
|
+
# Text blocks have whitespace stripped, so we need to add it back in at
|
233
|
+
# the beginning. Because this might be in something like a blockquote,
|
234
|
+
# we optionally strip off a prefix given to the function.
|
235
|
+
lines[0] = element_line(element).sub(prefix, "")
|
236
|
+
lines
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
##
|
242
|
+
# Adds a 'level' option to all elements to show how nested they are
|
243
|
+
|
244
|
+
def add_levels(elements, level=1)
|
245
|
+
elements.each do |e|
|
246
|
+
e.options[:element_level] = level
|
247
|
+
add_levels(e.children, level+1)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Modified version of the kramdown parser to add in features/changes
|
2
|
+
# appropriate for markdownlint, but which don't make sense to try to put
|
3
|
+
# upstream.
|
4
|
+
require 'kramdown/parser/gfm'
|
5
|
+
|
6
|
+
module Kramdown
|
7
|
+
module Parser
|
8
|
+
class MarkdownLint < Kramdown::Parser::Kramdown
|
9
|
+
|
10
|
+
def initialize(source, options)
|
11
|
+
super
|
12
|
+
i = @block_parsers.index(:codeblock_fenced)
|
13
|
+
@block_parsers.delete(:codeblock_fenced)
|
14
|
+
@block_parsers.insert(i, :codeblock_fenced_gfm)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Add location information to text elements
|
18
|
+
def add_text(text, tree = @tree, type = @text_type)
|
19
|
+
super
|
20
|
+
if tree.children.last
|
21
|
+
tree.children.last.options[:location] = @src.current_line_number
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Regular kramdown parser, but with GFM style fenced code blocks
|
26
|
+
FENCED_CODEBLOCK_MATCH = Kramdown::Parser::GFM::FENCED_CODEBLOCK_MATCH
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|