Skip to content

Commit d42887e

Browse files
committed
#353 New scanner: HAML!
1 parent 03583bb commit d42887e

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

Changes-1.0.textile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ h3. General changes
1515
* *NEW*: The new Diff scanner colorizes code inside of the diff, and highlights inline changes.
1616
* *NEW*: Extended support and usage of HTML5 and CSS 3 features.
1717
* *NEW*: Direct Streaming
18+
* *NEW* scanners: Clojure and HAML
1819
* *CHANGED*: Token classes (used as CSS classes) are readable names; breaks you stylesheet!
1920
* *IMPROVED* documentation
2021
* *IMPROVED* speed: faster startup (using @autoload@), scanning, and encoding
@@ -212,6 +213,13 @@ h3. @Scanners::Diff@
212213

213214
h3. *RENAMED*: @Scanners::ERB@ (was @Scanners::RHTML@)
214215

216+
h3. *NEW*: @Scanners::HAML@
217+
218+
It uses the new :state options of the HTML and Ruby scanners.
219+
220+
Some rare cases are not considered (like @#{...}@ snippets inside of :javascript blocks),
221+
but it highlights pretty well.
222+
215223
h3. @Scanners::HTML@
216224

217225
* *FIXED*: Closes open string groups.

lib/coderay/helpers/file_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def shebang filename
8888
'groovy' => :groovy,
8989
'gvy' => :groovy,
9090
'h' => :c,
91+
'haml' => :haml,
9192
'htm' => :page,
9293
'html' => :page,
9394
'html.erb' => :erb,

lib/coderay/scanners/haml.rb

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
module CodeRay
2+
module Scanners
3+
4+
load :ruby
5+
load :html
6+
load :java_script
7+
8+
class HAML < Scanner
9+
10+
register_for :haml
11+
title 'HAML Template'
12+
13+
KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
14+
15+
protected
16+
17+
def setup
18+
super
19+
@ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true
20+
@embedded_ruby_scanner = CodeRay.scanner :ruby, :tokens => @tokens, :keep_tokens => true, :state => @ruby_scanner.interpreted_string_state
21+
@html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true
22+
end
23+
24+
def scan_tokens encoder, options
25+
26+
match = nil
27+
code = ''
28+
29+
until eos?
30+
31+
if bol?
32+
if match = scan(/!!!.*/)
33+
encoder.text_token match, :doctype
34+
next
35+
end
36+
37+
if match = scan(/(?>( *)(\/(?!\[if)|-\#|:javascript|:ruby|:\w+) *)(?=\n)/)
38+
encoder.text_token match, :comment
39+
40+
code = self[2]
41+
if match = scan(/(?:\n+#{self[1]} .*)+/)
42+
case code
43+
when '/', '-#'
44+
encoder.text_token match, :comment
45+
when ':javascript'
46+
# TODO: recognize #{...} snippets inside JavaScript
47+
@java_script_scanner ||= CodeRay.scanner :java_script, :tokens => @tokens, :keep_tokens => true
48+
@java_script_scanner.tokenize match, :tokens => encoder
49+
when ':ruby'
50+
@ruby_scanner.tokenize match, :tokens => encoder
51+
when /:\w+/
52+
encoder.text_token match, :comment
53+
else
54+
raise 'else-case reached: %p' % [code]
55+
end
56+
end
57+
end
58+
59+
if match = scan(/ +/)
60+
encoder.text_token match, :space
61+
end
62+
63+
if match = scan(/\/.*/)
64+
encoder.text_token match, :comment
65+
next
66+
end
67+
68+
if match = scan(/\\/)
69+
encoder.text_token match, :plain
70+
if match = scan(/.+/)
71+
@html_scanner.tokenize match, :tokens => encoder
72+
end
73+
next
74+
end
75+
76+
tag = false
77+
78+
if match = scan(/%[\w:]+\/?/)
79+
encoder.text_token match, :tag
80+
# if match = scan(/( +)(.+)/)
81+
# encoder.text_token self[1], :space
82+
# @embedded_ruby_scanner.tokenize self[2], :tokens => encoder
83+
# end
84+
tag = true
85+
end
86+
87+
while match = scan(/([.#])[-\w]*\w/)
88+
encoder.text_token match, self[1] == '#' ? :constant : :class
89+
tag = true
90+
end
91+
92+
if tag && match = scan(/(\()([^)]+)?(\))?/)
93+
# TODO: recognize title=@title, class="widget_#{@widget.number}"
94+
encoder.text_token self[1], :plain
95+
@html_scanner.tokenize self[2], :tokens => encoder, :state => :attribute if self[2]
96+
encoder.text_token self[3], :plain if self[3]
97+
end
98+
99+
if tag && match = scan(/\{/)
100+
encoder.text_token match, :plain
101+
102+
code = ''
103+
level = 1
104+
while true
105+
code << scan(/([^\{\},\n]|, *\n?)*/)
106+
case match = getch
107+
when '{'
108+
level += 1
109+
code << match
110+
when '}'
111+
level -= 1
112+
if level > 0
113+
code << match
114+
else
115+
break
116+
end
117+
when "\n", ",", nil
118+
break
119+
end
120+
end
121+
@ruby_scanner.tokenize code, :tokens => encoder unless code.empty?
122+
123+
encoder.text_token match, :plain if match
124+
end
125+
126+
if tag && match = scan(/(\[)([^\]\n]+)?(\])?/)
127+
encoder.text_token self[1], :plain
128+
@ruby_scanner.tokenize self[2], :tokens => encoder if self[2]
129+
encoder.text_token self[3], :plain if self[3]
130+
end
131+
132+
if tag && match = scan(/\//)
133+
encoder.text_token match, :tag
134+
end
135+
136+
if scan(/(>?<?[-=]|[&!]=|(& |!)|~)( *)([^,\n\|]+(?:(, *|\|(?=.|\n.*\|$))\n?[^,\n\|]*)*)?/)
137+
encoder.text_token self[1] + self[3], :plain
138+
if self[4]
139+
if self[2]
140+
@embedded_ruby_scanner.tokenize self[4], :tokens => encoder
141+
else
142+
@ruby_scanner.tokenize self[4], :tokens => encoder
143+
end
144+
end
145+
elsif match = scan(/((?:<|><?)(?![!?\/\w]))?(.+)?/)
146+
encoder.text_token self[1], :plain if self[1]
147+
# TODO: recognize #{...} snippets
148+
@html_scanner.tokenize self[2], :tokens => encoder if self[2]
149+
end
150+
151+
elsif match = scan(/.+/)
152+
@html_scanner.tokenize match, :tokens => encoder
153+
154+
end
155+
156+
if match = scan(/\n/)
157+
encoder.text_token match, :space
158+
end
159+
end
160+
161+
encoder
162+
163+
end
164+
165+
end
166+
167+
end
168+
end

0 commit comments

Comments
 (0)