Skip to content

Blank line paragraph preservation#9872

Draft
linonetwo wants to merge 1 commit into
TiddlyWiki:masterfrom
linonetwo:feat/wikitext-blankline-paragraphs
Draft

Blank line paragraph preservation#9872
linonetwo wants to merge 1 commit into
TiddlyWiki:masterfrom
linonetwo:feat/wikitext-blankline-paragraphs

Conversation

@linonetwo

@linonetwo linonetwo commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

closes #9871

This PR introduces a blankline parse tree rule when preservation is enabled:

  • A\n\nB remains two paragraphs separated by the normal paragraph separator
  • A\n\n\nB becomes parseblock, blankline, parseblock
  • A\n\n\n\nB becomes parseblock, blankline, blankline, parseblock

It also handles extra blank lines after non-paragraph block rules. This matters for content such as lists followed by macros or paragraphs, where the block rule may consume trailing whitespace before the outer block parser sees it.

What changed

  • Adds preserveBlankLines parser option support and $:/config/Parser/PreserveBlankLines config support
  • Passes preserveBlankLines through wiki.parseText() to the parser
  • Adds blankline parse tree nodes with class="tc-blankline"
  • Serializes blankline nodes back to the extra newline they represent
  • Adds a small view-mode style so rendered blankline paragraphs have visible height

Compatibility

The behavior is opt-in for now:

  • pass { preserveBlankLines: true } to wiki.parseText(); or
  • set $:/config/Parser/PreserveBlankLines to yes

With the option disabled, existing behavior is preserved: extra blank lines are ignored as before.

Future path to remove the option

I don't really want the preserveBlankLines option, but directly enable blank line support breaks many test, so I have to add it now, until we decide to enable it globally.

If the project decides that preserving extra blank lines should become the default wikitext behavior, the feature option can be removed by:

  • making the blankline-preserving paths in core/modules/parsers/wikiparser/wikiparser.js unconditional
  • removing the $:/config/Parser/PreserveBlankLines check and the preserveBlankLines parser option
  • removing preserveBlankLines pass-through from core/modules/wiki.js
  • keeping the blankline serializer support, because parse trees may still contain blankline nodes

Tests to update if the option is removed:

  • editions/test/tiddlers/tests/test-wikitext-blanklines.js
    • remove or invert the disabled/default-ignored expectation
    • remove the config toggle test once there is no option
    • keep coverage for paragraph, leading, trailing, list-to-paragraph, and list-to-macro blank lines
  • any parser or serializer fixture that intentionally expected A\n\n\nB to collapse should be updated to expect a blankline empty paragraph instead
  • WYSIWYG/ProseMirror integration tests can drop explicit { preserveBlankLines: true } parser calls once the parser behavior is default

Copilot AI review requested due to automatic review settings June 6, 2026 17:17
@netlify

netlify Bot commented Jun 6, 2026

Copy link
Copy Markdown

Deploy Preview for tiddlywiki-previews ready!

Name Link
🔨 Latest commit 63ecf97
🔍 Latest deploy log https://app.netlify.com/projects/tiddlywiki-previews/deploys/6a245624a9b3bc0008a8b9a0
😎 Deploy Preview https://deploy-preview-9872--tiddlywiki-previews.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

📊 Build Size Comparison: empty.html

Branch Size
Base (master) 2491.8 KB
PR 2495.7 KB

Diff: ⬆️ Increase: +3.9 KB


⚠️ Change Note Status

This PR appears to contain code changes but doesn't include a change note.

Please add a change note by creating a .tid file in editions/tw5.com/tiddlers/releasenotes/<version>/

📚 Documentation: Release Notes and Changes

💡 Note: If this is a documentation-only change, you can ignore this message.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Confirmed: linonetwo has already signed the Contributor License Agreement (see contributing.md)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR adds an opt-in (and configurable) way to preserve extra blank lines in TiddlyWiki wikitext by representing them as empty paragraph blocks, and ensures they round-trip through serialization and display.

Changes:

  • Introduces preserveBlankLines parser option (and $:/config/Parser/PreserveBlankLines) to emit explicit blankline parse tree nodes for additional blank lines.
  • Updates wikitext serialization to round-trip blankline nodes back into the correct newline sequences.
  • Adds theme styling and a dedicated test spec to validate parsing/serialization behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
themes/tiddlywiki/vanilla/base.tid Adds CSS to ensure empty blankline paragraphs have visible height
plugins/tiddlywiki/wikitext-serialize/utils/parsetree.js Serializes blankline parse tree nodes back into newline text
editions/test/tiddlers/tests/test-wikitext-blanklines.js Adds tests covering parsing + serialization for preserved blank lines
core/modules/wiki.js Plumbs preserveBlankLines through wiki.parseText() into parsers
core/modules/parsers/wikiparser/wikiparser.js Implements blank line node generation and integrates it into block parsing

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +271 to +293
WikiParser.prototype.makeBlankLineBlocks = function(start,whitespace,options) {
options = options || {};
var newlineMatches = whitespace.match(/\r?\n/g),
newlineCount = newlineMatches ? newlineMatches.length : 0,
blankLineCount = options.leading ? Math.max(0,newlineCount - 1) : Math.max(0,newlineCount - 2),
blankLineBlocks = [];
for(var index = 0; index < blankLineCount; index++) {
blankLineBlocks.push({
type: "element",
tag: "p",
attributes: {
class: {name: "class", type: "string", value: "tc-blankline"}
},
orderedAttributes: [
{name: "class", type: "string", value: "tc-blankline"}
],
children: [],
start: start,
end: start,
rule: "blankline",
isLeadingBlankLine: !!options.leading && index === 0
});
}
Comment on lines +273 to +274
var newlineMatches = whitespace.match(/\r?\n/g),
newlineCount = newlineMatches ? newlineMatches.length : 0,
Comment on lines +57 to +63
it("should preserve extra blank lines as empty paragraphs", function() {
expect(paragraphCount("A\n\n\nB")).toBe(3);
expect(parse("A\n\n\nB")[1].attributes.class.value).toBe("tc-blankline");
expect(serialize("A\n\n\nB")).toBe("A\n\n\nB\n\n");
expect(paragraphCount("A\n\n\n\nB")).toBe(4);
expect(serialize("A\n\n\n\nB")).toBe("A\n\n\n\nB\n\n");
});
@linonetwo linonetwo changed the title Add opt-in blank line paragraph preservation Blank line paragraph preservation Jun 6, 2026
@linonetwo linonetwo marked this pull request as draft June 6, 2026 17:27
@Jermolene

Copy link
Copy Markdown
Member

Thanks @linonetwo I think this approach looks promising.

@linonetwo

linonetwo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

If this way is valid, I would expect remove the preserveBlankLines option, make it works by default.
I find some unit tests will fail, to make this PR looks simple (not update those test for now) so I added that preserveBlankLines option.

If I make multiple-line support enabled by default, Will this break some backward-compatility?

@pmario

pmario commented Jun 7, 2026

Copy link
Copy Markdown
Member

Do you have the same problem with HTML comments? <!-- does this text survive a roundtrip? -->

@linonetwo

Copy link
Copy Markdown
Contributor Author

I will take a look. Maybe we should make it CST instead of AST. Keep all details in the wikitext so we can preserve it at-is.

@pmario

pmario commented Jun 8, 2026

Copy link
Copy Markdown
Member

hmmm, I think CST is overkill on the library side, if you think about Lezer ...

IMO we would need 3 things.

  • Keep comment nodes
  • Remember node start/stop indicators eg: > or <<< was used to create a blockquote
  • Instead of skipping whitespace, add it as eg: pre-text to the following node

IMO that should give us all the info we need.

On the other hand. I personally would like to have a "TW prettyprinter" active all the time, which uses "safe" TW syntax. ... But that's a completely different story.

@pmario

pmario commented Jun 8, 2026

Copy link
Copy Markdown
Member

The pattern is called: "trivia-enriched tree".

It seems Roslyn (the C# compiler) does almost exactly this: the semantic tree stays clean, and every token carries leading/trailing trivia (whitespace and comments).

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.

[IDEA] Allow empty lines to be rendered

4 participants