irb 1.7.0 → 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
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