irb 1.1.0.pre.3

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.
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: