markdown_formatter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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