coderay 1.0.9 → 1.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Rakefile +2 -0
- data/bin/coderay +4 -4
- data/lib/coderay.rb +2 -3
- data/lib/coderay/encoders/debug.rb +5 -17
- data/lib/coderay/encoders/debug_lint.rb +62 -0
- data/lib/coderay/encoders/html.rb +84 -84
- data/lib/coderay/encoders/html/css.rb +7 -7
- data/lib/coderay/encoders/html/numbering.rb +24 -19
- data/lib/coderay/encoders/html/output.rb +1 -1
- data/lib/coderay/encoders/lint.rb +57 -0
- data/lib/coderay/encoders/statistic.rb +0 -1
- data/lib/coderay/encoders/terminal.rb +121 -105
- data/lib/coderay/helpers/file_type.rb +54 -47
- data/lib/coderay/helpers/plugin.rb +4 -13
- data/lib/coderay/scanner.rb +58 -26
- data/lib/coderay/scanners/c.rb +1 -1
- data/lib/coderay/scanners/cpp.rb +1 -1
- data/lib/coderay/scanners/css.rb +22 -25
- data/lib/coderay/scanners/diff.rb +53 -31
- data/lib/coderay/scanners/groovy.rb +17 -4
- data/lib/coderay/scanners/html.rb +38 -16
- data/lib/coderay/scanners/java.rb +1 -1
- data/lib/coderay/scanners/java_script.rb +30 -6
- data/lib/coderay/scanners/json.rb +15 -12
- data/lib/coderay/scanners/lua.rb +280 -0
- data/lib/coderay/scanners/php.rb +22 -4
- data/lib/coderay/scanners/python.rb +3 -3
- data/lib/coderay/scanners/raydebug.rb +8 -8
- data/lib/coderay/scanners/ruby.rb +2 -2
- data/lib/coderay/scanners/sass.rb +232 -0
- data/lib/coderay/scanners/sql.rb +7 -4
- data/lib/coderay/scanners/taskpaper.rb +36 -0
- data/lib/coderay/scanners/yaml.rb +2 -2
- data/lib/coderay/styles/alpha.rb +31 -21
- data/lib/coderay/token_kinds.rb +68 -71
- data/lib/coderay/tokens.rb +23 -77
- data/lib/coderay/version.rb +1 -1
- data/test/functional/examples.rb +3 -3
- data/test/functional/for_redcloth.rb +4 -10
- metadata +13 -14
- data/lib/coderay/helpers/gzip.rb +0 -41
@@ -1,13 +1,13 @@
|
|
1
1
|
module CodeRay
|
2
2
|
module Scanners
|
3
|
-
|
3
|
+
|
4
4
|
# HTML Scanner
|
5
5
|
#
|
6
6
|
# Alias: +xhtml+
|
7
7
|
#
|
8
8
|
# See also: Scanners::XML
|
9
9
|
class HTML < Scanner
|
10
|
-
|
10
|
+
|
11
11
|
register_for :html
|
12
12
|
|
13
13
|
KINDS_NOT_LOC = [
|
@@ -33,7 +33,8 @@ module Scanners
|
|
33
33
|
)
|
34
34
|
|
35
35
|
IN_ATTRIBUTE = WordList::CaseIgnoring.new(nil).
|
36
|
-
add(EVENT_ATTRIBUTES, :script)
|
36
|
+
add(EVENT_ATTRIBUTES, :script).
|
37
|
+
add(['style'], :style)
|
37
38
|
|
38
39
|
ATTR_NAME = /[\w.:-]+/ # :nodoc:
|
39
40
|
TAG_END = /\/?>/ # :nodoc:
|
@@ -75,9 +76,14 @@ module Scanners
|
|
75
76
|
def scan_java_script encoder, code
|
76
77
|
if code && !code.empty?
|
77
78
|
@java_script_scanner ||= Scanners::JavaScript.new '', :keep_tokens => true
|
78
|
-
# encoder.begin_group :inline
|
79
79
|
@java_script_scanner.tokenize code, :tokens => encoder
|
80
|
-
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def scan_css encoder, code, state = [:initial]
|
84
|
+
if code && !code.empty?
|
85
|
+
@css_scanner ||= Scanners::CSS.new '', :keep_tokens => true
|
86
|
+
@css_scanner.tokenize code, :tokens => encoder, :state => state
|
81
87
|
end
|
82
88
|
end
|
83
89
|
|
@@ -99,7 +105,15 @@ module Scanners
|
|
99
105
|
case state
|
100
106
|
|
101
107
|
when :initial
|
102
|
-
if match = scan(
|
108
|
+
if match = scan(/<!\[CDATA\[/)
|
109
|
+
encoder.text_token match, :inline_delimiter
|
110
|
+
if match = scan(/.*?\]\]>/m)
|
111
|
+
encoder.text_token match[0..-4], :plain
|
112
|
+
encoder.text_token ']]>', :inline_delimiter
|
113
|
+
elsif match = scan(/.+/)
|
114
|
+
encoder.text_token match, :error
|
115
|
+
end
|
116
|
+
elsif match = scan(/<!--(?:.*?-->|.*)/m)
|
103
117
|
encoder.text_token match, :comment
|
104
118
|
elsif match = scan(/<!(\w+)(?:.*?>|.*)|\]>/m)
|
105
119
|
encoder.text_token match, :doctype
|
@@ -110,7 +124,7 @@ module Scanners
|
|
110
124
|
elsif match = scan(/<\/[-\w.:]*>?/m)
|
111
125
|
in_tag = nil
|
112
126
|
encoder.text_token match, :tag
|
113
|
-
elsif match = scan(/<(?:(script)|[-\w.:]+)(>)?/m)
|
127
|
+
elsif match = scan(/<(?:(script|style)|[-\w.:]+)(>)?/m)
|
114
128
|
encoder.text_token match, :tag
|
115
129
|
in_tag = self[1]
|
116
130
|
if self[2]
|
@@ -161,17 +175,21 @@ module Scanners
|
|
161
175
|
encoder.text_token match, :attribute_value
|
162
176
|
state = :attribute
|
163
177
|
elsif match = scan(/["']/)
|
164
|
-
if in_attribute == :script
|
165
|
-
encoder.begin_group :
|
166
|
-
encoder.text_token match, :
|
178
|
+
if in_attribute == :script || in_attribute == :style
|
179
|
+
encoder.begin_group :string
|
180
|
+
encoder.text_token match, :delimiter
|
167
181
|
if scan(/javascript:[ \t]*/)
|
168
182
|
encoder.text_token matched, :comment
|
169
183
|
end
|
170
184
|
code = scan_until(match == '"' ? /(?="|\z)/ : /(?='|\z)/)
|
171
|
-
|
185
|
+
if in_attribute == :script
|
186
|
+
scan_java_script encoder, code
|
187
|
+
else
|
188
|
+
scan_css encoder, code, [:block]
|
189
|
+
end
|
172
190
|
match = scan(/["']/)
|
173
|
-
encoder.text_token match, :
|
174
|
-
encoder.end_group :
|
191
|
+
encoder.text_token match, :delimiter if match
|
192
|
+
encoder.end_group :string
|
175
193
|
state = :attribute
|
176
194
|
in_attribute = nil
|
177
195
|
else
|
@@ -206,19 +224,23 @@ module Scanners
|
|
206
224
|
|
207
225
|
when :in_special_tag
|
208
226
|
case in_tag
|
209
|
-
when 'script'
|
227
|
+
when 'script', 'style'
|
210
228
|
encoder.text_token match, :space if match = scan(/[ \t]*\n/)
|
211
229
|
if scan(/(\s*<!--)(?:(.*?)(-->)|(.*))/m)
|
212
230
|
code = self[2] || self[4]
|
213
231
|
closing = self[3]
|
214
232
|
encoder.text_token self[1], :comment
|
215
233
|
else
|
216
|
-
code = scan_until(/(?=(?:\n\s*)
|
234
|
+
code = scan_until(/(?=(?:\n\s*)?<\/#{in_tag}>)|\z/)
|
217
235
|
closing = false
|
218
236
|
end
|
219
237
|
unless code.empty?
|
220
238
|
encoder.begin_group :inline
|
221
|
-
|
239
|
+
if in_tag == 'script'
|
240
|
+
scan_java_script encoder, code
|
241
|
+
else
|
242
|
+
scan_css encoder, code
|
243
|
+
end
|
222
244
|
encoder.end_group :inline
|
223
245
|
end
|
224
246
|
encoder.text_token closing, :comment if closing
|
@@ -147,7 +147,7 @@ module Scanners
|
|
147
147
|
elsif match = scan(/ \\ | $ /x)
|
148
148
|
encoder.end_group state
|
149
149
|
state = :initial
|
150
|
-
encoder.text_token match, :error
|
150
|
+
encoder.text_token match, :error unless match.empty?
|
151
151
|
else
|
152
152
|
raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
|
153
153
|
end
|
@@ -54,10 +54,17 @@ module Scanners
|
|
54
54
|
|
55
55
|
protected
|
56
56
|
|
57
|
+
def setup
|
58
|
+
@state = :initial
|
59
|
+
end
|
60
|
+
|
57
61
|
def scan_tokens encoder, options
|
58
62
|
|
59
|
-
state = :
|
60
|
-
string_delimiter
|
63
|
+
state, string_delimiter = options[:state] || @state
|
64
|
+
if string_delimiter
|
65
|
+
encoder.begin_group state
|
66
|
+
end
|
67
|
+
|
61
68
|
value_expected = true
|
62
69
|
key_expected = false
|
63
70
|
function_expected = false
|
@@ -72,9 +79,10 @@ module Scanners
|
|
72
79
|
value_expected = true if !value_expected && match.index(?\n)
|
73
80
|
encoder.text_token match, :space
|
74
81
|
|
75
|
-
elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
|
82
|
+
elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .*() ) !mx)
|
76
83
|
value_expected = true
|
77
84
|
encoder.text_token match, :comment
|
85
|
+
state = :open_multi_line_comment if self[1]
|
78
86
|
|
79
87
|
elsif check(/\.?\d/)
|
80
88
|
key_expected = value_expected = false
|
@@ -175,20 +183,36 @@ module Scanners
|
|
175
183
|
encoder.text_token match, :content
|
176
184
|
elsif match = scan(/ \\ | $ /x)
|
177
185
|
encoder.end_group state
|
178
|
-
encoder.text_token match, :error
|
186
|
+
encoder.text_token match, :error unless match.empty?
|
187
|
+
string_delimiter = nil
|
179
188
|
key_expected = value_expected = false
|
180
189
|
state = :initial
|
181
190
|
else
|
182
|
-
raise_inspect "else case
|
191
|
+
raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder
|
183
192
|
end
|
184
193
|
|
194
|
+
when :open_multi_line_comment
|
195
|
+
if match = scan(%r! .*? \*/ !mx)
|
196
|
+
state = :initial
|
197
|
+
else
|
198
|
+
match = scan(%r! .+ !mx)
|
199
|
+
end
|
200
|
+
value_expected = true
|
201
|
+
encoder.text_token match, :comment if match
|
202
|
+
|
185
203
|
else
|
186
|
-
|
204
|
+
#:nocov:
|
205
|
+
raise_inspect 'Unknown state: %p' % [state], encoder
|
206
|
+
#:nocov:
|
187
207
|
|
188
208
|
end
|
189
209
|
|
190
210
|
end
|
191
211
|
|
212
|
+
if options[:keep_state]
|
213
|
+
@state = state, string_delimiter
|
214
|
+
end
|
215
|
+
|
192
216
|
if [:string, :regexp].include? state
|
193
217
|
encoder.end_group state
|
194
218
|
end
|
@@ -14,15 +14,21 @@ module Scanners
|
|
14
14
|
|
15
15
|
ESCAPE = / [bfnrt\\"\/] /x # :nodoc:
|
16
16
|
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc:
|
17
|
+
KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x
|
17
18
|
|
18
19
|
protected
|
19
20
|
|
21
|
+
def setup
|
22
|
+
@state = :initial
|
23
|
+
end
|
24
|
+
|
20
25
|
# See http://json.org/ for a definition of the JSON lexic/grammar.
|
21
26
|
def scan_tokens encoder, options
|
27
|
+
state = options[:state] || @state
|
22
28
|
|
23
|
-
|
24
|
-
|
25
|
-
|
29
|
+
if [:string, :key].include? state
|
30
|
+
encoder.begin_group state
|
31
|
+
end
|
26
32
|
|
27
33
|
until eos?
|
28
34
|
|
@@ -32,18 +38,11 @@ module Scanners
|
|
32
38
|
if match = scan(/ \s+ /x)
|
33
39
|
encoder.text_token match, :space
|
34
40
|
elsif match = scan(/"/)
|
35
|
-
state =
|
41
|
+
state = check(/#{KEY}/o) ? :key : :string
|
36
42
|
encoder.begin_group state
|
37
43
|
encoder.text_token match, :delimiter
|
38
44
|
elsif match = scan(/ [:,\[{\]}] /x)
|
39
45
|
encoder.text_token match, :operator
|
40
|
-
case match
|
41
|
-
when ':' then key_expected = false
|
42
|
-
when ',' then key_expected = true if stack.last == :object
|
43
|
-
when '{' then stack << :object; key_expected = true
|
44
|
-
when '[' then stack << :array
|
45
|
-
when '}', ']' then stack.pop # no error recovery, but works for valid JSON
|
46
|
-
end
|
47
46
|
elsif match = scan(/ true | false | null /x)
|
48
47
|
encoder.text_token match, :value
|
49
48
|
elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x)
|
@@ -70,7 +69,7 @@ module Scanners
|
|
70
69
|
encoder.text_token match, :content
|
71
70
|
elsif match = scan(/ \\ | $ /x)
|
72
71
|
encoder.end_group state
|
73
|
-
encoder.text_token match, :error
|
72
|
+
encoder.text_token match, :error unless match.empty?
|
74
73
|
state = :initial
|
75
74
|
else
|
76
75
|
raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
|
@@ -82,6 +81,10 @@ module Scanners
|
|
82
81
|
end
|
83
82
|
end
|
84
83
|
|
84
|
+
if options[:keep_state]
|
85
|
+
@state = state
|
86
|
+
end
|
87
|
+
|
85
88
|
if [:string, :key].include? state
|
86
89
|
encoder.end_group state
|
87
90
|
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module CodeRay
|
4
|
+
module Scanners
|
5
|
+
|
6
|
+
# Scanner for the Lua[http://lua.org] programming lanuage.
|
7
|
+
#
|
8
|
+
# The language’s complete syntax is defined in
|
9
|
+
# {the Lua manual}[http://www.lua.org/manual/5.2/manual.html],
|
10
|
+
# which is what this scanner tries to conform to.
|
11
|
+
class Lua < Scanner
|
12
|
+
|
13
|
+
register_for :lua
|
14
|
+
file_extension 'lua'
|
15
|
+
title 'Lua'
|
16
|
+
|
17
|
+
# Keywords used in Lua.
|
18
|
+
KEYWORDS = %w[and break do else elseif end
|
19
|
+
for function goto if in
|
20
|
+
local not or repeat return
|
21
|
+
then until while
|
22
|
+
]
|
23
|
+
|
24
|
+
# Constants set by the Lua core.
|
25
|
+
PREDEFINED_CONSTANTS = %w[false true nil]
|
26
|
+
|
27
|
+
# The expressions contained in this array are parts of Lua’s `basic'
|
28
|
+
# library. Although it’s not entirely necessary to load that library,
|
29
|
+
# it is highly recommended and one would have to provide own implementations
|
30
|
+
# of some of these expressions if one does not do so. They however aren’t
|
31
|
+
# keywords, neither are they constants, but nearly predefined, so they
|
32
|
+
# get tagged as `predefined' rather than anything else.
|
33
|
+
#
|
34
|
+
# This list excludes values of form `_UPPERCASE' because the Lua manual
|
35
|
+
# requires such identifiers to be reserved by Lua anyway and they are
|
36
|
+
# highlighted directly accordingly, without the need for specific
|
37
|
+
# identifiers to be listed here.
|
38
|
+
PREDEFINED_EXPRESSIONS = %w[
|
39
|
+
assert collectgarbage dofile error getmetatable
|
40
|
+
ipairs load loadfile next pairs pcall print
|
41
|
+
rawequal rawget rawlen rawset select setmetatable
|
42
|
+
tonumber tostring type xpcall
|
43
|
+
]
|
44
|
+
|
45
|
+
# Automatic token kind selection for normal words.
|
46
|
+
IDENT_KIND = CodeRay::WordList.new(:ident).
|
47
|
+
add(KEYWORDS, :keyword).
|
48
|
+
add(PREDEFINED_CONSTANTS, :predefined_constant).
|
49
|
+
add(PREDEFINED_EXPRESSIONS, :predefined)
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# Scanner initialization.
|
54
|
+
def setup
|
55
|
+
@state = :initial
|
56
|
+
@brace_depth = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
# CodeRay entry hook. Starts parsing.
|
60
|
+
def scan_tokens(encoder, options)
|
61
|
+
state = options[:state] || @state
|
62
|
+
brace_depth = @brace_depth
|
63
|
+
num_equals = nil
|
64
|
+
|
65
|
+
until eos?
|
66
|
+
case state
|
67
|
+
|
68
|
+
when :initial
|
69
|
+
if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]]
|
70
|
+
num_equals = match.count("=") # Number must match for comment end
|
71
|
+
encoder.begin_group(:comment)
|
72
|
+
encoder.text_token(match, :delimiter)
|
73
|
+
state = :long_comment
|
74
|
+
|
75
|
+
elsif match = scan(/--.*$/) # --Lua comment
|
76
|
+
encoder.text_token(match, :comment)
|
77
|
+
|
78
|
+
elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]]
|
79
|
+
num_equals = match.count("=") # Number must match for comment end
|
80
|
+
encoder.begin_group(:string)
|
81
|
+
encoder.text_token(match, :delimiter)
|
82
|
+
state = :long_string
|
83
|
+
|
84
|
+
elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label::
|
85
|
+
encoder.text_token(match, :label)
|
86
|
+
|
87
|
+
elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua
|
88
|
+
encoder.text_token(match, :predefined)
|
89
|
+
|
90
|
+
elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits)
|
91
|
+
kind = IDENT_KIND[match]
|
92
|
+
|
93
|
+
# Extra highlighting for entities following certain keywords
|
94
|
+
if kind == :keyword and match == "function"
|
95
|
+
state = :function_expected
|
96
|
+
elsif kind == :keyword and match == "goto"
|
97
|
+
state = :goto_label_expected
|
98
|
+
elsif kind == :keyword and match == "local"
|
99
|
+
state = :local_var_expected
|
100
|
+
end
|
101
|
+
|
102
|
+
encoder.text_token(match, kind)
|
103
|
+
|
104
|
+
elsif match = scan(/\{/) # Opening table brace {
|
105
|
+
encoder.begin_group(:map)
|
106
|
+
encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter)
|
107
|
+
brace_depth += 1
|
108
|
+
state = :map
|
109
|
+
|
110
|
+
elsif match = scan(/\}/) # Closing table brace }
|
111
|
+
if brace_depth == 1
|
112
|
+
brace_depth = 0
|
113
|
+
encoder.text_token(match, :delimiter)
|
114
|
+
encoder.end_group(:map)
|
115
|
+
elsif brace_depth == 0 # Mismatched brace
|
116
|
+
encoder.text_token(match, :error)
|
117
|
+
else
|
118
|
+
brace_depth -= 1
|
119
|
+
encoder.text_token(match, :inline_delimiter)
|
120
|
+
encoder.end_group(:map)
|
121
|
+
state = :map
|
122
|
+
end
|
123
|
+
|
124
|
+
elsif match = scan(/["']/) # String delimiters " and '
|
125
|
+
encoder.begin_group(:string)
|
126
|
+
encoder.text_token(match, :delimiter)
|
127
|
+
start_delim = match
|
128
|
+
state = :string
|
129
|
+
|
130
|
+
# ↓Prefix hex number ←|→ decimal number
|
131
|
+
elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
|
132
|
+
encoder.text_token(match, :float)
|
133
|
+
|
134
|
+
# ↓Prefix hex number ←|→ decimal number
|
135
|
+
elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
|
136
|
+
encoder.text_token(match, :integer)
|
137
|
+
|
138
|
+
elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators
|
139
|
+
encoder.text_token(match, :operator)
|
140
|
+
|
141
|
+
elsif match = scan(/\s+/) # Space
|
142
|
+
encoder.text_token(match, :space)
|
143
|
+
|
144
|
+
else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors.
|
145
|
+
encoder.text_token(getch, :error)
|
146
|
+
end
|
147
|
+
|
148
|
+
# It may be that we’re scanning a full-blown subexpression of a table
|
149
|
+
# (tables can contain full expressions in parts).
|
150
|
+
# If this is the case, return to :map scanning state.
|
151
|
+
state = :map if state == :initial && brace_depth >= 1
|
152
|
+
|
153
|
+
when :function_expected
|
154
|
+
if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name
|
155
|
+
encoder.text_token(match, :operator)
|
156
|
+
state = :initial
|
157
|
+
elsif match = scan(/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x) # function tbl.subtbl.foo() | function tbl:foo() # Colon only allowed as last separator
|
158
|
+
encoder.text_token(match, :ident)
|
159
|
+
elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo()
|
160
|
+
encoder.text_token(match, :function)
|
161
|
+
state = :initial
|
162
|
+
elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace
|
163
|
+
encoder.text_token(match, :space)
|
164
|
+
else
|
165
|
+
encoder.text_token(getch, :error)
|
166
|
+
state = :initial
|
167
|
+
end
|
168
|
+
|
169
|
+
when :goto_label_expected
|
170
|
+
if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
|
171
|
+
encoder.text_token(match, :label)
|
172
|
+
state = :initial
|
173
|
+
elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace
|
174
|
+
encoder.text_token(match, :space)
|
175
|
+
else
|
176
|
+
encoder.text_token(getch, :error)
|
177
|
+
end
|
178
|
+
|
179
|
+
when :local_var_expected
|
180
|
+
if match = scan(/function/) # local function ...
|
181
|
+
encoder.text_token(match, :keyword)
|
182
|
+
state = :function_expected
|
183
|
+
elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
|
184
|
+
encoder.text_token(match, :local_variable)
|
185
|
+
elsif match = scan(/,/)
|
186
|
+
encoder.text_token(match, :operator)
|
187
|
+
elsif match = scan(/\=/)
|
188
|
+
encoder.text_token(match, :operator)
|
189
|
+
# After encountering the equal sign, arbitrary expressions are
|
190
|
+
# allowed again, so just return to the main state for further
|
191
|
+
# parsing.
|
192
|
+
state = :initial
|
193
|
+
elsif match = scan(/\n/)
|
194
|
+
encoder.text_token(match, :space)
|
195
|
+
state = :initial
|
196
|
+
elsif match = scan(/\s+/)
|
197
|
+
encoder.text_token(match, :space)
|
198
|
+
else
|
199
|
+
encoder.text_token(getch, :error)
|
200
|
+
end
|
201
|
+
|
202
|
+
when :long_comment
|
203
|
+
if match = scan(/.*?(?=\]={#{num_equals}}\])/m)
|
204
|
+
encoder.text_token(match, :content)
|
205
|
+
|
206
|
+
delim = scan(/\]={#{num_equals}}\]/)
|
207
|
+
encoder.text_token(delim, :delimiter)
|
208
|
+
else # No terminator found till EOF
|
209
|
+
encoder.text_token(rest, :error)
|
210
|
+
terminate
|
211
|
+
end
|
212
|
+
encoder.end_group(:comment)
|
213
|
+
state = :initial
|
214
|
+
|
215
|
+
when :long_string
|
216
|
+
if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences
|
217
|
+
encoder.text_token(match, :content)
|
218
|
+
|
219
|
+
delim = scan(/\]={#{num_equals}}\]/)
|
220
|
+
encoder.text_token(delim, :delimiter)
|
221
|
+
else # No terminator found till EOF
|
222
|
+
encoder.text_token(rest, :error)
|
223
|
+
terminate
|
224
|
+
end
|
225
|
+
encoder.end_group(:string)
|
226
|
+
state = :initial
|
227
|
+
|
228
|
+
when :string
|
229
|
+
if match = scan(/[^\\#{start_delim}\n]+/) # Everything except \ and the start delimiter character is string content (newlines are only allowed if preceeded by \ or \z)
|
230
|
+
encoder.text_token(match, :content)
|
231
|
+
elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m)
|
232
|
+
encoder.text_token(match, :char)
|
233
|
+
elsif match = scan(Regexp.compile(start_delim))
|
234
|
+
encoder.text_token(match, :delimiter)
|
235
|
+
encoder.end_group(:string)
|
236
|
+
state = :initial
|
237
|
+
elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings
|
238
|
+
encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings
|
239
|
+
encoder.end_group(:string)
|
240
|
+
state = :initial
|
241
|
+
else
|
242
|
+
encoder.text_token(getch, :error)
|
243
|
+
end
|
244
|
+
|
245
|
+
when :map
|
246
|
+
if match = scan(/[,;]/)
|
247
|
+
encoder.text_token(match, :operator)
|
248
|
+
elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x)
|
249
|
+
encoder.text_token(match, :key)
|
250
|
+
encoder.text_token(scan(/\s+/), :space) if check(/\s+/)
|
251
|
+
encoder.text_token(scan(/\=/), :operator)
|
252
|
+
state = :initial
|
253
|
+
elsif match = scan(/\s+/m)
|
254
|
+
encoder.text_token(match, :space)
|
255
|
+
else
|
256
|
+
# Note this clause doesn’t advance the scan pointer, it’s a kind of
|
257
|
+
# "retry with other options" (the :initial state then of course
|
258
|
+
# advances the pointer).
|
259
|
+
state = :initial
|
260
|
+
end
|
261
|
+
else
|
262
|
+
raise
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
if options[:keep_state]
|
268
|
+
@state = state
|
269
|
+
end
|
270
|
+
|
271
|
+
encoder.end_group :string if [:string].include? state
|
272
|
+
brace_depth.times { encoder.end_group :map }
|
273
|
+
|
274
|
+
encoder
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
end
|