Repository: naturalcrit/homebrewery
Pull request #3908 (2024-11-23 02:51:40)

V4 proposed aligned paragraph tokens
## Description

This implements the suggested syntax for V4 paragraph text alignment tokens from https://github.com/naturalcrit/homebrewery/discussions/2879

## Related Issues or Discussions

https://github.com/naturalcrit/homebrewery/discussions/2879

## QA Instructions, Screenshots, Recordings

- insert `:- ` in front of a paragraph. It should align left. You should see a class of mdParagraphJustifyLeft on the paragraph tag
- insert `-: ` in front of a paragraph. It should align right. You should see a class of mdParagraphJustifyRight on the paragraph tag
- insert `:-: ` in front of a paragraph. It should align center. You should see a class of mdParagraphJustifyCenter on the paragraph tag

### Reviewer Checklist




Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2025e8fe..c195df81 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -73,6 +73,9 @@ jobs:
       - run:
           name: Test - Non-Breaking Spaces
           command: npm run test:non-breaking-spaces
+      - run:
+          name: Test - Paragraph Justification
+          command: npm run test:paragraph-justification
       - run:
           name: Test - Variables
           command: npm run test:variables
diff --git a/package.json b/package.json
index a74dcf3e..d70b05c6 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
     "test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
     "test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace",
     "test:non-breaking-spaces": "jest tests/markdown/non-breaking-spaces.test.js --verbose --noStackTrace",
+    "test:paragraph-justification": "jest tests/markdown/paragraph-justification.test.js --verbose --noStackTrace",
     "test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
     "test:route": "jest tests/routes/static-pages.test.js --verbose",
     "test:safehtml": "jest tests/html/safeHTML.test.js --verbose",
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index ffd2395f..16dc1444 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -371,6 +371,43 @@ const superSubScripts = {
 	}
 };
 
+
+const justifiedParagraphClasses = [];
+justifiedParagraphClasses[2] = 'Left';
+justifiedParagraphClasses[4] = 'Right';
+justifiedParagraphClasses[6] = 'Center';
+
+const justifiedParagraphs = {
+	name  : 'justifiedParagraphs',
+	level : 'block',
+	start(src) {
+		return src.match(/\n(?:-:|:-|-:) {1}/m)?.index;
+	},  // Hint to Marked.js to stop and check for a match
+	tokenizer(src, tokens) {
+		const regex  = /^(((:-))|((-:))|((:-:))) .+(\n(([^\n].*\n)*(\n|$))|$)/ygm;
+		const match = regex.exec(src);
+		if(match?.length) {
+			let whichJustify;
+			if(match[2]?.length) whichJustify = 2;
+			if(match[4]?.length) whichJustify = 4;
+			if(match[6]?.length) whichJustify = 6;
+			return {
+				type   : 'justifiedParagraphs', // Should match "name" above
+				raw    : match[0],     // Text to consume from the source
+				length : match[whichJustify].length,
+				text   : match[0].slice(match[whichJustify].length),
+				class  : justifiedParagraphClasses[whichJustify],
+				tokens : this.lexer.inlineTokens(match[0].slice(match[whichJustify].length + 1))
+			};
+		}
+	},
+	renderer(token) {
+		return `<p align="${token.class}">${this.parser.parseInline(token.tokens)}</p>`;
+	}
+
+};
+
+
 const forcedParagraphBreaks = {
 	name  : 'hardBreaks',
 	level : 'block',
@@ -768,7 +805,7 @@ const tableTerminators = [
 ];
 
 Marked.use(MarkedVariables());
-Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks,
+Marked.use({ extensions : [justifiedParagraphs, definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks,
 	nonbreakingSpaces, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
 Marked.use(mustacheInjectBlock);
 Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 4f838a5d..65eeee68 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -505,4 +505,4 @@ body { counter-reset : page-numbers 0; }
 		counter-increment : page-numbers;
 	}
 
-}
\ No newline at end of file
+}


Test patch:
diff --git a/tests/markdown/paragraph-justification.test.js b/tests/markdown/paragraph-justification.test.js
new file mode 100644
index 00000000..48b311e8
--- /dev/null
+++ b/tests/markdown/paragraph-justification.test.js
@@ -0,0 +1,27 @@
+/* eslint-disable max-lines */
+
+import Markdown from 'naturalcrit/markdown.js';
+
+describe('Justification', ()=>{
+	test('Left Justify', function() {
+		const source = ':- Hello';
+		const rendered = Markdown.render(source);
+		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p align=\"Left\">Hello</p>`);
+	});
+	test('Right Justify', function() {
+		const source = '-: Hello';
+		const rendered = Markdown.render(source);
+		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p align=\"Right\">Hello</p>`);
+	});
+	test('Center Justify', function() {
+		const source = ':-: Hello';
+		const rendered = Markdown.render(source);
+		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p align=\"Center\">Hello</p>`);
+	});
+
+	test('Ignored inside a code block', function() {
+		const source = '```\n\n:- Hello\n\n```\n';
+		const rendered = Markdown.render(source);
+		expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<pre><code>\n:- Hello\n</code></pre>\n`);
+	});
+});