Repository: LadybirdBrowser/ladybird
File: Meta/check-newlines-at-eof.py (Python)

Code before:
    blank_lines_at_eof_errors = []

    did_fail = False
    for filename in find_files_here_or_argv():
        with open(filename, "r") as f:
            f.seek(0, os.SEEK_END)

            file_size = f.tell()

            no_newline_local = False
            if file_size == 0:
                no_newline_local = True
            else:
                f.seek(file_size - 1, os.SEEK_SET)
                if f.read(1) != "\n":
                    no_newline_local = True

            if no_newline_local:
                did_fail = True
                no_newline_at_eof_errors.append(filename)
                continue

            # note: here we have implicitly seeked to the end of the file

            while True:
                current_pos = f.tell()
                if current_pos < 2:
                    break

                f.seek(current_pos - 2, os.SEEK_SET)
                char = f.read(1)
                if not char.isspace():
                    break
                if char == "\n":
                    did_fail = True
                    blank_lines_at_eof_errors.append(filename)
                    break

    if no_newline_at_eof_errors:
        print("Files with no newline at the end:", " ".join(no_newline_at_eof_errors))
    if blank_lines_at_eof_errors:
        print("Files that have blank lines at the end:", " ".join(blank_lines_at_eof_errors))

    if did_fail:
        sys.exit(1)


if __name__ == "__main__":
    os.chdir(os.path.dirname(__file__) + "/..")
    run()


Review comment (bug):
Thanks, this definitely fixes that issue, but personally find the new logic even more difficult to follow around the file pointer for the 'blank lines' case. I had an attempt of writing a version that just reads the entire file rather than the logic of seeking around. I got something like:

```
def run():
    """Check files checked in to git for trailing newlines at end of file."""
    no_newline_at_eof_errors = []
    whitespace_at_eof_errors = []

    for filename in find_files_here_or_argv():
        with open(filename, "r", newline='') as f:
            content = f.read()

        # Check for missing trailing newline
        if not content.endswith("\n"):
            no_newline_at_eof_errors.append(filename)
            continue

        # Check for extra whitespace at EOF
        # Strip whitespace to find first non-whitespace content
        stripped = content.rstrip()
        trailing_whitespace = len(content) - len(stripped)
            
        # If there are 2+ trailing whitespace characters, that means extra whitespace at EOF
        if trailing_whitespace > 1:
            whitespace_at_eof_errors.append(filename)

    if no_newline_at_eof_errors:
        print("Files with no newline at the end:", " ".join(no_newline_at_eof_errors))
    if whitespace_at_eof_errors:
        print("Files that have whitespace at the end:", " ".join(whitespace_at_eof_errors))

    if no_newline_at_eof_errors or whitespace_at_eof_errors:
        sys.exit(1)
```

Not that performance is too big a concern, but this actually ended up being marginally faster on my machine also. Maybe something along those lines would be an improvement? There are probably other edge cases neither catch that might get changed in the future, like extra spaces on the newline

Code after:
    whitespace_at_eof_errors = []

    for filename in find_files_here_or_argv():
        with open(filename, "r", newline="") as f:
            content = f.read()

        if not content.endswith("\n"):
            no_newline_at_eof_errors.append(filename)
            continue

        stripped = content.rstrip()
        trailing_whitespace = len(content) - len(stripped)
        if trailing_whitespace > 1:
            whitespace_at_eof_errors.append(filename)

    if no_newline_at_eof_errors:
        print("Files with no newline at the end:", " ".join(no_newline_at_eof_errors))
    if whitespace_at_eof_errors:
        print("Files that have whitespace at the end:", " ".join(whitespace_at_eof_errors))

    if no_newline_at_eof_errors or whitespace_at_eof_errors:
        sys.exit(1)


if __name__ == "__main__":
    os.chdir(os.path.dirname(__file__) + "/..")
    run()