irb 1.14.0 → 1.18.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.
- checksums.yaml +4 -4
- data/.rdoc_options +5 -0
- data/CONTRIBUTING.md +52 -0
- data/EXTEND_IRB.md +3 -0
- data/Gemfile +11 -1
- data/README.md +13 -293
- data/doc/COMMAND_LINE_OPTIONS.md +69 -0
- data/doc/COMPARED_WITH_PRY.md +22 -0
- data/doc/Configurations.md +274 -0
- data/doc/EXTEND_IRB.md +122 -0
- data/doc/Index.md +705 -0
- data/doc/irb/irb.rd.ja +1 -1
- data/lib/irb/color.rb +251 -162
- data/lib/irb/color_printer.rb +10 -9
- data/lib/irb/command/base.rb +46 -13
- data/lib/irb/command/copy.rb +83 -0
- data/lib/irb/command/debug.rb +8 -6
- data/lib/irb/command/help.rb +1 -1
- data/lib/irb/command/history.rb +1 -1
- data/lib/irb/command/internal_helpers.rb +7 -4
- data/lib/irb/command/ls.rb +29 -15
- data/lib/irb/completion.rb +81 -57
- data/lib/irb/context.rb +132 -53
- data/lib/irb/debug/ui.rb +2 -4
- data/lib/irb/debug.rb +9 -12
- data/lib/irb/default_commands.rb +19 -8
- data/lib/irb/easter-egg.rb +13 -7
- data/lib/irb/ext/multi-irb.rb +2 -0
- data/lib/irb/history.rb +84 -55
- data/lib/irb/init.rb +7 -2
- data/lib/irb/input-method.rb +167 -130
- data/lib/irb/inspector.rb +42 -36
- data/lib/irb/lc/help-message +2 -2
- data/lib/irb/lc/ja/help-message +4 -4
- data/lib/irb/nesting_parser.rb +364 -213
- data/lib/irb/pager.rb +137 -7
- data/lib/irb/ruby-lex.rb +230 -302
- data/lib/irb/ruby_logo.aa +79 -37
- data/lib/irb/source_finder.rb +15 -22
- data/lib/irb/startup_message.rb +83 -0
- data/lib/irb/statement.rb +21 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +22 -31
- data/lib/irb.rb +148 -986
- data/man/irb.1 +37 -2
- metadata +42 -12
- data/.document +0 -4
- data/Rakefile +0 -54
- data/bin/console +0 -6
- data/bin/setup +0 -6
- data/irb.gemspec +0 -46
data/lib/irb/nesting_parser.rb
CHANGED
|
@@ -1,237 +1,388 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'prism'
|
|
4
|
+
|
|
2
5
|
module IRB
|
|
3
6
|
module NestingParser
|
|
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
|
-
if args.include?(:receiver)
|
|
29
|
-
case t.event
|
|
30
|
-
when :on_lparen, :on_ivar, :on_gvar, :on_cvar
|
|
31
|
-
# def (receiver). | def @ivar. | def $gvar. | def @@cvar.
|
|
32
|
-
next_args << :dot
|
|
33
|
-
when :on_kw
|
|
34
|
-
case t.tok
|
|
35
|
-
when 'self', 'true', 'false', 'nil'
|
|
36
|
-
# def self(arg) | def self.
|
|
37
|
-
next_args.push(:arg, :dot)
|
|
38
|
-
else
|
|
39
|
-
# def if(arg)
|
|
40
|
-
skip = true
|
|
41
|
-
next_args << :arg
|
|
42
|
-
end
|
|
43
|
-
when :on_op, :on_backtick
|
|
44
|
-
# def +(arg)
|
|
45
|
-
skip = true
|
|
46
|
-
next_args << :arg
|
|
47
|
-
when :on_ident, :on_const
|
|
48
|
-
# def a(arg) | def a.
|
|
49
|
-
next_args.push(:arg, :dot)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
if args.include?(:dot)
|
|
53
|
-
# def receiver.name
|
|
54
|
-
next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
|
|
55
|
-
end
|
|
56
|
-
if args.include?(:name)
|
|
57
|
-
if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
|
|
58
|
-
# def name(arg) | def receiver.name(arg)
|
|
59
|
-
next_args << :arg
|
|
60
|
-
skip = true
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
if args.include?(:arg)
|
|
64
|
-
case t.event
|
|
65
|
-
when :on_nl, :on_semicolon
|
|
66
|
-
# def receiver.f;
|
|
67
|
-
body = :normal
|
|
68
|
-
when :on_lparen
|
|
69
|
-
# def receiver.f()
|
|
70
|
-
next_args << :eq
|
|
71
|
-
else
|
|
72
|
-
if t.event == :on_op && t.tok == '='
|
|
73
|
-
# def receiver.f =
|
|
74
|
-
body = :oneliner
|
|
75
|
-
else
|
|
76
|
-
# def receiver.f arg
|
|
77
|
-
next_args << :arg_without_paren
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
if args.include?(:eq)
|
|
82
|
-
if t.event == :on_op && t.tok == '='
|
|
83
|
-
body = :oneliner
|
|
84
|
-
else
|
|
85
|
-
body = :normal
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
if args.include?(:arg_without_paren)
|
|
89
|
-
if %i[on_semicolon on_nl].include?(t.event)
|
|
90
|
-
# def f a;
|
|
91
|
-
body = :normal
|
|
92
|
-
else
|
|
93
|
-
# def f a, b
|
|
94
|
-
next_args << :arg_without_paren
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
if body == :oneliner
|
|
98
|
-
opens.pop
|
|
99
|
-
elsif body
|
|
100
|
-
opens[-1] = [last_tok, nil]
|
|
7
|
+
NestingElem = Struct.new(:pos, :event, :tok)
|
|
8
|
+
|
|
9
|
+
class NestingVisitor < Prism::Visitor
|
|
10
|
+
def initialize
|
|
11
|
+
# Array of [column, priority(+1/-1), NestingElem(open) or nil(close)] per line.
|
|
12
|
+
# priority is +1 for open, -1 for close so that close comes before open when sorted.
|
|
13
|
+
# Example:
|
|
14
|
+
# if cond
|
|
15
|
+
# else
|
|
16
|
+
# end
|
|
17
|
+
# `else` closes `if` at column 0 first and then opens `else` at column 0 next.
|
|
18
|
+
@lines = []
|
|
19
|
+
|
|
20
|
+
# Array of open heredoc NestingElem per line
|
|
21
|
+
@heredocs = []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def nestings
|
|
25
|
+
size = [@lines.size, @heredocs.size].max
|
|
26
|
+
nesting = []
|
|
27
|
+
size.times.map do |line_index|
|
|
28
|
+
@lines[line_index]&.sort_by { |col, pri| [col, pri] }&.each do |col, pri, elem|
|
|
29
|
+
if elem
|
|
30
|
+
nesting << elem
|
|
101
31
|
else
|
|
102
|
-
|
|
32
|
+
nesting.pop
|
|
103
33
|
end
|
|
104
34
|
end
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
skip = true if t.event == :on_kw && t.tok == 'do'
|
|
108
|
-
opens[-1] = [last_tok, nil]
|
|
35
|
+
@heredocs[line_index]&.sort_by { |elem| elem.pos[1] }&.reverse_each do |elem|
|
|
36
|
+
nesting << elem
|
|
109
37
|
end
|
|
38
|
+
nesting.dup
|
|
110
39
|
end
|
|
40
|
+
end
|
|
111
41
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
42
|
+
def heredoc_open(node)
|
|
43
|
+
elem = NestingElem.new([node.location.start_line, node.location.start_column], :on_heredoc_beg, node.opening)
|
|
44
|
+
(@heredocs[node.location.start_line - 1] ||= []) << elem
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def open(line, column, elem)
|
|
48
|
+
(@lines[line - 1] ||= []) << [column, +1, elem]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def close(line, column)
|
|
52
|
+
(@lines[line - 1] ||= []) << [column, -1]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Checks if a node (if, while, etc) is a modifier form that does not need `end` closing.
|
|
56
|
+
# modifier node: `a if b`, non-modifier node: `if a; b; end`
|
|
57
|
+
def modifier_node?(node, keyword_loc)
|
|
58
|
+
!(keyword_loc && node.location.start_line == keyword_loc.start_line && node.location.start_column == keyword_loc.start_column)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def open_location(location, type, tok)
|
|
62
|
+
open(location.start_line, location.start_column, NestingElem.new([location.start_line, location.start_column], type, tok))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def close_location(location)
|
|
66
|
+
close(location.end_line, location.end_column)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def close_location_start(location)
|
|
70
|
+
close(location.start_line, location.start_column)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def close_end_keyword_loc(node)
|
|
74
|
+
close_location(node.end_keyword_loc) if node.end_keyword == 'end'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def close_closing_loc(node)
|
|
78
|
+
close_location(node.closing_loc) if node.closing_loc && !node.closing.empty?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def visit_for_node(node)
|
|
82
|
+
super
|
|
83
|
+
open_location(node.location, :on_kw, 'for')
|
|
84
|
+
close_end_keyword_loc(node)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def visit_while_node(node)
|
|
88
|
+
super
|
|
89
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
90
|
+
|
|
91
|
+
open_location(node.location, :on_kw, 'while')
|
|
92
|
+
close_closing_loc(node)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def visit_until_node(node)
|
|
96
|
+
super
|
|
97
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
98
|
+
|
|
99
|
+
open_location(node.location, :on_kw, 'until')
|
|
100
|
+
close_closing_loc(node)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def visit_if_node(node)
|
|
104
|
+
super
|
|
105
|
+
return if !node.if_keyword || modifier_node?(node, node.if_keyword_loc)
|
|
106
|
+
|
|
107
|
+
open_location(node.location, :on_kw, node.if_keyword)
|
|
108
|
+
if node.subsequent
|
|
109
|
+
close_location_start(node.subsequent.location)
|
|
110
|
+
else
|
|
111
|
+
close_end_keyword_loc(node)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def visit_unless_node(node)
|
|
116
|
+
super
|
|
117
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
118
|
+
|
|
119
|
+
open_location(node.location, :on_kw, 'unless')
|
|
120
|
+
if node.else_clause
|
|
121
|
+
close_location_start(node.else_clause.location)
|
|
122
|
+
else
|
|
123
|
+
close_end_keyword_loc(node)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def visit_case_node(node)
|
|
128
|
+
super
|
|
129
|
+
open_location(node.location, :on_kw, 'case')
|
|
130
|
+
if node.else_clause
|
|
131
|
+
close_location_start(node.else_clause.location)
|
|
132
|
+
else
|
|
133
|
+
close_end_keyword_loc(node)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
alias visit_case_match_node visit_case_node
|
|
137
|
+
|
|
138
|
+
def visit_when_node(node)
|
|
139
|
+
super
|
|
140
|
+
close_location_start(node.location)
|
|
141
|
+
open_location(node.location, :on_kw, 'when')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def visit_in_node(node)
|
|
145
|
+
super
|
|
146
|
+
close_location_start(node.location)
|
|
147
|
+
open_location(node.location, :on_kw, 'in')
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def visit_else_node(node)
|
|
151
|
+
super
|
|
152
|
+
if node.else_keyword == 'else'
|
|
153
|
+
open_location(node.location, :on_kw, 'else')
|
|
154
|
+
close_end_keyword_loc(node)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def visit_ensure_node(node)
|
|
159
|
+
super
|
|
160
|
+
return if modifier_node?(node, node.ensure_keyword_loc)
|
|
161
|
+
|
|
162
|
+
close_location_start(node.location)
|
|
163
|
+
open_location(node.location, :on_kw, 'ensure')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def visit_rescue_node(node)
|
|
167
|
+
super
|
|
168
|
+
return if modifier_node?(node, node.keyword_loc)
|
|
169
|
+
|
|
170
|
+
close_location_start(node.location)
|
|
171
|
+
open_location(node.location, :on_kw, 'rescue')
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def visit_begin_node(node)
|
|
175
|
+
super
|
|
176
|
+
if node.begin_keyword
|
|
177
|
+
open_location(node.location, :on_kw, 'begin')
|
|
178
|
+
close_end_keyword_loc(node)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def visit_block_node(node)
|
|
183
|
+
super
|
|
184
|
+
open_location(node.location, node.opening == '{' ? :on_lbrace : :on_kw, node.opening)
|
|
185
|
+
close_closing_loc(node)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def visit_array_node(node)
|
|
189
|
+
super
|
|
190
|
+
type =
|
|
191
|
+
case node.opening
|
|
192
|
+
when nil
|
|
193
|
+
# `x = 1, 2` doesn't have opening
|
|
194
|
+
nil
|
|
195
|
+
when '['
|
|
196
|
+
:bracket
|
|
197
|
+
when /\A%W/
|
|
198
|
+
:on_words_beg
|
|
199
|
+
when /\A%w/
|
|
200
|
+
:on_qwords_beg
|
|
201
|
+
when /\A%I/
|
|
202
|
+
:on_symbols_beg
|
|
203
|
+
when /\A%i/
|
|
204
|
+
:on_qsymbols_beg
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
if type
|
|
208
|
+
open_location(node.location, type, node.opening)
|
|
209
|
+
close_closing_loc(node)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def visit_hash_node(node)
|
|
214
|
+
super
|
|
215
|
+
open_location(node.location, :on_lbrace, '{')
|
|
216
|
+
close_closing_loc(node)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def heredoc_string_like(node, type)
|
|
220
|
+
if node.opening&.start_with?('<<')
|
|
221
|
+
heredoc_open(node)
|
|
222
|
+
# Heredoc closing contains trailing newline. We need to exclude it
|
|
223
|
+
close_location_start(node.closing_loc) if node.closing_loc && !node.closing.empty?
|
|
224
|
+
elsif node.opening
|
|
225
|
+
return if node.opening == '?' && node.closing.nil? # Character literal has no closing
|
|
226
|
+
open_location(node.location, type, node.opening)
|
|
227
|
+
if node.closing && node.closing != ''
|
|
228
|
+
# Closing of `"#{\n` is "\n". We need to treat it as not-closed.
|
|
229
|
+
close_location_start(node.closing_loc) if node.opening.match?(/\n\z/) || node.closing != "\n"
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def visit_embedded_statements_node(node)
|
|
235
|
+
super
|
|
236
|
+
open_location(node.location, :on_embexpr_beg, '#{')
|
|
237
|
+
close_closing_loc(node)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def visit_interpolated_string_node(node)
|
|
241
|
+
super
|
|
242
|
+
heredoc_string_like(node, :on_tstring_beg)
|
|
243
|
+
end
|
|
244
|
+
alias visit_string_node visit_interpolated_string_node
|
|
245
|
+
|
|
246
|
+
def visit_interpolated_x_string_node(node)
|
|
247
|
+
super
|
|
248
|
+
heredoc_string_like(node, :on_backtick)
|
|
249
|
+
end
|
|
250
|
+
alias visit_x_string_node visit_interpolated_x_string_node
|
|
251
|
+
|
|
252
|
+
def visit_symbol_node(node)
|
|
253
|
+
super
|
|
254
|
+
unless node.opening.nil? || node.opening.empty? || node.opening == ':'
|
|
255
|
+
# :"sym" or %s[sym]
|
|
256
|
+
open_location(node.location, :on_symbeg, node.opening)
|
|
257
|
+
close_closing_loc(node)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
alias visit_interpolated_symbol_node visit_symbol_node
|
|
261
|
+
|
|
262
|
+
def visit_regular_expression_node(node)
|
|
263
|
+
super
|
|
264
|
+
open_location(node.location, :on_regexp_beg, node.opening)
|
|
265
|
+
close_closing_loc(node)
|
|
266
|
+
end
|
|
267
|
+
alias visit_interpolated_regular_expression_node visit_regular_expression_node
|
|
268
|
+
|
|
269
|
+
def visit_parentheses_node(node)
|
|
270
|
+
super
|
|
271
|
+
open_location(node.location, :on_lparen, '(')
|
|
272
|
+
close_closing_loc(node)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def visit_call_node(node)
|
|
276
|
+
super
|
|
277
|
+
type =
|
|
278
|
+
case node.opening
|
|
279
|
+
when '('
|
|
280
|
+
:on_lparen
|
|
281
|
+
when '['
|
|
282
|
+
:on_lbracket
|
|
172
283
|
end
|
|
284
|
+
|
|
285
|
+
if type
|
|
286
|
+
open_location(node.opening_loc, type, node.opening)
|
|
287
|
+
close_closing_loc(node)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def visit_block_parameters_node(node)
|
|
292
|
+
super
|
|
293
|
+
if node.opening == '('
|
|
294
|
+
open_location(node.location, :on_lparen, '(')
|
|
295
|
+
close_closing_loc(node)
|
|
173
296
|
end
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def visit_lambda_node(node)
|
|
300
|
+
super
|
|
301
|
+
open_location(node.opening_loc, :on_tlambeg, node.opening)
|
|
302
|
+
close_closing_loc(node)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def visit_super_node(node)
|
|
306
|
+
super
|
|
307
|
+
if node.lparen
|
|
308
|
+
open_location(node.lparen_loc, :on_lparen, '(')
|
|
309
|
+
close_location(node.rparen_loc) if node.rparen == ')'
|
|
178
310
|
end
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
311
|
+
end
|
|
312
|
+
alias visit_yield_node visit_super_node
|
|
313
|
+
alias visit_defined_node visit_super_node
|
|
314
|
+
|
|
315
|
+
def visit_def_node(node)
|
|
316
|
+
super
|
|
317
|
+
open_location(node.location, :on_kw, 'def')
|
|
318
|
+
if node.lparen == '('
|
|
319
|
+
open_location(node.lparen_loc, :on_lparen, '(')
|
|
320
|
+
close_location(node.rparen_loc) if node.rparen == ')'
|
|
182
321
|
end
|
|
183
|
-
if
|
|
184
|
-
|
|
185
|
-
|
|
322
|
+
if node.equal
|
|
323
|
+
close_location(node.equal_loc)
|
|
324
|
+
else
|
|
325
|
+
close_end_keyword_loc(node)
|
|
186
326
|
end
|
|
187
|
-
yield t, opens if block_given?
|
|
188
327
|
end
|
|
189
|
-
opens.map(&:first) + pending_heredocs.reverse
|
|
190
|
-
end
|
|
191
328
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
329
|
+
def visit_class_node(node)
|
|
330
|
+
super
|
|
331
|
+
open_location(node.location, :on_kw, 'class')
|
|
332
|
+
close_end_keyword_loc(node)
|
|
333
|
+
end
|
|
334
|
+
alias visit_singleton_class_node visit_class_node
|
|
335
|
+
|
|
336
|
+
def visit_module_node(node)
|
|
337
|
+
super
|
|
338
|
+
open_location(node.location, :on_kw, 'module')
|
|
339
|
+
close_end_keyword_loc(node)
|
|
340
|
+
end
|
|
195
341
|
end
|
|
196
342
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
prev_opens
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
line_tokens = []
|
|
343
|
+
class << self
|
|
344
|
+
|
|
345
|
+
# Return a list of open nesting elements at EOF position
|
|
346
|
+
def open_nestings(parse_lex_result)
|
|
347
|
+
parse_by_line(parse_lex_result).last[1]
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Calculates nesting information [prev_opens, next_opens, min_depth] for each line.
|
|
351
|
+
# Example code
|
|
352
|
+
# ["hello
|
|
353
|
+
# world"+(
|
|
354
|
+
# First line
|
|
355
|
+
# prev_opens: []
|
|
356
|
+
# next_opens: [lbracket, tstring_beg]
|
|
357
|
+
# min_depth: 0 (minimum at beginning of line)
|
|
358
|
+
# Second line
|
|
359
|
+
# prev_opens: [lbracket, tstring_beg]
|
|
360
|
+
# next_opens: [lbracket, lparen]
|
|
361
|
+
# min_depth: 1 (minimum just after tstring_end)
|
|
362
|
+
|
|
363
|
+
def parse_by_line(parse_lex_result)
|
|
364
|
+
visitor = NestingVisitor.new
|
|
365
|
+
node, tokens = parse_lex_result.value
|
|
366
|
+
node.accept(visitor)
|
|
367
|
+
tokens.each do |token,|
|
|
368
|
+
case token.type
|
|
369
|
+
when :EMBDOC_BEGIN
|
|
370
|
+
visitor.open_location(token.location, :on_embdoc_beg, '=begin')
|
|
371
|
+
when :EMBDOC_END
|
|
372
|
+
visitor.close_location_start(token.location)
|
|
228
373
|
end
|
|
229
|
-
|
|
230
|
-
|
|
374
|
+
end
|
|
375
|
+
nestings = visitor.nestings
|
|
376
|
+
last_nesting = nestings.last || []
|
|
377
|
+
|
|
378
|
+
num_lines = parse_lex_result.source.source.lines.size
|
|
379
|
+
num_lines.times.map do |i|
|
|
380
|
+
prev_opens = i == 0 ? [] : nestings[i - 1] || last_nesting
|
|
381
|
+
opens = nestings[i] || last_nesting
|
|
382
|
+
min_depth = prev_opens.zip(opens).take_while { |s, e| s == e }.size
|
|
383
|
+
[prev_opens, opens, min_depth]
|
|
231
384
|
end
|
|
232
385
|
end
|
|
233
|
-
output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
|
|
234
|
-
output
|
|
235
386
|
end
|
|
236
387
|
end
|
|
237
388
|
end
|