Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance ExpensiMark blockquote parsing for Live Markdown Preview #602

Conversation

Skalakid
Copy link
Contributor

@Skalakid Skalakid commented Nov 13, 2023

This PR:

  • adds new shouldKeepWhitespace flag to the parser.replace function in ExpensiMark, which allows trailing whitespace in blockquotes
  • changes quote rule logic when shouldKeepWhitespace is true
  • allows trailing whitespace in blockquote in edit mode
  • allows leading spaces in blockquote in edit mode
Screen.Recording.2023-11-13.at.12.29.14.mov

Fixed Issues

$ Expensify/App#31178
$ Expensify/App#31179
$ Expensify/App#31182

Tests

To test these changes I've added proper tests connected to all needed behaviors for Live Markdown Preview. To test it by yourself you will need example app.

QA

Same as tests

Copy link

github-actions bot commented Nov 13, 2023

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@Skalakid
Copy link
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@Skalakid Skalakid changed the title Allow trailing whitespace in blockquote in edit mode Enhance ExpensiMark blockquote parsing for Live Markdown Preview Nov 13, 2023
@Skalakid Skalakid marked this pull request as ready for review November 14, 2023 08:41
@Skalakid Skalakid requested a review from a team as a code owner November 14, 2023 08:41
@melvin-bot melvin-bot bot requested review from youssef-lr and removed request for a team November 14, 2023 08:42
@@ -668,7 +668,7 @@ test('Test urls with unmatched closing parentheses autolinks correctly', () => {
testString: 'google.com/(toto))titi)',
resultString: '<a href="https://google.com/(toto)" target="_blank" rel="noreferrer noopener">google.com/(toto)</a>)titi)',
},

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

@@ -1491,3 +1490,91 @@ test('Mention', () => {
testString = '@[email protected]';
expect(parser.replace(testString)).toBe('<mention-user>@[email protected]</mention-user>');
});

describe('edit mode', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Even though this is the terminology I've used to explain the task, I would refrain from using "edit mode" here since ExpensiMark is stateless and thus has no context of editing. Let's substitute it with "when should keep whitespace flag is enabled" or something related to it.

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

test('quote with space', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing important but let's emphasize the fact that here we have exactly one space.

Suggested change
test('quote with space', () => {
test('quote with single space', () => {

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

test('quote with a lot of spaces', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
test('quote with a lot of spaces', () => {
test('quote with multiple spaces', () => {

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

test('mixed blocqoutes', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
test('mixed blocqoutes', () => {
test('separate blockqoutes', () => {

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

test('nested quote and heading', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's group these tests in describe('nested heading in blockquote') to avoid repetition in test names.

});

test('nested quote and heading', () => {
const quoteTestStartString = '># Hello world';
Copy link
Contributor

Choose a reason for hiding this comment

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

test('without spaces')

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

test('nested quote and heading with space between', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
test('nested quote and heading with space between', () => {
test('with single spaces', () => {

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

test('nested quote and heading with many spaces after #', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
test('nested quote and heading with many spaces after #', () => {
test('with multiple spaces after #', () => {

Comment on lines 78 to 81
if (shouldKeepWhitespace) {
return textToProcess.replace(/^# ( *(?! )(?:(?!<pre>|\n|\r\n).)+)/gm, replacement);
}
return textToProcess.replace(/^# +(?! )((?:(?!<pre>|\n|\r\n).)+)/gm, replacement);
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of calling .replace in two places, can we assign regex conditionally, i.e.

Suggested change
if (shouldKeepWhitespace) {
return textToProcess.replace(/^# ( *(?! )(?:(?!<pre>|\n|\r\n).)+)/gm, replacement);
}
return textToProcess.replace(/^# +(?! )((?:(?!<pre>|\n|\r\n).)+)/gm, replacement);
const regexp = shouldKeepWhitespace
? /^# ( *(?! )(?:(?!<pre>|\n|\r\n).)+)/gm
: /^# +(?! )((?:(?!<pre>|\n|\r\n).)+)/gm;
return textToProcess.replace(regexp, replacement);

);
if (shouldKeepWhitespace) {
return textToProcess.replace(regex, g1 => replacement(g1, shouldKeepWhitespace));
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we skip calling modifyTextForQuote in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure, after skipping modifyTextForQuote 13 test fails since this method is trimming spaces and newlines in strings. I didn't want to break the existing behavior so I've added a new one but behind the shouldKeepWhitespace flag

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, thanks for explanation.

Copy link
Contributor

@youssef-lr youssef-lr left a comment

Choose a reason for hiding this comment

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

LGTM!

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.

3 participants