diff --git a/src/formatters/captureChoiceFormatter-frontmatter.test.ts b/src/formatters/captureChoiceFormatter-frontmatter.test.ts index dd1e747d..d60f91c3 100644 --- a/src/formatters/captureChoiceFormatter-frontmatter.test.ts +++ b/src/formatters/captureChoiceFormatter-frontmatter.test.ts @@ -478,6 +478,28 @@ describe('CaptureChoiceFormatter insert after end-of-section spacing', () => { expect(second).toBe(['Target', 'Existing', 'One', '', 'Two', '', ''].join('\n')); }); + it('preserves insertion order when format has no trailing newline and EOF blanks exist', async () => { + const { formatter, file } = createFormatter(); + const choice = createInsertAfterChoice('# H'); + const initial = ['# H', 'A', '', ''].join('\n'); + + const first = await formatter.formatContentWithFile( + 'X', + choice, + initial, + file, + ); + + const second = await formatter.formatContentWithFile( + 'Y', + choice, + first, + file, + ); + + expect(second).toBe(['# H', 'A', 'X', 'Y'].join('\n')); + }); + it('does not change behavior when insert-at-end is disabled', async () => { const { formatter, file } = createFormatter(); const choice = createInsertAfterChoice('# Journal', { insertAtEnd: false }); diff --git a/src/formatters/captureChoiceFormatter.ts b/src/formatters/captureChoiceFormatter.ts index 34af94bf..1cc56071 100644 --- a/src/formatters/captureChoiceFormatter.ts +++ b/src/formatters/captureChoiceFormatter.ts @@ -266,7 +266,8 @@ export class CaptureChoiceFormatter extends CompleteFormatter { private findInsertAfterPositionAtSectionEnd( lines: string[], sectionEndIndex: number, - body: string, + fileContent: string, + insertedText: string, ): number { if (sectionEndIndex < 0) return sectionEndIndex; @@ -284,10 +285,16 @@ export class CaptureChoiceFormatter extends CompleteFormatter { return sectionEndIndex; } + // For entries without trailing newline, keep insertion anchored at the + // section end so repeated captures preserve order. + if (!insertedText.endsWith("\n")) { + return sectionEndIndex; + } + // split("\n") keeps a trailing empty string when content ends in "\n". // We keep one trailing slot so the next insertion preserves capture spacing // without introducing an extra blank line before the inserted text. - if (body.endsWith("\n")) { + if (fileContent.endsWith("\n")) { return Math.max(sectionEndIndex, position - 1); } @@ -334,6 +341,7 @@ export class CaptureChoiceFormatter extends CompleteFormatter { fileContentLines, endOfSectionIndex ?? fileContentLines.length - 1, this.fileContent, + formatted, ); } else { const blankLineMode = @@ -470,8 +478,9 @@ export class CaptureChoiceFormatter extends CompleteFormatter { fileContentLines, endOfSectionIndex ?? fileContentLines.length - 1, this.fileContent, + insertAfterLineAndFormatted, ); - } + } const newFileContent = this.insertTextAfterPositionInBody( insertAfterLineAndFormatted,