coderay 0.8.357 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/README +4 -3
- data/lib/coderay.rb +2 -1
- data/lib/coderay/encoder.rb +41 -15
- data/lib/coderay/encoders/_map.rb +3 -1
- data/lib/coderay/encoders/comment_filter.rb +43 -0
- data/lib/coderay/encoders/div.rb +2 -3
- data/lib/coderay/encoders/filter.rb +75 -0
- data/lib/coderay/encoders/html.rb +20 -3
- data/lib/coderay/encoders/html/css.rb +1 -1
- data/lib/coderay/encoders/html/numerization.rb +11 -2
- data/lib/coderay/encoders/html/output.rb +10 -1
- data/lib/coderay/encoders/json.rb +69 -0
- data/lib/coderay/encoders/lines_of_code.rb +90 -0
- data/lib/coderay/encoders/page.rb +1 -2
- data/lib/coderay/encoders/span.rb +2 -3
- data/lib/coderay/encoders/term.rb +137 -0
- data/lib/coderay/encoders/text.rb +4 -4
- data/lib/coderay/encoders/token_class_filter.rb +84 -0
- data/lib/coderay/encoders/xml.rb +1 -0
- data/lib/coderay/for_redcloth.rb +9 -4
- data/lib/coderay/helpers/file_type.rb +54 -15
- data/lib/coderay/helpers/plugin.rb +21 -3
- data/lib/coderay/helpers/word_list.rb +19 -4
- data/lib/coderay/scanner.rb +33 -2
- data/lib/coderay/scanners/_map.rb +10 -4
- data/lib/coderay/scanners/c.rb +61 -23
- data/lib/coderay/scanners/cpp.rb +228 -0
- data/lib/coderay/scanners/css.rb +9 -1
- data/lib/coderay/scanners/debug.rb +1 -0
- data/lib/coderay/scanners/delphi.rb +2 -2
- data/lib/coderay/scanners/diff.rb +1 -0
- data/lib/coderay/scanners/groovy.rb +263 -0
- data/lib/coderay/scanners/html.rb +9 -2
- data/lib/coderay/scanners/java.rb +18 -14
- data/lib/coderay/scanners/java_script.rb +42 -13
- data/lib/coderay/scanners/json.rb +7 -1
- data/lib/coderay/scanners/nitro_xhtml.rb +4 -0
- data/lib/coderay/scanners/php.rb +526 -0
- data/lib/coderay/scanners/plaintext.rb +4 -1
- data/lib/coderay/scanners/python.rb +285 -0
- data/lib/coderay/scanners/rhtml.rb +3 -0
- data/lib/coderay/scanners/ruby.rb +29 -11
- data/lib/coderay/scanners/ruby/patterns.rb +26 -20
- data/lib/coderay/scanners/scheme.rb +3 -0
- data/lib/coderay/scanners/sql.rb +162 -0
- data/lib/coderay/scanners/xml.rb +1 -1
- data/lib/coderay/scanners/yaml.rb +4 -1
- data/lib/coderay/styles/cycnus.rb +11 -7
- data/lib/coderay/token_classes.rb +4 -1
- data/lib/coderay/tokens.rb +50 -46
- metadata +14 -4
- data/lib/coderay/encoders/tokens.rb +0 -44
data/lib/coderay/scanners/css.rb
CHANGED
@@ -5,6 +5,14 @@ module Scanners
|
|
5
5
|
|
6
6
|
register_for :css
|
7
7
|
|
8
|
+
KINDS_NOT_LOC = [
|
9
|
+
:comment,
|
10
|
+
:class, :pseudo_class, :type,
|
11
|
+
:constant, :directive,
|
12
|
+
:key, :value, :operator, :color, :float,
|
13
|
+
:error, :important,
|
14
|
+
]
|
15
|
+
|
8
16
|
module RE
|
9
17
|
NonASCII = /[\x80-\xFF]/
|
10
18
|
Hex = /[0-9a-fA-F]/
|
@@ -58,7 +66,7 @@ module Scanners
|
|
58
66
|
elsif case states.last
|
59
67
|
when :initial, :media
|
60
68
|
if scan(/(?>#{RE::Ident})(?!\()|\*/ox)
|
61
|
-
kind = :
|
69
|
+
kind = :type
|
62
70
|
elsif scan RE::Class
|
63
71
|
kind = :class
|
64
72
|
elsif scan RE::Id
|
@@ -30,11 +30,11 @@ module Scanners
|
|
30
30
|
'virtual', 'write', 'writeonly'
|
31
31
|
]
|
32
32
|
|
33
|
-
IDENT_KIND = CaseIgnoringWordList.new(:ident
|
33
|
+
IDENT_KIND = CaseIgnoringWordList.new(:ident).
|
34
34
|
add(RESERVED_WORDS, :reserved).
|
35
35
|
add(DIRECTIVES, :directive)
|
36
36
|
|
37
|
-
NAME_FOLLOWS = CaseIgnoringWordList.new(false
|
37
|
+
NAME_FOLLOWS = CaseIgnoringWordList.new(false).
|
38
38
|
add(%w(procedure function .))
|
39
39
|
|
40
40
|
private
|
@@ -0,0 +1,263 @@
|
|
1
|
+
module CodeRay
|
2
|
+
module Scanners
|
3
|
+
|
4
|
+
load :java
|
5
|
+
|
6
|
+
class Groovy < Java
|
7
|
+
|
8
|
+
include Streamable
|
9
|
+
register_for :groovy
|
10
|
+
|
11
|
+
# TODO: Check this!
|
12
|
+
GROOVY_KEYWORDS = %w[
|
13
|
+
as assert def in
|
14
|
+
]
|
15
|
+
KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
|
16
|
+
case instanceof new return throw typeof while as assert in
|
17
|
+
]
|
18
|
+
GROOVY_MAGIC_VARIABLES = %w[ it ]
|
19
|
+
|
20
|
+
IDENT_KIND = Java::IDENT_KIND.dup.
|
21
|
+
add(GROOVY_KEYWORDS, :keyword).
|
22
|
+
add(GROOVY_MAGIC_VARIABLES, :local_variable)
|
23
|
+
|
24
|
+
ESCAPE = / [bfnrtv$\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
|
25
|
+
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # no 4-byte unicode chars? U[a-fA-F0-9]{8}
|
26
|
+
REGEXP_ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | \d | [bBdDsSwW\/] /x
|
27
|
+
|
28
|
+
# TODO: interpretation inside ', ", /
|
29
|
+
STRING_CONTENT_PATTERN = {
|
30
|
+
"'" => /(?>\\[^\\'\n]+|[^\\'\n]+)+/,
|
31
|
+
'"' => /[^\\$"\n]+/,
|
32
|
+
"'''" => /(?>[^\\']+|'(?!''))+/,
|
33
|
+
'"""' => /(?>[^\\$"]+|"(?!""))+/,
|
34
|
+
'/' => /[^\\$\/\n]+/,
|
35
|
+
}
|
36
|
+
|
37
|
+
def scan_tokens tokens, options
|
38
|
+
|
39
|
+
state = :initial
|
40
|
+
inline_block_stack = []
|
41
|
+
inline_block_paren_depth = nil
|
42
|
+
string_delimiter = nil
|
43
|
+
import_clause = class_name_follows = last_token = after_def = false
|
44
|
+
value_expected = true
|
45
|
+
|
46
|
+
until eos?
|
47
|
+
|
48
|
+
kind = nil
|
49
|
+
match = nil
|
50
|
+
|
51
|
+
case state
|
52
|
+
|
53
|
+
when :initial
|
54
|
+
|
55
|
+
if match = scan(/ \s+ | \\\n /x)
|
56
|
+
tokens << [match, :space]
|
57
|
+
if match.index ?\n
|
58
|
+
import_clause = after_def = false
|
59
|
+
value_expected = true unless value_expected
|
60
|
+
end
|
61
|
+
next
|
62
|
+
|
63
|
+
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
|
64
|
+
value_expected = true
|
65
|
+
after_def = false
|
66
|
+
kind = :comment
|
67
|
+
|
68
|
+
elsif bol? && scan(/ \#!.* /x)
|
69
|
+
kind = :doctype
|
70
|
+
|
71
|
+
elsif import_clause && scan(/ (?!as) #{IDENT} (?: \. #{IDENT} )* (?: \.\* )? /ox)
|
72
|
+
after_def = value_expected = false
|
73
|
+
kind = :include
|
74
|
+
|
75
|
+
elsif match = scan(/ #{IDENT} | \[\] /ox)
|
76
|
+
kind = IDENT_KIND[match]
|
77
|
+
value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
|
78
|
+
if last_token == '.'
|
79
|
+
kind = :ident
|
80
|
+
elsif class_name_follows
|
81
|
+
kind = :class
|
82
|
+
class_name_follows = false
|
83
|
+
elsif after_def && check(/\s*[({]/)
|
84
|
+
kind = :method
|
85
|
+
after_def = false
|
86
|
+
elsif kind == :ident && last_token != '?' && check(/:/)
|
87
|
+
kind = :key
|
88
|
+
else
|
89
|
+
class_name_follows = true if match == 'class' || (import_clause && match == 'as')
|
90
|
+
import_clause = match == 'import'
|
91
|
+
after_def = true if match == 'def'
|
92
|
+
end
|
93
|
+
|
94
|
+
elsif scan(/;/)
|
95
|
+
import_clause = after_def = false
|
96
|
+
value_expected = true
|
97
|
+
kind = :operator
|
98
|
+
|
99
|
+
elsif scan(/\{/)
|
100
|
+
class_name_follows = after_def = false
|
101
|
+
value_expected = true
|
102
|
+
kind = :operator
|
103
|
+
if !inline_block_stack.empty?
|
104
|
+
inline_block_paren_depth += 1
|
105
|
+
end
|
106
|
+
|
107
|
+
# TODO: ~'...', ~"..." and ~/.../ style regexps
|
108
|
+
elsif match = scan(/ \.\.<? | \*?\.(?!\d)@? | \.& | \?:? | [,?:(\[] | -[->] | \+\+ |
|
109
|
+
&& | \|\| | \*\*=? | ==?~ | <=?>? | [-+*%^~&|>=!]=? | <<<?=? | >>>?=? /x)
|
110
|
+
value_expected = true
|
111
|
+
value_expected = :regexp if match == '~'
|
112
|
+
after_def = false
|
113
|
+
kind = :operator
|
114
|
+
|
115
|
+
elsif match = scan(/ [)\]}] /x)
|
116
|
+
value_expected = after_def = false
|
117
|
+
if !inline_block_stack.empty? && match == '}'
|
118
|
+
inline_block_paren_depth -= 1
|
119
|
+
if inline_block_paren_depth == 0 # closing brace of inline block reached
|
120
|
+
tokens << [match, :inline_delimiter]
|
121
|
+
tokens << [:close, :inline]
|
122
|
+
state, string_delimiter, inline_block_paren_depth = inline_block_stack.pop
|
123
|
+
next
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
elsif check(/[\d.]/)
|
128
|
+
after_def = value_expected = false
|
129
|
+
if scan(/0[xX][0-9A-Fa-f]+/)
|
130
|
+
kind = :hex
|
131
|
+
elsif scan(/(?>0[0-7]+)(?![89.eEfF])/)
|
132
|
+
kind = :oct
|
133
|
+
elsif scan(/\d+[fFdD]|\d*\.\d+(?:[eE][+-]?\d+)?[fFdD]?|\d+[eE][+-]?\d+[fFdD]?/)
|
134
|
+
kind = :float
|
135
|
+
elsif scan(/\d+[lLgG]?/)
|
136
|
+
kind = :integer
|
137
|
+
end
|
138
|
+
|
139
|
+
elsif match = scan(/'''|"""/)
|
140
|
+
after_def = value_expected = false
|
141
|
+
state = :multiline_string
|
142
|
+
tokens << [:open, :string]
|
143
|
+
string_delimiter = match
|
144
|
+
kind = :delimiter
|
145
|
+
|
146
|
+
# TODO: record.'name'
|
147
|
+
elsif match = scan(/["']/)
|
148
|
+
after_def = value_expected = false
|
149
|
+
state = match == '/' ? :regexp : :string
|
150
|
+
tokens << [:open, state]
|
151
|
+
string_delimiter = match
|
152
|
+
kind = :delimiter
|
153
|
+
|
154
|
+
elsif value_expected && (match = scan(/\//))
|
155
|
+
after_def = value_expected = false
|
156
|
+
tokens << [:open, :regexp]
|
157
|
+
state = :regexp
|
158
|
+
string_delimiter = '/'
|
159
|
+
kind = :delimiter
|
160
|
+
|
161
|
+
elsif scan(/ @ #{IDENT} /ox)
|
162
|
+
after_def = value_expected = false
|
163
|
+
kind = :annotation
|
164
|
+
|
165
|
+
elsif scan(/\//)
|
166
|
+
after_def = false
|
167
|
+
value_expected = true
|
168
|
+
kind = :operator
|
169
|
+
|
170
|
+
else
|
171
|
+
getch
|
172
|
+
kind = :error
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
when :string, :regexp, :multiline_string
|
177
|
+
if scan(STRING_CONTENT_PATTERN[string_delimiter])
|
178
|
+
kind = :content
|
179
|
+
|
180
|
+
elsif match = scan(state == :multiline_string ? /'''|"""/ : /["'\/]/)
|
181
|
+
tokens << [match, :delimiter]
|
182
|
+
if state == :regexp
|
183
|
+
# TODO: regexp modifiers? s, m, x, i?
|
184
|
+
modifiers = scan(/[ix]+/)
|
185
|
+
tokens << [modifiers, :modifier] if modifiers && !modifiers.empty?
|
186
|
+
end
|
187
|
+
state = :string if state == :multiline_string
|
188
|
+
tokens << [:close, state]
|
189
|
+
string_delimiter = nil
|
190
|
+
after_def = value_expected = false
|
191
|
+
state = :initial
|
192
|
+
next
|
193
|
+
|
194
|
+
elsif (state == :string || state == :multiline_string) &&
|
195
|
+
(match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
|
196
|
+
if string_delimiter[0] == ?' && !(match == "\\\\" || match == "\\'")
|
197
|
+
kind = :content
|
198
|
+
else
|
199
|
+
kind = :char
|
200
|
+
end
|
201
|
+
elsif state == :regexp && scan(/ \\ (?: #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
|
202
|
+
kind = :char
|
203
|
+
|
204
|
+
elsif match = scan(/ \$ #{IDENT} /mox)
|
205
|
+
tokens << [:open, :inline]
|
206
|
+
tokens << ['$', :inline_delimiter]
|
207
|
+
match = match[1..-1]
|
208
|
+
tokens << [match, IDENT_KIND[match]]
|
209
|
+
tokens << [:close, :inline]
|
210
|
+
next
|
211
|
+
elsif match = scan(/ \$ \{ /x)
|
212
|
+
tokens << [:open, :inline]
|
213
|
+
tokens << ['${', :inline_delimiter]
|
214
|
+
inline_block_stack << [state, string_delimiter, inline_block_paren_depth]
|
215
|
+
inline_block_paren_depth = 1
|
216
|
+
state = :initial
|
217
|
+
next
|
218
|
+
|
219
|
+
elsif scan(/ \$ /mx)
|
220
|
+
kind = :content
|
221
|
+
|
222
|
+
elsif scan(/ \\. /mx)
|
223
|
+
kind = :content
|
224
|
+
|
225
|
+
elsif scan(/ \\ | \n /x)
|
226
|
+
tokens << [:close, state]
|
227
|
+
kind = :error
|
228
|
+
after_def = value_expected = false
|
229
|
+
state = :initial
|
230
|
+
|
231
|
+
else
|
232
|
+
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
233
|
+
end
|
234
|
+
|
235
|
+
else
|
236
|
+
raise_inspect 'Unknown state', tokens
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
match ||= matched
|
241
|
+
if $DEBUG and not kind
|
242
|
+
raise_inspect 'Error token %p in line %d' %
|
243
|
+
[[match, kind], line], tokens
|
244
|
+
end
|
245
|
+
raise_inspect 'Empty token', tokens unless match
|
246
|
+
|
247
|
+
last_token = match unless [:space, :comment, :doctype].include? kind
|
248
|
+
|
249
|
+
tokens << [match, kind]
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
if [:multiline_string, :string, :regexp].include? state
|
254
|
+
tokens << [:close, state]
|
255
|
+
end
|
256
|
+
|
257
|
+
tokens
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end
|
@@ -6,6 +6,13 @@ module Scanners
|
|
6
6
|
|
7
7
|
include Streamable
|
8
8
|
register_for :html
|
9
|
+
|
10
|
+
KINDS_NOT_LOC = [
|
11
|
+
:comment, :doctype, :preprocessor,
|
12
|
+
:tag, :attribute_name, :operator,
|
13
|
+
:attribute_value, :delimiter, :content,
|
14
|
+
:plain, :entity, :error
|
15
|
+
]
|
9
16
|
|
10
17
|
ATTR_NAME = /[\w.:-]+/
|
11
18
|
ATTR_VALUE_UNQUOTED = ATTR_NAME
|
@@ -68,9 +75,9 @@ module Scanners
|
|
68
75
|
kind = :preprocessor
|
69
76
|
elsif scan(/<\?.*?\?>|<%.*?%>/m)
|
70
77
|
kind = :comment
|
71
|
-
elsif scan(/<\/[-\
|
78
|
+
elsif scan(/<\/[-\w.:]*>/m)
|
72
79
|
kind = :tag
|
73
|
-
elsif match = scan(/<[-\
|
80
|
+
elsif match = scan(/<[-\w.:]+>?/m)
|
74
81
|
kind = :tag
|
75
82
|
state = :attribute unless match[-1] == ?>
|
76
83
|
elsif scan(/[^<>&]+/)
|
@@ -7,30 +7,33 @@ module Scanners
|
|
7
7
|
register_for :java
|
8
8
|
helper :builtin_types
|
9
9
|
|
10
|
-
#
|
10
|
+
# http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
|
11
11
|
KEYWORDS = %w[
|
12
|
-
break case catch continue default do else
|
13
|
-
|
14
|
-
return switch throw
|
15
|
-
debugger export
|
12
|
+
assert break case catch continue default do else
|
13
|
+
finally for if instanceof import new package
|
14
|
+
return switch throw try typeof while
|
15
|
+
debugger export
|
16
16
|
]
|
17
|
-
|
17
|
+
RESERVED = %w[ const goto ]
|
18
|
+
CONSTANTS = %w[ false null true ]
|
18
19
|
MAGIC_VARIABLES = %w[ this super ]
|
19
20
|
TYPES = %w[
|
20
|
-
boolean byte char class
|
21
|
-
|
21
|
+
boolean byte char class double enum float int interface long
|
22
|
+
short void
|
23
|
+
] << '[]' # because int[] should be highlighted as a type
|
22
24
|
DIRECTIVES = %w[
|
23
25
|
abstract extends final implements native private protected public
|
24
|
-
static strictfp synchronized
|
26
|
+
static strictfp synchronized throws transient volatile
|
25
27
|
]
|
26
28
|
|
27
|
-
# Reserved for future use.
|
28
|
-
|
29
29
|
IDENT_KIND = WordList.new(:ident).
|
30
30
|
add(KEYWORDS, :keyword).
|
31
|
+
add(RESERVED, :reserved).
|
32
|
+
add(CONSTANTS, :pre_constant).
|
31
33
|
add(MAGIC_VARIABLES, :local_variable).
|
32
34
|
add(TYPES, :type).
|
33
35
|
add(BuiltinTypes::List, :pre_type).
|
36
|
+
add(BuiltinTypes::List.select { |builtin| builtin[/(Error|Exception)$/] }, :exception).
|
34
37
|
add(DIRECTIVES, :directive)
|
35
38
|
|
36
39
|
ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
|
@@ -61,8 +64,9 @@ module Scanners
|
|
61
64
|
tokens << [match, :space]
|
62
65
|
next
|
63
66
|
|
64
|
-
elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
|
65
|
-
|
67
|
+
elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
|
68
|
+
tokens << [match, :comment]
|
69
|
+
next
|
66
70
|
|
67
71
|
elsif import_clause && scan(/ #{IDENT} (?: \. #{IDENT} )* /ox)
|
68
72
|
kind = :include
|
@@ -79,7 +83,7 @@ module Scanners
|
|
79
83
|
class_name_follows = true if match == 'class' || match == 'interface'
|
80
84
|
end
|
81
85
|
|
82
|
-
elsif scan(/ \.(?!\d) | [,?:(\[
|
86
|
+
elsif scan(/ \.(?!\d) | [,?:()\[\]}] | -- | \+\+ | && | \|\| | \*\*=? | [-+*\/%^~&|<>=!]=? | <<<?=? | >>>?=? /x)
|
83
87
|
kind = :operator
|
84
88
|
|
85
89
|
elsif scan(/;/)
|
@@ -11,14 +11,17 @@ module Scanners
|
|
11
11
|
# The actual JavaScript keywords.
|
12
12
|
KEYWORDS = %w[
|
13
13
|
break case catch continue default delete do else
|
14
|
-
|
15
|
-
return switch throw
|
14
|
+
finally for function if in instanceof new
|
15
|
+
return switch throw try typeof var void while with
|
16
|
+
]
|
17
|
+
PREDEFINED_CONSTANTS = %w[
|
18
|
+
false null true undefined
|
16
19
|
]
|
17
20
|
|
18
21
|
MAGIC_VARIABLES = %w[ this arguments ] # arguments was introduced in JavaScript 1.4
|
19
22
|
|
20
23
|
KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
|
21
|
-
case delete in instanceof new return throw typeof
|
24
|
+
case delete in instanceof new return throw typeof with
|
22
25
|
]
|
23
26
|
|
24
27
|
# Reserved for future use.
|
@@ -31,6 +34,7 @@ module Scanners
|
|
31
34
|
|
32
35
|
IDENT_KIND = WordList.new(:ident).
|
33
36
|
add(RESERVED_WORDS, :reserved).
|
37
|
+
add(PREDEFINED_CONSTANTS, :pre_constant).
|
34
38
|
add(MAGIC_VARIABLES, :local_variable).
|
35
39
|
add(KEYWORDS, :keyword)
|
36
40
|
|
@@ -53,6 +57,7 @@ module Scanners
|
|
53
57
|
string_delimiter = nil
|
54
58
|
value_expected = true
|
55
59
|
key_expected = false
|
60
|
+
function_expected = false
|
56
61
|
|
57
62
|
until eos?
|
58
63
|
|
@@ -72,7 +77,7 @@ module Scanners
|
|
72
77
|
value_expected = true
|
73
78
|
kind = :comment
|
74
79
|
|
75
|
-
elsif check(
|
80
|
+
elsif check(/\.?\d/)
|
76
81
|
key_expected = value_expected = false
|
77
82
|
if scan(/0[xX][0-9A-Fa-f]+/)
|
78
83
|
kind = :hex
|
@@ -83,29 +88,42 @@ module Scanners
|
|
83
88
|
elsif scan(/\d+/)
|
84
89
|
kind = :integer
|
85
90
|
end
|
91
|
+
|
92
|
+
elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
|
93
|
+
# FIXME: scan over nested tags
|
94
|
+
xml_scanner.tokenize match
|
95
|
+
value_expected = false
|
96
|
+
next
|
86
97
|
|
87
98
|
elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
|
88
99
|
value_expected = true
|
89
100
|
last_operator = match[-1]
|
90
101
|
key_expected = (last_operator == ?{) || (last_operator == ?,)
|
102
|
+
function_expected = false
|
91
103
|
kind = :operator
|
92
104
|
|
93
105
|
elsif scan(/ [)\]}]+ /x)
|
94
|
-
key_expected = value_expected = false
|
106
|
+
function_expected = key_expected = value_expected = false
|
95
107
|
kind = :operator
|
96
108
|
|
97
109
|
elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
|
98
110
|
kind = IDENT_KIND[match]
|
99
111
|
value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
|
112
|
+
# TODO: labels
|
100
113
|
if kind == :ident
|
101
|
-
if match.index(?$)
|
114
|
+
if match.index(?$) # $ allowed inside an identifier
|
102
115
|
kind = :predefined
|
116
|
+
elsif function_expected
|
117
|
+
kind = :function
|
118
|
+
elsif check(/\s*[=:]\s*function\b/)
|
119
|
+
kind = :function
|
103
120
|
elsif key_expected && check(/\s*:/)
|
104
121
|
kind = :key
|
105
122
|
end
|
106
123
|
end
|
124
|
+
function_expected = (kind == :keyword) && (match == 'function')
|
107
125
|
key_expected = false
|
108
|
-
|
126
|
+
|
109
127
|
elsif match = scan(/["']/)
|
110
128
|
if key_expected && check(KEY_CHECK_PATTERN[match])
|
111
129
|
state = :key
|
@@ -158,25 +176,25 @@ module Scanners
|
|
158
176
|
elsif scan(/\\./m)
|
159
177
|
kind = :content
|
160
178
|
elsif scan(/ \\ | $ /x)
|
161
|
-
tokens << [:close,
|
179
|
+
tokens << [:close, state]
|
162
180
|
kind = :error
|
163
181
|
key_expected = value_expected = false
|
164
182
|
state = :initial
|
165
183
|
else
|
166
|
-
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
184
|
+
raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
|
167
185
|
end
|
168
186
|
|
169
187
|
else
|
170
|
-
raise_inspect 'Unknown state', tokens
|
188
|
+
raise_inspect 'Unknown state', tokens
|
171
189
|
|
172
190
|
end
|
173
191
|
|
174
192
|
match ||= matched
|
175
193
|
if $DEBUG and not kind
|
176
194
|
raise_inspect 'Error token %p in line %d' %
|
177
|
-
[[match, kind], line], tokens
|
195
|
+
[[match, kind], line], tokens
|
178
196
|
end
|
179
|
-
raise_inspect 'Empty token', tokens
|
197
|
+
raise_inspect 'Empty token', tokens unless match
|
180
198
|
|
181
199
|
tokens << [match, kind]
|
182
200
|
|
@@ -189,7 +207,18 @@ module Scanners
|
|
189
207
|
tokens
|
190
208
|
end
|
191
209
|
|
192
|
-
|
210
|
+
protected
|
193
211
|
|
212
|
+
def reset_instance
|
213
|
+
super
|
214
|
+
@xml_scanner.reset if defined? @xml_scanner
|
215
|
+
end
|
216
|
+
|
217
|
+
def xml_scanner
|
218
|
+
@xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => true
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
194
223
|
end
|
195
224
|
end
|