summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners
diff options
context:
space:
mode:
authorKornelius Kalnbach <murphy@rubychan.de>2016-02-13 16:12:48 +0100
committerKornelius Kalnbach <murphy@rubychan.de>2016-02-13 16:12:48 +0100
commit0b8c69cfb7a65bec04c44e58e5776e323d2aa1af (patch)
treefd81bf6229bfc0d173f5b744534a76b5c70eb440 /lib/coderay/scanners
parent916711c9983483c39f9a68c29e21a0ed40004bd2 (diff)
parent0a1f500d524ff0fb5eeafef051ccbb641954a87a (diff)
downloadcoderay-paint-integration.tar.gz
Merge branch 'master' into paint-integrationpaint-integration
Diffstat (limited to 'lib/coderay/scanners')
-rw-r--r--lib/coderay/scanners/css.rb8
-rw-r--r--lib/coderay/scanners/debug.rb26
-rw-r--r--lib/coderay/scanners/diff.rb4
-rw-r--r--lib/coderay/scanners/go.rb208
-rw-r--r--lib/coderay/scanners/groovy.rb21
-rw-r--r--lib/coderay/scanners/html.rb54
-rw-r--r--lib/coderay/scanners/json.rb25
-rw-r--r--lib/coderay/scanners/lua.rb280
-rw-r--r--lib/coderay/scanners/php.rb26
-rw-r--r--lib/coderay/scanners/python.rb4
-rw-r--r--lib/coderay/scanners/raydebug.rb45
-rw-r--r--lib/coderay/scanners/ruby.rb21
-rw-r--r--lib/coderay/scanners/ruby/patterns.rb7
-rw-r--r--lib/coderay/scanners/ruby/string_state.rb22
-rw-r--r--lib/coderay/scanners/sass.rb47
-rw-r--r--lib/coderay/scanners/sql.rb51
-rw-r--r--lib/coderay/scanners/taskpaper.rb36
-rw-r--r--lib/coderay/scanners/yaml.rb4
18 files changed, 753 insertions, 136 deletions
diff --git a/lib/coderay/scanners/css.rb b/lib/coderay/scanners/css.rb
index 732f9c5..55d5239 100644
--- a/lib/coderay/scanners/css.rb
+++ b/lib/coderay/scanners/css.rb
@@ -25,7 +25,7 @@ module Scanners
HexColor = /#(?:#{Hex}{6}|#{Hex}{3})/
- Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)/
+ Num = /-?(?:[0-9]*\.[0-9]+|[0-9]+)n?/
Name = /#{NMChar}+/
Ident = /-?#{NMStart}#{NMChar}*/
AtKeyword = /@#{Ident}/
@@ -53,7 +53,7 @@ module Scanners
end
def scan_tokens encoder, options
- states = Array(options[:state] || @state)
+ states = Array(options[:state] || @state).dup
value_expected = @value_expected
until eos?
@@ -145,10 +145,10 @@ module Scanners
start = match[/^\w+\(/]
encoder.text_token start, :delimiter
if match[-1] == ?)
- encoder.text_token match[start.size..-2], :content
+ encoder.text_token match[start.size..-2], :content if match.size > start.size + 1
encoder.text_token ')', :delimiter
else
- encoder.text_token match[start.size..-1], :content
+ encoder.text_token match[start.size..-1], :content if match.size > start.size
end
encoder.end_group :function
diff --git a/lib/coderay/scanners/debug.rb b/lib/coderay/scanners/debug.rb
index 566bfa7..83ede9a 100644
--- a/lib/coderay/scanners/debug.rb
+++ b/lib/coderay/scanners/debug.rb
@@ -1,9 +1,11 @@
+require 'set'
+
module CodeRay
module Scanners
# = Debug Scanner
#
- # Interprets the output of the Encoders::Debug encoder.
+ # Interprets the output of the Encoders::Debug encoder (basically the inverse function).
class Debug < Scanner
register_for :debug
@@ -11,6 +13,11 @@ module Scanners
protected
+ def setup
+ super
+ @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set
+ end
+
def scan_tokens encoder, options
opened_tokens = []
@@ -21,16 +28,19 @@ module Scanners
encoder.text_token match, :space
elsif match = scan(/ (\w+) \( ( [^\)\\]* ( \\. [^\)\\]* )* ) \)? /x)
- kind = self[1].to_sym
- match = self[2].gsub(/\\(.)/m, '\1')
- unless TokenKinds.has_key? kind
- kind = :error
- match = matched
+ if @known_token_kinds.include? self[1]
+ encoder.text_token self[2].gsub(/\\(.)/m, '\1'), self[1].to_sym
+ else
+ encoder.text_token matched, :unknown
end
- encoder.text_token match, kind
elsif match = scan(/ (\w+) ([<\[]) /x)
- kind = self[1].to_sym
+ if @known_token_kinds.include? self[1]
+ kind = self[1].to_sym
+ else
+ kind = :unknown
+ end
+
opened_tokens << kind
case self[2]
when '<'
diff --git a/lib/coderay/scanners/diff.rb b/lib/coderay/scanners/diff.rb
index af0f755..74a6c27 100644
--- a/lib/coderay/scanners/diff.rb
+++ b/lib/coderay/scanners/diff.rb
@@ -69,7 +69,7 @@ module Scanners
state = :added
elsif match = scan(/\\ .*/)
encoder.text_token match, :comment
- elsif match = scan(/@@(?>[^@\n]*)@@/)
+ elsif match = scan(/@@(?>[^@\n]+)@@/)
content_scanner.state = :initial unless match?(/\n\+/)
content_scanner_entry_state = nil
if check(/\n|$/)
@@ -100,7 +100,7 @@ module Scanners
next
elsif match = scan(/-/)
deleted_lines_count += 1
- if options[:inline_diff] && deleted_lines_count == 1 && (changed_lines_count = 1 + check(/.*(?:\n\-.*)*/).count("\n")) && match?(/(?>.*(?:\n\-.*){#{changed_lines_count - 1}}(?:\n\+.*){#{changed_lines_count}})$(?!\n\+)/)
+ if options[:inline_diff] && deleted_lines_count == 1 && (changed_lines_count = 1 + check(/.*(?:\n\-.*)*/).count("\n")) && changed_lines_count <= 100_000 && match?(/(?>.*(?:\n\-.*){#{changed_lines_count - 1}}(?:\n\+.*){#{changed_lines_count}})$(?!\n\+)/)
deleted_lines = Array.new(changed_lines_count) { |i| skip(/\n\-/) if i > 0; scan(/.*/) }
inserted_lines = Array.new(changed_lines_count) { |i| skip(/\n\+/) ; scan(/.*/) }
diff --git a/lib/coderay/scanners/go.rb b/lib/coderay/scanners/go.rb
new file mode 100644
index 0000000..99fdd63
--- /dev/null
+++ b/lib/coderay/scanners/go.rb
@@ -0,0 +1,208 @@
+module CodeRay
+module Scanners
+
+ class Go < Scanner
+
+ register_for :go
+ file_extension 'go'
+
+ # http://golang.org/ref/spec#Keywords
+ KEYWORDS = [
+ 'break', 'default', 'func', 'interface', 'select',
+ 'case', 'defer', 'go', 'map', 'struct',
+ 'chan', 'else', 'goto', 'package', 'switch',
+ 'const', 'fallthrough', 'if', 'range', 'type',
+ 'continue', 'for', 'import', 'return', 'var',
+ ] # :nodoc:
+
+ # http://golang.org/ref/spec#Types
+ PREDEFINED_TYPES = [
+ 'bool',
+ 'uint8', 'uint16', 'uint32', 'uint64',
+ 'int8', 'int16', 'int32', 'int64',
+ 'float32', 'float64',
+ 'complex64', 'complex128',
+ 'byte', 'rune', 'string', 'error',
+ 'uint', 'int', 'uintptr',
+ ] # :nodoc:
+
+ PREDEFINED_CONSTANTS = [
+ 'nil', 'iota',
+ 'true', 'false',
+ ] # :nodoc:
+
+ PREDEFINED_FUNCTIONS = %w[
+ append cap close complex copy delete imag len
+ make new panic print println real recover
+ ] # :nodoc:
+
+ IDENT_KIND = WordList.new(:ident).
+ add(KEYWORDS, :keyword).
+ add(PREDEFINED_TYPES, :predefined_type).
+ add(PREDEFINED_CONSTANTS, :predefined_constant).
+ add(PREDEFINED_FUNCTIONS, :predefined) # :nodoc:
+
+ ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc:
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc:
+
+ protected
+
+ def scan_tokens encoder, options
+
+ state = :initial
+ label_expected = true
+ case_expected = false
+ label_expected_before_preproc_line = nil
+ in_preproc_line = false
+
+ until eos?
+
+ case state
+
+ when :initial
+
+ if match = scan(/ \s+ | \\\n /x)
+ if in_preproc_line && match != "\\\n" && match.index(?\n)
+ in_preproc_line = false
+ case_expected = false
+ label_expected = label_expected_before_preproc_line
+ end
+ encoder.text_token match, :space
+
+ elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
+ encoder.text_token match, :comment
+
+ elsif match = scan(/ <?- (?![\d.]) | [+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
+ if case_expected
+ label_expected = true if match == ':'
+ case_expected = false
+ end
+ encoder.text_token match, :operator
+
+ elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
+ kind = IDENT_KIND[match]
+ if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
+ kind = :label
+ label_expected = false
+ match << matched
+ else
+ label_expected = false
+ if kind == :keyword
+ case match
+ when 'case', 'default'
+ case_expected = true
+ end
+ end
+ end
+ encoder.text_token match, kind
+
+ elsif match = scan(/L?"/)
+ encoder.begin_group :string
+ if match[0] == ?L
+ encoder.text_token 'L', :modifier
+ match = '"'
+ end
+ encoder.text_token match, :delimiter
+ state = :string
+
+ elsif match = scan(/ ` ([^`]+)? (`)? /x)
+ encoder.begin_group :shell
+ encoder.text_token '`', :delimiter
+ encoder.text_token self[1], :content if self[1]
+ encoder.text_token self[2], :delimiter if self[2]
+ encoder.end_group :shell
+
+ elsif match = scan(/ \# \s* if \s* 0 /x)
+ match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
+ encoder.text_token match, :comment
+
+ elsif match = scan(/#[ \t]*(\w*)/)
+ encoder.text_token match, :preprocessor
+ in_preproc_line = true
+ label_expected_before_preproc_line = label_expected
+ state = :include_expected if self[1] == 'include'
+
+ elsif match = scan(/ L?' (?: [^\'\n\\] | \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) )? '? /ox)
+ label_expected = false
+ encoder.text_token match, :char
+
+ elsif match = scan(/\$/)
+ encoder.text_token match, :ident
+
+ elsif match = scan(/-?\d*(\.\d*)?([eE][+-]?\d+)?i/)
+ label_expected = false
+ encoder.text_token match, :imaginary
+
+ elsif match = scan(/-?0[xX][0-9A-Fa-f]+/)
+ label_expected = false
+ encoder.text_token match, :hex
+
+ elsif match = scan(/-?(?:0[0-7]+)(?![89.eEfF])/)
+ label_expected = false
+ encoder.text_token match, :octal
+
+ elsif match = scan(/-?(?:\d*\.\d+|\d+\.)(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/)
+ label_expected = false
+ encoder.text_token match, :float
+
+ elsif match = scan(/-?(?:\d+)(?![.eEfF])L?L?/)
+ label_expected = false
+ encoder.text_token match, :integer
+
+ else
+ encoder.text_token getch, :error
+
+ end
+
+ when :string
+ if match = scan(/[^\\\n"]+/)
+ encoder.text_token match, :content
+ elsif match = scan(/"/)
+ encoder.text_token match, :delimiter
+ encoder.end_group :string
+ state = :initial
+ label_expected = false
+ elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
+ encoder.text_token match, :char
+ elsif match = scan(/ \\ /x)
+ encoder.text_token match, :error
+ elsif match = scan(/$/)
+ encoder.end_group :string
+ state = :initial
+ label_expected = false
+ else
+ raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
+ end
+
+ when :include_expected
+ if match = scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
+ encoder.text_token match, :include
+ state = :initial
+
+ elsif match = scan(/\s+/)
+ encoder.text_token match, :space
+ state = :initial if match.index ?\n
+
+ else
+ state = :initial
+
+ end
+
+ else
+ raise_inspect 'Unknown state', encoder
+
+ end
+
+ end
+
+ if state == :string
+ encoder.end_group :string
+ end
+
+ encoder
+ end
+
+ end
+
+end
+end
diff --git a/lib/coderay/scanners/groovy.rb b/lib/coderay/scanners/groovy.rb
index cf55daf..c64454f 100644
--- a/lib/coderay/scanners/groovy.rb
+++ b/lib/coderay/scanners/groovy.rb
@@ -36,9 +36,12 @@ module Scanners
protected
+ def setup
+ @state = :initial
+ end
+
def scan_tokens encoder, options
-
- state = :initial
+ state = options[:state] || @state
inline_block_stack = []
inline_block_paren_depth = nil
string_delimiter = nil
@@ -223,7 +226,7 @@ module Scanners
encoder.text_token match, :content # TODO: Shouldn't this be :error?
elsif match = scan(/ \\ | \n /x)
- encoder.end_group state
+ encoder.end_group state == :regexp ? :regexp : :string
encoder.text_token match, :error
after_def = value_expected = false
state = :initial
@@ -243,7 +246,17 @@ module Scanners
end
if [:multiline_string, :string, :regexp].include? state
- encoder.end_group state
+ encoder.end_group state == :regexp ? :regexp : :string
+ end
+
+ if options[:keep_state]
+ @state = state
+ end
+
+ until inline_block_stack.empty?
+ state, = *inline_block_stack.pop
+ encoder.end_group :inline
+ encoder.end_group state == :regexp ? :regexp : :string
end
encoder
diff --git a/lib/coderay/scanners/html.rb b/lib/coderay/scanners/html.rb
index 3ba3b79..ebe7b01 100644
--- a/lib/coderay/scanners/html.rb
+++ b/lib/coderay/scanners/html.rb
@@ -1,13 +1,13 @@
module CodeRay
module Scanners
-
+
# HTML Scanner
#
# Alias: +xhtml+
#
# See also: Scanners::XML
class HTML < Scanner
-
+
register_for :html
KINDS_NOT_LOC = [
@@ -33,7 +33,8 @@ module Scanners
)
IN_ATTRIBUTE = WordList::CaseIgnoring.new(nil).
- add(EVENT_ATTRIBUTES, :script)
+ add(EVENT_ATTRIBUTES, :script).
+ add(['style'], :style)
ATTR_NAME = /[\w.:-]+/ # :nodoc:
TAG_END = /\/?>/ # :nodoc:
@@ -75,9 +76,14 @@ module Scanners
def scan_java_script encoder, code
if code && !code.empty?
@java_script_scanner ||= Scanners::JavaScript.new '', :keep_tokens => true
- # encoder.begin_group :inline
@java_script_scanner.tokenize code, :tokens => encoder
- # encoder.end_group :inline
+ end
+ end
+
+ def scan_css encoder, code, state = [:initial]
+ if code && !code.empty?
+ @css_scanner ||= Scanners::CSS.new '', :keep_tokens => true
+ @css_scanner.tokenize code, :tokens => encoder, :state => state
end
end
@@ -99,7 +105,15 @@ module Scanners
case state
when :initial
- if match = scan(/<!--(?:.*?-->|.*)/m)
+ if match = scan(/<!\[CDATA\[/)
+ encoder.text_token match, :inline_delimiter
+ if match = scan(/.*?\]\]>/m)
+ encoder.text_token match[0..-4], :plain
+ encoder.text_token ']]>', :inline_delimiter
+ elsif match = scan(/.+/)
+ encoder.text_token match, :error
+ end
+ elsif match = scan(/<!--(?:.*?-->|.*)/m)
encoder.text_token match, :comment
elsif match = scan(/<!(\w+)(?:.*?>|.*)|\]>/m)
encoder.text_token match, :doctype
@@ -110,7 +124,7 @@ module Scanners
elsif match = scan(/<\/[-\w.:]*>?/m)
in_tag = nil
encoder.text_token match, :tag
- elsif match = scan(/<(?:(script)|[-\w.:]+)(>)?/m)
+ elsif match = scan(/<(?:(script|style)|[-\w.:]+)(>)?/m)
encoder.text_token match, :tag
in_tag = self[1]
if self[2]
@@ -161,17 +175,21 @@ module Scanners
encoder.text_token match, :attribute_value
state = :attribute
elsif match = scan(/["']/)
- if in_attribute == :script
- encoder.begin_group :inline
- encoder.text_token match, :inline_delimiter
+ if in_attribute == :script || in_attribute == :style
+ encoder.begin_group :string
+ encoder.text_token match, :delimiter
if scan(/javascript:[ \t]*/)
encoder.text_token matched, :comment
end
code = scan_until(match == '"' ? /(?="|\z)/ : /(?='|\z)/)
- scan_java_script encoder, code
+ if in_attribute == :script
+ scan_java_script encoder, code
+ else
+ scan_css encoder, code, [:block]
+ end
match = scan(/["']/)
- encoder.text_token match, :inline_delimiter if match
- encoder.end_group :inline
+ encoder.text_token match, :delimiter if match
+ encoder.end_group :string
state = :attribute
in_attribute = nil
else
@@ -206,19 +224,23 @@ module Scanners
when :in_special_tag
case in_tag
- when 'script'
+ when 'script', 'style'
encoder.text_token match, :space if match = scan(/[ \t]*\n/)
if scan(/(\s*<!--)(?:(.*?)(-->)|(.*))/m)
code = self[2] || self[4]
closing = self[3]
encoder.text_token self[1], :comment
else
- code = scan_until(/(?=(?:\n\s*)?<\/script>)|\z/)
+ code = scan_until(/(?=(?:\n\s*)?<\/#{in_tag}>)|\z/)
closing = false
end
unless code.empty?
encoder.begin_group :inline
- scan_java_script encoder, code
+ if in_tag == 'script'
+ scan_java_script encoder, code
+ else
+ scan_css encoder, code
+ end
encoder.end_group :inline
end
encoder.text_token closing, :comment if closing
diff --git a/lib/coderay/scanners/json.rb b/lib/coderay/scanners/json.rb
index 4e0f462..b09970c 100644
--- a/lib/coderay/scanners/json.rb
+++ b/lib/coderay/scanners/json.rb
@@ -14,15 +14,21 @@ module Scanners
ESCAPE = / [bfnrt\\"\/] /x # :nodoc:
UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc:
+ KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x
protected
+ def setup
+ @state = :initial
+ end
+
# See http://json.org/ for a definition of the JSON lexic/grammar.
def scan_tokens encoder, options
+ state = options[:state] || @state
- state = :initial
- stack = []
- key_expected = false
+ if [:string, :key].include? state
+ encoder.begin_group state
+ end
until eos?
@@ -32,18 +38,11 @@ module Scanners
if match = scan(/ \s+ /x)
encoder.text_token match, :space
elsif match = scan(/"/)
- state = key_expected ? :key : :string
+ state = check(/#{KEY}/o) ? :key : :string
encoder.begin_group state
encoder.text_token match, :delimiter
elsif match = scan(/ [:,\[{\]}] /x)
encoder.text_token match, :operator
- case match
- when ':' then key_expected = false
- when ',' then key_expected = true if stack.last == :object
- when '{' then stack << :object; key_expected = true
- when '[' then stack << :array
- when '}', ']' then stack.pop # no error recovery, but works for valid JSON
- end
elsif match = scan(/ true | false | null /x)
encoder.text_token match, :value
elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x)
@@ -82,6 +81,10 @@ module Scanners
end
end
+ if options[:keep_state]
+ @state = state
+ end
+
if [:string, :key].include? state
encoder.end_group state
end
diff --git a/lib/coderay/scanners/lua.rb b/lib/coderay/scanners/lua.rb
new file mode 100644
index 0000000..fb1e45a
--- /dev/null
+++ b/lib/coderay/scanners/lua.rb
@@ -0,0 +1,280 @@
+# encoding: utf-8
+
+module CodeRay
+module Scanners
+
+ # Scanner for the Lua[http://lua.org] programming lanuage.
+ #
+ # The language’s complete syntax is defined in
+ # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html],
+ # which is what this scanner tries to conform to.
+ class Lua < Scanner
+
+ register_for :lua
+ file_extension 'lua'
+ title 'Lua'
+
+ # Keywords used in Lua.
+ KEYWORDS = %w[and break do else elseif end
+ for function goto if in
+ local not or repeat return
+ then until while
+ ]
+
+ # Constants set by the Lua core.
+ PREDEFINED_CONSTANTS = %w[false true nil]
+
+ # The expressions contained in this array are parts of Lua’s `basic'
+ # library. Although it’s not entirely necessary to load that library,
+ # it is highly recommended and one would have to provide own implementations
+ # of some of these expressions if one does not do so. They however aren’t
+ # keywords, neither are they constants, but nearly predefined, so they
+ # get tagged as `predefined' rather than anything else.
+ #
+ # This list excludes values of form `_UPPERCASE' because the Lua manual
+ # requires such identifiers to be reserved by Lua anyway and they are
+ # highlighted directly accordingly, without the need for specific
+ # identifiers to be listed here.
+ PREDEFINED_EXPRESSIONS = %w[
+ assert collectgarbage dofile error getmetatable
+ ipairs load loadfile next pairs pcall print
+ rawequal rawget rawlen rawset select setmetatable
+ tonumber tostring type xpcall
+ ]
+
+ # Automatic token kind selection for normal words.
+ IDENT_KIND = CodeRay::WordList.new(:ident).
+ add(KEYWORDS, :keyword).
+ add(PREDEFINED_CONSTANTS, :predefined_constant).
+ add(PREDEFINED_EXPRESSIONS, :predefined)
+
+ protected
+
+ # Scanner initialization.
+ def setup
+ @state = :initial
+ @brace_depth = 0
+ end
+
+ # CodeRay entry hook. Starts parsing.
+ def scan_tokens(encoder, options)
+ state = options[:state] || @state
+ brace_depth = @brace_depth
+ num_equals = nil
+
+ until eos?
+ case state
+
+ when :initial
+ if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]]
+ num_equals = match.count("=") # Number must match for comment end
+ encoder.begin_group(:comment)
+ encoder.text_token(match, :delimiter)
+ state = :long_comment
+
+ elsif match = scan(/--.*$/) # --Lua comment
+ encoder.text_token(match, :comment)
+
+ elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]]
+ num_equals = match.count("=") # Number must match for comment end
+ encoder.begin_group(:string)
+ encoder.text_token(match, :delimiter)
+ state = :long_string
+
+ elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label::
+ encoder.text_token(match, :label)
+
+ elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua
+ encoder.text_token(match, :predefined)
+
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits)
+ kind = IDENT_KIND[match]
+
+ # Extra highlighting for entities following certain keywords
+ if kind == :keyword and match == "function"
+ state = :function_expected
+ elsif kind == :keyword and match == "goto"
+ state = :goto_label_expected
+ elsif kind == :keyword and match == "local"
+ state = :local_var_expected
+ end
+
+ encoder.text_token(match, kind)
+
+ elsif match = scan(/\{/) # Opening table brace {
+ encoder.begin_group(:map)
+ encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter)
+ brace_depth += 1
+ state = :map
+
+ elsif match = scan(/\}/) # Closing table brace }
+ if brace_depth == 1
+ brace_depth = 0
+ encoder.text_token(match, :delimiter)
+ encoder.end_group(:map)
+ elsif brace_depth == 0 # Mismatched brace
+ encoder.text_token(match, :error)
+ else
+ brace_depth -= 1
+ encoder.text_token(match, :inline_delimiter)
+ encoder.end_group(:map)
+ state = :map
+ end
+
+ elsif match = scan(/["']/) # String delimiters " and '
+ encoder.begin_group(:string)
+ encoder.text_token(match, :delimiter)
+ start_delim = match
+ state = :string
+
+ # ↓Prefix hex number ←|→ decimal number
+ elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
+ encoder.text_token(match, :float)
+
+ # ↓Prefix hex number ←|→ decimal number
+ elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
+ encoder.text_token(match, :integer)
+
+ elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators
+ encoder.text_token(match, :operator)
+
+ elsif match = scan(/\s+/) # Space
+ encoder.text_token(match, :space)
+
+ else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors.
+ encoder.text_token(getch, :error)
+ end
+
+ # It may be that we’re scanning a full-blown subexpression of a table
+ # (tables can contain full expressions in parts).
+ # If this is the case, return to :map scanning state.
+ state = :map if state == :initial && brace_depth >= 1
+
+ when :function_expected
+ if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name
+ encoder.text_token(match, :operator)
+ state = :initial
+ 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
+ encoder.text_token(match, :ident)
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo()
+ encoder.text_token(match, :function)
+ state = :initial
+ elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace
+ encoder.text_token(match, :space)
+ else
+ encoder.text_token(getch, :error)
+ state = :initial
+ end
+
+ when :goto_label_expected
+ if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
+ encoder.text_token(match, :label)
+ state = :initial
+ elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace
+ encoder.text_token(match, :space)
+ else
+ encoder.text_token(getch, :error)
+ end
+
+ when :local_var_expected
+ if match = scan(/function/) # local function ...
+ encoder.text_token(match, :keyword)
+ state = :function_expected
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
+ encoder.text_token(match, :local_variable)
+ elsif match = scan(/,/)
+ encoder.text_token(match, :operator)
+ elsif match = scan(/\=/)
+ encoder.text_token(match, :operator)
+ # After encountering the equal sign, arbitrary expressions are
+ # allowed again, so just return to the main state for further
+ # parsing.
+ state = :initial
+ elsif match = scan(/\n/)
+ encoder.text_token(match, :space)
+ state = :initial
+ elsif match = scan(/\s+/)
+ encoder.text_token(match, :space)
+ else
+ encoder.text_token(getch, :error)
+ end
+
+ when :long_comment
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m)
+ encoder.text_token(match, :content)
+
+ delim = scan(/\]={#{num_equals}}\]/)
+ encoder.text_token(delim, :delimiter)
+ else # No terminator found till EOF
+ encoder.text_token(rest, :error)
+ terminate
+ end
+ encoder.end_group(:comment)
+ state = :initial
+
+ when :long_string
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences
+ encoder.text_token(match, :content)
+
+ delim = scan(/\]={#{num_equals}}\]/)
+ encoder.text_token(delim, :delimiter)
+ else # No terminator found till EOF
+ encoder.text_token(rest, :error)
+ terminate
+ end
+ encoder.end_group(:string)
+ state = :initial
+
+ when :string
+ 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)
+ encoder.text_token(match, :content)
+ elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m)
+ encoder.text_token(match, :char)
+ elsif match = scan(Regexp.compile(start_delim))
+ encoder.text_token(match, :delimiter)
+ encoder.end_group(:string)
+ state = :initial
+ elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings
+ encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings
+ encoder.end_group(:string)
+ state = :initial
+ else
+ encoder.text_token(getch, :error)
+ end
+
+ when :map
+ if match = scan(/[,;]/)
+ encoder.text_token(match, :operator)
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x)
+ encoder.text_token(match, :key)
+ encoder.text_token(scan(/\s+/), :space) if check(/\s+/)
+ encoder.text_token(scan(/\=/), :operator)
+ state = :initial
+ elsif match = scan(/\s+/m)
+ encoder.text_token(match, :space)
+ else
+ # Note this clause doesn’t advance the scan pointer, it’s a kind of
+ # "retry with other options" (the :initial state then of course
+ # advances the pointer).
+ state = :initial
+ end
+ else
+ raise
+ end
+
+ end
+
+ if options[:keep_state]
+ @state = state
+ end
+
+ encoder.end_group :string if [:string].include? state
+ brace_depth.times { encoder.end_group :map }
+
+ encoder
+ end
+
+ end
+
+end
+end
diff --git a/lib/coderay/scanners/php.rb b/lib/coderay/scanners/php.rb
index 6c68834..7a8d75d 100644
--- a/lib/coderay/scanners/php.rb
+++ b/lib/coderay/scanners/php.rb
@@ -265,7 +265,7 @@ module Scanners
@html_scanner.tokenize match unless match.empty?
end
- when :php
+ when :php, :php_inline
if match = scan(/\s+/)
encoder.text_token match, :space
@@ -332,7 +332,7 @@ module Scanners
if states.size == 1
encoder.text_token match, :error
else
- states.pop
+ state = states.pop
if states.last.is_a?(::Array)
delimiter = states.last[1]
states[-1] = states.last[0]
@@ -340,6 +340,7 @@ module Scanners
encoder.end_group :inline
else
encoder.text_token match, :operator
+ encoder.end_group :inline if state == :php_inline
label_expected = true
end
end
@@ -350,7 +351,14 @@ module Scanners
elsif match = scan(RE::PHP_END)
encoder.text_token match, :inline_delimiter
- states = [:initial]
+ while state = states.pop
+ encoder.end_group :string if [:sqstring, :dqstring].include? state
+ if state.is_a? Array
+ encoder.end_group :inline
+ encoder.end_group :string if [:sqstring, :dqstring].include? state.first
+ end
+ end
+ states << :initial
elsif match = scan(/<<<(?:(#{RE::IDENTIFIER})|"(#{RE::IDENTIFIER})"|'(#{RE::IDENTIFIER})')/o)
encoder.begin_group :string
@@ -400,6 +408,7 @@ module Scanners
elsif match = scan(/\\/)
encoder.text_token match, :error
else
+ encoder.end_group :string
states.pop
end
@@ -459,7 +468,7 @@ module Scanners
encoder.begin_group :inline
states[-1] = [states.last, delimiter]
delimiter = nil
- states.push :php
+ states.push :php_inline
encoder.text_token match, :delimiter
else
encoder.text_token match, :content
@@ -469,6 +478,7 @@ module Scanners
elsif match = scan(/\$/)
encoder.text_token match, :content
else
+ encoder.end_group :string
states.pop
end
@@ -500,6 +510,14 @@ module Scanners
end
+ while state = states.pop
+ encoder.end_group :string if [:sqstring, :dqstring].include? state
+ if state.is_a? Array
+ encoder.end_group :inline
+ encoder.end_group :string if [:sqstring, :dqstring].include? state.first
+ end
+ end
+
encoder
end
diff --git a/lib/coderay/scanners/python.rb b/lib/coderay/scanners/python.rb
index a9492ab..09c8b6e 100644
--- a/lib/coderay/scanners/python.rb
+++ b/lib/coderay/scanners/python.rb
@@ -157,12 +157,12 @@ module Scanners
encoder.text_token match, :operator
elsif match = scan(/(u?r?|b)?("""|"|'''|')/i)
+ modifiers = self[1]
string_delimiter = self[2]
- string_type = docstring_coming ? :docstring : :string
+ string_type = docstring_coming ? :docstring : (modifiers == 'b' ? :binary : :string)
docstring_coming = false if docstring_coming
encoder.begin_group string_type
string_raw = false
- modifiers = self[1]
unless modifiers.empty?
string_raw = !!modifiers.index(?r)
encoder.text_token modifiers, :modifier
diff --git a/lib/coderay/scanners/raydebug.rb b/lib/coderay/scanners/raydebug.rb
index 7a21354..1effdc8 100644
--- a/lib/coderay/scanners/raydebug.rb
+++ b/lib/coderay/scanners/raydebug.rb
@@ -1,23 +1,30 @@
+require 'set'
+
module CodeRay
module Scanners
-
- # = Debug Scanner
+
+ # = Raydebug Scanner
#
- # Parses the output of the Encoders::Debug encoder.
+ # Highlights the output of the Encoders::Debug encoder.
class Raydebug < Scanner
-
+
register_for :raydebug
file_extension 'raydebug'
title 'CodeRay Token Dump'
protected
+ def setup
+ super
+ @known_token_kinds = TokenKinds.keys.map(&:to_s).to_set
+ end
+
def scan_tokens encoder, options
-
+
opened_tokens = []
-
+
until eos?
-
+
if match = scan(/\s+/)
encoder.text_token match, :space
@@ -26,20 +33,22 @@ module Scanners
encoder.text_token kind, :class
encoder.text_token '(', :operator
match = self[2]
- encoder.text_token match, kind.to_sym
+ unless match.empty?
+ if @known_token_kinds.include? kind
+ encoder.text_token match, kind.to_sym
+ else
+ encoder.text_token match, :plain
+ end
+ end
encoder.text_token match, :operator if match = scan(/\)/)
elsif match = scan(/ (\w+) ([<\[]) /x)
- kind = self[1]
- case self[2]
- when '<'
- encoder.text_token kind, :class
- when '['
- encoder.text_token kind, :class
+ encoder.text_token self[1], :class
+ if @known_token_kinds.include? self[1]
+ kind = self[1].to_sym
else
- raise 'CodeRay bug: This case should not be reached.'
+ kind = :unknown
end
- kind = kind.to_sym
opened_tokens << kind
encoder.begin_group kind
encoder.text_token self[2], :operator
@@ -59,8 +68,8 @@ module Scanners
encoder
end
-
+
end
-
+
end
end
diff --git a/lib/coderay/scanners/ruby.rb b/lib/coderay/scanners/ruby.rb
index c282f31..5b8de42 100644
--- a/lib/coderay/scanners/ruby.rb
+++ b/lib/coderay/scanners/ruby.rb
@@ -164,15 +164,19 @@ module Scanners
end
elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx)
- encoder.begin_group :string
if match.size == 1
+ kind = check(self.class::StringState.simple_key_pattern(match)) ? :key : :string
+ encoder.begin_group kind
encoder.text_token match, :delimiter
- state = self.class::StringState.new :string, match == '"', match # important for streaming
+ state = self.class::StringState.new kind, match == '"', match # important for streaming
else
+ kind = value_expected == true && scan(/:/) ? :key : :string
+ encoder.begin_group kind
encoder.text_token match[0,1], :delimiter
encoder.text_token match[1..-2], :content if match.size > 2
encoder.text_token match[-1,1], :delimiter
- encoder.end_group :string
+ encoder.end_group kind
+ encoder.text_token ':', :operator if kind == :key
value_expected = false
end
@@ -191,11 +195,14 @@ module Scanners
encoder.text_token match, :error
method_call_expected = false
else
- encoder.text_token match, self[1] ? :float : :integer # TODO: send :hex/:octal/:binary
+ kind = self[1] ? :float : :integer # TODO: send :hex/:octal/:binary
+ match << 'r' if match !~ /e/i && scan(/r/)
+ match << 'i' if scan(/i/)
+ encoder.text_token match, kind
end
value_expected = false
- elsif match = scan(/ [-+!~^\/]=? | [:;] | [*|&]{1,2}=? | >>? /x)
+ elsif match = scan(/ [-+!~^\/]=? | [:;] | &\. | [*|&]{1,2}=? | >>? /x)
value_expected = true
encoder.text_token match, :operator
@@ -208,7 +215,7 @@ module Scanners
encoder.end_group kind
heredocs ||= [] # create heredocs if empty
heredocs << self.class::StringState.new(kind, quote != "'", delim,
- self[1] == '-' ? :indented : :linestart)
+ self[1] ? :indented : :linestart)
value_expected = false
elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o)
@@ -269,7 +276,7 @@ module Scanners
end
if last_state
- state = last_state
+ state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens
last_state = nil
end
diff --git a/lib/coderay/scanners/ruby/patterns.rb b/lib/coderay/scanners/ruby/patterns.rb
index ed071d2..e5a156d 100644
--- a/lib/coderay/scanners/ruby/patterns.rb
+++ b/lib/coderay/scanners/ruby/patterns.rb
@@ -114,7 +114,7 @@ module Scanners
# NOTE: This is not completely correct, but
# nobody needs heredoc delimiters ending with \n.
HEREDOC_OPEN = /
- << (-)? # $1 = float
+ << ([-~])? # $1 = float
(?:
( [A-Za-z_0-9]+ ) # $2 = delim
|
@@ -157,13 +157,16 @@ module Scanners
yield
])
- FANCY_STRING_START = / % ( [QqrsWwx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x
+ FANCY_STRING_START = / % ( [iIqQrswWx] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /x
FANCY_STRING_KIND = Hash.new(:string).merge({
+ 'i' => :symbol,
+ 'I' => :symbol,
'r' => :regexp,
's' => :symbol,
'x' => :shell,
})
FANCY_STRING_INTERPRETED = Hash.new(true).merge({
+ 'i' => false,
'q' => false,
's' => false,
'w' => false,
diff --git a/lib/coderay/scanners/ruby/string_state.rb b/lib/coderay/scanners/ruby/string_state.rb
index 2f398d1..95f1e83 100644
--- a/lib/coderay/scanners/ruby/string_state.rb
+++ b/lib/coderay/scanners/ruby/string_state.rb
@@ -16,7 +16,6 @@ module Scanners
STRING_PATTERN = Hash.new do |h, k|
delim, interpreted = *k
- # delim = delim.dup # workaround for old Ruby
delim_pattern = Regexp.escape(delim)
if closing_paren = CLOSING_PAREN[delim]
delim_pattern << Regexp.escape(closing_paren)
@@ -29,12 +28,21 @@ module Scanners
# '| [|?*+(){}\[\].^$]'
# end
- h[k] =
- if interpreted && delim != '#'
- / (?= [#{delim_pattern}] | \# [{$@] ) /mx
- else
- / (?= [#{delim_pattern}] ) /mx
- end
+ if interpreted && delim != '#'
+ / (?= [#{delim_pattern}] | \# [{$@] ) /mx
+ else
+ / (?= [#{delim_pattern}] ) /mx
+ end.tap do |pattern|
+ h[k] = pattern if (delim.respond_to?(:ord) ? delim.ord : delim[0]) < 256
+ end
+ end
+
+ def self.simple_key_pattern delim
+ if delim == "'"
+ / (?> (?: [^\\']+ | \\. )* ) ' : /mx
+ else
+ / (?> (?: [^\\"\#]+ | \\. | \#\$[\\"] | \#\{[^\{\}]+\} | \#(?!\{) )* ) " : /mx
+ end
end
def initialize kind, interpreted, delim, heredoc = false
diff --git a/lib/coderay/scanners/sass.rb b/lib/coderay/scanners/sass.rb
index 167051d..e3296b9 100644
--- a/lib/coderay/scanners/sass.rb
+++ b/lib/coderay/scanners/sass.rb
@@ -7,11 +7,6 @@ module Scanners
register_for :sass
file_extension 'sass'
- STRING_CONTENT_PATTERN = {
- "'" => /(?:[^\n\'\#]+|\\\n|#{RE::Escape}|#(?!\{))+/,
- '"' => /(?:[^\n\"\#]+|\\\n|#{RE::Escape}|#(?!\{))+/,
- }
-
protected
def setup
@@ -19,8 +14,9 @@ module Scanners
end
def scan_tokens encoder, options
- states = Array(options[:state] || @state)
- string_delimiter = nil
+ states = Array(options[:state] || @state).dup
+
+ encoder.begin_group :string if states.last == :sqstring || states.last == :dqstring
until eos?
@@ -48,7 +44,7 @@ module Scanners
elsif case states.last
when :initial, :media, :sass_inline
if match = scan(/(?>#{RE::Ident})(?!\()/ox)
- encoder.text_token match, value_expected ? :value : (check(/.*:/) ? :key : :tag)
+ encoder.text_token match, value_expected ? :value : (check(/.*:(?![a-z])/) ? :key : :tag)
next
elsif !value_expected && (match = scan(/\*/))
encoder.text_token match, :tag
@@ -91,24 +87,23 @@ module Scanners
next
end
- when :string
- if match = scan(STRING_CONTENT_PATTERN[string_delimiter])
+ when :sqstring, :dqstring
+ if match = scan(states.last == :sqstring ? /(?:[^\n\'\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o : /(?:[^\n\"\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o)
encoder.text_token match, :content
elsif match = scan(/['"]/)
encoder.text_token match, :delimiter
encoder.end_group :string
- string_delimiter = nil
states.pop
elsif match = scan(/#\{/)
encoder.begin_group :inline
encoder.text_token match, :inline_delimiter
states.push :sass_inline
elsif match = scan(/ \\ | $ /x)
- encoder.end_group :string
+ encoder.end_group states.last
encoder.text_token match, :error unless match.empty?
states.pop
else
- raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder
+ raise_inspect "else case #{states.last} reached; %p not handled." % peek(1), encoder
end
when :include
@@ -119,7 +114,7 @@ module Scanners
else
#:nocov:
- raise_inspect 'Unknown state', encoder
+ raise_inspect 'Unknown state: %p' % [states.last], encoder
#:nocov:
end
@@ -157,15 +152,15 @@ module Scanners
elsif match = scan(/['"]/)
encoder.begin_group :string
- string_delimiter = match
encoder.text_token match, :delimiter
if states.include? :sass_inline
- content = scan_until(/(?=#{string_delimiter}|\}|\z)/)
+ # no nesting, just scan the string until delimiter
+ content = scan_until(/(?=#{match}|\}|\z)/)
encoder.text_token content, :content unless content.empty?
- encoder.text_token string_delimiter, :delimiter if scan(/#{string_delimiter}/)
+ encoder.text_token match, :delimiter if scan(/#{match}/)
encoder.end_group :string
else
- states.push :string
+ states.push match == "'" ? :sqstring : :dqstring
end
elsif match = scan(/#{RE::Function}/o)
@@ -176,7 +171,7 @@ module Scanners
encoder.text_token match[start.size..-2], :content
encoder.text_token ')', :delimiter
else
- encoder.text_token match[start.size..-1], :content
+ encoder.text_token match[start.size..-1], :content if start.size < match.size
end
encoder.end_group :function
@@ -195,7 +190,7 @@ module Scanners
elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/)
encoder.text_token match, :color
- elsif match = scan(/@else if\b|#{RE::AtKeyword}/)
+ elsif match = scan(/@else if\b|#{RE::AtKeyword}/o)
encoder.text_token match, :directive
value_expected = true
@@ -214,8 +209,18 @@ module Scanners
end
+ states.pop if states.last == :include
+
if options[:keep_state]
- @state = states
+ @state = states.dup
+ end
+
+ while state = states.pop
+ if state == :sass_inline
+ encoder.end_group :inline
+ elsif state == :sqstring || state == :dqstring
+ encoder.end_group :string
+ end
end
encoder
diff --git a/lib/coderay/scanners/sql.rb b/lib/coderay/scanners/sql.rb
index b757278..7d57f77 100644
--- a/lib/coderay/scanners/sql.rb
+++ b/lib/coderay/scanners/sql.rb
@@ -1,8 +1,9 @@
-module CodeRay module Scanners
+module CodeRay
+module Scanners
# by Josh Goebel
class SQL < Scanner
-
+
register_for :sql
KEYWORDS = %w(
@@ -56,6 +57,12 @@ module CodeRay module Scanners
STRING_PREFIXES = /[xnb]|_\w+/i
+ STRING_CONTENT_PATTERN = {
+ '"' => / (?: [^\\"] | "" )+ /x,
+ "'" => / (?: [^\\'] | '' )+ /x,
+ '`' => / (?: [^\\`] | `` )+ /x,
+ }
+
def scan_tokens encoder, options
state = :initial
@@ -89,7 +96,7 @@ module CodeRay module Scanners
state = :string
encoder.text_token match, :delimiter
- elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9]* /x)
+ elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9\$]* /x)
encoder.text_token match, name_expected ? :ident : (match[0] == ?@ ? :variable : IDENT_KIND[match])
name_expected = false
@@ -114,41 +121,28 @@ module CodeRay module Scanners
end
elsif state == :string
- if match = scan(/[^\\"'`]+/)
- string_content << match
- next
+ if match = scan(STRING_CONTENT_PATTERN[string_type])
+ encoder.text_token match, :content
elsif match = scan(/["'`]/)
if string_type == match
if peek(1) == string_type # doubling means escape
- string_content << string_type << getch
- next
- end
- unless string_content.empty?
- encoder.text_token string_content, :content
- string_content = ''
+ encoder.text_token match + getch, :content
+ else
+ encoder.text_token match, :delimiter
+ encoder.end_group :string
+ state = :initial
+ string_type = nil
end
- encoder.text_token match, :delimiter
- encoder.end_group :string
- state = :initial
- string_type = nil
else
- string_content << match
+ encoder.text_token match, :content
end
elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
- unless string_content.empty?
- encoder.text_token string_content, :content
- string_content = ''
- end
encoder.text_token match, :char
elsif match = scan(/ \\ . /mox)
- string_content << match
- next
+ encoder.text_token match, :content
elsif match = scan(/ \\ | $ /x)
- unless string_content.empty?
- encoder.text_token string_content, :content
- string_content = ''
- end
encoder.text_token match, :error unless match.empty?
+ encoder.end_group :string
state = :initial
else
raise "else case \" reached; %p not handled." % peek(1), encoder
@@ -171,4 +165,5 @@ module CodeRay module Scanners
end
-end end \ No newline at end of file
+end
+end
diff --git a/lib/coderay/scanners/taskpaper.rb b/lib/coderay/scanners/taskpaper.rb
new file mode 100644
index 0000000..42670bc
--- /dev/null
+++ b/lib/coderay/scanners/taskpaper.rb
@@ -0,0 +1,36 @@
+module CodeRay
+module Scanners
+
+ class Taskpaper < Scanner
+
+ register_for :taskpaper
+ file_extension 'taskpaper'
+
+ protected
+
+ def scan_tokens encoder, options
+ until eos?
+ if match = scan(/\S.*:.*$/) # project
+ encoder.text_token(match, :namespace)
+ elsif match = scan(/-.+@done.*/) # completed task
+ encoder.text_token(match, :done)
+ elsif match = scan(/-(?:[^@\n]+|@(?!due))*/) # task
+ encoder.text_token(match, :plain)
+ elsif match = scan(/@due.*/) # comment
+ encoder.text_token(match, :important)
+ elsif match = scan(/.+/) # comment
+ encoder.text_token(match, :comment)
+ elsif match = scan(/\s+/) # space
+ encoder.text_token(match, :space)
+ else # other
+ encoder.text_token getch, :error
+ end
+ end
+
+ encoder
+ end
+
+ end
+
+end
+end
diff --git a/lib/coderay/scanners/yaml.rb b/lib/coderay/scanners/yaml.rb
index 96f4e93..32c8e2c 100644
--- a/lib/coderay/scanners/yaml.rb
+++ b/lib/coderay/scanners/yaml.rb
@@ -47,7 +47,7 @@ module Scanners
when !check(/(?:"[^"]*")(?=: |:$)/) && match = scan(/"/)
encoder.begin_group :string
encoder.text_token match, :delimiter
- encoder.text_token match, :content if match = scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx)
+ encoder.text_token match, :content if (match = scan(/ [^"\\]* (?: \\. [^"\\]* )* /mx)) && !match.empty?
encoder.text_token match, :delimiter if match = scan(/"/)
encoder.end_group :string
next
@@ -84,7 +84,7 @@ module Scanners
when match = scan(/(?:"[^"\n]*"|'[^'\n]*')(?= *:(?: |$))/)
encoder.begin_group :key
encoder.text_token match[0,1], :delimiter
- encoder.text_token match[1..-2], :content
+ encoder.text_token match[1..-2], :content if match.size > 2
encoder.text_token match[-1,1], :delimiter
encoder.end_group :key
key_indent = column(pos - match.size) - 1