diff --git a/Gemfile b/Gemfile index 35b4ca5..8bfca51 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ gem 'minitest', '5.16.2', require: false gem 'rake', '13.0.6', require: false gem 'rdoc', '6.4.0', require: false gem 'rspec-rails', '5.1.2', require: false -gem 'rubocop', '1.53.1', require: false +gem 'rubocop', '1.60.2', require: false gem 'rubocop-rspec', '2.22.0', require: false gem 'simplecov', '0.22.0', require: false gem 'slop', '4.9.2', require: false diff --git a/README.md b/README.md index 7407fa3..7813730 100644 --- a/README.md +++ b/README.md @@ -211,9 +211,9 @@ and put a dummy `#1` marker everywhere. /** * @todo #36 Multiline text can use the same prefix in all lines or the same * amount of spaces. - So this will be added to the puzzle description. If you want to divide the - * puzzle by empty line, just add a space to that line - * + * So this will be added to the puzzle description. If you want to divide the + * puzzle by empty line, just add a backspace to that line + * \ * and continue the text after. * * This line is not part of the puzzle, because the line before contains less diff --git a/features/step_definitions/steps.rb b/features/step_definitions/steps.rb index 1c8b825..8a194bd 100644 --- a/features/step_definitions/steps.rb +++ b/features/step_definitions/steps.rb @@ -83,10 +83,6 @@ raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}" unless @stdout.include?(txt) end -Then(/^Stdout is empty$/) do - raise "STDOUT is not empty:\n#{@stdout}" unless @stdout == '' -end - Then(/^XML file "([^"]+)" matches "([^"]+)"$/) do |file, xpath| raise "File #{file} doesn't exit" unless File.exist?(file) @@ -103,14 +99,6 @@ if content.index(substring).nil? end -Then(/^Exit code is zero$/) do - raise "Non-zero exit code #{@exitstatus}" unless @exitstatus.zero? -end - -Then(/^Exit code is not zero$/) do - raise 'Zero exit code' if @exitstatus.zero? -end - When(/^I run bash with$/) do |text| FileUtils.copy_entry(@cwd, File.join(@dir, 'pdd')) @stdout = `#{text}` @@ -122,11 +110,3 @@ @stdout = `#{text}` @exitstatus = $CHILD_STATUS.exitstatus end - -Given(/^It is Unix$/) do - pending if Gem.win_platform? -end - -Given(/^It is Windows$/) do - pending unless Gem.win_platform? -end diff --git a/lib/pdd/source.rb b/lib/pdd/source.rb index 69037a3..534f4d8 100644 --- a/lib/pdd/source.rb +++ b/lib/pdd/source.rb @@ -144,13 +144,38 @@ def minutes(num, units) def tail(lines, prefix, start) return [] if lines.empty? prefix = " #{' ' * start}" if prefix.empty? # fallback to space indentation - empty_prefix = ' ' * (prefix.length + 1) - prefix = "#{prefix} " if lines[0][prefix.length, 1]&.start_with?(' ') - lines - .take_while do |t| - match_markers(t).none? && (t.start_with?(prefix) || t.start_with?(empty_prefix)) + prefix = puzzle_text_prefix(lines, prefix) + tail = lines + .take_while { |t| puzzle_text?(t, prefix) } + .map do |t| + content = t[prefix.length, t.length]&.lstrip + puzzle_empty_line?(content, '') ? '' : content + end + tail.pop if tail[-1].eql?('') + tail + end + + def puzzle_text_prefix(lines, prefix) + return prefix if lines.empty? + i = 0 + while i < lines.length + l = lines[i] unless puzzle_empty_line?(lines[i], prefix) + unless l.nil? + return l.start_with?("#{prefix} ") ? "#{prefix} " : prefix end - .map { |t| t[prefix.length, t.length]&.lstrip } + i += 1 + end + prefix + end + + def puzzle_text?(line, prefix) + return false unless match_markers(line).none? + line.start_with?(prefix) || puzzle_empty_line?(line, prefix) + end + + def puzzle_empty_line?(line, prefix) + return true if line.nil? + line.start_with?(prefix.lstrip) && line.gsub(prefix, '').chomp.strip.eql?('\\') end # @todo #75:30min Let's make it possible to fetch Subversion data diff --git a/test/test_source.rb b/test/test_source.rb index f770f1b..0eb2f07 100644 --- a/test/test_source.rb +++ b/test/test_source.rb @@ -48,7 +48,7 @@ def test_parsing assert_equal 2, list.size puzzle = list.first assert_equal '2-3', puzzle.props[:lines] - assert_equal 'привет, how are you doing?', \ + assert_equal 'привет, how are you doing?', puzzle.props[:body] assert_equal '44', puzzle.props[:ticket] assert puzzle.props[:author].nil? diff --git a/test/test_source_todo.rb b/test/test_source_todo.rb index d6c722d..600007d 100644 --- a/test/test_source_todo.rb +++ b/test/test_source_todo.rb @@ -112,6 +112,17 @@ def test_todo_colon_parsing ) end + def test_todo_backslash_escape + check_valid_puzzle( + " + // TODO #45 task description with \\ + ", + '2-2', + 'task description with \\', + '45' + ) + end + def test_multiple_todo_colon check_valid_puzzle( " @@ -150,22 +161,47 @@ def test_todo_colon_parsing_multi_line_with_empty_line ) end - # space is needed in test data in the comment - # rubocop:disable Layout/TrailingWhitespace def test_todo_colon_parsing_multi_line_with_empty_line_and_space check_valid_puzzle( ' // TODO: #46 task description - // - // second line after empty line is a part of the puzzle in case of space exists + // \ + // second line after empty line is a part of the puzzle in case of backslash exists ', '2-4', 'task description second line after empty line is a part ' \ - 'of the puzzle in case of space exists', + 'of the puzzle in case of backslash exists', '46' ) end - # rubocop:enable Layout/TrailingWhitespace + + def test_todo_colon_parsing_double_puzzle_with_empty_line + check_valid_puzzle( + ' + // TODO: #46 task description for first + // \ + // TODO: #47 task description + ', + '2-2', + 'task description for first', + '46', + 2 + ) + end + + def test_todo_colon_parsing_multi_line_random_prefix + check_valid_puzzle( + ' + ~~ + ~~ @todo #44 First + ~~ and + ~~ second + ', + '3-4', + 'First and', + '44' + ) + end def test_todo_failing_no_ticket check_invalid_puzzle( diff --git a/test_assets/puzzles/44-660e9d6f b/test_assets/puzzles/44-660e9d6f index c65f59c..f662706 100644 --- a/test_assets/puzzles/44-660e9d6f +++ b/test_assets/puzzles/44-660e9d6f @@ -2,7 +2,7 @@ // @todo #44 This puzzle // consists // of -// +// \ // two // paragraphs