summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/ruby/string_state.rb
blob: 2f398d1edd0f9a43e52725c979b8e95fd05f7fe7 (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
# encoding: utf-8
module CodeRay
module Scanners
  
  class Ruby
    
    class StringState < Struct.new :type, :interpreted, :delim, :heredoc,
      :opening_paren, :paren_depth, :pattern, :next_state  # :nodoc: all
      
      CLOSING_PAREN = Hash[ *%w[
        ( )
        [ ]
        < >
        { }
      ] ].each { |k,v| k.freeze; v.freeze }  # debug, if I try to change it with <<
      
      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)
        end
        delim_pattern << '\\\\' unless delim == '\\'
        
        # special_escapes =
        #   case interpreted
        #   when :regexp_symbols
        #     '| [|?*+(){}\[\].^$]'
        #   end
        
        h[k] =
          if interpreted && delim != '#'
            / (?= [#{delim_pattern}] | \# [{$@] ) /mx
          else
            / (?= [#{delim_pattern}] ) /mx
          end
      end
      
      def initialize kind, interpreted, delim, heredoc = false
        if heredoc
          pattern = heredoc_pattern delim, interpreted, heredoc == :indented
          delim = nil
        else
          pattern = STRING_PATTERN[ [delim, interpreted] ]
          if closing_paren = CLOSING_PAREN[delim]
            opening_paren = delim
            delim = closing_paren
            paren_depth = 1
          end
        end
        super kind, interpreted, delim, heredoc, opening_paren, paren_depth, pattern, :initial
      end
      
      def heredoc_pattern delim, interpreted, indented
        # delim = delim.dup  # workaround for old Ruby
        delim_pattern = Regexp.escape(delim)
        delim_pattern = / (?:\A|\n) #{ '(?>[ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x
        if interpreted
          / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx  # $1 set == end of heredoc
        else
          / (?= #{delim_pattern}() | \\ ) /mx
        end
      end
      
    end
    
  end
  
end
end