diff --git a/frontend_tests/node_tests/markdown.js b/frontend_tests/node_tests/markdown.js index fce285420e..0f4d2c60b5 100644 --- a/frontend_tests/node_tests/markdown.js +++ b/frontend_tests/node_tests/markdown.js @@ -184,7 +184,7 @@ stream_data.add_sub(amp_stream); run_test("fenced_block_defaults", () => { const input = "\n```\nfenced code\n```\n\nand then after\n"; const expected = - '\n\n
fenced code\n
\n\n\n\nand then after\n\n'; + '\n\n
fenced code\n
\n\n\nand then after\n\n'; const output = fenced_code.process_fenced_code(input); assert.equal(output, expected); }); @@ -294,13 +294,13 @@ run_test("marked", () => { { input: "\n```\nfenced code\n```\n\nand then after\n", expected: - '
fenced code\n
\n\n\n

and then after

', + '
fenced code\n
\n

and then after

', }, { input: "\n```\n fenced code trailing whitespace \n```\n\nand then after\n", expected: - '
    fenced code trailing whitespace\n
\n\n\n

and then after

', + '
    fenced code trailing whitespace\n
\n

and then after

', }, { input: "* a\n* list \n* here", @@ -309,12 +309,12 @@ run_test("marked", () => { { input: "\n```c#\nfenced code special\n```\n\nand then after\n", expected: - '
fenced code special\n
\n\n\n

and then after

', + '
fenced code special\n
\n

and then after

', }, { input: "\n```vb.net\nfenced code dot\n```\n\nand then after\n", expected: - '
fenced code dot\n
\n\n\n

and then after

', + '
fenced code dot\n
\n

and then after

', }, { input: "Some text first\n* a\n* list \n* here\n\nand then after", diff --git a/requirements/dev.txt b/requirements/dev.txt index 293e88fe62..6f4d2d98bc 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -578,9 +578,9 @@ lxml==4.5.2 \ markdown-include==0.6.0 \ --hash=sha256:6f5d680e36f7780c7f0f61dca53ca581bd50d1b56137ddcd6353efafa0c3e4a2 \ # via -r requirements/common.in -markdown==3.2.2 \ - --hash=sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17 \ - --hash=sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59 \ +markdown==3.3.1 \ + --hash=sha256:10db1204a6c4aff7c7cf3cf25cc02761703baea54b6fb5e2b9ce2c186d81116f \ + --hash=sha256:c3ce9ebb035c078cac0f2036068d054e7dc34354eeecc49c173c33c96b124af6 \ # via -r requirements/common.in, markdown-include markupsafe==1.1.1 \ --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ diff --git a/requirements/prod.txt b/requirements/prod.txt index 785c16edbe..bb173de28d 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -407,9 +407,9 @@ lxml==4.5.2 \ markdown-include==0.6.0 \ --hash=sha256:6f5d680e36f7780c7f0f61dca53ca581bd50d1b56137ddcd6353efafa0c3e4a2 \ # via -r requirements/common.in -markdown==3.2.2 \ - --hash=sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17 \ - --hash=sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59 \ +markdown==3.3.1 \ + --hash=sha256:10db1204a6c4aff7c7cf3cf25cc02761703baea54b6fb5e2b9ce2c186d81116f \ + --hash=sha256:c3ce9ebb035c078cac0f2036068d054e7dc34354eeecc49c173c33c96b124af6 \ # via -r requirements/common.in, markdown-include markupsafe==1.1.1 \ --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ diff --git a/static/shared/js/fenced_code.js b/static/shared/js/fenced_code.js index b65f42a817..cf29e276e3 100644 --- a/static/shared/js/fenced_code.js +++ b/static/shared/js/fenced_code.js @@ -51,7 +51,7 @@ export function wrap_code(code, lang) { } // Trim trailing \n until there's just one left // This mirrors how pygments handles code input - return header + _.escape(code.replace(/^\n+|\n+$/g, "")) + "\n\n"; + return header + _.escape(code.replace(/^\n+|\n+$/g, "")) + "\n"; } function wrap_quote(text) { diff --git a/static/third/marked/lib/marked.js b/static/third/marked/lib/marked.js index 12b69d4179..a8fd74bbba 100644 --- a/static/third/marked/lib/marked.js +++ b/static/third/marked/lib/marked.js @@ -1188,8 +1188,6 @@ Parser.prototype.parse = function(src) { safe = stash[2]; if (!safe) { html = escape(html); - } else { - html += '\n'; } output = output.replace('

' + key + '

', html) } diff --git a/version.py b/version.py index 60a0ababf4..663d32cf94 100644 --- a/version.py +++ b/version.py @@ -44,4 +44,4 @@ API_FEATURE_LEVEL = 34 # historical commits sharing the same major version, in which case a # minor version bump suffices. -PROVISION_VERSION = '111.3' +PROVISION_VERSION = '112.0' diff --git a/zerver/lib/markdown/fenced_code.py b/zerver/lib/markdown/fenced_code.py index ed116b8860..50f127cc50 100644 --- a/zerver/lib/markdown/fenced_code.py +++ b/zerver/lib/markdown/fenced_code.py @@ -391,7 +391,7 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor): lang=(lang or None), noclasses=self.codehilite_conf['noclasses'][0]) - code = highliter.hilite() + code = highliter.hilite().rstrip('\n') else: code = CODE_WRAP.format(langclass, self._escape(text)) diff --git a/zerver/tests/fixtures/markdown_test_cases.json b/zerver/tests/fixtures/markdown_test_cases.json index 291414d3fd..5dd482675a 100644 --- a/zerver/tests/fixtures/markdown_test_cases.json +++ b/zerver/tests/fixtures/markdown_test_cases.json @@ -23,7 +23,7 @@ { "name": "ampampamp", "input": "& & &\n~~~~\n& & &\n~~~~\n & & &", - "expected_output": "

& & &

\n
& & &\n
\n\n\n
& & &\n
" + "expected_output": "

& & &

\n
& & &\n
\n
& & &\n
" }, { "name": "basic_paragraph", @@ -34,8 +34,8 @@ { "name": "codeblock_multiline", "input": "Hamlet once said\n~~~~\ndef func():\n x = 1\n\n y = 2\n\n z = 3\n~~~~\nAnd all was good.", - "expected_output": "

Hamlet once said

\n
def func():\n    x = 1\n\n    y = 2\n\n    z = 3\n
\n\n\n

And all was good.

", - "text_content": "Hamlet once said\ndef func():\n x = 1\n\n y = 2\n\n z = 3\n\n\n\nAnd all was good." + "expected_output": "

Hamlet once said

\n
def func():\n    x = 1\n\n    y = 2\n\n    z = 3\n
\n

And all was good.

", + "text_content": "Hamlet once said\ndef func():\n x = 1\n\n y = 2\n\n z = 3\n\nAnd all was good." }, { "name": "test", @@ -45,8 +45,8 @@ { "name": "codeblock_trailing_whitespace", "input": "Hamlet once said\n~~~~\ndef func():\n x = 1\n\n y = 2\t\t\n\n z = 3 \n~~~~\nAnd all was good.", - "expected_output": "

Hamlet once said

\n
def func():\n    x = 1\n\n    y = 2\n\n    z = 3\n
\n\n\n

And all was good.

", - "text_content": "Hamlet once said\ndef func():\n x = 1\n\n y = 2\n\n z = 3\n\n\n\nAnd all was good." + "expected_output": "

Hamlet once said

\n
def func():\n    x = 1\n\n    y = 2\n\n    z = 3\n
\n

And all was good.

", + "text_content": "Hamlet once said\ndef func():\n x = 1\n\n y = 2\n\n z = 3\n\nAnd all was good." }, { "name": "inline_code_spaces", @@ -63,14 +63,14 @@ { "name": "codeblock_backticks", "input": "\n```\nfenced code\n```\n\n```inline code```\n", - "expected_output": "
fenced code\n
\n\n\n

inline code

", - "text_content": "fenced code\n\n\n\ninline code" + "expected_output": "
fenced code\n
\n

inline code

", + "text_content": "fenced code\n\ninline code" }, { "name": "hanging_multi_codeblock", "input": "Hamlet said:\n~~~~\ndef speak(self):\n x = 1\n# Comment to make this code block longer to test Trac #1162\n~~~~\n\nThen he mentioned ````y = 4 + x**2```` and\n~~~~\ndef foobar(self):\n return self.baz()", - "expected_output": "

Hamlet said:

\n
def speak(self):\n    x = 1\n# Comment to make this code block longer to test Trac #1162\n
\n\n\n

Then he mentioned y = 4 + x**2 and

\n
def foobar(self):\n    return self.baz()\n
", - "text_content": "Hamlet said:\ndef speak(self):\n x = 1\n# Comment to make this code block longer to test Trac #1162\n\n\n\nThen he mentioned y = 4 + x**2 and\ndef foobar(self):\n return self.baz()\n" + "expected_output": "

Hamlet said:

\n
def speak(self):\n    x = 1\n# Comment to make this code block longer to test Trac #1162\n
\n

Then he mentioned y = 4 + x**2 and

\n
def foobar(self):\n    return self.baz()\n
", + "text_content": "Hamlet said:\ndef speak(self):\n x = 1\n# Comment to make this code block longer to test Trac #1162\n\nThen he mentioned y = 4 + x**2 and\ndef foobar(self):\n return self.baz()\n" }, { "name": "fenced_quote", @@ -87,7 +87,7 @@ { "name": "complexly_nested_quote", "input": "I heard about this second hand...\n~~~ quote\n\nHe said:\n~~~ quote\nThe customer is complaining.\n\nThey looked at this code:\n``` \ndef hello(): print 'hello\n```\nThey would prefer:\n~~~\ndef hello()\n puts 'hello'\nend\n~~~\n\nPlease advise.\n~~~\n\nShe said:\n~~~ quote\nJust send them this:\n```\necho \"hello\n\"\n```\n~~~", - "expected_output": "

I heard about this second hand...

\n
\n

He said:

\n
\n

The customer is complaining.

\n

They looked at this code:

\n
def hello(): print 'hello\n
\n\n\n

They would prefer:

\n
\n

def hello()
\n puts 'hello'
\nend

\n
\n

Please advise.

\n
She said:\n~~~ quote\nJust send them this:\n```\necho "hello\n"\n```\n
", + "expected_output": "

I heard about this second hand...

\n
\n

He said:

\n
\n

The customer is complaining.

\n

They looked at this code:

\n
def hello(): print 'hello\n
\n

They would prefer:

\n
\n

def hello()
\n puts 'hello'
\nend

\n
\n

Please advise.

\n
She said:\n~~~ quote\nJust send them this:\n```\necho "hello\n"\n```\n
", "text_content": "I heard about this second hand...\n> He said:\n> > The customer is complaining.\n> > They looked at this code:\n> > def hello(): print 'hello\n> > They would prefer:\n> def hello()\n> puts 'hello'\n> end\n\nPlease advise.\nShe said:\n~~~ quote\nJust send them this:\n```\necho \"hello\n\"\n```\n" }, { @@ -860,39 +860,39 @@ { "name": "spoilers_fenced_spoiler", "input": "```spoiler header\ncontent\n```\noutside spoiler\n", - "expected_output": "
\n\n

header

\n
\n\n

content

\n
\n\n

outside spoiler

", + "expected_output": "
\n

header

\n
\n

content

\n
\n

outside spoiler

", "text_content": "header (…)\noutside spoiler" }, { "name": "spoilers_empty_header", "input": "```spoiler\ncontent\n```\noutside spoiler\n", - "expected_output": "
\n\n
\n\n

content

\n
\n\n

outside spoiler

", + "expected_output": "
\n
\n

content

\n
\n

outside spoiler

", "text_content": "(…)\noutside spoiler" }, { "name": "spoilers_script_tags", "input": "```spoiler \n\n```", - "expected_output": "
\n\n

<script>alert(1)</script>

\n
\n\n

<script>alert(1)</script>

\n
", - "marked_expected_output": "
\n\n

<script>alert(1)</script>\n\n

\n
\n\n

<script>alert(1)</script>\n\n

\n
", + "expected_output": "
\n

<script>alert(1)</script>

\n
\n

<script>alert(1)</script>

\n
", + "marked_expected_output": "
\n

<script>alert(1)</script>\n\n

\n
\n

<script>alert(1)</script>\n\n

\n
", "text_content": " (…)\n" }, { "name": "spoilers_block_quote", "input": "~~~quote\n```spoiler header\ncontent\n```\noutside spoiler\n~~~\noutside quote", - "expected_output": "
\n
\n\n

header

\n
\n\n

content

\n
\n\n

outside spoiler

\n
\n

outside quote

", + "expected_output": "
\n
\n

header

\n
\n

content

\n
\n

outside spoiler

\n
\n

outside quote

", "text_content": "> header (…)\n> outside spoiler\n\noutside quote" }, { "name": "spoilers_with_header_markdown", "input": "```spoiler [Header](https://example.com) :smile:\ncontent\n```", - "expected_output": "
\n\n

Header :smile:

\n
\n\n

content

\n
", + "expected_output": "
\n

Header :smile:

\n
\n

content

\n
", "text_content": "Header 🙂 (…)\n" }, { "name": "spoiler_with_inline_image", "input": "```spoiler header\nContent http://example.com/image.png\n```", - "expected_output": "
\n\n

header

\n
\n\n

Content http://example.com/image.png

\n
", - "marked_expected_output": "
\n\n

header

\n
\n\n

Content http://example.com/image.png

\n
", + "expected_output": "
\n

header

\n
\n

Content http://example.com/image.png

\n
", + "marked_expected_output": "
\n

header

\n
\n

Content http://example.com/image.png

\n
", "text_content": "header (…)\n" } ], diff --git a/zerver/tests/test_import_export.py b/zerver/tests/test_import_export.py index 3d521c51c1..8968da4923 100644 --- a/zerver/tests/test_import_export.py +++ b/zerver/tests/test_import_export.py @@ -990,7 +990,7 @@ class ImportExportTest(ZulipTestCase): original_msg = Message.objects.get(content=special_characters_message, sender__realm=original_realm) self.assertEqual( original_msg.rendered_content, - '
'\n
\n\n\n' + '
'\n
\n' f'

@Polonius

', ) imported_polonius_user = UserProfile.objects.get(delivery_email=self.example_email("polonius"), diff --git a/zerver/tests/test_markdown.py b/zerver/tests/test_markdown.py index 109b30ba8d..96c344ff15 100644 --- a/zerver/tests/test_markdown.py +++ b/zerver/tests/test_markdown.py @@ -725,7 +725,7 @@ class MarkdownTest(ZulipTestCase): msg = """\n```spoiler Check out this Pycon Video\nhttps://www.youtube.com/watch?v=0c46YHS3RY8\n```""" converted = markdown_convert_wrapper(msg) - self.assertEqual(converted, '
\n\n

Check out this Pycon Video

\n
') + self.assertEqual(converted, '
\n

Check out this Pycon Video

\n
') # Test youtube urls in normal messages. msg = '[Youtube link](https://www.youtube.com/watch?v=0c46YHS3RY8)' @@ -896,7 +896,7 @@ class MarkdownTest(ZulipTestCase): msg = '```spoiler secret tweet\nTweet: http://twitter.com/wdaher/status/287977969287315456\n```' converted = markdown_convert_wrapper(msg) - rendered_spoiler = "
\n\n

secret tweet

\n
\n\n

Tweet: {}

\n{}
" + rendered_spoiler = "
\n

secret tweet

\n
\n

Tweet: {}

\n{}
" self.assertEqual(converted, rendered_spoiler.format( make_link('http://twitter.com/wdaher/status/287977969287315456'), make_inline_twitter_preview('http://twitter.com/wdaher/status/287977969287315456', normal_tweet_html))) @@ -2134,8 +2134,6 @@ class MarkdownTest(ZulipTestCase):
&copy;
             &copy;
             
- -

Test quote:

©