diff --git a/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb b/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb index 5ad8320b..0d62718e 100644 --- a/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb +++ b/lib/annotate_rb/model_annotator/file_parser/parsed_file.rb @@ -45,7 +45,7 @@ def parse annotation_start = @finder.annotation_start annotation_end = @finder.annotation_end - if @file_lines[annotation_start - 1]&.strip&.empty? + if annotation_start > 0 && @file_lines[annotation_start - 1]&.strip&.empty? annotation_start -= 1 has_leading_whitespace = true end diff --git a/spec/lib/annotate_rb/model_annotator/file_parser/parsed_file_spec.rb b/spec/lib/annotate_rb/model_annotator/file_parser/parsed_file_spec.rb new file mode 100644 index 00000000..3c3caccb --- /dev/null +++ b/spec/lib/annotate_rb/model_annotator/file_parser/parsed_file_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +RSpec.describe AnnotateRb::ModelAnnotator::FileParser::ParsedFile do + describe "#parse" do + subject { described_class.new(file_content, new_annotations, parser_klass, options).parse } + + let(:parser_klass) { AnnotateRb::ModelAnnotator::FileParser::CustomParser } + let(:options) { AnnotateRb::Options.new({}) } + let(:new_annotations) do + <<~ANNOTATIONS + # == Schema Information + # + # Table name: users + # + # id :bigint not null, primary key + # + ANNOTATIONS + end + + # Regression: when the annotation is at the very top of the file (annotation_start == 0), + # `@file_lines[annotation_start - 1]` used to wrap around to `@file_lines[-1]` (the last + # line). If the file ended with a blank line, this was mistaken for leading whitespace, + # decremented annotation_start to -1, and produced an empty `annotations_with_whitespace`. + # An empty removal string is a no-op, so old annotations were never removed and duplicated. + context "when the annotation is at the top of the file and the file ends with a blank line" do + let(:file_content) do + "# == Schema Information\n" \ + "#\n" \ + "# Table name: users\n" \ + "#\n" \ + "# id :bigint not null, primary key\n" \ + "#\n" \ + "class User < ApplicationRecord\n" \ + "end\n" \ + "\n" + end + + it "does not treat the trailing blank line as leading whitespace" do + expect(subject.has_leading_whitespace?).to eq(false) + end + + it "captures the annotation in annotations_with_whitespace" do + expect(subject.annotations_with_whitespace).not_to be_empty + expect(subject.annotations_with_whitespace).to include("# == Schema Information") + end + end + end +end