kramdown-parser-gfm 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3856f442e4f25e0cd69a7a6b0302634673ada7b16592abee13d5461150129ba
4
- data.tar.gz: 8ab357991ab37fa96ce81d36c1cd9ac1a13e2257d5570be7b309651890486e8f
3
+ metadata.gz: 12b3b498708f289068f922756799bda525f86c09e90b7ce711543b979aab8f6b
4
+ data.tar.gz: 9af4b649b95b0a3edc022936d8d8b373aff33c44ef4e2da0fd77864d32ada944
5
5
  SHA512:
6
- metadata.gz: be89c6b85f0db3cde488514d56479180d73761af392fdce84b8edd72359566fcb0f53d3b6c33a1e6d3e92b19e8bf72537fed04bd1ea29f079487557cffc9b940
7
- data.tar.gz: 19c2d1c58f2c76a4d36466bbad27b6a400b25ac1486bba33351a6be1a4d06df829d8285e27705831c9ff32a405064ef64ca2e388caed519c32709f86260c3099
6
+ metadata.gz: bbe45d4a04461082c451f6bb3de41a6b24f8682b5e0b1111c1e4bd61aefe7b9408adad0d910441ebc0b25f8e7c6bb7eb14096a5c4531e5492f39117755de935e
7
+ data.tar.gz: 892b44df78e01fd92858615fd0c79dad35093f05f0f929a1c00bc2d4fd1743570cc7773858c2b883d3ad3f1802c8034dbe74eac47b55ccfcb37c28bdd4a014ad
@@ -1,3 +1,4 @@
1
- Count Name
1
+ Count Name
2
2
  ======= ====
3
- 4 Thomas Leitner <t_leitner@gmx.at>
3
+ 15 Ashwin Maroli <ashmaroli@gmail.com>
4
+ 5 Thomas Leitner <t_leitner@gmx.at>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.1.0
@@ -7,4 +7,4 @@
7
7
  #++
8
8
  #
9
9
 
10
- require 'kramdown/parser/gfm'
10
+ require_relative 'kramdown/parser/gfm'
@@ -10,77 +10,32 @@
10
10
  require 'kramdown/options'
11
11
  require 'kramdown/parser/kramdown'
12
12
 
13
- module Kramdown
14
- module Options
15
-
16
- define(:hard_wrap, Boolean, true, <<EOF)
17
- Interprets line breaks literally
18
-
19
- Insert HTML `<br />` tags inside paragraphs where the original Markdown
20
- document had newlines (by default, Markdown ignores these newlines).
21
-
22
- Default: true
23
- Used by: GFM parser
24
- EOF
25
-
26
- define(:gfm_quirks, Object, [:paragraph_end], <<EOF) do |val|
27
- Enables a set of GFM specific quirks
28
-
29
- The way how GFM is transformed on Github often differs from the way
30
- kramdown does things. Many of these differences are negligible but
31
- others are not.
32
-
33
- This option allows one to enable/disable certain GFM quirks, i.e. ways
34
- in which GFM parsing differs from kramdown parsing.
35
-
36
- The value has to be a list of quirk names that should be enabled,
37
- separated by commas. Possible names are:
38
-
39
- * paragraph_end
40
-
41
- Disables the kramdown restriction that at least one blank line has to
42
- be used after a paragraph before a new block element can be started.
43
-
44
- Note that if this quirk is used, lazy line wrapping does not fully
45
- work anymore!
46
-
47
- * no_auto_typographic
48
-
49
- Disables automatic conversion of some characters into their
50
- corresponding typographic symbols (like `--` to em-dash etc).
51
- This helps to achieve results closer to what GitHub Flavored
52
- Markdown produces.
53
-
54
- Default: paragraph_end
55
- Used by: GFM parser
56
- EOF
57
- val = simple_array_validator(val, :gfm_quirks)
58
- val.map! {|v| str_to_sym(v.to_s)}
59
- val
60
- end
61
-
62
- end
13
+ require_relative 'gfm/options'
63
14
 
15
+ module Kramdown
64
16
  module Parser
65
17
 
66
18
  # This class provides a parser implementation for the GFM dialect of Markdown.
67
19
  class GFM < Kramdown::Parser::Kramdown
68
20
 
69
- VERSION = '1.0.1'
21
+ VERSION = '1.1.0'
22
+
23
+ attr_reader :paragraph_end
70
24
 
71
25
  def initialize(source, options)
72
26
  super
73
27
  @options[:auto_id_stripping] = true
74
28
  @id_counter = Hash.new(-1)
75
29
 
76
- @span_parsers.delete(:line_break) if @options[:hard_wrap]
30
+ @span_parsers.delete(:line_break) if @options[:hard_wrap]
77
31
  @span_parsers.delete(:typographic_syms) if @options[:gfm_quirks].include?(:no_auto_typographic)
32
+
78
33
  if @options[:gfm_quirks].include?(:paragraph_end)
79
34
  atx_header_parser = :atx_header_gfm_quirk
80
- @paragraph_end = self.class::PARAGRAPH_END_GFM
35
+ @paragraph_end = self.class::PARAGRAPH_END_GFM
81
36
  else
82
37
  atx_header_parser = :atx_header_gfm
83
- @paragraph_end = self.class::PARAGRAPH_END
38
+ @paragraph_end = self.class::PARAGRAPH_END
84
39
  end
85
40
 
86
41
  {codeblock_fenced: :codeblock_fenced_gfm,
@@ -93,6 +48,8 @@ EOF
93
48
  i = @span_parsers.index(:escaped_chars)
94
49
  @span_parsers[i] = :escaped_chars_gfm if i
95
50
  @span_parsers << :strikethrough_gfm
51
+
52
+ @hard_line_break = "#{@options[:hard_wrap] ? '' : '\\'}\n"
96
53
  end
97
54
 
98
55
  def parse
@@ -102,20 +59,8 @@ EOF
102
59
 
103
60
  def update_elements(element)
104
61
  element.children.map! do |child|
105
- if child.type == :text &&
106
- child.value.include?(hard_line_break = "#{@options[:hard_wrap] ? '' : '\\'}\n")
107
- children = []
108
- lines = child.value.split(hard_line_break, -1)
109
- omit_trailing_br = (Kramdown::Element.category(element) == :block &&
110
- element.children[-1] == child && lines[-1].empty?)
111
- lines.each_with_index do |line, index|
112
- new_element_options = {location: child.options[:location] + index}
113
-
114
- children << Element.new(:text, (index > 0 ? "\n#{line}" : line), nil, new_element_options)
115
- children << Element.new(:br, nil, nil, new_element_options) if index < lines.size - 2 ||
116
- (index == lines.size - 2 && !omit_trailing_br)
117
- end
118
- children
62
+ if child.type == :text && child.value.include?(@hard_line_break)
63
+ update_text_type(element, child)
119
64
  elsif child.type == :html_element
120
65
  child
121
66
  elsif child.type == :header && @options[:auto_ids] && !child.attr.key?('id')
@@ -133,22 +78,24 @@ EOF
133
78
  raw_text = +''
134
79
 
135
80
  append_text = lambda do |child|
136
- if child.type == :text || child.type == :codespan || child.type == :math
81
+ case child.type
82
+ when :text, :codespan, :math
137
83
  raw_text << child.value
138
- elsif child.type == :entity
84
+ when :entity
139
85
  raw_text << child.value.char
140
- elsif child.type == :smart_quote
86
+ when :smart_quote
141
87
  raw_text << ::Kramdown::Utils::Entities.entity(child.value.to_s).char
142
- elsif child.type == :typographic_sym
143
- raw_text << if child.value == :laquo_space
88
+ when :typographic_sym
89
+ raw_text << case child.value
90
+ when :laquo_space
144
91
  "« "
145
- elsif child.value == :raquo_space
92
+ when :raquo_space
146
93
  " »"
147
94
  else
148
95
  ::Kramdown::Utils::Entities.entity(child.value.to_s).char
149
96
  end
150
97
  else
151
- child.children.each {|c| append_text.call(c) }
98
+ child.children.each { |c| append_text.call(c) }
152
99
  end
153
100
  end
154
101
 
@@ -156,18 +103,21 @@ EOF
156
103
  item.options[:raw_text] = raw_text
157
104
  end
158
105
 
159
- NON_WORD_RE = /[^\p{Word}\- \t]/
106
+ NON_WORD_RE = /[^\p{Word}\- \t]/.freeze
160
107
 
161
108
  def generate_gfm_header_id(text)
162
109
  result = text.downcase
163
110
  result.gsub!(NON_WORD_RE, '')
164
111
  result.tr!(" \t", '-')
112
+
165
113
  @id_counter[result] += 1
166
- result << (@id_counter[result] > 0 ? "-#{@id_counter[result]}" : '')
114
+ counter_result = @id_counter[result]
115
+ result << "-#{counter_result}" if counter_result > 0
116
+
167
117
  @options[:auto_id_prefix] + result
168
118
  end
169
119
 
170
- ATX_HEADER_START = /^(?<level>\#{1,6})[\t ]+(?<contents>.*)\n/
120
+ ATX_HEADER_START = /^(?<level>\#{1,6})[\t ]+(?<contents>.*)\n/.freeze
171
121
  define_parser(:atx_header_gfm, ATX_HEADER_START, nil, 'parse_atx_header')
172
122
  define_parser(:atx_header_gfm_quirk, ATX_HEADER_START)
173
123
 
@@ -176,16 +126,17 @@ EOF
176
126
  text, id = parse_header_contents
177
127
  text.sub!(/[\t ]#+\z/, '') && text.rstrip!
178
128
  return false if text.empty?
129
+
179
130
  add_header(@src["level"].length, text, id)
180
131
  true
181
132
  end
182
133
 
183
- FENCED_CODEBLOCK_START = /^[ ]{0,3}[~`]{3,}/
184
- FENCED_CODEBLOCK_MATCH = /^[ ]{0,3}(([~`]){3,})\s*?((\S+?)(?:\?\S*)?)?\s*?\n(.*?)^[ ]{0,3}\1\2*\s*?\n/m
134
+ FENCED_CODEBLOCK_START = /^[ ]{0,3}[~`]{3,}/.freeze
135
+ FENCED_CODEBLOCK_MATCH = /^[ ]{0,3}(([~`]){3,})\s*?((\S+?)(?:\?\S*)?)?\s*?\n(.*?)^[ ]{0,3}\1\2*\s*?\n/m.freeze
185
136
  define_parser(:codeblock_fenced_gfm, FENCED_CODEBLOCK_START, nil, 'parse_codeblock_fenced')
186
137
 
187
- STRIKETHROUGH_DELIM = /~~/
188
- STRIKETHROUGH_MATCH = /#{STRIKETHROUGH_DELIM}[^\s~](.*?)[^\s~]#{STRIKETHROUGH_DELIM}/m
138
+ STRIKETHROUGH_DELIM = /~~/.freeze
139
+ STRIKETHROUGH_MATCH = /#{STRIKETHROUGH_DELIM}(?!\s|~).*?[^\s~]#{STRIKETHROUGH_DELIM}/m.freeze
189
140
  define_parser(:strikethrough_gfm, STRIKETHROUGH_MATCH, '~~')
190
141
 
191
142
  def parse_strikethrough_gfm
@@ -204,24 +155,29 @@ EOF
204
155
  el
205
156
  end
206
157
 
158
+ LIST_TYPES = [:ul, :ol].freeze
159
+
207
160
  # To handle task-lists we override the parse method for lists, converting matching text into
208
161
  # checkbox input elements where necessary (as well as applying classes to the ul/ol and li
209
162
  # elements).
210
163
  def parse_list
211
164
  super
212
- current_list = @tree.children.select {|element| [:ul, :ol].include?(element.type) }.last
165
+ current_list = @tree.children.select { |element| LIST_TYPES.include?(element.type) }.last
213
166
 
214
- is_tasklist = false
167
+ is_tasklist = false
215
168
  box_unchecked = '<input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />'
216
- box_checked = '<input type="checkbox" class="task-list-item-checkbox" ' \
169
+ box_checked = '<input type="checkbox" class="task-list-item-checkbox" ' \
217
170
  'disabled="disabled" checked="checked" />'
218
171
 
219
172
  current_list.children.each do |li|
220
- next unless !li.children.empty? && li.children[0].type == :p
173
+ list_items = li.children
174
+ next unless !list_items.empty? && list_items[0].type == :p
175
+
221
176
  # li -> p -> raw_text
222
- checked = li.children[0].children[0].value.gsub!(/\A\s*\[ \]\s+/, box_unchecked)
223
- unchecked = li.children[0].children[0].value.gsub!(/\A\s*\[x\]\s+/i, box_checked)
224
- is_tasklist ||= (!checked.nil? || !unchecked.nil?)
177
+ descendant = list_items[0].children[0].value
178
+ checked = descendant.gsub!(/\A\s*\[ \]\s+/, box_unchecked)
179
+ unchecked = descendant.gsub!(/\A\s*\[x\]\s+/i, box_checked)
180
+ is_tasklist ||= checked || unchecked
225
181
 
226
182
  li.attr['class'] = 'task-list-item' if is_tasklist
227
183
  end
@@ -231,14 +187,32 @@ EOF
231
187
  true
232
188
  end
233
189
 
234
- ESCAPED_CHARS_GFM = /\\([\\.*_+`<>()\[\]{}#!:\|"'\$=\-~])/
190
+ ESCAPED_CHARS_GFM = /\\([\\.*_+`<>()\[\]{}#!:\|"'\$=\-~])/.freeze
235
191
  define_parser(:escaped_chars_gfm, ESCAPED_CHARS_GFM, '\\\\', :parse_escaped_chars)
236
192
 
237
- PARAGRAPH_END_GFM = /#{LAZY_END}|#{LIST_START}|#{ATX_HEADER_START}|
238
- #{DEFINITION_LIST_START}|#{BLOCKQUOTE_START}|#{FENCED_CODEBLOCK_START}/x
193
+ PARAGRAPH_END_GFM = Regexp.union(
194
+ LAZY_END, LIST_START, ATX_HEADER_START, DEFINITION_LIST_START,
195
+ BLOCKQUOTE_START, FENCED_CODEBLOCK_START
196
+ )
197
+
198
+ private
199
+
200
+ def update_text_type(element, child)
201
+ children = []
202
+ lines = child.value.split(@hard_line_break, -1)
203
+ omit_trailing_br = (lines[-1].empty? && Kramdown::Element.category(element) == :block &&
204
+ element.children[-1] == child)
205
+
206
+ lines.each_with_index do |line, index|
207
+ new_element_options = {location: child.options[:location] + index}
208
+ children << Element.new(:text, (index > 0 ? "\n#{line}" : line), nil, new_element_options)
209
+
210
+ if index < lines.size - 2 || (index == lines.size - 2 && !omit_trailing_br)
211
+ children << Element.new(:br, nil, nil, new_element_options)
212
+ end
213
+ end
239
214
 
240
- def paragraph_end
241
- @paragraph_end
215
+ children
242
216
  end
243
217
 
244
218
  end
@@ -0,0 +1,60 @@
1
+ # -*- coding: utf-8; frozen_string_literal: true -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2019 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown-parser-gfm which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+ module Options
12
+
13
+ define(:hard_wrap, Boolean, true, <<~EOF)
14
+ Interprets line breaks literally
15
+
16
+ Insert HTML `<br />` tags inside paragraphs where the original Markdown
17
+ document had newlines (by default, Markdown ignores these newlines).
18
+
19
+ Default: true
20
+ Used by: GFM parser
21
+ EOF
22
+
23
+ define(:gfm_quirks, Object, [:paragraph_end], <<~EOF) do |val|
24
+ Enables a set of GFM specific quirks
25
+
26
+ The way how GFM is transformed on Github often differs from the way
27
+ kramdown does things. Many of these differences are negligible but
28
+ others are not.
29
+
30
+ This option allows one to enable/disable certain GFM quirks, i.e. ways
31
+ in which GFM parsing differs from kramdown parsing.
32
+
33
+ The value has to be a list of quirk names that should be enabled,
34
+ separated by commas. Possible names are:
35
+
36
+ * paragraph_end
37
+
38
+ Disables the kramdown restriction that at least one blank line has to
39
+ be used after a paragraph before a new block element can be started.
40
+
41
+ Note that if this quirk is used, lazy line wrapping does not fully
42
+ work anymore!
43
+
44
+ * no_auto_typographic
45
+
46
+ Disables automatic conversion of some characters into their
47
+ corresponding typographic symbols (like `--` to em-dash etc).
48
+ This helps to achieve results closer to what GitHub Flavored
49
+ Markdown produces.
50
+
51
+ Default: paragraph_end
52
+ Used by: GFM parser
53
+ EOF
54
+ val = simple_array_validator(val, :gfm_quirks)
55
+ val.map! { |v| str_to_sym(v.to_s) }
56
+ val
57
+ end
58
+
59
+ end
60
+ end
@@ -18,13 +18,13 @@ Encoding.default_external = 'utf-8'
18
18
  class TestFiles < Minitest::Test
19
19
 
20
20
  # Generate test methods for gfm-to-html conversion
21
- Dir[File.dirname(__FILE__) + '/testcases/**/*.text'].each do |text_file|
21
+ Dir[__dir__ + '/testcases/**/*.text'].each do |text_file|
22
22
  basename = text_file.sub(/\.text$/, '')
23
23
 
24
24
  html_file = basename + '.html'
25
25
  next unless File.exist?(html_file)
26
26
 
27
- define_method('test_gfm_' + text_file.tr('.', '_') + "_to_html") do
27
+ define_method('test_gfm_' + File.basename(text_file, '.*') + '_to_html') do
28
28
  opts_file = basename + '.options'
29
29
  opts_file = File.join(File.dirname(html_file), 'options') if !File.exist?(opts_file)
30
30
  options = File.exist?(opts_file) ? YAML::load(File.read(opts_file)) : {auto_ids: false, footnote_nr: 1}
@@ -7,14 +7,8 @@
7
7
  <pre><code>Unbalanced bottom heavy
8
8
  </code></pre>
9
9
 
10
- <div class="language-ruby highlighter-coderay"><div class="CodeRay">
11
- <div class="code"><pre><span class="line-numbers"><a href="#n1" name="n1">1</a></span>language no space
12
- </pre></div>
13
- </div>
14
- </div>
10
+ <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">language</span> <span class="n">no</span> <span class="n">space</span>
11
+ </code></pre></div></div>
15
12
 
16
- <div class="language-ruby highlighter-coderay"><div class="CodeRay">
17
- <div class="code"><pre><span class="line-numbers"><a href="#n1" name="n1">1</a></span>language with space
18
- </pre></div>
19
- </div>
20
- </div>
13
+ <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">language</span> <span class="n">with</span> <span class="n">space</span>
14
+ </code></pre></div></div>
@@ -1,9 +1,9 @@
1
1
  <p>normal</p>
2
2
 
3
- <pre><code class="language-ruby">require 'kramdown'
3
+ <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'kramdown'</span>
4
4
 
5
- Kramdown::Document.new(text).to_html
6
- </code></pre>
5
+ <span class="no">Kramdown</span><span class="o">::</span><span class="no">Document</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">text</span><span class="p">).</span><span class="nf">to_html</span>
6
+ </code></pre></div></div>
7
7
 
8
8
  <p>indent with tab</p>
9
9
 
@@ -16,5 +16,5 @@ Kramdown::Document.new(text).to_html
16
16
 
17
17
  <p>indent with 2 spaces</p>
18
18
 
19
- <pre><code class="language-js"> console.log("hello");
20
- </code></pre>
19
+ <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"hello"</span><span class="p">);</span>
20
+ </code></pre></div></div>
@@ -25,3 +25,9 @@ test<br />
25
25
  <p>This should ~~not be struck.</p>
26
26
 
27
27
  <p>This <del>is a complex <em>strike</em> through *test ~~with nesting</del> involved* here~~.</p>
28
+
29
+ <p>This <del>is a complex <em>strike</em> through *test</del> with apparent nesting <del>involved* here</del>.</p>
30
+
31
+ <p><del>中文</del></p>
32
+
33
+ <p><del>a</del></p>
@@ -25,3 +25,9 @@ I ~~don't even~~~ have an extra tilde.
25
25
  This should ~~not be struck.
26
26
 
27
27
  This ~~is a complex *strike* through *test ~~with nesting~~ involved* here~~.
28
+
29
+ This ~~is a complex *strike* through *test~~ with apparent nesting ~~involved* here~~.
30
+
31
+ ~~中文~~
32
+
33
+ ~~a~~
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kramdown-parser-gfm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-13 00:00:00.000000000 Z
11
+ date: 2019-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kramdown
@@ -35,13 +35,12 @@ files:
35
35
  - VERSION
36
36
  - lib/kramdown-parser-gfm.rb
37
37
  - lib/kramdown/parser/gfm.rb
38
+ - lib/kramdown/parser/gfm/options.rb
38
39
  - test/test_files.rb
39
40
  - test/testcases/atx_header.html
40
41
  - test/testcases/atx_header.text
41
- - test/testcases/backticks_disable_highlighting.html
42
- - test/testcases/backticks_disable_highlighting.options
43
- - test/testcases/backticks_disable_highlighting.text
44
42
  - test/testcases/backticks_syntax.html
43
+ - test/testcases/backticks_syntax.options
45
44
  - test/testcases/backticks_syntax.text
46
45
  - test/testcases/codeblock_fenced.html
47
46
  - test/testcases/codeblock_fenced.options
@@ -1,2 +0,0 @@
1
- <pre><code class="language-ruby">Kramdown::Document.new(text, :input =&gt; 'GFM')
2
- </code></pre>
@@ -1,3 +0,0 @@
1
- ```ruby
2
- Kramdown::Document.new(text, :input => 'GFM')
3
- ```