Skip to content

Don't strip trailing newlines from files on save#92

Merged
dannysmith merged 5 commits intomainfrom
stop-removing-newlines
Jan 21, 2026
Merged

Don't strip trailing newlines from files on save#92
dannysmith merged 5 commits intomainfrom
stop-removing-newlines

Conversation

@dannysmith
Copy link
Copy Markdown
Owner

@dannysmith dannysmith commented Jan 20, 2026

Closes #91

Summary by CodeRabbit

  • Bug Fixes

    • Preserve multiple trailing newlines while ensuring at least one newline at end of rebuilt markdown files.
    • Only clear the editor "dirty" state after save when the saved content matches the current editor content, reducing race-condition resets.
  • Enhancements

    • Preserve the editor cursor position during programmatic content updates to prevent cursor jumping.
  • Tests

    • Updated dummy test content files used for editor/content tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 20, 2026

Walkthrough

Preserve existing extra trailing newlines while ensuring at least one newline when parsing and rebuilding markdown; update save flow to only clear dirty flags if persisted content matches current editor state and expose saveFile; and preserve cursor position when programmatic editor updates occur.

Changes

Cohort / File(s) Summary
Trailing newline logic updates
src-tauri/src/commands/files.rs
Parse and three rebuild helpers now detect whether original content ended with a newline and ensure at least one trailing newline while preserving any additional trailing newlines.
Editor save / dirtiness handling
src/hooks/editor/useEditorActions.ts
After save, re-read current state and compare saved content/frontmatter to decide isDirty/isFrontmatterDirty; useEditorActions() now returns an object including saveFile.
Editor cursor preservation
src/components/editor/Editor.tsx
Capture current selection anchor and clamp/set the cursor when applying programmatic content replacements to avoid cursor jumping.
Test content fixtures
test/dummy-astro-project/src/content/notes/copyedit-test.md, test/dummy-astro-project/src/content/notes/undated-note-zebra.md
Added/modified lines and blank lines in test fixtures to exercise newline-preservation behaviour.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hop through lines with gentle paws,
I tuck one newline and keep your draws.
Leave all the blank space at the end,
Save true and let your edits mend.
A little rabbit cheers your pen.

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Changes to test markdown files appear to be demo data resets; however, the header corruption in copyedit-test.md ('thh#' instead of '#') and the unusual prose text suggest unintentional modifications rather than purposeful demo updates. Verify whether test file modifications in copyedit-test.md are intentional demo data updates or accidental corruption; clarify the purpose of the prose additions in undated-note-zebra.md.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarises the main objective of the pull request: preserving trailing newlines during file saves instead of stripping them.
Linked Issues check ✅ Passed The pull request addresses all primary objectives from issue #91: preserves multiple trailing newlines, prevents cursor movement issues via race-condition handling, treats trailing-newline handling conservatively, and ensures at least one final newline.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dannysmith
Copy link
Copy Markdown
Owner Author

Unexpected Cursor jump is more noticeable after write with this change. Notes from Claude which were initially incorrect...

Option 2 - Actually, this prevents race conditions rather than creating them. The logic is simple:
  // After save succeeds:
  const currentContent = useEditorStore.getState().editorContent
  if (currentContent === savedContent) {
    // Safe - nothing changed during save
    setState({ isDirty: false, ... })
  } else {
    // User typed more - stay dirty
    setState({ lastSaveTimestamp: Date.now() })
  }
  This is ~5 lines and surgically addresses the root cause.

And after a bit of "read the fucking codebase and stop lying to me mate"...

The flow with Option 2:

  Scenario A - User typed during save (race condition):
  1. Capture content B, start saving
  2. User types → content now C
  3. Save completes, compare: C ≠ B → keep isDirty: true
  4. Query refetches B from disk
  5. useEditorFileContent sees isDirty: true → does NOT overwrite
  6. User keeps their content C, no cursor jump

  Scenario B - User didn't type during save (normal):
  1. Capture content B, save
  2. Content still B
  3. Save completes, compare: B = B → set isDirty: false
  4. Query refetches B from disk
  5. Store already has B → no actual state change
  6. Editor comparison: CodeMirror has B, store has B → no replacement needed
  7. No cursor jump

We should address this as part of this PR.

…file as clean if content hasn't changed during save
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/hooks/editor/useEditorActions.ts`:
- Around line 95-100: The race-check clears dirty flags based only on
editorContent which can drop unsaved frontmatter edits; update the save
completion logic in useEditorActions (where useEditorStore.getState() is used)
to compare both current editorContent and current editorFrontmatter to the saved
editorContent and editorFrontmatter (or at minimum include frontmatter in the
equality check), and when setting useEditorStore.setState preserve any existing
isFrontmatterDirty by OR-ing the previous state's isFrontmatterDirty (i.e., only
clear isFrontmatterDirty if both content and frontmatter match the saved values)
so in-useEditorStore, use the current state values to determine final isDirty
and isFrontmatterDirty rather than blindly clearing them.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/dummy-astro-project/src/content/notes/copyedit-test.md`:
- Line 10: Replace the corrupted markdown header string "thh# Copyedit Mode
Testing" with a valid heading by removing the stray "thh" so it reads "#
Copyedit Mode Testing" (or adjust to the intended heading level like "##" if
needed); locate the line containing "thh# Copyedit Mode Testing" in
test/dummy-astro-project/src/content/notes/copyedit-test.md and update it
accordingly, or leave as-is if the "thh" prefix was intentionally included for
testing.

… and frontmatter for unsaved edits to improve state management during saves.
@dannysmith dannysmith merged commit 4684bb3 into main Jan 21, 2026
8 checks passed
@dannysmith dannysmith deleted the stop-removing-newlines branch January 21, 2026 01:05
@coderabbitai coderabbitai bot mentioned this pull request Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Don't strip trailing newlines when writing to disk

1 participant