| Class | SM::AttributeManager |
| In: |
markup/simple_markup/inline.rb
|
| Parent: | Object |
| NULL | = | "\000".freeze | ||
| A_PROTECT | = | 004 | We work by substituting non-printing characters in to the text. For now I‘m assuming that I can substitute a character in the range 0..8 for a 7 bit character without damaging the encoded string, but this might be optimistic | |
| PROTECT_ATTR | = | A_PROTECT.chr | ||
| MATCHING_WORD_PAIRS | = | {} | This maps delimiters that occur around words (such as bold or tt) where the start and end delimiters and the same. This lets us optimize the regexp | |
| WORD_PAIR_MAP | = | {} | And this is used when the delimiters aren‘t the same. In this case the hash maps a pattern to the attribute character | |
| HTML_TAGS | = | {} | This maps HTML tags to the corresponding attribute char | |
| SPECIAL | = | {} | And this maps special sequences to a name. A special sequence is something like a WikiWord | |
| PROTECTABLE | = | [ "<" << "\\" ] | A \ in front of a character that would normally be processed turns off processing. We do this by turning < into <#{PROTECT} |
# File markup/simple_markup/inline.rb, line 208
208: def initialize
209: add_word_pair("*", "*", :BOLD)
210: add_word_pair("_", "_", :EM)
211: add_word_pair("+", "+", :TT)
212:
213: add_html("em", :EM)
214: add_html("i", :EM)
215: add_html("b", :BOLD)
216: add_html("tt", :TT)
217: add_html("code", :TT)
218:
219: add_special(/<!--(.*?)-->/, :COMMENT)
220: end
# File markup/simple_markup/inline.rb, line 238
238: def add_html(tag, name)
239: HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
240: end
# File markup/simple_markup/inline.rb, line 242
242: def add_special(pattern, name)
243: SPECIAL[pattern] = Attribute.bitmap_for(name)
244: end
# File markup/simple_markup/inline.rb, line 222
222: def add_word_pair(start, stop, name)
223: raise "Word flags may not start '<'" if start[0] == ?<
224: bitmap = Attribute.bitmap_for(name)
225: if start == stop
226: MATCHING_WORD_PAIRS[start] = bitmap
227: else
228: pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
229: # "([A-Za-z]+)" +
230: "(\\S+)" +
231: "(" + Regexp.escape(stop) +")")
232: WORD_PAIR_MAP[pattern] = bitmap
233: end
234: PROTECTABLE << start[0,1]
235: PROTECTABLE.uniq!
236: end
# File markup/simple_markup/inline.rb, line 127
127: def change_attribute(current, new)
128: diff = current ^ new
129: attribute(new & diff, current & diff)
130: end
# File markup/simple_markup/inline.rb, line 132
132: def changed_attribute_by_name(current_set, new_set)
133: current = new = 0
134: current_set.each {|name| current |= Attribute.bitmap_for(name) }
135: new_set.each {|name| new |= Attribute.bitmap_for(name) }
136: change_attribute(current, new)
137: end
Map attributes like textto the sequence \001\002<char>\001\003<char>, where <char> is a per-attribute specific character
# File markup/simple_markup/inline.rb, line 148
148: def convert_attrs(str, attrs)
149: # first do matching ones
150: tags = MATCHING_WORD_PAIRS.keys.join("")
151: re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
152: # re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
153: 1 while str.gsub!(Regexp.new(re)) {
154: attr = MATCHING_WORD_PAIRS[$2];
155: attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
156: $1 + NULL*$2.length + $3 + NULL*$2.length + $4
157: }
158:
159: # then non-matching
160: unless WORD_PAIR_MAP.empty?
161: WORD_PAIR_MAP.each do |regexp, attr|
162: str.gsub!(regexp) {
163: attrs.set_attrs($`.length + $1.length, $2.length, attr)
164: NULL*$1.length + $2 + NULL*$3.length
165: }
166: end
167: end
168: end
# File markup/simple_markup/inline.rb, line 170
170: def convert_html(str, attrs)
171: tags = HTML_TAGS.keys.join("|")
172: re = "<(#{tags})>(.*?)</\\1>"
173: 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
174: attr = HTML_TAGS[$1.downcase]
175: html_length = $1.length + 2
176: seq = NULL * html_length
177: attrs.set_attrs($`.length + html_length, $2.length, attr)
178: seq + $2 + seq + NULL
179: }
180: end
# File markup/simple_markup/inline.rb, line 182
182: def convert_specials(str, attrs)
183: unless SPECIAL.empty?
184: SPECIAL.each do |regexp, attr|
185: str.scan(regexp) do
186: attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
187: end
188: end
189: end
190: end
# File markup/simple_markup/inline.rb, line 139
139: def copy_string(start_pos, end_pos)
140: res = @str[start_pos...end_pos]
141: res.gsub!(/\000/, '')
142: res
143: end
# File markup/simple_markup/inline.rb, line 263
263: def display_attributes
264: puts
265: puts @str.tr(NULL, "!")
266: bit = 1
267: 16.times do |bno|
268: line = ""
269: @str.length.times do |i|
270: if (@attrs[i] & bit) == 0
271: line << " "
272: else
273: if bno.zero?
274: line << "S"
275: else
276: line << ("%d" % (bno+1))
277: end
278: end
279: end
280: puts(line) unless line =~ /^ *$/
281: bit <<= 1
282: end
283: end
# File markup/simple_markup/inline.rb, line 246
246: def flow(str)
247: @str = str
248:
249: puts("Before flow, str='#{@str.dump}'") if $DEBUG
250: mask_protected_sequences
251:
252: @attrs = AttrSpan.new(@str.length)
253:
254: puts("After protecting, str='#{@str.dump}'") if $DEBUG
255: convert_attrs(@str, @attrs)
256: convert_html(@str, @attrs)
257: convert_specials(str, @attrs)
258: unmask_protected_sequences
259: puts("After flow, str='#{@str.dump}'") if $DEBUG
260: return split_into_flow
261: end
# File markup/simple_markup/inline.rb, line 199
199: def mask_protected_sequences
200: protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
201: @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
202: end
# File markup/simple_markup/inline.rb, line 285
285: def split_into_flow
286:
287: display_attributes if $DEBUG
288:
289: res = []
290: current_attr = 0
291: str = ""
292:
293:
294: str_len = @str.length
295:
296: # skip leading invisible text
297: i = 0
298: i += 1 while i < str_len and @str[i] == "\0"
299: start_pos = i
300:
301: # then scan the string, chunking it on attribute changes
302: while i < str_len
303: new_attr = @attrs[i]
304: if new_attr != current_attr
305: if i > start_pos
306: res << copy_string(start_pos, i)
307: start_pos = i
308: end
309:
310: res << change_attribute(current_attr, new_attr)
311: current_attr = new_attr
312:
313: if (current_attr & Attribute::SPECIAL) != 0
314: i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
315: res << Special.new(current_attr, copy_string(start_pos, i))
316: start_pos = i
317: next
318: end
319: end
320:
321: # move on, skipping any invisible characters
322: begin
323: i += 1
324: end while i < str_len and @str[i] == "\0"
325: end
326:
327: # tidy up trailing text
328: if start_pos < str_len
329: res << copy_string(start_pos, str_len)
330: end
331:
332: # and reset to all attributes off
333: res << change_attribute(current_attr, 0) if current_attr != 0
334:
335: return res
336: end