astel 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,138 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Astel
4
- module Refactor
5
- def indentation_of(node)
6
- @source_file.indentation_at(node.location.start_offset)
7
- end
8
-
9
- def indent(text, level: 1)
10
- prefix = @source_file.indent_unit * level
11
- text.lines(chomp: false).map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join
12
- end
13
-
14
- def dedent(text)
15
- widths = text.lines.filter_map do |line|
16
- next if line.strip.empty?
17
-
18
- line[/\A[ \t]*/].bytesize
19
- end
20
- width = widths.min || 0
21
- text.lines(chomp: false).map { |line| line.strip.empty? ? line : line.byteslice(width..) }.join
22
- end
23
-
24
- def reindent(text, to:)
25
- prefix = indentation_of(to)
26
- dedent(text).lines(chomp: false).map { |line| line.strip.empty? ? line : "#{prefix}#{line}" }.join
27
- end
28
-
29
- def insert_into_body(node, source_text, position: :end)
30
- statements = body_statements(node)
31
- target, after, section_break = body_insertion_target(node, statements, position)
32
- first_statement = statements&.body&.first
33
- child_indent = if first_statement &&
34
- @source_file.line_at(first_statement.location.start_offset) !=
35
- @source_file.line_at(node.location.start_offset)
36
- indentation_of(first_statement)
37
- else
38
- "#{indentation_of(node)}#{@source_file.indent_unit}"
39
- end
40
- content = format_body_source(source_text, child_indent)
41
-
42
- if after
43
- line = @source_file.line_location_with_newline(target.location.end_offset - 1)
44
- suffix = line_terminated?(line) ? '' : @source_file.newline
45
- insert_after(line, "#{suffix}#{content}#{@source_file.newline}")
46
- else
47
- offset = target ? @source_file.line_start(target.location.start_offset) : body_closing_location(node).start_offset
48
- prefix = needs_leading_newline?(offset) ? @source_file.newline : ''
49
- suffix = section_break ? @source_file.newline * 2 : @source_file.newline
50
- insert_before(offset...offset, "#{prefix}#{content}#{suffix}")
51
- end
52
- end
53
-
54
- def remove_line(node)
55
- location = node.location
56
- first = @source_file.line_location(location.start_offset)
57
- last_offset = [location.end_offset - 1, location.start_offset].max
58
- last = @source_file.line_location(last_offset)
59
- before = @source_file.source.byteslice(first.start_offset, location.start_offset - first.start_offset)
60
- after = @source_file.source.byteslice(location.end_offset, last.end_offset - location.end_offset)
61
- return remove(location) unless before.strip.empty? && after.strip.empty?
62
-
63
- ending = @source_file.line_location_with_newline(last_offset).end_offset
64
- remove(first.start_offset...ending)
65
- end
66
-
67
- def insert_line_before(node, text)
68
- line_start = @source_file.line_start(node.location.start_offset)
69
- content = format_body_source(text, indentation_of(node))
70
- insert_before(line_start...line_start, "#{content}#{@source_file.newline}")
71
- end
72
-
73
- def insert_line_after(node, text)
74
- line = @source_file.line_location_with_newline(node.location.end_offset - 1)
75
- content = format_body_source(text, indentation_of(node))
76
- prefix = line_terminated?(line) ? '' : @source_file.newline
77
- insert_after(line, "#{prefix}#{content}#{@source_file.newline}")
78
- end
79
-
80
- def comment_out(node)
81
- first = @source_file.line_start(node.location.start_offset)
82
- last = @source_file.line_end([node.location.end_offset - 1, node.location.start_offset].max)
83
- text = @source_file.source.byteslice(first, last - first)
84
- commented = text.lines(chomp: false).map do |line|
85
- indentation = line[/\A[ \t]*/]
86
- "#{indentation}# #{line.delete_prefix(indentation)}"
87
- end.join
88
- replace(first...last, commented)
89
- end
90
-
91
- private
92
-
93
- def body_statements(node)
94
- body = node.respond_to?(:body) ? node.body : nil
95
- body if body.is_a?(Prism::StatementsNode)
96
- end
97
-
98
- def body_closing_location(node)
99
- return node.end_keyword_loc if node.respond_to?(:end_keyword_loc) && node.end_keyword_loc
100
- return node.closing_loc if node.respond_to?(:closing_loc) && node.closing_loc
101
-
102
- raise ArgumentError, 'node does not have a delimited body'
103
- end
104
-
105
- def body_insertion_target(node, statements, position)
106
- body = statements&.body || []
107
- case position
108
- when :beginning then return [body.first, false, false]
109
- when :end then return [nil, false, false]
110
- when :before_private
111
- marker = body.find { |child| visibility_marker?(child) }
112
- return [marker, false, !marker.nil?]
113
- when Hash
114
- return [position.fetch(:after), true, false] if position.key?(:after)
115
- return [position.fetch(:before), false, false] if position.key?(:before)
116
- end
117
- raise ArgumentError, 'unsupported body insertion position'
118
- end
119
-
120
- def visibility_marker?(node)
121
- node.is_a?(Prism::CallNode) && node.receiver.nil? && node.arguments.nil? &&
122
- %i[private protected].include?(node.name)
123
- end
124
-
125
- def format_body_source(text, indentation)
126
- normalized = dedent(text.to_s).gsub(/\r?\n/, @source_file.newline).delete_suffix(@source_file.newline)
127
- normalized.lines(chomp: false).map { |line| line.strip.empty? ? line : "#{indentation}#{line}" }.join
128
- end
129
-
130
- def needs_leading_newline?(offset)
131
- offset.positive? && ![10, 13].include?(@source_file.source.getbyte(offset - 1))
132
- end
133
-
134
- def line_terminated?(location)
135
- location.end_offset.positive? && @source_file.source.getbyte(location.end_offset - 1) == 10
136
- end
137
- end
138
- end