Repository: platers/obsidian-linter

Issue:
FR: Allow a Newline Between Footnotes
- [x] I have verified that I am on the latest version of the Linter

## Describe the Bug

My personal and preference - and the preference of the official markdown documentation - is to always have a newline in between footnotes. These newlines, however, get removed by the Linter without me having enabled a rule doing so.

## How to Reproduce

Steps to reproduce the behavior:

1. Create a markdown document in Obsidian
2. Write two Footnotes which are separated by a newline. E.g:

```markdown
This is the first footnote.[^1] This is the second.[^2]

[^1]: Hey, I am a footnote!

[^2]: Me too!
```

3. Lint the current file
4. The lines get removed

## Expected Behavior

I do not think that these lines should be removed without an explicit rule enabling the removal, as the official markdown documentation uses separating newlines between footnotes (see [official documentation for footnotes](https://www.markdownguide.org/extended-syntax/#footnotes)). If people prefer to have the lines removed it should be a configurable rule.

## Device

- [x] Desktop
- [x] Mobile

PS: I am not quite sure whether a bug report was the right label to use. It certainly does not cause a file to be corrupted or Obsidian to crash, however it also isn't a feature request, since it requests the removal of a feature / unintended side effect of another rule which stands in conflict with the official markdown documentation. I guess it would be a feature request if you decided to make the removal of the newline a configurable rule - which would probably be the best option.

Hints from issue comments:
@EvilsSouls: Update: I just checked and this behavior is caused by the `move footnotes to the bottom` rule. I still like the functionality of this rule, however just not the side effect of it removing the newlines.@pjkaufman: I am thinking that this would be a new option for `Move Footnotes to the Bottom` in order to make there be a newline between the footnotes as you point out.

I am not really familiar with the official markdown documentation, but I can certainly take a look at them and see if I notice anything else that could be wrong.@pjkaufman: I have added the option to `Move Footnotes to the Bottom`. It should be present on master and in the next release. Please let us know if it is not working or present in either of those.@EvilsSouls: > I have added the option to `Move Footnotes to the Bottom`. It should be present on master and in the next release. Please let us know if it is not working or present in either of those.

Thank you very much! I'll try it out later.

Solution patch:
diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts
index c17204d..0b97b7b 100644
--- a/src/lang/locale/en.ts
+++ b/src/lang/locale/en.ts
@@ -532,6 +532,10 @@ export default {
     'move-footnotes-to-the-bottom': {
       'name': 'Move Footnotes to the bottom',
       'description': 'Move all footnotes to the bottom of the document and makes sure they are sorted based on the order they are referenced in the file\'s body.',
+      'include-blank-line-between-footnotes': {
+        'name': 'Include Blank Line Between Footnotes',
+        'description': 'Includes a blank line between footnotes when enabled.',
+      },
     },
     // move-math-block-indicators-to-their-own-line.ts
     'move-math-block-indicators-to-their-own-line': {
diff --git a/src/rules/move-footnotes-to-the-bottom.ts b/src/rules/move-footnotes-to-the-bottom.ts
index e26ab16..cf53392 100644
--- a/src/rules/move-footnotes-to-the-bottom.ts
+++ b/src/rules/move-footnotes-to-the-bottom.ts
@@ -1,10 +1,12 @@
 import {Options, RuleType} from '../rules';
-import RuleBuilder, {ExampleBuilder, OptionBuilderBase} from './rule-builder';
+import RuleBuilder, {BooleanOptionBuilder, ExampleBuilder, OptionBuilderBase} from './rule-builder';
 import dedent from 'ts-dedent';
 import {IgnoreTypes} from '../utils/ignore-types';
 import {moveFootnotesToEnd} from '../utils/mdast';
 
-class MoveFootnotesToTheBottomOptions implements Options {}
+class MoveFootnotesToTheBottomOptions implements Options {
+  includeBlankLineBetweenFootnotes?: boolean = false;
+}
 
 @RuleBuilder.register
 export default class MoveFootnotesToTheBottom extends RuleBuilder<MoveFootnotesToTheBottomOptions> {
@@ -20,7 +22,7 @@ export default class MoveFootnotesToTheBottom extends RuleBuilder<MoveFootnotesT
     return MoveFootnotesToTheBottomOptions;
   }
   apply(text: string, options: MoveFootnotesToTheBottomOptions): string {
-    return moveFootnotesToEnd(text);
+    return moveFootnotesToEnd(text, options.includeBlankLineBetweenFootnotes);
   }
   get exampleBuilders(): ExampleBuilder<MoveFootnotesToTheBottomOptions>[] {
     return [
@@ -46,9 +48,42 @@ export default class MoveFootnotesToTheBottom extends RuleBuilder<MoveFootnotesT
           [^2]: second footnote
         `,
       }),
+      new ExampleBuilder({
+        description: 'Moving footnotes to the bottom with including a blank line between footnotes',
+        before: dedent`
+          Lorem ipsum, consectetur adipiscing elit. [^1] Donec dictum turpis quis ipsum pellentesque.
+          ${''}
+          [^1]: first footnote
+          ${''}
+          Quisque lorem est, fringilla sed enim at, sollicitudin lacinia nisi.[^2]
+          [^2]: second footnote
+          ${''}
+          Maecenas malesuada dignissim purus ac volutpat.
+        `,
+        after: dedent`
+          Lorem ipsum, consectetur adipiscing elit. [^1] Donec dictum turpis quis ipsum pellentesque.
+          ${''}
+          Quisque lorem est, fringilla sed enim at, sollicitudin lacinia nisi.[^2]
+          Maecenas malesuada dignissim purus ac volutpat.
+          ${''}
+          [^1]: first footnote
+          ${''}
+          [^2]: second footnote
+        `,
+        options: {
+          includeBlankLineBetweenFootnotes: true,
+        },
+      }),
     ];
   }
   get optionBuilders(): OptionBuilderBase<MoveFootnotesToTheBottomOptions>[] {
-    return [];
+    return [
+      new BooleanOptionBuilder({
+        OptionsClass: MoveFootnotesToTheBottomOptions,
+        nameKey: 'rules.move-footnotes-to-the-bottom.include-blank-line-between-footnotes.name',
+        descriptionKey: 'rules.move-footnotes-to-the-bottom.include-blank-line-between-footnotes.description',
+        optionsKey: 'includeBlankLineBetweenFootnotes',
+      }),
+    ];
   }
 }
diff --git a/src/utils/mdast.ts b/src/utils/mdast.ts
index d6118a5..f4351b3 100644
--- a/src/utils/mdast.ts
+++ b/src/utils/mdast.ts
@@ -186,9 +186,10 @@ function getHeaderTextPositions(text: string): PositionPlusText[] {
 /**
  * Moves footnote declarations to the end of the document.
  * @param {string} text The text to move footnotes in
+ * @param {boolean} includeBlankLinesBetweenFootnotes Whether to have a blank line between footnotes
  * @return {string} The text with footnote declarations moved to the end
  */
-export function moveFootnotesToEnd(text: string): string {
+export function moveFootnotesToEnd(text: string, includeBlankLinesBetweenFootnotes: boolean): string {
   const positions: Position[] = getPositions(MDAstTypes.Footnote, text);
   let footnotes: string[] = [];
 
@@ -273,10 +274,17 @@ export function moveFootnotesToEnd(text: string): string {
 
   // Add the footnotes to the end of the document
   if (footnotes.length > 0) {
-    text = text.trimEnd() + '\n';
+    text = text.trimEnd();
   }
+  let whitespaceBetweenFootnotes = '\n';
+  if (includeBlankLinesBetweenFootnotes) {
+    whitespaceBetweenFootnotes = '\n\n';
+  } else {
+    text += '\n';
+  }
+
   for (const footnote of footnotes) {
-    text += '\n' + footnote;
+    text += whitespaceBetweenFootnotes + footnote;
   }
 
   return text;



Test patch:
diff --git a/__tests__/move-footnotes-to-the-bottom.test.ts b/__tests__/move-footnotes-to-the-bottom.test.ts
index 179b0ff..fba557f 100644
--- a/__tests__/move-footnotes-to-the-bottom.test.ts
+++ b/__tests__/move-footnotes-to-the-bottom.test.ts
@@ -305,5 +305,25 @@ ruleTest({
         [^3]: [3333](333)
       `,
     },
+    { // accounts for https://github.com/platers/obsidian-linter/issues/1392
+      testName: 'Moving footnotes to the bottom of the file when including blank lines between footnotes should not change a file with footnotes at the end with blank lines between them already',
+      before: dedent`
+        This is the first footnote.[^1] This is the second.[^2]
+        ${''}
+        [^1]: Hey, I am a footnote!
+        ${''}
+        [^2]: Me too!
+      `,
+      after: dedent`
+        This is the first footnote.[^1] This is the second.[^2]
+        ${''}
+        [^1]: Hey, I am a footnote!
+        ${''}
+        [^2]: Me too!
+      `,
+      options: {
+        includeBlankLineBetweenFootnotes: true,
+      },
+    },
   ],
 });