irb 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "ruby-lex"
3
+ require 'prism'
4
4
 
5
5
  module IRB
6
6
  class SourceFinder
@@ -44,21 +44,12 @@ module IRB
44
44
  private
45
45
 
46
46
  def find_end
47
- lex = RubyLex.new
48
47
  code = file_content
49
48
  lines = code.lines[(@line - 1)..-1]
50
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
51
- prev_tokens = []
52
-
53
- # chunk with line number
54
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
55
- code = lines[0..lnum].join
56
- prev_tokens.concat chunk
57
- continue = lex.should_continue?(prev_tokens)
58
- syntax = lex.check_code_syntax(code, local_variables: [])
59
- if !continue && syntax == :valid
60
- return @line + lnum
61
- end
49
+
50
+ lines.each_with_index do |line, index|
51
+ sub_code = lines.take(index + 1).join
52
+ return @line + index if Prism.parse_success?(sub_code)
62
53
  end
63
54
  @line
64
55
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "color"
4
+ require_relative "version"
5
+
6
+ module IRB
7
+ module StartupMessage
8
+ TIPS = [
9
+ 'Type "help" for commands, "help <cmd>" for details',
10
+ '"show_doc method" to view documentation',
11
+ '"ls [object]" to see methods and properties',
12
+ '"ls [object] -g pattern" to filter methods and properties',
13
+ '"edit method" to open the method\'s source in editor',
14
+ '"cd object" to navigate into an object',
15
+ '"show_source method" to view source code',
16
+ '"copy expr" to copy the output to clipboard',
17
+ '"debug" to start integration with the "debug" gem',
18
+ '"history -g pattern" to search history',
19
+ ].freeze
20
+
21
+ class << self
22
+ def display
23
+ logo_lines = load_logo
24
+ info_lines = build_info_lines
25
+
26
+ output = if logo_lines
27
+ combine_logo_and_info(logo_lines, info_lines)
28
+ else
29
+ info_lines.join("\n")
30
+ end
31
+
32
+ # Add a blank line to not immediately touch warning messages
33
+ puts
34
+ puts output
35
+ puts
36
+ end
37
+
38
+ private
39
+
40
+ def load_logo
41
+ encoding = STDOUT.external_encoding || Encoding.default_external
42
+ return nil unless encoding == Encoding::UTF_8
43
+
44
+ logo = IRB.send(:easter_egg_logo, :unicode_small)
45
+ return nil unless logo
46
+
47
+ logo.chomp.lines.map(&:chomp)
48
+ end
49
+
50
+ def build_info_lines
51
+ version_line = "#{Color.colorize('IRB', [:BOLD])} v#{VERSION} - Ruby #{RUBY_VERSION}"
52
+ tip_line = colorize_tip(TIPS.sample)
53
+ dir_line = Color.colorize(short_pwd, [:CYAN])
54
+
55
+ [version_line, tip_line, dir_line]
56
+ end
57
+
58
+ def colorize_tip(tip)
59
+ tip.gsub(/"[^"]*"/) { |match| Color.colorize(match, [:YELLOW]) }
60
+ end
61
+
62
+ def combine_logo_and_info(logo_lines, info_lines)
63
+ max_lines = [logo_lines.size, info_lines.size].max
64
+ lines = max_lines.times.map do |i|
65
+ logo_part = logo_lines[i] || ""
66
+ info_part = info_lines[i] || ""
67
+ colored_logo = Color.colorize(logo_part, [:RED, :BOLD])
68
+ "#{colored_logo} #{info_part}"
69
+ end
70
+ lines.join("\n")
71
+ end
72
+
73
+ def short_pwd
74
+ dir = Dir.pwd
75
+ home = ENV['HOME']
76
+ if home && (dir == home || dir.start_with?("#{home}/"))
77
+ dir = "~#{dir[home.size..]}"
78
+ end
79
+ dir
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/irb/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
- VERSION = "1.16.0"
8
+ VERSION = "1.18.0"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2025-12-17"
10
+ @LAST_UPDATE_DATE = "2026-04-21"
11
11
  end
data/lib/irb.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # by Keiju ISHITSUKA(keiju@ruby-lang.org)
6
6
  #
7
7
 
8
- require "ripper"
8
+ require "prism"
9
9
  require "reline"
10
10
 
11
11
  require_relative "irb/init"
@@ -21,6 +21,7 @@ require_relative "irb/color"
21
21
 
22
22
  require_relative "irb/version"
23
23
  require_relative "irb/easter-egg"
24
+ require_relative "irb/startup_message"
24
25
  require_relative "irb/debug"
25
26
  require_relative "irb/pager"
26
27
 
@@ -50,7 +51,13 @@ module IRB
50
51
  irb = Irb.new(nil, @CONF[:SCRIPT])
51
52
  else
52
53
  irb = Irb.new
54
+
55
+ # Only display the banner in the irb executable
56
+ if @CONF[:SHOW_BANNER] && ap_path&.end_with?("exe/irb")
57
+ StartupMessage.display
58
+ end
53
59
  end
60
+
54
61
  irb.run(@CONF)
55
62
  end
56
63
 
@@ -77,6 +84,15 @@ module IRB
77
84
  PROMPT_MAIN_TRUNCATE_OMISSION = '...'
78
85
  CONTROL_CHARACTERS_PATTERN = "\x00-\x1F"
79
86
 
87
+ # Track the nesting depth of run loops. This is used for history management
88
+ # only the outermost run loop should load/save history.
89
+ @run_nesting_depth = 0
90
+
91
+ class << self
92
+ # TODO: When refactoring to v2.0, find a better way to manage and track nesting sessions.
93
+ attr_accessor :run_nesting_depth
94
+ end
95
+
80
96
  # Returns the current context of this irb session
81
97
  attr_reader :context
82
98
  # The lexer used by this irb session
@@ -148,7 +164,10 @@ module IRB
148
164
  end
149
165
 
150
166
  def run(conf = IRB.conf)
151
- in_nested_session = !!conf[:MAIN_CONTEXT]
167
+ # Use run_nesting_depth to determine if we're in a nested session.
168
+ in_nested_session = Irb.run_nesting_depth > 0
169
+ Irb.run_nesting_depth += 1
170
+
152
171
  conf[:IRB_RC].call(context) if conf[:IRB_RC]
153
172
  prev_context = conf[:MAIN_CONTEXT]
154
173
  conf[:MAIN_CONTEXT] = context
@@ -174,6 +193,8 @@ module IRB
174
193
  eval_input
175
194
  end
176
195
  ensure
196
+ Irb.run_nesting_depth -= 1
197
+
177
198
  # Do not restore to nil. It will cause IRB crash when used with threads.
178
199
  IRB.conf[:MAIN_CONTEXT] = prev_context if prev_context
179
200
 
@@ -251,11 +272,10 @@ module IRB
251
272
  code << line
252
273
  return code if command?(code)
253
274
 
254
- tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
275
+ continue, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
255
276
  return code if terminated
256
277
 
257
278
  line_offset += 1
258
- continue = @scanner.should_continue?(tokens)
259
279
  prompt = generate_prompt(opens, continue, line_offset)
260
280
  end
261
281
  end
@@ -316,23 +336,25 @@ module IRB
316
336
  else
317
337
  next true if command?(code)
318
338
 
319
- _tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
339
+ _continue, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
320
340
  terminated
321
341
  end
322
342
  end
323
343
  end
324
344
  if @context.io.respond_to?(:dynamic_prompt)
325
345
  @context.io.dynamic_prompt do |lines|
326
- tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
327
- line_results = IRB::NestingParser.parse_by_line(tokens)
346
+ code = lines.map{ |l| l + "\n" }.join
347
+ parse_lex_result = Prism.parse_lex(code, scopes: [@context.local_variables])
348
+ line_results = IRB::NestingParser.parse_by_line(parse_lex_result)
349
+
350
+ tokens = parse_lex_result.value[1].map(&:first)
351
+ tokens_by_line = tokens.group_by {|t| t.location.start_line - 1 }
352
+
328
353
  tokens_until_line = []
329
- line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
330
- line_tokens.each do |token, _s|
331
- # Avoid appending duplicated token. Tokens that include "n" like multiline
332
- # tstring_content can exist in multiple lines.
333
- tokens_until_line << token if token != tokens_until_line.last
334
- end
335
- continue = @scanner.should_continue?(tokens_until_line)
354
+ line_results.map.with_index do |(_prev_opens, next_opens, _min_depth), line_num_offset|
355
+ line = lines[line_num_offset]
356
+ tokens_until_line.concat(tokens_by_line[line_num_offset] || [])
357
+ continue = @scanner.should_continue?(tokens_until_line, line, line_num_offset + 1)
336
358
  generate_prompt(next_opens, continue, line_num_offset)
337
359
  end
338
360
  end
@@ -344,8 +366,8 @@ module IRB
344
366
  next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
345
367
 
346
368
  code = lines[0..line_index].map { |l| "#{l}\n" }.join
347
- tokens = RubyLex.ripper_lex_without_warning(code, local_variables: @context.local_variables)
348
- @scanner.process_indent_level(tokens, lines, line_index, is_newline)
369
+ parse_lex_result = Prism.parse_lex(code, scopes: [@context.local_variables])
370
+ @scanner.process_indent_level(parse_lex_result, lines, line_index, is_newline)
349
371
  end
350
372
  end
351
373
  end
@@ -584,7 +606,7 @@ module IRB
584
606
  end
585
607
 
586
608
  def generate_prompt(opens, continue, line_offset)
587
- ltype = @scanner.ltype_from_open_tokens(opens)
609
+ ltype = @scanner.ltype_from_open_nestings(opens)
588
610
  indent = @scanner.calc_indent_level(opens)
589
611
  continue = opens.any? || continue
590
612
  line_no = @line_no + line_offset
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.16.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
@@ -10,6 +10,20 @@ bindir: exe
10
10
  cert_chain: []
11
11
  date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: prism
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: reline
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +159,7 @@ files:
145
159
  - lib/irb/ruby-lex.rb
146
160
  - lib/irb/ruby_logo.aa
147
161
  - lib/irb/source_finder.rb
162
+ - lib/irb/startup_message.rb
148
163
  - lib/irb/statement.rb
149
164
  - lib/irb/version.rb
150
165
  - lib/irb/workspace.rb