kramdown-parser-gfm 1.0.1 → 1.1.0

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 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
- ```