-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathasciidoctor-extensions.rb
129 lines (117 loc) · 4.55 KB
/
asciidoctor-extensions.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require 'asciidoctor'
require 'asciidoctor/extensions'
require 'asciidoctor/converter/html5'
require 'parslet'
module Git
module Documentation
class AdocSynopsisQuote < Parslet::Parser
# parse a string like "git add -p [--root=<path>]" as series of tokens keywords, grammar signs and placeholders
# where placeholders are UTF-8 words separated by '-', enclosed in '<' and '>'
rule(:space) { match('[\s\t\n ]').repeat(1) }
rule(:space?) { space.maybe }
rule(:keyword) { match('[-a-zA-Z0-9:+=~@,\./_\^\$\'"\*%!{}#]').repeat(1) }
rule(:placeholder) { str('<') >> match('[[:word:]]|-').repeat(1) >> str('>') }
rule(:opt_or_alt) { match('[\[\] |()]') >> space? }
rule(:ellipsis) { str('...') >> match('\]|$').present? }
rule(:grammar) { opt_or_alt | ellipsis }
rule(:ignore) { match('[\'`]') }
rule(:token) do
grammar.as(:grammar) | placeholder.as(:placeholder) | space.as(:grammar) |
ignore.as(:ignore) | keyword.as(:keyword)
end
rule(:tokens) { token.repeat(1) }
root(:tokens)
end
class EscapedSynopsisQuote < Parslet::Parser
rule(:space) { match('[\s\t\n ]').repeat(1) }
rule(:space?) { space.maybe }
rule(:keyword) { match('[-a-zA-Z0-9:+=~@,\./_\^\$\'"\*%!{}#]').repeat(1) }
rule(:placeholder) { str('<') >> match('[[:word:]]|-').repeat(1) >> str('>') }
rule(:opt_or_alt) { match('[\[\] |()]') >> space? }
rule(:ellipsis) { str('...') >> match('\]|$').present? }
rule(:grammar) { opt_or_alt | ellipsis }
rule(:ignore) { match('[\'`]') }
rule(:token) do
grammar.as(:grammar) | placeholder.as(:placeholder) | space.as(:grammar) |
ignore.as(:ignore) | keyword.as(:keyword)
end
rule(:tokens) { token.repeat(1) }
root(:tokens)
end
class SynopsisQuoteToAdoc < Parslet::Transform
rule(grammar: simple(:grammar)) { grammar.to_s }
rule(keyword: simple(:keyword)) { "{empty}`#{keyword}`{empty}" }
rule(placeholder: simple(:placeholder)) { "__#{placeholder}__" }
rule(ignore: simple(:ignore)) { '' }
end
class SynopsisQuoteToHtml5 < Parslet::Transform
rule(grammar: simple(:grammar)) { grammar.to_s }
rule(keyword: simple(:keyword)) { "<code>#{keyword}</code>" }
rule(placeholder: simple(:placeholder)) { "<em>#{placeholder}</em>" }
rule(ignore: simple(:ignore)) { '' }
end
class SynopsisConverter
def convert(parslet_parser, parslet_transform, reader, logger = nil)
reader.lines.map do |l|
parslet_transform.apply(parslet_parser.parse(l)).join
end.join("\n")
rescue Parslet::ParseFailed
logger&.info "synopsis parsing failed for '#{reader.lines.join(' ')}'"
reader.lines.map do |l|
parslet_transform.apply(placeholder: l)
end.join("\n")
end
end
class SynopsisBlock < Asciidoctor::Extensions::BlockProcessor
use_dsl
named :synopsis
parse_content_as :simple
def process(parent, reader, attrs)
outlines = SynopsisConverter.new.convert(
AdocSynopsisQuote.new,
SynopsisQuoteToAdoc.new,
reader,
parent.document.logger
)
create_block parent, :verse, outlines, attrs
end
end
# register a html5 converter that takes in charge
# to convert monospaced text into Git style synopsis
class GitHTMLConverter < Asciidoctor::Converter::Html5Converter
extend Asciidoctor::Converter::Config
register_for 'html5'
def convert_inline_quoted(node)
if node.type == :monospaced
SynopsisConverter.new.convert(
EscapedSynopsisQuote.new,
SynopsisQuoteToHtml5.new,
node.text,
node.document.logger
)
else
open, close, tag = QUOTE_TAGS[node.type]
if node.id
class_attr = node.role ? %( class="#{node.role}") : ''
if tag
%(#{open.chop} id="#{node.id}"#{class_attr}>#{node.text}#{close})
else
%(<span id="#{node.id}"#{class_attr}>#{open}#{node.text}#{close}</span>)
end
elsif node.role
if tag
%(#{open.chop} class="#{node.role}">#{node.text}#{close})
else
%(<span class="#{node.role}">#{open}#{node.text}#{close}</span>)
end
else
%(#{open}#{node.text}#{close})
end
end
end
end
end
end
Asciidoctor::Extensions.register do
block Git::Documentation::SynopsisBlock
end