Module: Synvert::Core::Engine::Elegant

Included in:
Haml, Slim
Defined in:
lib/synvert/core/engine/elegant.rb

Overview

Engine::Elegant provides some helper methods for engines who read block code without end. For those engines, it will try to insert end\n when encode, while delete end\n when generate_transform_proc.

Constant Summary collapse

END_LINE =
"end\n"
WHITESPACE =
' '
DO_BLOCK_REGEX =
/ do(\s|\z|\n)/
IF_KEYWORDS =
%w[if unless begin case for while until]
ELSE_KEYWORDS =
%w[else elsif when in rescue ensure]

Instance Method Summary collapse

Instance Method Details

#generate_transform_proc(encoded_source) ⇒ Proc

Generate transform proc, it's used to adjust start and end position of actions. Due to the fact that we insert end\n when encode the source code, we need to adjust start and end position of actions to match the original source code. e.g. if end\n exists in position 10, action start position is 20 and end position is 30, then action start position should be 16 and end position should be 26.

Parameters:

  • encoded_source (String)

    encoded source.

Returns:

  • (Proc)

    transform proc.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/synvert/core/engine/elegant.rb', line 25

def generate_transform_proc(encoded_source)
  proc do |actions|
    start = 0
    indices = []
    loop do
      index = encoded_source[start..-1].index(END_LINE)
      break unless index

      indices << (start + index)
      start += index + END_LINE.length
    end
    indices.each do |index|
      NodeMutation::Helper.iterate_actions(actions) do |action|
        action.start -= END_LINE.length if action.start > index
        action.end -= END_LINE.length if action.end > index
      end
    end
  end
end

#insert_end?(leading_spaces_counts, current_leading_spaces_count, equal = true) ⇒ Boolean

Check if the current leading_spaces_count is less than or equal to the last leading_spaces_count in leading_spaces_counts. If so, pop the last leading_spaces_count and return true.

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
55
# File 'lib/synvert/core/engine/elegant.rb', line 47

def insert_end?(leading_spaces_counts, current_leading_spaces_count, equal = true)
  operation = equal ? :<= : :<
  if !leading_spaces_counts.empty? && current_leading_spaces_count.send(operation, leading_spaces_counts.last)
    leading_spaces_counts.pop
    true
  else
    false
  end
end

#scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/synvert/core/engine/elegant.rb', line 79

def scan_ruby_expression(scanner, new_code, leading_spaces_counts, leading_spaces_count)
  if insert_end?(leading_spaces_counts, leading_spaces_count)
    new_code << END_LINE
  end
  rest = scanner.scan(/.*?(\z|\n)/)
  if rest =~ DO_BLOCK_REGEX
    leading_spaces_counts << leading_spaces_count
  end
  new_code << rest

  while rest.rstrip.end_with?(',')
    rest = scanner.scan(/.*?(\z|\n)/)
    if rest =~ DO_BLOCK_REGEX
      leading_spaces_counts << leading_spaces_count
    end
    new_code << rest
  end
end

#scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/synvert/core/engine/elegant.rb', line 98

def scan_ruby_interpolation_and_plain_text(scanner, new_code, leading_spaces_counts, leading_spaces_count)
  if insert_end?(leading_spaces_counts, leading_spaces_count)
    new_code << END_LINE
  end
  while scanner.scan(/(.*?)(\\*)#\{/) # it matches interpolation "  #{current_user.login}"
    new_code << (WHITESPACE * scanner.matched.size)
    unless scanner.matched[-3] == '\\'
      count = 1
      while scanner.scan(/.*?([\{\}])/)
        if scanner.matched[-1] == '}'
          count -= 1
          if count == 0
            new_code << (scanner.matched[0..-2] + ';')
            break
          else
            new_code << scanner.matched
          end
        else
          count += 1
          new_code << scanner.matched
        end
      end
    end
  end
  if scanner.scan(/.*?\z/)
    new_code << (WHITESPACE * scanner.matched.size)
  end
  if scanner.scan(/.*?\n/)
    new_code << ((WHITESPACE * (scanner.matched.size - 1)) + "\n")
  end
end

#scan_ruby_statement(scanner, new_code, leading_spaces_counts, leading_spaces_count) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/synvert/core/engine/elegant.rb', line 57

def scan_ruby_statement(scanner, new_code, leading_spaces_counts, leading_spaces_count)
  new_code << scanner.scan(/\s*/)
  keyword = scanner.scan(/\w+/)
  rest = scanner.scan(/.*?(\z|\n)/)
  if insert_end?(leading_spaces_counts, leading_spaces_count, !ELSE_KEYWORDS.include?(keyword))
    new_code << END_LINE
  end
  if IF_KEYWORDS.include?(keyword) || rest =~ DO_BLOCK_REGEX
    leading_spaces_counts << leading_spaces_count
  end
  new_code << keyword
  new_code << rest

  while rest.rstrip.end_with?(',')
    rest = scanner.scan(/.*?(\z|\n)/)
    if IF_KEYWORDS.include?(keyword) || rest =~ DO_BLOCK_REGEX
      leading_spaces_counts << leading_spaces_count
    end
    new_code << rest
  end
end