irb 1.15.1 → 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.
data/lib/irb/ruby_logo.aa CHANGED
@@ -116,3 +116,7 @@ TYPE: UNICODE
116
116
  ⢻⣾⡇ ⠘⣷ ⣼⠃ ⠘⣷⣠⣴⠟⠋ ⠙⢷⣄⢸⣿
117
117
  ⠻⣧⡀ ⠘⣧⣰⡏ ⢀⣠⣤⠶⠛⠉⠛⠛⠛⠛⠛⠛⠻⢶⣶⣶⣶⣶⣶⣤⣤⣽⣿⣿
118
118
  ⠈⠛⠷⢦⣤⣽⣿⣥⣤⣶⣶⡿⠿⠿⠶⠶⠶⠶⠾⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠁
119
+ TYPE: UNICODE_SMALL
120
+ ⢀⡴⠊⢉⡟⢿
121
+ ⣎⣀⣴⡋⡟⣻
122
+ ⣟⣼⣱⣽⣟⣾
@@ -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
@@ -29,10 +29,13 @@ module IRB
29
29
 
30
30
  def colorized_content
31
31
  if !binary_file? && file_exist?
32
- end_line = find_end
33
32
  # To correctly colorize, we need to colorize full content and extract the relevant lines.
34
- colored = IRB::Color.colorize_code(file_content)
35
- colored.lines[@line - 1...end_line].join
33
+ colored_lines = IRB::Color.colorize_code(file_content).lines
34
+
35
+ # Handle wrong line number case: line_no passed to eval is wrong, file is edited after load, etc
36
+ return if colored_lines.size < @line
37
+
38
+ colored_lines[@line - 1...find_end].join
36
39
  elsif @ast_source
37
40
  IRB::Color.colorize_code(@ast_source)
38
41
  end
@@ -41,21 +44,12 @@ module IRB
41
44
  private
42
45
 
43
46
  def find_end
44
- lex = RubyLex.new
45
47
  code = file_content
46
48
  lines = code.lines[(@line - 1)..-1]
47
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
48
- prev_tokens = []
49
-
50
- # chunk with line number
51
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
52
- code = lines[0..lnum].join
53
- prev_tokens.concat chunk
54
- continue = lex.should_continue?(prev_tokens)
55
- syntax = lex.check_code_syntax(code, local_variables: [])
56
- if !continue && syntax == :valid
57
- return @line + lnum
58
- 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)
59
53
  end
60
54
  @line
61
55
  end
@@ -114,7 +108,7 @@ module IRB
114
108
  when "owner"
115
109
  target_method = owner_receiver.instance_method(method)
116
110
  when "receiver"
117
- target_method = owner_receiver.method(method)
111
+ target_method = Kernel.instance_method(:method).bind_call(owner_receiver, method)
118
112
  end
119
113
  super_level.times do |s|
120
114
  target_method = target_method.super_method if target_method
@@ -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.15.1"
8
+ VERSION = "1.18.0"
9
9
  @RELEASE_VERSION = VERSION
10
- @LAST_UPDATE_DATE = "2025-01-22"
10
+ @LAST_UPDATE_DATE = "2026-04-21"
11
11
  end
data/lib/irb/workspace.rb CHANGED
@@ -55,6 +55,15 @@ EOF
55
55
  # Note that this will typically be IRB::TOPLEVEL_BINDING
56
56
  # This is to avoid RubyGems' local variables (see issue #17623)
57
57
  @binding = TOPLEVEL_BINDING.dup
58
+
59
+ when 5 # binding in Ruby::Box
60
+ unless defined?(Ruby::Box)
61
+ puts 'Context-mode 5 (binding in Ruby::Box) requires Ruby 4.0 or later.'
62
+ raise NameError, 'Ruby::Box not defined'
63
+ end
64
+
65
+ puts 'Context-mode 5 (binding in Ruby::Box) is experimental. It may be removed or changed without notice.'
66
+ @binding = Ruby::Box.new.eval('Kernel.binding')
58
67
  end
59
68
  end
60
69
 
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
@@ -87,6 +103,7 @@ module IRB
87
103
  # Creates a new irb session
88
104
  def initialize(workspace = nil, input_method = nil, from_binding: false)
89
105
  @from_binding = from_binding
106
+ @prompt_part_cache = nil
90
107
  @context = Context.new(self, workspace, input_method)
91
108
  @context.workspace.load_helper_methods_to_main
92
109
  @signal_status = :IN_IRB
@@ -147,7 +164,10 @@ module IRB
147
164
  end
148
165
 
149
166
  def run(conf = IRB.conf)
150
- 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
+
151
171
  conf[:IRB_RC].call(context) if conf[:IRB_RC]
152
172
  prev_context = conf[:MAIN_CONTEXT]
153
173
  conf[:MAIN_CONTEXT] = context
@@ -173,6 +193,8 @@ module IRB
173
193
  eval_input
174
194
  end
175
195
  ensure
196
+ Irb.run_nesting_depth -= 1
197
+
176
198
  # Do not restore to nil. It will cause IRB crash when used with threads.
177
199
  IRB.conf[:MAIN_CONTEXT] = prev_context if prev_context
178
200
 
@@ -238,13 +260,7 @@ module IRB
238
260
  end
239
261
  end
240
262
 
241
- def readmultiline
242
- prompt = generate_prompt([], false, 0)
243
-
244
- # multiline
245
- return read_input(prompt) if @context.io.respond_to?(:check_termination)
246
-
247
- # nomultiline
263
+ def read_input_nomultiline(prompt)
248
264
  code = +''
249
265
  line_offset = 0
250
266
  loop do
@@ -256,15 +272,28 @@ module IRB
256
272
  code << line
257
273
  return code if command?(code)
258
274
 
259
- 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)
260
276
  return code if terminated
261
277
 
262
278
  line_offset += 1
263
- continue = @scanner.should_continue?(tokens)
264
279
  prompt = generate_prompt(opens, continue, line_offset)
265
280
  end
266
281
  end
267
282
 
283
+ def readmultiline
284
+ with_prompt_part_cached do
285
+ prompt = generate_prompt([], false, 0)
286
+
287
+ if @context.io.respond_to?(:check_termination)
288
+ # multiline
289
+ read_input(prompt)
290
+ else
291
+ # nomultiline
292
+ read_input_nomultiline(prompt)
293
+ end
294
+ end
295
+ end
296
+
268
297
  def each_top_level_statement
269
298
  loop do
270
299
  code = readmultiline
@@ -307,23 +336,25 @@ module IRB
307
336
  else
308
337
  next true if command?(code)
309
338
 
310
- _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)
311
340
  terminated
312
341
  end
313
342
  end
314
343
  end
315
344
  if @context.io.respond_to?(:dynamic_prompt)
316
345
  @context.io.dynamic_prompt do |lines|
317
- tokens = RubyLex.ripper_lex_without_warning(lines.map{ |l| l + "\n" }.join, local_variables: @context.local_variables)
318
- 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
+
319
353
  tokens_until_line = []
320
- line_results.map.with_index do |(line_tokens, _prev_opens, next_opens, _min_depth), line_num_offset|
321
- line_tokens.each do |token, _s|
322
- # Avoid appending duplicated token. Tokens that include "n" like multiline
323
- # tstring_content can exist in multiple lines.
324
- tokens_until_line << token if token != tokens_until_line.last
325
- end
326
- 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)
327
358
  generate_prompt(next_opens, continue, line_num_offset)
328
359
  end
329
360
  end
@@ -335,8 +366,8 @@ module IRB
335
366
  next nil if !is_newline && lines[line_index]&.byteslice(0, byte_pointer)&.match?(/\A\s*\z/)
336
367
 
337
368
  code = lines[0..line_index].map { |l| "#{l}\n" }.join
338
- tokens = RubyLex.ripper_lex_without_warning(code, local_variables: @context.local_variables)
339
- @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)
340
371
  end
341
372
  end
342
373
  end
@@ -567,8 +598,15 @@ module IRB
567
598
 
568
599
  private
569
600
 
601
+ def with_prompt_part_cached
602
+ @prompt_part_cache = {}
603
+ yield
604
+ ensure
605
+ @prompt_part_cache = nil
606
+ end
607
+
570
608
  def generate_prompt(opens, continue, line_offset)
571
- ltype = @scanner.ltype_from_open_tokens(opens)
609
+ ltype = @scanner.ltype_from_open_nestings(opens)
572
610
  indent = @scanner.calc_indent_level(opens)
573
611
  continue = opens.any? || continue
574
612
  line_no = @line_no + line_offset
@@ -598,25 +636,29 @@ module IRB
598
636
  end
599
637
 
600
638
  def truncate_prompt_main(str) # :nodoc:
601
- str = str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
602
- if str.size <= PROMPT_MAIN_TRUNCATE_LENGTH
603
- str
604
- else
605
- str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
639
+ if str.size > PROMPT_MAIN_TRUNCATE_LENGTH
640
+ str = str[0, PROMPT_MAIN_TRUNCATE_LENGTH - PROMPT_MAIN_TRUNCATE_OMISSION.size] + PROMPT_MAIN_TRUNCATE_OMISSION
606
641
  end
642
+ str.tr(CONTROL_CHARACTERS_PATTERN, ' ')
607
643
  end
608
644
 
609
645
  def format_prompt(format, ltype, indent, line_no) # :nodoc:
646
+ # @prompt_part_cache could be nil in unit tests
647
+ part_cache = @prompt_part_cache || {}
610
648
  format.gsub(/%([0-9]+)?([a-zA-Z%])/) do
611
649
  case $2
612
650
  when "N"
613
651
  @context.irb_name
614
652
  when "m"
615
- main_str = @context.safe_method_call_on_main(:to_s) rescue "!#{$!.class}"
616
- truncate_prompt_main(main_str)
653
+ part_cache[:m] ||= (
654
+ main_str = "#{@context.safe_method_call_on_main(:to_s)}" rescue "!#{$!.class}"
655
+ truncate_prompt_main(main_str)
656
+ )
617
657
  when "M"
618
- main_str = @context.safe_method_call_on_main(:inspect) rescue "!#{$!.class}"
619
- truncate_prompt_main(main_str)
658
+ part_cache[:M] ||= (
659
+ main_str = "#{@context.safe_method_call_on_main(:inspect)}" rescue "!#{$!.class}"
660
+ truncate_prompt_main(main_str)
661
+ )
620
662
  when "l"
621
663
  ltype
622
664
  when "i"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.1
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
8
8
  - Keiju ISHITSUKA
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-22 00:00:00.000000000 Z
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
@@ -61,16 +75,20 @@ executables:
61
75
  extensions: []
62
76
  extra_rdoc_files: []
63
77
  files:
78
+ - ".rdoc_options"
79
+ - CONTRIBUTING.md
80
+ - EXTEND_IRB.md
64
81
  - Gemfile
65
82
  - LICENSE.txt
66
83
  - README.md
67
- - Rakefile
68
- - bin/console
69
- - bin/setup
84
+ - doc/COMMAND_LINE_OPTIONS.md
85
+ - doc/COMPARED_WITH_PRY.md
86
+ - doc/Configurations.md
87
+ - doc/EXTEND_IRB.md
88
+ - doc/Index.md
70
89
  - doc/irb/irb-tools.rd.ja
71
90
  - doc/irb/irb.rd.ja
72
91
  - exe/irb
73
- - irb.gemspec
74
92
  - lib/irb.rb
75
93
  - lib/irb/cmd/nop.rb
76
94
  - lib/irb/color.rb
@@ -141,6 +159,7 @@ files:
141
159
  - lib/irb/ruby-lex.rb
142
160
  - lib/irb/ruby_logo.aa
143
161
  - lib/irb/source_finder.rb
162
+ - lib/irb/startup_message.rb
144
163
  - lib/irb/statement.rb
145
164
  - lib/irb/version.rb
146
165
  - lib/irb/workspace.rb
@@ -170,7 +189,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
189
  - !ruby/object:Gem::Version
171
190
  version: '0'
172
191
  requirements: []
173
- rubygems_version: 3.6.3
192
+ rubygems_version: 3.6.7
174
193
  specification_version: 4
175
194
  summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).
176
195
  test_files: []
data/Rakefile DELETED
@@ -1,52 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
- require "rdoc/task"
4
-
5
- Rake::TestTask.new(:test) do |t|
6
- t.libs << "test" << "test/lib"
7
- t.libs << "lib"
8
- t.ruby_opts << "-rhelper"
9
- t.test_files = FileList["test/irb/**/test_*.rb"]
10
- end
11
-
12
- # To make sure they have been correctly setup for Ruby CI.
13
- desc "Run each irb test file in isolation."
14
- task :test_in_isolation do
15
- failed = false
16
-
17
- FileList["test/irb/**/test_*.rb"].each do |test_file|
18
- ENV["TEST"] = test_file
19
- begin
20
- Rake::Task["test"].execute
21
- rescue
22
- failed = true
23
- msg = "Test '#{test_file}' failed when being executed in isolation. Please make sure 'rake test TEST=#{test_file}' passes."
24
- separation_line = '=' * msg.length
25
-
26
- puts <<~MSG
27
- #{separation_line}
28
- #{msg}
29
- #{separation_line}
30
- MSG
31
- end
32
- end
33
-
34
- fail "Some tests failed when being executed in isolation" if failed
35
- end
36
-
37
- Rake::TestTask.new(:test_yamatanooroti) do |t|
38
- t.libs << 'test' << "test/lib"
39
- t.libs << 'lib'
40
- #t.loader = :direct
41
- t.ruby_opts << "-rhelper"
42
- t.pattern = 'test/irb/yamatanooroti/test_*.rb'
43
- end
44
-
45
- task :default => :test
46
-
47
- RDoc::Task.new do |rdoc|
48
- rdoc.title = "IRB Documentation"
49
- rdoc.main = "Index.md"
50
- rdoc.rdoc_dir = "_site"
51
- rdoc.options.push("lib")
52
- end
data/bin/console DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require_relative "../lib/irb"
5
-
6
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
data/irb.gemspec DELETED
@@ -1,46 +0,0 @@
1
- begin
2
- require_relative "lib/irb/version"
3
- rescue LoadError
4
- # for Ruby core repository
5
- require_relative "version"
6
- end
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = "irb"
10
- spec.version = IRB::VERSION
11
- spec.authors = ["aycabta", "Keiju ISHITSUKA"]
12
- spec.email = ["aycabta@gmail.com", "keiju@ruby-lang.org"]
13
-
14
- spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
15
- spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
16
- spec.homepage = "https://github.com/ruby/irb"
17
- spec.licenses = ["Ruby", "BSD-2-Clause"]
18
-
19
- spec.metadata["homepage_uri"] = spec.homepage
20
- spec.metadata["source_code_uri"] = spec.homepage
21
- spec.metadata["documentation_uri"] = "https://ruby.github.io/irb/"
22
- spec.metadata["changelog_uri"] = "#{spec.homepage}/releases"
23
-
24
- spec.files = [
25
- "Gemfile",
26
- "LICENSE.txt",
27
- "README.md",
28
- "Rakefile",
29
- "bin/console",
30
- "bin/setup",
31
- "doc/irb/irb-tools.rd.ja",
32
- "doc/irb/irb.rd.ja",
33
- "exe/irb",
34
- "irb.gemspec",
35
- "man/irb.1",
36
- ] + Dir.glob("lib/**/*")
37
- spec.bindir = "exe"
38
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
39
- spec.require_paths = ["lib"]
40
-
41
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
42
-
43
- spec.add_dependency "reline", ">= 0.4.2"
44
- spec.add_dependency "rdoc", ">= 4.0.0"
45
- spec.add_dependency "pp", ">= 0.6.0"
46
- end