irb 1.16.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/Gemfile +8 -2
- data/lib/irb/color.rb +251 -163
- data/lib/irb/command/base.rb +35 -0
- data/lib/irb/command/copy.rb +11 -1
- data/lib/irb/command/internal_helpers.rb +6 -3
- data/lib/irb/command/ls.rb +6 -4
- data/lib/irb/completion.rb +38 -16
- data/lib/irb/context.rb +1 -1
- data/lib/irb/debug.rb +2 -2
- data/lib/irb/init.rb +3 -0
- data/lib/irb/input-method.rb +141 -111
- data/lib/irb/nesting_parser.rb +362 -213
- data/lib/irb/pager.rb +8 -0
- data/lib/irb/ruby-lex.rb +204 -303
- data/lib/irb/ruby_logo.aa +4 -0
- data/lib/irb/source_finder.rb +5 -14
- data/lib/irb/startup_message.rb +83 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +39 -17
- metadata +16 -1
data/lib/irb/nesting_parser.rb
CHANGED
|
@@ -1,238 +1,387 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'prism'
|
|
4
|
+
|
|
2
5
|
module IRB
|
|
3
6
|
module NestingParser
|
|
4
|
-
|
|
7
|
+
NestingElem = Struct.new(:pos, :event, :tok)
|
|
5
8
|
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
when :in_alias_undef
|
|
17
|
-
skip = t.event == :on_kw
|
|
18
|
-
when :in_unquoted_symbol
|
|
19
|
-
unless IGNORE_TOKENS.include?(t.event)
|
|
20
|
-
opens.pop
|
|
21
|
-
skip = true
|
|
22
|
-
end
|
|
23
|
-
when :in_lambda_head
|
|
24
|
-
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
|
25
|
-
when :in_method_head
|
|
26
|
-
unless IGNORE_TOKENS.include?(t.event)
|
|
27
|
-
next_args = []
|
|
28
|
-
body = nil
|
|
29
|
-
if args.include?(:receiver)
|
|
30
|
-
case t.event
|
|
31
|
-
when :on_lparen, :on_ivar, :on_gvar, :on_cvar
|
|
32
|
-
# def (receiver). | def @ivar. | def $gvar. | def @@cvar.
|
|
33
|
-
next_args << :dot
|
|
34
|
-
when :on_kw
|
|
35
|
-
case t.tok
|
|
36
|
-
when 'self', 'true', 'false', 'nil'
|
|
37
|
-
# def self(arg) | def self.
|
|
38
|
-
next_args.push(:arg, :dot)
|
|
39
|
-
else
|
|
40
|
-
# def if(arg)
|
|
41
|
-
skip = true
|
|
42
|
-
next_args << :arg
|
|
43
|
-
end
|
|
44
|
-
when :on_op, :on_backtick
|
|
45
|
-
# def +(arg)
|
|
46
|
-
skip = true
|
|
47
|
-
next_args << :arg
|
|
48
|
-
when :on_ident, :on_const
|
|
49
|
-
# def a(arg) | def a.
|
|
50
|
-
next_args.push(:arg, :dot)
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
if args.include?(:dot)
|
|
54
|
-
# def receiver.name
|
|
55
|
-
next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
|
|
56
|
-
end
|
|
57
|
-
if args.include?(:name)
|
|
58
|
-
if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
|
|
59
|
-
# def name(arg) | def receiver.name(arg)
|
|
60
|
-
next_args << :arg
|
|
61
|
-
skip = true
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
if args.include?(:arg)
|
|
65
|
-
case t.event
|
|
66
|
-
when :on_nl, :on_semicolon
|
|
67
|
-
# def receiver.f;
|
|
68
|
-
body = :normal
|
|
69
|
-
when :on_lparen
|
|
70
|
-
# def receiver.f()
|
|
71
|
-
next_args << :eq
|
|
72
|
-
else
|
|
73
|
-
if t.event == :on_op && t.tok == '='
|
|
74
|
-
# def receiver.f =
|
|
75
|
-
body = :oneliner
|
|
76
|
-
else
|
|
77
|
-
# def receiver.f arg
|
|
78
|
-
next_args << :arg_without_paren
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
if args.include?(:eq)
|
|
83
|
-
if t.event == :on_op && t.tok == '='
|
|
84
|
-
body = :oneliner
|
|
85
|
-
else
|
|
86
|
-
body = :normal
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
if args.include?(:arg_without_paren)
|
|
90
|
-
if %i[on_semicolon on_nl].include?(t.event)
|
|
91
|
-
# def f a;
|
|
92
|
-
body = :normal
|
|
93
|
-
else
|
|
94
|
-
# def f a, b
|
|
95
|
-
next_args << :arg_without_paren
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
if body == :oneliner
|
|
99
|
-
opens.pop
|
|
100
|
-
elsif body
|
|
101
|
-
opens[-1] = [last_tok, nil]
|
|
102
|
-
else
|
|
103
|
-
opens[-1] = [last_tok, :in_method_head, next_args]
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
when :in_for_while_until_condition
|
|
107
|
-
if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
|
|
108
|
-
skip = true if t.event == :on_kw && t.tok == 'do'
|
|
109
|
-
opens[-1] = [last_tok, nil]
|
|
110
|
-
end
|
|
111
|
-
end
|
|
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 = []
|
|
112
19
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
end
|
|
127
|
-
when 'while', 'until'
|
|
128
|
-
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
|
129
|
-
opens << [t, :in_for_while_until_condition]
|
|
130
|
-
end
|
|
131
|
-
when 'ensure', 'rescue'
|
|
132
|
-
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
|
133
|
-
opens.pop
|
|
134
|
-
opens << [t, nil]
|
|
135
|
-
end
|
|
136
|
-
when 'alias'
|
|
137
|
-
opens << [t, :in_alias_undef, 2]
|
|
138
|
-
when 'undef'
|
|
139
|
-
opens << [t, :in_alias_undef, 1]
|
|
140
|
-
when 'elsif', 'else', 'when'
|
|
141
|
-
opens.pop
|
|
142
|
-
opens << [t, nil]
|
|
143
|
-
when 'for'
|
|
144
|
-
opens << [t, :in_for_while_until_condition]
|
|
145
|
-
when 'in'
|
|
146
|
-
if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
|
|
147
|
-
opens.pop
|
|
148
|
-
opens << [t, nil]
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
when :on_tlambda
|
|
152
|
-
opens << [t, :in_lambda_head]
|
|
153
|
-
when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
|
|
154
|
-
opens << [t, nil]
|
|
155
|
-
when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
|
|
156
|
-
opens.pop
|
|
157
|
-
when :on_heredoc_beg
|
|
158
|
-
pending_heredocs << t
|
|
159
|
-
when :on_heredoc_end
|
|
160
|
-
opens.pop
|
|
161
|
-
when :on_backtick
|
|
162
|
-
opens << [t, nil] unless t.state == Ripper::EXPR_ARG
|
|
163
|
-
when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
|
|
164
|
-
opens << [t, nil]
|
|
165
|
-
when :on_tstring_end, :on_regexp_end, :on_label_end
|
|
166
|
-
opens.pop
|
|
167
|
-
when :on_symbeg
|
|
168
|
-
if t.tok == ':'
|
|
169
|
-
opens << [t, :in_unquoted_symbol]
|
|
170
|
-
else
|
|
171
|
-
opens << [t, nil]
|
|
172
|
-
end
|
|
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
|
|
31
|
+
else
|
|
32
|
+
nesting.pop
|
|
173
33
|
end
|
|
174
34
|
end
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
35
|
+
@heredocs[line_index]&.sort_by { |elem| elem.pos[1] }&.reverse_each do |elem|
|
|
36
|
+
nesting << elem
|
|
37
|
+
end
|
|
38
|
+
nesting.dup
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
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
|
|
179
205
|
end
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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"
|
|
183
230
|
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
187
283
|
end
|
|
188
|
-
|
|
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)
|
|
296
|
+
end
|
|
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 == ')'
|
|
310
|
+
end
|
|
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 == ')'
|
|
189
321
|
end
|
|
190
|
-
|
|
322
|
+
if node.equal
|
|
323
|
+
close_location(node.equal_loc)
|
|
324
|
+
else
|
|
325
|
+
close_end_keyword_loc(node)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
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)
|
|
191
340
|
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
class << self
|
|
192
344
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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]
|
|
196
348
|
end
|
|
197
349
|
|
|
198
|
-
# Calculates
|
|
350
|
+
# Calculates nesting information [prev_opens, next_opens, min_depth] for each line.
|
|
199
351
|
# Example code
|
|
200
352
|
# ["hello
|
|
201
353
|
# world"+(
|
|
202
354
|
# First line
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# min_depth: 0 (minimum at beginning of line)
|
|
355
|
+
# prev_opens: []
|
|
356
|
+
# next_opens: [lbracket, tstring_beg]
|
|
357
|
+
# min_depth: 0 (minimum at beginning of line)
|
|
207
358
|
# Second line
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
def parse_by_line(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
line_tokens << [t, line]
|
|
223
|
-
next if line[-1] != "\n"
|
|
224
|
-
next_opens = opens.map(&:first)
|
|
225
|
-
output << [line_tokens, prev_opens, next_opens, min_depth]
|
|
226
|
-
prev_opens = next_opens
|
|
227
|
-
min_depth = prev_opens.size
|
|
228
|
-
line_tokens = []
|
|
229
|
-
end
|
|
230
|
-
else
|
|
231
|
-
line_tokens << [t, t.tok]
|
|
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)
|
|
232
373
|
end
|
|
233
374
|
end
|
|
234
|
-
|
|
235
|
-
|
|
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]
|
|
384
|
+
end
|
|
236
385
|
end
|
|
237
386
|
end
|
|
238
387
|
end
|
data/lib/irb/pager.rb
CHANGED
|
@@ -42,6 +42,14 @@ module IRB
|
|
|
42
42
|
# SIGTERM not supported (windows)
|
|
43
43
|
Process.kill("KILL", pid)
|
|
44
44
|
end
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
# Wait for the pager process to terminate.
|
|
48
|
+
# Reading next input from Reline before the pager process is fully terminated
|
|
49
|
+
# may cause issues like raw/cooked mode not being controlled properly.
|
|
50
|
+
Process.waitpid(pid) if pid
|
|
51
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
52
|
+
end
|
|
45
53
|
rescue Errno::ESRCH
|
|
46
54
|
# Pager process already terminated
|
|
47
55
|
end
|