From 5c464ebb22e70239bb2fec6e95feb9be3b9cf048 Mon Sep 17 00:00:00 2001 From: Adrian Hirt <13788379+Adrian-Hirt@users.noreply.github.com> Date: Sun, 28 Apr 2024 00:25:59 +0200 Subject: [PATCH] Fix `block_keyword` method returning keywords used as arguments of method call (#505) * Fix `block_keyword` method returning keywords used as arguments of methods * Fix linter errors * Replace block keyword regex by custom regex to correctly handle newlines Co-authored-by: Maxime Lapointe --------- Co-authored-by: Maxime Lapointe --- .../ruby_extraction/chunk_extractor.rb | 6 +- .../ruby_extraction/chunk_extractor_spec.rb | 69 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/lib/haml_lint/ruby_extraction/chunk_extractor.rb b/lib/haml_lint/ruby_extraction/chunk_extractor.rb index a565cc03..4c855919 100644 --- a/lib/haml_lint/ruby_extraction/chunk_extractor.rb +++ b/lib/haml_lint/ruby_extraction/chunk_extractor.rb @@ -17,6 +17,10 @@ class ChunkExtractor ::Haml::Parser.new('', {}) end + # HAML strips newlines when handling multi-line statements (using pipes or trailing comma) + # We don't. So the regex must be fixed to correctly detect the start of the string. + BLOCK_KEYWORD_REGEX = Regexp.new(Haml::Parser::BLOCK_KEYWORD_REGEX.source.sub('^', '\A')) + def initialize(document, script_output_prefix:) @document = document @script_output_prefix = script_output_prefix @@ -692,7 +696,7 @@ def self.block_keyword(code) return keyword end - return unless keyword = code.scan(Haml::Parser::BLOCK_KEYWORD_REGEX)[0] + return unless keyword = code.scan(BLOCK_KEYWORD_REGEX)[0] keyword[0] || keyword[1] end end diff --git a/spec/haml_lint/ruby_extraction/chunk_extractor_spec.rb b/spec/haml_lint/ruby_extraction/chunk_extractor_spec.rb index 92e4c578..15f4d4a9 100644 --- a/spec/haml_lint/ruby_extraction/chunk_extractor_spec.rb +++ b/spec/haml_lint/ruby_extraction/chunk_extractor_spec.rb @@ -213,4 +213,73 @@ def do_test end end end + + describe '.block_keyword' do + it 'should work for empty strings' do + expect(described_class.block_keyword('')).to eq(nil) + expect(described_class.block_keyword(' ')).to eq(nil) + end + + it 'should extract keywords from simple input' do + # Cases where we can use ` value` + %w[if unless case].each do |keyword| + input = <<~HAML + - #{keyword} foobar + = foo + HAML + + expect(described_class.block_keyword(input)).to eq(keyword) + end + + # Case for the `begin` keyword + begin_input = <<~HAML + - begin + HAML + + expect(described_class.block_keyword(begin_input)).to eq('begin') + + # Case for the `for` keyword + for_input = <<~HAML + for user in User.all do + HAML + + expect(described_class.block_keyword(for_input)).to eq('for') + + # Cases where we can use ` value do` + %w[until while].each do |keyword| + input = <<~HAML + #{keyword} foobar do + HAML + + expect(described_class.block_keyword(input)).to eq(keyword) + end + end + + it 'should not raise exception when keyword is used as keyword argument' do + # Everything on single line, should work + input_single_line = '= helper foo: true, bar: true, if: false' + expect(described_class.block_keyword(input_single_line)).to eq(nil) + + # Keyword as symbol not first on new line should work + input_multiline1 = <<~HAML + = helper foo: true, + bar: true, if: false + HAML + expect(described_class.block_keyword(input_multiline1)).to eq(nil) + + # Keyword as symbol first on new line should also work + input_multiline2 = <<~HAML + = helper foo: true, bar: true, + if: false + HAML + expect(described_class.block_keyword(input_multiline2)).to eq(nil) + + # Testing with another keyword + input_multiline3 = <<~HAML + = helper foo: true, bar: true, + for: User.first + HAML + expect(described_class.block_keyword(input_multiline3)).to eq(nil) + end + end end