зеркало из https://github.com/github/ruby.git
[ruby/irb] Group class methods under `class << self`
(https://github.com/ruby/irb/pull/981) https://github.com/ruby/irb/commit/cdaa356df2
This commit is contained in:
Родитель
4fe3082b63
Коммит
4a4e1bf357
64
lib/irb.rb
64
lib/irb.rb
|
@ -880,40 +880,42 @@ module IRB
|
||||||
# An exception raised by IRB.irb_abort
|
# An exception raised by IRB.irb_abort
|
||||||
class Abort < Exception;end
|
class Abort < Exception;end
|
||||||
|
|
||||||
# The current IRB::Context of the session, see IRB.conf
|
class << self
|
||||||
#
|
# The current IRB::Context of the session, see IRB.conf
|
||||||
# irb
|
#
|
||||||
# irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
|
# irb
|
||||||
# foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
|
# irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
|
||||||
def IRB.CurrentContext # :nodoc:
|
# foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
|
||||||
IRB.conf[:MAIN_CONTEXT]
|
def CurrentContext # :nodoc:
|
||||||
end
|
conf[:MAIN_CONTEXT]
|
||||||
|
|
||||||
# Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING`
|
|
||||||
def IRB.start(ap_path = nil)
|
|
||||||
STDOUT.sync = true
|
|
||||||
$0 = File::basename(ap_path, ".rb") if ap_path
|
|
||||||
|
|
||||||
IRB.setup(ap_path)
|
|
||||||
|
|
||||||
if @CONF[:SCRIPT]
|
|
||||||
irb = Irb.new(nil, @CONF[:SCRIPT])
|
|
||||||
else
|
|
||||||
irb = Irb.new
|
|
||||||
end
|
end
|
||||||
irb.run(@CONF)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Quits irb
|
# Initializes IRB and creates a new Irb.irb object at the `TOPLEVEL_BINDING`
|
||||||
def IRB.irb_exit(*) # :nodoc:
|
def start(ap_path = nil)
|
||||||
throw :IRB_EXIT, false
|
STDOUT.sync = true
|
||||||
end
|
$0 = File::basename(ap_path, ".rb") if ap_path
|
||||||
|
|
||||||
# Aborts then interrupts irb.
|
setup(ap_path)
|
||||||
#
|
|
||||||
# Will raise an Abort exception, or the given `exception`.
|
if @CONF[:SCRIPT]
|
||||||
def IRB.irb_abort(irb, exception = Abort) # :nodoc:
|
irb = Irb.new(nil, @CONF[:SCRIPT])
|
||||||
irb.context.thread.raise exception, "abort then interrupt!"
|
else
|
||||||
|
irb = Irb.new
|
||||||
|
end
|
||||||
|
irb.run(@CONF)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Quits irb
|
||||||
|
def irb_exit(*) # :nodoc:
|
||||||
|
throw :IRB_EXIT, false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Aborts then interrupts irb.
|
||||||
|
#
|
||||||
|
# Will raise an Abort exception, or the given `exception`.
|
||||||
|
def irb_abort(irb, exception = Abort) # :nodoc:
|
||||||
|
irb.context.thread.raise exception, "abort then interrupt!"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Irb
|
class Irb
|
||||||
|
|
|
@ -10,8 +10,10 @@ module IRB
|
||||||
module Command
|
module Command
|
||||||
class CommandArgumentError < StandardError; end
|
class CommandArgumentError < StandardError; end
|
||||||
|
|
||||||
def self.extract_ruby_args(*args, **kwargs)
|
class << self
|
||||||
throw :EXTRACT_RUBY_ARGS, [args, kwargs]
|
def extract_ruby_args(*args, **kwargs)
|
||||||
|
throw :EXTRACT_RUBY_ARGS, [args, kwargs]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Base
|
class Base
|
||||||
|
@ -31,6 +33,12 @@ module IRB
|
||||||
@help_message
|
@help_message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def execute(irb_context, arg)
|
||||||
|
new(irb_context).execute(arg)
|
||||||
|
rescue CommandArgumentError => e
|
||||||
|
puts e.message
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def highlight(text)
|
def highlight(text)
|
||||||
|
@ -38,12 +46,6 @@ module IRB
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.execute(irb_context, arg)
|
|
||||||
new(irb_context).execute(arg)
|
|
||||||
rescue CommandArgumentError => e
|
|
||||||
puts e.message
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(irb_context)
|
def initialize(irb_context)
|
||||||
@irb_context = irb_context
|
@irb_context = irb_context
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,13 +58,15 @@ module IRB
|
||||||
end
|
end
|
||||||
|
|
||||||
class DebugCommand < Debug
|
class DebugCommand < Debug
|
||||||
def self.category
|
class << self
|
||||||
"Debugging"
|
def category
|
||||||
end
|
"Debugging"
|
||||||
|
end
|
||||||
|
|
||||||
def self.description
|
def description
|
||||||
command_name = self.name.split("::").last.downcase
|
command_name = self.name.split("::").last.downcase
|
||||||
"Start the debugger of debug.gem and run its `#{command_name}` command."
|
"Start the debugger of debug.gem and run its `#{command_name}` command."
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -259,10 +259,12 @@ module IRB
|
||||||
# Deprecated. Doesn't have any effect.
|
# Deprecated. Doesn't have any effect.
|
||||||
@EXTEND_COMMANDS = []
|
@EXTEND_COMMANDS = []
|
||||||
|
|
||||||
# Drepcated. Use Command.regiser instead.
|
class << self
|
||||||
def self.def_extend_command(cmd_name, cmd_class, _, *aliases)
|
# Drepcated. Use Command.regiser instead.
|
||||||
Command._register_with_aliases(cmd_name, cmd_class, *aliases)
|
def def_extend_command(cmd_name, cmd_class, _, *aliases)
|
||||||
Command.class_variable_set(:@@command_override_policies, nil)
|
Command._register_with_aliases(cmd_name, cmd_class, *aliases)
|
||||||
|
Command.class_variable_set(:@@command_override_policies, nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,11 +171,13 @@ module IRB
|
||||||
end
|
end
|
||||||
|
|
||||||
class ReadlineInputMethod < StdioInputMethod
|
class ReadlineInputMethod < StdioInputMethod
|
||||||
def self.initialize_readline
|
class << self
|
||||||
require "readline"
|
def initialize_readline
|
||||||
rescue LoadError
|
require "readline"
|
||||||
else
|
rescue LoadError
|
||||||
include ::Readline
|
else
|
||||||
|
include ::Readline
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
include HistorySavingAbility
|
include HistorySavingAbility
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
module IRB # :nodoc:
|
module IRB # :nodoc:
|
||||||
|
|
||||||
|
|
||||||
# Convenience method to create a new Inspector, using the given +inspect+
|
# Convenience method to create a new Inspector, using the given +inspect+
|
||||||
# proc, and optional +init+ proc and passes them to Inspector.new
|
# proc, and optional +init+ proc and passes them to Inspector.new
|
||||||
#
|
#
|
||||||
|
@ -43,38 +42,40 @@ module IRB # :nodoc:
|
||||||
# +:marshal+:: Using Marshal.dump
|
# +:marshal+:: Using Marshal.dump
|
||||||
INSPECTORS = {}
|
INSPECTORS = {}
|
||||||
|
|
||||||
# Determines the inspector to use where +inspector+ is one of the keys passed
|
class << self
|
||||||
# during inspector definition.
|
# Determines the inspector to use where +inspector+ is one of the keys passed
|
||||||
def self.keys_with_inspector(inspector)
|
# during inspector definition.
|
||||||
INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
|
def keys_with_inspector(inspector)
|
||||||
end
|
INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k}
|
||||||
|
|
||||||
# Example
|
|
||||||
#
|
|
||||||
# Inspector.def_inspector(key, init_p=nil){|v| v.inspect}
|
|
||||||
# Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect}
|
|
||||||
# Inspector.def_inspector(key, inspector)
|
|
||||||
# Inspector.def_inspector([key1,...], inspector)
|
|
||||||
def self.def_inspector(key, arg=nil, &block)
|
|
||||||
if block_given?
|
|
||||||
inspector = IRB::Inspector(block, arg)
|
|
||||||
else
|
|
||||||
inspector = arg
|
|
||||||
end
|
end
|
||||||
|
|
||||||
case key
|
# Example
|
||||||
when Array
|
#
|
||||||
for k in key
|
# Inspector.def_inspector(key, init_p=nil){|v| v.inspect}
|
||||||
def_inspector(k, inspector)
|
# Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect}
|
||||||
|
# Inspector.def_inspector(key, inspector)
|
||||||
|
# Inspector.def_inspector([key1,...], inspector)
|
||||||
|
def def_inspector(key, arg=nil, &block)
|
||||||
|
if block_given?
|
||||||
|
inspector = IRB::Inspector(block, arg)
|
||||||
|
else
|
||||||
|
inspector = arg
|
||||||
|
end
|
||||||
|
|
||||||
|
case key
|
||||||
|
when Array
|
||||||
|
for k in key
|
||||||
|
def_inspector(k, inspector)
|
||||||
|
end
|
||||||
|
when Symbol
|
||||||
|
INSPECTORS[key] = inspector
|
||||||
|
INSPECTORS[key.to_s] = inspector
|
||||||
|
when String
|
||||||
|
INSPECTORS[key] = inspector
|
||||||
|
INSPECTORS[key.intern] = inspector
|
||||||
|
else
|
||||||
|
INSPECTORS[key] = inspector
|
||||||
end
|
end
|
||||||
when Symbol
|
|
||||||
INSPECTORS[key] = inspector
|
|
||||||
INSPECTORS[key.to_s] = inspector
|
|
||||||
when String
|
|
||||||
INSPECTORS[key] = inspector
|
|
||||||
INSPECTORS[key.intern] = inspector
|
|
||||||
else
|
|
||||||
INSPECTORS[key] = inspector
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,235 +3,237 @@ module IRB
|
||||||
module NestingParser
|
module NestingParser
|
||||||
IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
|
IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
|
||||||
|
|
||||||
# Scan each token and call the given block with array of token and other information for parsing
|
class << self
|
||||||
def self.scan_opens(tokens)
|
# Scan each token and call the given block with array of token and other information for parsing
|
||||||
opens = []
|
def scan_opens(tokens)
|
||||||
pending_heredocs = []
|
opens = []
|
||||||
first_token_on_line = true
|
pending_heredocs = []
|
||||||
tokens.each do |t|
|
first_token_on_line = true
|
||||||
skip = false
|
tokens.each do |t|
|
||||||
last_tok, state, args = opens.last
|
skip = false
|
||||||
case state
|
last_tok, state, args = opens.last
|
||||||
when :in_alias_undef
|
case state
|
||||||
skip = t.event == :on_kw
|
when :in_alias_undef
|
||||||
when :in_unquoted_symbol
|
skip = t.event == :on_kw
|
||||||
unless IGNORE_TOKENS.include?(t.event)
|
when :in_unquoted_symbol
|
||||||
opens.pop
|
unless IGNORE_TOKENS.include?(t.event)
|
||||||
skip = true
|
opens.pop
|
||||||
end
|
skip = true
|
||||||
when :in_lambda_head
|
end
|
||||||
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
when :in_lambda_head
|
||||||
when :in_method_head
|
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
||||||
unless IGNORE_TOKENS.include?(t.event)
|
when :in_method_head
|
||||||
next_args = []
|
unless IGNORE_TOKENS.include?(t.event)
|
||||||
body = nil
|
next_args = []
|
||||||
if args.include?(:receiver)
|
body = nil
|
||||||
case t.event
|
if args.include?(:receiver)
|
||||||
when :on_lparen, :on_ivar, :on_gvar, :on_cvar
|
case t.event
|
||||||
# def (receiver). | def @ivar. | def $gvar. | def @@cvar.
|
when :on_lparen, :on_ivar, :on_gvar, :on_cvar
|
||||||
next_args << :dot
|
# def (receiver). | def @ivar. | def $gvar. | def @@cvar.
|
||||||
when :on_kw
|
next_args << :dot
|
||||||
case t.tok
|
when :on_kw
|
||||||
when 'self', 'true', 'false', 'nil'
|
case t.tok
|
||||||
# def self(arg) | def self.
|
when 'self', 'true', 'false', 'nil'
|
||||||
next_args.push(:arg, :dot)
|
# def self(arg) | def self.
|
||||||
else
|
next_args.push(:arg, :dot)
|
||||||
# def if(arg)
|
else
|
||||||
|
# def if(arg)
|
||||||
|
skip = true
|
||||||
|
next_args << :arg
|
||||||
|
end
|
||||||
|
when :on_op, :on_backtick
|
||||||
|
# def +(arg)
|
||||||
skip = true
|
skip = true
|
||||||
next_args << :arg
|
next_args << :arg
|
||||||
|
when :on_ident, :on_const
|
||||||
|
# def a(arg) | def a.
|
||||||
|
next_args.push(:arg, :dot)
|
||||||
end
|
end
|
||||||
when :on_op, :on_backtick
|
|
||||||
# def +(arg)
|
|
||||||
skip = true
|
|
||||||
next_args << :arg
|
|
||||||
when :on_ident, :on_const
|
|
||||||
# def a(arg) | def a.
|
|
||||||
next_args.push(:arg, :dot)
|
|
||||||
end
|
end
|
||||||
end
|
if args.include?(:dot)
|
||||||
if args.include?(:dot)
|
# def receiver.name
|
||||||
# def receiver.name
|
next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
|
||||||
next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
|
|
||||||
end
|
|
||||||
if args.include?(:name)
|
|
||||||
if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
|
|
||||||
# def name(arg) | def receiver.name(arg)
|
|
||||||
next_args << :arg
|
|
||||||
skip = true
|
|
||||||
end
|
end
|
||||||
end
|
if args.include?(:name)
|
||||||
if args.include?(:arg)
|
if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
|
||||||
case t.event
|
# def name(arg) | def receiver.name(arg)
|
||||||
when :on_nl, :on_semicolon
|
next_args << :arg
|
||||||
# def receiver.f;
|
skip = true
|
||||||
body = :normal
|
end
|
||||||
when :on_lparen
|
end
|
||||||
# def receiver.f()
|
if args.include?(:arg)
|
||||||
next_args << :eq
|
case t.event
|
||||||
else
|
when :on_nl, :on_semicolon
|
||||||
|
# def receiver.f;
|
||||||
|
body = :normal
|
||||||
|
when :on_lparen
|
||||||
|
# def receiver.f()
|
||||||
|
next_args << :eq
|
||||||
|
else
|
||||||
|
if t.event == :on_op && t.tok == '='
|
||||||
|
# def receiver.f =
|
||||||
|
body = :oneliner
|
||||||
|
else
|
||||||
|
# def receiver.f arg
|
||||||
|
next_args << :arg_without_paren
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if args.include?(:eq)
|
||||||
if t.event == :on_op && t.tok == '='
|
if t.event == :on_op && t.tok == '='
|
||||||
# def receiver.f =
|
|
||||||
body = :oneliner
|
body = :oneliner
|
||||||
else
|
else
|
||||||
# def receiver.f arg
|
body = :normal
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if args.include?(:arg_without_paren)
|
||||||
|
if %i[on_semicolon on_nl].include?(t.event)
|
||||||
|
# def f a;
|
||||||
|
body = :normal
|
||||||
|
else
|
||||||
|
# def f a, b
|
||||||
next_args << :arg_without_paren
|
next_args << :arg_without_paren
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
if body == :oneliner
|
||||||
if args.include?(:eq)
|
opens.pop
|
||||||
if t.event == :on_op && t.tok == '='
|
elsif body
|
||||||
body = :oneliner
|
opens[-1] = [last_tok, nil]
|
||||||
else
|
else
|
||||||
body = :normal
|
opens[-1] = [last_tok, :in_method_head, next_args]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if args.include?(:arg_without_paren)
|
when :in_for_while_until_condition
|
||||||
if %i[on_semicolon on_nl].include?(t.event)
|
if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
|
||||||
# def f a;
|
skip = true if t.event == :on_kw && t.tok == 'do'
|
||||||
body = :normal
|
|
||||||
else
|
|
||||||
# def f a, b
|
|
||||||
next_args << :arg_without_paren
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if body == :oneliner
|
|
||||||
opens.pop
|
|
||||||
elsif body
|
|
||||||
opens[-1] = [last_tok, nil]
|
opens[-1] = [last_tok, nil]
|
||||||
else
|
|
||||||
opens[-1] = [last_tok, :in_method_head, next_args]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
when :in_for_while_until_condition
|
|
||||||
if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
|
|
||||||
skip = true if t.event == :on_kw && t.tok == 'do'
|
|
||||||
opens[-1] = [last_tok, nil]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless skip
|
unless skip
|
||||||
case t.event
|
case t.event
|
||||||
when :on_kw
|
when :on_kw
|
||||||
case t.tok
|
case t.tok
|
||||||
when 'begin', 'class', 'module', 'do', 'case'
|
when 'begin', 'class', 'module', 'do', 'case'
|
||||||
opens << [t, nil]
|
|
||||||
when 'end'
|
|
||||||
opens.pop
|
|
||||||
when 'def'
|
|
||||||
opens << [t, :in_method_head, [:receiver, :name]]
|
|
||||||
when 'if', 'unless'
|
|
||||||
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
|
||||||
opens << [t, nil]
|
opens << [t, nil]
|
||||||
end
|
when 'end'
|
||||||
when 'while', 'until'
|
opens.pop
|
||||||
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
when 'def'
|
||||||
|
opens << [t, :in_method_head, [:receiver, :name]]
|
||||||
|
when 'if', 'unless'
|
||||||
|
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
||||||
|
opens << [t, nil]
|
||||||
|
end
|
||||||
|
when 'while', 'until'
|
||||||
|
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
||||||
|
opens << [t, :in_for_while_until_condition]
|
||||||
|
end
|
||||||
|
when 'ensure', 'rescue'
|
||||||
|
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
||||||
|
opens.pop
|
||||||
|
opens << [t, nil]
|
||||||
|
end
|
||||||
|
when 'alias'
|
||||||
|
opens << [t, :in_alias_undef, 2]
|
||||||
|
when 'undef'
|
||||||
|
opens << [t, :in_alias_undef, 1]
|
||||||
|
when 'elsif', 'else', 'when'
|
||||||
|
opens.pop
|
||||||
|
opens << [t, nil]
|
||||||
|
when 'for'
|
||||||
opens << [t, :in_for_while_until_condition]
|
opens << [t, :in_for_while_until_condition]
|
||||||
|
when 'in'
|
||||||
|
if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
|
||||||
|
opens.pop
|
||||||
|
opens << [t, nil]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
when 'ensure', 'rescue'
|
when :on_tlambda
|
||||||
unless t.state.allbits?(Ripper::EXPR_LABEL)
|
opens << [t, :in_lambda_head]
|
||||||
opens.pop
|
when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
|
||||||
opens << [t, nil]
|
opens << [t, nil]
|
||||||
end
|
when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
|
||||||
when 'alias'
|
|
||||||
opens << [t, :in_alias_undef, 2]
|
|
||||||
when 'undef'
|
|
||||||
opens << [t, :in_alias_undef, 1]
|
|
||||||
when 'elsif', 'else', 'when'
|
|
||||||
opens.pop
|
opens.pop
|
||||||
|
when :on_heredoc_beg
|
||||||
|
pending_heredocs << t
|
||||||
|
when :on_heredoc_end
|
||||||
|
opens.pop
|
||||||
|
when :on_backtick
|
||||||
|
opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG)
|
||||||
|
when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
|
||||||
opens << [t, nil]
|
opens << [t, nil]
|
||||||
when 'for'
|
when :on_tstring_end, :on_regexp_end, :on_label_end
|
||||||
opens << [t, :in_for_while_until_condition]
|
opens.pop
|
||||||
when 'in'
|
when :on_symbeg
|
||||||
if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
|
if t.tok == ':'
|
||||||
opens.pop
|
opens << [t, :in_unquoted_symbol]
|
||||||
|
else
|
||||||
opens << [t, nil]
|
opens << [t, nil]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
when :on_tlambda
|
end
|
||||||
opens << [t, :in_lambda_head]
|
if t.event == :on_nl || t.event == :on_semicolon
|
||||||
when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
|
first_token_on_line = true
|
||||||
opens << [t, nil]
|
elsif t.event != :on_sp
|
||||||
when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
|
first_token_on_line = false
|
||||||
opens.pop
|
end
|
||||||
when :on_heredoc_beg
|
if pending_heredocs.any? && t.tok.include?("\n")
|
||||||
pending_heredocs << t
|
pending_heredocs.reverse_each { |t| opens << [t, nil] }
|
||||||
when :on_heredoc_end
|
pending_heredocs = []
|
||||||
opens.pop
|
end
|
||||||
when :on_backtick
|
if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end
|
||||||
opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG)
|
tok, state, arg = opens.pop
|
||||||
when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
|
opens << [tok, state, arg - 1] if arg >= 1
|
||||||
opens << [t, nil]
|
end
|
||||||
when :on_tstring_end, :on_regexp_end, :on_label_end
|
yield t, opens if block_given?
|
||||||
opens.pop
|
end
|
||||||
when :on_symbeg
|
opens.map(&:first) + pending_heredocs.reverse
|
||||||
if t.tok == ':'
|
end
|
||||||
opens << [t, :in_unquoted_symbol]
|
|
||||||
else
|
def open_tokens(tokens)
|
||||||
opens << [t, nil]
|
# scan_opens without block will return a list of open tokens at last token position
|
||||||
|
scan_opens(tokens)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line.
|
||||||
|
# Example code
|
||||||
|
# ["hello
|
||||||
|
# world"+(
|
||||||
|
# First line
|
||||||
|
# line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]]
|
||||||
|
# prev_opens: []
|
||||||
|
# next_tokens: [lbracket, tstring_beg]
|
||||||
|
# min_depth: 0 (minimum at beginning of line)
|
||||||
|
# Second line
|
||||||
|
# line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']]
|
||||||
|
# prev_opens: [lbracket, tstring_beg]
|
||||||
|
# next_tokens: [lbracket, lparen]
|
||||||
|
# min_depth: 1 (minimum just after tstring_end)
|
||||||
|
def parse_by_line(tokens)
|
||||||
|
line_tokens = []
|
||||||
|
prev_opens = []
|
||||||
|
min_depth = 0
|
||||||
|
output = []
|
||||||
|
last_opens = scan_opens(tokens) do |t, opens|
|
||||||
|
depth = t == opens.last&.first ? opens.size - 1 : opens.size
|
||||||
|
min_depth = depth if depth < min_depth
|
||||||
|
if t.tok.include?("\n")
|
||||||
|
t.tok.each_line do |line|
|
||||||
|
line_tokens << [t, line]
|
||||||
|
next if line[-1] != "\n"
|
||||||
|
next_opens = opens.map(&:first)
|
||||||
|
output << [line_tokens, prev_opens, next_opens, min_depth]
|
||||||
|
prev_opens = next_opens
|
||||||
|
min_depth = prev_opens.size
|
||||||
|
line_tokens = []
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
line_tokens << [t, t.tok]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if t.event == :on_nl || t.event == :on_semicolon
|
output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
|
||||||
first_token_on_line = true
|
output
|
||||||
elsif t.event != :on_sp
|
|
||||||
first_token_on_line = false
|
|
||||||
end
|
|
||||||
if pending_heredocs.any? && t.tok.include?("\n")
|
|
||||||
pending_heredocs.reverse_each { |t| opens << [t, nil] }
|
|
||||||
pending_heredocs = []
|
|
||||||
end
|
|
||||||
if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end
|
|
||||||
tok, state, arg = opens.pop
|
|
||||||
opens << [tok, state, arg - 1] if arg >= 1
|
|
||||||
end
|
|
||||||
yield t, opens if block_given?
|
|
||||||
end
|
end
|
||||||
opens.map(&:first) + pending_heredocs.reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.open_tokens(tokens)
|
|
||||||
# scan_opens without block will return a list of open tokens at last token position
|
|
||||||
scan_opens(tokens)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line.
|
|
||||||
# Example code
|
|
||||||
# ["hello
|
|
||||||
# world"+(
|
|
||||||
# First line
|
|
||||||
# line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]]
|
|
||||||
# prev_opens: []
|
|
||||||
# next_tokens: [lbracket, tstring_beg]
|
|
||||||
# min_depth: 0 (minimum at beginning of line)
|
|
||||||
# Second line
|
|
||||||
# line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']]
|
|
||||||
# prev_opens: [lbracket, tstring_beg]
|
|
||||||
# next_tokens: [lbracket, lparen]
|
|
||||||
# min_depth: 1 (minimum just after tstring_end)
|
|
||||||
def self.parse_by_line(tokens)
|
|
||||||
line_tokens = []
|
|
||||||
prev_opens = []
|
|
||||||
min_depth = 0
|
|
||||||
output = []
|
|
||||||
last_opens = scan_opens(tokens) do |t, opens|
|
|
||||||
depth = t == opens.last&.first ? opens.size - 1 : opens.size
|
|
||||||
min_depth = depth if depth < min_depth
|
|
||||||
if t.tok.include?("\n")
|
|
||||||
t.tok.each_line do |line|
|
|
||||||
line_tokens << [t, line]
|
|
||||||
next if line[-1] != "\n"
|
|
||||||
next_opens = opens.map(&:first)
|
|
||||||
output << [line_tokens, prev_opens, next_opens, min_depth]
|
|
||||||
prev_opens = next_opens
|
|
||||||
min_depth = prev_opens.size
|
|
||||||
line_tokens = []
|
|
||||||
end
|
|
||||||
else
|
|
||||||
line_tokens << [t, t.tok]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,29 +36,6 @@ module IRB
|
||||||
:massign,
|
:massign,
|
||||||
]
|
]
|
||||||
|
|
||||||
class TerminateLineInput < StandardError
|
|
||||||
def initialize
|
|
||||||
super("Terminate Line Input")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.compile_with_errors_suppressed(code, line_no: 1)
|
|
||||||
begin
|
|
||||||
result = yield code, line_no
|
|
||||||
rescue ArgumentError
|
|
||||||
# Ruby can issue an error for the code if there is an
|
|
||||||
# incomplete magic comment for encoding in it. Force an
|
|
||||||
# expression with a new line before the code in this
|
|
||||||
# case to prevent magic comment handling. To make sure
|
|
||||||
# line numbers in the lexed code remain the same,
|
|
||||||
# decrease the line number by one.
|
|
||||||
code = ";\n#{code}"
|
|
||||||
line_no -= 1
|
|
||||||
result = yield code, line_no
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
ERROR_TOKENS = [
|
ERROR_TOKENS = [
|
||||||
:on_parse_error,
|
:on_parse_error,
|
||||||
:compile_error,
|
:compile_error,
|
||||||
|
@ -68,70 +45,102 @@ module IRB
|
||||||
:on_param_error
|
:on_param_error
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.generate_local_variables_assign_code(local_variables)
|
LTYPE_TOKENS = %i[
|
||||||
"#{local_variables.join('=')}=nil;" unless local_variables.empty?
|
on_heredoc_beg on_tstring_beg
|
||||||
|
on_regexp_beg on_symbeg on_backtick
|
||||||
|
on_symbols_beg on_qsymbols_beg
|
||||||
|
on_words_beg on_qwords_beg
|
||||||
|
]
|
||||||
|
|
||||||
|
class TerminateLineInput < StandardError
|
||||||
|
def initialize
|
||||||
|
super("Terminate Line Input")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Some part of the code is not included in Ripper's token.
|
class << self
|
||||||
# Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr.
|
def compile_with_errors_suppressed(code, line_no: 1)
|
||||||
# With interpolated tokens, tokens.map(&:tok).join will be equal to code.
|
begin
|
||||||
def self.interpolate_ripper_ignored_tokens(code, tokens)
|
result = yield code, line_no
|
||||||
line_positions = [0]
|
rescue ArgumentError
|
||||||
code.lines.each do |line|
|
# Ruby can issue an error for the code if there is an
|
||||||
line_positions << line_positions.last + line.bytesize
|
# incomplete magic comment for encoding in it. Force an
|
||||||
|
# expression with a new line before the code in this
|
||||||
|
# case to prevent magic comment handling. To make sure
|
||||||
|
# line numbers in the lexed code remain the same,
|
||||||
|
# decrease the line number by one.
|
||||||
|
code = ";\n#{code}"
|
||||||
|
line_no -= 1
|
||||||
|
result = yield code, line_no
|
||||||
|
end
|
||||||
|
result
|
||||||
end
|
end
|
||||||
prev_byte_pos = 0
|
|
||||||
interpolated = []
|
def generate_local_variables_assign_code(local_variables)
|
||||||
prev_line = 1
|
"#{local_variables.join('=')}=nil;" unless local_variables.empty?
|
||||||
tokens.each do |t|
|
end
|
||||||
line, col = t.pos
|
|
||||||
byte_pos = line_positions[line - 1] + col
|
# Some part of the code is not included in Ripper's token.
|
||||||
if prev_byte_pos < byte_pos
|
# Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr.
|
||||||
tok = code.byteslice(prev_byte_pos...byte_pos)
|
# With interpolated tokens, tokens.map(&:tok).join will be equal to code.
|
||||||
|
def interpolate_ripper_ignored_tokens(code, tokens)
|
||||||
|
line_positions = [0]
|
||||||
|
code.lines.each do |line|
|
||||||
|
line_positions << line_positions.last + line.bytesize
|
||||||
|
end
|
||||||
|
prev_byte_pos = 0
|
||||||
|
interpolated = []
|
||||||
|
prev_line = 1
|
||||||
|
tokens.each do |t|
|
||||||
|
line, col = t.pos
|
||||||
|
byte_pos = line_positions[line - 1] + col
|
||||||
|
if prev_byte_pos < byte_pos
|
||||||
|
tok = code.byteslice(prev_byte_pos...byte_pos)
|
||||||
|
pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
|
||||||
|
interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
|
||||||
|
prev_line += tok.count("\n")
|
||||||
|
end
|
||||||
|
interpolated << t
|
||||||
|
prev_byte_pos = byte_pos + t.tok.bytesize
|
||||||
|
prev_line += t.tok.count("\n")
|
||||||
|
end
|
||||||
|
if prev_byte_pos < code.bytesize
|
||||||
|
tok = code.byteslice(prev_byte_pos..)
|
||||||
pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
|
pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
|
||||||
interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
|
interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
|
||||||
prev_line += tok.count("\n")
|
|
||||||
end
|
end
|
||||||
interpolated << t
|
interpolated
|
||||||
prev_byte_pos = byte_pos + t.tok.bytesize
|
|
||||||
prev_line += t.tok.count("\n")
|
|
||||||
end
|
|
||||||
if prev_byte_pos < code.bytesize
|
|
||||||
tok = code.byteslice(prev_byte_pos..)
|
|
||||||
pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]]
|
|
||||||
interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0)
|
|
||||||
end
|
|
||||||
interpolated
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.ripper_lex_without_warning(code, local_variables: [])
|
|
||||||
verbose, $VERBOSE = $VERBOSE, nil
|
|
||||||
lvars_code = generate_local_variables_assign_code(local_variables)
|
|
||||||
original_code = code
|
|
||||||
if lvars_code
|
|
||||||
code = "#{lvars_code}\n#{code}"
|
|
||||||
line_no = 0
|
|
||||||
else
|
|
||||||
line_no = 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
|
def ripper_lex_without_warning(code, local_variables: [])
|
||||||
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
|
verbose, $VERBOSE = $VERBOSE, nil
|
||||||
tokens = []
|
lvars_code = generate_local_variables_assign_code(local_variables)
|
||||||
lexer.scan.each do |t|
|
original_code = code
|
||||||
next if t.pos.first == 0
|
if lvars_code
|
||||||
prev_tk = tokens.last
|
code = "#{lvars_code}\n#{code}"
|
||||||
position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
|
line_no = 0
|
||||||
if position_overlapped
|
else
|
||||||
tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
|
line_no = 1
|
||||||
else
|
end
|
||||||
tokens << t
|
|
||||||
|
compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no|
|
||||||
|
lexer = Ripper::Lexer.new(inner_code, '-', line_no)
|
||||||
|
tokens = []
|
||||||
|
lexer.scan.each do |t|
|
||||||
|
next if t.pos.first == 0
|
||||||
|
prev_tk = tokens.last
|
||||||
|
position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize
|
||||||
|
if position_overlapped
|
||||||
|
tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event)
|
||||||
|
else
|
||||||
|
tokens << t
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
interpolate_ripper_ignored_tokens(original_code, tokens)
|
||||||
end
|
end
|
||||||
interpolate_ripper_ignored_tokens(original_code, tokens)
|
ensure
|
||||||
|
$VERBOSE = verbose
|
||||||
end
|
end
|
||||||
ensure
|
|
||||||
$VERBOSE = verbose
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_code_state(code, local_variables:)
|
def check_code_state(code, local_variables:)
|
||||||
|
@ -391,13 +400,6 @@ module IRB
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LTYPE_TOKENS = %i[
|
|
||||||
on_heredoc_beg on_tstring_beg
|
|
||||||
on_regexp_beg on_symbeg on_backtick
|
|
||||||
on_symbols_beg on_qsymbols_beg
|
|
||||||
on_words_beg on_qwords_beg
|
|
||||||
]
|
|
||||||
|
|
||||||
def ltype_from_open_tokens(opens)
|
def ltype_from_open_tokens(opens)
|
||||||
start_token = opens.reverse_each.find do |tok|
|
start_token = opens.reverse_each.find do |tok|
|
||||||
LTYPE_TOKENS.include?(tok.event)
|
LTYPE_TOKENS.include?(tok.event)
|
||||||
|
|
|
@ -176,11 +176,13 @@ EOF
|
||||||
end
|
end
|
||||||
|
|
||||||
module HelpersContainer
|
module HelpersContainer
|
||||||
def self.install_helper_methods
|
class << self
|
||||||
HelperMethod.helper_methods.each do |name, helper_method_class|
|
def install_helper_methods
|
||||||
define_method name do |*args, **opts, &block|
|
HelperMethod.helper_methods.each do |name, helper_method_class|
|
||||||
helper_method_class.instance.execute(*args, **opts, &block)
|
define_method name do |*args, **opts, &block|
|
||||||
end unless method_defined?(name)
|
helper_method_class.instance.execute(*args, **opts, &block)
|
||||||
|
end unless method_defined?(name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ module TestIRB
|
||||||
restore_encodings
|
restore_encodings
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_eval_input
|
def test_eval_input
|
||||||
verbose, $VERBOSE = $VERBOSE, nil
|
verbose, $VERBOSE = $VERBOSE, nil
|
||||||
input = TestInputMethod.new([
|
input = TestInputMethod.new([
|
||||||
|
|
|
@ -80,7 +80,6 @@ module TestIRB
|
||||||
assert_equal(nil, workspace.code_around_binding)
|
assert_equal(nil, workspace.code_around_binding)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_toplevel_binding_local_variables
|
def test_toplevel_binding_local_variables
|
||||||
bug17623 = '[ruby-core:102468]'
|
bug17623 = '[ruby-core:102468]'
|
||||||
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
|
bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
|
||||||
|
|
Загрузка…
Ссылка в новой задаче