mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Don't echo results of assignment expressions
This commit is contained in:
		
							parent
							
								
									842364792f
								
							
						
					
					
						commit
						1ee88c51b3
					
				
					 5 changed files with 189 additions and 1 deletions
				
			
		
							
								
								
									
										44
									
								
								lib/irb.rb
									
										
									
									
									
								
							
							
						
						
									
										44
									
								
								lib/irb.rb
									
										
									
									
									
								
							|  | @ -10,6 +10,7 @@ | |||
| # | ||||
| # | ||||
| require "e2mmap" | ||||
| require "ripper" | ||||
| 
 | ||||
| require "irb/init" | ||||
| require "irb/context" | ||||
|  | @ -410,6 +411,35 @@ module IRB | |||
|   end | ||||
| 
 | ||||
|   class Irb | ||||
|     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, | ||||
|     ] | ||||
|     # 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 | ||||
| 
 | ||||
|     # Creates a new irb session | ||||
|     def initialize(workspace = nil, input_method = nil, output_method = nil) | ||||
|       @context = Context.new(self, workspace, input_method, output_method) | ||||
|  | @ -498,7 +528,7 @@ module IRB | |||
|           begin | ||||
|             line.untaint | ||||
|             @context.evaluate(line, line_no, exception: exc) | ||||
|             output_value if @context.echo? | ||||
|             output_value if @context.echo? && (@context.echo_on_assignment? || !assignment_expression?(line)) | ||||
|           rescue Interrupt => exc | ||||
|           rescue SystemExit, SignalException | ||||
|             raise | ||||
|  | @ -717,6 +747,18 @@ module IRB | |||
|       format("#<%s: %s>", self.class, ary.join(", ")) | ||||
|     end | ||||
| 
 | ||||
|     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 selement of the top level array is an | ||||
|       # array of parsed expressions. The first element of each expression is the | ||||
|       # expression's type. | ||||
|       ASSIGNMENT_NODE_TYPES.include?(Ripper.sexp(line)&.dig(1,-1,0)) | ||||
|     end | ||||
| 
 | ||||
|     ATTR_TTY = "\e[%sm" | ||||
|     def ATTR_TTY.[](*a) self % a.join(";"); end | ||||
|     ATTR_PLAIN = "" | ||||
|  |  | |||
|  | @ -121,6 +121,11 @@ module IRB | |||
|       if @echo.nil? | ||||
|         @echo = true | ||||
|       end | ||||
| 
 | ||||
|       @echo_on_assignment = IRB.conf[:ECHO_ON_ASSIGNMENT] | ||||
|       if @echo_on_assignment.nil? | ||||
|         @echo_on_assignment = false | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     # The top-level workspace, see WorkSpace#main | ||||
|  | @ -236,6 +241,15 @@ module IRB | |||
|     #     puts "omg" | ||||
|     #     # omg | ||||
|     attr_accessor :echo | ||||
|     # Whether to echo for assignment expressions | ||||
|     # | ||||
|     # Uses IRB.conf[:ECHO_ON_ASSIGNMENT] if available, or defaults to +false+. | ||||
|     # | ||||
|     #     a = "omg" | ||||
|     #     IRB.CurrentContext.echo_on_assignment = true | ||||
|     #     a = "omg" | ||||
|     #     #=> omg | ||||
|     attr_accessor :echo_on_assignment | ||||
|     # Whether verbose messages are displayed or not. | ||||
|     # | ||||
|     # A copy of the default <code>IRB.conf[:VERBOSE]</code> | ||||
|  | @ -261,6 +275,7 @@ module IRB | |||
|     alias ignore_sigint? ignore_sigint | ||||
|     alias ignore_eof? ignore_eof | ||||
|     alias echo? echo | ||||
|     alias echo_on_assignment? echo_on_assignment | ||||
| 
 | ||||
|     # Returns whether messages are displayed or not. | ||||
|     def verbose? | ||||
|  |  | |||
|  | @ -51,6 +51,7 @@ module IRB # :nodoc: | |||
|     @CONF[:IGNORE_SIGINT] = true | ||||
|     @CONF[:IGNORE_EOF] = false | ||||
|     @CONF[:ECHO] = nil | ||||
|     @CONF[:ECHO_ON_ASSIGNMENT] = nil | ||||
|     @CONF[:VERBOSE] = nil | ||||
| 
 | ||||
|     @CONF[:EVAL_HISTORY] = nil | ||||
|  | @ -172,6 +173,10 @@ module IRB # :nodoc: | |||
|         @CONF[:ECHO] = true | ||||
|       when "--noecho" | ||||
|         @CONF[:ECHO] = false | ||||
|       when "--echo-on-assignment" | ||||
|         @CONF[:ECHO_ON_ASSIGNMENT] = true | ||||
|       when "--noecho-on-assignment" | ||||
|         @CONF[:ECHO_ON_ASSIGNMENT] = false | ||||
|       when "--verbose" | ||||
|         @CONF[:VERBOSE] = true | ||||
|       when "--noverbose" | ||||
|  |  | |||
|  | @ -26,6 +26,10 @@ module TestIRB | |||
|       def encoding | ||||
|         Encoding.default_external | ||||
|       end | ||||
| 
 | ||||
|       def reset | ||||
|         @line_no = 0 | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def setup | ||||
|  | @ -84,5 +88,126 @@ module TestIRB | |||
|     def test_default_config | ||||
|       assert_equal(true, @context.use_colorize?) | ||||
|     end | ||||
| 
 | ||||
|     def test_assignment_expression | ||||
|       input = TestInputMethod.new | ||||
|       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) | ||||
|       [ | ||||
|         "foo = bar", | ||||
|         "@foo = bar", | ||||
|         "$foo = bar", | ||||
|         "@@foo = bar", | ||||
|         "::Foo = bar", | ||||
|         "a::Foo = bar", | ||||
|         "Foo = bar", | ||||
|         "foo.bar = 1", | ||||
|         "foo[1] = bar", | ||||
|         "foo += bar", | ||||
|         "foo -= bar", | ||||
|         "foo ||= bar", | ||||
|         "foo &&= bar", | ||||
|         "foo, bar = 1, 2", | ||||
|         "foo.bar=(1)", | ||||
|         "foo; foo = bar", | ||||
|         "foo; foo = bar; ;\n ;", | ||||
|         "foo\nfoo = bar", | ||||
|       ].each do |exp| | ||||
|         assert( | ||||
|           irb.assignment_expression?(exp), | ||||
|           "#{exp.inspect}: should be an assignment expression" | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       [ | ||||
|         "foo", | ||||
|         "foo.bar", | ||||
|         "foo[0]", | ||||
|         "foo = bar; foo", | ||||
|         "foo = bar\nfoo", | ||||
|       ].each do |exp| | ||||
|         refute( | ||||
|           irb.assignment_expression?(exp), | ||||
|           "#{exp.inspect}: should not be an assignment expression" | ||||
|         ) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def test_echo_on_assignment | ||||
|       input = TestInputMethod.new([ | ||||
|         "a = 1\n", | ||||
|         "a\n", | ||||
|         "a, b = 2, 3\n", | ||||
|         "a\n", | ||||
|         "b\n", | ||||
|         "b = 4\n", | ||||
|         "_\n" | ||||
|       ]) | ||||
|       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) | ||||
| 
 | ||||
|       # The default | ||||
|       irb.context.echo = true | ||||
|       irb.context.echo_on_assignment = false | ||||
|       out, err = capture_io do | ||||
|         irb.eval_input | ||||
|       end | ||||
|       assert_empty err | ||||
|       assert_equal("=> 1\n=> 2\n=> 3\n=> 4\n", out) | ||||
| 
 | ||||
|       # Everything is output, like before echo_on_assignment was introduced | ||||
|       input.reset | ||||
|       irb.context.echo = true | ||||
|       irb.context.echo_on_assignment = true | ||||
|       out, err = capture_io do | ||||
|         irb.eval_input | ||||
|       end | ||||
|       assert_empty err | ||||
|       assert_equal("=> 1\n=> 1\n=> [2, 3]\n=> 2\n=> 3\n=> 4\n=> 4\n", out) | ||||
| 
 | ||||
|       # Nothing is output when echo is false | ||||
|       input.reset | ||||
|       irb.context.echo = false | ||||
|       irb.context.echo_on_assignment = false | ||||
|       out, err = capture_io do | ||||
|         irb.eval_input | ||||
|       end | ||||
|       assert_empty err | ||||
|       assert_equal("", out) | ||||
| 
 | ||||
|       # Nothing is output when echo is false even if echo_on_assignment is true | ||||
|       input.reset | ||||
|       irb.context.echo = false | ||||
|       irb.context.echo_on_assignment = true | ||||
|       out, err = capture_io do | ||||
|         irb.eval_input | ||||
|       end | ||||
|       assert_empty err | ||||
|       assert_equal("", out) | ||||
|     end | ||||
| 
 | ||||
|     def test_echo_on_assignment_conf | ||||
|       # Default | ||||
|       IRB.conf[:ECHO] = nil | ||||
|       IRB.conf[:ECHO_ON_ASSIGNMENT] = nil | ||||
|       input = TestInputMethod.new() | ||||
|       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) | ||||
| 
 | ||||
|       assert(irb.context.echo?, "echo? should be true by default") | ||||
|       refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") | ||||
| 
 | ||||
|       # Explicitly set :ECHO to false | ||||
|       IRB.conf[:ECHO] = false | ||||
|       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) | ||||
| 
 | ||||
|       refute(irb.context.echo?, "echo? should be false when IRB.conf[:ECHO] is set to false") | ||||
|       refute(irb.context.echo_on_assignment?, "echo_on_assignment? should be false by default") | ||||
| 
 | ||||
|       # Explicitly set :ECHO_ON_ASSIGNMENT to true | ||||
|       IRB.conf[:ECHO] = nil | ||||
|       IRB.conf[:ECHO_ON_ASSIGNMENT] = true | ||||
|       irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input) | ||||
| 
 | ||||
|       assert(irb.context.echo?, "echo? should be true by default") | ||||
|       assert(irb.context.echo_on_assignment?, "echo_on_assignment? should be true when IRB.conf[:ECHO_ON_ASSIGNMENT] is set to true") | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ module TestIRB | |||
|       bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : [] | ||||
|       assert_in_out_err(bundle_exec + %w[-rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, /Exception: foo/, []) | ||||
|       e = Exception.new("foo") | ||||
|       puts e.inspect | ||||
|       def e.backtrace; nil; end | ||||
|       raise e | ||||
| IRB | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Steven Willis
						Steven Willis