diff --git a/CHANGELOG.md b/CHANGELOG.md index be01d2e..ba84cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## HEAD (unreleased) +- Support "endless" oneline method definitions for Ruby 3+ (https://github.com/zombocom/dead_end/pull/80) - Reduce timeout to 1 second (https://github.com/zombocom/dead_end/pull/79) - Logically consecutive lines (such as chained methods are now joined) (https://github.com/zombocom/dead_end/pull/78) - Output improvement for cases where the only line is an single `end` (https://github.com/zombocom/dead_end/pull/78) diff --git a/lib/dead_end/code_line.rb b/lib/dead_end/code_line.rb index ebf8a49..8718fdb 100644 --- a/lib/dead_end/code_line.rb +++ b/lib/dead_end/code_line.rb @@ -60,6 +60,8 @@ def initialize(line:, index:, lex:) end_count += 1 if lex.is_end? end + kw_count -= oneliner_method_count + @is_kw = (kw_count - end_count) > 0 @is_end = (end_count - kw_count) > 0 end @@ -195,5 +197,37 @@ def trailing_slash? last.token == TRAILING_SLASH end + + # Endless method detection + # + # From https://github.com/ruby/irb/commit/826ae909c9c93a2ddca6f9cfcd9c94dbf53d44ab + # Detecting a "oneliner" seems to need a state machine. + # This can be done by looking mostly at the "state" (last value): + # + # ENDFN -> BEG (token = '=' ) -> END + # + private def oneliner_method_count + oneliner_count = 0 + in_oneliner_def = nil + + @lex.each do |lex| + if in_oneliner_def.nil? + in_oneliner_def = :ENDFN if lex.state.allbits?(Ripper::EXPR_ENDFN) + elsif lex.state.allbits?(Ripper::EXPR_ENDFN) + # Continue + elsif lex.state.allbits?(Ripper::EXPR_BEG) + in_oneliner_def = :BODY if lex.token == "=" + elsif lex.state.allbits?(Ripper::EXPR_END) + # We found an endless method, count it + oneliner_count += 1 if in_oneliner_def == :BODY + + in_oneliner_def = nil + else + in_oneliner_def = nil + end + end + + oneliner_count + end end end diff --git a/spec/unit/code_line_spec.rb b/spec/unit/code_line_spec.rb index 29324cf..1eb1a86 100644 --- a/spec/unit/code_line_spec.rb +++ b/spec/unit/code_line_spec.rb @@ -4,6 +4,17 @@ module DeadEnd RSpec.describe CodeLine do + it "supports endless method definitions" do + skip("Unsupported ruby version") unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3") + + line = CodeLine.from_source(<<~'EOM').first + def square(x) = x * x + EOM + + expect(line.is_kw?).to be_falsey + expect(line.is_end?).to be_falsey + end + it "retains original line value, after being marked invisible" do line = CodeLine.from_source(<<~'EOM').first puts "lol"