irb 1.15.1 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rdoc_options +5 -0
- data/CONTRIBUTING.md +52 -0
- data/EXTEND_IRB.md +3 -0
- data/Gemfile +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 +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 +69 -52
- 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/init.rb +3 -0
- data/lib/irb/input-method.rb +158 -122
- 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 +362 -213
- data/lib/irb/pager.rb +9 -1
- data/lib/irb/ruby-lex.rb +225 -299
- data/lib/irb/ruby_logo.aa +4 -0
- data/lib/irb/source_finder.rb +12 -18
- data/lib/irb/startup_message.rb +83 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb/workspace.rb +9 -0
- data/lib/irb.rb +75 -33
- metadata +26 -7
- data/Rakefile +0 -52
- data/bin/console +0 -6
- data/bin/setup +0 -6
- data/irb.gemspec +0 -46
data/lib/irb/completion.rb
CHANGED
|
@@ -8,30 +8,33 @@
|
|
|
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
|
|
14
37
|
# 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
38
|
|
|
36
39
|
HELP_COMMAND_PREPOSING = /\Ahelp\s+/
|
|
37
40
|
|
|
@@ -60,13 +63,13 @@ module IRB
|
|
|
60
63
|
|
|
61
64
|
def retrieve_gem_and_system_load_path
|
|
62
65
|
candidates = (GEM_PATHS | $LOAD_PATH)
|
|
63
|
-
candidates.
|
|
66
|
+
candidates.filter_map do |p|
|
|
64
67
|
if p.respond_to?(:to_path)
|
|
65
68
|
p.to_path
|
|
66
69
|
else
|
|
67
70
|
String(p) rescue nil
|
|
68
71
|
end
|
|
69
|
-
end.
|
|
72
|
+
end.sort
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
def retrieve_files_to_require_from_load_path
|
|
@@ -96,6 +99,12 @@ module IRB
|
|
|
96
99
|
end
|
|
97
100
|
end
|
|
98
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
|
+
|
|
99
108
|
def retrieve_files_to_require_relative_from_current_dir
|
|
100
109
|
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
|
|
101
110
|
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
|
@@ -127,12 +136,21 @@ module IRB
|
|
|
127
136
|
|
|
128
137
|
return commands unless result
|
|
129
138
|
|
|
130
|
-
|
|
139
|
+
encoded_candidates = result.completion_candidates.filter_map do |i|
|
|
140
|
+
encoded = i.encode(Encoding.default_external)
|
|
141
|
+
target + encoded
|
|
142
|
+
rescue Encoding::UndefinedConversionError
|
|
143
|
+
# If the string cannot be converted, we just ignore it
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
commands | encoded_candidates
|
|
131
147
|
end
|
|
132
148
|
|
|
133
149
|
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
136
154
|
end
|
|
137
155
|
end
|
|
138
156
|
|
|
@@ -179,28 +197,15 @@ module IRB
|
|
|
179
197
|
else
|
|
180
198
|
return nil # It's not String literal
|
|
181
199
|
end
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
break
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
|
|
191
|
-
|
|
192
|
-
case tok.tok
|
|
193
|
-
when 'require'
|
|
194
|
-
retrieve_files_to_require_from_load_path.select { |path|
|
|
195
|
-
path.start_with?(actual_target)
|
|
196
|
-
}.map { |path|
|
|
197
|
-
quote + path
|
|
200
|
+
|
|
201
|
+
case preposing
|
|
202
|
+
when /(^|[^\w])require\(? *\z/
|
|
203
|
+
retrieve_files_to_require_from_load_path.filter_map { |path|
|
|
204
|
+
quote + path if path.start_with?(actual_target)
|
|
198
205
|
}
|
|
199
|
-
when
|
|
200
|
-
retrieve_files_to_require_relative_from_current_dir.
|
|
201
|
-
path.start_with?(actual_target)
|
|
202
|
-
}.map { |path|
|
|
203
|
-
quote + path
|
|
206
|
+
when /(^|[^\w])require_relative\(? *\z/
|
|
207
|
+
retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
|
|
208
|
+
quote + path if path.start_with?(actual_target)
|
|
204
209
|
}
|
|
205
210
|
end
|
|
206
211
|
end
|
|
@@ -218,12 +223,17 @@ module IRB
|
|
|
218
223
|
# It doesn't make sense to propose commands with other preposing
|
|
219
224
|
commands = [] unless preposing.empty?
|
|
220
225
|
|
|
221
|
-
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.
|
|
226
|
+
completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.filter_map do |i|
|
|
227
|
+
i.encode(Encoding.default_external)
|
|
228
|
+
rescue Encoding::UndefinedConversionError
|
|
229
|
+
# If the string cannot be converted, we just ignore it
|
|
230
|
+
nil
|
|
231
|
+
end
|
|
222
232
|
commands | completion_data
|
|
223
233
|
end
|
|
224
234
|
|
|
225
|
-
def doc_namespace(
|
|
226
|
-
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)
|
|
227
237
|
end
|
|
228
238
|
|
|
229
239
|
def retrieve_completion_data(input, bind:, doc_namespace:)
|
|
@@ -287,7 +297,7 @@ module IRB
|
|
|
287
297
|
nil
|
|
288
298
|
else
|
|
289
299
|
sym = $1
|
|
290
|
-
candidates = Symbol.all_symbols.
|
|
300
|
+
candidates = Symbol.all_symbols.filter_map do |s|
|
|
291
301
|
s.inspect
|
|
292
302
|
rescue EncodingError
|
|
293
303
|
# ignore
|
|
@@ -459,12 +469,19 @@ module IRB
|
|
|
459
469
|
eval("#{perfect_match_var}.class.name", bind) rescue nil
|
|
460
470
|
else
|
|
461
471
|
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 |=
|
|
472
|
+
candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
|
|
463
473
|
candidates.find{ |i| i == input }
|
|
464
474
|
end
|
|
465
475
|
else
|
|
466
476
|
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 |=
|
|
477
|
+
candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
|
|
478
|
+
|
|
479
|
+
target_encoding = Encoding.default_external
|
|
480
|
+
candidates = candidates.compact.filter_map do |candidate|
|
|
481
|
+
candidate.encoding == target_encoding ? candidate : candidate.encode(target_encoding)
|
|
482
|
+
rescue EncodingError
|
|
483
|
+
nil
|
|
484
|
+
end
|
|
468
485
|
candidates.grep(/^#{Regexp.quote(input)}/).sort
|
|
469
486
|
end
|
|
470
487
|
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
|
data/lib/irb/ext/multi-irb.rb
CHANGED
|
@@ -65,6 +65,7 @@ module IRB
|
|
|
65
65
|
@current_job = irb
|
|
66
66
|
th.run
|
|
67
67
|
Thread.stop
|
|
68
|
+
Thread.pass
|
|
68
69
|
@current_job = irb(Thread.current)
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -220,6 +221,7 @@ module IRB
|
|
|
220
221
|
end
|
|
221
222
|
end
|
|
222
223
|
Thread.stop
|
|
224
|
+
Thread.pass
|
|
223
225
|
@JobManager.current_job = @JobManager.irb(Thread.current)
|
|
224
226
|
end
|
|
225
227
|
|
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
|
@@ -26,10 +26,11 @@ module IRB
|
|
|
26
26
|
|
|
27
27
|
def winsize
|
|
28
28
|
if instance_variable_defined?(:@stdout) && @stdout.tty?
|
|
29
|
-
@stdout.winsize
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
winsize = @stdout.winsize
|
|
30
|
+
# If width or height is 0, something is wrong.
|
|
31
|
+
return winsize unless winsize.include? 0
|
|
32
32
|
end
|
|
33
|
+
[24, 80]
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
# Whether this input method is still readable when there is no more data to
|
|
@@ -175,10 +176,15 @@ module IRB
|
|
|
175
176
|
class ReadlineInputMethod < StdioInputMethod
|
|
176
177
|
class << self
|
|
177
178
|
def initialize_readline
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
return if defined?(self::Readline)
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
require 'readline'
|
|
183
|
+
const_set(:Readline, ::Readline)
|
|
184
|
+
rescue LoadError
|
|
185
|
+
const_set(:Readline, ::Reline)
|
|
186
|
+
end
|
|
187
|
+
const_set(:HISTORY, self::Readline::HISTORY)
|
|
182
188
|
end
|
|
183
189
|
end
|
|
184
190
|
|
|
@@ -216,8 +222,8 @@ module IRB
|
|
|
216
222
|
def gets
|
|
217
223
|
Readline.input = @stdin
|
|
218
224
|
Readline.output = @stdout
|
|
219
|
-
if l = readline(@prompt, false)
|
|
220
|
-
HISTORY.push(l) if !l.empty?
|
|
225
|
+
if l = Readline.readline(@prompt, false)
|
|
226
|
+
Readline::HISTORY.push(l) if !l.empty? && l != Readline::HISTORY.to_a.last
|
|
221
227
|
@line[@line_no += 1] = l + "\n"
|
|
222
228
|
else
|
|
223
229
|
@eof = true
|
|
@@ -239,7 +245,7 @@ module IRB
|
|
|
239
245
|
|
|
240
246
|
# For debug message
|
|
241
247
|
def inspect
|
|
242
|
-
readline_impl =
|
|
248
|
+
readline_impl = Readline == ::Reline ? 'Reline' : 'ext/readline'
|
|
243
249
|
str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
|
|
244
250
|
inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
|
|
245
251
|
str += " and #{inputrc_path}" if File.exist?(inputrc_path)
|
|
@@ -249,6 +255,16 @@ module IRB
|
|
|
249
255
|
|
|
250
256
|
class RelineInputMethod < StdioInputMethod
|
|
251
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
|
|
252
268
|
include HistorySavingAbility
|
|
253
269
|
# Creates a new input method object using Reline
|
|
254
270
|
def initialize(completor)
|
|
@@ -299,9 +315,17 @@ module IRB
|
|
|
299
315
|
@auto_indent_proc = block
|
|
300
316
|
end
|
|
301
317
|
|
|
302
|
-
def
|
|
318
|
+
def retrieve_document_target(matched)
|
|
303
319
|
preposing, _target, postposing, bind = @completion_params
|
|
304
|
-
@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
|
|
305
329
|
end
|
|
306
330
|
|
|
307
331
|
def rdoc_ri_driver
|
|
@@ -322,146 +346,158 @@ module IRB
|
|
|
322
346
|
input_method = self # self is changed in the lambda below.
|
|
323
347
|
->() {
|
|
324
348
|
dialog.trap_key = nil
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
# When option/alt is not configured as a meta key in terminal emulator,
|
|
328
|
-
# option/alt + d will send a unicode character depend on OS keyboard setting.
|
|
329
|
-
[195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
|
|
330
|
-
[226, 136, 130] # "∂" Alt+d on Mac keyboard.
|
|
331
|
-
]
|
|
332
|
-
|
|
333
|
-
if just_cursor_moving and completion_journey_data.nil?
|
|
349
|
+
|
|
350
|
+
if just_cursor_moving && completion_journey_data.nil?
|
|
334
351
|
return nil
|
|
335
352
|
end
|
|
336
353
|
cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
|
|
337
|
-
return nil if result.nil?
|
|
354
|
+
return nil if result.nil? || pointer.nil? || pointer < 0
|
|
338
355
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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)
|
|
342
359
|
|
|
343
|
-
|
|
360
|
+
x, width = input_method.dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
|
|
361
|
+
return nil unless x
|
|
344
362
|
|
|
345
|
-
|
|
363
|
+
dialog.trap_key = ALT_D_SEQUENCES
|
|
346
364
|
|
|
347
365
|
if key.match?(dialog.name)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
# so we need to turn on and off alternate screen manually.
|
|
354
|
-
begin
|
|
355
|
-
print "\e[?1049h"
|
|
356
|
-
driver.display_names([name])
|
|
357
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
358
|
-
ensure
|
|
359
|
-
print "\e[?1049l"
|
|
360
|
-
end
|
|
366
|
+
begin
|
|
367
|
+
print "\e[?1049h"
|
|
368
|
+
input_method.display_document(matched_text)
|
|
369
|
+
ensure
|
|
370
|
+
print "\e[?1049l"
|
|
361
371
|
end
|
|
362
372
|
end
|
|
363
373
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
used_for_class = false
|
|
373
|
-
if not name =~ /#|\./
|
|
374
|
-
found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
|
|
375
|
-
if not found.empty?
|
|
376
|
-
doc = driver.class_document(name, found, klasses, includes, extends)
|
|
377
|
-
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
|
|
378
382
|
end
|
|
379
383
|
end
|
|
380
|
-
unless
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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)
|
|
389
424
|
end
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
|
408
443
|
width = right_width
|
|
409
444
|
x = right_x
|
|
410
|
-
|
|
445
|
+
else
|
|
411
446
|
width = left_width
|
|
412
447
|
x = left_x
|
|
413
|
-
else # Both are negative width.
|
|
414
|
-
return nil
|
|
415
448
|
end
|
|
416
|
-
|
|
449
|
+
elsif right_width.positive? && left_width <= 0
|
|
450
|
+
width = right_width
|
|
417
451
|
x = right_x
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
dialog.trap_key = alt_d
|
|
422
|
-
mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
|
|
423
|
-
if show_easter_egg
|
|
424
|
-
type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
|
|
425
|
-
contents = IRB.send(:easter_egg_logo, type).split("\n")
|
|
426
|
-
message = "Press #{mod_key}+d to see more"
|
|
427
|
-
contents[0][0, message.size] = message
|
|
452
|
+
elsif right_width <= 0 && left_width.positive?
|
|
453
|
+
width = left_width
|
|
454
|
+
x = left_x
|
|
428
455
|
else
|
|
429
|
-
|
|
430
|
-
contents = [message] + doc.accept(formatter).split("\n")
|
|
456
|
+
return nil
|
|
431
457
|
end
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
}
|
|
458
|
+
else
|
|
459
|
+
x = right_x
|
|
460
|
+
end
|
|
461
|
+
[x, width]
|
|
437
462
|
end
|
|
438
463
|
|
|
439
464
|
def display_document(matched)
|
|
440
|
-
|
|
441
|
-
return unless
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
|
447
480
|
|
|
448
|
-
|
|
449
|
-
|
|
481
|
+
if matched =~ /\A(?:::)?RubyVM/ && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
482
|
+
IRB.__send__(:easter_egg)
|
|
483
|
+
return
|
|
484
|
+
end
|
|
450
485
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
|
454
496
|
begin
|
|
455
|
-
driver.
|
|
497
|
+
driver.display_names([target.name])
|
|
456
498
|
rescue RDoc::RI::Driver::NotFoundError
|
|
457
499
|
end
|
|
458
500
|
end
|
|
459
|
-
driver.display(out)
|
|
460
|
-
else
|
|
461
|
-
begin
|
|
462
|
-
driver.display_names([namespace])
|
|
463
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
464
|
-
end
|
|
465
501
|
end
|
|
466
502
|
end
|
|
467
503
|
|
|
@@ -474,7 +510,7 @@ module IRB
|
|
|
474
510
|
Reline.prompt_proc = @prompt_proc
|
|
475
511
|
Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
|
|
476
512
|
if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
|
|
477
|
-
Reline::HISTORY.push(l) if !l.empty?
|
|
513
|
+
Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
|
|
478
514
|
@line[@line_no += 1] = l + "\n"
|
|
479
515
|
else
|
|
480
516
|
@eof = true
|