summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/nitro_html.rb
blob: 86d49929e01eef1b65c090244363363a0e2a4efc (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
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