From b45686eb3b22e7f448713bdf1ddb0075a43285f5 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Sat, 23 Nov 2024 11:18:44 -0600 Subject: [PATCH 01/10] Create an element for serial non-breaking spaces as proposed in V4 discussion --- shared/naturalcrit/markdown.js | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 4c1a2f92a..45d573262 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -391,6 +391,27 @@ const forcedParagraphBreaks = { } }; +const nonbreakingSpaces = { + name : 'nonbreakingSpaces', + level : 'inline', + start(src) { return src.match(/:>+/m)?.index; }, // Hint to Marked.js to stop and check for a match + tokenizer(src, tokens) { + const regex = /:(>+)/ym; + const match = regex.exec(src); + if(match?.length) { + return { + type : 'nonbreakingSpaces', // Should match "name" above + raw : match[0], // Text to consume from the source + length : match[1].length, + text : '' + }; + } + }, + renderer(token) { + return ` `.repeat(token.length).concat('\n'); + } +}; + const definitionListsSingleLine = { name : 'definitionListsSingleLine', level : 'block', @@ -748,11 +769,12 @@ const tableTerminators = [ ]; Marked.use(MarkedVariables()); -Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts, - mustacheSpans, mustacheDivs, mustacheInjectInline] }); +Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, + nonbreakingSpaces, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); Marked.use(mustacheInjectBlock); Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false }); -Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); +Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), + MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); function cleanUrl(href) { try { From 596c4ad68dc1764804c1aa36d7702bc016251e1b Mon Sep 17 00:00:00 2001 From: David Bolack Date: Wed, 4 Dec 2024 21:24:48 -0600 Subject: [PATCH 02/10] Add Tests --- shared/naturalcrit/markdown.js | 4 +- tests/markdown/horizontalSpaces.test.js | 49 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/markdown/horizontalSpaces.test.js diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 45d573262..6ceee2b03 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -415,9 +415,9 @@ const nonbreakingSpaces = { const definitionListsSingleLine = { name : 'definitionListsSingleLine', level : 'block', - start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match + start(src) { return src.match(/\n[^\n]*?::[^:\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match tokenizer(src, tokens) { - const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; + const regex = /^([^\n]*?)::([^\:\>][^\n]*)(?:\n|$)/ym; let match; let endIndex = 0; const definitions = []; diff --git a/tests/markdown/horizontalSpaces.test.js b/tests/markdown/horizontalSpaces.test.js new file mode 100644 index 000000000..6789b934c --- /dev/null +++ b/tests/markdown/horizontalSpaces.test.js @@ -0,0 +1,49 @@ +/* eslint-disable max-lines */ + +import Markdown from 'naturalcrit/markdown.js'; + +describe('Hard Breaks', ()=>{ + test('Single Break', function() { + const source = ':>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

 \n

`); + }); + + test('Double Break', function() { + const source = ':>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  \n

`); + }); + + test('Triple Break', function() { + const source = ':>>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n

`); + }); + + test('Many Break', function() { + const source = ':>>>>>>>>>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

          \n

`); + }); + + test('Multiple sets of Breaks', function() { + const source = ':>>>\n:>>>\n:>>>'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n\n   \n\n   \n

`); + }); + + test('Break directly between two paragraphs', function() { + const source = 'Line 1\n:>>\nLine 2'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Line 1\n  \n\nLine 2

`); + }); + + test('Ignored inside a code block', function() { + const source = '```\n\n:>\n\n```\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n:>\n
`); + }); + +}); + From c63b6ffaf0857ef5f9a28e9a070a91133b33ed79 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 17 Dec 2024 21:38:32 -0600 Subject: [PATCH 03/10] Add test for a pair of inline horizontal breaks --- shared/naturalcrit/markdown.js | 2 +- tests/markdown/horizontalSpaces.test.js | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 6ceee2b03..6b737e02f 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -408,7 +408,7 @@ const nonbreakingSpaces = { } }, renderer(token) { - return ` `.repeat(token.length).concat('\n'); + return ` `.repeat(token.length).concat(''); } }; diff --git a/tests/markdown/horizontalSpaces.test.js b/tests/markdown/horizontalSpaces.test.js index 6789b934c..1ef8ea043 100644 --- a/tests/markdown/horizontalSpaces.test.js +++ b/tests/markdown/horizontalSpaces.test.js @@ -33,6 +33,12 @@ describe('Hard Breaks', ()=>{ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n\n   \n\n   \n

`); }); + test('Pair of inline Breaks', function() { + const source = ':>>:>>'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  \n  \n

`); + }); + test('Break directly between two paragraphs', function() { const source = 'Line 1\n:>>\nLine 2'; const rendered = Markdown.render(source).trim(); From 912f9f0cf689a34648e00517010b15cbaa63eca1 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 17 Dec 2024 21:40:56 -0600 Subject: [PATCH 04/10] Remove extraneous linefeeds in horizontalbreaks --- tests/markdown/horizontalSpaces.test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/markdown/horizontalSpaces.test.js b/tests/markdown/horizontalSpaces.test.js index 1ef8ea043..5e5611e0f 100644 --- a/tests/markdown/horizontalSpaces.test.js +++ b/tests/markdown/horizontalSpaces.test.js @@ -6,43 +6,43 @@ describe('Hard Breaks', ()=>{ test('Single Break', function() { const source = ':>\n\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

 \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

 

`); }); test('Double Break', function() { const source = ':>>\n\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  

`); }); test('Triple Break', function() { const source = ':>>>\n\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   

`); }); test('Many Break', function() { const source = ':>>>>>>>>>>\n\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

          \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

          

`); }); test('Multiple sets of Breaks', function() { const source = ':>>>\n:>>>\n:>>>'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n\n   \n\n   \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n   \n   

`); }); test('Pair of inline Breaks', function() { const source = ':>>:>>'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  \n  \n

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    

`); }); test('Break directly between two paragraphs', function() { const source = 'Line 1\n:>>\nLine 2'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Line 1\n  \n\nLine 2

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Line 1\n  \nLine 2

`); }); test('Ignored inside a code block', function() { From 6301a66fd3e8f583edb08b59e96415f242a955e0 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Thu, 19 Dec 2024 17:44:48 -0600 Subject: [PATCH 05/10] Add additional tests --- tests/markdown/horizontalSpaces.test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/markdown/horizontalSpaces.test.js b/tests/markdown/horizontalSpaces.test.js index 5e5611e0f..94e1a93b9 100644 --- a/tests/markdown/horizontalSpaces.test.js +++ b/tests/markdown/horizontalSpaces.test.js @@ -51,5 +51,17 @@ describe('Hard Breaks', ()=>{ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n:>\n
`); }); + + test('I am actually a definition list!', function() { + const source = 'Term\n::> Definition 1\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
`); + }); + + test('I am actually a two term definition list!', function() { + const source = 'Term\n::> Definition 1\n::>> Definition 2'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
\n
>> Definition 2
`); + }); }); From 6e9d293bbe33b900217795a03ea341f1681c6f85 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 19 Dec 2024 23:09:43 -0500 Subject: [PATCH 06/10] Rename tests to "Non-breaking Spaces" Hard Breaks name was leftover from copying the `::::` test file. --- tests/markdown/horizontalSpaces.test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/markdown/horizontalSpaces.test.js b/tests/markdown/horizontalSpaces.test.js index 94e1a93b9..20bb8b892 100644 --- a/tests/markdown/horizontalSpaces.test.js +++ b/tests/markdown/horizontalSpaces.test.js @@ -2,44 +2,44 @@ import Markdown from 'naturalcrit/markdown.js'; -describe('Hard Breaks', ()=>{ - test('Single Break', function() { +describe('Non-Breaking Spaces', ()=>{ + test('Single Space', function() { const source = ':>\n\n'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

 

`); }); - test('Double Break', function() { + test('Double Space', function() { const source = ':>>\n\n'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

  

`); }); - test('Triple Break', function() { + test('Triple Space', function() { const source = ':>>>\n\n'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   

`); }); - test('Many Break', function() { + test('Many Space', function() { const source = ':>>>>>>>>>>\n\n'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

          

`); }); - test('Multiple sets of Breaks', function() { + test('Multiple sets of Spaces', function() { const source = ':>>>\n:>>>\n:>>>'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

   \n   \n   

`); }); - test('Pair of inline Breaks', function() { + test('Pair of inline Spaces', function() { const source = ':>>:>>'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    

`); }); - test('Break directly between two paragraphs', function() { + test('Space directly between two paragraphs', function() { const source = 'Line 1\n:>>\nLine 2'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Line 1\n  \nLine 2

`); @@ -58,7 +58,7 @@ describe('Hard Breaks', ()=>{ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
`); }); - test('I am actually a two term definition list!', function() { + test('I am actually a two-term definition list!', function() { const source = 'Term\n::> Definition 1\n::>> Definition 2'; const rendered = Markdown.render(source).trim(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
\n
>> Definition 2
`); From ed8c4d0eef0c7b8e951951f6b679536687699e04 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 19 Dec 2024 23:14:32 -0500 Subject: [PATCH 07/10] Add tests to circleCi --- .circleci/config.yml | 3 +++ package.json | 1 + .../{horizontalSpaces.test.js => non-breaking-spaces.test.js} | 0 3 files changed, 4 insertions(+) rename tests/markdown/{horizontalSpaces.test.js => non-breaking-spaces.test.js} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 00cbdf5bc..51c598f02 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,6 +70,9 @@ jobs: - run: name: Test - Hard Breaks command: npm run test:hard-breaks + - run: + name: Test - Non-Breaking Spaces + command: npm run test:non-breaking-spaces - run: name: Test - Variables command: npm run test:variables diff --git a/package.json b/package.json index 3b192214a..d084a866b 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace", "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: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/tests/markdown/horizontalSpaces.test.js b/tests/markdown/non-breaking-spaces.test.js similarity index 100% rename from tests/markdown/horizontalSpaces.test.js rename to tests/markdown/non-breaking-spaces.test.js From aae5367ad25d7c41f7ad851a38b95e34609adda6 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 20 Dec 2024 11:01:55 -0500 Subject: [PATCH 08/10] Add test case for single-line definition list --- tests/markdown/non-breaking-spaces.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/markdown/non-breaking-spaces.test.js b/tests/markdown/non-breaking-spaces.test.js index 20bb8b892..9dad4eb0f 100644 --- a/tests/markdown/non-breaking-spaces.test.js +++ b/tests/markdown/non-breaking-spaces.test.js @@ -51,6 +51,11 @@ describe('Non-Breaking Spaces', ()=>{ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n:>\n
`); }); + test('I am actually a single-line definition list!', function() { + const source = 'Term ::> Definition 1\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
> Definition 1
\n
`); + }); test('I am actually a definition list!', function() { const source = 'Term\n::> Definition 1\n'; From 50fcffb2538986fa5716f348980679c631e4213a Mon Sep 17 00:00:00 2001 From: David Bolack Date: Fri, 20 Dec 2024 14:06:20 -0600 Subject: [PATCH 09/10] Revert exclusion on single definition list regex This permits `Term ::> Definition` to process as a single line definition list --- shared/naturalcrit/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 6b737e02f..b40887259 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -417,7 +417,7 @@ const definitionListsSingleLine = { level : 'block', start(src) { return src.match(/\n[^\n]*?::[^:\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match tokenizer(src, tokens) { - const regex = /^([^\n]*?)::([^\:\>][^\n]*)(?:\n|$)/ym; + const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; let match; let endIndex = 0; const definitions = []; From adb1db1d3c28115ede5e8182755b06dcf63c39c7 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Fri, 20 Dec 2024 15:39:57 -0500 Subject: [PATCH 10/10] Revert one more regex change --- shared/naturalcrit/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index b40887259..852243d81 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -415,7 +415,7 @@ const nonbreakingSpaces = { const definitionListsSingleLine = { name : 'definitionListsSingleLine', level : 'block', - start(src) { return src.match(/\n[^\n]*?::[^:\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match + start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match tokenizer(src, tokens) { const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; let match;