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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5228c22000696caa8318b56171e1238202f103e63c470db4d215ce2d0e46625
|
|
4
|
+
data.tar.gz: ac556bc9f4fe3abfef0310a1330d609d424c1196e4a75ba086bae789b0e973d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe37b6ecb5348d4cee255f42fd0be29d75b609b7edf45e0e8b6699dcb2865c4d888fbb282087acbfe2eaa9235890e60f7be80545ddb292b9adb4d4c29fd8b111
|
|
7
|
+
data.tar.gz: 063aab58e48c5ff5e6bb5ca306ad820469c96a691d1f90d227a045c6fb2d9a5b423c76e4099a213fee21f079c52839556e13b8a8529779df982f418756eb89ff
|
data/Gemfile
CHANGED
|
@@ -24,8 +24,14 @@ gem "debug", github: "ruby/debug"
|
|
|
24
24
|
|
|
25
25
|
gem "rdoc", ">= 6.11.0"
|
|
26
26
|
|
|
27
|
+
if ENV['PRISM_VERSION'] == 'latest'
|
|
28
|
+
gem "prism", github: "ruby/prism"
|
|
29
|
+
elsif ENV['PRISM_VERSION']
|
|
30
|
+
gem "prism", ENV['PRISM_VERSION']
|
|
31
|
+
else
|
|
32
|
+
gem "prism", "!= 1.8.0"
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
if RUBY_VERSION >= "3.0.0" && !is_truffleruby
|
|
28
|
-
# TODO: Remove this after rbs is released with tsort in its dependencies
|
|
29
|
-
gem "rbs", github: "ruby/rbs" if RUBY_VERSION >= "3.2"
|
|
30
36
|
gem "repl_type_completor"
|
|
31
37
|
end
|
data/lib/irb/color.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'reline'
|
|
3
|
-
require '
|
|
3
|
+
require 'prism'
|
|
4
4
|
require_relative 'ruby-lex'
|
|
5
5
|
|
|
6
6
|
module IRB # :nodoc:
|
|
@@ -18,65 +18,102 @@ module IRB # :nodoc:
|
|
|
18
18
|
CYAN = 36
|
|
19
19
|
WHITE = 37
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
21
|
+
# Following pry's colors where possible
|
|
22
|
+
TOKEN_SEQS = {
|
|
23
|
+
KEYWORD_NIL: [CYAN, BOLD],
|
|
24
|
+
KEYWORD_SELF: [CYAN, BOLD],
|
|
25
|
+
KEYWORD_TRUE: [CYAN, BOLD],
|
|
26
|
+
KEYWORD_FALSE: [CYAN, BOLD],
|
|
27
|
+
KEYWORD___FILE__: [CYAN, BOLD],
|
|
28
|
+
KEYWORD___LINE__: [CYAN, BOLD],
|
|
29
|
+
KEYWORD___ENCODING__: [CYAN, BOLD],
|
|
30
|
+
CHARACTER_LITERAL: [BLUE, BOLD],
|
|
31
|
+
BACK_REFERENCE: [GREEN, BOLD],
|
|
32
|
+
BACKTICK: [RED, BOLD],
|
|
33
|
+
COMMENT: [BLUE, BOLD],
|
|
34
|
+
EMBDOC_BEGIN: [BLUE, BOLD],
|
|
35
|
+
EMBDOC_LINE: [BLUE, BOLD],
|
|
36
|
+
EMBDOC_END: [BLUE, BOLD],
|
|
37
|
+
CONSTANT: [BLUE, BOLD, UNDERLINE],
|
|
38
|
+
EMBEXPR_BEGIN: [RED],
|
|
39
|
+
EMBEXPR_END: [RED],
|
|
40
|
+
EMBVAR: [RED],
|
|
41
|
+
FLOAT: [MAGENTA, BOLD],
|
|
42
|
+
GLOBAL_VARIABLE: [GREEN, BOLD],
|
|
43
|
+
HEREDOC_START: [RED],
|
|
44
|
+
HEREDOC_END: [RED],
|
|
45
|
+
FLOAT_IMAGINARY: [BLUE, BOLD],
|
|
46
|
+
INTEGER_IMAGINARY: [BLUE, BOLD],
|
|
47
|
+
FLOAT_RATIONAL_IMAGINARY: [BLUE, BOLD],
|
|
48
|
+
INTEGER_RATIONAL_IMAGINARY: [BLUE, BOLD],
|
|
49
|
+
INTEGER: [BLUE, BOLD],
|
|
50
|
+
INTEGER_RATIONAL: [BLUE, BOLD],
|
|
51
|
+
FLOAT_RATIONAL: [BLUE, BOLD],
|
|
52
|
+
KEYWORD_END: [GREEN],
|
|
53
|
+
KEYWORD_CLASS: [GREEN],
|
|
54
|
+
KEYWORD_MODULE: [GREEN],
|
|
55
|
+
KEYWORD_IF: [GREEN],
|
|
56
|
+
KEYWORD_IF_MODIFIER: [GREEN],
|
|
57
|
+
KEYWORD_UNLESS_MODIFIER: [GREEN],
|
|
58
|
+
KEYWORD_WHILE_MODIFIER: [GREEN],
|
|
59
|
+
KEYWORD_UNTIL_MODIFIER: [GREEN],
|
|
60
|
+
KEYWORD_RESCUE_MODIFIER: [GREEN],
|
|
61
|
+
KEYWORD_THEN: [GREEN],
|
|
62
|
+
KEYWORD_UNLESS: [GREEN],
|
|
63
|
+
KEYWORD_ELSE: [GREEN],
|
|
64
|
+
KEYWORD_ELSIF: [GREEN],
|
|
65
|
+
KEYWORD_WHILE: [GREEN],
|
|
66
|
+
KEYWORD_UNTIL: [GREEN],
|
|
67
|
+
KEYWORD_CASE: [GREEN],
|
|
68
|
+
KEYWORD_WHEN: [GREEN],
|
|
69
|
+
KEYWORD_IN: [GREEN],
|
|
70
|
+
KEYWORD_DEF: [GREEN],
|
|
71
|
+
KEYWORD_DO: [GREEN],
|
|
72
|
+
KEYWORD_DO_BLOCK: [GREEN],
|
|
73
|
+
KEYWORD_DO_LOOP: [GREEN],
|
|
74
|
+
KEYWORD_FOR: [GREEN],
|
|
75
|
+
KEYWORD_BEGIN: [GREEN],
|
|
76
|
+
KEYWORD_RESCUE: [GREEN],
|
|
77
|
+
KEYWORD_ENSURE: [GREEN],
|
|
78
|
+
KEYWORD_ALIAS: [GREEN],
|
|
79
|
+
KEYWORD_UNDEF: [GREEN],
|
|
80
|
+
KEYWORD_BEGIN_UPCASE: [GREEN],
|
|
81
|
+
KEYWORD_END_UPCASE: [GREEN],
|
|
82
|
+
KEYWORD_YIELD: [GREEN],
|
|
83
|
+
KEYWORD_REDO: [GREEN],
|
|
84
|
+
KEYWORD_RETRY: [GREEN],
|
|
85
|
+
KEYWORD_NEXT: [GREEN],
|
|
86
|
+
KEYWORD_BREAK: [GREEN],
|
|
87
|
+
KEYWORD_SUPER: [GREEN],
|
|
88
|
+
KEYWORD_RETURN: [GREEN],
|
|
89
|
+
KEYWORD_DEFINED: [GREEN],
|
|
90
|
+
KEYWORD_NOT: [GREEN],
|
|
91
|
+
KEYWORD_AND: [GREEN],
|
|
92
|
+
KEYWORD_OR: [GREEN],
|
|
93
|
+
LABEL: [MAGENTA],
|
|
94
|
+
LABEL_END: [RED, BOLD],
|
|
95
|
+
NUMBERED_REFERENCE: [GREEN, BOLD],
|
|
96
|
+
PERCENT_UPPER_W: [RED, BOLD],
|
|
97
|
+
PERCENT_LOWER_W: [RED, BOLD],
|
|
98
|
+
PERCENT_LOWER_X: [RED, BOLD],
|
|
99
|
+
REGEXP_BEGIN: [RED, BOLD],
|
|
100
|
+
REGEXP_END: [RED, BOLD],
|
|
101
|
+
STRING_BEGIN: [RED, BOLD],
|
|
102
|
+
STRING_CONTENT: [RED],
|
|
103
|
+
STRING_END: [RED, BOLD],
|
|
104
|
+
__END__: [GREEN],
|
|
105
|
+
# tokens from syntax tree traversal
|
|
106
|
+
method_name: [CYAN, BOLD],
|
|
107
|
+
message_name: [CYAN],
|
|
108
|
+
symbol: [YELLOW],
|
|
109
|
+
# special colorization
|
|
110
|
+
error: [RED, REVERSE],
|
|
111
|
+
}.transform_values do |styles|
|
|
112
|
+
styles.map { |style| "\e[#{style}m" }.join
|
|
75
113
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
private_constant :ERROR_TOKENS
|
|
114
|
+
CLEAR_SEQ = "\e[#{CLEAR}m"
|
|
115
|
+
OPERATORS = %i(!= !~ =~ == === <=> > >= < <= & | ^ >> << - + % / * ** -@ +@ ~ ! [] []=)
|
|
116
|
+
private_constant :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
|
|
80
117
|
|
|
81
118
|
class << self
|
|
82
119
|
def colorable?
|
|
@@ -113,14 +150,13 @@ module IRB # :nodoc:
|
|
|
113
150
|
end
|
|
114
151
|
|
|
115
152
|
def clear(colorable: colorable?)
|
|
116
|
-
|
|
117
|
-
"\e[#{CLEAR}m"
|
|
153
|
+
colorable ? CLEAR_SEQ : ''
|
|
118
154
|
end
|
|
119
155
|
|
|
120
156
|
def colorize(text, seq, colorable: colorable?)
|
|
121
157
|
return text unless colorable
|
|
122
158
|
seq = seq.map { |s| "\e[#{const_get(s)}m" }.join('')
|
|
123
|
-
"#{seq}#{text}#{
|
|
159
|
+
"#{seq}#{text}#{CLEAR_SEQ}"
|
|
124
160
|
end
|
|
125
161
|
|
|
126
162
|
# If `complete` is false (code is incomplete), this does not warn compile_error.
|
|
@@ -129,135 +165,187 @@ module IRB # :nodoc:
|
|
|
129
165
|
def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
|
|
130
166
|
return code unless colorable
|
|
131
167
|
|
|
132
|
-
|
|
168
|
+
result = Prism.parse_lex(code, scopes: [local_variables])
|
|
169
|
+
|
|
170
|
+
# IRB::ColorPrinter skips colorizing syntax invalid fragments
|
|
171
|
+
return Reline::Unicode.escape_for_print(code) if ignore_error && !result.success?
|
|
172
|
+
|
|
173
|
+
prism_node, prism_tokens = result.value
|
|
174
|
+
errors = result.errors
|
|
175
|
+
|
|
176
|
+
unless complete
|
|
177
|
+
errors = filter_incomplete_code_errors(errors, prism_tokens)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
visitor = ColorizeVisitor.new
|
|
181
|
+
prism_node.accept(visitor)
|
|
182
|
+
|
|
183
|
+
error_tokens = errors.map { |e| [e.location.start_line, e.location.start_column, 0, e.location.end_line, e.location.end_column, :error, e.location.slice] }
|
|
184
|
+
error_tokens.reject! { |t| t.last.match?(/\A\s*\z/) }
|
|
185
|
+
tokens = prism_tokens.map { |t,| [t.location.start_line, t.location.start_column, 2, t.location.end_line, t.location.end_column, t.type, t.value] }
|
|
186
|
+
tokens.pop if tokens.last&.[](5) == :EOF
|
|
187
|
+
|
|
133
188
|
colored = +''
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
colored << Reline::Unicode.escape_for_print(
|
|
141
|
-
|
|
189
|
+
line_index = 0
|
|
190
|
+
col = 0
|
|
191
|
+
lines = code.lines
|
|
192
|
+
flush = -> next_line_index, next_col {
|
|
193
|
+
return if next_line_index == line_index && next_col == col
|
|
194
|
+
(line_index...[next_line_index, lines.size].min).each do |ln|
|
|
195
|
+
colored << Reline::Unicode.escape_for_print(lines[line_index].byteslice(col..))
|
|
196
|
+
line_index = ln + 1
|
|
197
|
+
col = 0
|
|
142
198
|
end
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if ignore_error && ERROR_TOKENS.include?(token)
|
|
146
|
-
return Reline::Unicode.escape_for_print(code)
|
|
199
|
+
unless col == next_col
|
|
200
|
+
colored << Reline::Unicode.escape_for_print(lines[next_line_index].byteslice(col..next_col - 1))
|
|
147
201
|
end
|
|
202
|
+
}
|
|
148
203
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
204
|
+
(visitor.tokens + tokens + error_tokens).sort.each do |start_line, start_column, _priority, end_line, end_column, type, value|
|
|
205
|
+
next if start_line - 1 < line_index || (start_line - 1 == line_index && start_column < col)
|
|
206
|
+
|
|
207
|
+
flush.call(start_line - 1, start_column)
|
|
208
|
+
if type == :__END__
|
|
209
|
+
color = TOKEN_SEQS[type]
|
|
210
|
+
end_line = start_line
|
|
211
|
+
value = '__END__'
|
|
212
|
+
end_column = start_column + 7
|
|
213
|
+
else
|
|
214
|
+
color = TOKEN_SEQS[type]
|
|
215
|
+
end
|
|
216
|
+
if color
|
|
217
|
+
value.split(/(\n)/).each do |s|
|
|
218
|
+
colored << (s == "\n" ? s : "#{color}#{Reline::Unicode.escape_for_print(s)}#{CLEAR_SEQ}")
|
|
157
219
|
end
|
|
220
|
+
else
|
|
221
|
+
colored << value
|
|
158
222
|
end
|
|
223
|
+
line_index = end_line - 1
|
|
224
|
+
col = end_column
|
|
159
225
|
end
|
|
160
|
-
|
|
161
|
-
if lvars_code
|
|
162
|
-
raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
|
|
163
|
-
colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
|
|
164
|
-
end
|
|
226
|
+
flush.call lines.size, 0
|
|
165
227
|
colored
|
|
166
228
|
end
|
|
167
229
|
|
|
168
|
-
|
|
230
|
+
class ColorizeVisitor < Prism::Visitor
|
|
231
|
+
attr_reader :tokens
|
|
232
|
+
def initialize
|
|
233
|
+
@tokens = []
|
|
234
|
+
end
|
|
169
235
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
seen.delete(obj)
|
|
176
|
-
end
|
|
236
|
+
def dispatch(location, type)
|
|
237
|
+
if location
|
|
238
|
+
@tokens << [location.start_line, location.start_column, 1, location.end_line, location.end_column, type, location.slice]
|
|
239
|
+
end
|
|
240
|
+
end
|
|
177
241
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
byte_pos = 0
|
|
183
|
-
line_positions = [0]
|
|
184
|
-
inner_code.lines.each do |line|
|
|
185
|
-
line_positions << line_positions.last + line.bytesize
|
|
242
|
+
def visit_array_node(node)
|
|
243
|
+
if node.opening&.match?(/\A%[iI]/)
|
|
244
|
+
dispatch node.opening_loc, :symbol
|
|
245
|
+
dispatch node.closing_loc, :symbol
|
|
186
246
|
end
|
|
247
|
+
super
|
|
248
|
+
end
|
|
187
249
|
|
|
188
|
-
|
|
189
|
-
|
|
250
|
+
def visit_def_node(node)
|
|
251
|
+
dispatch node.name_loc, :method_name
|
|
252
|
+
super
|
|
253
|
+
end
|
|
190
254
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
255
|
+
def visit_alias_method_node(node)
|
|
256
|
+
dispatch_alias_method_name node.new_name
|
|
257
|
+
dispatch_alias_method_name node.old_name
|
|
258
|
+
super
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def visit_call_node(node)
|
|
262
|
+
if node.call_operator_loc.nil? && OPERATORS.include?(node.name)
|
|
263
|
+
# Operators should not be colored as method call
|
|
264
|
+
elsif (node.call_operator_loc.nil? || node.call_operator_loc.slice == "::") &&
|
|
265
|
+
/\A\p{Upper}/.match?(node.name)
|
|
266
|
+
# Constant-like methods should not be colored as method call
|
|
267
|
+
else
|
|
268
|
+
dispatch node.message_loc, :message_name
|
|
269
|
+
end
|
|
270
|
+
super
|
|
271
|
+
end
|
|
195
272
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
273
|
+
def visit_call_operator_write_node(node)
|
|
274
|
+
dispatch node.message_loc, :message_name
|
|
275
|
+
super
|
|
276
|
+
end
|
|
277
|
+
alias visit_call_and_write_node visit_call_operator_write_node
|
|
278
|
+
alias visit_call_or_write_node visit_call_operator_write_node
|
|
279
|
+
|
|
280
|
+
def visit_interpolated_symbol_node(node)
|
|
281
|
+
dispatch node.opening_loc, :symbol
|
|
282
|
+
node.parts.each do |part|
|
|
283
|
+
case part
|
|
284
|
+
when Prism::StringNode
|
|
285
|
+
dispatch part.content_loc, :symbol
|
|
286
|
+
when Prism::EmbeddedStatementsNode
|
|
287
|
+
dispatch part.opening_loc, :symbol
|
|
288
|
+
dispatch part.closing_loc, :symbol
|
|
289
|
+
when Prism::EmbeddedVariableNode
|
|
290
|
+
dispatch part.operator_loc, :symbol
|
|
200
291
|
end
|
|
201
292
|
end
|
|
293
|
+
dispatch node.closing_loc, :symbol
|
|
294
|
+
super
|
|
295
|
+
end
|
|
202
296
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
297
|
+
def visit_symbol_node(node)
|
|
298
|
+
if (node.opening_loc.nil? && node.closing == ':') || node.closing&.match?(/\A['"]:\z/)
|
|
299
|
+
# Colorize { symbol: 1 } and { 'symbol': 1 } as label
|
|
300
|
+
dispatch node.location, :LABEL
|
|
301
|
+
else
|
|
302
|
+
dispatch node.opening_loc, :symbol
|
|
303
|
+
dispatch node.value_loc, :symbol
|
|
304
|
+
dispatch node.closing_loc, :symbol
|
|
206
305
|
end
|
|
207
|
-
# yield uncolorable DATA section
|
|
208
|
-
yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize
|
|
209
306
|
end
|
|
210
|
-
ensure
|
|
211
|
-
$VERBOSE = verbose
|
|
212
|
-
end
|
|
213
307
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
[CYAN, BOLD]
|
|
221
|
-
elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; (expr & (exprs || 0)) != 0)
|
|
222
|
-
seq
|
|
223
|
-
else
|
|
224
|
-
nil
|
|
308
|
+
private
|
|
309
|
+
|
|
310
|
+
def dispatch_alias_method_name(node)
|
|
311
|
+
if node.type == :symbol_node && node.opening_loc.nil?
|
|
312
|
+
dispatch node.value_loc, :method_name
|
|
313
|
+
end
|
|
225
314
|
end
|
|
226
315
|
end
|
|
227
|
-
end
|
|
228
316
|
|
|
229
|
-
|
|
230
|
-
class SymbolState
|
|
231
|
-
def initialize
|
|
232
|
-
# Push `true` to detect Symbol. `false` to increase the nest level for non-Symbol.
|
|
233
|
-
@stack = []
|
|
234
|
-
end
|
|
317
|
+
private
|
|
235
318
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return prev_state
|
|
252
|
-
when :on_tstring_end # :on_tstring_end may close Symbol
|
|
253
|
-
@stack.pop
|
|
254
|
-
return prev_state
|
|
255
|
-
when :on_embexpr_end
|
|
256
|
-
@stack.pop
|
|
319
|
+
FILTERED_ERROR_TYPES = [
|
|
320
|
+
:class_name, :module_name, # `class`, `class owner_module`
|
|
321
|
+
:write_target_unexpected, # `a, b`
|
|
322
|
+
:parameter_wild_loose_comma, # `def f(a,`
|
|
323
|
+
:argument_no_forwarding_star, # `[*`
|
|
324
|
+
:argument_no_forwarding_star_star, # `f(**`
|
|
325
|
+
:argument_no_forwarding_ampersand, # `f(&`
|
|
326
|
+
:def_endless, # `def f =`
|
|
327
|
+
:embdoc_term, # `=begin`
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
# Filter out syntax errors that are likely to be caused by incomplete code, to avoid showing misleading error highlights to users.
|
|
331
|
+
def filter_incomplete_code_errors(errors, tokens)
|
|
332
|
+
last_non_comment_space_token, = tokens.reverse_each.find do |t,|
|
|
333
|
+
t.type != :COMMENT && t.type != :EOF && t.type != :IGNORED_NEWLINE && t.type != :NEWLINE
|
|
257
334
|
end
|
|
258
|
-
|
|
335
|
+
last_offset = last_non_comment_space_token ? last_non_comment_space_token.location.end_offset : 0
|
|
336
|
+
errors.reject do |error|
|
|
337
|
+
error.message.match?(/\Aexpected a|unexpected end-of-input|unterminated/) ||
|
|
338
|
+
(error.location.end_offset == last_offset && FILTERED_ERROR_TYPES.include?(error.type))
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def without_circular_ref(obj, seen:, &block)
|
|
343
|
+
return false if seen.key?(obj)
|
|
344
|
+
seen[obj] = true
|
|
345
|
+
block.call
|
|
346
|
+
ensure
|
|
347
|
+
seen.delete(obj)
|
|
259
348
|
end
|
|
260
349
|
end
|
|
261
|
-
private_constant :SymbolState
|
|
262
350
|
end
|
|
263
351
|
end
|
data/lib/irb/command/base.rb
CHANGED
|
@@ -37,11 +37,46 @@ module IRB
|
|
|
37
37
|
puts e.message
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Returns formatted lines for display in the doc dialog popup.
|
|
41
|
+
def doc_dialog_content(name, width)
|
|
42
|
+
lines = []
|
|
43
|
+
lines << Color.colorize(name, [:BOLD, :BLUE]) + Color.colorize(" (command)", [:CYAN])
|
|
44
|
+
lines << ""
|
|
45
|
+
lines.concat(wrap_lines(description, width))
|
|
46
|
+
if help_message
|
|
47
|
+
lines << ""
|
|
48
|
+
lines.concat(wrap_lines(help_message, width))
|
|
49
|
+
end
|
|
50
|
+
lines
|
|
51
|
+
end
|
|
52
|
+
|
|
40
53
|
private
|
|
41
54
|
|
|
42
55
|
def highlight(text)
|
|
43
56
|
Color.colorize(text, [:BOLD, :BLUE])
|
|
44
57
|
end
|
|
58
|
+
|
|
59
|
+
def wrap_lines(text, width)
|
|
60
|
+
text.lines.flat_map do |line|
|
|
61
|
+
line = line.chomp
|
|
62
|
+
next [''] if line.empty?
|
|
63
|
+
next [line] if line.length <= width
|
|
64
|
+
|
|
65
|
+
indent = line[/\A\s*/]
|
|
66
|
+
parts = line.strip.split(/(\s+)/)
|
|
67
|
+
result = []
|
|
68
|
+
current = indent.dup
|
|
69
|
+
parts.each do |part|
|
|
70
|
+
if current != indent && current.length + part.length > width
|
|
71
|
+
result << current.rstrip
|
|
72
|
+
current = indent.dup
|
|
73
|
+
end
|
|
74
|
+
current << part unless current == indent && part.match?(/\A\s+\z/)
|
|
75
|
+
end
|
|
76
|
+
result << current.rstrip unless current == indent
|
|
77
|
+
result
|
|
78
|
+
end
|
|
79
|
+
end
|
|
45
80
|
end
|
|
46
81
|
|
|
47
82
|
def initialize(irb_context)
|
data/lib/irb/command/copy.rb
CHANGED
|
@@ -54,6 +54,8 @@ module IRB
|
|
|
54
54
|
def clipboard_program
|
|
55
55
|
@clipboard_program ||= if IRB.conf[:COPY_COMMAND]
|
|
56
56
|
IRB.conf[:COPY_COMMAND]
|
|
57
|
+
elsif executable?("clip.exe")
|
|
58
|
+
"clip.exe"
|
|
57
59
|
elsif executable?("pbcopy")
|
|
58
60
|
"pbcopy"
|
|
59
61
|
elsif executable?("xclip")
|
|
@@ -62,12 +64,20 @@ module IRB
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
def executable?(command)
|
|
65
|
-
|
|
67
|
+
if windows?
|
|
68
|
+
system("where #{command} > NUL 2>&1")
|
|
69
|
+
else
|
|
70
|
+
system("which #{command} > /dev/null 2>&1")
|
|
71
|
+
end
|
|
66
72
|
end
|
|
67
73
|
|
|
68
74
|
def clipboard_available?
|
|
69
75
|
!!clipboard_program
|
|
70
76
|
end
|
|
77
|
+
|
|
78
|
+
def windows?
|
|
79
|
+
/mingw|mswin/.match?(RUBY_PLATFORM)
|
|
80
|
+
end
|
|
71
81
|
end
|
|
72
82
|
end
|
|
73
83
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'prism'
|
|
4
|
+
|
|
3
5
|
module IRB
|
|
4
6
|
module Command
|
|
5
7
|
# Internal use only, for default command's backward compatibility.
|
|
@@ -7,9 +9,10 @@ module IRB
|
|
|
7
9
|
def unwrap_string_literal(str)
|
|
8
10
|
return if str.empty?
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
result = Prism.parse(str)
|
|
13
|
+
body = result.value.statements.body
|
|
14
|
+
if result.success? && body.size == 1 && body.first.is_a?(Prism::StringNode)
|
|
15
|
+
body.first.unescaped
|
|
13
16
|
else
|
|
14
17
|
str
|
|
15
18
|
end
|
data/lib/irb/command/ls.rb
CHANGED
|
@@ -55,11 +55,13 @@ module IRB
|
|
|
55
55
|
|
|
56
56
|
o = Output.new(grep: grep)
|
|
57
57
|
|
|
58
|
-
klass
|
|
58
|
+
klass = Kernel.instance_method(:class).bind(obj).call
|
|
59
|
+
obj_is_class_or_module = Module === obj
|
|
60
|
+
klass = obj_is_class_or_module ? obj : klass
|
|
59
61
|
|
|
60
|
-
o.dump("constants", obj.constants) if
|
|
62
|
+
o.dump("constants", obj.constants) if obj_is_class_or_module
|
|
61
63
|
dump_methods(o, klass, obj)
|
|
62
|
-
o.dump("instance variables",
|
|
64
|
+
o.dump("instance variables", Kernel.instance_method(:instance_variables).bind(obj).call)
|
|
63
65
|
o.dump("class variables", klass.class_variables)
|
|
64
66
|
o.dump("locals", locals) if locals
|
|
65
67
|
o.print_result
|
|
@@ -67,7 +69,7 @@ module IRB
|
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
def dump_methods(o, klass, obj)
|
|
70
|
-
singleton_class = begin obj.
|
|
72
|
+
singleton_class = begin Kernel.instance_method(:singleton_class).bind(obj).call; rescue TypeError; nil end
|
|
71
73
|
dumped_mods = Array.new
|
|
72
74
|
ancestors = klass.ancestors
|
|
73
75
|
ancestors = ancestors.reject { |c| c >= Object } if klass < Object
|