Skip to content

Commit

Permalink
Merge pull request #691 from software-mansion-labs/#41696-quotes-with…
Browse files Browse the repository at this point in the history
…-space

Recognise only blockquotes followed by space
  • Loading branch information
thienlnam authored May 8, 2024
2 parents 182ef7f + f4b1253 commit a018512
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 31 deletions.
87 changes: 58 additions & 29 deletions __tests__/ExpensiMark-HTML-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ test('Test heading markdown replacement', () => {

// Sections starting with > are successfully wrapped with <blockquote></blockquote>
test('Test quote markdown replacement', () => {
const quoteTestStartString = '>This is a *quote* that started on a new line.\nHere is a >quote that did not\n```\nhere is a codefenced quote\n>it should not be quoted\n```';
const quoteTestStartString = '> This is a *quote* that started on a new line.\nHere is a >quote that did not\n```\nhere is a codefenced quote\n>it should not be quoted\n```';
const quoteTestReplacedString = '<blockquote>This is a <strong>quote</strong> that started on a new line.</blockquote>Here is a &gt;quote that did not<br /><pre>here&#32;is&#32;a&#32;codefenced&#32;quote<br />&gt;it&#32;should&#32;not&#32;be&#32;quoted<br /></pre>';

expect(parser.replace(quoteTestStartString)).toBe(quoteTestReplacedString);
Expand Down Expand Up @@ -1016,33 +1016,33 @@ test('Test autolink replacement to avoid parsing nested links', () => {
});

test('Test quotes markdown replacement with text matching inside and outside codefence without spaces', () => {
const testString = 'The next line should be quoted\n>Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence```';
const testString = 'The next line should be quoted\n> Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence```';

const resultString = 'The next line should be quoted<br /><blockquote>Hello,I’mtext</blockquote><pre>The&#32;next&#32;line&#32;should&#32;not&#32;be&#32;quoted<br />&gt;Hello,I’mtext<br />since&#32;its&#32;inside&#32;a&#32;codefence</pre>';

expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with text matching inside and outside codefence at the same line', () => {
const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```\nsince its inside a codefence';
const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```\nsince its inside a codefence';

const resultString = 'The next line should be quoted<br /><blockquote>Hello,I’mtext</blockquote>The next line should not be quoted<br /><pre>&gt;Hello,I’mtext</pre>since its inside a codefence';

expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with text matching inside and outside codefence at the end of the text', () => {
const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```';
const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```';

const resultString = 'The next line should be quoted<br /><blockquote>Hello,I’mtext</blockquote>The next line should not be quoted<br /><pre>&gt;Hello,I’mtext</pre>';

expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with text matching inside and outside codefence with quotes at the end of the text', () => {
const testString = 'The next line should be quoted\n```>Hello,I’mtext```\nThe next line should not be quoted\n>Hello,I’mtext';
const testString = 'The next line should be quoted\n```> Hello,I’mtext```\nThe next line should not be quoted\n> Hello,I’mtext';

const resultString = 'The next line should be quoted<br /><pre>&gt;Hello,I’mtext</pre>The next line should not be quoted<br /><blockquote>Hello,I’mtext</blockquote>';
const resultString = 'The next line should be quoted<br /><pre>&gt;&#32;Hello,I’mtext</pre>The next line should not be quoted<br /><blockquote>Hello,I’mtext</blockquote>';

expect(parser.replace(testString)).toBe(resultString);
});
Expand All @@ -1068,31 +1068,31 @@ test('Test quotes markdown replacement with text starts with blank quote', () =>
});

test('Test quotes markdown replacement with quotes starts with blank quote row', () => {
const testString = '> \n>test';
const testString = '> \n> test';
const resultString = '<blockquote>test</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with quotes ends with blank quote rows', () => {
const testString = '>test\n> \n>';
const testString = '> test\n> \n>';
const resultString = '<blockquote>test</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with quotes includes a middle blank quote row', () => {
const testString = '>test\n> \n>test';
const testString = '> test\n> \n> test';
const resultString = '<blockquote>test<br /><br />test</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with quotes includes multiple middle blank quote rows', () => {
const testString = '>test\n> \n> \n>test\ntest\n>test\n> \n> \n> \n>test';
const testString = '> test\n> \n> \n> test\ntest\n> test\n> \n> \n> \n> test';
const resultString = '<blockquote>test<br /><br /><br />test</blockquote>test<br /><blockquote>test<br /><br /><br /><br />test</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test quotes markdown replacement with text includes blank quotes', () => {
const testString = '> \n>quote1 line a\n> quote1 line b\ntest\n> \ntest\n>quote2 line a\n> \n> \n>quote2 line b with an empty line above';
const testString = '> \n> quote1 line a\n> quote1 line b\ntest\n> \ntest\n> quote2 line a\n> \n> \n> quote2 line b with an empty line above';
const resultString = '<blockquote>quote1 line a<br />quote1 line b</blockquote>test<br />&gt; <br />test<br /><blockquote>quote2 line a<br /><br /><br />quote2 line b with an empty line above</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});
Expand All @@ -1103,6 +1103,35 @@ test('Test quotes markdown replacement with text includes multiple spaces', () =
expect(parser.replace(quoteTestStartString)).toBe(quoteTestReplacedString);
});

test('Test markdown quotes without spaces after > should not be parsed', () => {
const testString = '>test';
const resultString = '&gt;test';
expect(parser.replace(testString)).toBe(resultString);
});
test('Test markdown quotes without spaces after > should not be parsed', () => {
const testString = '>>>test';
const resultString = '&gt;&gt;&gt;test';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test markdown quotes without spaces after > should not be parsed', () => {
const testString = '> >>test';
const resultString = '<blockquote>&gt;&gt;test</blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test markdown quotes without spaces after > should not be parsed', () => {
const testString = '> > > test';
const resultString = '<blockquote><blockquote><blockquote>test</blockquote></blockquote></blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Test markdown quotes without spaces after > should not be parsed', () => {
const testString = '>>> test';
const resultString = '<blockquote><blockquote><blockquote>test</blockquote></blockquote></blockquote>';
expect(parser.replace(testString)).toBe(resultString);
});

test('Single char matching', () => {
const testString = ' *1* char _1_ char ~1~ char';
const resultString = ' <strong>1</strong> char <em>1</em> char <del>1</del> char';
Expand Down Expand Up @@ -1687,8 +1716,8 @@ test('Mention', () => {

describe('when should keep raw input flag is enabled', () => {
test('quote without space', () => {
const quoteTestStartString = '>Hello world';
const quoteTestReplacedString = '<blockquote>Hello world</blockquote>';
const quoteTestStartString = '> Hello world';
const quoteTestReplacedString = '<blockquote> Hello world</blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});
Expand All @@ -1708,23 +1737,23 @@ describe('when should keep raw input flag is enabled', () => {
});

test('multiple quotes', () => {
const quoteTestStartString = '>Hello my\n>beautiful\n>world\n';
const quoteTestReplacedString = '<blockquote>Hello my</blockquote>\n<blockquote>beautiful</blockquote>\n<blockquote>world</blockquote>\n';
const quoteTestStartString = '> Hello my\n> beautiful\n> world\n';
const quoteTestReplacedString = '<blockquote> Hello my</blockquote>\n<blockquote> beautiful</blockquote>\n<blockquote> world</blockquote>\n';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});

test('separate blockqoutes', () => {
const quoteTestStartString = '>Lorem ipsum\ndolor\n>sit amet';
const quoteTestReplacedString = '<blockquote>Lorem ipsum</blockquote>\ndolor\n<blockquote>sit amet</blockquote>';
const quoteTestStartString = '> Lorem ipsum\ndolor\n> sit amet';
const quoteTestReplacedString = '<blockquote> Lorem ipsum</blockquote>\ndolor\n<blockquote> sit amet</blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});

describe('nested heading in blockquote', () => {
test('without spaces', () => {
const quoteTestStartString = '># Hello world';
const quoteTestReplacedString = '<blockquote><h1>Hello world</h1></blockquote>';
const quoteTestStartString = '> # Hello world';
const quoteTestReplacedString = '<blockquote> <h1>Hello world</h1></blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});
Expand All @@ -1737,39 +1766,39 @@ describe('when should keep raw input flag is enabled', () => {
});

test('with multiple spaces after #', () => {
const quoteTestStartString = '># Hello world';
const quoteTestReplacedString = '<blockquote><h1> Hello world</h1></blockquote>';
const quoteTestStartString = '> # Hello world';
const quoteTestReplacedString = '<blockquote> <h1> Hello world</h1></blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});
});

describe('trailing whitespace after blockquote', () => {
test('nothing', () => {
const quoteTestStartString = '>Hello world!';
const quoteTestReplacedString = '<blockquote>Hello world!</blockquote>';
const quoteTestStartString = '> Hello world!';
const quoteTestReplacedString = '<blockquote> Hello world!</blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});

test('space', () => {
const quoteTestStartString = '>Hello world ';
const quoteTestReplacedString = '<blockquote>Hello world </blockquote>';
const quoteTestStartString = '> Hello world ';
const quoteTestReplacedString = '<blockquote> Hello world </blockquote>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});

test('newline', () => {
const quoteTestStartString = '>Hello world\n';
const quoteTestReplacedString = '<blockquote>Hello world</blockquote>\n';
const quoteTestStartString = '> Hello world\n';
const quoteTestReplacedString = '<blockquote> Hello world</blockquote>\n';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});
});

test('quote with other markdowns', () => {
const quoteTestStartString = '>This is a *quote* that started on a new line.\nHere is a >quote that did not\n```\nhere is a codefenced quote\n>it should not be quoted\n```';
const quoteTestReplacedString = '<blockquote>This is a <strong>quote</strong> that started on a new line.</blockquote>\nHere is a &gt;quote that did not\n<pre data-code-raw=\"\nhere is a codefenced quote\n&gt;it should not be quoted\n\">here&#32;is&#32;a&#32;codefenced&#32;quote\n&gt;it&#32;should&#32;not&#32;be&#32;quoted\n</pre>';
const quoteTestStartString = '> This is a *quote* that started on a new line.\nHere is a >quote that did not\n```\nhere is a codefenced quote\n>it should not be quoted\n```';
const quoteTestReplacedString = '<blockquote> This is a <strong>quote</strong> that started on a new line.</blockquote>\nHere is a &gt;quote that did not\n<pre data-code-raw=\"\nhere is a codefenced quote\n&gt;it should not be quoted\n\">here&#32;is&#32;a&#32;codefenced&#32;quote\n&gt;it&#32;should&#32;not&#32;be&#32;quoted\n</pre>';

expect(parser.replace(quoteTestStartString, {shouldKeepRawInput: true})).toBe(quoteTestReplacedString);
});
Expand Down
4 changes: 2 additions & 2 deletions lib/ExpensiMark.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export default class ExpensiMark {
// block quotes naturally appear on their own line. Blockquotes should not appear in code fences or
// inline code blocks. A single prepending space should be stripped if it exists
process: (textToProcess, replacement, shouldKeepRawInput = false) => {
const regex = /^&gt; *(?! )(?![^<]*(?:<\/pre>|<\/code>))([^\v\n\r]+)/gm;
const regex = /^(?:&gt;)+ +(?! )(?![^<]*(?:<\/pre>|<\/code>))([^\v\n\r]+)/gm;
const replaceFunction = (g1) => replacement(g1, shouldKeepRawInput);
if (shouldKeepRawInput) {
return textToProcess.replace(regex, replaceFunction);
Expand All @@ -268,7 +268,7 @@ export default class ExpensiMark {
const filterRules = ['heading1'];

// if we don't reach the max quote depth we allow the recursive call to process possible quote
if (this.currentQuoteDepth < this.maxQuoteDepth - 1) {
if (this.currentQuoteDepth < this.maxQuoteDepth - 1 || isStartingWithSpace) {
filterRules.push('quote');
this.currentQuoteDepth++;
}
Expand Down

0 comments on commit a018512

Please sign in to comment.