markdown_formatter 1.0.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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +3 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +46 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/exe/mdfmt +27 -0
  14. data/lib/markdown_formatter.rb +57 -0
  15. data/lib/markdown_formatter/ast.rb +47 -0
  16. data/lib/markdown_formatter/cli.rb +13 -0
  17. data/lib/markdown_formatter/nodes/base.rb +11 -0
  18. data/lib/markdown_formatter/nodes/blank.rb +9 -0
  19. data/lib/markdown_formatter/nodes/block_quote.rb +22 -0
  20. data/lib/markdown_formatter/nodes/code_block.rb +9 -0
  21. data/lib/markdown_formatter/nodes/code_span.rb +9 -0
  22. data/lib/markdown_formatter/nodes/container_block.rb +23 -0
  23. data/lib/markdown_formatter/nodes/emphasis.rb +10 -0
  24. data/lib/markdown_formatter/nodes/entity.rb +9 -0
  25. data/lib/markdown_formatter/nodes/footnote.rb +9 -0
  26. data/lib/markdown_formatter/nodes/header.rb +9 -0
  27. data/lib/markdown_formatter/nodes/html_element.rb +9 -0
  28. data/lib/markdown_formatter/nodes/image.rb +9 -0
  29. data/lib/markdown_formatter/nodes/inline.rb +29 -0
  30. data/lib/markdown_formatter/nodes/leaf_block.rb +27 -0
  31. data/lib/markdown_formatter/nodes/link.rb +9 -0
  32. data/lib/markdown_formatter/nodes/list.rb +23 -0
  33. data/lib/markdown_formatter/nodes/list_item.rb +38 -0
  34. data/lib/markdown_formatter/nodes/paragraph.rb +39 -0
  35. data/lib/markdown_formatter/nodes/smart_quote.rb +14 -0
  36. data/lib/markdown_formatter/nodes/strong.rb +10 -0
  37. data/lib/markdown_formatter/nodes/thematic_break.rb +9 -0
  38. data/lib/markdown_formatter/nodes/typographic_sym.rb +9 -0
  39. data/lib/markdown_formatter/version.rb +3 -0
  40. data/lib/monkey_patches/kramdown/parser/gfm.rb +21 -0
  41. data/lib/monkey_patches/kramdown/parser/kramdown/autolink.rb +16 -0
  42. data/lib/monkey_patches/kramdown/parser/kramdown/blockquote.rb +22 -0
  43. data/lib/monkey_patches/kramdown/parser/kramdown/code_block.rb +34 -0
  44. data/lib/monkey_patches/kramdown/parser/kramdown/code_span.rb +31 -0
  45. data/lib/monkey_patches/kramdown/parser/kramdown/emphasis.rb +48 -0
  46. data/lib/monkey_patches/kramdown/parser/kramdown/escaped_chars.rb +12 -0
  47. data/lib/monkey_patches/kramdown/parser/kramdown/footnote.rb +27 -0
  48. data/lib/monkey_patches/kramdown/parser/kramdown/header.rb +38 -0
  49. data/lib/monkey_patches/kramdown/parser/kramdown/horizontal_rule.rb +14 -0
  50. data/lib/monkey_patches/kramdown/parser/kramdown/html.rb +60 -0
  51. data/lib/monkey_patches/kramdown/parser/kramdown/link.rb +116 -0
  52. data/lib/monkey_patches/kramdown/parser/kramdown/list.rb +115 -0
  53. data/lib/monkey_patches/kramdown/parser/kramdown/paragraph.rb +26 -0
  54. data/lib/monkey_patches/kramdown/parser/kramdown/typographic_symbol.rb +26 -0
  55. data/lib/monkey_patches/kramdown/utils/entities.rb +1911 -0
  56. data/markdown_formatter.gemspec +29 -0
  57. metadata +192 -0
  58. metadata.gz.sig +0 -0
@@ -0,0 +1,9 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class Image < Base
4
+ def to_s
5
+ node.dig(:options, :raw_text).gsub(/\R/, "")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class Inline < Base
4
+ def to_s
5
+ case node[:type]
6
+ when :a
7
+ Link.new(c).to_s
8
+ when :text
9
+ node[:value]
10
+ when :br
11
+ # skip
12
+ when :smart_quote
13
+ SmartQuote.new(c).to_s
14
+ when :typographic_sym
15
+ TypographicSym.new(c).to_s
16
+ when :img
17
+ Image.new(c).to_s
18
+ when :entity
19
+ Entity.new(c).to_s
20
+ when :footnote
21
+ Footnote.new(c).to_s
22
+ else
23
+ pp c
24
+ raise "Unexpected type `#{c[:type]}'"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class LeafBlock < Base
4
+ def to_s
5
+ case node[:type]
6
+ when :a
7
+ Link.new(c).to_s
8
+ when :p
9
+ Paragraph.new(c).to_s
10
+ when :strong
11
+ Strong.new(c).to_s
12
+ when :codespan
13
+ CodeSpan.new(c).to_s
14
+ when :em
15
+ Emphasis.new(c).to_s
16
+ when :html_element
17
+ str += HTMLElement.new(c).to_s
18
+ when :footnote
19
+ str += Footnote.new(c).to_s
20
+ else
21
+ pp c
22
+ raise "Unexpected type `#{c[:type]}'"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class Link < Base
4
+ def to_s
5
+ node.dig(:options, :raw_text).gsub(/\]\(\s*/, "](").gsub(/\R/, " ")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class List < Base
4
+ def initialize(node, nest_level = 0)
5
+ super(node)
6
+ @nest_level = nest_level
7
+ end
8
+
9
+ def to_s
10
+ node[:children].map do |c|
11
+ case c[:type]
12
+ when :blank
13
+ ""
14
+ when :li
15
+ ListItem.new(c, @nest_level).to_s
16
+ else
17
+ raise "Unexpected type. #{c[:type]}"
18
+ end
19
+ end.join("\n") + "\n"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class ListItem < Base
4
+ def initialize(node, nest_level = 0)
5
+ super(node)
6
+ @nest_level = nest_level
7
+ end
8
+
9
+ def to_s
10
+ /^(?<prefix>\s*([0-9]+\.|[-+*])\s*)/ =~ node.dig(:options, :raw_text)
11
+ indents = node.dig(:options, :raw_text).lstrip.split(/\R/).map do |line|
12
+ line.match(/^\s+/).to_s
13
+ end
14
+
15
+ (prefix + node[:children].map { |c|
16
+ if c[:type] != :blank
17
+ indent, * = indents.shift(c.dig(:options, :raw_text).scan(/\R/).size)
18
+ else
19
+ indents.shift(c[:value].scan(/\R/).size)
20
+ end
21
+
22
+ case c[:type]
23
+ when :p
24
+ indent + Paragraph.new(c).to_s.gsub(/\R/, " ").split.join(" ")
25
+ when :blank
26
+ ""
27
+ when :codeblock
28
+ CodeBlock.new(c).to_s.indent(indent.size)
29
+ when :ul
30
+ List.new(c, @nest_level + 1).to_s.chomp
31
+ else
32
+ raise "Unexpected type #{c[:type]}"
33
+ end
34
+ }.join("\n")).indent(@nest_level.zero? ? 0 : 2)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class Paragraph < Base
4
+ def to_s
5
+ node[:children].map do |c|
6
+ case c[:type]
7
+ when :a
8
+ Link.new(c).to_s
9
+ when :text
10
+ c[:value]
11
+ when :strong
12
+ Strong.new(c).to_s
13
+ when :codespan
14
+ CodeSpan.new(c).to_s
15
+ when :br
16
+ # skip
17
+ when :em
18
+ Emphasis.new(c).to_s
19
+ when :smart_quote
20
+ SmartQuote.new(c).to_s
21
+ when :typographic_sym
22
+ TypographicSym.new(c).to_s
23
+ when :img
24
+ Image.new(c).to_s
25
+ when :html_element
26
+ HTMLElement.new(c).to_s
27
+ when :entity
28
+ Entity.new(c).to_s
29
+ when :footnote
30
+ Footnote.new(c).to_s
31
+ else
32
+ pp c
33
+ raise "Unexpected type `#{c[:type]}'"
34
+ end
35
+ end.join
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class SmartQuote < Base
4
+ def to_s
5
+ case node[:value]
6
+ when /rsquo|lsquo/
7
+ ?'
8
+ when /rdquo|ldquo/
9
+ ?"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class Strong < Base
4
+ def to_s
5
+ delim = node.dig(:options, :delim)
6
+ "%s%s%s" % [delim, Paragraph.new(node).to_s, delim]
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class ThematicBreak < Base
4
+ def to_s
5
+ node.dig(:options, :raw_text)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module MarkdownFormatter
2
+ module ASTNode
3
+ class TypographicSym < Base
4
+ def to_s
5
+ node.dig(:options, :symbol)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module MarkdownFormatter
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,21 @@
1
+ module Kramdown
2
+ module Parser
3
+ class GFM < Kramdown::Parser::Kramdown
4
+
5
+ # Copied from kramdown/parser/kramdown/header.rb, removed the first line
6
+ def parse_atx_header_gfm_quirk
7
+ start_line_number = @src.current_line_number
8
+ @src.check(ATX_HEADER_MATCH)
9
+ level, text, id = @src[1], @src[2].to_s.strip, @src[3]
10
+ return false if text.empty?
11
+
12
+ @src.pos += @src.matched_size
13
+ el = new_block_el(:header, nil, nil, :level => level.length, :raw_text => text, :location => start_line_number, :original_text => @src[0].chomp)
14
+ add_text(text, el)
15
+ el.attr['id'] = id if id
16
+ @tree.children << el
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+
5
+ # Parse the autolink at the current location.
6
+ def parse_autolink
7
+ start_line_number = @src.current_line_number
8
+ @src.pos += @src.matched_size
9
+ href = (@src[2].nil? ? "mailto:#{@src[1]}" : @src[1])
10
+ el = Element.new(:a, nil, {'href' => href}, :location => start_line_number, :raw_text => @src[0].dup)
11
+ add_text(@src[1].sub(/^mailto:/, ''), el)
12
+ @tree.children << el
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+
5
+ # Parse the blockquote at the current location.
6
+ def parse_blockquote
7
+ start_line_number = @src.current_line_number
8
+ result = @src.scan(PARAGRAPH_MATCH)
9
+ while !@src.match?(self.class::LAZY_END)
10
+ result << @src.scan(PARAGRAPH_MATCH)
11
+ end
12
+ raw_text = result.dup
13
+ result.gsub!(BLOCKQUOTE_START, '')
14
+
15
+ el = new_block_el(:blockquote, nil, nil, :location => start_line_number, :raw_text => raw_text)
16
+ @tree.children << el
17
+ parse_blocks(el, result)
18
+ true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+
5
+ # Parse the indented codeblock at the current location.
6
+ def parse_codeblock
7
+ start_line_number = @src.current_line_number
8
+ data = @src.scan(self.class::CODEBLOCK_MATCH)
9
+ data.gsub!(/\n( {0,3}\S)/, ' \\1')
10
+ data.gsub!(INDENT, '')
11
+ @tree.children << new_block_el(:codeblock, data, nil, :location => start_line_number, :raw_text => @src.matched.dup)
12
+ true
13
+ end
14
+
15
+ # Parse the fenced codeblock at the current location.
16
+ def parse_codeblock_fenced
17
+ if @src.check(self.class::FENCED_CODEBLOCK_MATCH)
18
+ start_line_number = @src.current_line_number
19
+ @src.pos += @src.matched_size
20
+ el = new_block_el(:codeblock, @src[5], nil, :location => start_line_number, :raw_text => @src.matched.dup)
21
+ lang = @src[3].to_s.strip
22
+ unless lang.empty?
23
+ el.options[:lang] = lang
24
+ el.attr['class'] = "language-#{@src[4]}"
25
+ end
26
+ @tree.children << el
27
+ true
28
+ else
29
+ false
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+ # Parse the codespan at the current scanner location.
5
+ def parse_codespan
6
+ start_line_number = @src.current_line_number
7
+ result = @src.scan(CODESPAN_DELIMITER)
8
+ simple = (result.length == 1)
9
+ saved_pos = @src.save_pos
10
+
11
+ if simple && @src.pre_match =~ /\s\Z/ && @src.match?(/\s/)
12
+ add_text(result)
13
+ return
14
+ end
15
+
16
+ if text = @src.scan_until(/#{result}/)
17
+ raw_text = result + text
18
+ text.sub!(/#{result}\Z/, '')
19
+ if !simple
20
+ text = text[1..-1] if text[0..0] == ' '
21
+ text = text[0..-2] if text[-1..-1] == ' '
22
+ end
23
+ @tree.children << Element.new(:codespan, text, nil, :location => start_line_number, :raw_text => raw_text)
24
+ else
25
+ @src.revert_pos(saved_pos)
26
+ add_text(result)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+
5
+ # Parse the emphasis at the current location.
6
+ def parse_emphasis
7
+ start_line_number = @src.current_line_number
8
+ saved_pos = @src.save_pos
9
+
10
+ result = @src.scan(EMPHASIS_START)
11
+ element = (result.length == 2 ? :strong : :em)
12
+ type = result[0..0]
13
+
14
+ if (type == '_' && @src.pre_match =~ /[[:alpha:]-]\z/) || @src.check(/\s/) ||
15
+ @tree.type == element || @stack.any? { |el, _| el.type == element }
16
+ add_text(result)
17
+ return
18
+ end
19
+
20
+ sub_parse = lambda do |delim, elem|
21
+ el = Element.new(elem, nil, nil, :location => start_line_number, :delim => delim)
22
+ stop_re = /#{Regexp.escape(delim)}/
23
+ found = parse_spans(el, stop_re) do
24
+ (@src.pre_match[-1, 1] !~ /\s/) &&
25
+ (elem != :em || !@src.match?(/#{Regexp.escape(delim*2)}(?!#{Regexp.escape(delim)})/)) &&
26
+ (type != '_' || !@src.match?(/#{Regexp.escape(delim)}[[:alnum:]]/)) && el.children.size > 0
27
+ end
28
+ [found, el, stop_re]
29
+ end
30
+
31
+ found, el, stop_re = sub_parse.call(result, element)
32
+ if !found && element == :strong && @tree.type != :em
33
+ @src.revert_pos(saved_pos)
34
+ @src.pos += 1
35
+ found, el, stop_re = sub_parse.call(type, :em)
36
+ end
37
+ if found
38
+ @src.scan(stop_re)
39
+ @tree.children << el
40
+ else
41
+ @src.revert_pos(saved_pos)
42
+ @src.pos += result.length
43
+ add_text(result)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+
5
+ # Parse the backslash-escaped character at the current location.
6
+ def parse_escaped_chars
7
+ @src.pos += @src.matched_size
8
+ add_text(@src[0])
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ module Kramdown
2
+ module Parser
3
+ class Kramdown
4
+ # Parse the footnote marker at the current location.
5
+ def parse_footnote_marker
6
+ start_line_number = @src.current_line_number
7
+ @src.pos += @src.matched_size
8
+ fn_def = @footnotes[@src[1]]
9
+ if fn_def
10
+ if fn_def[:eob]
11
+ update_attr_with_ial(fn_def[:eob].attr, fn_def[:eob].options[:ial] || {})
12
+ fn_def[:attr] = fn_def[:eob].attr
13
+ fn_def[:options] = fn_def[:eob].options
14
+ fn_def.delete(:eob)
15
+ end
16
+ fn_def[:marker] ||= []
17
+ fn_def[:marker].push(Element.new(:footnote, fn_def[:content], fn_def[:attr],
18
+ fn_def[:options].merge(:name => @src[1], :location => start_line_number, :raw_text => @src[0].dup)))
19
+ @tree.children << fn_def[:marker].last
20
+ else
21
+ warning("Footnote definition for '#{@src[1]}' not found on line #{start_line_number}")
22
+ add_text(@src.matched)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end