lib/node_mutation/adapter/parser.rb in node_mutation-1.18.3 vs lib/node_mutation/adapter/parser.rb in node_mutation-1.19.0
- old
+ new
@@ -13,10 +13,53 @@
else
node.loc.expression.source
end
end
+ # It gets the new source code after evaluating the node.
+ # @param node [Parser::AST::Node] The node to evaluate.
+ # @param code [String] The code to evaluate.
+ # @return [String] The new source code.
+ # @example
+ # node = Parser::CurrentRuby.parse('Factory.define :user do; end')
+ # rewritten_source(node, '{{call.receiver}}').to eq 'Factory'
+ #
+ # # index for node array
+ # node = Parser::CurrentRuby.parse("test(foo, bar)")
+ # rewritten_source(node, '{{arguments.0}}')).to eq 'foo'
+ #
+ # # {key}_pair for hash node
+ # node = Parser::CurrentRuby.parse("after_commit :do_index, on: :create, if: :indexable?")
+ # rewritten_source(node, '{{arguments.-1.on_pair}}')).to eq 'on: :create'
+ #
+ # # {key}_value for hash node
+ # node = Parser::CurrentRuby.parse("after_commit :do_index, on: :create, if: :indexable?")
+ # rewritten_source(node, '{{arguments.-1.on_value}}')).to eq ':create'
+ #
+ # # to_single_quote for str node
+ # node = Parser::CurrentRuby.parse('"foo"')
+ # rewritten_source(node, 'to_single_quote') => "'foo'"
+ #
+ # # to_double_quote for str node
+ # node = Parser::CurrentRuby.parse("'foo'")
+ # rewritten_source(node, 'to_double_quote') => '"foo"'
+ #
+ # # to_symbol for str node
+ # node = Parser::CurrentRuby.parse("'foo'")
+ # rewritten_source(node, 'to_symbol') => ':foo'
+ #
+ # # to_lambda_literal for block node
+ # node = Parser::CurrentRuby.parse('lambda { foobar }')
+ # rewritten_source(node, 'to_lambda_literal') => '-> { foobar }'
+ #
+ # # strip_curly_braces for hash node
+ # node = Parser::CurrentRuby.parse("{ foo: 'bar' }")
+ # rewritten_source(node, 'strip_curly_braces') => "foo: 'bar'"
+ #
+ # # wrap_curly_braces for hash node
+ # node = Parser::CurrentRuby.parse("test(foo: 'bar')")
+ # rewritten_source(node.arguments.first, 'wrap_curly_braces') => "{ foo: 'bar' }"
def rewritten_source(node, code)
code.gsub(/{{(.+?)}}/m) do
old_code = Regexp.last_match(1)
evaluated = child_node_by_name(node, old_code)
case evaluated
@@ -54,10 +97,50 @@
def file_source(node)
node.loc.expression.source_buffer.source
end
+ # Get the range of the child node.
+ # @param node [Parser::AST::Node] The node.
+ # @param child_name [String] THe name to find child node.
+ # @return {NodeMutation::Struct::Range} The range of the child node.
+ # @example
+ # node = Parser::CurrentRuby.parse('Factory.define :user do; end')
+ # child_node_range(node, 'caller.receiver') => { start: 0, end: 'Factory'.length }
+ #
+ # # node array
+ # node = Parser::CurrentRuby.parse('foobar arg1, arg2)')
+ # child_node_range(node, 'arguments') => { start: 'foobar '.length, end: 'foobar arg1, arg2'.length }
+ #
+ # # index for node array
+ # node = Parser::CurrentRuby.parse('foobar(arg1, arg2)')
+ # child_node_range(node, 'arguments.-1') => { start: 'foobar(arg1, '.length, end: 'foobar(arg1, arg2'.length }
+ #
+ # # pips for block node
+ # node = Parser::CurrentRuby.parse('Factory.define :user do |user|; end')
+ # child_node_range(node, 'pipes') => { start: 'Factory.deine :user do '.length, end: 'Factory.define :user do |user|'.length }
+ #
+ # # parentheses for def and defs node
+ # node = Parser::CurrentRuby.parse('def foo(bar); end')
+ # child_node_range(node, 'parentheses') => { start: 'def foo'.length, end: 'def foo(bar)'.length }
+ #
+ # # double_colon for const node
+ # node = Parser::CurrentRuby.parse('Foo::Bar')
+ # child_node_range(node, 'double_colon') => { start: 'Foo'.length, end: 'Foo::'.length }
+ #
+ # # self and dot for defs node
+ # node = Parser::CurrentRuby.parse('def self.foo(bar); end')
+ # child_node_range(node, 'self') => { start: 'def '.length, end: 'def self'.length }
+ # child_node_range(node, 'dot') => { start: 'def self'.length, end: 'def self.'.length }
+ #
+ # # dot for send and csend node
+ # node = Parser::CurrentRuby.parse('foo.bar(test)')
+ # child_node_range(node, 'self') => { start: 'foo'.length, end: 'foo.'.length }
+ #
+ # # parentheses for send and csend node
+ # node = Parser::CurrentRuby.parse('foo.bar(test)')
+ # child_node_range(node, 'parentheses') => { start: 'foo.bar'.length, end: 'foo.bar(test)'.length }
def child_node_range(node, child_name)
direct_child_name, nested_child_name = child_name.to_s.split('.', 2)
if node.is_a?(Array)
if direct_child_name =~ INDEX_REGEXP
@@ -86,31 +169,12 @@
NodeMutation::Struct::Range.new(
node.arguments.first.loc.expression.begin_pos - 1,
node.arguments.last.loc.expression.end_pos + 1
)
end
- when %i[block arguments], %i[def arguments], %i[defs arguments]
- if node.arguments.empty?
- nil
- else
- NodeMutation::Struct::Range.new(
- node.arguments.first.loc.expression.begin_pos,
- node.arguments.last.loc.expression.end_pos
- )
- end
- when %i[block body], %i[class body], %i[def body], %i[defs body], %i[module body]
- if node.body.empty?
- nil
- else
- NodeMutation::Struct::Range.new(
- node.body.first.loc.expression.begin_pos,
- node.body.last.loc.expression.end_pos
- )
- end
when %i[class name], %i[const name], %i[cvar name], %i[def name], %i[defs name],
%i[gvar name], %i[ivar name], %i[lvar name]
-
NodeMutation::Struct::Range.new(node.loc.name.begin_pos, node.loc.name.end_pos)
when %i[const double_colon]
NodeMutation::Struct::Range.new(node.loc.double_colon.begin_pos, node.loc.double_colon.end_pos)
when %i[defs dot]
NodeMutation::Struct::Range.new(node.loc.operator.begin_pos, node.loc.operator.end_pos) if node.loc.operator
@@ -208,11 +272,28 @@
return child_node
end
if node.respond_to?(direct_child_name)
child_node = node.send(direct_child_name)
- elsif direct_child_name.include?('(') && direct_child_name.include?(')')
- child_node = node.instance_eval(direct_child_name)
+ elsif direct_child_name == 'to_symbol' && node.type == :str
+ child_node = ":#{node.to_value}"
+ elsif direct_child_name == 'to_single_quote' && node.type == :str
+ child_node = "'#{node.to_value}'"
+ elsif direct_child_name == 'to_double_quote' && node.type == :str
+ child_node = "\"#{node.to_value}\""
+ elsif direct_child_name == 'to_lambda_literal' && node.type == :block && node.caller.type == :send && node.caller.receiver.nil? && node.caller.message == :lambda
+ new_source = node.to_source
+ if node.arguments.size > 1
+ new_source = new_source[0...node.arguments.first.loc.expression.begin_pos - 2] + new_source[node.arguments.last.loc.expression.end_pos + 1..-1]
+ new_source = new_source.sub('lambda', "->(#{node.arguments.map(&:to_source).join(', ')})")
+ else
+ new_source = new_source.sub('lambda', '->')
+ end
+ child_node = new_source
+ elsif direct_child_name == 'strip_curly_braces' && node.type == :hash
+ child_node = node.to_source.sub(/^{(.*)}$/) { Regexp.last_match(1).strip }
+ elsif direct_child_name == 'wrap_curly_braces' && node.type == :hash
+ child_node = "{ #{node.to_source} }"
else
raise NodeMutation::MethodNotSupported, "#{direct_child_name} is not supported for #{get_source(node)}"
end
return child_node_by_name(child_node, nested_child_name) if nested_child_name