irb 1.15.2 → 1.17.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 +9 -1
- 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 +198 -167
- data/lib/irb/command/copy.rb +11 -1
- data/lib/irb/completion.rb +31 -36
- data/lib/irb/context.rb +9 -8
- data/lib/irb/debug.rb +3 -3
- data/lib/irb/easter-egg.rb +3 -1
- data/lib/irb/ext/multi-irb.rb +2 -0
- data/lib/irb/input-method.rb +2 -2
- data/lib/irb/inspector.rb +1 -1
- data/lib/irb/lc/help-message +2 -2
- data/lib/irb/lc/ja/help-message +1 -1
- data/lib/irb/nesting_parser.rb +361 -213
- data/lib/irb/ruby-lex.rb +67 -41
- data/lib/irb/source_finder.rb +7 -4
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +9 -0
- data/lib/irb.rb +61 -28
- metadata +25 -7
- data/Rakefile +0 -52
- data/bin/console +0 -6
- data/bin/setup +0 -6
- data/irb.gemspec +0 -48
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,100 @@ 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_LOOP: [GREEN],
|
|
73
|
+
KEYWORD_FOR: [GREEN],
|
|
74
|
+
KEYWORD_BEGIN: [GREEN],
|
|
75
|
+
KEYWORD_RESCUE: [GREEN],
|
|
76
|
+
KEYWORD_ENSURE: [GREEN],
|
|
77
|
+
KEYWORD_ALIAS: [GREEN],
|
|
78
|
+
KEYWORD_UNDEF: [GREEN],
|
|
79
|
+
KEYWORD_BEGIN_UPCASE: [GREEN],
|
|
80
|
+
KEYWORD_END_UPCASE: [GREEN],
|
|
81
|
+
KEYWORD_YIELD: [GREEN],
|
|
82
|
+
KEYWORD_REDO: [GREEN],
|
|
83
|
+
KEYWORD_RETRY: [GREEN],
|
|
84
|
+
KEYWORD_NEXT: [GREEN],
|
|
85
|
+
KEYWORD_BREAK: [GREEN],
|
|
86
|
+
KEYWORD_SUPER: [GREEN],
|
|
87
|
+
KEYWORD_RETURN: [GREEN],
|
|
88
|
+
KEYWORD_DEFINED: [GREEN],
|
|
89
|
+
KEYWORD_NOT: [GREEN],
|
|
90
|
+
KEYWORD_AND: [GREEN],
|
|
91
|
+
KEYWORD_OR: [GREEN],
|
|
92
|
+
LABEL: [MAGENTA],
|
|
93
|
+
LABEL_END: [RED, BOLD],
|
|
94
|
+
NUMBERED_REFERENCE: [GREEN, BOLD],
|
|
95
|
+
PERCENT_UPPER_W: [RED, BOLD],
|
|
96
|
+
PERCENT_LOWER_W: [RED, BOLD],
|
|
97
|
+
PERCENT_LOWER_X: [RED, BOLD],
|
|
98
|
+
REGEXP_BEGIN: [RED, BOLD],
|
|
99
|
+
REGEXP_END: [RED, BOLD],
|
|
100
|
+
STRING_BEGIN: [RED, BOLD],
|
|
101
|
+
STRING_CONTENT: [RED],
|
|
102
|
+
STRING_END: [RED, BOLD],
|
|
103
|
+
__END__: [GREEN],
|
|
104
|
+
# tokens from syntax tree traversal
|
|
105
|
+
method_name: [BLUE, BOLD],
|
|
106
|
+
symbol: [YELLOW],
|
|
107
|
+
# special colorization
|
|
108
|
+
error: [RED, REVERSE],
|
|
109
|
+
const_env: [CYAN, BOLD],
|
|
110
|
+
}.transform_values do |styles|
|
|
111
|
+
styles.map { |style| "\e[#{style}m" }.join
|
|
75
112
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
ERROR_TOKENS = TOKEN_SEQ_EXPRS.keys.select { |k| k.to_s.end_with?('error') }
|
|
79
|
-
private_constant :ERROR_TOKENS
|
|
113
|
+
CLEAR_SEQ = "\e[#{CLEAR}m"
|
|
114
|
+
private_constant :TOKEN_SEQS, :CLEAR_SEQ
|
|
80
115
|
|
|
81
116
|
class << self
|
|
82
117
|
def colorable?
|
|
@@ -113,14 +148,13 @@ module IRB # :nodoc:
|
|
|
113
148
|
end
|
|
114
149
|
|
|
115
150
|
def clear(colorable: colorable?)
|
|
116
|
-
|
|
117
|
-
"\e[#{CLEAR}m"
|
|
151
|
+
colorable ? CLEAR_SEQ : ''
|
|
118
152
|
end
|
|
119
153
|
|
|
120
154
|
def colorize(text, seq, colorable: colorable?)
|
|
121
155
|
return text unless colorable
|
|
122
156
|
seq = seq.map { |s| "\e[#{const_get(s)}m" }.join('')
|
|
123
|
-
"#{seq}#{text}#{
|
|
157
|
+
"#{seq}#{text}#{CLEAR_SEQ}"
|
|
124
158
|
end
|
|
125
159
|
|
|
126
160
|
# If `complete` is false (code is incomplete), this does not warn compile_error.
|
|
@@ -129,135 +163,132 @@ module IRB # :nodoc:
|
|
|
129
163
|
def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
|
|
130
164
|
return code unless colorable
|
|
131
165
|
|
|
132
|
-
|
|
133
|
-
colored = +''
|
|
134
|
-
lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
|
|
135
|
-
code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
|
|
166
|
+
result = Prism.parse_lex(code, scopes: [local_variables])
|
|
136
167
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
end
|
|
168
|
+
# IRB::ColorPrinter skips colorizing syntax invalid fragments
|
|
169
|
+
return Reline::Unicode.escape_for_print(code) if ignore_error && !result.success?
|
|
170
|
+
|
|
171
|
+
errors = result.errors
|
|
172
|
+
unless complete
|
|
173
|
+
errors = errors.reject { |error| error.message =~ /\Aexpected a|unexpected end-of-input|unterminated/ }
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
prism_node, prism_tokens = result.value
|
|
177
|
+
visitor = ColorizeVisitor.new
|
|
178
|
+
prism_node.accept(visitor)
|
|
179
|
+
|
|
180
|
+
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] }
|
|
181
|
+
error_tokens.reject! { |t| t.last.match?(/\A\s*\z/) }
|
|
182
|
+
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] }
|
|
183
|
+
tokens.pop if tokens.last&.[](5) == :EOF
|
|
143
184
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
185
|
+
colored = +''
|
|
186
|
+
line_index = 0
|
|
187
|
+
col = 0
|
|
188
|
+
lines = code.lines
|
|
189
|
+
flush = -> next_line_index, next_col {
|
|
190
|
+
return if next_line_index == line_index && next_col == col
|
|
191
|
+
(line_index...[next_line_index, lines.size].min).each do |ln|
|
|
192
|
+
colored << Reline::Unicode.escape_for_print(lines[line_index].byteslice(col..))
|
|
193
|
+
line_index = ln + 1
|
|
194
|
+
col = 0
|
|
195
|
+
end
|
|
196
|
+
unless col == next_col
|
|
197
|
+
colored << Reline::Unicode.escape_for_print(lines[next_line_index].byteslice(col..next_col - 1))
|
|
147
198
|
end
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
(visitor.tokens + tokens + error_tokens).sort.each do |start_line, start_column, _priority, end_line, end_column, type, value|
|
|
202
|
+
next if start_line - 1 < line_index || (start_line - 1 == line_index && start_column < col)
|
|
148
203
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
204
|
+
flush.call(start_line - 1, start_column)
|
|
205
|
+
if type == :CONSTANT && value == 'ENV'
|
|
206
|
+
color = TOKEN_SEQS[:const_env]
|
|
207
|
+
elsif type == :__END__
|
|
208
|
+
color = TOKEN_SEQS[type]
|
|
209
|
+
end_line = start_line
|
|
210
|
+
value = '__END__'
|
|
211
|
+
end_column = start_column + 7
|
|
212
|
+
else
|
|
213
|
+
color = TOKEN_SEQS[type]
|
|
214
|
+
end
|
|
215
|
+
if color
|
|
216
|
+
value.split(/(\n)/).each do |s|
|
|
217
|
+
colored << (s == "\n" ? s : "#{color}#{Reline::Unicode.escape_for_print(s)}#{CLEAR_SEQ}")
|
|
157
218
|
end
|
|
219
|
+
else
|
|
220
|
+
colored << value
|
|
158
221
|
end
|
|
222
|
+
line_index = end_line - 1
|
|
223
|
+
col = end_column
|
|
159
224
|
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
|
|
225
|
+
flush.call lines.size, 0
|
|
165
226
|
colored
|
|
166
227
|
end
|
|
167
228
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
block.call
|
|
174
|
-
ensure
|
|
175
|
-
seen.delete(obj)
|
|
176
|
-
end
|
|
229
|
+
class ColorizeVisitor < Prism::Visitor
|
|
230
|
+
attr_reader :tokens
|
|
231
|
+
def initialize
|
|
232
|
+
@tokens = []
|
|
233
|
+
end
|
|
177
234
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no)
|
|
182
|
-
byte_pos = 0
|
|
183
|
-
line_positions = [0]
|
|
184
|
-
inner_code.lines.each do |line|
|
|
185
|
-
line_positions << line_positions.last + line.bytesize
|
|
235
|
+
def dispatch(location, type)
|
|
236
|
+
if location
|
|
237
|
+
@tokens << [location.start_line, location.start_column, 1, location.end_line, location.end_column, type, location.slice]
|
|
186
238
|
end
|
|
239
|
+
end
|
|
187
240
|
|
|
188
|
-
|
|
189
|
-
|
|
241
|
+
def visit_array_node(node)
|
|
242
|
+
if node.opening&.match?(/\A%[iI]/)
|
|
243
|
+
dispatch node.opening_loc, :symbol
|
|
244
|
+
dispatch node.closing_loc, :symbol
|
|
245
|
+
end
|
|
246
|
+
super
|
|
247
|
+
end
|
|
190
248
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
249
|
+
def visit_def_node(node)
|
|
250
|
+
dispatch node.name_loc, :method_name
|
|
251
|
+
super
|
|
252
|
+
end
|
|
195
253
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
254
|
+
def visit_interpolated_symbol_node(node)
|
|
255
|
+
dispatch node.opening_loc, :symbol
|
|
256
|
+
node.parts.each do |part|
|
|
257
|
+
case part
|
|
258
|
+
when Prism::StringNode
|
|
259
|
+
dispatch part.content_loc, :symbol
|
|
260
|
+
when Prism::EmbeddedStatementsNode
|
|
261
|
+
dispatch part.opening_loc, :symbol
|
|
262
|
+
dispatch part.closing_loc, :symbol
|
|
263
|
+
when Prism::EmbeddedVariableNode
|
|
264
|
+
dispatch part.operator_loc, :symbol
|
|
200
265
|
end
|
|
201
266
|
end
|
|
267
|
+
dispatch node.closing_loc, :symbol
|
|
268
|
+
super
|
|
269
|
+
end
|
|
202
270
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
271
|
+
def visit_symbol_node(node)
|
|
272
|
+
if (node.opening_loc.nil? && node.closing == ':') || node.closing&.match?(/\A['"]:\z/)
|
|
273
|
+
# Colorize { symbol: 1 } and { 'symbol': 1 } as label
|
|
274
|
+
dispatch node.location, :LABEL
|
|
275
|
+
else
|
|
276
|
+
dispatch node.opening_loc, :symbol
|
|
277
|
+
dispatch node.value_loc, :symbol
|
|
278
|
+
dispatch node.closing_loc, :symbol
|
|
206
279
|
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
280
|
end
|
|
210
|
-
ensure
|
|
211
|
-
$VERBOSE = verbose
|
|
212
281
|
end
|
|
213
282
|
|
|
214
|
-
|
|
215
|
-
if ERROR_TOKENS.include?(token)
|
|
216
|
-
TOKEN_SEQ_EXPRS[token][0]
|
|
217
|
-
elsif in_symbol
|
|
218
|
-
[YELLOW]
|
|
219
|
-
elsif TOKEN_KEYWORDS.fetch(token, []).include?(str)
|
|
220
|
-
[CYAN, BOLD]
|
|
221
|
-
elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; (expr & (exprs || 0)) != 0)
|
|
222
|
-
seq
|
|
223
|
-
else
|
|
224
|
-
nil
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# A class to manage a state to know whether the current token is for Symbol or not.
|
|
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
|
|
283
|
+
private
|
|
235
284
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
when :on_ident, :on_op, :on_const, :on_ivar, :on_cvar, :on_gvar, :on_kw, :on_backtick
|
|
243
|
-
if @stack.last # Pop only when it's Symbol
|
|
244
|
-
@stack.pop
|
|
245
|
-
return prev_state
|
|
246
|
-
end
|
|
247
|
-
when :on_tstring_beg
|
|
248
|
-
@stack << false
|
|
249
|
-
when :on_embexpr_beg
|
|
250
|
-
@stack << false
|
|
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
|
|
257
|
-
end
|
|
258
|
-
@stack.last
|
|
285
|
+
def without_circular_ref(obj, seen:, &block)
|
|
286
|
+
return false if seen.key?(obj)
|
|
287
|
+
seen[obj] = true
|
|
288
|
+
block.call
|
|
289
|
+
ensure
|
|
290
|
+
seen.delete(obj)
|
|
259
291
|
end
|
|
260
292
|
end
|
|
261
|
-
private_constant :SymbolState
|
|
262
293
|
end
|
|
263
294
|
end
|
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
|
data/lib/irb/completion.rb
CHANGED
|
@@ -12,26 +12,6 @@ module IRB
|
|
|
12
12
|
|
|
13
13
|
# Set of reserved words used by Ruby, you should not use these for
|
|
14
14
|
# constants or variables
|
|
15
|
-
ReservedWords = %w[
|
|
16
|
-
__ENCODING__ __LINE__ __FILE__
|
|
17
|
-
BEGIN END
|
|
18
|
-
alias and
|
|
19
|
-
begin break
|
|
20
|
-
case class
|
|
21
|
-
def defined? do
|
|
22
|
-
else elsif end ensure
|
|
23
|
-
false for
|
|
24
|
-
if in
|
|
25
|
-
module
|
|
26
|
-
next nil not
|
|
27
|
-
or
|
|
28
|
-
redo rescue retry return
|
|
29
|
-
self super
|
|
30
|
-
then true
|
|
31
|
-
undef unless until
|
|
32
|
-
when while
|
|
33
|
-
yield
|
|
34
|
-
]
|
|
35
15
|
|
|
36
16
|
HELP_COMMAND_PREPOSING = /\Ahelp\s+/
|
|
37
17
|
|
|
@@ -60,13 +40,13 @@ module IRB
|
|
|
60
40
|
|
|
61
41
|
def retrieve_gem_and_system_load_path
|
|
62
42
|
candidates = (GEM_PATHS | $LOAD_PATH)
|
|
63
|
-
candidates.
|
|
43
|
+
candidates.filter_map do |p|
|
|
64
44
|
if p.respond_to?(:to_path)
|
|
65
45
|
p.to_path
|
|
66
46
|
else
|
|
67
47
|
String(p) rescue nil
|
|
68
48
|
end
|
|
69
|
-
end.
|
|
49
|
+
end.sort
|
|
70
50
|
end
|
|
71
51
|
|
|
72
52
|
def retrieve_files_to_require_from_load_path
|
|
@@ -127,7 +107,14 @@ module IRB
|
|
|
127
107
|
|
|
128
108
|
return commands unless result
|
|
129
109
|
|
|
130
|
-
|
|
110
|
+
encoded_candidates = result.completion_candidates.filter_map do |i|
|
|
111
|
+
encoded = i.encode(Encoding.default_external)
|
|
112
|
+
target + encoded
|
|
113
|
+
rescue Encoding::UndefinedConversionError
|
|
114
|
+
# If the string cannot be converted, we just ignore it
|
|
115
|
+
nil
|
|
116
|
+
end
|
|
117
|
+
commands | encoded_candidates
|
|
131
118
|
end
|
|
132
119
|
|
|
133
120
|
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
@@ -179,7 +166,7 @@ module IRB
|
|
|
179
166
|
else
|
|
180
167
|
return nil # It's not String literal
|
|
181
168
|
end
|
|
182
|
-
tokens = RubyLex.ripper_lex_without_warning(preposing.
|
|
169
|
+
tokens = RubyLex.ripper_lex_without_warning(preposing.rstrip)
|
|
183
170
|
tok = nil
|
|
184
171
|
tokens.reverse_each do |t|
|
|
185
172
|
unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
|
|
@@ -191,16 +178,12 @@ module IRB
|
|
|
191
178
|
|
|
192
179
|
case tok.tok
|
|
193
180
|
when 'require'
|
|
194
|
-
retrieve_files_to_require_from_load_path.
|
|
195
|
-
path.start_with?(actual_target)
|
|
196
|
-
}.map { |path|
|
|
197
|
-
quote + path
|
|
181
|
+
retrieve_files_to_require_from_load_path.filter_map { |path|
|
|
182
|
+
quote + path if path.start_with?(actual_target)
|
|
198
183
|
}
|
|
199
184
|
when 'require_relative'
|
|
200
|
-
retrieve_files_to_require_relative_from_current_dir.
|
|
201
|
-
path.start_with?(actual_target)
|
|
202
|
-
}.map { |path|
|
|
203
|
-
quote + path
|
|
185
|
+
retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
|
|
186
|
+
quote + path if path.start_with?(actual_target)
|
|
204
187
|
}
|
|
205
188
|
end
|
|
206
189
|
end
|
|
@@ -218,7 +201,12 @@ module IRB
|
|
|
218
201
|
# It doesn't make sense to propose commands with other preposing
|
|
219
202
|
commands = [] unless preposing.empty?
|
|
220
203
|
|
|
221
|
-
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.
|
|
204
|
+
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.filter_map do |i|
|
|
205
|
+
i.encode(Encoding.default_external)
|
|
206
|
+
rescue Encoding::UndefinedConversionError
|
|
207
|
+
# If the string cannot be converted, we just ignore it
|
|
208
|
+
nil
|
|
209
|
+
end
|
|
222
210
|
commands | completion_data
|
|
223
211
|
end
|
|
224
212
|
|
|
@@ -287,7 +275,7 @@ module IRB
|
|
|
287
275
|
nil
|
|
288
276
|
else
|
|
289
277
|
sym = $1
|
|
290
|
-
candidates = Symbol.all_symbols.
|
|
278
|
+
candidates = Symbol.all_symbols.filter_map do |s|
|
|
291
279
|
s.inspect
|
|
292
280
|
rescue EncodingError
|
|
293
281
|
# ignore
|
|
@@ -459,12 +447,19 @@ module IRB
|
|
|
459
447
|
eval("#{perfect_match_var}.class.name", bind) rescue nil
|
|
460
448
|
else
|
|
461
449
|
candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
|
|
462
|
-
candidates |=
|
|
450
|
+
candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
|
|
463
451
|
candidates.find{ |i| i == input }
|
|
464
452
|
end
|
|
465
453
|
else
|
|
466
454
|
candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
|
|
467
|
-
candidates |=
|
|
455
|
+
candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
|
|
456
|
+
|
|
457
|
+
target_encoding = Encoding.default_external
|
|
458
|
+
candidates = candidates.compact.filter_map do |candidate|
|
|
459
|
+
candidate.encoding == target_encoding ? candidate : candidate.encode(target_encoding)
|
|
460
|
+
rescue EncodingError
|
|
461
|
+
nil
|
|
462
|
+
end
|
|
468
463
|
candidates.grep(/^#{Regexp.quote(input)}/).sort
|
|
469
464
|
end
|
|
470
465
|
end
|
data/lib/irb/context.rb
CHANGED
|
@@ -632,14 +632,15 @@ module IRB
|
|
|
632
632
|
command_class = Command.load_command(command)
|
|
633
633
|
end
|
|
634
634
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
635
|
+
if command_class
|
|
636
|
+
# Check whether the command conflicts with existing methods
|
|
637
|
+
public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
|
|
638
|
+
private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
|
|
639
|
+
if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
|
|
640
|
+
return Statement::Command.new(code, command_class, arg)
|
|
641
|
+
end
|
|
642
642
|
end
|
|
643
|
+
Statement::Expression.new(code, is_assignment_expression)
|
|
643
644
|
end
|
|
644
645
|
|
|
645
646
|
def colorize_input(input, complete:)
|
|
@@ -648,7 +649,7 @@ module IRB
|
|
|
648
649
|
parsed_input = parse_input(input, false)
|
|
649
650
|
if parsed_input.is_a?(Statement::Command)
|
|
650
651
|
name, sep, arg = input.split(/(\s+)/, 2)
|
|
651
|
-
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
|
|
652
|
+
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars) if arg
|
|
652
653
|
"#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
|
|
653
654
|
else
|
|
654
655
|
IRB::Color.colorize_code(input, complete: complete, local_variables: lvars)
|
data/lib/irb/debug.rb
CHANGED
|
@@ -49,7 +49,7 @@ module IRB
|
|
|
49
49
|
def DEBUGGER__.capture_frames(*args)
|
|
50
50
|
frames = capture_frames_without_irb(*args)
|
|
51
51
|
frames.reject! do |frame|
|
|
52
|
-
frame.realpath&.start_with?(IRB_DIR) || frame.path
|
|
52
|
+
frame.realpath&.start_with?(IRB_DIR) || frame.path&.start_with?("<internal:")
|
|
53
53
|
end
|
|
54
54
|
frames
|
|
55
55
|
end
|
|
@@ -60,7 +60,7 @@ module IRB
|
|
|
60
60
|
if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
|
|
61
61
|
Reline.output_modifier_proc = proc do |input, complete:|
|
|
62
62
|
unless input.strip.empty?
|
|
63
|
-
cmd = input
|
|
63
|
+
cmd = input[/\S+/]
|
|
64
64
|
|
|
65
65
|
if !complete && DEBUGGER__.commands.key?(cmd)
|
|
66
66
|
input = input.sub(/\n$/, " # debug command\n")
|
|
@@ -87,7 +87,7 @@ module IRB
|
|
|
87
87
|
module SkipPathHelperForIRB
|
|
88
88
|
def skip_internal_path?(path)
|
|
89
89
|
# The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
|
|
90
|
-
super || path
|
|
90
|
+
super || path&.match?(IRB_DIR) || path&.match?('<internal:prelude>')
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
data/lib/irb/easter-egg.rb
CHANGED
|
@@ -121,12 +121,13 @@ module IRB
|
|
|
121
121
|
interrupted = false
|
|
122
122
|
prev_trap = trap("SIGINT") { interrupted = true }
|
|
123
123
|
canvas = Canvas.new(Reline.get_screen_size)
|
|
124
|
+
otio = Reline::IOGate.prep
|
|
124
125
|
Reline::IOGate.set_winch_handler do
|
|
125
126
|
canvas = Canvas.new(Reline.get_screen_size)
|
|
126
127
|
end
|
|
127
128
|
ruby_model = RubyModel.new
|
|
128
129
|
print "\e[?25l" # hide cursor
|
|
129
|
-
0.
|
|
130
|
+
(0..).each do |i|
|
|
130
131
|
buff = canvas.draw do
|
|
131
132
|
ruby_model.render_frame(i) do |p1, p2|
|
|
132
133
|
canvas.line(p1, p2)
|
|
@@ -139,6 +140,7 @@ module IRB
|
|
|
139
140
|
end
|
|
140
141
|
rescue Interrupt
|
|
141
142
|
ensure
|
|
143
|
+
Reline::IOGate.deprep(otio)
|
|
142
144
|
print "\e[?25h" # show cursor
|
|
143
145
|
trap("SIGINT", prev_trap)
|
|
144
146
|
end
|