Class: Markascend::LineUnit

Inherits:
Struct
  • Object
show all
Defined in:
lib/markascend/line_unit.rb,
lib/markascend/line_unit.rb

Overview

process a line with (maybe) a followed up indented block

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#blockObject

Returns the value of attribute block

Returns:

  • (Object)

    the current value of block



2
3
4
# File 'lib/markascend/line_unit.rb', line 2

def block
  @block
end

#envObject

Returns the value of attribute env

Returns:

  • (Object)

    the current value of env



2
3
4
# File 'lib/markascend/line_unit.rb', line 2

def env
  @env
end

#lineObject

Returns the value of attribute line

Returns:

  • (Object)

    the current value of line



2
3
4
# File 'lib/markascend/line_unit.rb', line 2

def line
  @line
end

Instance Method Details

#parse(out = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/markascend/line_unit.rb', line 5

def parse out=nil
  @out = []
  @src = ::StringScanner.new line
  parsers = env.line_units
  while parsers.any?{|p| send p}
  end

  if out
    @out.each do |token|
      out << token
    end
  else
    @out.join
  end
end


48
49
50
51
52
53
54
55
56
57
# File 'lib/markascend/line_unit.rb', line 48

def parse_auto_link
  # TODO make protocol configurable
  if (s = @src.scan /(https|http|ftp|mailto)\:\/\/\S+/)
    s.gsub! /"/, '\\"'
    @out << '<a href="#{s}">'
    @out << (::Markascend.escape_html s)
    @out << '</a>'
    true
  end
end

#parse_bold_italic(emit = true) ⇒ Object



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
# File 'lib/markascend/line_unit.rb', line 136

def parse_bold_italic emit=true
  pos = @src.pos
  if s = @src.scan(/\*\*/)
    term = /\*\*/
    out = ['<b>']
    out_end = '</b>'
  elsif s = @src.scan(/\*/)
    term = /\*/
    out = ['<i>']
    out_end = '</i>'
  else
    return
  end

  loop do
    if res = @src.scan(/\\[\\\*]/)
      out << res[1]
    elsif @src.scan(term)
      out << out_end
      break
    elsif res = parse_bold_italic(false)
      out << res
    elsif res = parse_char(false)
      out << res
    else
      # failed
      @src.pos = pos
      return
    end
  end

  if emit
    @out << out.join
    true
  else
    out.join
  end
end

#parse_char(emit = true) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/markascend/line_unit.rb', line 175

def parse_char emit=true
  # entity list generated by:
  # ruby -rregexp_optimized_union -e'puts Regexp.optimized_union($stdin.read.scan(/(?<=ENTITY )\w+/)).source' < tools/html4_entities.dtd
  # dtd from http://www.w3.org/TR/html4/sgml/entities.html
  if c = @src.scan(/
      &(?:
        n(?:bsp|ot|tilde)|i(?:excl|quest|grave|acute|circ|uml)|c(?:e(?:nt|dil)|urren|opy|cedil)|p(?:ound|lusmn|ara)|y(?:en|acute|uml)|brvbar|s(?:ect|hy|up[123]|zlig)|u(?:ml|grave|acute|circ|uml)|o(?:rd[fm]|grave|acute|circ|tilde|uml|slash)|laquo|r(?:eg|aquo)|m(?:acr|i(?:cro|ddot))|d(?:eg|ivide)|a(?:c(?:ute|irc)|grave|acute|tilde|uml|ring|elig)|frac(?:1[24]|34)|A(?:grave|acute|circ|tilde|uml|ring|Elig)|Ccedil|E(?:grave|acute|circ|uml|TH)|I(?:grave|acute|circ|uml)|Ntilde|O(?:grave|acute|circ|tilde|uml|slash)|t(?:imes|horn)|U(?:grave|acute|circ|uml)|Yacute|THORN|e(?:grave|acute|circ|uml|th)
        |\#\d{4}
        |\#x\h{4}
      );
    /x)
  elsif c = @src.scan(/\\\W/)
    c = ::Markascend.escape_html c[1]
  elsif c = @src.scan(/\n/)
    c = :'<br>'
  elsif c = @src.scan(/./)
    c = ::Markascend.escape_html c
  else
    return
  end

  if emit
    @out << c
    true
  else
    c
  end
end

#parse_inline_codeObject

the same as markdown



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/markascend/line_unit.rb', line 22

def parse_inline_code
  if s = @src.scan(/
      (`{1,})(\ ?)
      .*?
      \2\1
    /x)
    s =~ /^
      (`{1,})(\ ?)
      (.*?)
      \2\1
    $/x
    @out << (::Markascend.hilite $3, env.hi, true)
    true
  end
end


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
130
131
132
133
134
# File 'lib/markascend/line_unit.rb', line 74

def parse_link
  pos = @src.pos
  return unless @src.scan(/\[/)

  footnote = @src.scan(/[\.\:]/)
  content = ''
  loop do
    if res = @src.scan(/\]/)
      break
    elsif res = parse_char(false)
      content << res
    else
      # failed
      @src.pos = pos
      return
    end
  end

  if footnote and env.sandbox
    scan_lexical_parens || scan_recursive_braces
    @out << ::Markascend.escape_html(@src.string[pos..(@src.pos)])
    return true
  end

  case footnote
  when '.'
    unless explain = scan_lexical_parens || scan_recursive_braces
      @src.pos = pos
      return
    end

    footnotes = env.footnotes
    if content =~ /\A\s*\z/
      content = footnotes.size + 1
    end
    raise "Already defined footnote: #{content}" if footnotes.has_key?(content)
    footnotes[content] = explain
    # TODO id prefix configurable
    @out << %Q|<a href="#footnote-#{footnotes.size}">#{content}</a>|
    true
  when ':'
    footnotes = env.footnotes
    if content =~ /\A\s*(\d+)\s*\z/
      @out << [:footnote_id_ref, $1.to_i]
    else
      content.strip!
      @out << [:footnote_acronym_ref, content]
    end
    true
  else
    if addr = scan_lexical_parens || scan_recursive_braces
      # TODO smarter addr to recognize things like a.b.com
      addr = ::Markascend.escape_attr addr
      @out << %Q|<a href="#{addr}">#{content}</a>|
      true
    else
      @src.pos = pos
      nil
    end
  end
end

#parse_macroObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/markascend/line_unit.rb', line 59

def parse_macro
  start = @src.pos
  return unless macro = @src.scan(/\\(?!\d)\w+/)
  macro = macro[1..-1]
  if block = scan_lexical_parens
    inline_content = @src.string[start..(@src.pos)]
  elsif block = scan_recursive_braces
    inline_content = @src.string[start..(@src.pos)]
  else
    block = self.block
  end
  @out << ::Markascend::Macro.new(env, block, inline_content).parse(macro)
  true
end

#parse_mathObject

no escape chars, but \ and $ are treated as atomic parsing units



39
40
41
42
43
44
45
46
# File 'lib/markascend/line_unit.rb', line 39

def parse_math
  if (s = @src.scan /\$(?:\\[\\\$]|[^\$])*\$/)
    @out << '<code class="math">'
    @out << (::Markascend.escape_html s[1...-1])
    @out << '</code>'
    true
  end
end

#scan_lexical_parensObject

(…)



216
217
218
219
220
221
222
223
224
# File 'lib/markascend/line_unit.rb', line 216

def scan_lexical_parens
  if s = @src.scan(/
      \(
        (?: \\ [\\\)] | [^\)] )+  # lexical rule
      \)
    /x)
    s[1...-1].gsub(/\\[\)\\]/){|x| x[1]}
  end
end

#scan_recursive_bracesObject



205
206
207
208
209
210
211
212
213
# File 'lib/markascend/line_unit.rb', line 205

def scan_recursive_braces
  if s = @src.scan(/
      (?<braces> \{
        ([^\{]+ | \g<braces>)*  # recursive rule
      \})
    /x)
    s[1...-1]
  end
end