irb 1.8.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/irb.rb CHANGED
@@ -140,6 +140,10 @@ require_relative "irb/debug"
140
140
  #
141
141
  # IRB.conf[:USE_AUTOCOMPLETE] = false
142
142
  #
143
+ # To enable enhanced completion using type information, add the following to your +.irbrc+:
144
+ #
145
+ # IRB.conf[:COMPLETOR] = :type
146
+ #
143
147
  # === History
144
148
  #
145
149
  # By default, irb will store the last 1000 commands you used in
@@ -367,24 +371,6 @@ module IRB
367
371
  # An exception raised by IRB.irb_abort
368
372
  class Abort < Exception;end
369
373
 
370
- @CONF = {}
371
- # Displays current configuration.
372
- #
373
- # Modifying the configuration is achieved by sending a message to IRB.conf.
374
- #
375
- # See IRB@Configuration for more information.
376
- def IRB.conf
377
- @CONF
378
- end
379
-
380
- # Returns the current version of IRB, including release version and last
381
- # updated date.
382
- def IRB.version
383
- if v = @CONF[:VERSION] then return v end
384
-
385
- @CONF[:VERSION] = format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
386
- end
387
-
388
374
  # The current IRB::Context of the session, see IRB.conf
389
375
  #
390
376
  # irb
@@ -431,12 +417,18 @@ module IRB
431
417
  PROMPT_MAIN_TRUNCATE_OMISSION = '...'.freeze
432
418
  CONTROL_CHARACTERS_PATTERN = "\x00-\x1F".freeze
433
419
 
420
+ # Returns the current context of this irb session
421
+ attr_reader :context
422
+ # The lexer used by this irb session
423
+ attr_accessor :scanner
424
+
434
425
  # Creates a new irb session
435
426
  def initialize(workspace = nil, input_method = nil)
436
427
  @context = Context.new(self, workspace, input_method)
437
428
  @context.workspace.load_commands_to_main
438
429
  @signal_status = :IN_IRB
439
- @scanner = RubyLex.new(@context)
430
+ @scanner = RubyLex.new
431
+ @line_no = 1
440
432
  end
441
433
 
442
434
  # A hook point for `debug` command's breakpoint after :IRB_EXIT as well as its clean-up
@@ -454,7 +446,7 @@ module IRB
454
446
  workspace = IRB::WorkSpace.new(binding)
455
447
  context.workspace = workspace
456
448
  context.workspace.load_commands_to_main
457
- scanner.increase_line_no(1)
449
+ @line_no += 1
458
450
 
459
451
  # When users run:
460
452
  # 1. Debugging commands, like `step 2`
@@ -476,7 +468,7 @@ module IRB
476
468
  end
477
469
 
478
470
  if input&.include?("\n")
479
- scanner.increase_line_no(input.count("\n") - 1)
471
+ @line_no += input.count("\n") - 1
480
472
  end
481
473
 
482
474
  input
@@ -508,39 +500,8 @@ module IRB
508
500
  end
509
501
  end
510
502
 
511
- # Returns the current context of this irb session
512
- attr_reader :context
513
- # The lexer used by this irb session
514
- attr_accessor :scanner
515
-
516
503
  # Evaluates input for this session.
517
504
  def eval_input
518
- @scanner.set_prompt do
519
- |ltype, indent, continue, line_no|
520
- if ltype
521
- f = @context.prompt_s
522
- elsif continue
523
- f = @context.prompt_c
524
- else
525
- f = @context.prompt_i
526
- end
527
- f = "" unless f
528
- if @context.prompting?
529
- @context.io.prompt = p = prompt(f, ltype, indent, line_no)
530
- else
531
- @context.io.prompt = p = ""
532
- end
533
- if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
534
- unless ltype
535
- prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
536
- ind = prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
537
- indent * 2 - p.size
538
- @context.io.prompt = p + " " * ind if ind > 0
539
- end
540
- end
541
- @context.io.prompt
542
- end
543
-
544
505
  configure_io
545
506
 
546
507
  each_top_level_statement do |statement, line_no|
@@ -572,8 +533,9 @@ module IRB
572
533
  end
573
534
  end
574
535
 
575
- def read_input
536
+ def read_input(prompt)
576
537
  signal_status(:IN_INPUT) do
538
+ @context.io.prompt = prompt
577
539
  if l = @context.io.gets
578
540
  print l if @context.verbose?
579
541
  else
@@ -591,16 +553,16 @@ module IRB
591
553
  end
592
554
 
593
555
  def readmultiline
594
- @scanner.save_prompt_to_context_io([], false, 0)
556
+ prompt = generate_prompt([], false, 0)
595
557
 
596
558
  # multiline
597
- return read_input if @context.io.respond_to?(:check_termination)
559
+ return read_input(prompt) if @context.io.respond_to?(:check_termination)
598
560
 
599
561
  # nomultiline
600
562
  code = ''
601
563
  line_offset = 0
602
564
  loop do
603
- line = read_input
565
+ line = read_input(prompt)
604
566
  unless line
605
567
  return code.empty? ? nil : code
606
568
  end
@@ -610,12 +572,12 @@ module IRB
610
572
  # Accept any single-line input for symbol aliases or commands that transform args
611
573
  return code if single_line_command?(code)
612
574
 
613
- tokens, opens, terminated = @scanner.check_code_state(code)
575
+ tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
614
576
  return code if terminated
615
577
 
616
578
  line_offset += 1
617
579
  continue = @scanner.should_continue?(tokens)
618
- @scanner.save_prompt_to_context_io(opens, continue, line_offset)
580
+ prompt = generate_prompt(opens, continue, line_offset)
619
581
  end
620
582
  end
621
583
 
@@ -625,9 +587,9 @@ module IRB
625
587
  break unless code
626
588
 
627
589
  if code != "\n"
628
- yield build_statement(code), @scanner.line_no
590
+ yield build_statement(code), @line_no
629
591
  end
630
- @scanner.increase_line_no(code.count("\n"))
592
+ @line_no += code.count("\n")
631
593
  rescue RubyLex::TerminateLineInput
632
594
  end
633
595
  end
@@ -643,7 +605,8 @@ module IRB
643
605
  if command_class
644
606
  Statement::Command.new(code, command, arg, command_class)
645
607
  else
646
- Statement::Expression.new(code, @scanner.assignment_expression?(code))
608
+ is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
609
+ Statement::Expression.new(code, is_assignment_expression)
647
610
  end
648
611
  end
649
612
 
@@ -656,7 +619,7 @@ module IRB
656
619
  if @context.io.respond_to?(:check_termination)
657
620
  @context.io.check_termination do |code|
658
621
  if Reline::IOGate.in_pasting?
659
- rest = @scanner.check_termination_in_prev_line(code)
622
+ rest = @scanner.check_termination_in_prev_line(code, local_variables: @context.local_variables)
660
623
  if rest
661
624
  Reline.delete_text
662
625
  rest.bytes.reverse_each do |c|
@@ -670,7 +633,7 @@ module IRB
670
633
  # Accept any single-line input for symbol aliases or commands that transform args
671
634
  next true if single_line_command?(code)
672
635
 
673
- _tokens, _opens, terminated = @scanner.check_code_state(code)
636
+ _tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
674
637
  terminated
675
638
  end
676
639
  end
@@ -678,7 +641,7 @@ module IRB
678
641
  if @context.io.respond_to?(:dynamic_prompt)
679
642
  @context.io.dynamic_prompt do |lines|
680
643
  lines << '' if lines.empty?
681
- tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, context: @context)
644
+ tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
682
645
  line_results = IRB::NestingParser.parse_by_line(tokens)
683
646
  tokens_until_line = []
684
647
  line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
@@ -687,7 +650,7 @@ module IRB
687
650
  tokens_until_line << token if token != tokens_until_line.last
688
651
  end
689
652
  continue = @scanner.should_continue?(tokens_until_line)
690
- @scanner.prompt(next_opens, continue, line_num_offset)
653
+ generate_prompt(next_opens, continue, line_num_offset)
691
654
  end
692
655
  end
693
656
  end
@@ -698,7 +661,7 @@ module IRB
698
661
  next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
699
662
 
700
663
  code = lines[0..line_index].map { |l| "#{l}\n" }.join
701
- tokens = RubyLex.ripper_lex_without_warning(code, context: @context)
664
+ tokens = RubyLex.ripper_lex_without_warning(code, local_variables: @context.local_variables)
702
665
  @scanner.process_indent_level(tokens, lines, line_index, is_newline)
703
666
  end
704
667
  end
@@ -828,16 +791,6 @@ module IRB
828
791
  end
829
792
  end
830
793
 
831
- # Evaluates the given block using the given +context+ as the Context.
832
- def suspend_context(context)
833
- @context, back_context = context, @context
834
- begin
835
- yield back_context
836
- ensure
837
- @context = back_context
838
- end
839
- end
840
-
841
794
  # Handler for the signal SIGINT, see Kernel#trap for more information.
842
795
  def signal_handle
843
796
  unless @context.ignore_sigint?
@@ -873,54 +826,6 @@ module IRB
873
826
  end
874
827
  end
875
828
 
876
- def truncate_prompt_main(str) # :nodoc:
877
- str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
878
- if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
879
- str
880
- else
881
- str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
882
- end
883
- end
884
-
885
- def prompt(prompt, ltype, indent, line_no) # :nodoc:
886
- p = prompt.dup
887
- p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
888
- case $2
889
- when "N"
890
- @context.irb_name
891
- when "m"
892
- truncate_prompt_main(@context.main.to_s)
893
- when "M"
894
- truncate_prompt_main(@context.main.inspect)
895
- when "l"
896
- ltype
897
- when "i"
898
- if indent < 0
899
- if $1
900
- "-".rjust($1.to_i)
901
- else
902
- "-"
903
- end
904
- else
905
- if $1
906
- format("%" + $1 + "d", indent)
907
- else
908
- indent.to_s
909
- end
910
- end
911
- when "n"
912
- if $1
913
- format("%" + $1 + "d", line_no)
914
- else
915
- line_no.to_s
916
- end
917
- when "%"
918
- "%"
919
- end
920
- end
921
- p
922
- end
923
-
924
829
  def output_value(omit = false) # :nodoc:
925
830
  str = @context.inspect_last_value
926
831
  multiline_p = str.include?("\n")
@@ -973,28 +878,84 @@ module IRB
973
878
  end
974
879
  format("#<%s: %s>", self.class, ary.join(", "))
975
880
  end
976
- end
977
881
 
978
- def @CONF.inspect
979
- IRB.version unless self[:VERSION]
980
-
981
- array = []
982
- for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
983
- case k
984
- when :MAIN_CONTEXT, :__TMP__EHV__
985
- array.push format("CONF[:%s]=...myself...", k.id2name)
986
- when :PROMPT
987
- s = v.collect{
988
- |kk, vv|
989
- ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
990
- format(":%s=>{%s}", kk.id2name, ss.join(", "))
991
- }
992
- array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
882
+ private
883
+
884
+ def generate_prompt(opens, continue, line_offset)
885
+ ltype = @scanner.ltype_from_open_tokens(opens)
886
+ indent = @scanner.calc_indent_level(opens)
887
+ continue = opens.any? || continue
888
+ line_no = @line_no + line_offset
889
+
890
+ if ltype
891
+ f = @context.prompt_s
892
+ elsif continue
893
+ f = @context.prompt_c
993
894
  else
994
- array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
895
+ f = @context.prompt_i
896
+ end
897
+ f = "" unless f
898
+ if @context.prompting?
899
+ p = format_prompt(f, ltype, indent, line_no)
900
+ else
901
+ p = ""
902
+ end
903
+ if @context.auto_indent_mode and !@context.io.respond_to?(:auto_indent)
904
+ unless ltype
905
+ prompt_i = @context.prompt_i.nil? ? "" : @context.prompt_i
906
+ ind = format_prompt(prompt_i, ltype, indent, line_no)[/.*\z/].size +
907
+ indent * 2 - p.size
908
+ p += " " * ind if ind > 0
909
+ end
910
+ end
911
+ p
912
+ end
913
+
914
+ def truncate_prompt_main(str) # :nodoc:
915
+ str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
916
+ if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
917
+ str
918
+ else
919
+ str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
920
+ end
921
+ end
922
+
923
+ def format_prompt(format, ltype, indent, line_no) # :nodoc:
924
+ format.gsub(/%([0-9]+)?([a-zA-Z])/) do
925
+ case $2
926
+ when "N"
927
+ @context.irb_name
928
+ when "m"
929
+ truncate_prompt_main(@context.main.to_s)
930
+ when "M"
931
+ truncate_prompt_main(@context.main.inspect)
932
+ when "l"
933
+ ltype
934
+ when "i"
935
+ if indent < 0
936
+ if $1
937
+ "-".rjust($1.to_i)
938
+ else
939
+ "-"
940
+ end
941
+ else
942
+ if $1
943
+ format("%" + $1 + "d", indent)
944
+ else
945
+ indent.to_s
946
+ end
947
+ end
948
+ when "n"
949
+ if $1
950
+ format("%" + $1 + "d", line_no)
951
+ else
952
+ line_no.to_s
953
+ end
954
+ when "%"
955
+ "%"
956
+ end
995
957
  end
996
958
  end
997
- array.join("\n")
998
959
  end
999
960
  end
1000
961
 
data/man/irb.1 CHANGED
@@ -140,6 +140,13 @@ Use autocompletion.
140
140
  Don't use autocompletion.
141
141
  .Pp
142
142
  .Pp
143
+ .It Fl -regexp-completor
144
+ Use regexp based completion.
145
+ .Pp
146
+ .It Fl -type-completor
147
+ Use type based completion.
148
+ .Pp
149
+ .Pp
143
150
  .It Fl -verbose
144
151
  Show details.
145
152
  .Pp
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-09-05 00:00:00.000000000 Z
12
+ date: 2023-11-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: reline
@@ -118,6 +118,11 @@ files:
118
118
  - lib/irb/ruby_logo.aa
119
119
  - lib/irb/source_finder.rb
120
120
  - lib/irb/statement.rb
121
+ - lib/irb/type_completion/completor.rb
122
+ - lib/irb/type_completion/methods.rb
123
+ - lib/irb/type_completion/scope.rb
124
+ - lib/irb/type_completion/type_analyzer.rb
125
+ - lib/irb/type_completion/types.rb
121
126
  - lib/irb/version.rb
122
127
  - lib/irb/workspace.rb
123
128
  - lib/irb/ws-for-case-2.rb
@@ -147,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
152
  - !ruby/object:Gem::Version
148
153
  version: '0'
149
154
  requirements: []
150
- rubygems_version: 3.4.10
155
+ rubygems_version: 3.5.0.dev
151
156
  signing_key:
152
157
  specification_version: 4
153
158
  summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).