irb 1.17.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/lib/irb/color.rb +65 -8
- data/lib/irb/command/base.rb +35 -0
- 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/init.rb +3 -0
- data/lib/irb/input-method.rb +141 -111
- data/lib/irb/nesting_parser.rb +1 -0
- data/lib/irb/pager.rb +8 -0
- data/lib/irb/ruby-lex.rb +164 -264
- 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 +16 -7
- metadata +2 -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/lib/irb/color.rb
CHANGED
|
@@ -69,6 +69,7 @@ module IRB # :nodoc:
|
|
|
69
69
|
KEYWORD_IN: [GREEN],
|
|
70
70
|
KEYWORD_DEF: [GREEN],
|
|
71
71
|
KEYWORD_DO: [GREEN],
|
|
72
|
+
KEYWORD_DO_BLOCK: [GREEN],
|
|
72
73
|
KEYWORD_DO_LOOP: [GREEN],
|
|
73
74
|
KEYWORD_FOR: [GREEN],
|
|
74
75
|
KEYWORD_BEGIN: [GREEN],
|
|
@@ -102,16 +103,17 @@ module IRB # :nodoc:
|
|
|
102
103
|
STRING_END: [RED, BOLD],
|
|
103
104
|
__END__: [GREEN],
|
|
104
105
|
# tokens from syntax tree traversal
|
|
105
|
-
method_name: [
|
|
106
|
+
method_name: [CYAN, BOLD],
|
|
107
|
+
message_name: [CYAN],
|
|
106
108
|
symbol: [YELLOW],
|
|
107
109
|
# special colorization
|
|
108
110
|
error: [RED, REVERSE],
|
|
109
|
-
const_env: [CYAN, BOLD],
|
|
110
111
|
}.transform_values do |styles|
|
|
111
112
|
styles.map { |style| "\e[#{style}m" }.join
|
|
112
113
|
end
|
|
113
114
|
CLEAR_SEQ = "\e[#{CLEAR}m"
|
|
114
|
-
|
|
115
|
+
OPERATORS = %i(!= !~ =~ == === <=> > >= < <= & | ^ >> << - + % / * ** -@ +@ ~ ! [] []=)
|
|
116
|
+
private_constant :TOKEN_SEQS, :CLEAR_SEQ, :OPERATORS
|
|
115
117
|
|
|
116
118
|
class << self
|
|
117
119
|
def colorable?
|
|
@@ -168,12 +170,13 @@ module IRB # :nodoc:
|
|
|
168
170
|
# IRB::ColorPrinter skips colorizing syntax invalid fragments
|
|
169
171
|
return Reline::Unicode.escape_for_print(code) if ignore_error && !result.success?
|
|
170
172
|
|
|
173
|
+
prism_node, prism_tokens = result.value
|
|
171
174
|
errors = result.errors
|
|
175
|
+
|
|
172
176
|
unless complete
|
|
173
|
-
errors = errors
|
|
177
|
+
errors = filter_incomplete_code_errors(errors, prism_tokens)
|
|
174
178
|
end
|
|
175
179
|
|
|
176
|
-
prism_node, prism_tokens = result.value
|
|
177
180
|
visitor = ColorizeVisitor.new
|
|
178
181
|
prism_node.accept(visitor)
|
|
179
182
|
|
|
@@ -202,9 +205,7 @@ module IRB # :nodoc:
|
|
|
202
205
|
next if start_line - 1 < line_index || (start_line - 1 == line_index && start_column < col)
|
|
203
206
|
|
|
204
207
|
flush.call(start_line - 1, start_column)
|
|
205
|
-
if type == :
|
|
206
|
-
color = TOKEN_SEQS[:const_env]
|
|
207
|
-
elsif type == :__END__
|
|
208
|
+
if type == :__END__
|
|
208
209
|
color = TOKEN_SEQS[type]
|
|
209
210
|
end_line = start_line
|
|
210
211
|
value = '__END__'
|
|
@@ -251,6 +252,31 @@ module IRB # :nodoc:
|
|
|
251
252
|
super
|
|
252
253
|
end
|
|
253
254
|
|
|
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
|
|
272
|
+
|
|
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
|
+
|
|
254
280
|
def visit_interpolated_symbol_node(node)
|
|
255
281
|
dispatch node.opening_loc, :symbol
|
|
256
282
|
node.parts.each do |part|
|
|
@@ -278,10 +304,41 @@ module IRB # :nodoc:
|
|
|
278
304
|
dispatch node.closing_loc, :symbol
|
|
279
305
|
end
|
|
280
306
|
end
|
|
307
|
+
|
|
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
|
|
314
|
+
end
|
|
281
315
|
end
|
|
282
316
|
|
|
283
317
|
private
|
|
284
318
|
|
|
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
|
|
334
|
+
end
|
|
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
|
+
|
|
285
342
|
def without_circular_ref(obj, seen:, &block)
|
|
286
343
|
return false if seen.key?(obj)
|
|
287
344
|
seen[obj] = true
|
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)
|
|
@@ -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
|
data/lib/irb/completion.rb
CHANGED
|
@@ -8,6 +8,29 @@
|
|
|
8
8
|
require_relative 'ruby-lex'
|
|
9
9
|
|
|
10
10
|
module IRB
|
|
11
|
+
class DocumentTarget # :nodoc:
|
|
12
|
+
attr_reader :name
|
|
13
|
+
|
|
14
|
+
def initialize(name)
|
|
15
|
+
@name = name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class CommandDocument < DocumentTarget # :nodoc:
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Represents a method/class documentation target. May hold multiple names
|
|
23
|
+
# when the receiver is ambiguous (e.g. `{}.any?` could be Hash#any? or Proc#any?).
|
|
24
|
+
# The dialog popup uses only the first name; the full-screen display renders all.
|
|
25
|
+
class MethodDocument < DocumentTarget # :nodoc:
|
|
26
|
+
attr_reader :names
|
|
27
|
+
|
|
28
|
+
def initialize(*names)
|
|
29
|
+
super(names.first)
|
|
30
|
+
@names = names
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
11
34
|
class BaseCompletor # :nodoc:
|
|
12
35
|
|
|
13
36
|
# Set of reserved words used by Ruby, you should not use these for
|
|
@@ -76,6 +99,12 @@ module IRB
|
|
|
76
99
|
end
|
|
77
100
|
end
|
|
78
101
|
|
|
102
|
+
def command_document_target(preposing, matched)
|
|
103
|
+
if preposing.empty? && IRB::Command.command_names.include?(matched)
|
|
104
|
+
CommandDocument.new(matched)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
79
108
|
def retrieve_files_to_require_relative_from_current_dir
|
|
80
109
|
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
|
|
81
110
|
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
|
@@ -118,8 +147,10 @@ module IRB
|
|
|
118
147
|
end
|
|
119
148
|
|
|
120
149
|
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
121
|
-
|
|
122
|
-
|
|
150
|
+
command_document_target(preposing, matched) || begin
|
|
151
|
+
result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
|
|
152
|
+
result&.doc_namespace('')
|
|
153
|
+
end
|
|
123
154
|
end
|
|
124
155
|
end
|
|
125
156
|
|
|
@@ -166,22 +197,13 @@ module IRB
|
|
|
166
197
|
else
|
|
167
198
|
return nil # It's not String literal
|
|
168
199
|
end
|
|
169
|
-
tokens = RubyLex.ripper_lex_without_warning(preposing.rstrip)
|
|
170
|
-
tok = nil
|
|
171
|
-
tokens.reverse_each do |t|
|
|
172
|
-
unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
|
|
173
|
-
tok = t
|
|
174
|
-
break
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
|
|
178
200
|
|
|
179
|
-
case
|
|
180
|
-
when
|
|
201
|
+
case preposing
|
|
202
|
+
when /(^|[^\w])require\(? *\z/
|
|
181
203
|
retrieve_files_to_require_from_load_path.filter_map { |path|
|
|
182
204
|
quote + path if path.start_with?(actual_target)
|
|
183
205
|
}
|
|
184
|
-
when
|
|
206
|
+
when /(^|[^\w])require_relative\(? *\z/
|
|
185
207
|
retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
|
|
186
208
|
quote + path if path.start_with?(actual_target)
|
|
187
209
|
}
|
|
@@ -210,8 +232,8 @@ module IRB
|
|
|
210
232
|
commands | completion_data
|
|
211
233
|
end
|
|
212
234
|
|
|
213
|
-
def doc_namespace(
|
|
214
|
-
retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
|
235
|
+
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
236
|
+
command_document_target(preposing, matched) || retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
|
215
237
|
end
|
|
216
238
|
|
|
217
239
|
def retrieve_completion_data(input, bind:, doc_namespace:)
|
data/lib/irb/init.rb
CHANGED
|
@@ -87,6 +87,7 @@ module IRB # :nodoc:
|
|
|
87
87
|
@CONF[:IGNORE_SIGINT] = true
|
|
88
88
|
@CONF[:IGNORE_EOF] = false
|
|
89
89
|
@CONF[:USE_PAGER] = true
|
|
90
|
+
@CONF[:SHOW_BANNER] = true
|
|
90
91
|
@CONF[:EXTRA_DOC_DIRS] = []
|
|
91
92
|
@CONF[:ECHO] = nil
|
|
92
93
|
@CONF[:ECHO_ON_ASSIGNMENT] = nil
|
|
@@ -345,6 +346,8 @@ module IRB # :nodoc:
|
|
|
345
346
|
opt = $1 || argv.shift
|
|
346
347
|
prompt_mode = opt.upcase.tr("-", "_").intern
|
|
347
348
|
@CONF[:PROMPT_MODE] = prompt_mode
|
|
349
|
+
when "--nobanner"
|
|
350
|
+
@CONF[:SHOW_BANNER] = false
|
|
348
351
|
when "--noprompt"
|
|
349
352
|
@CONF[:PROMPT_MODE] = :NULL
|
|
350
353
|
when "--script"
|
data/lib/irb/input-method.rb
CHANGED
|
@@ -255,6 +255,16 @@ module IRB
|
|
|
255
255
|
|
|
256
256
|
class RelineInputMethod < StdioInputMethod
|
|
257
257
|
HISTORY = Reline::HISTORY
|
|
258
|
+
ALT_KEY_NAME = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
|
|
259
|
+
PRESS_ALT_D_TO_READ_FULL_DOC = "Press #{ALT_KEY_NAME}+d to read the full document".freeze
|
|
260
|
+
PRESS_ALT_D_TO_SEE_MORE = "Press #{ALT_KEY_NAME}+d to see more".freeze
|
|
261
|
+
ALT_D_SEQUENCES = [
|
|
262
|
+
[27, 100], # Normal Alt+d when convert-meta isn't used.
|
|
263
|
+
# When option/alt is not configured as a meta key in terminal emulator,
|
|
264
|
+
# option/alt + d will send a unicode character depend on OS keyboard setting.
|
|
265
|
+
[195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
|
|
266
|
+
[226, 136, 130] # "∂" Alt+d on Mac keyboard.
|
|
267
|
+
].freeze
|
|
258
268
|
include HistorySavingAbility
|
|
259
269
|
# Creates a new input method object using Reline
|
|
260
270
|
def initialize(completor)
|
|
@@ -305,9 +315,17 @@ module IRB
|
|
|
305
315
|
@auto_indent_proc = block
|
|
306
316
|
end
|
|
307
317
|
|
|
308
|
-
def
|
|
318
|
+
def retrieve_document_target(matched)
|
|
309
319
|
preposing, _target, postposing, bind = @completion_params
|
|
310
|
-
@completor.doc_namespace(preposing, matched, postposing, bind: bind)
|
|
320
|
+
result = @completor.doc_namespace(preposing, matched, postposing, bind: bind)
|
|
321
|
+
case result
|
|
322
|
+
when DocumentTarget, nil
|
|
323
|
+
result
|
|
324
|
+
when Array
|
|
325
|
+
MethodDocument.new(*result)
|
|
326
|
+
when String
|
|
327
|
+
MethodDocument.new(result)
|
|
328
|
+
end
|
|
311
329
|
end
|
|
312
330
|
|
|
313
331
|
def rdoc_ri_driver
|
|
@@ -328,146 +346,158 @@ module IRB
|
|
|
328
346
|
input_method = self # self is changed in the lambda below.
|
|
329
347
|
->() {
|
|
330
348
|
dialog.trap_key = nil
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
# When option/alt is not configured as a meta key in terminal emulator,
|
|
334
|
-
# option/alt + d will send a unicode character depend on OS keyboard setting.
|
|
335
|
-
[195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
|
|
336
|
-
[226, 136, 130] # "∂" Alt+d on Mac keyboard.
|
|
337
|
-
]
|
|
338
|
-
|
|
339
|
-
if just_cursor_moving and completion_journey_data.nil?
|
|
349
|
+
|
|
350
|
+
if just_cursor_moving && completion_journey_data.nil?
|
|
340
351
|
return nil
|
|
341
352
|
end
|
|
342
353
|
cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
|
|
343
|
-
return nil if result.nil?
|
|
354
|
+
return nil if result.nil? || pointer.nil? || pointer < 0
|
|
344
355
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
356
|
+
matched_text = result[pointer]
|
|
357
|
+
show_easter_egg = matched_text&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
358
|
+
target = show_easter_egg ? nil : input_method.retrieve_document_target(matched_text)
|
|
348
359
|
|
|
349
|
-
|
|
360
|
+
x, width = input_method.dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
|
|
361
|
+
return nil unless x
|
|
350
362
|
|
|
351
|
-
|
|
363
|
+
dialog.trap_key = ALT_D_SEQUENCES
|
|
352
364
|
|
|
353
365
|
if key.match?(dialog.name)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
# so we need to turn on and off alternate screen manually.
|
|
360
|
-
begin
|
|
361
|
-
print "\e[?1049h"
|
|
362
|
-
driver.display_names([name])
|
|
363
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
364
|
-
ensure
|
|
365
|
-
print "\e[?1049l"
|
|
366
|
-
end
|
|
366
|
+
begin
|
|
367
|
+
print "\e[?1049h"
|
|
368
|
+
input_method.display_document(matched_text)
|
|
369
|
+
ensure
|
|
370
|
+
print "\e[?1049l"
|
|
367
371
|
end
|
|
368
372
|
end
|
|
369
373
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
used_for_class = false
|
|
379
|
-
if not name =~ /#|\./
|
|
380
|
-
found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
|
|
381
|
-
if not found.empty?
|
|
382
|
-
doc = driver.class_document(name, found, klasses, includes, extends)
|
|
383
|
-
used_for_class = true
|
|
374
|
+
contents = case target
|
|
375
|
+
when CommandDocument
|
|
376
|
+
input_method.command_doc_dialog_contents(target.name, width)
|
|
377
|
+
when MethodDocument
|
|
378
|
+
input_method.rdoc_dialog_contents(target.name, width)
|
|
379
|
+
else
|
|
380
|
+
if show_easter_egg
|
|
381
|
+
input_method.easter_egg_dialog_contents
|
|
384
382
|
end
|
|
385
383
|
end
|
|
386
|
-
unless
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
384
|
+
return nil unless contents
|
|
385
|
+
|
|
386
|
+
contents = contents.take(preferred_dialog_height)
|
|
387
|
+
y = cursor_pos_to_render.y
|
|
388
|
+
Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
|
|
389
|
+
}
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def command_doc_dialog_contents(command_name, width)
|
|
393
|
+
command_class = IRB::Command.load_command(command_name)
|
|
394
|
+
return unless command_class
|
|
395
|
+
|
|
396
|
+
[PRESS_ALT_D_TO_READ_FULL_DOC, ""] + command_class.doc_dialog_content(command_name, width)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def easter_egg_dialog_contents
|
|
400
|
+
type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
|
|
401
|
+
lines = IRB.send(:easter_egg_logo, type).split("\n")
|
|
402
|
+
lines[0][0, PRESS_ALT_D_TO_SEE_MORE.size] = PRESS_ALT_D_TO_SEE_MORE
|
|
403
|
+
lines
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def rdoc_dialog_contents(name, width)
|
|
407
|
+
driver = rdoc_ri_driver
|
|
408
|
+
return unless driver
|
|
409
|
+
|
|
410
|
+
name = driver.expand_name(name)
|
|
411
|
+
|
|
412
|
+
doc = if name =~ /#|\./
|
|
413
|
+
d = RDoc::Markup::Document.new
|
|
414
|
+
driver.add_method(d, name)
|
|
415
|
+
d
|
|
416
|
+
else
|
|
417
|
+
found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
|
|
418
|
+
if found.empty?
|
|
419
|
+
d = RDoc::Markup::Document.new
|
|
420
|
+
driver.add_method(d, name)
|
|
421
|
+
d
|
|
422
|
+
else
|
|
423
|
+
driver.class_document(name, found, klasses, includes, extends)
|
|
395
424
|
end
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
formatter = RDoc::Markup::ToAnsi.new
|
|
428
|
+
formatter.width = width
|
|
429
|
+
[PRESS_ALT_D_TO_READ_FULL_DOC] + doc.accept(formatter).split("\n")
|
|
430
|
+
rescue RDoc::RI::Driver::NotFoundError
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
|
|
434
|
+
width = 40
|
|
435
|
+
right_x = cursor_pos_to_render.x + autocomplete_dialog.width
|
|
436
|
+
if right_x + width > screen_width
|
|
437
|
+
right_width = screen_width - (right_x + 1)
|
|
438
|
+
left_x = autocomplete_dialog.column - width
|
|
439
|
+
left_x = 0 if left_x < 0
|
|
440
|
+
left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
|
|
441
|
+
if right_width.positive? && left_width.positive?
|
|
442
|
+
if right_width >= left_width
|
|
414
443
|
width = right_width
|
|
415
444
|
x = right_x
|
|
416
|
-
|
|
445
|
+
else
|
|
417
446
|
width = left_width
|
|
418
447
|
x = left_x
|
|
419
|
-
else # Both are negative width.
|
|
420
|
-
return nil
|
|
421
448
|
end
|
|
422
|
-
|
|
449
|
+
elsif right_width.positive? && left_width <= 0
|
|
450
|
+
width = right_width
|
|
423
451
|
x = right_x
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
dialog.trap_key = alt_d
|
|
428
|
-
mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
|
|
429
|
-
if show_easter_egg
|
|
430
|
-
type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
|
|
431
|
-
contents = IRB.send(:easter_egg_logo, type).split("\n")
|
|
432
|
-
message = "Press #{mod_key}+d to see more"
|
|
433
|
-
contents[0][0, message.size] = message
|
|
452
|
+
elsif right_width <= 0 && left_width.positive?
|
|
453
|
+
width = left_width
|
|
454
|
+
x = left_x
|
|
434
455
|
else
|
|
435
|
-
|
|
436
|
-
contents = [message] + doc.accept(formatter).split("\n")
|
|
456
|
+
return nil
|
|
437
457
|
end
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
458
|
+
else
|
|
459
|
+
x = right_x
|
|
460
|
+
end
|
|
461
|
+
[x, width]
|
|
443
462
|
end
|
|
444
463
|
|
|
445
464
|
def display_document(matched)
|
|
446
|
-
|
|
447
|
-
return unless
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
465
|
+
target = retrieve_document_target(matched)
|
|
466
|
+
return unless target
|
|
467
|
+
|
|
468
|
+
case target
|
|
469
|
+
when CommandDocument
|
|
470
|
+
command_class = IRB::Command.load_command(target.name)
|
|
471
|
+
if command_class
|
|
472
|
+
content = command_class.help_message || command_class.description
|
|
473
|
+
Pager.page(retain_content: true) do |io|
|
|
474
|
+
io.puts content
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
when MethodDocument
|
|
478
|
+
driver = rdoc_ri_driver
|
|
479
|
+
return unless driver
|
|
453
480
|
|
|
454
|
-
|
|
455
|
-
|
|
481
|
+
if matched =~ /\A(?:::)?RubyVM/ && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
482
|
+
IRB.__send__(:easter_egg)
|
|
483
|
+
return
|
|
484
|
+
end
|
|
456
485
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
486
|
+
if target.names.length > 1
|
|
487
|
+
out = RDoc::Markup::Document.new
|
|
488
|
+
target.names.each do |m|
|
|
489
|
+
begin
|
|
490
|
+
driver.add_method(out, m)
|
|
491
|
+
rescue RDoc::RI::Driver::NotFoundError
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
driver.display(out)
|
|
495
|
+
else
|
|
460
496
|
begin
|
|
461
|
-
driver.
|
|
497
|
+
driver.display_names([target.name])
|
|
462
498
|
rescue RDoc::RI::Driver::NotFoundError
|
|
463
499
|
end
|
|
464
500
|
end
|
|
465
|
-
driver.display(out)
|
|
466
|
-
else
|
|
467
|
-
begin
|
|
468
|
-
driver.display_names([namespace])
|
|
469
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
470
|
-
end
|
|
471
501
|
end
|
|
472
502
|
end
|
|
473
503
|
|
data/lib/irb/nesting_parser.rb
CHANGED
|
@@ -222,6 +222,7 @@ module IRB
|
|
|
222
222
|
# Heredoc closing contains trailing newline. We need to exclude it
|
|
223
223
|
close_location_start(node.closing_loc) if node.closing_loc && !node.closing.empty?
|
|
224
224
|
elsif node.opening
|
|
225
|
+
return if node.opening == '?' && node.closing.nil? # Character literal has no closing
|
|
225
226
|
open_location(node.location, type, node.opening)
|
|
226
227
|
if node.closing && node.closing != ''
|
|
227
228
|
# Closing of `"#{\n` is "\n". We need to treat it as not-closed.
|