Class: IRB::Irb
Constant Summary collapse
- ASSIGNMENT_NODE_TYPES =
[ # Local, instance, global, class, constant, instance, and index assignment: # "foo = bar", # "@foo = bar", # "$foo = bar", # "@@foo = bar", # "::Foo = bar", # "a::Foo = bar", # "Foo = bar" # "foo.bar = 1" # "foo[1] = bar" :assign, # Operation assignment: # "foo += bar" # "foo -= bar" # "foo ||= bar" # "foo &&= bar" :opassign, # Multiple assignment: # "foo, bar = 1, 2 :massign, ]
- PROMPT_MAIN_TRUNCATE_LENGTH =
Note: instance and index assignment expressions could also be written like: “foo.bar=(1)” and “foo.[]=(1, bar)”, when expressed that way, the former be parsed as :assign and echo will be suppressed, but the latter is parsed as a :method_add_arg and the output won’t be suppressed
32
- PROMPT_MAIN_TRUNCATE_OMISSION =
'...'.freeze
- CONTROL_CHARACTERS_PATTERN =
"\x00-\x1F".freeze
- ATTR_TTY =
"\e[%sm"
- ATTR_PLAIN =
""
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the current context of this irb session.
-
#scanner ⇒ Object
The lexer used by this irb session.
Instance Method Summary collapse
- #assignment_expression?(line) ⇒ Boolean
- #convert_invalid_byte_sequence(str, enc) ⇒ Object
-
#debug_break ⇒ Object
A hook point for ‘debug` command’s TracePoint after :IRB_EXIT as well as its clean-up.
- #encode_with_invalid_byte_sequence(str, enc) ⇒ Object
-
#eval_input ⇒ Object
Evaluates input for this session.
- #handle_exception(exc) ⇒ Object
-
#initialize(workspace = nil, input_method = nil) ⇒ Irb
constructor
Creates a new irb session.
-
#inspect ⇒ Object
Outputs the local variables to this current session, including #signal_status and #context, using IRB::Locale.
-
#output_value(omit = false) ⇒ Object
:nodoc:.
-
#prompt(prompt, ltype, indent, line_no) ⇒ Object
:nodoc:.
- #run(conf = IRB.conf) ⇒ Object
-
#signal_handle ⇒ Object
Handler for the signal SIGINT, see Kernel#trap for more information.
-
#signal_status(status) ⇒ Object
Evaluates the given block using the given
status
. -
#suspend_context(context) ⇒ Object
Evaluates the given block using the given
context
as the Context. -
#suspend_input_method(input_method) ⇒ Object
Evaluates the given block using the given
input_method
as the Context#io. -
#suspend_name(path = nil, name = nil) ⇒ Object
Evaluates the given block using the given
path
as the Context#irb_path andname
as the Context#irb_name. -
#suspend_workspace(workspace) ⇒ Object
Evaluates the given block using the given
workspace
as the Context#workspace. -
#truncate_prompt_main(str) ⇒ Object
:nodoc:.
Constructor Details
#initialize(workspace = nil, input_method = nil) ⇒ Irb
Creates a new irb session
471 472 473 474 475 476 |
# File 'lib/irb.rb', line 471 def initialize(workspace = nil, input_method = nil) @context = Context.new(self, workspace, input_method) @context.main.extend ExtendCommandBundle @signal_status = :IN_IRB @scanner = RubyLex.new(@context) end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the current context of this irb session
508 509 510 |
# File 'lib/irb.rb', line 508 def context @context end |
#scanner ⇒ Object
The lexer used by this irb session
510 511 512 |
# File 'lib/irb.rb', line 510 def scanner @scanner end |
Instance Method Details
#assignment_expression?(line) ⇒ Boolean
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
# File 'lib/irb.rb', line 883 def assignment_expression?(line) # Try to parse the line and check if the last of possibly multiple # expressions is an assignment type. # If the expression is invalid, Ripper.sexp should return nil which will # result in false being returned. Any valid expression should return an # s-expression where the second element of the top level array is an # array of parsed expressions. The first element of each expression is the # expression's type. verbose, $VERBOSE = $VERBOSE, nil code = "#{RubyLex.generate_local_variables_assign_code(@context.local_variables) || 'nil;'}\n#{line}" # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part. node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0) ASSIGNMENT_NODE_TYPES.include?(node_type) ensure $VERBOSE = verbose end |
#convert_invalid_byte_sequence(str, enc) ⇒ Object
613 614 615 616 617 618 |
# File 'lib/irb.rb', line 613 def convert_invalid_byte_sequence(str, enc) str.force_encoding(enc) str.scrub { |c| c.bytes.map{ |b| "\\x#{b.to_s(16).upcase}" }.join } end |
#debug_break ⇒ Object
A hook point for ‘debug` command’s TracePoint after :IRB_EXIT as well as its clean-up
479 480 481 482 483 484 485 486 487 |
# File 'lib/irb.rb', line 479 def debug_break # it means the debug command is executed if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb) # after leaving this initial breakpoint, revert the capture_frames patch DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb) # and remove the redundant method DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb) end end |
#encode_with_invalid_byte_sequence(str, enc) ⇒ Object
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 |
# File 'lib/irb.rb', line 620 def encode_with_invalid_byte_sequence(str, enc) conv = Encoding::Converter.new(str.encoding, enc) dst = String.new begin ret = conv.primitive_convert(str, dst) case ret when :invalid_byte_sequence conv.insert_output(conv.primitive_errinfo[3].dump[1..-2]) redo when :undefined_conversion c = conv.primitive_errinfo[3].dup.force_encoding(conv.primitive_errinfo[1]) conv.insert_output(c.dump[1..-2]) redo when :incomplete_input conv.insert_output(conv.primitive_errinfo[3].dump[1..-2]) when :finished end break end while nil dst end |
#eval_input ⇒ Object
Evaluates input for this session.
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/irb.rb', line 513 def eval_input exc = nil @scanner.set_prompt do |ltype, indent, continue, line_no| if ltype f = @context.prompt_s elsif continue f = @context.prompt_c elsif indent > 0 f = @context.prompt_n else f = @context.prompt_i end f = "" unless f if @context.prompting? @context.io.prompt = p = prompt(f, ltype, indent, line_no) else @context.io.prompt = p = "" end if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent) unless ltype prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i ind = prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size + indent * 2 - p.size ind += 2 if continue @context.io.prompt = p + " " * ind if ind > 0 end end @context.io.prompt end @scanner.set_input(@context.io) do signal_status(:IN_INPUT) do if l = @context.io.gets print l if @context.verbose? else if @context.ignore_eof? and @context.io.readable_after_eof? l = "\n" if @context.verbose? printf "Use \"exit\" to leave %s\n", @context.ap_name end else print "\n" if @context.prompting? end end l end end @scanner.set_auto_indent @scanner.each_top_level_statement do |line, line_no| signal_status(:IN_EVAL) do begin line.untaint if RUBY_VERSION < '2.7' if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty? IRB.set_measure_callback end # Assignment expression check should be done before @context.evaluate to handle code like `a /2#/ if false; a = 1` is_assignment = assignment_expression?(line) if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? result = nil last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) } IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) { |chain, item| _name, callback, arg = item proc { callback.(@context, line, line_no, arg, exception: exc) do chain.call end } }.call @context.set_last_value(result) else @context.evaluate(line, line_no, exception: exc) end if @context.echo? if is_assignment if @context.echo_on_assignment? output_value(@context.echo_on_assignment? == :truncate) end else output_value end end rescue Interrupt => exc rescue SystemExit, SignalException raise rescue Exception => exc else exc = nil next end handle_exception(exc) @context.workspace.local_variable_set(:_, exc) exc = nil end end end |
#handle_exception(exc) ⇒ Object
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
# File 'lib/irb.rb', line 642 def handle_exception(exc) if exc.backtrace && exc.backtrace[0] =~ /\/irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ && !(SyntaxError === exc) && !(EncodingError === exc) # The backtrace of invalid encoding hash (ex. {"\xAE": 1}) raises EncodingError without lineno. irb_bug = true else irb_bug = false end if exc.backtrace order = nil if RUBY_VERSION < '3.0.0' if STDOUT.tty? = exc.(order: :bottom) order = :bottom else = exc.(order: :top) order = :top end else # '3.0.0' <= RUBY_VERSION = exc.(order: :top) order = :top end = convert_invalid_byte_sequence(, exc..encoding) = encode_with_invalid_byte_sequence(, IRB.conf[:LC_MESSAGES].encoding) unless .encoding.to_s.casecmp?(IRB.conf[:LC_MESSAGES].encoding.to_s) = .gsub(/((?:^\t.+$\n)+)/) { |m| case order when :top lines = m.split("\n") when :bottom lines = m.split("\n").reverse end unless irb_bug lines = lines.map { |l| @context.workspace.filter_backtrace(l) }.compact if lines.size > @context.back_trace_limit omit = lines.size - @context.back_trace_limit lines = lines[0..(@context.back_trace_limit - 1)] lines << "\t... %d levels..." % omit end end lines = lines.reverse if order == :bottom lines.map{ |l| l + "\n" }.join } # The "<top (required)>" in "(irb)" may be the top level of IRB so imitate the main object. = .gsub(/\(irb\):(?<num>\d+):in `<(?<frame>top \(required\))>'/) { "(irb):#{$~[:num]}:in `<main>'" } puts end print "Maybe IRB bug!\n" if irb_bug end |
#inspect ⇒ Object
Outputs the local variables to this current session, including #signal_status and #context, using IRB::Locale.
868 869 870 871 872 873 874 875 876 877 878 879 880 881 |
# File 'lib/irb.rb', line 868 def inspect ary = [] for iv in instance_variables case (iv = iv.to_s) when "@signal_status" ary.push format("%s=:%s", iv, @signal_status.id2name) when "@context" ary.push format("%s=%s", iv, eval(iv).__to_s__) else ary.push format("%s=%s", iv, eval(iv)) end end format("#<%s: %s>", self.class, ary.join(", ")) end |
#output_value(omit = false) ⇒ Object
:nodoc:
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
# File 'lib/irb.rb', line 830 def output_value(omit = false) # :nodoc: str = @context.inspect_last_value multiline_p = str.include?("\n") if omit winwidth = @context.io.winsize.last if multiline_p first_line = str.split("\n").first result = @context.newline_before_multiline_output? ? (@context.return_format % first_line) : first_line output_width = Reline::Unicode.calculate_width(result, true) diff_size = output_width - Reline::Unicode.calculate_width(first_line, true) if diff_size.positive? and output_width > winwidth lines, _ = Reline::Unicode.split_by_width(first_line, winwidth - diff_size - 3) str = "%s..." % lines.first str += "\e[0m" if Color.colorable? multiline_p = false else str = str.gsub(/(\A.*?\n).*/m, "\\1...") str += "\e[0m" if Color.colorable? end else output_width = Reline::Unicode.calculate_width(@context.return_format % str, true) diff_size = output_width - Reline::Unicode.calculate_width(str, true) if diff_size.positive? and output_width > winwidth lines, _ = Reline::Unicode.split_by_width(str, winwidth - diff_size - 3) str = "%s..." % lines.first str += "\e[0m" if Color.colorable? end end end if multiline_p && @context.newline_before_multiline_output? printf @context.return_format, "\n#{str}" else printf @context.return_format, str end end |
#prompt(prompt, ltype, indent, line_no) ⇒ Object
:nodoc:
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 |
# File 'lib/irb.rb', line 791 def prompt(prompt, ltype, indent, line_no) # :nodoc: p = prompt.dup p.gsub!(/%([0-9]+)?([a-zA-Z])/) do case $2 when "N" @context.irb_name when "m" truncate_prompt_main(@context.main.to_s) when "M" truncate_prompt_main(@context.main.inspect) when "l" ltype when "i" if indent < 0 if $1 "-".rjust($1.to_i) else "-" end else if $1 format("%" + $1 + "d", indent) else indent.to_s end end when "n" if $1 format("%" + $1 + "d", line_no) else line_no.to_s end when "%" "%" end end p end |
#run(conf = IRB.conf) ⇒ Object
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
# File 'lib/irb.rb', line 489 def run(conf = IRB.conf) conf[:IRB_RC].call(context) if conf[:IRB_RC] conf[:MAIN_CONTEXT] = context prev_trap = trap("SIGINT") do signal_handle end begin catch(:IRB_EXIT) do eval_input end ensure trap("SIGINT", prev_trap) conf[:AT_EXIT].each{|hook| hook.call} end end |
#signal_handle ⇒ Object
Handler for the signal SIGINT, see Kernel#trap for more information.
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 |
# File 'lib/irb.rb', line 748 def signal_handle unless @context.ignore_sigint? print "\nabort!\n" if @context.verbose? exit end case @signal_status when :IN_INPUT print "^C\n" raise RubyLex::TerminateLineInput when :IN_EVAL IRB.irb_abort(self) when :IN_LOAD IRB.irb_abort(self, LoadAbort) when :IN_IRB # ignore else # ignore other cases as well end end |
#signal_status(status) ⇒ Object
Evaluates the given block using the given status
.
770 771 772 773 774 775 776 777 778 779 780 |
# File 'lib/irb.rb', line 770 def signal_status(status) return yield if @signal_status == :IN_LOAD signal_status_back = @signal_status @signal_status = status begin yield ensure @signal_status = signal_status_back end end |
#suspend_context(context) ⇒ Object
Evaluates the given block using the given context
as the Context.
738 739 740 741 742 743 744 745 |
# File 'lib/irb.rb', line 738 def suspend_context(context) @context, back_context = context, @context begin yield back_context ensure @context = back_context end end |
#suspend_input_method(input_method) ⇒ Object
Evaluates the given block using the given input_method
as the Context#io.
Used by the irb commands source
and irb_load
, see IRB@IRB+Sessions for more information.
727 728 729 730 731 732 733 734 735 |
# File 'lib/irb.rb', line 727 def suspend_input_method(input_method) back_io = @context.io @context.instance_eval{@io = input_method} begin yield back_io ensure @context.instance_eval{@io = back_io} end end |
#suspend_name(path = nil, name = nil) ⇒ Object
Evaluates the given block using the given path
as the Context#irb_path and name
as the Context#irb_name.
Used by the irb command source
, see IRB@IRB+Sessions for more information.
697 698 699 700 701 702 703 704 705 706 |
# File 'lib/irb.rb', line 697 def suspend_name(path = nil, name = nil) @context.irb_path, back_path = path, @context.irb_path if path @context.irb_name, back_name = name, @context.irb_name if name begin yield back_path, back_name ensure @context.irb_path = back_path if path @context.irb_name = back_name if name end end |
#suspend_workspace(workspace) ⇒ Object
Evaluates the given block using the given workspace
as the Context#workspace.
Used by the irb command irb_load
, see IRB@IRB+Sessions for more information.
713 714 715 716 717 718 719 720 |
# File 'lib/irb.rb', line 713 def suspend_workspace(workspace) @context.workspace, back_workspace = workspace, @context.workspace begin yield back_workspace ensure @context.workspace = back_workspace end end |
#truncate_prompt_main(str) ⇒ Object
:nodoc:
782 783 784 785 786 787 788 789 |
# File 'lib/irb.rb', line 782 def truncate_prompt_main(str) # :nodoc: str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ') if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH str else str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION end end |