Module: Astel::Rewriter::Structured

Included in:
Astel::Rewriter
Defined in:
lib/astel/rewriter/structured.rb,
lib/astel/rewriter/structured/comments.rb,
lib/astel/rewriter/structured/formatting.rb,
lib/astel/rewriter/structured/collections.rb,
lib/astel/rewriter/structured/source_indenter.rb

Instance Method Summary collapse

Instance Method Details

#comment_out(node) ⇒ Object



56
57
58
59
60
61
62
63
64
65
# File 'lib/astel/rewriter/structured/formatting.rb', line 56

def comment_out(node)
  first = @source_file.line_start(node.location.start_offset)
  last = @source_file.line_end([node_end_offset(node) - 1, node.location.start_offset].max)
  text = @source_file.source.byteslice(first, last - first)
  commented = text.lines(chomp: false).map do |line|
    indentation = line[/\A[ \t]*/]
    "#{indentation}# #{line.delete_prefix(indentation)}"
  end.join
  replace(first...last, commented)
end

#dedent(text) ⇒ Object



17
18
19
20
# File 'lib/astel/rewriter/structured/formatting.rb', line 17

def dedent(text)
  width = dedentation_width(text)
  text.lines(chomp: false).map { |line| line.strip.empty? ? line : line.byteslice(width..) }.join
end

#find_keyword_argument(call_node, name) ⇒ Object



8
9
10
11
12
13
14
# File 'lib/astel/rewriter/structured.rb', line 8

def find_keyword_argument(call_node, name)
  keyword_hashes(call_node).each do |keyword_hash|
    match = keyword_hash.elements.find { |element| keyword_name(element) == name.to_sym }
    return match if match
  end
  nil
end

#indent(text, level: 1) ⇒ Object



12
13
14
15
# File 'lib/astel/rewriter/structured/formatting.rb', line 12

def indent(text, level: 1)
  prefix = @source_file.indent_unit * level
  text.lines(chomp: false).map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join
end

#indentation_of(node) ⇒ Object



8
9
10
# File 'lib/astel/rewriter/structured/formatting.rb', line 8

def indentation_of(node)
  @source_file.indentation_at(node.location.start_offset)
end

#insert_into_body(node, source_text, position: :end) ⇒ Object



27
28
29
30
31
32
33
34
# File 'lib/astel/rewriter/structured/formatting.rb', line 27

def insert_into_body(node, source_text, position: :end)
  statements = body_statements(node)
  target, after, section_break = body_insertion_target(node, statements, position)
  content = format_body_source(source_text, body_child_indentation(node, statements))
  return insert_body_source_after(node, target, content) if after

  insert_body_source_before(node, target, content, section_break: section_break)
end

#insert_keyword_argument(call_node, text, position: :last) ⇒ Object

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/astel/rewriter/structured.rb', line 43

def insert_keyword_argument(call_node, text, position: :last)
  raise ArgumentError, 'position must be :first or :last' unless i[first last].include?(position)

  arguments = call_node.arguments
  return insert_first_keyword_argument(call_node, text) unless arguments

  keyword_hash = keyword_hashes(call_node).first
  return insert_into_keyword_hash(keyword_hash, text, position) if keyword_hash

  index = arguments.arguments.length
  insert_list_element(arguments, index, text)
end

#insert_line_after(node, text) ⇒ Object



49
50
51
52
53
54
# File 'lib/astel/rewriter/structured/formatting.rb', line 49

def insert_line_after(node, text)
  line = @source_file.line_location_with_newline(node_end_offset(node) - 1)
  content = format_body_source(text, indentation_of(node))
  prefix = line_terminated?(line) ? '' : @source_file.newline
  insert_after(line, "#{prefix}#{content}#{@source_file.newline}")
end

#insert_line_before(node, text) ⇒ Object



43
44
45
46
47
# File 'lib/astel/rewriter/structured/formatting.rb', line 43

def insert_line_before(node, text)
  line_start = @source_file.line_start(node.location.start_offset)
  content = format_body_source(text, indentation_of(node))
  insert_before(line_start...line_start, "#{content}#{@source_file.newline}")
end

#insert_list_element(collection_node, index, text) ⇒ Object

Raises:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/astel/rewriter/structured/collections.rb', line 30

def insert_list_element(collection_node, index, text)
  elements = collection_elements(collection_node)
  raise IndexError, 'list index is outside the collection' unless index.between?(0, elements.length)

  if elements.empty?
    opening = collection_node.respond_to?(:opening_loc) && collection_node.opening_loc
    raise ArgumentError, 'cannot locate an empty collection body' unless opening

    return insert_into_empty_delimited(collection_node, text)
  end

  if index < elements.length
    target = elements[index]
    return insert_before(target.location, "#{text}#{list_separator(collection_node, target)}")
  end

  last = elements.last
  insert_after(last.location, "#{list_separator(collection_node, last)}#{text}")
end

#leading_comments(node) ⇒ Object



6
7
8
9
# File 'lib/astel/rewriter/structured/comments.rb', line 6

def leading_comments(node)
  attach_comments
  node.location.leading_comments
end

#reindent(text, to:) ⇒ Object



22
23
24
25
# File 'lib/astel/rewriter/structured/formatting.rb', line 22

def reindent(text, to:)
  prefix = indentation_of(to)
  dedent(text).lines(chomp: false).map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join
end

#remove_keyword_argument(call_node, name) ⇒ Object



16
17
18
19
20
21
22
23
24
# File 'lib/astel/rewriter/structured.rb', line 16

def remove_keyword_argument(call_node, name)
  argument = find_keyword_argument(call_node, name)
  return self unless argument

  keyword_hash = keyword_hashes(call_node).find { |hash| hash.elements.include?(argument) }
  return remove_sole_keyword_argument(call_node, keyword_hash) if keyword_hash.elements.one?

  remove_list_element(keyword_hash, argument)
end

#remove_line(node) ⇒ Object



36
37
38
39
40
41
# File 'lib/astel/rewriter/structured/formatting.rb', line 36

def remove_line(node)
  remove_lines_if_isolated(
    start_offset: node.location.start_offset,
    end_offset: node_end_offset(node)
  )
end

#remove_list_element(collection_node, element_node) ⇒ Object



6
7
8
# File 'lib/astel/rewriter/structured/collections.rb', line 6

def remove_list_element(collection_node, element_node)
  remove_list_elements(collection_node, [element_node])
end

#remove_list_elements(collection_node, element_nodes) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/astel/rewriter/structured/collections.rb', line 10

def remove_list_elements(collection_node, element_nodes)
  elements = collection_elements(collection_node)
  indexes = element_indexes(elements, element_nodes)
  return self if indexes.empty?

  trailing_end = trailing_comma_end(elements.last)
  start_offset = elements.first.location.start_offset
  content = content_without_elements(
    elements, indexes, start_offset: start_offset, end_offset: trailing_end
  )
  extra_ranges = indexes.flat_map { |index| heredoc_ranges(elements[index]) }
  return replace(start_offset...trailing_end, content) if extra_ranges.empty?

  transaction(raise_on_conflict: true) do |rewriter|
    rewriter.replace(start_offset...trailing_end, content)
    extra_ranges.each { |range| rewriter.remove(range) }
  end
  self
end

#remove_with_comments(node) ⇒ Object



16
17
18
19
20
21
22
# File 'lib/astel/rewriter/structured/comments.rb', line 16

def remove_with_comments(node)
  comments = leading_comments(node)
  trailing = trailing_comment(node)
  start_offset = comments.empty? ? node.location.start_offset : comments.first.location.start_offset
  end_offset = [node_end_offset(node), trailing&.location&.end_offset].compact.max
  remove_lines_if_isolated(start_offset: start_offset, end_offset: end_offset)
end

#replace_keyword_key(assoc_node, name) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/astel/rewriter/structured.rb', line 26

def replace_keyword_key(assoc_node, name)
  if implicit_keyword_value?(assoc_node)
    return replace(assoc_node.location, "#{name}: #{assoc_node.value.value.name}")
  end

  replacement = assoc_node.operator_loc ? ":#{name}" : "#{name}:"
  replace(assoc_node.key.location, replacement)
end

#replace_keyword_value(assoc_node, replacement) ⇒ Object



35
36
37
38
39
40
41
# File 'lib/astel/rewriter/structured.rb', line 35

def replace_keyword_value(assoc_node, replacement)
  if implicit_keyword_value?(assoc_node)
    return replace(assoc_node.location, "#{@source_file.slice(assoc_node.key.location)} #{replacement}")
  end

  replace_expression(assoc_node.value, replacement)
end

#trailing_comment(node) ⇒ Object



11
12
13
14
# File 'lib/astel/rewriter/structured/comments.rb', line 11

def trailing_comment(node)
  attach_comments
  node.location.trailing_comments.first
end