The trailing blank lines EditorConfig did not fix
I kept seeing the same noise in my git diffs: files that should not have changed showing a one-line diff at the very bottom, where a blank line or two had appeared at the end. Cursor was adding them, quietly, on save. Across a homelab repo with hundreds of YAML, Python, and Jinja2 files, the noise added up fast.
I went looking for a fix and landed on EditorConfig. It helped, but not the way I expected, and not with the actual problem. Here is what I learned.
First, the wrong diagnosis
My first assumption was trailing whitespace: stray spaces or tabs at the end of lines. That is the classic editor-noise problem, so I grepped for it and found about 90 files with trailing spaces. I cleaned them up and felt productive.
It did not fix the diffs. The blank-line noise kept coming back.
When I looked more carefully, the real issue was different. It was not whitespace at the end of lines, it was multiple empty lines at the end of files. I found 443 files with two or more trailing newlines. Cursor was appending blank lines to the bottom of files, and trailing-whitespace cleanup does nothing about that, because a blank line has no whitespace to trim.
That distinction turned out to be the whole story:
- Trailing whitespace is spaces or tabs before the line break.
trim_trailing_whitespaceremoves it. - Trailing blank lines are empty lines at the end of the file. Nothing in the EditorConfig standard removes them.
What EditorConfig actually does
I added a .editorconfig at the repo root anyway, because consistent formatting is worth having:
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.py]
indent_style = space
indent_size = 4
Most modern editors, including Cursor and VS Code, read this automatically. It pins line endings to LF, enforces per-language indentation, and trims trailing whitespace on save.
Here is the part I got wrong, and the part the documentation makes clear once you read it: insert_final_newline = true ensures a file ends with a newline. It does not collapse multiple trailing blank lines down to one. There is no EditorConfig property for that. People have asked for it (editorconfig#483, “Strip multiple blank lines from the end of the file”), and it is still not in the spec.
So EditorConfig made my files more consistent, but it would not have prevented the exact problem that sent me looking. A file with three blank lines at the end already ends with a newline, so insert_final_newline sees nothing to do.
What actually fixed it
Two things, working together.
An editor setting for the in-editor case. VS Code and Cursor have files.trimFinalNewlines, which trims everything after the final newline on save. EditorConfig does not control it, so it goes in the editor’s own settings:
{
"files.trimFinalNewlines": true,
"files.insertFinalNewline": true
}
A pre-commit hook for enforcement. The editor setting only helps on machines configured for it. The thing that actually guarantees the result is the pre-commit framework’s end-of-file-fixer hook, which my repo already runs:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
end-of-file-fixer ensures every file is either empty or ends with exactly one newline. It strips the extra blank lines at commit time, no matter which editor produced them or how it was configured. That is the difference between hoping every editor cooperates and enforcing the result at the one place every change passes through.
The one-time cleanup
Before the hook could keep things tidy, I had to fix the existing mess. I normalized 588 files down to a single trailing newline, removing 589 extra newlines across the tree. The changes were newline-only, no content touched, which made the large diff easy to review and safe to commit in one pass. After that, the pre-commit hook keeps new commits clean automatically.
Reflection
- Diagnose the actual symptom. Trailing whitespace and trailing blank lines look similar in a diff and have completely different fixes. I wasted a pass treating one as the other.
- EditorConfig is necessary but not sufficient. It standardizes formatting across editors, but it has no rule for multiple trailing blank lines, so it cannot solve that on its own.
- Enforce at the choke point. Editor settings depend on every machine being configured the same way. A pre-commit hook runs on every commit regardless, so that is where the guarantee belongs.
- Read what the property actually promises.
insert_final_newlineguarantees a file ends with a newline, not that it ends with exactly one. The gap between those two readings was my whole problem.
Related reading
Watching Claude work: VS Code in a cmux browser pane
Why I stopped juggling terminals for AI edits, how I run VS Code for the web beside Claude Code in cmux, and the config that ties it together.
Making my AI agents default to MCP for Plane
My coding agents kept reaching straight for the Plane REST API. I wrote a rule to make MCP the default instead. Here is why, and the handful of cases where direct HTTP is still the right call.
Cursor Tips: Optimize Your Workflow
Explore essential tips for using the Cursor IDE to enhance your development workflow, from organizing chats to using AI effectively.
Ready to Transform Your Career?
Let's work together to unlock your potential and achieve your professional goals.