irb 1.7.0 → 1.7.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa3c9603f48d730988d34f454a10b58021caa677ab8832d9764e39e61bb49abb
4
- data.tar.gz: c46d1e32c81a8f22593e174af095070159af6a61508c97b3ef4b0b9d28301d58
3
+ metadata.gz: 9400febd023df0efde88c58ff524d4261ffe727404faf5cc719036e8691b7804
4
+ data.tar.gz: c46f543dc01f54f1ef6eaa37c2fdad212e06ffeecec0775e65f47423b68f8f7b
5
5
  SHA512:
6
- metadata.gz: 3e99185c909180d01a3310f7db11437181268b930d267f2fe365c06cd7a442ad3a8cdf7d5863fb19e17885a23aad46b993a2e5c21fb8632ff2f91285e469be4b
7
- data.tar.gz: 44978be86d00d0fc435e81e4cadc5abad08d47519a189db02e34d7eb3435e9ac0cc0adf470e845edba1e7d866574298e1b7b8bba5190d8be538c396cd0179d07
6
+ metadata.gz: ecca8c833a94435b5d4b2abfea2669b9e6d688976d6010cff13f8440651c3379616b202e2be101bf78ac706ca108abe2b98871984e02f8a3f295522b6e820c0e
7
+ data.tar.gz: 7877a1a4857f5b9163745057d8a9fdf6440f40272b71699fc481c889520c40cf77f07e3f48488fec7d8ef086d89cf0463900bc8257ba72447bf3164595081f55
data/irb.gemspec CHANGED
@@ -41,5 +41,5 @@ Gem::Specification.new do |spec|
41
41
 
42
42
  spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
43
43
 
44
- spec.add_dependency "reline", ">= 0.3.0"
44
+ spec.add_dependency "reline", ">= 0.3.6"
45
45
  end
@@ -11,24 +11,20 @@ module IRB
11
11
  description "Show information about IRB."
12
12
 
13
13
  def execute
14
- Class.new {
15
- def inspect
16
- str = "Ruby version: #{RUBY_VERSION}\n"
17
- str += "IRB version: #{IRB.version}\n"
18
- str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
19
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
20
- str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
21
- str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
22
- str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
23
- str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
24
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
25
- codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
26
- str += "Code page: #{codepage}\n"
27
- end
28
- str
29
- end
30
- alias_method :to_s, :inspect
31
- }.new
14
+ str = "Ruby version: #{RUBY_VERSION}\n"
15
+ str += "IRB version: #{IRB.version}\n"
16
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
17
+ str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
18
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19
+ str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
20
+ str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
21
+ str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
22
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
23
+ codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
24
+ str += "Code page: #{codepage}\n"
25
+ end
26
+ puts str
27
+ nil
32
28
  end
33
29
  end
34
30
  end
data/lib/irb/cmd/nop.rb CHANGED
@@ -30,23 +30,19 @@ module IRB
30
30
  end
31
31
  end
32
32
 
33
- def self.execute(conf, *opts, **kwargs, &block)
34
- command = new(conf)
33
+ def self.execute(irb_context, *opts, **kwargs, &block)
34
+ command = new(irb_context)
35
35
  command.execute(*opts, **kwargs, &block)
36
36
  rescue CommandArgumentError => e
37
37
  puts e.message
38
38
  end
39
39
 
40
- def initialize(conf)
41
- @irb_context = conf
40
+ def initialize(irb_context)
41
+ @irb_context = irb_context
42
42
  end
43
43
 
44
44
  attr_reader :irb_context
45
45
 
46
- def irb
47
- @irb_context.irb
48
- end
49
-
50
46
  def execute(*opts)
51
47
  #nop
52
48
  end
@@ -14,7 +14,7 @@ module IRB
14
14
  def execute(*args)
15
15
  commands_info = IRB::ExtendCommandBundle.all_commands_info
16
16
  commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
17
- longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length
17
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
18
18
 
19
19
  output = StringIO.new
20
20
 
@@ -58,9 +58,9 @@ module IRB
58
58
  tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
59
59
  code = lines[0..lnum].join
60
60
  prev_tokens.concat chunk
61
- continue = lex.process_continue(prev_tokens)
62
- code_block_open = lex.check_code_block(code, prev_tokens)
63
- if !continue && !code_block_open
61
+ continue = lex.should_continue?(prev_tokens)
62
+ syntax = lex.check_code_syntax(code)
63
+ if !continue && syntax == :valid
64
64
  return first_line + lnum
65
65
  end
66
66
  end
@@ -218,7 +218,7 @@ module IRB
218
218
  else
219
219
  sym = $1
220
220
  candidates = Symbol.all_symbols.collect do |s|
221
- ":" + s.id2name.encode(Encoding.default_external)
221
+ s.inspect
222
222
  rescue EncodingError
223
223
  # ignore
224
224
  end
@@ -233,7 +233,7 @@ module IRB
233
233
  if doc_namespace
234
234
  candidates.find { |i| i == receiver }
235
235
  else
236
- candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
236
+ candidates.grep(/^#{Regexp.quote(receiver)}/).collect{|e| "::" + e}
237
237
  end
238
238
 
239
239
  when /^([A-Z].*)::([^:.]*)$/
data/lib/irb/context.rb CHANGED
@@ -8,6 +8,7 @@ require_relative "workspace"
8
8
  require_relative "inspector"
9
9
  require_relative "input-method"
10
10
  require_relative "output-method"
11
+ require_relative "history"
11
12
 
12
13
  module IRB
13
14
  # A class that wraps the current state of the irb session, including the
@@ -151,6 +152,27 @@ module IRB
151
152
  @command_aliases = IRB.conf[:COMMAND_ALIASES]
152
153
  end
153
154
 
155
+ def save_history=(val)
156
+ IRB.conf[:SAVE_HISTORY] = val
157
+ if val
158
+ (IRB.conf[:MAIN_CONTEXT] || self).init_save_history
159
+ end
160
+ end
161
+
162
+ def save_history
163
+ IRB.conf[:SAVE_HISTORY]
164
+ end
165
+
166
+ # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
167
+ def history_file
168
+ IRB.conf[:HISTORY_FILE]
169
+ end
170
+
171
+ # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
172
+ def history_file=(hist)
173
+ IRB.conf[:HISTORY_FILE] = hist
174
+ end
175
+
154
176
  # The top-level workspace, see WorkSpace#main
155
177
  def main
156
178
  @workspace.main
@@ -473,28 +495,31 @@ module IRB
473
495
  @inspect_mode
474
496
  end
475
497
 
476
- def evaluate(line, line_no, exception: nil) # :nodoc:
498
+ def evaluate(line, line_no) # :nodoc:
477
499
  @line_no = line_no
478
- if exception
479
- line_no -= 1
480
- line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
481
- @workspace.local_variable_set(:_, exception)
482
- end
500
+ result = nil
483
501
 
484
- # Transform a non-identifier alias (@, $) or keywords (next, break)
485
- command, args = line.split(/\s/, 2)
486
- if original = command_aliases[command.to_sym]
487
- line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
488
- command = original
502
+ if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
503
+ IRB.set_measure_callback
489
504
  end
490
505
 
491
- # Hook command-specific transformation
492
- command_class = ExtendCommandBundle.load_command(command)
493
- if command_class&.respond_to?(:transform_args)
494
- line = "#{command} #{command_class.transform_args(args)}"
506
+ if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
507
+ last_proc = proc do
508
+ result = @workspace.evaluate(line, irb_path, line_no)
509
+ end
510
+ IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item|
511
+ _name, callback, arg = item
512
+ proc do
513
+ callback.(self, line, line_no, arg) do
514
+ chain.call
515
+ end
516
+ end
517
+ end.call
518
+ else
519
+ result = @workspace.evaluate(line, irb_path, line_no)
495
520
  end
496
521
 
497
- set_last_value(@workspace.evaluate(line, irb_path, line_no))
522
+ set_last_value(result)
498
523
  end
499
524
 
500
525
  def inspect_last_value # :nodoc:
@@ -551,5 +576,11 @@ module IRB
551
576
  command = command_aliases.fetch(command.to_sym, command)
552
577
  ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
553
578
  end
579
+
580
+ def init_save_history# :nodoc:
581
+ unless (class<<@io;self;end).include?(HistorySavingAbility)
582
+ @io.extend(HistorySavingAbility)
583
+ end
584
+ end
554
585
  end
555
586
  end
@@ -40,14 +40,14 @@ module IRB # :nodoc:
40
40
  #
41
41
  # If +no+ is +nil+, execution result history isn't used (default).
42
42
  #
43
- # History values are available via <code>__</code> variable, see
44
- # IRB::History.
43
+ # EvalHistory values are available via <code>__</code> variable, see
44
+ # IRB::EvalHistory.
45
45
  def eval_history=(no)
46
46
  if no
47
47
  if defined?(@eval_history) && @eval_history
48
48
  @eval_history_values.size(no)
49
49
  else
50
- @eval_history_values = History.new(no)
50
+ @eval_history_values = EvalHistory.new(no)
51
51
  IRB.conf[:__TMP__EHV__] = @eval_history_values
52
52
  @workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
53
53
  IRB.conf.delete(:__TMP_EHV__)
@@ -89,7 +89,7 @@ module IRB # :nodoc:
89
89
  # __[1]
90
90
  # # => 10
91
91
  #
92
- class History
92
+ class EvalHistory
93
93
 
94
94
  def initialize(size = 16) # :nodoc:
95
95
  @size = size
@@ -42,6 +42,7 @@ module IRB # :nodoc:
42
42
  #
43
43
  # See Irb#suspend_input_method for more information.
44
44
  def source_file(path)
45
+ irb = irb_context.irb
45
46
  irb.suspend_name(path, File.basename(path)) do
46
47
  FileInputMethod.open(path) do |io|
47
48
  irb.suspend_input_method(io) do
@@ -66,6 +67,7 @@ module IRB # :nodoc:
66
67
  #
67
68
  # See Irb#suspend_input_method for more information.
68
69
  def load_file(path, priv = nil)
70
+ irb = irb_context.irb
69
71
  irb.suspend_name(path, File.basename(path)) do
70
72
 
71
73
  if priv
@@ -289,7 +289,7 @@ module IRB # :nodoc:
289
289
  alias_method to, from
290
290
  }
291
291
  else
292
- Kernel.print "irb: warn: can't alias #{to} from #{from}.\n"
292
+ Kernel.warn "irb: warn: can't alias #{to} from #{from}.\n"
293
293
  end
294
294
  end
295
295
 
@@ -316,10 +316,9 @@ module IRB # :nodoc:
316
316
  CE = ContextExtender # :nodoc:
317
317
 
318
318
  @EXTEND_COMMANDS = [
319
- [:eval_history=, "ext/history.rb"],
319
+ [:eval_history=, "ext/eval_history.rb"],
320
320
  [:use_tracer=, "ext/tracer.rb"],
321
321
  [:use_loader=, "ext/use-loader.rb"],
322
- [:save_history=, "ext/save-history.rb"],
323
322
  ]
324
323
 
325
324
  # Installs the default context extensions as irb commands:
@@ -327,7 +326,6 @@ module IRB # :nodoc:
327
326
  # Context#eval_history=:: +irb/ext/history.rb+
328
327
  # Context#use_tracer=:: +irb/ext/tracer.rb+
329
328
  # Context#use_loader=:: +irb/ext/use-loader.rb+
330
- # Context#save_history=:: +irb/ext/save-history.rb+
331
329
  def self.install_extend_commands
332
330
  for args in @EXTEND_COMMANDS
333
331
  def_extend_command(*args)
@@ -1,55 +1,4 @@
1
- # frozen_string_literal: false
2
- #
3
- # save-history.rb -
4
- # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5
- #
6
-
7
1
  module IRB
8
- module HistorySavingAbility # :nodoc:
9
- end
10
-
11
- class Context
12
- def init_save_history# :nodoc:
13
- unless (class<<@io;self;end).include?(HistorySavingAbility)
14
- @io.extend(HistorySavingAbility)
15
- end
16
- end
17
-
18
- # A copy of the default <code>IRB.conf[:SAVE_HISTORY]</code>
19
- def save_history
20
- IRB.conf[:SAVE_HISTORY]
21
- end
22
-
23
- remove_method(:save_history=) if method_defined?(:save_history=)
24
- # Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls
25
- # #init_save_history with this context.
26
- #
27
- # Will store the number of +val+ entries of history in the #history_file
28
- #
29
- # Add the following to your +.irbrc+ to change the number of history
30
- # entries stored to 1000:
31
- #
32
- # IRB.conf[:SAVE_HISTORY] = 1000
33
- def save_history=(val)
34
- IRB.conf[:SAVE_HISTORY] = val
35
- if val
36
- main_context = IRB.conf[:MAIN_CONTEXT]
37
- main_context = self unless main_context
38
- main_context.init_save_history
39
- end
40
- end
41
-
42
- # A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
43
- def history_file
44
- IRB.conf[:HISTORY_FILE]
45
- end
46
-
47
- # Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
48
- def history_file=(hist)
49
- IRB.conf[:HISTORY_FILE] = hist
50
- end
51
- end
52
-
53
2
  module HistorySavingAbility # :nodoc:
54
3
  def HistorySavingAbility.extended(obj)
55
4
  IRB.conf[:AT_EXIT].push proc{obj.save_history}
@@ -4,7 +4,6 @@
4
4
  # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5
5
  #
6
6
 
7
- require_relative 'src_encoding'
8
7
  require_relative 'completion'
9
8
  require 'io/console'
10
9
  require 'reline'
@@ -256,8 +255,6 @@ module IRB
256
255
  end
257
256
 
258
257
  class RelineInputMethod < InputMethod
259
- include Reline
260
-
261
258
  # Creates a new input method object using Reline
262
259
  def initialize
263
260
  IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)
@@ -270,9 +267,7 @@ module IRB
270
267
  @stdin = ::IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
271
268
  @stdout = ::IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
272
269
 
273
- if Reline.respond_to?("basic_word_break_characters=")
274
- Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
275
- end
270
+ Reline.basic_word_break_characters = IRB::InputCompletor::BASIC_WORD_BREAK_CHARACTERS
276
271
  Reline.completion_append_character = nil
277
272
  Reline.completer_quote_characters = ''
278
273
  Reline.completion_proc = IRB::InputCompletor::CompletionProc
@@ -401,10 +396,10 @@ module IRB
401
396
  mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
402
397
  message = "Press #{mod_key}+d to read the full document"
403
398
  contents = [message] + doc.accept(formatter).split("\n")
404
- contents = contents.take(preferred_dialog_height) if respond_to?(:preferred_dialog_height)
399
+ contents = contents.take(Reline.preferred_dialog_height)
405
400
 
406
401
  y = cursor_pos_to_render.y
407
- DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
402
+ Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
408
403
  }
409
404
 
410
405
  # Reads the next line from this input method.
@@ -415,8 +410,8 @@ module IRB
415
410
  Reline.output = @stdout
416
411
  Reline.prompt_proc = @prompt_proc
417
412
  Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
418
- if l = readmultiline(@prompt, false, &@check_termination_proc)
419
- HISTORY.push(l) if !l.empty?
413
+ if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
414
+ Reline::HISTORY.push(l) if !l.empty?
420
415
  @line[@line_no += 1] = l + "\n"
421
416
  else
422
417
  @eof = true
@@ -458,11 +453,7 @@ module IRB
458
453
  def inspect
459
454
  config = Reline::Config.new
460
455
  str = "RelineInputMethod with Reline #{Reline::VERSION}"
461
- if config.respond_to?(:inputrc_path)
462
- inputrc_path = File.expand_path(config.inputrc_path)
463
- else
464
- inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
465
- end
456
+ inputrc_path = File.expand_path(config.inputrc_path)
466
457
  str += " and #{inputrc_path}" if File.exist?(inputrc_path)
467
458
  str
468
459
  end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+ module IRB
3
+ module NestingParser
4
+ IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
5
+
6
+ # Scan each token and call the given block with array of token and other information for parsing
7
+ def self.scan_opens(tokens)
8
+ opens = []
9
+ pending_heredocs = []
10
+ first_token_on_line = true
11
+ tokens.each do |t|
12
+ skip = false
13
+ last_tok, state, args = opens.last
14
+ case state
15
+ when :in_unquoted_symbol
16
+ unless IGNORE_TOKENS.include?(t.event)
17
+ opens.pop
18
+ skip = true
19
+ end
20
+ when :in_lambda_head
21
+ opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
22
+ when :in_method_head
23
+ unless IGNORE_TOKENS.include?(t.event)
24
+ next_args = []
25
+ body = nil
26
+ if args.include?(:receiver)
27
+ case t.event
28
+ when :on_lparen, :on_ivar, :on_gvar, :on_cvar
29
+ # def (receiver). | def @ivar. | def $gvar. | def @@cvar.
30
+ next_args << :dot
31
+ when :on_kw
32
+ case t.tok
33
+ when 'self', 'true', 'false', 'nil'
34
+ # def self(arg) | def self.
35
+ next_args.push(:arg, :dot)
36
+ else
37
+ # def if(arg)
38
+ skip = true
39
+ next_args << :arg
40
+ end
41
+ when :on_op, :on_backtick
42
+ # def +(arg)
43
+ skip = true
44
+ next_args << :arg
45
+ when :on_ident, :on_const
46
+ # def a(arg) | def a.
47
+ next_args.push(:arg, :dot)
48
+ end
49
+ end
50
+ if args.include?(:dot)
51
+ # def receiver.name
52
+ next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::')
53
+ end
54
+ if args.include?(:name)
55
+ if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event)
56
+ # def name(arg) | def receiver.name(arg)
57
+ next_args << :arg
58
+ skip = true
59
+ end
60
+ end
61
+ if args.include?(:arg)
62
+ case t.event
63
+ when :on_nl, :on_semicolon
64
+ # def recever.f;
65
+ body = :normal
66
+ when :on_lparen
67
+ # def recever.f()
68
+ next_args << :eq
69
+ else
70
+ if t.event == :on_op && t.tok == '='
71
+ # def receiver.f =
72
+ body = :oneliner
73
+ else
74
+ # def recever.f arg
75
+ next_args << :arg_without_paren
76
+ end
77
+ end
78
+ end
79
+ if args.include?(:eq)
80
+ if t.event == :on_op && t.tok == '='
81
+ body = :oneliner
82
+ else
83
+ body = :normal
84
+ end
85
+ end
86
+ if args.include?(:arg_without_paren)
87
+ if %i[on_semicolon on_nl].include?(t.event)
88
+ # def f a;
89
+ body = :normal
90
+ else
91
+ # def f a, b
92
+ next_args << :arg_without_paren
93
+ end
94
+ end
95
+ if body == :oneliner
96
+ opens.pop
97
+ elsif body
98
+ opens[-1] = [last_tok, nil]
99
+ else
100
+ opens[-1] = [last_tok, :in_method_head, next_args]
101
+ end
102
+ end
103
+ when :in_for_while_until_condition
104
+ if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do')
105
+ skip = true if t.event == :on_kw && t.tok == 'do'
106
+ opens[-1] = [last_tok, nil]
107
+ end
108
+ end
109
+
110
+ unless skip
111
+ case t.event
112
+ when :on_kw
113
+ case t.tok
114
+ when 'begin', 'class', 'module', 'do', 'case'
115
+ opens << [t, nil]
116
+ when 'end'
117
+ opens.pop
118
+ when 'def'
119
+ opens << [t, :in_method_head, [:receiver, :name]]
120
+ when 'if', 'unless'
121
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
122
+ opens << [t, nil]
123
+ end
124
+ when 'while', 'until'
125
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
126
+ opens << [t, :in_for_while_until_condition]
127
+ end
128
+ when 'ensure', 'rescue'
129
+ unless t.state.allbits?(Ripper::EXPR_LABEL)
130
+ opens.pop
131
+ opens << [t, nil]
132
+ end
133
+ when 'elsif', 'else', 'when'
134
+ opens.pop
135
+ opens << [t, nil]
136
+ when 'for'
137
+ opens << [t, :in_for_while_until_condition]
138
+ when 'in'
139
+ if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line
140
+ opens.pop
141
+ opens << [t, nil]
142
+ end
143
+ end
144
+ when :on_tlambda
145
+ opens << [t, :in_lambda_head]
146
+ when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
147
+ opens << [t, nil]
148
+ when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
149
+ opens.pop
150
+ when :on_heredoc_beg
151
+ pending_heredocs << t
152
+ when :on_heredoc_end
153
+ opens.pop
154
+ when :on_backtick
155
+ opens << [t, nil] if t.state.allbits?(Ripper::EXPR_BEG)
156
+ when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg
157
+ opens << [t, nil]
158
+ when :on_tstring_end, :on_regexp_end, :on_label_end
159
+ opens.pop
160
+ when :on_symbeg
161
+ if t.tok == ':'
162
+ opens << [t, :in_unquoted_symbol]
163
+ else
164
+ opens << [t, nil]
165
+ end
166
+ end
167
+ end
168
+ if t.event == :on_nl || t.event == :on_semicolon
169
+ first_token_on_line = true
170
+ elsif t.event != :on_sp
171
+ first_token_on_line = false
172
+ end
173
+ if pending_heredocs.any? && t.tok.include?("\n")
174
+ pending_heredocs.reverse_each { |t| opens << [t, nil] }
175
+ pending_heredocs = []
176
+ end
177
+ yield t, opens if block_given?
178
+ end
179
+ opens.map(&:first) + pending_heredocs.reverse
180
+ end
181
+
182
+ def self.open_tokens(tokens)
183
+ # scan_opens without block will return a list of open tokens at last token position
184
+ scan_opens(tokens)
185
+ end
186
+
187
+ # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line.
188
+ # Example code
189
+ # ["hello
190
+ # world"+(
191
+ # First line
192
+ # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]]
193
+ # prev_opens: []
194
+ # next_tokens: [lbracket, tstring_beg]
195
+ # min_depth: 0 (minimum at beginning of line)
196
+ # Second line
197
+ # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']]
198
+ # prev_opens: [lbracket, tstring_beg]
199
+ # next_tokens: [lbracket, lparen]
200
+ # min_depth: 1 (minimum just after tstring_end)
201
+ def self.parse_by_line(tokens)
202
+ line_tokens = []
203
+ prev_opens = []
204
+ min_depth = 0
205
+ output = []
206
+ last_opens = scan_opens(tokens) do |t, opens|
207
+ depth = t == opens.last&.first ? opens.size - 1 : opens.size
208
+ min_depth = depth if depth < min_depth
209
+ if t.tok.include?("\n")
210
+ t.tok.each_line do |line|
211
+ line_tokens << [t, line]
212
+ next if line[-1] != "\n"
213
+ next_opens = opens.map(&:first)
214
+ output << [line_tokens, prev_opens, next_opens, min_depth]
215
+ prev_opens = next_opens
216
+ min_depth = prev_opens.size
217
+ line_tokens = []
218
+ end
219
+ else
220
+ line_tokens << [t, t.tok]
221
+ end
222
+ end
223
+ output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
224
+ output
225
+ end
226
+ end
227
+ end