summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/nitro_html.rb
blob: 119924b91fdc7d9d66c4b61b7764e689b4e864a2 (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
module CodeRay
module Scanners

	load :html
	load :ruby

	# RHTML Scanner
	# 
	# $Id$
	class NitroHTML < Scanner

		include Streamable
		register_for :nitro_html

		NITRO_RUBY_BLOCK = /
			<\?r
			(?>
				[^\?]*
				(?> \?(?!>) [^\?]* )* 
			)
			(?: \?> )?
		|
			<ruby>
			(?>
				[^<]*
				(?> <(?!\/ruby>) [^<]* )* 
			)
			(?: <\/ruby> )?
		|
			<%
			(?>
				[^%]*
				(?> %(?!>) [^%]* )*
			)
			(?: %> )?
		/mx

		NITRO_VALUE_BLOCK = /
			\#
			(?:
				\{
				[^{}]*
				(?>
					\{ [^}]* \}
					(?> [^{}]* )
				)*
				\}?
			| \| [^|]* \|?
			| \( [^)]* \)?
			| \[ [^\]]* \]?
			| \\ [^\\]* \\?
			)
		/x

		NITRO_ENTITY = /
			% (?: \#\d+ | \w+ ) ;
		/

		START_OF_RUBY = /
			(?=[<\#%])
			< (?: \?r | % | ruby> )
		| \# [{(|]
		| % (?: \#\d+ | \w+ ) ;
		/x		

		CLOSING_PAREN = Hash.new do |h, p|
			h[p] = p
		end.update( {
			'(' => ')',
			'[' => ']',
			'{' => '}',
		} )
		
	private
		
		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 scan_tokens tokens, options
			
			until eos?

				if (match = scan_until(/(?=#{START_OF_RUBY})/o) || 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 : ''
					tokens << [:open, :inline]
					tokens << [start_tag, :delimiter]
					code = match[start_tag.size .. -1 - end_tag.size]
					@ruby_scanner.tokenize code
					tokens << [end_tag, :delimiter] unless end_tag.empty?
					tokens << [:close, :inline]

				elsif match = scan(/#{NITRO_RUBY_BLOCK}/o)
					start_tag = '<?r'
					end_tag = match[-2,2] == '?>' ? '?>' : ''
					tokens << [:open, :inline]
					tokens << [start_tag, :delimiter]
					code = match[start_tag.size .. -(end_tag.size)-1]
					@ruby_scanner.tokenize code
					tokens << [end_tag, :delimiter] unless end_tag.empty?
					tokens << [:close, :inline]

				elsif entity = scan(/#{NITRO_ENTITY}/o)
					tokens << [entity, :entity]

				else
					raise_inspect 'else-case reached!', tokens
				end

			end

			tokens

		end

	end

end
end