@@ -27,21 +27,33 @@ class CodeLine
2727 # Returns an array of CodeLine objects
2828 # from the source string
2929 def self . from_source ( source )
30- tokens_for_line = LexAll . new ( source : source ) . each_with_object ( Hash . new { |h , k | h [ k ] = [ ] } ) { |token , hash | hash [ token . line ] << token }
30+ ast , tokens = Prism . parse_lex ( source ) . value
31+ visitor = Visitor . new
32+ visitor . visit ( ast )
33+ tokens . sort_by! { |token , _state | token . location . start_line }
34+
35+ prev_token = nil
36+ tokens . map! do |token , _state |
37+ prev_token = Token . new ( token , prev_token , visitor )
38+ end
39+
40+ tokens_for_line = tokens . each_with_object ( Hash . new { |h , k | h [ k ] = [ ] } ) { |token , hash | hash [ token . line ] << token }
3141 source . lines . map . with_index do |line , index |
3242 CodeLine . new (
3343 line : line ,
3444 index : index ,
35- tokens : tokens_for_line [ index + 1 ]
45+ tokens : tokens_for_line [ index + 1 ] ,
46+ consecutive : visitor . consecutive_lines . include? ( index + 1 )
3647 )
3748 end
3849 end
3950
4051 attr_reader :line , :index , :tokens , :line_number , :indent
41- def initialize ( line :, index :, tokens :)
52+ def initialize ( line :, index :, tokens :, consecutive : )
4253 @tokens = tokens
4354 @line = line
4455 @index = index
56+ @consecutive = consecutive
4557 @original = line
4658 @line_number = @index + 1
4759 strip_line = line . dup
@@ -150,91 +162,36 @@ def <=>(other)
150162 index <=> other . index
151163 end
152164
153- # [Not stable API]
154- #
155- # Lines that have a `on_ignored_nl` type token and NOT
156- # a `BEG` type seem to be a good proxy for the ability
157- # to join multiple lines into one.
158- #
159- # This predicate method is used to determine when those
160- # two criteria have been met.
161- #
162- # The one known case this doesn't handle is:
163- #
164- # Ripper.lex <<~EOM
165- # a &&
166- # b ||
167- # c
168- # EOM
169- #
170- # For some reason this introduces `on_ignore_newline` but with BEG type
171- def ignore_newline_not_beg?
172- @ignore_newline_not_beg
165+ # Can this line be logically joined together
166+ # with the following line? Determined by walking
167+ # the AST
168+ def consecutive?
169+ @consecutive
173170 end
174171
175- # Determines if the given line has a trailing slash
172+ # Determines if the given line has a trailing slash.
173+ # Simply check if the line contains a backslash after
174+ # the content of the last token.
176175 #
177176 # lines = CodeLine.from_source(<<~EOM)
178177 # it "foo" \
179178 # EOM
180179 # expect(lines.first.trailing_slash?).to eq(true)
181180 #
182181 def trailing_slash?
183- last = @tokens . last
184-
185- # Older versions of prism diverged slightly from Ripper in compatibility mode
186- case last &.type
187- when :on_sp
188- last . value == TRAILING_SLASH
189- when :on_tstring_end
190- true
191- else
192- false
193- end
182+ return unless ( last = @tokens . last )
183+ @line . byteindex ( TRAILING_SLASH , last . location . end_column ) != nil
194184 end
195185
196- # Endless method detection
197- #
198- # From https://github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab
199- # Detecting a "oneliner" seems to need a state machine.
200- # This can be done by looking mostly at the "state" (last value):
201- #
202- # ENDFN -> BEG (token = '=' ) -> END
203- #
204186 private def set_kw_end
205- oneliner_count = 0
206- in_oneliner_def = nil
207-
208187 kw_count = 0
209188 end_count = 0
210189
211- @ignore_newline_not_beg = false
212190 @tokens . each do |token |
213191 kw_count += 1 if token . is_kw?
214192 end_count += 1 if token . is_end?
215-
216- if token . type == :on_ignored_nl
217- @ignore_newline_not_beg = !token . expr_beg?
218- end
219-
220- if in_oneliner_def . nil?
221- in_oneliner_def = :ENDFN if token . state . allbits? ( Ripper ::EXPR_ENDFN )
222- elsif token . state . allbits? ( Ripper ::EXPR_ENDFN )
223- # Continue
224- elsif token . state . allbits? ( Ripper ::EXPR_BEG )
225- in_oneliner_def = :BODY if token . value == "="
226- elsif token . state . allbits? ( Ripper ::EXPR_END )
227- # We found an endless method, count it
228- oneliner_count += 1 if in_oneliner_def == :BODY
229-
230- in_oneliner_def = nil
231- else
232- in_oneliner_def = nil
233- end
234193 end
235194
236- kw_count -= oneliner_count
237-
238195 @is_kw = ( kw_count - end_count ) > 0
239196 @is_end = ( end_count - kw_count ) > 0
240197 end
0 commit comments