mdl 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/mdl +2 -2
- data/lib/mdl.rb +41 -32
- data/lib/mdl/cli.rb +92 -90
- data/lib/mdl/config.rb +2 -1
- data/lib/mdl/doc.rb +45 -54
- data/lib/mdl/kramdown_parser.rb +1 -1
- data/lib/mdl/rules.rb +206 -167
- data/lib/mdl/ruleset.rb +11 -10
- data/lib/mdl/style.rb +18 -16
- data/lib/mdl/styles/cirosantilli.rb +2 -2
- data/lib/mdl/styles/default.rb +1 -1
- data/lib/mdl/version.rb +1 -1
- data/mdl.gemspec +20 -18
- metadata +63 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 870d0bc411951c203fcf0e352e6e903e4f7008b17543259dea919ea5780042fc
|
4
|
+
data.tar.gz: f796b7b245267e16f5e7e4d5be79b030b3f6775987f1df6838c526c4a7f7b58f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85bfc0cf05053e6bfc9aaa1c8f62c2fc1f99ba8f9b450c262fc1716de529e934e2cac5cb23c77c7a6c49ec33c7bddcc8c9e3cf17964c3b426695dafb4dbcd51f
|
7
|
+
data.tar.gz: '013659e46c8ec5ce00c4d8a096853badf15da2a0396cd33119f84a603eb525e62a4a44fadc04fc24c282c8a55cda90c811146b39a17db2eb28a744209ace5b15'
|
data/bin/mdl
CHANGED
@@ -3,8 +3,8 @@ begin
|
|
3
3
|
require 'mdl'
|
4
4
|
rescue LoadError
|
5
5
|
# For running in development without bundler
|
6
|
-
|
6
|
+
$LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
|
7
7
|
require 'mdl'
|
8
8
|
end
|
9
9
|
|
10
|
-
MarkdownLint
|
10
|
+
MarkdownLint.run
|
data/lib/mdl.rb
CHANGED
@@ -7,50 +7,55 @@ require_relative 'mdl/style'
|
|
7
7
|
require_relative 'mdl/version'
|
8
8
|
|
9
9
|
require 'kramdown'
|
10
|
+
require 'mixlib/shellout'
|
10
11
|
|
12
|
+
# Primary MDL container
|
11
13
|
module MarkdownLint
|
12
|
-
def self.run(argv=ARGV)
|
14
|
+
def self.run(argv = ARGV)
|
13
15
|
cli = MarkdownLint::CLI.new
|
14
16
|
cli.run(argv)
|
15
17
|
ruleset = RuleSet.new
|
16
|
-
unless Config[:skip_default_ruleset]
|
17
|
-
|
18
|
-
|
19
|
-
unless Config[:rulesets].nil?
|
20
|
-
Config[:rulesets].each do |r|
|
21
|
-
ruleset.load(r)
|
22
|
-
end
|
18
|
+
ruleset.load_default unless Config[:skip_default_ruleset]
|
19
|
+
Config[:rulesets]&.each do |r|
|
20
|
+
ruleset.load(r)
|
23
21
|
end
|
24
22
|
rules = ruleset.rules
|
25
23
|
Style.load(Config[:style], rules)
|
26
24
|
# Rule option filter
|
27
25
|
if Config[:rules]
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
26
|
+
unless Config[:rules][:include].empty?
|
27
|
+
rules.select! do |r, v|
|
28
|
+
Config[:rules][:include].include?(r) or
|
29
|
+
!(Config[:rules][:include] & v.aliases).empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
unless Config[:rules][:exclude].empty?
|
33
|
+
rules.select! do |r, v|
|
34
|
+
!Config[:rules][:exclude].include?(r) and
|
35
|
+
(Config[:rules][:exclude] & v.aliases).empty?
|
36
|
+
end
|
37
|
+
end
|
34
38
|
end
|
35
39
|
# Tag option filter
|
36
40
|
if Config[:tags]
|
37
|
-
rules.
|
41
|
+
rules.reject! { |_r, v| (v.tags & Config[:tags][:include]).empty? } \
|
38
42
|
unless Config[:tags][:include].empty?
|
39
|
-
rules.select! {|
|
43
|
+
rules.select! { |_r, v| (v.tags & Config[:tags][:exclude]).empty? } \
|
40
44
|
unless Config[:tags][:exclude].empty?
|
41
45
|
end
|
42
46
|
|
43
47
|
if Config[:list_rules]
|
44
|
-
puts
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
puts 'Enabled rules:'
|
49
|
+
rules.each do |id, rule|
|
50
|
+
if Config[:verbose]
|
51
|
+
puts "#{id} (#{rule.aliases.join(', ')}) [#{rule.tags.join(', ')}] " +
|
52
|
+
"- #{rule.description}"
|
53
|
+
elsif Config[:show_aliases]
|
54
|
+
puts "#{rule.aliases.first || id} - #{rule.description}"
|
55
|
+
else
|
56
|
+
puts "#{id} - #{rule.description}"
|
53
57
|
end
|
58
|
+
end
|
54
59
|
exit 0
|
55
60
|
end
|
56
61
|
|
@@ -59,7 +64,9 @@ module MarkdownLint
|
|
59
64
|
if Dir.exist?(filename)
|
60
65
|
if Config[:git_recurse]
|
61
66
|
Dir.chdir(filename) do
|
62
|
-
cli.cli_arguments[i] =
|
67
|
+
cli.cli_arguments[i] =
|
68
|
+
Mixlib::ShellOut.new("git ls-files '*.md' '*.markdown'")
|
69
|
+
.run_command.stdout.lines
|
63
70
|
end
|
64
71
|
else
|
65
72
|
cli.cli_arguments[i] = Dir["#{filename}/**/*.{md,markdown}"]
|
@@ -73,9 +80,9 @@ module MarkdownLint
|
|
73
80
|
cli.cli_arguments.each do |filename|
|
74
81
|
puts "Checking #{filename}..." if Config[:verbose]
|
75
82
|
doc = Doc.new_from_file(filename, Config[:ignore_front_matter])
|
76
|
-
filename = '(stdin)' if filename ==
|
83
|
+
filename = '(stdin)' if filename == '-'
|
77
84
|
if Config[:show_kramdown_warnings]
|
78
|
-
status = 2
|
85
|
+
status = 2 unless doc.parsed.warnings.empty?
|
79
86
|
doc.parsed.warnings.each do |w|
|
80
87
|
puts "#{filename}: Kramdown Warning: #{w}"
|
81
88
|
end
|
@@ -83,7 +90,8 @@ module MarkdownLint
|
|
83
90
|
rules.sort.each do |id, rule|
|
84
91
|
puts "Processing rule #{id}" if Config[:verbose]
|
85
92
|
error_lines = rule.check.call(doc)
|
86
|
-
next if error_lines.nil?
|
93
|
+
next if error_lines.nil? || error_lines.empty?
|
94
|
+
|
87
95
|
status = 1
|
88
96
|
error_lines.each do |line|
|
89
97
|
line += doc.offset # Correct line numbers for any yaml front matter
|
@@ -96,7 +104,8 @@ module MarkdownLint
|
|
96
104
|
'description' => rule.description,
|
97
105
|
}
|
98
106
|
elsif Config[:show_aliases]
|
99
|
-
puts "#{filename}:#{line}: #{rule.aliases.first || id}
|
107
|
+
puts "#{filename}:#{line}: #{rule.aliases.first || id} " +
|
108
|
+
rule.description.to_s
|
100
109
|
else
|
101
110
|
puts "#{filename}:#{line}: #{id} #{rule.description}"
|
102
111
|
end
|
@@ -108,8 +117,8 @@ module MarkdownLint
|
|
108
117
|
require 'json'
|
109
118
|
puts JSON.generate(results)
|
110
119
|
elsif status != 0
|
111
|
-
puts "\nA detailed description of the rules is available at "
|
112
|
-
|
120
|
+
puts "\nA detailed description of the rules is available at " +
|
121
|
+
'https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md'
|
113
122
|
end
|
114
123
|
exit status
|
115
124
|
end
|
data/lib/mdl/cli.rb
CHANGED
@@ -2,124 +2,125 @@ require 'mixlib/cli'
|
|
2
2
|
require 'pathname'
|
3
3
|
|
4
4
|
module MarkdownLint
|
5
|
+
# Our Mixlib::CLI class
|
5
6
|
class CLI
|
6
7
|
include Mixlib::CLI
|
7
8
|
|
8
|
-
CONFIG_FILE = '.mdlrc'
|
9
|
+
CONFIG_FILE = '.mdlrc'.freeze
|
9
10
|
|
10
|
-
banner "Usage: #{File.basename($
|
11
|
+
banner "Usage: #{File.basename($PROGRAM_NAME)} [options] [FILE.md|DIR ...]"
|
11
12
|
|
12
13
|
option :show_aliases,
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
:short => '-a',
|
15
|
+
:long => '--[no-]show-aliases',
|
16
|
+
:description =>
|
17
|
+
'Show rule alias instead of rule ID when viewing rules',
|
18
|
+
:boolean => true
|
17
19
|
|
18
20
|
option :config_file,
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
:short => '-c',
|
22
|
+
:long => '--config FILE',
|
23
|
+
:description => 'The configuration file to use',
|
24
|
+
:default => CONFIG_FILE.to_s
|
23
25
|
|
24
26
|
option :verbose,
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
:short => '-v',
|
28
|
+
:long => '--[no-]verbose',
|
29
|
+
:description => 'Increase verbosity',
|
30
|
+
:boolean => true
|
29
31
|
|
30
32
|
option :ignore_front_matter,
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
:short => '-i',
|
34
|
+
:long => '--[no-]ignore-front-matter',
|
35
|
+
:boolean => true,
|
36
|
+
:description => 'Ignore YAML front matter'
|
35
37
|
|
36
38
|
option :show_kramdown_warnings,
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
:short => '-w',
|
40
|
+
:long => '--[no-]warnings',
|
41
|
+
:description => 'Show kramdown warnings',
|
42
|
+
:boolean => true
|
41
43
|
|
42
44
|
option :tags,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
:short => '-t',
|
46
|
+
:long => '--tags TAG1,TAG2',
|
47
|
+
:description => 'Only process rules with these tags',
|
48
|
+
:proc => proc { |v| toggle_list(v, true) }
|
47
49
|
|
48
50
|
option :rules,
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
:short => '-r',
|
52
|
+
:long => '--rules RULE1,RULE2',
|
53
|
+
:description => 'Only process these rules',
|
54
|
+
:proc => proc { |v| toggle_list(v) }
|
53
55
|
|
54
56
|
option :style,
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
:short => '-s',
|
58
|
+
:long => '--style STYLE',
|
59
|
+
:description => 'Load the given style'
|
58
60
|
|
59
61
|
option :list_rules,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
62
|
+
:short => '-l',
|
63
|
+
:long => '--list-rules',
|
64
|
+
:boolean => true,
|
65
|
+
:description => "Don't process any files, just list enabled rules"
|
64
66
|
|
65
67
|
option :git_recurse,
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
:short => '-g',
|
69
|
+
:long => '--git-recurse',
|
70
|
+
:boolean => true,
|
71
|
+
:description =>
|
72
|
+
'Only process files known to git when given a directory'
|
70
73
|
|
71
74
|
option :rulesets,
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
:short => '-u',
|
76
|
+
:long => '--rulesets RULESET1,RULESET2',
|
77
|
+
:proc => proc { |v| v.split(',') },
|
78
|
+
:description => 'Specify additional ruleset files to load'
|
76
79
|
|
77
80
|
option :skip_default_ruleset,
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
81
|
+
:short => '-d',
|
82
|
+
:long => '--skip-default-ruleset',
|
83
|
+
:boolean => true,
|
84
|
+
:description => "Don't load the default markdownlint ruleset"
|
82
85
|
|
83
86
|
option :help,
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
:on => :tail,
|
88
|
+
:short => '-h',
|
89
|
+
:long => '--help',
|
90
|
+
:description => 'Show this message',
|
91
|
+
:boolean => true,
|
92
|
+
:show_options => true,
|
93
|
+
:exit => 0
|
91
94
|
|
92
95
|
option :version,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
96
|
+
:on => :tail,
|
97
|
+
:short => '-V',
|
98
|
+
:long => '--version',
|
99
|
+
:description => 'Show version',
|
100
|
+
:boolean => true,
|
101
|
+
:proc => proc { puts MarkdownLint::VERSION },
|
102
|
+
:exit => 0
|
100
103
|
|
101
104
|
option :json,
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
105
|
+
:short => '-j',
|
106
|
+
:long => '--json',
|
107
|
+
:description => 'JSON output',
|
108
|
+
:boolean => true
|
106
109
|
|
107
|
-
def run(argv=ARGV)
|
110
|
+
def run(argv = ARGV)
|
108
111
|
parse_options(argv)
|
109
112
|
|
110
113
|
# Load the config file if it's present
|
111
114
|
filename = CLI.probe_config_file(config[:config_file])
|
112
115
|
|
113
116
|
# Only fall back to ~/.mdlrc if we are using the default value for -c
|
114
|
-
if filename.nil?
|
117
|
+
if filename.nil? && (config[:config_file] == CONFIG_FILE)
|
115
118
|
filename = File.expand_path("~/#{CONFIG_FILE}")
|
116
119
|
end
|
117
120
|
|
118
|
-
if
|
121
|
+
if !filename.nil? && File.exist?(filename)
|
119
122
|
MarkdownLint::Config.from_file(filename.to_s)
|
120
|
-
if config[:verbose]
|
121
|
-
puts "Loaded config from #{filename}"
|
122
|
-
end
|
123
|
+
puts "Loaded config from #{filename}" if config[:verbose]
|
123
124
|
end
|
124
125
|
|
125
126
|
# Put values in the config file
|
@@ -128,30 +129,31 @@ module MarkdownLint
|
|
128
129
|
# Set the correct format for any rules/tags configuration loaded from
|
129
130
|
# the config file. Ideally this would probably be done as part of the
|
130
131
|
# config class itself rather than here.
|
131
|
-
MarkdownLint::Config[:rules]
|
132
|
-
MarkdownLint::Config[:rules]
|
133
|
-
|
134
|
-
|
135
|
-
|
132
|
+
unless MarkdownLint::Config[:rules].nil?
|
133
|
+
MarkdownLint::Config[:rules] = CLI.toggle_list(
|
134
|
+
MarkdownLint::Config[:rules],
|
135
|
+
)
|
136
|
+
end
|
137
|
+
unless MarkdownLint::Config[:tags].nil?
|
138
|
+
MarkdownLint::Config[:tags] = CLI.toggle_list(
|
139
|
+
MarkdownLint::Config[:tags], true
|
140
|
+
)
|
141
|
+
end
|
136
142
|
|
137
143
|
# Read from stdin if we didn't provide a filename
|
138
|
-
if cli_arguments.empty?
|
139
|
-
cli_arguments << "-"
|
140
|
-
end
|
144
|
+
cli_arguments << '-' if cli_arguments.empty? && !config[:list_rules]
|
141
145
|
end
|
142
146
|
|
143
|
-
def self.toggle_list(parts, to_sym=false)
|
144
|
-
if parts.class == String
|
145
|
-
parts = parts.split(',')
|
146
|
-
end
|
147
|
+
def self.toggle_list(parts, to_sym = false)
|
148
|
+
parts = parts.split(',') if parts.class == String
|
147
149
|
if parts.class == Array
|
148
|
-
inc = parts.
|
149
|
-
exc = parts.select{|p| p.start_with?('~')}.map{|p| p[1..-1]}
|
150
|
+
inc = parts.reject { |p| p.start_with?('~') }
|
151
|
+
exc = parts.select { |p| p.start_with?('~') }.map { |p| p[1..-1] }
|
150
152
|
if to_sym
|
151
|
-
inc.map!
|
152
|
-
exc.map!
|
153
|
+
inc.map!(&:to_sym)
|
154
|
+
exc.map!(&:to_sym)
|
153
155
|
end
|
154
|
-
{:include => inc, :exclude => exc}
|
156
|
+
{ :include => inc, :exclude => exc }
|
155
157
|
else
|
156
158
|
# We already converted the string into a list of include/exclude
|
157
159
|
# pairs, so just return as is
|
data/lib/mdl/config.rb
CHANGED
data/lib/mdl/doc.rb
CHANGED
@@ -4,7 +4,6 @@ require_relative 'kramdown_parser'
|
|
4
4
|
module MarkdownLint
|
5
5
|
##
|
6
6
|
# Representation of the markdown document passed to rule checks
|
7
|
-
|
8
7
|
class Doc
|
9
8
|
##
|
10
9
|
# A list of raw markdown source lines. Note that the list is 0-indexed,
|
@@ -12,32 +11,26 @@ module MarkdownLint
|
|
12
11
|
# subtract 1 from a line number to get the correct line. The element_line*
|
13
12
|
# methods take care of this for you.
|
14
13
|
|
15
|
-
attr_reader :lines
|
14
|
+
attr_reader :lines, :parsed, :elements, :offset
|
16
15
|
|
17
16
|
##
|
18
17
|
# A Kramdown::Document object containing the parsed markdown document.
|
19
18
|
|
20
|
-
attr_reader :parsed
|
21
|
-
|
22
19
|
##
|
23
20
|
# A list of top level Kramdown::Element objects from the parsed document.
|
24
21
|
|
25
|
-
attr_reader :elements
|
26
|
-
|
27
22
|
##
|
28
23
|
# The line number offset which is greater than zero when the
|
29
24
|
# markdown file contains YAML front matter that should be ignored.
|
30
25
|
|
31
|
-
attr_reader :offset
|
32
|
-
|
33
26
|
##
|
34
27
|
# Create a new document given a string containing the markdown source
|
35
28
|
|
36
29
|
def initialize(text, ignore_front_matter = false)
|
37
30
|
regex = /^---\n(.*?)---\n\n?/m
|
38
|
-
if ignore_front_matter
|
31
|
+
if ignore_front_matter && regex.match(text)
|
39
32
|
@offset = regex.match(text).to_s.split("\n").length
|
40
|
-
text.sub!(regex,'')
|
33
|
+
text.sub!(regex, '')
|
41
34
|
else
|
42
35
|
@offset = 0
|
43
36
|
end
|
@@ -51,10 +44,10 @@ module MarkdownLint
|
|
51
44
|
# Alternate 'constructor' passing in a filename
|
52
45
|
|
53
46
|
def self.new_from_file(filename, ignore_front_matter = false)
|
54
|
-
if filename ==
|
55
|
-
|
47
|
+
if filename == '-'
|
48
|
+
new($stdin.read, ignore_front_matter)
|
56
49
|
else
|
57
|
-
|
50
|
+
new(File.read(filename, :encoding => 'UTF-8'), ignore_front_matter)
|
58
51
|
end
|
59
52
|
end
|
60
53
|
|
@@ -69,8 +62,8 @@ module MarkdownLint
|
|
69
62
|
# If +nested+ is set to false, this returns only top level elements of a
|
70
63
|
# given type.
|
71
64
|
|
72
|
-
def find_type(type, nested=true)
|
73
|
-
find_type_elements(type, nested).map
|
65
|
+
def find_type(type, nested = true)
|
66
|
+
find_type_elements(type, nested).map(&:options)
|
74
67
|
end
|
75
68
|
|
76
69
|
##
|
@@ -83,14 +76,12 @@ module MarkdownLint
|
|
83
76
|
# If +nested+ is set to false, this returns only top level elements of a
|
84
77
|
# given type.
|
85
78
|
|
86
|
-
def find_type_elements(type, nested=true, elements
|
79
|
+
def find_type_elements(type, nested = true, elements = @elements)
|
87
80
|
results = []
|
88
|
-
if type.class == Symbol
|
89
|
-
type = [type]
|
90
|
-
end
|
81
|
+
type = [type] if type.class == Symbol
|
91
82
|
elements.each do |e|
|
92
83
|
results.push(e) if type.include?(e.type)
|
93
|
-
if nested
|
84
|
+
if nested && !e.children.empty?
|
94
85
|
results.concat(find_type_elements(type, nested, e.children))
|
95
86
|
end
|
96
87
|
end
|
@@ -107,19 +98,19 @@ module MarkdownLint
|
|
107
98
|
# Unlike find_type_elements, this method will always search for nested
|
108
99
|
# elements, and skip the element types given to nested_except.
|
109
100
|
|
110
|
-
def find_type_elements_except(
|
101
|
+
def find_type_elements_except(
|
102
|
+
type, nested_except = [], elements = @elements
|
103
|
+
)
|
111
104
|
results = []
|
112
|
-
if type.class == Symbol
|
113
|
-
|
114
|
-
end
|
115
|
-
if nested_except.class == Symbol
|
116
|
-
nested_except = [nested_except]
|
117
|
-
end
|
105
|
+
type = [type] if type.class == Symbol
|
106
|
+
nested_except = [nested_except] if nested_except.class == Symbol
|
118
107
|
elements.each do |e|
|
119
108
|
results.push(e) if type.include?(e.type)
|
120
|
-
|
121
|
-
|
122
|
-
|
109
|
+
next if nested_except.include?(e.type) || e.children.empty?
|
110
|
+
|
111
|
+
results.concat(
|
112
|
+
find_type_elements_except(type, nested_except, e.children),
|
113
|
+
)
|
123
114
|
end
|
124
115
|
results
|
125
116
|
end
|
@@ -167,11 +158,12 @@ module MarkdownLint
|
|
167
158
|
|
168
159
|
def header_style(header)
|
169
160
|
if header.type != :header
|
170
|
-
raise
|
161
|
+
raise 'header_style called with non-header element'
|
171
162
|
end
|
163
|
+
|
172
164
|
line = element_line(header)
|
173
|
-
if line.start_with?(
|
174
|
-
if line.strip.end_with?(
|
165
|
+
if line.start_with?('#')
|
166
|
+
if line.strip.end_with?('#')
|
175
167
|
:atx_closed
|
176
168
|
else
|
177
169
|
:atx
|
@@ -187,10 +179,9 @@ module MarkdownLint
|
|
187
179
|
# item. You can pass in either the element itself or an options hash here.
|
188
180
|
|
189
181
|
def list_style(item)
|
190
|
-
if item.type != :li
|
191
|
-
|
192
|
-
|
193
|
-
line = element_line(item).strip.gsub(/^>\s+/,'')
|
182
|
+
raise 'list_style called with non-list element' if item.type != :li
|
183
|
+
|
184
|
+
line = element_line(item).strip.gsub(/^>\s+/, '')
|
194
185
|
if line.start_with?('*')
|
195
186
|
:asterisk
|
196
187
|
elsif line.start_with?('+')
|
@@ -211,22 +202,24 @@ module MarkdownLint
|
|
211
202
|
# indent of 8 spaces. You need to pass in the raw string here.
|
212
203
|
|
213
204
|
def indent_for(line)
|
214
|
-
|
205
|
+
line.match(/^\s*/)[0].gsub("\t", ' ' * 8).length
|
215
206
|
end
|
216
207
|
|
217
208
|
##
|
218
209
|
# Returns line numbers for lines that match the given regular expression
|
219
210
|
|
220
|
-
def matching_lines(
|
221
|
-
@lines.each_with_index.select{|text,
|
222
|
-
|
211
|
+
def matching_lines(regex)
|
212
|
+
@lines.each_with_index.select { |text, _linenum| regex.match(text) }
|
213
|
+
.map do |i|
|
214
|
+
i[1] + 1
|
215
|
+
end
|
223
216
|
end
|
224
217
|
|
225
218
|
##
|
226
219
|
# Returns line numbers for lines that match the given regular expression.
|
227
220
|
# Only considers text inside of 'text' elements (i.e. regular markdown
|
228
221
|
# text and not code/links or other elements).
|
229
|
-
def matching_text_element_lines(
|
222
|
+
def matching_text_element_lines(regex, exclude_nested = [:a])
|
230
223
|
matches = []
|
231
224
|
find_type_elements_except(:text, exclude_nested).each do |e|
|
232
225
|
first_line = e.options[:location]
|
@@ -234,9 +227,10 @@ module MarkdownLint
|
|
234
227
|
# the current element. It's better to just not match in these cases
|
235
228
|
# rather than crash.
|
236
229
|
next if first_line.nil?
|
230
|
+
|
237
231
|
lines = e.value.split("\n")
|
238
232
|
lines.each_with_index do |l, i|
|
239
|
-
matches << first_line + i if
|
233
|
+
matches << first_line + i if regex.match(l)
|
240
234
|
end
|
241
235
|
end
|
242
236
|
matches
|
@@ -246,31 +240,29 @@ module MarkdownLint
|
|
246
240
|
# Extracts the text from an element whose children consist of text
|
247
241
|
# elements and other things
|
248
242
|
|
249
|
-
def extract_text(element, prefix=
|
243
|
+
def extract_text(element, prefix = '', restore_whitespace = true)
|
250
244
|
quotes = {
|
251
245
|
:rdquo => '"',
|
252
246
|
:ldquo => '"',
|
253
247
|
:lsquo => "'",
|
254
|
-
:rsquo => "'"
|
248
|
+
:rsquo => "'",
|
255
249
|
}
|
256
250
|
# If anything goes amiss here, e.g. unknown type, then nil will be
|
257
251
|
# returned and we'll just not catch that part of the text, which seems
|
258
252
|
# like a sensible failure mode.
|
259
|
-
lines = element.children.map
|
253
|
+
lines = element.children.map do |e|
|
260
254
|
if e.type == :text
|
261
255
|
e.value
|
262
|
-
elsif
|
256
|
+
elsif %i{strong em p codespan}.include?(e.type)
|
263
257
|
extract_text(e, prefix, restore_whitespace).join("\n")
|
264
258
|
elsif e.type == :smart_quote
|
265
259
|
quotes[e.value]
|
266
260
|
end
|
267
|
-
|
261
|
+
end.join.split("\n")
|
268
262
|
# Text blocks have whitespace stripped, so we need to add it back in at
|
269
263
|
# the beginning. Because this might be in something like a blockquote,
|
270
264
|
# we optionally strip off a prefix given to the function.
|
271
|
-
if restore_whitespace
|
272
|
-
lines[0] = element_line(element).sub(prefix, "")
|
273
|
-
end
|
265
|
+
lines[0] = element_line(element).sub(prefix, '') if restore_whitespace
|
274
266
|
lines
|
275
267
|
end
|
276
268
|
|
@@ -280,13 +272,12 @@ module MarkdownLint
|
|
280
272
|
# Adds a 'level' and 'parent' option to all elements to show how nested they
|
281
273
|
# are
|
282
274
|
|
283
|
-
def add_annotations(elements, level=1, parent=nil)
|
275
|
+
def add_annotations(elements, level = 1, parent = nil)
|
284
276
|
elements.each do |e|
|
285
277
|
e.options[:element_level] = level
|
286
278
|
e.options[:parent] = parent
|
287
|
-
add_annotations(e.children, level+1, e)
|
279
|
+
add_annotations(e.children, level + 1, e)
|
288
280
|
end
|
289
281
|
end
|
290
|
-
|
291
282
|
end
|
292
283
|
end
|