mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

* Get rid of inconsistent dll linkages against vcpkg readline * [ruby/irb] Enhance colored inspect outputdffcdb5269
* [ruby/irb] Add color_printer.rb to gemspecb4df0fd8b2
* [ruby/irb] Fix failing tests7723ade899
* irb: add more syntax errors colorizing support (#3967) * [ruby/irb] Do not colorize partially-correct inspect This is to prevent a yellow-mixed output for ActiveSupport::TimeWithZone. Follows up https://github.com/ruby/irb/pull/159 and https://github.com/ruby/ruby/pull/3967.a5804c3560
* [ruby/irb] Remove unnecessary ignore_error in dispatch_seq Just forgotten ina5804c3560
e42e548793
* Increase timeout for reline with --jit-wait for failures like: http://ci.rvm.jp/logfiles/brlog.trunk-mjit-wait.20201229-130509 http://ci.rvm.jp/logfiles/brlog.trunk-mjit-wait.20201229-165132 http://ci.rvm.jp/logfiles/brlog.trunk-mjit-wait.20201228-015519 * [ruby/irb] Stringify when a non-object is passed to PP#text If a nested object is passed to #pp, it may be sometimes passed to the #text method as an object without being stringified. This is fixed on the Ruby main repository;433a3be86a
but it was a bug of Ripper so still needs this workaround for using irb as a gem on Ruby 3.0.0 or earlier. Co-authored-by: k0kubun <takashikkbn@gmail.com>8d13df22ee
* [ruby/irb] Newline in oneliner def doesn't reset indent This closes ruby/irb#132.43456dcf5e
* [ruby/irb] Escape invalid byte sequence in Exception This fixes ruby/irb#141.0815317d42
* [ruby/irb] Handle indentations related to keyword "do" correctly This fixes ruby/irb#158.964643400b
* [ruby/irb] Heredoc may contain multiple newlines in a single token Use the start token as the indentation criteria so that it works properly in heredoc. ref. https://github.com/ruby/reline/pull/2429704808dfd
* [ruby/irb] Use Ripper::Lexer#scan to take broken tokens ref. https://github.com/ruby/reline/pull/24254f90cb6c9
* [ruby/irb] Use error tokens if there are no correct tokens in the same place For example, the broken code "%www" will result in only one error token.9fa39a7cf3
* [ruby/irb] Ensure to restore $VERBOSEcef474a76a
* 600x larger timeout for Reline I didn't notice it's msec. 2.5s is too short. http://ci.rvm.jp/results/trunk-mjit-wait@phosphorus-docker/3311385 * [ruby/irb] fix typo in `IRB::Irb#convert_invalid_byte_sequence`d09d3c3d68
* [ruby/irb] do not escape a predicate method for doc namespace * Fixes #88d431a30af4
* [ruby/irb] refactoring an error handling in `IRB::Inspector` * moved rescue clause to `#inspect_value` to catch all failures in inspectors * test with all (currently five kind of) inspect modes - tweaked the input due to only `Marshal` can inspect(dump) a `BasicObject`9d112fab8e
* [ruby/irb] Use Exception#full_message to show backtrace in the correct order [Bug #17466]1c76845cca
* [ruby/irb] Fix BACK_TRACE_LIMIT logic30dc5d43fe
* irb: Drop lines from backtrace for tests in Ruby repository * [ruby/reline] Update cursor correctly when just cursor moving This fixes ruby/reline#236 and ruby/reline#239.3e3c89d00b
* [ruby/reline] Correct var names in Reline were different from vi-*-mode-string8255fc93b9
* [ruby/reline] Remove debug printd7fbaedc6a
* [ruby/reline] Suppress crashing when auto_indent_proc returns broken indent info Co-authored-by: Juanito Fatas <me@juanitofatas.com>7c24276275
* [ruby/reline] Suppress crashing when dynamic_prompt_proc returns a broken prompt list Co-authored-by: Juanito Fatas <me@juanitofatas.com>558f7be168
* [ruby/reline] Suppress auto indent for adding newlines in pasting Co-authored-by: Juanito Fatas <me@juanitofatas.com>074bb017a7
* [ruby/reline] Add acknowledgments and license for rb-readline19df59b916
* [ruby/irb] Fix comment, irb gem supports 2.5.0 or older36118015ba
* should use `assert_include` here. Random ordering test can introduce antoher candidate so it should be `assert_include`. * [ruby/irb] Add missing require This is useful if you want to use IRB::ColorPrinter as a library like: ``` begin require 'irb/color_printer' IRB::ColorPrinter.pp(obj) rescue LoadError pp(obj) end ```f8461691c7
* [ruby/irb] Make IRB::ColorPrinter.pp compatible with PP.pp The incompatible interface is not helpful, again if you want to use it as a standalone library, falling it back to PP. Original PP.pp also ends with `out << "\n"`.4c74c7d84c
* Suppress constant redefinition warnings * Fix the failing test with XDG_CONFIG_HOME * [ruby/irb] Version 1.3.1c230d08911
* [ruby/reline] Handle ed_search_{prev,next}_history in multiline correctly The current line was being handled incorrectly when displaying the hit history, so it has been fixed to be correct.a3df4343b3
* [ruby/reline] Move the cursor correctly when deleting at eol This fixes ruby/reline#246.07a73ba601
* [ruby/reline] Version 0.2.1a3b3c6ee60
* [ruby/reline] Initialize a variable just in case29b10f6e98
* [ruby/reline] Tests with yamatanooroti don't need chdir Because of chdir, log files ware created in temporary directries on Windows.200b469a68
* [ruby/reline] Windows needs more times to wait rendering53ff2b09c7
* [ruby/reline] Support for change in Windows-specific behavior at eol The behavior of automatically moving the cursor to the next line when displaying a char at the eol on Windows suddenly disappeared.cad4de6ee8
* [ruby/reline] Reline::Windows.erase_after_cursor erases attributes too68b961dfc7
* [ruby/irb] [ruby/irb] [ruby/reline] Version 0.2.2dfb710946f
1a1cdf9628
fe99faf8bd
* [ruby/irb] handle `__ENCODING__` as a keyword as wella6a33d908f
* [ruby/irb] handle repeated exception separatelyfcf6b34bc5
* [ruby/irb] skip a failling test on TruffleRuby * due to the difference of backtrace pointed out by @aycabta5e00a0ae61
* [ruby/irb] Version 1.3.2a7699026cc
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org> Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com> Co-authored-by: Nobuhiro IMAI <nov@yo.rim.or.jp> Co-authored-by: Koichi Sasada <ko1@atdot.net> Co-authored-by: Hiroshi SHIBATA <hsbt@ruby-lang.org>
333 lines
9.3 KiB
Ruby
333 lines
9.3 KiB
Ruby
# frozen_string_literal: false
|
|
#
|
|
# irb/completion.rb -
|
|
# $Release Version: 0.9$
|
|
# $Revision$
|
|
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
|
|
# From Original Idea of shugo@ruby-lang.org
|
|
#
|
|
|
|
autoload :RDoc, "rdoc"
|
|
|
|
module IRB
|
|
module InputCompletor # :nodoc:
|
|
|
|
|
|
# Set of reserved words used by Ruby, you should not use these for
|
|
# constants or variables
|
|
ReservedWords = %w[
|
|
__ENCODING__ __LINE__ __FILE__
|
|
BEGIN END
|
|
alias and
|
|
begin break
|
|
case class
|
|
def defined? do
|
|
else elsif end ensure
|
|
false for
|
|
if in
|
|
module
|
|
next nil not
|
|
or
|
|
redo rescue retry return
|
|
self super
|
|
then true
|
|
undef unless until
|
|
when while
|
|
yield
|
|
]
|
|
|
|
BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
|
|
|
|
CompletionProc = proc { |input|
|
|
retrieve_completion_data(input).compact.map{ |i| i.encode(Encoding.default_external) }
|
|
}
|
|
|
|
def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
|
|
case input
|
|
when /^((["'`]).*\2)\.([^.]*)$/
|
|
# String
|
|
receiver = $1
|
|
message = $3
|
|
|
|
candidates = String.instance_methods.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
"String.#{message}"
|
|
else
|
|
select_message(receiver, message, candidates)
|
|
end
|
|
|
|
when /^(\/[^\/]*\/)\.([^.]*)$/
|
|
# Regexp
|
|
receiver = $1
|
|
message = $2
|
|
|
|
candidates = Regexp.instance_methods.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
"Regexp.#{message}"
|
|
else
|
|
select_message(receiver, message, candidates)
|
|
end
|
|
|
|
when /^([^\]]*\])\.([^.]*)$/
|
|
# Array
|
|
receiver = $1
|
|
message = $2
|
|
|
|
candidates = Array.instance_methods.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
"Array.#{message}"
|
|
else
|
|
select_message(receiver, message, candidates)
|
|
end
|
|
|
|
when /^([^\}]*\})\.([^.]*)$/
|
|
# Proc or Hash
|
|
receiver = $1
|
|
message = $2
|
|
|
|
proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
|
|
hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
["Proc.#{message}", "Hash.#{message}"]
|
|
else
|
|
select_message(receiver, message, proc_candidates | hash_candidates)
|
|
end
|
|
|
|
when /^(:[^:.]*)$/
|
|
# Symbol
|
|
return nil if doc_namespace
|
|
sym = $1
|
|
candidates = Symbol.all_symbols.collect do |s|
|
|
":" + s.id2name.encode(Encoding.default_external)
|
|
rescue Encoding::UndefinedConversionError
|
|
# ignore
|
|
end
|
|
candidates.grep(/^#{Regexp.quote(sym)}/)
|
|
|
|
when /^::([A-Z][^:\.\(]*)$/
|
|
# Absolute Constant or class methods
|
|
receiver = $1
|
|
candidates = Object.constants.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
candidates.find { |i| i == receiver }
|
|
else
|
|
candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
|
|
end
|
|
|
|
when /^([A-Z].*)::([^:.]*)$/
|
|
# Constant or class methods
|
|
receiver = $1
|
|
message = $2
|
|
begin
|
|
candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
|
|
candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
|
|
rescue Exception
|
|
candidates = []
|
|
end
|
|
if doc_namespace
|
|
"#{receiver}::#{message}"
|
|
else
|
|
select_message(receiver, message, candidates, "::")
|
|
end
|
|
|
|
when /^(:[^:.]+)(\.|::)([^.]*)$/
|
|
# Symbol
|
|
receiver = $1
|
|
sep = $2
|
|
message = $3
|
|
|
|
candidates = Symbol.instance_methods.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
"Symbol.#{message}"
|
|
else
|
|
select_message(receiver, message, candidates, sep)
|
|
end
|
|
|
|
when /^(?<num>-?(?:0[dbo])?[0-9_]+(?:\.[0-9_]+)?(?:(?:[eE][+-]?[0-9]+)?i?|r)?)(?<sep>\.|::)(?<mes>[^.]*)$/
|
|
# Numeric
|
|
receiver = $~[:num]
|
|
sep = $~[:sep]
|
|
message = $~[:mes]
|
|
|
|
begin
|
|
instance = eval(receiver, bind)
|
|
if doc_namespace
|
|
"#{instance.class.name}.#{message}"
|
|
else
|
|
candidates = instance.methods.collect{|m| m.to_s}
|
|
select_message(receiver, message, candidates, sep)
|
|
end
|
|
rescue Exception
|
|
if doc_namespace
|
|
nil
|
|
else
|
|
candidates = []
|
|
end
|
|
end
|
|
|
|
when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/
|
|
# Numeric(0xFFFF)
|
|
receiver = $1
|
|
sep = $2
|
|
message = $3
|
|
|
|
begin
|
|
instance = eval(receiver, bind)
|
|
if doc_namespace
|
|
"#{instance.class.name}.#{message}"
|
|
else
|
|
candidates = instance.methods.collect{|m| m.to_s}
|
|
select_message(receiver, message, candidates, sep)
|
|
end
|
|
rescue Exception
|
|
if doc_namespace
|
|
nil
|
|
else
|
|
candidates = []
|
|
end
|
|
end
|
|
|
|
when /^(\$[^.]*)$/
|
|
# global var
|
|
gvar = $1
|
|
all_gvars = global_variables.collect{|m| m.to_s}
|
|
if doc_namespace
|
|
all_gvars.find{ |i| i == gvar }
|
|
else
|
|
all_gvars.grep(Regexp.new(Regexp.quote(gvar)))
|
|
end
|
|
|
|
when /^([^."].*)(\.|::)([^.]*)$/
|
|
# variable.func or func.func
|
|
receiver = $1
|
|
sep = $2
|
|
message = $3
|
|
|
|
gv = eval("global_variables", bind).collect{|m| m.to_s}.push("true", "false", "nil")
|
|
lv = eval("local_variables", bind).collect{|m| m.to_s}
|
|
iv = eval("instance_variables", bind).collect{|m| m.to_s}
|
|
cv = eval("self.class.constants", bind).collect{|m| m.to_s}
|
|
|
|
if (gv | lv | iv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver
|
|
# foo.func and foo is var. OR
|
|
# foo::func and foo is var. OR
|
|
# foo::Const and foo is var. OR
|
|
# Foo::Bar.func
|
|
begin
|
|
candidates = []
|
|
rec = eval(receiver, bind)
|
|
if sep == "::" and rec.kind_of?(Module)
|
|
candidates = rec.constants.collect{|m| m.to_s}
|
|
end
|
|
candidates |= rec.methods.collect{|m| m.to_s}
|
|
rescue Exception
|
|
candidates = []
|
|
end
|
|
else
|
|
# func1.func2
|
|
candidates = []
|
|
to_ignore = ignored_modules
|
|
ObjectSpace.each_object(Module){|m|
|
|
next if (to_ignore.include?(m) rescue true)
|
|
candidates.concat m.instance_methods(false).collect{|x| x.to_s}
|
|
}
|
|
candidates.sort!
|
|
candidates.uniq!
|
|
end
|
|
if doc_namespace
|
|
"#{rec.class.name}#{sep}#{candidates.find{ |i| i == message }}"
|
|
else
|
|
select_message(receiver, message, candidates, sep)
|
|
end
|
|
|
|
when /^\.([^.]*)$/
|
|
# unknown(maybe String)
|
|
|
|
receiver = ""
|
|
message = $1
|
|
|
|
candidates = String.instance_methods(true).collect{|m| m.to_s}
|
|
if doc_namespace
|
|
"String.#{candidates.find{ |i| i == message }}"
|
|
else
|
|
select_message(receiver, message, candidates)
|
|
end
|
|
|
|
else
|
|
candidates = eval("methods | private_methods | local_variables | instance_variables | self.class.constants", bind).collect{|m| m.to_s}
|
|
candidates |= ReservedWords
|
|
|
|
if doc_namespace
|
|
candidates.find{ |i| i == input }
|
|
else
|
|
candidates.grep(/^#{Regexp.quote(input)}/)
|
|
end
|
|
end
|
|
end
|
|
|
|
PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
|
|
RDocRIDriver ||= RDoc::RI::Driver.new
|
|
if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
IRB.__send__(:easter_egg)
|
|
return
|
|
end
|
|
namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
|
return unless namespace
|
|
if namespace.is_a?(Array)
|
|
out = RDoc::Markup::Document.new
|
|
namespace.each do |m|
|
|
begin
|
|
RDocRIDriver.add_method(out, m)
|
|
rescue RDoc::RI::Driver::NotFoundError
|
|
end
|
|
end
|
|
RDocRIDriver.display(out)
|
|
else
|
|
begin
|
|
RDocRIDriver.display_names([namespace])
|
|
rescue RDoc::RI::Driver::NotFoundError
|
|
end
|
|
end
|
|
}
|
|
|
|
# Set of available operators in Ruby
|
|
Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
|
|
|
|
def self.select_message(receiver, message, candidates, sep = ".")
|
|
candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
|
|
case e
|
|
when /^[a-zA-Z_]/
|
|
receiver + sep + e
|
|
when /^[0-9]/
|
|
when *Operators
|
|
#receiver + " " + e
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.ignored_modules
|
|
# We could cache the result, but this is very fast already.
|
|
# By using this approach, we avoid Module#name calls, which are
|
|
# relatively slow when there are a lot of anonymous modules defined.
|
|
s = {}
|
|
|
|
scanner = lambda do |m|
|
|
next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses.
|
|
s[m] = true
|
|
m.constants(false).each do |c|
|
|
value = m.const_get(c)
|
|
scanner.call(value) if value.is_a?(Module)
|
|
end
|
|
end
|
|
|
|
%i(IRB RubyLex).each do |sym|
|
|
next unless Object.const_defined?(sym)
|
|
scanner.call(Object.const_get(sym))
|
|
end
|
|
|
|
s.delete(IRB::Context) if defined?(IRB::Context)
|
|
|
|
s
|
|
end
|
|
end
|
|
end
|