From 5170861d42c5b52eb8c3c36a5317b8c1a2a91b15 Mon Sep 17 00:00:00 2001 From: Elias Date: Tue, 13 Oct 2026 18:03:27 +0200 Subject: [PATCH] Enhancement (config): Add option `$paragraphSeparator` to separate paragraphs with a newline instead of a blank line In OneNote, the paragraphs of an outline are on consecutive lines, but pandoc separates each paragraph with a blank line. So a page's lines are spread apart in the markdown, and paragraphs that follow each other cannot be told apart from paragraphs separated by an empty paragraph. The new option `$paragraphSeparator = 2` separates paragraphs with a newline instead, which markdown viewers like Obsidian show as a line break. Paragraphs separated by an empty paragraph, as well as lists, tables, headings, and quotes, are still separated by a blank line. Lines that would turn the previous line into a heading (e.g. `===`) are also still separated by a blank line. Co-Authored-By: Claude Opus 5.5 --- ConvertOneNote2MarkDown-v2.Tests.ps1 | 114 +++++++++++++++++++++++++++ ConvertOneNote2MarkDown-v2.ps1 | 22 ++++++ README.md | 1 + config.example.ps1 | 5 ++ 4 files changed, 142 insertions(+) diff --git a/ConvertOneNote2MarkDown-v2.Tests.ps1 b/ConvertOneNote2MarkDown-v2.Tests.ps1 index 72d1ae7..4d687d2 100644 --- a/ConvertOneNote2MarkDown-v2.Tests.ps1 +++ b/ConvertOneNote2MarkDown-v2.Tests.ps1 @@ -1298,6 +1298,120 @@ some more } + It "Should honor config paragraphSeparator" { + $params['Config']['paragraphSeparator']['value'] = 2 + $params['Config']['keepspaces']['value'] = 2 # Keep non-breaking spaces and '>', so that only this option is tested + + $result = @( New-SectionGroupConversionConfig @params 6>$null ) + + # 15 pages from 'test' notebook, 15 pages from 'test2' notebook + $result.Count | Should -Be 48 + + $fakeMarkdownContent = @" +foo + +bar + +$( [char]0x00A0 ) + +paragraph after an empty paragraph + +![](../media/somepage-image1.png) + +# heading + +paragraph before a list + +- list item 1 +- list item 2 + +paragraph after a list + +| table | +|-------| +| cell | + +paragraph after a table + +> quote 1 +> +> quote 2 + +paragraph after a quote + +=== + +"@ -replace "`r", '' # On some Windows Powershell 5 versions, a here-string will contain `\r`, so let's ensure that doesn't happen. + + foreach ($pageCfg in $result) { + # Mutate + $mutated = $fakeMarkdownContent + foreach ($m in $pageCfg['mutations']) { + foreach ($r in $m['replacements']) { + $mutated = $mutated -replace $r['searchRegex'], $r['replacement'] + } + } + + # Should separate paragraphs with a newline, but not empty paragraphs, lists, tables, headings, and quotes, and not lines that would turn the previous line into a heading. Ignore first 8 lines for page header + $split = $mutated -split "`n" + $expectedBody = $split[8..($split.Count - 1)] -join "`n" + $expectedBody | Should -Be $( @" +foo +bar + +$( [char]0x00A0 ) + +paragraph after an empty paragraph +![](../media/somepage-image1.png) + +# heading + +paragraph before a list + +- list item 1 +- list item 2 + +paragraph after a list + +| table | +|-------| +| cell | + +paragraph after a table + +> quote 1 +> quote 2 + +paragraph after a quote + +=== + +"@ -replace "`r", '') # On some Windows Powershell 5 versions, a here-string will contain `\r`, so let's ensure that doesn't happen. + } + + $params['Config']['paragraphSeparator']['value'] = 1 + + $result = @( New-SectionGroupConversionConfig @params 6>$null ) + + # 15 pages from 'test' notebook, 15 pages from 'test2' notebook + $result.Count | Should -Be 48 + + foreach ($pageCfg in $result) { + # Mutate + $mutated = $fakeMarkdownContent + foreach ($m in $pageCfg['mutations']) { + foreach ($r in $m['replacements']) { + $mutated = $mutated -replace $r['searchRegex'], $r['replacement'] + } + } + + # Should keep blank lines between paragraphs. Ignore first 8 lines for page header + $split = $mutated -split "`n" + $expectedBody = $split[8..($split.Count - 1)] -join "`n" + $expectedBody | Should -Be $fakeMarkdownContent + } + } + It "Should honor config keepescape" { $params['Config']['keepescape']['value'] = 1 diff --git a/ConvertOneNote2MarkDown-v2.ps1 b/ConvertOneNote2MarkDown-v2.ps1 index 3668cba..fc7e3d6 100644 --- a/ConvertOneNote2MarkDown-v2.ps1 +++ b/ConvertOneNote2MarkDown-v2.ps1 @@ -155,6 +155,16 @@ Whether to include page timestamp and separator at top of document Whether to clear extra newlines between unordered (bullet) and ordered (numbered) list items, non-breaking spaces from blank lines, and `>` after unordered lists 1: Clear - Default 2: Don't clear +'@ + default = 1 + value = 1 + validateRange = 1,2 + } + paragraphSeparator = @{ + description = @' +Whether to separate paragraphs with a blank line, or with a newline like in OneNote. Paragraphs that are separated by an empty paragraph are still separated by a blank line. Lists, tables, headings, and quotes are still separated by a blank line. Some markdown viewers (e.g. Obsidian) show a newline as a line break, while others (e.g. GitHub) join the lines into one paragraph +1: Blank line - Default +2: Newline '@ default = 1 value = 1 @@ -912,6 +922,18 @@ Function New-SectionGroupConversionConfig { } ) } + if ($config['paragraphSeparator']['value'] -eq 2) { + @{ + description = 'Separate paragraphs with a newline instead of a blank line' + replacements = @( + @{ + # E.g. 'foo\n\nbar' -> 'foo\nbar', and '> foo\n>\n> bar' -> '> foo\n> bar'. Only for lines of paragraphs, i.e. not for lists, tables, headings, code, and empty paragraphs (i.e. lines containing only non-breaking spaces), and not before lines that would turn the previous line into a heading + searchRegex = '(?m)^((?:>[ \t]?)*)(?![\s#>|`~<:-]|[*+]\s|\d+[.)](?:\s|$)|\[\^|=+[ \t]*$)([^\r\n]+)\r*\n[> \t]*\r*\n(?=\1(?![\s#>|`~<:-]|[*+]\s|\d+[.)](?:\s|$)|\[\^|=+[ \t]*$)[^\r\n])' + replacement = "`$1`$2`n" + } + ) + } + } @{ description = 'Add heading' replacements = @( diff --git a/README.md b/README.md index c23006f..c450b6e 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The powershell script `ConvertOneNote2MarkDown-v2.ps1` will utilize the OneNote * `markdown_strict` (original unextended Markdown) * Improved headers, with title now as a `#` heading, standardized `DateTime` format for created and modified dates, and horizontal line to separate from rest of document * Choose whether to clear extra newlines between unordered (bullet) and ordered (numbered) list items, non-breaking spaces from blank lines, and `>` after unordered lists, which are created when converting with Pandoc +* Choose whether to separate paragraphs with a blank line, or with a newline like in OneNote * Choose whether to remove `\` escape symbol that are created when converting with Pandoc * Choose whether to use Line Feed (`LF`) or Carriage Return + Line Feed (`CRLF`) for new lines * Choose whether to include a `.pdf` export alongside the `.md` file. `.md` does not preserve `InkDrawing` (i.e. overlayed drawings, highlights, pen marks) absolute positions within a page, but a `.pdf` export is a complete page snapshot that preserves `InkDrawing` absolute positions within a page. diff --git a/config.example.ps1 b/config.example.ps1 index 6077bdf..fad5541 100644 --- a/config.example.ps1 +++ b/config.example.ps1 @@ -73,6 +73,11 @@ $headerTimestampEnabled = 1 # 2: Don't clear $keepspaces = 1 +# Whether to separate paragraphs with a blank line, or with a newline like in OneNote. Paragraphs that are separated by an empty paragraph are still separated by a blank line. Lists, tables, headings, and quotes are still separated by a blank line. Some markdown viewers (e.g. Obsidian) show a newline as a line break, while others (e.g. GitHub) join the lines into one paragraph +# 1: Blank line - Default +# 2: Newline +$paragraphSeparator = 1 + # Whether to clear escape symbols from md files. See: https://pandoc.org/MANUAL.html#backslash-escapes # 1: Clear all '\' characters - Default # 2: Clear all '\' characters except those preceding alphanumeric characters