summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/nitro_xhtml.rb
blob: cf8c5aaf315a0a6f1b46df0cdf2a63fcb5bab6a7 (plain)
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
130
131
132
133
134
135
136
module CodeRay
module Scanners
  
  load :html
  load :ruby
  
  # Nitro XHTML Scanner
  # 
  # Alias: +nitro+
  class NitroXHTML < Scanner
    
    register_for :nitro_xhtml
    file_extension :xhtml
    title 'Nitro XHTML'
    
    KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
    
    NITRO_RUBY_BLOCK = /
      <\?r
      (?>
        [^\?]*
        (?> \?(?!>) [^\?]* )*
      )
      (?: \?> )?
    |
      <ruby>
      (?>
        [^<]*
        (?> <(?!\/ruby>) [^<]* )*
      )
      (?: <\/ruby> )?
    |
      <%
      (?>
        [^%]*
        (?> %(?!>) [^%]* )*
      )
      (?: %> )?
    /mx  # :nodoc:
    
    NITRO_VALUE_BLOCK = /
      \#
      (?:
        \{
        [^{}]*
        (?>
          \{ [^}]* \}
          (?> [^{}]* )
        )*
        \}?
      | \| [^|]* \|?
      | \( [^)]* \)?
      | \[ [^\]]* \]?
      | \\ [^\\]* \\?
      )
    /x  # :nodoc:
    
    NITRO_ENTITY = /
      % (?: \#\d+ | \w+ ) ;
    /  # :nodoc:
    
    START_OF_RUBY = /
      (?=[<\#%])
      < (?: \?r | % | ruby> )
    | \# [{(|]
    | % (?: \#\d+ | \w+ ) ;
    /x  # :nodoc:
    
    CLOSING_PAREN = Hash.new { |h, p| h[p] = p }  # :nodoc:
    CLOSING_PAREN.update( {
      '(' => ')',
      '[' => ']',
      '{' => '}',
    } )
    
  protected
    
    def setup
      @ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true
      @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
    end
    
    def reset_instance
      super
      @html_scanner.reset
    end
    
    def scan_tokens encoder, options
      
      until eos?
        
        if (match = scan_until(/(?=#{START_OF_RUBY})/o) || match = scan_until(/\z/)) and not match.empty?
          @html_scanner.tokenize match
          
        elsif match = scan(/#{NITRO_VALUE_BLOCK}/o)
          start_tag = match[0,2]
          delimiter = CLOSING_PAREN[start_tag[1,1]]
          end_tag = match[-1,1] == delimiter ? delimiter : ''
          encoder.begin_group :inline
          encoder.text_token start_tag, :inline_delimiter
          code = match[start_tag.size .. -1 - end_tag.size]
          @ruby_scanner.tokenize code, :tokens => encoder
          encoder.text_token end_tag, :inline_delimiter unless end_tag.empty?
          encoder.end_group :inline
          
        elsif match = scan(/#{NITRO_RUBY_BLOCK}/o)
          start_tag = '<?r'
          end_tag = match[-2,2] == '?>' ? '?>' : ''
          encoder.begin_group :inline
          encoder.text_token start_tag, :inline_delimiter
          code = match[start_tag.size .. -(end_tag.size)-1]
          @ruby_scanner.tokenize code, :tokens => encoder
          encoder.text_token end_tag, :inline_delimiter unless end_tag.empty?
          encoder.end_group :inline
          
        elsif entity = scan(/#{NITRO_ENTITY}/o)
          encoder.text_token entity, :entity
          
        elsif scan(/%/)
          encoder.text_token matched, :error
          
        else
          raise_inspect 'else-case reached!', encoder
          
        end
        
      end
      
      encoder
      
    end
    
  end
  
end
end