Class: Kramdown::Parser::RFC2629Kramdown

Inherits:
Kramdown show all
Defined in:
lib/kramdown-rfc2629.rb

Constant Summary collapse

XREF_BASE =

a token for a reference

/#{REXML::XMLTokens::NAME_CHAR}+/
XREF_TXT =

parenthesized text

/(?:[^\(]|\([^\)]*\))+/
XREF_RE =
/#{XREF_BASE}(?: \(#{XREF_TXT}\))?/
XREF_RE_M =

matching version of XREF_RE

/\A(#{XREF_BASE})(?: \((#{XREF_TXT})\))?/
XREF_SINGLE =
/(?:Section|Appendix) #{XREF_RE}/
XREF_MULTI =
/(?:Sections|Appendices) (?:#{XREF_RE}, )*#{XREF_RE},? and #{XREF_RE}/
XREF_ANY =
/(?:#{XREF_SINGLE}|#{XREF_MULTI})/
SECTIONS_RE =
/(?:#{XREF_ANY} and )?#{XREF_ANY}/
XREF_START =
/\{\{(?:(?:\{(.*?\n??.*?)\}(?:\{(.*?\n??.*?)\})?)|(\X*?))((?:\}\})|\})/u
IREF_START =
/\(\(\((.*?)\)\)\)/u
PI_BLOCK_START =

warn [:OPT_SPACE, OPT_SPACE, HTML_INSTRUCTION_RE].inspect

/^#{OPT_SPACE}<\?/u
PI_SPAN_START =
/<\?/u

Constants included from Kramdown

RFCXML_SPAN_ELEMENTS

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*doc) ⇒ RFC2629Kramdown

Returns a new instance of RFC2629Kramdown.



100
101
102
103
104
105
106
# File 'lib/kramdown-rfc2629.rb', line 100

def initialize(*doc)
  super
  @span_parsers.unshift(:xref)
  @span_parsers.unshift(:iref)
  @span_parsers.unshift(:span_pi)
  @block_parsers.unshift(:block_pi)
end

Class Method Details

.idref_cleanup(href) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
# File 'lib/kramdown-rfc2629.rb', line 117

def self.idref_cleanup(href)
  # can't start an IDREF with a number or reserved start
  if href =~ / /
    if $options.v3
      warn "** space(s) in cross-reference '#{href}' -- are you trying to use section references?"
    else
      warn "** space(s) in cross-reference '#{href}' -- note that section references are supported in v3 mode only."
    end
  end
  href.gsub(/\A(?:[0-9]|section-|u-|figure-|table-|iref-)/) { "_#{$&}" }
end

Instance Method Details

#handle_bares(s, attr, format, href, last_join = nil) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/kramdown-rfc2629.rb', line 134

def handle_bares(s, attr, format, href, last_join = nil)
  if s.match(/\A(#{XREF_ANY}) and (#{XREF_ANY})\z/)
    handle_bares($1, {}, nil, href, " and ")
    handle_bares($2, {}, nil, href, " of ")
    return
  end

  href = href.split(' ')[0] # Remove any trailing (...)
  target1, target2 = href.split("@", 2)
  multi = last_join != nil
  (sn, s) = s.split(' ', 2)
  loop do
    m = s.match(/\A#{XREF_RE_M}(, (?:and )?| and )?/)
    break if not m

    if not multi and not m[2] and not m[3] and not target2
      # Modify |attr| if there is a single reference.  This can only be
      # used if there is only one section reference and the section part
      # has no title.
      attr['section'] = m[1]
      attr['sectionFormat'] = format
      attr['text'] = m[2]
      return
    end

    if sn
      @tree.children << Element.new(:text, "#{sn} ", {})
      sn = nil
    end

    multi = true
    s[m[0]] = ''

    attr1 = { 'target' => target1, 'section' => m[1], 'sectionFormat' => 'bare', 'text' => m[2] }
    @tree.children << Element.new(:xref, nil, attr1)
    andof = m[3] || last_join || " of "
    if andof == " of " && target2
      andof += rfc_mention(target1)
    end
    @tree.children << Element.new(:text, andof, {})
  end
end

#parse_block_piObject



265
266
267
268
269
270
271
272
273
274
275
# File 'lib/kramdown-rfc2629.rb', line 265

def parse_block_pi
  # warn [:BLOCK].inspect
  line = @src.current_line_number
  if (result = @src.scan(HTML_INSTRUCTION_RE))
    @tree.children << Element.new(:xml_pi, result, nil, category: :block, location: line)
    @src.scan(TRAILING_WHITESPACE)
    true
  else
    false
  end
end

#parse_irefObject

Introduce new (((target))) syntax for irefs



251
252
253
254
255
256
# File 'lib/kramdown-rfc2629.rb', line 251

def parse_iref
  @src.pos += @src.matched_size
  href = @src[1]
  el = Element.new(:iref, nil, {'target' => href}) # XXX
  @tree.children << el
end

#parse_span_piObject



280
281
282
283
284
285
286
287
288
# File 'lib/kramdown-rfc2629.rb', line 280

def parse_span_pi
  # warn [:SPAN].inspect
  line = @src.current_line_number
  if (result = @src.scan(HTML_INSTRUCTION_RE))
    @tree.children << Element.new(:xml_pi, result, nil, category: :span, location: line)
  else
    add_text(@src.getch)
  end
end

#parse_xrefObject

Introduce new {target} syntax for empty xrefs, which would otherwise be an ugly ![!](target) or ![ ](target) (I’d rather use [[target]], but that somehow clashes with links.)



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/kramdown-rfc2629.rb', line 182

def parse_xref
  @src.pos += @src.matched_size
  unless @src[4] == "}}"
    warn "*** #{@src[0]}: unmatched braces #{@src[4].inspect}"
  end
  if contact_name = @src[1]
    attr = {'fullname' => contact_name.gsub("\n", " ")}
    if ascii_name = @src[2]
      attr["asciiFullname"] = ascii_name.gsub("\n", " ")
    end
    el = Element.new(:contact, nil, attr)
  else
    href = @src[3]
    attr = {}
    handled_subref = false
    if $options.v3
      # match Section ... of ...; set section, sectionFormat
      case href.gsub(/[\u00A0\s]+/, ' ') # may need nbsp and/or newlines
      when /\A(#{SECTIONS_RE}) of (.*)\z/
        href = $2
        handle_bares($1, attr, "of", href)
        handled_subref = true
      when /\A(.*), (#{SECTIONS_RE})\z/
        href = $1
        handle_bares($2, attr, "comma", href)
        handled_subref = true
      when /\A(.*) \((#{SECTIONS_RE})\)\z/
        href = $1
        handle_bares($2, attr, "parens", href)
        handled_subref = true
      when /#{XREF_RE_M}<(.+)\z/
        href = $3
        if $2
          attr['section'] = $2
          attr['relative'] = "#" << $1
        else
          attr['section'] = $1
        end
        attr['sectionFormat'] = 'bare'
      when /\A<<(.+)\z/
        href = $1
        attr['format'] = 'title'
      when /\A<(.+)\z/
        href = $1
        attr['format'] = 'counter'
      end
    end
    if href.match(/#{XREF_RE_M}\z/)
      href = $1
      attr['text'] = $2
    end
    target1, target2 = href.split("@", 2) # should do this only for sectionref...
    if target2
      href = target2
      unless handled_subref
        @tree.children << Element.new(:text, rfc_mention(target1), {})
      end
    end
    href = self.class.idref_cleanup(href)
    attr['target'] = href
    el = Element.new(:xref, nil, attr)
  end
  @tree.children << el
end

#replace_abbreviations(el, regexps = nil) ⇒ Object



88
89
90
91
92
93
94
95
96
97
# File 'lib/kramdown-rfc2629.rb', line 88

def replace_abbreviations(el, regexps = nil)
  unless regexps          # DUPLICATED AND MODIFIED CODE FROM UPSTREAM, CHECK ON UPSTREAM UPGRADE
    sorted_abbrevs = @root.options[:abbrev_defs].keys.sort {|a, b| b.length <=> a.length }
    regexps = [Regexp.union(*sorted_abbrevs.map {|k|
                              /#{Regexp.escape(k).gsub(/\\\s/, "[\\s\\p{Z}]+").force_encoding(Encoding::UTF_8)}/})]
    regexps << /(?=(?:\W|^)#{regexps.first}(?!\w))/ # regexp should only match on word boundaries
    # warn regexps.inspect
  end
  super(el, regexps)
end

#rfc_mention(target1) ⇒ Object

only works for RFCnnnn



129
130
131
132
# File 'lib/kramdown-rfc2629.rb', line 129

def rfc_mention(target1)  # only works for RFCnnnn
  target1 =~ /\A([A-Z]*)(.*)\z/
  "#$1 #$2 "
end