lib/maruku/attributes.rb in maruku-0.6.1 vs lib/maruku/attributes.rb in maruku-0.7.0.beta1
- old
+ new
@@ -1,227 +1,122 @@
-#--
-# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
-#
-# This file is part of Maruku.
-#
-# Maruku is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# Maruku is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Maruku; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-#++
+module MaRuKu
+ # This represents a list of attributes specified in the Markdown document
+ # that apply to a Markdown-generated tag.
+ # What was `{#id .class key="val" ref}` in the Markdown
+ # is parsed into `[[:id, 'id'], [:class, 'class'], ['key', 'val'], [:ref, 'ref']]`.
+ class AttributeList < Array
+ def to_s
+ map do |k, v|
+ value = quote_if_needed(v)
+ case k
+ when :id; "#" + value
+ when :class; "." + value
+ when :ref; value
+ else quote_if_needed(k) + "=" + value
+ end
+ end.join(' ')
+ end
+ alias to_md to_s
+ private
-class String
- def quote_if_needed
- if /[\s\'\"]/.match self
- inspect
- else
- self
- end
- end
-end
+ def quote_if_needed(str)
+ (str =~ /[\s'"]/) ? str.inspect : str
+ end
+ end
-module MaRuKu;
- MagicChar = ':'
-
- class AttributeList < Array
-
- # An attribute list becomes
- # {#id .cl key="val" ref}
- # [ [:id, 'id'], [:class, 'id'], ['key', 'val'], [ :ref, 'ref' ]]
+ module In::Markdown::SpanLevelParser
+ def md_al(s = [])
+ AttributeList.new(s)
+ end
- private :push
-
- def push_key_val(key, val);
- raise "Bad #{key.inspect}=#{val.inspect}" if not key and val
- push [key, val]
- end
- def push_ref(ref_id);
-
- raise "Bad :ref #{ref_id.inspect}" if not ref_id
- push [:ref, ref_id+""]
+ # @return [AttributeList, nil]
+ def read_attribute_list(src, con=nil, break_on_chars=nil)
+ break_on_chars = Array(break_on_chars)
+ separators = break_on_chars + ['=', ' ', "\t"]
+ escaped = Maruku::EscapedCharInQuotes
-# p "Now ", self ########################################
- end
- def push_class(val);
- raise "Bad :id #{val.inspect}" if not val
- push [:class, val]
- end
- def push_id(val);
- raise "Bad :id #{val.inspect}" if not val
- push [:id, val]
- end
-
- def to_s
- map do |k,v|
- case k
- when :id; "#" + v.quote_if_needed
- when :class; "." + v.quote_if_needed
- when :ref; v.quote_if_needed
- else k.quote_if_needed + "=" + v.quote_if_needed
- end
- end . join(' ')
- end
- alias to_md to_s
- end
-
-end
+ al = AttributeList.new
+ loop do
+ src.consume_whitespace
+ break if break_on_chars.include? src.cur_char
-module MaRuKu; module In; module Markdown; module SpanLevelParser
-
- def unit_tests_for_attribute_lists
- [
- [ "", [], "Empty lists are allowed" ],
- [ "=", :throw, "Bad char to begin a list with." ],
- [ "a =b", :throw, "No whitespace before `=`." ],
- [ "a= b", :throw, "No whitespace after `=`." ],
+ case src.cur_char
+ when ':'
+ src.ignore_char
+ when nil
+ break # we're done here.
+ when '=' # error
+ src.ignore_char
+ maruku_error "In attribute lists, cannot start identifier with `=`."
+ tell_user "Ignoring and continuing."
+ when '#' # id definition
+ src.ignore_char
+ if id = read_quoted_or_unquoted(src, con, escaped, separators)
+ al << [:id, id]
+ else
+ maruku_error 'Could not read `id` attribute.', src, con
+ tell_user 'Ignoring bad `id` attribute.'
+ end
+ when '.' # class definition
+ src.ignore_char
+ if klass = read_quoted_or_unquoted(src, con, escaped, separators)
+ al << [:class, klass]
+ else
+ maruku_error 'Could not read `class` attribute.', src, con
+ tell_user 'Ignoring bad `class` attribute.'
+ end
+ else
+ unless key = read_quoted_or_unquoted(src, con, escaped, separators)
+ maruku_error 'Could not read key or reference.'
+ next
+ end
- [ "a b", [[:ref, 'a'],[:ref, 'b']], "More than one ref" ],
- [ "a b c", [[:ref, 'a'],[:ref, 'b'],[:ref, 'c']], "More than one ref" ],
- [ "hello notfound", [[:ref, 'hello'],[:ref, 'notfound']]],
+ if src.cur_char != '=' && key.length > 0
+ al << [:ref, key]
+ next
+ end
- [ "'a'", [[:ref, 'a']], "Quoted value." ],
- [ '"a"' ],
+ src.ignore_char # skip the =
+ if val = read_quoted_or_unquoted(src, con, escaped, separators)
+ al << [key, val]
+ else
+ maruku_error "Could not read value for key #{key.inspect}.", src, con
+ tell_user "Ignoring key #{key.inspect}"
+ end
+ end
+ end
+ al
+ end
- [ "a=b", [['a','b']], "Simple key/val" ],
- [ "'a'=b" ],
- [ "'a'='b'" ],
- [ "a='b'" ],
+ def merge_ial(elements, src, con)
+ # Apply each IAL to the element before
+ (elements + [nil]).each_cons(3) do |before, e, after|
+ next unless ial?(e)
- [ 'a="b\'"', [['a',"b\'"]], "Key/val with quotes" ],
- [ 'a=b\''],
- [ 'a="\\\'b\'"', [['a',"\'b\'"]], "Key/val with quotes" ],
-
- ['"', :throw, "Unclosed quotes"],
- ["'"],
- ["'a "],
- ['"a '],
-
- [ "#a", [[:id, 'a']], "Simple ID" ],
- [ "#'a'" ],
- [ '#"a"' ],
+ if before.kind_of? MDElement
+ before.al = e.ial
+ elsif after.kind_of? MDElement
+ after.al = e.ial
+ else
+ maruku_error <<ERR, src, con
+It's unclear which element the attribute list {:#{e.ial.to_s}}
+is referring to. The element before is a #{before.class},
+the element after is a #{after.class}.
+ before: #{before.inspect}
+ after: #{after.inspect}
+ERR
+ end
+ end
- [ "#", :throw, "Unfinished '#'." ],
- [ ".", :throw, "Unfinished '.'." ],
- [ "# a", :throw, "No white-space after '#'." ],
- [ ". a", :throw, "No white-space after '.' ." ],
-
- [ "a=b c=d", [['a','b'],['c','d']], "Tabbing" ],
- [ " \ta=b \tc='d' "],
- [ "\t a=b\t c='d'\t\t"],
-
- [ ".\"a'", :throw, "Mixing quotes is bad." ],
-
- ].map { |s, expected, comment|
- @expected = (expected ||= @expected)
- @comment = (comment ||= (last=@comment) )
- (comment == last && (comment += (@count+=1).to_s)) || @count = 1
- expected = [md_ial(expected)] if expected.kind_of? Array
- ["{#{MagicChar}#{s}}", expected, "Attributes: #{comment}"]
- }
- end
-
- def md_al(s=[]); AttributeList.new(s) end
+ unless Globals[:debug_keep_ials]
+ elements.delete_if {|x| ial?(x) && x != elements.first}
+ end
+ end
- # returns nil or an AttributeList
- def read_attribute_list(src, con, break_on_chars)
-
- separators = break_on_chars + [?=,?\ ,?\t]
- escaped = Maruku::EscapedCharInQuotes
-
- al = AttributeList.new
- while true
- src.consume_whitespace
- break if break_on_chars.include? src.cur_char
-
- case src.cur_char
- when nil
- maruku_error "Attribute list terminated by EOF:\n "+
- "#{al.inspect}" , src, con
- tell_user "I try to continue and return partial attribute list:\n"+
- al.inspect
- break
- when ?= # error
- maruku_error "In attribute lists, cannot start identifier with `=`."
- tell_user "I try to continue"
- src.ignore_char
- when ?# # id definition
- src.ignore_char
- if id = read_quoted_or_unquoted(src, con, escaped, separators)
- al.push_id id
- else
- maruku_error 'Could not read `id` attribute.', src, con
- tell_user 'Trying to ignore bad `id` attribute.'
- end
- when ?. # class definition
- src.ignore_char
- if klass = read_quoted_or_unquoted(src, con, escaped, separators)
- al.push_class klass
- else
- maruku_error 'Could not read `class` attribute.', src, con
- tell_user 'Trying to ignore bad `class` attribute.'
- end
- else
- if key = read_quoted_or_unquoted(src, con, escaped, separators)
- if src.cur_char == ?=
- src.ignore_char # skip the =
- if val = read_quoted_or_unquoted(src, con, escaped, separators)
- al.push_key_val(key, val)
- else
- maruku_error "Could not read value for key #{key.inspect}.",
- src, con
- tell_user "Ignoring key #{key.inspect}."
- end
- else
- al.push_ref key
- end
- else
- maruku_error 'Could not read key or reference.'
- end
- end # case
- end # while true
- al
- end
-
-
- # We need a helper
- def is_ial(e); e.kind_of? MDElement and e.node_type == :ial end
+ private
- def merge_ial(elements, src, con)
-
- # Apply each IAL to the element before
- elements.each_with_index do |e, i|
- if is_ial(e) && i>= 1 then
- before = elements[i-1]
- after = elements[i+1]
- if before.kind_of? MDElement
- before.al = e.ial
- elsif after.kind_of? MDElement
- after.al = e.ial
- else
- maruku_error "It is not clear to me what element this IAL {:#{e.ial.to_md}} \n"+
- "is referring to. The element before is a #{before.class.to_s}, \n"+
- "the element after is a #{after.class.to_s}.\n"+
- "\n before: #{before.inspect}"+
- "\n after: #{after.inspect}",
- src, con
- # xxx dire se c'รจ empty vicino
- end
- end
- end
-
- if not Globals[:debug_keep_ials]
- elements.delete_if {|x| is_ial(x) unless x == elements.first}
- end
- end
-
-end end end end
-#module MaRuKu; module In; module Markdown; module SpanLevelParser
+ def ial?(e)
+ e.is_a?(MDElement) && e.node_type == :ial
+ end
+ end
+end