irb 1.1.0.pre.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +55 -0
  4. data/exe/irb +11 -0
  5. data/irb.gemspec +29 -0
  6. data/lib/irb.rb +855 -0
  7. data/lib/irb/cmd/chws.rb +34 -0
  8. data/lib/irb/cmd/fork.rb +39 -0
  9. data/lib/irb/cmd/help.rb +46 -0
  10. data/lib/irb/cmd/load.rb +67 -0
  11. data/lib/irb/cmd/nop.rb +39 -0
  12. data/lib/irb/cmd/pushws.rb +41 -0
  13. data/lib/irb/cmd/subirb.rb +43 -0
  14. data/lib/irb/color.rb +218 -0
  15. data/lib/irb/completion.rb +339 -0
  16. data/lib/irb/context.rb +459 -0
  17. data/lib/irb/ext/change-ws.rb +46 -0
  18. data/lib/irb/ext/history.rb +120 -0
  19. data/lib/irb/ext/loader.rb +129 -0
  20. data/lib/irb/ext/multi-irb.rb +265 -0
  21. data/lib/irb/ext/save-history.rb +117 -0
  22. data/lib/irb/ext/tracer.rb +72 -0
  23. data/lib/irb/ext/use-loader.rb +77 -0
  24. data/lib/irb/ext/workspaces.rb +67 -0
  25. data/lib/irb/extend-command.rb +328 -0
  26. data/lib/irb/frame.rb +81 -0
  27. data/lib/irb/help.rb +37 -0
  28. data/lib/irb/init.rb +312 -0
  29. data/lib/irb/input-method.rb +298 -0
  30. data/lib/irb/inspector.rb +142 -0
  31. data/lib/irb/lc/.document +4 -0
  32. data/lib/irb/lc/error.rb +32 -0
  33. data/lib/irb/lc/help-message +50 -0
  34. data/lib/irb/lc/ja/encoding_aliases.rb +11 -0
  35. data/lib/irb/lc/ja/error.rb +31 -0
  36. data/lib/irb/lc/ja/help-message +52 -0
  37. data/lib/irb/locale.rb +182 -0
  38. data/lib/irb/magic-file.rb +38 -0
  39. data/lib/irb/notifier.rb +232 -0
  40. data/lib/irb/output-method.rb +92 -0
  41. data/lib/irb/ruby-lex.rb +492 -0
  42. data/lib/irb/ruby-token.rb +267 -0
  43. data/lib/irb/slex.rb +282 -0
  44. data/lib/irb/src_encoding.rb +7 -0
  45. data/lib/irb/version.rb +17 -0
  46. data/lib/irb/workspace.rb +190 -0
  47. data/lib/irb/ws-for-case-2.rb +15 -0
  48. data/lib/irb/xmp.rb +170 -0
  49. metadata +133 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: false
2
+ module IRB
3
+ class << (MagicFile = Object.new)
4
+ # see parser_magic_comment in parse.y
5
+ ENCODING_SPEC_RE = %r"coding\s*[=:]\s*([[:alnum:]\-_]+)"
6
+
7
+ def open(path)
8
+ io = File.open(path, 'rb')
9
+ line = io.gets
10
+ line = io.gets if line[0,2] == "#!"
11
+ encoding = detect_encoding(line)
12
+ internal_encoding = encoding
13
+ encoding ||= IRB.default_src_encoding
14
+ io.rewind
15
+ io.set_encoding(encoding, internal_encoding)
16
+
17
+ if block_given?
18
+ begin
19
+ return (yield io)
20
+ ensure
21
+ io.close
22
+ end
23
+ else
24
+ return io
25
+ end
26
+ end
27
+
28
+ private
29
+ def detect_encoding(line)
30
+ return unless line[0] == ?#
31
+ line = line[1..-1]
32
+ line = $1 if line[/-\*-\s*(.*?)\s*-*-$/]
33
+ return nil unless ENCODING_SPEC_RE =~ line
34
+ encoding = $1
35
+ return encoding.sub(/-(?:mac|dos|unix)/i, '')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # notifier.rb - output methods used by irb
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+ require_relative "output-method"
15
+
16
+ module IRB
17
+ # An output formatter used internally by the lexer.
18
+ module Notifier
19
+ extend Exception2MessageMapper
20
+ def_exception :ErrUndefinedNotifier,
21
+ "undefined notifier level: %d is specified"
22
+ def_exception :ErrUnrecognizedLevel,
23
+ "unrecognized notifier level: %s is specified"
24
+
25
+ # Define a new Notifier output source, returning a new CompositeNotifier
26
+ # with the given +prefix+ and +output_method+.
27
+ #
28
+ # The optional +prefix+ will be appended to all objects being inspected
29
+ # during output, using the given +output_method+ as the output source. If
30
+ # no +output_method+ is given, StdioOutputMethod will be used, and all
31
+ # expressions will be sent directly to STDOUT without any additional
32
+ # formatting.
33
+ def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
34
+ CompositeNotifier.new(prefix, output_method)
35
+ end
36
+ module_function :def_notifier
37
+
38
+ # An abstract class, or superclass, for CompositeNotifier and
39
+ # LeveledNotifier to inherit. It provides several wrapper methods for the
40
+ # OutputMethod object used by the Notifier.
41
+ class AbstractNotifier
42
+ # Creates a new Notifier object
43
+ def initialize(prefix, base_notifier)
44
+ @prefix = prefix
45
+ @base_notifier = base_notifier
46
+ end
47
+
48
+ # The +prefix+ for this Notifier, which is appended to all objects being
49
+ # inspected during output.
50
+ attr_reader :prefix
51
+
52
+ # A wrapper method used to determine whether notifications are enabled.
53
+ #
54
+ # Defaults to +true+.
55
+ def notify?
56
+ true
57
+ end
58
+
59
+ # See OutputMethod#print for more detail.
60
+ def print(*opts)
61
+ @base_notifier.print prefix, *opts if notify?
62
+ end
63
+
64
+ # See OutputMethod#printn for more detail.
65
+ def printn(*opts)
66
+ @base_notifier.printn prefix, *opts if notify?
67
+ end
68
+
69
+ # See OutputMethod#printf for more detail.
70
+ def printf(format, *opts)
71
+ @base_notifier.printf(prefix + format, *opts) if notify?
72
+ end
73
+
74
+ # See OutputMethod#puts for more detail.
75
+ def puts(*objs)
76
+ if notify?
77
+ @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
78
+ end
79
+ end
80
+
81
+ # Same as #ppx, except it uses the #prefix given during object
82
+ # initialization.
83
+ # See OutputMethod#ppx for more detail.
84
+ def pp(*objs)
85
+ if notify?
86
+ @base_notifier.ppx @prefix, *objs
87
+ end
88
+ end
89
+
90
+ # Same as #pp, except it concatenates the given +prefix+ with the #prefix
91
+ # given during object initialization.
92
+ #
93
+ # See OutputMethod#ppx for more detail.
94
+ def ppx(prefix, *objs)
95
+ if notify?
96
+ @base_notifier.ppx @prefix+prefix, *objs
97
+ end
98
+ end
99
+
100
+ # Execute the given block if notifications are enabled.
101
+ def exec_if
102
+ yield(@base_notifier) if notify?
103
+ end
104
+ end
105
+
106
+ # A class that can be used to create a group of notifier objects with the
107
+ # intent of representing a leveled notification system for irb.
108
+ #
109
+ # This class will allow you to generate other notifiers, and assign them
110
+ # the appropriate level for output.
111
+ #
112
+ # The Notifier class provides a class-method Notifier.def_notifier to
113
+ # create a new composite notifier. Using the first composite notifier
114
+ # object you create, sibling notifiers can be initialized with
115
+ # #def_notifier.
116
+ class CompositeNotifier < AbstractNotifier
117
+ # Create a new composite notifier object with the given +prefix+, and
118
+ # +base_notifier+ to use for output.
119
+ def initialize(prefix, base_notifier)
120
+ super
121
+
122
+ @notifiers = [D_NOMSG]
123
+ @level_notifier = D_NOMSG
124
+ end
125
+
126
+ # List of notifiers in the group
127
+ attr_reader :notifiers
128
+
129
+ # Creates a new LeveledNotifier in the composite #notifiers group.
130
+ #
131
+ # The given +prefix+ will be assigned to the notifier, and +level+ will
132
+ # be used as the index of the #notifiers Array.
133
+ #
134
+ # This method returns the newly created instance.
135
+ def def_notifier(level, prefix = "")
136
+ notifier = LeveledNotifier.new(self, level, prefix)
137
+ @notifiers[level] = notifier
138
+ notifier
139
+ end
140
+
141
+ # Returns the leveled notifier for this object
142
+ attr_reader :level_notifier
143
+ alias level level_notifier
144
+
145
+ # Sets the leveled notifier for this object.
146
+ #
147
+ # When the given +value+ is an instance of AbstractNotifier,
148
+ # #level_notifier is set to the given object.
149
+ #
150
+ # When an Integer is given, #level_notifier is set to the notifier at the
151
+ # index +value+ in the #notifiers Array.
152
+ #
153
+ # If no notifier exists at the index +value+ in the #notifiers Array, an
154
+ # ErrUndefinedNotifier exception is raised.
155
+ #
156
+ # An ErrUnrecognizedLevel exception is raised if the given +value+ is not
157
+ # found in the existing #notifiers Array, or an instance of
158
+ # AbstractNotifier
159
+ def level_notifier=(value)
160
+ case value
161
+ when AbstractNotifier
162
+ @level_notifier = value
163
+ when Integer
164
+ l = @notifiers[value]
165
+ Notifier.Raise ErrUndefinedNotifier, value unless l
166
+ @level_notifier = l
167
+ else
168
+ Notifier.Raise ErrUnrecognizedLevel, value unless l
169
+ end
170
+ end
171
+
172
+ alias level= level_notifier=
173
+ end
174
+
175
+ # A leveled notifier is comparable to the composite group from
176
+ # CompositeNotifier#notifiers.
177
+ class LeveledNotifier < AbstractNotifier
178
+ include Comparable
179
+
180
+ # Create a new leveled notifier with the given +base+, and +prefix+ to
181
+ # send to AbstractNotifier.new
182
+ #
183
+ # The given +level+ is used to compare other leveled notifiers in the
184
+ # CompositeNotifier group to determine whether or not to output
185
+ # notifications.
186
+ def initialize(base, level, prefix)
187
+ super(prefix, base)
188
+
189
+ @level = level
190
+ end
191
+
192
+ # The current level of this notifier object
193
+ attr_reader :level
194
+
195
+ # Compares the level of this notifier object with the given +other+
196
+ # notifier.
197
+ #
198
+ # See the Comparable module for more information.
199
+ def <=>(other)
200
+ @level <=> other.level
201
+ end
202
+
203
+ # Whether to output messages to the output method, depending on the level
204
+ # of this notifier object.
205
+ def notify?
206
+ @base_notifier.level >= self
207
+ end
208
+ end
209
+
210
+ # NoMsgNotifier is a LeveledNotifier that's used as the default notifier
211
+ # when creating a new CompositeNotifier.
212
+ #
213
+ # This notifier is used as the +zero+ index, or level +0+, for
214
+ # CompositeNotifier#notifiers, and will not output messages of any sort.
215
+ class NoMsgNotifier < LeveledNotifier
216
+ # Creates a new notifier that should not be used to output messages.
217
+ def initialize
218
+ @base_notifier = nil
219
+ @level = 0
220
+ @prefix = ""
221
+ end
222
+
223
+ # Ensures notifications are ignored, see AbstractNotifier#notify? for
224
+ # more information.
225
+ def notify?
226
+ false
227
+ end
228
+ end
229
+
230
+ D_NOMSG = NoMsgNotifier.new # :nodoc:
231
+ end
232
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # output-method.rb - output methods used by irb
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+
15
+ module IRB
16
+ # An abstract output class for IO in irb. This is mainly used internally by
17
+ # IRB::Notifier. You can define your own output method to use with Irb.new,
18
+ # or Context.new
19
+ class OutputMethod
20
+ extend Exception2MessageMapper
21
+ def_exception :NotImplementedError, "Need to define `%s'"
22
+
23
+
24
+ # Open this method to implement your own output method, raises a
25
+ # NotImplementedError if you don't define #print in your own class.
26
+ def print(*opts)
27
+ OutputMethod.Raise NotImplementedError, "print"
28
+ end
29
+
30
+ # Prints the given +opts+, with a newline delimiter.
31
+ def printn(*opts)
32
+ print opts.join(" "), "\n"
33
+ end
34
+
35
+ # Extends IO#printf to format the given +opts+ for Kernel#sprintf using
36
+ # #parse_printf_format
37
+ def printf(format, *opts)
38
+ if /(%*)%I/ =~ format
39
+ format, opts = parse_printf_format(format, opts)
40
+ end
41
+ print sprintf(format, *opts)
42
+ end
43
+
44
+ # Returns an array of the given +format+ and +opts+ to be used by
45
+ # Kernel#sprintf, if there was a successful Regexp match in the given
46
+ # +format+ from #printf
47
+ #
48
+ # %
49
+ # <flag> [#0- +]
50
+ # <minimum field width> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
51
+ # <precision>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
52
+ # #<length modifier>(hh|h|l|ll|L|q|j|z|t)
53
+ # <conversion specifier>[diouxXeEfgGcsb%]
54
+ def parse_printf_format(format, opts)
55
+ return format, opts if $1.size % 2 == 1
56
+ end
57
+
58
+ # Calls #print on each element in the given +objs+, followed by a newline
59
+ # character.
60
+ def puts(*objs)
61
+ for obj in objs
62
+ print(*obj)
63
+ print "\n"
64
+ end
65
+ end
66
+
67
+ # Prints the given +objs+ calling Object#inspect on each.
68
+ #
69
+ # See #puts for more detail.
70
+ def pp(*objs)
71
+ puts(*objs.collect{|obj| obj.inspect})
72
+ end
73
+
74
+ # Prints the given +objs+ calling Object#inspect on each and appending the
75
+ # given +prefix+.
76
+ #
77
+ # See #puts for more detail.
78
+ def ppx(prefix, *objs)
79
+ puts(*objs.collect{|obj| prefix+obj.inspect})
80
+ end
81
+
82
+ end
83
+
84
+ # A standard output printer
85
+ class StdioOutputMethod < OutputMethod
86
+ # Prints the given +opts+ to standard output, see IO#print for more
87
+ # information.
88
+ def print(*opts)
89
+ STDOUT.print(*opts)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,492 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # irb/ruby-lex.rb - ruby lexcal analyzer
4
+ # $Release Version: 0.9.6$
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+ require "ripper"
15
+
16
+ # :stopdoc:
17
+ class RubyLex
18
+
19
+ extend Exception2MessageMapper
20
+ def_exception(:TerminateLineInput, "Terminate Line Input")
21
+
22
+ def initialize
23
+ @exp_line_no = @line_no = 1
24
+ @indent = 0
25
+ @continue = false
26
+ @line = ""
27
+ @prompt = nil
28
+ end
29
+
30
+ # io functions
31
+ def set_input(io, p = nil, &block)
32
+ @io = io
33
+ if @io.respond_to?(:check_termination)
34
+ @io.check_termination do |code|
35
+ code.gsub!(/\s*\z/, '').concat("\n")
36
+ ltype, indent, continue, code_block_open = check_state(code)
37
+ if ltype or indent > 0 or continue or code_block_open
38
+ false
39
+ else
40
+ true
41
+ end
42
+ end
43
+ end
44
+ if @io.respond_to?(:dynamic_prompt)
45
+ @io.dynamic_prompt do |lines|
46
+ lines << '' if lines.empty?
47
+ result = []
48
+ lines.each_index { |i|
49
+ c = lines[0..i].map{ |l| l + "\n" }.join
50
+ ltype, indent, continue, code_block_open = check_state(c)
51
+ result << @prompt.call(ltype, indent, continue || code_block_open, @line_no + i)
52
+ }
53
+ result
54
+ end
55
+ end
56
+ if p.respond_to?(:call)
57
+ @input = p
58
+ elsif block_given?
59
+ @input = block
60
+ else
61
+ @input = Proc.new{@io.gets}
62
+ end
63
+ end
64
+
65
+ def set_prompt(p = nil, &block)
66
+ p = block if block_given?
67
+ if p.respond_to?(:call)
68
+ @prompt = p
69
+ else
70
+ @prompt = Proc.new{print p}
71
+ end
72
+ end
73
+
74
+ def set_auto_indent(context)
75
+ if @io.respond_to?(:auto_indent) and context.auto_indent_mode
76
+ @io.auto_indent do |lines, line_index, byte_pointer, is_newline|
77
+ if is_newline
78
+ md = lines[line_index - 1].match(/(\A +)/)
79
+ prev_spaces = md.nil? ? 0 : md[1].count(' ')
80
+ @tokens = Ripper.lex(lines[0..line_index].join("\n"))
81
+ depth_difference = check_newline_depth_difference
82
+ prev_spaces + depth_difference * 2
83
+ else
84
+ code = line_index.zero? ? '' : lines[0..(line_index - 1)].map{ |l| l + "\n" }.join
85
+ last_line = lines[line_index]&.byteslice(0, byte_pointer)
86
+ code += last_line if last_line
87
+ @tokens = Ripper.lex(code)
88
+ corresponding_token_depth = check_corresponding_token_depth
89
+ if corresponding_token_depth
90
+ corresponding_token_depth
91
+ else
92
+ nil
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def check_state(code)
100
+ @tokens = Ripper.lex(code)
101
+ ltype = process_literal_type
102
+ indent = process_nesting_level
103
+ continue = process_continue
104
+ code_block_open = check_code_block(code)
105
+ [ltype, indent, continue, code_block_open]
106
+ end
107
+
108
+ def prompt
109
+ if @prompt
110
+ @prompt.call(@ltype, @indent, @continue, @line_no)
111
+ end
112
+ end
113
+
114
+ def initialize_input
115
+ @ltype = nil
116
+ @indent = 0
117
+ @continue = false
118
+ @line = ""
119
+ @exp_line_no = @line_no
120
+ @code_block_open = false
121
+ end
122
+
123
+ def each_top_level_statement
124
+ initialize_input
125
+ catch(:TERM_INPUT) do
126
+ loop do
127
+ begin
128
+ prompt
129
+ unless l = lex
130
+ throw :TERM_INPUT if @line == ''
131
+ else
132
+ @line_no += l.count("\n")
133
+ next if l == "\n"
134
+ @line.concat l
135
+ if @code_block_open or @ltype or @continue or @indent > 0
136
+ next
137
+ end
138
+ end
139
+ if @line != "\n"
140
+ @line.force_encoding(@io.encoding)
141
+ yield @line, @exp_line_no
142
+ end
143
+ break if @io.eof?
144
+ @line = ''
145
+ @exp_line_no = @line_no
146
+
147
+ @indent = 0
148
+ rescue TerminateLineInput
149
+ initialize_input
150
+ prompt
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ def lex
157
+ line = @input.call
158
+ if @io.respond_to?(:check_termination)
159
+ return line # multiline
160
+ end
161
+ code = @line + (line.nil? ? '' : line)
162
+ code.gsub!(/\s*\z/, '').concat("\n")
163
+ @tokens = Ripper.lex(code)
164
+ @continue = process_continue
165
+ @code_block_open = check_code_block(code)
166
+ @indent = process_nesting_level
167
+ @ltype = process_literal_type
168
+ line
169
+ end
170
+
171
+ def process_continue
172
+ # last token is always newline
173
+ if @tokens.size >= 2 and @tokens[-2][1] == :on_regexp_end
174
+ # end of regexp literal
175
+ return false
176
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_semicolon
177
+ return false
178
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_kw and ['begin', 'else', 'ensure'].include?(@tokens[-2][2])
179
+ return false
180
+ elsif @tokens.size >= 3 and @tokens[-3][1] == :on_symbeg and @tokens[-2][1] == :on_ivar
181
+ # This is for :@a or :@1 because :@1 ends with EXPR_FNAME
182
+ return false
183
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_ivar and @tokens[-2][2] =~ /\A@\d+\z/
184
+ # This is for @1
185
+ return false
186
+ elsif @tokens.size >= 2 and @tokens[-2][1] == :on_cvar and @tokens[-1][1] == :on_int
187
+ # This is for @@1 or :@@1 and ends with on_int because it's syntax error
188
+ return false
189
+ elsif !@tokens.empty? and @tokens.last[2] == "\\\n"
190
+ return true
191
+ elsif @tokens.size >= 1 and @tokens[-1][1] == :on_heredoc_end # "EOH\n"
192
+ return false
193
+ elsif @tokens.size >= 2 and defined?(Ripper::EXPR_BEG) and @tokens[-2][3].anybits?(Ripper::EXPR_BEG | Ripper::EXPR_FNAME)
194
+ # end of literal except for regexp
195
+ return true
196
+ end
197
+ false
198
+ end
199
+
200
+ def check_code_block(code)
201
+ return true if @tokens.empty?
202
+ if @tokens.last[1] == :on_heredoc_beg
203
+ return true
204
+ end
205
+
206
+ begin # check if parser error are available
207
+ verbose, $VERBOSE = $VERBOSE, nil
208
+ case RUBY_ENGINE
209
+ when 'jruby'
210
+ JRuby.compile_ir(code)
211
+ else
212
+ RubyVM::InstructionSequence.compile(code)
213
+ end
214
+ rescue SyntaxError => e
215
+ case e.message
216
+ when /unterminated (?:string|regexp) meets end of file/
217
+ # "unterminated regexp meets end of file"
218
+ #
219
+ # example:
220
+ # /
221
+ #
222
+ # "unterminated string meets end of file"
223
+ #
224
+ # example:
225
+ # '
226
+ return true
227
+ when /syntax error, unexpected end-of-input/
228
+ # "syntax error, unexpected end-of-input, expecting keyword_end"
229
+ #
230
+ # example:
231
+ # if ture
232
+ # hoge
233
+ # if false
234
+ # fuga
235
+ # end
236
+ return true
237
+ when /syntax error, unexpected keyword_end/
238
+ # "syntax error, unexpected keyword_end"
239
+ #
240
+ # example:
241
+ # if (
242
+ # end
243
+ #
244
+ # example:
245
+ # end
246
+ return false
247
+ when /syntax error, unexpected '\.'/
248
+ # "syntax error, unexpected '.'"
249
+ #
250
+ # example:
251
+ # .
252
+ return false
253
+ when /unexpected tREGEXP_BEG/
254
+ # "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('"
255
+ #
256
+ # example:
257
+ # method / f /
258
+ return false
259
+ when /numbered parameter outside block/
260
+ # "numbered parameter outside block"
261
+ #
262
+ # example:
263
+ # :@1
264
+ return false
265
+ end
266
+ ensure
267
+ $VERBOSE = verbose
268
+ end
269
+
270
+ if defined?(Ripper::EXPR_BEG)
271
+ last_lex_state = @tokens.last[3]
272
+ if last_lex_state.allbits?(Ripper::EXPR_BEG)
273
+ return false
274
+ elsif last_lex_state.allbits?(Ripper::EXPR_DOT)
275
+ return true
276
+ elsif last_lex_state.allbits?(Ripper::EXPR_CLASS)
277
+ return true
278
+ elsif last_lex_state.allbits?(Ripper::EXPR_FNAME)
279
+ return true
280
+ elsif last_lex_state.allbits?(Ripper::EXPR_VALUE)
281
+ return true
282
+ elsif last_lex_state.allbits?(Ripper::EXPR_ARG)
283
+ return false
284
+ end
285
+ end
286
+
287
+ false
288
+ end
289
+
290
+ def process_nesting_level
291
+ indent = 0
292
+ @tokens.each_with_index { |t, index|
293
+ case t[1]
294
+ when :on_lbracket, :on_lbrace, :on_lparen
295
+ indent += 1
296
+ when :on_rbracket, :on_rbrace, :on_rparen
297
+ indent -= 1
298
+ when :on_kw
299
+ next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
300
+ case t[2]
301
+ when 'do'
302
+ if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_CMDARG)
303
+ # method_with_bock do; end
304
+ indent += 1
305
+ else
306
+ # while cond do; end # also "until" or "for"
307
+ # This "do" doesn't increment indent because "while" already
308
+ # incremented.
309
+ end
310
+ when 'def', 'case', 'for', 'begin', 'class', 'module'
311
+ indent += 1
312
+ when 'if', 'unless', 'while', 'until'
313
+ # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
314
+ indent += 1 unless t[3].allbits?(Ripper::EXPR_LABEL)
315
+ when 'end'
316
+ indent -= 1
317
+ end
318
+ end
319
+ # percent literals are not indented
320
+ }
321
+ indent
322
+ end
323
+
324
+ def check_newline_depth_difference
325
+ depth_difference = 0
326
+ @tokens.each_with_index do |t, index|
327
+ case t[1]
328
+ when :on_ignored_nl, :on_nl
329
+ if index != (@tokens.size - 1)
330
+ depth_difference = 0
331
+ end
332
+ next
333
+ when :on_sp
334
+ next
335
+ end
336
+ case t[1]
337
+ when :on_lbracket, :on_lbrace, :on_lparen
338
+ depth_difference += 1
339
+ when :on_rbracket, :on_rbrace, :on_rparen
340
+ depth_difference -= 1
341
+ when :on_kw
342
+ next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
343
+ case t[2]
344
+ when 'do'
345
+ if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_CMDARG)
346
+ # method_with_bock do; end
347
+ depth_difference += 1
348
+ else
349
+ # while cond do; end # also "until" or "for"
350
+ # This "do" doesn't increment indent because "while" already
351
+ # incremented.
352
+ end
353
+ when 'def', 'case', 'for', 'begin', 'class', 'module'
354
+ depth_difference += 1
355
+ when 'if', 'unless', 'while', 'until'
356
+ # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
357
+ unless t[3].allbits?(Ripper::EXPR_LABEL)
358
+ depth_difference += 1
359
+ end
360
+ when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in'
361
+ depth_difference += 1
362
+ end
363
+ end
364
+ end
365
+ depth_difference
366
+ end
367
+
368
+ def check_corresponding_token_depth
369
+ corresponding_token_depth = nil
370
+ is_first_spaces_of_line = true
371
+ is_first_printable_of_line = true
372
+ spaces_of_nest = []
373
+ spaces_at_line_head = 0
374
+ @tokens.each_with_index do |t, index|
375
+ corresponding_token_depth = nil
376
+ case t[1]
377
+ when :on_ignored_nl, :on_nl
378
+ spaces_at_line_head = 0
379
+ is_first_spaces_of_line = true
380
+ is_first_printable_of_line = true
381
+ next
382
+ when :on_sp
383
+ spaces_at_line_head = t[2].count(' ') if is_first_spaces_of_line
384
+ is_first_spaces_of_line = false
385
+ next
386
+ end
387
+ case t[1]
388
+ when :on_lbracket, :on_lbrace, :on_lparen
389
+ spaces_of_nest.push(spaces_at_line_head)
390
+ when :on_rbracket, :on_rbrace, :on_rparen
391
+ if is_first_printable_of_line
392
+ corresponding_token_depth = spaces_of_nest.pop
393
+ else
394
+ spaces_of_nest.pop
395
+ corresponding_token_depth = nil
396
+ end
397
+ when :on_kw
398
+ next if index > 0 and @tokens[index - 1][3].allbits?(Ripper::EXPR_FNAME)
399
+ case t[2]
400
+ when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
401
+ spaces_of_nest.push(spaces_at_line_head)
402
+ when 'if', 'unless', 'while', 'until'
403
+ # postfix if/unless/while/until/rescue must be Ripper::EXPR_LABEL
404
+ unless t[3].allbits?(Ripper::EXPR_LABEL)
405
+ spaces_of_nest.push(spaces_at_line_head)
406
+ end
407
+ when 'else', 'elsif', 'rescue', 'ensure', 'when', 'in'
408
+ corresponding_token_depth = spaces_of_nest.last
409
+ when 'end'
410
+ if is_first_printable_of_line
411
+ corresponding_token_depth = spaces_of_nest.pop
412
+ else
413
+ spaces_of_nest.pop
414
+ corresponding_token_depth = nil
415
+ end
416
+ end
417
+ end
418
+ is_first_spaces_of_line = false
419
+ is_first_printable_of_line = false
420
+ end
421
+ corresponding_token_depth
422
+ end
423
+
424
+ def check_string_literal
425
+ i = 0
426
+ start_token = []
427
+ end_type = []
428
+ while i < @tokens.size
429
+ t = @tokens[i]
430
+ case t[1]
431
+ when :on_tstring_beg
432
+ start_token << t
433
+ end_type << [:on_tstring_end, :on_label_end]
434
+ when :on_regexp_beg
435
+ start_token << t
436
+ end_type << :on_regexp_end
437
+ when :on_symbeg
438
+ acceptable_single_tokens = %i{on_ident on_const on_op on_cvar on_ivar on_gvar on_kw}
439
+ if (i + 1) < @tokens.size and acceptable_single_tokens.all?{ |t| @tokens[i + 1][1] != t }
440
+ start_token << t
441
+ end_type << :on_tstring_end
442
+ end
443
+ when :on_backtick
444
+ start_token << t
445
+ end_type << :on_tstring_end
446
+ when :on_qwords_beg, :on_words_beg, :on_qsymbols_beg, :on_symbols_beg
447
+ start_token << t
448
+ end_type << :on_tstring_end
449
+ when :on_heredoc_beg
450
+ start_token << t
451
+ end_type << :on_heredoc_end
452
+ when *end_type.last
453
+ start_token.pop
454
+ end_type.pop
455
+ end
456
+ i += 1
457
+ end
458
+ start_token.last.nil? ? '' : start_token.last
459
+ end
460
+
461
+ def process_literal_type
462
+ start_token = check_string_literal
463
+ case start_token[1]
464
+ when :on_tstring_beg
465
+ case start_token[2]
466
+ when ?" then ?"
467
+ when /^%.$/ then ?"
468
+ when /^%Q.$/ then ?"
469
+ when ?' then ?'
470
+ when /^%q.$/ then ?'
471
+ end
472
+ when :on_regexp_beg then ?/
473
+ when :on_symbeg then ?:
474
+ when :on_backtick then ?`
475
+ when :on_qwords_beg then ?]
476
+ when :on_words_beg then ?]
477
+ when :on_qsymbols_beg then ?]
478
+ when :on_symbols_beg then ?]
479
+ when :on_heredoc_beg
480
+ start_token[2] =~ /<<[-~]?(['"`])[_a-zA-Z0-9]+\1/
481
+ case $1
482
+ when ?" then ?"
483
+ when ?' then ?'
484
+ when ?` then ?`
485
+ else ?"
486
+ end
487
+ else
488
+ nil
489
+ end
490
+ end
491
+ end
492
+ # :startdoc: