Skip to content

Commit

Permalink
Merge pull request #2704 from nextcloud/fix/link-text-escaping
Browse files Browse the repository at this point in the history
Do not remove escaping from brackets
  • Loading branch information
juliusknorr authored Jul 19, 2022
2 parents d363e83 + 8bdfcaf commit 61c83d7
Show file tree
Hide file tree
Showing 22 changed files with 163 additions and 24 deletions.
4 changes: 2 additions & 2 deletions js/editor-rich.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor-rich.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/editor.js

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions js/editor.js.LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,23 @@
*/

/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

/**
* @copyright Copyright (c) 2022
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
2 changes: 1 addition & 1 deletion js/editor.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-files.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-files.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-public.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-text.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-text.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-viewer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-viewer.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/vendors.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/vendors.js.map

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions src/extensions/KeepSyntax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const { Mark } = require('@tiptap/core')

/**
* Keep markdown untouched
*/
const KeepSyntax = Mark.create({
name: 'keep-syntax',
parseHTML() {
return [
{
tag: 'span.keep-md',
},
]
},
renderHTML() {
return ['span', { class: 'keep-md' }, 0]
},
toMarkdown: {
open: '',
close: '',
mixable: true,
escape: false,
expelEnclosingWhitespace: true,
},

/**
* Remove mark if there were manual changes
*/
onUpdate() {
const tr = this.editor.state.tr

this.editor.state.doc.descendants((node, pos, parent, index) => {
if (node.marks.findIndex(mark => mark.type.name === this.name) !== -1) {
if (node.type.name !== 'text' || node.text.length !== 1) {
tr.removeMark(pos, pos + node.nodeSize, this.type)
}
}
})
if (tr.docChanged) {
tr.setMeta('addToHistory', false)
tr.setMeta('preventUpdate', true)
this.editor.view.dispatch(tr)
}
},
})

export default KeepSyntax
2 changes: 0 additions & 2 deletions src/extensions/Markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ const createMarkdownSerializer = ({ nodes, marks }) => {
),
serialize(content, options) {
return this.serializer.serialize(content, { ...options, tightLists: true })
.split('\\[').join('[')
.split('\\]').join(']')
},
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/extensions/RichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import CodeBlock from '@tiptap/extension-code-block'
import HorizontalRule from '@tiptap/extension-horizontal-rule'
import Dropcursor from '@tiptap/extension-dropcursor'
import HardBreak from './HardBreak.js'
import KeepSyntax from './KeepSyntax.js'
import Table from './../nodes/Table.js'
import Image from './../nodes/Image.js'
import Heading from './../nodes/Heading.js'
Expand Down Expand Up @@ -87,6 +88,7 @@ export default Extension.create({
inline: true,
}),
Dropcursor,
KeepSyntax,
]
if (this.options.link !== false) {
defaultExtensions.push(Link.configure({
Expand Down
2 changes: 2 additions & 0 deletions src/extensions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Collaboration from './Collaboration.js'
import Markdown from './Markdown.js'
import PlainText from './PlainText.js'
import RichText from './RichText.js'
import KeepSyntax from './KeepSyntax.js'

export {
Emoji,
Expand All @@ -38,4 +39,5 @@ export {
Markdown,
PlainText,
RichText,
KeepSyntax,
}
2 changes: 2 additions & 0 deletions src/markdownit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import taskLists from '@hedgedoc/markdown-it-task-lists'
import underline from './underline.js'
import splitMixedLists from './splitMixedLists.js'
import callouts from './callouts.js'
import keepSyntax from './keepSyntax.js'

const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.enable('strikethrough')
Expand All @@ -11,5 +12,6 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.use(splitMixedLists)
.use(underline)
.use(callouts)
.use(keepSyntax)

export default markdownit
59 changes: 59 additions & 0 deletions src/markdownit/keepSyntax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @copyright Copyright (c) 2022
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

/**
* Add a mark for keeping special markdown syntax unescaped
*
* @param {object} md Markdown object
*/
export default function keepSyntax(md) {
const escaped = /((?<=\n)[#\-*+>]|[`*\\~[\]]+)/

md.core.ruler.before('text_join', 'tag-markdown-syntax', state => {
const open = new state.Token('keep_md_open', 'span', 1)
open.attrSet('class', 'keep-md')
const close = new state.Token('keep_md_close', 'span', -1)

for (let i = 0; i < state.tokens.length; i++) {
const block = state.tokens[i]
if (block.type !== 'inline') continue

for (let j = 0; j < block.children.length; j++) {
const token = block.children[j]
if (token.type === 'text') {
const match = escaped.exec(token.content)
if (match) {
const contentNext = match.index + match[0].length
block.children.splice(j, 1,
Object.assign({}, token, { content: token.content.slice(0, match.index) }),
Object.assign({}, open),
Object.assign({}, token, { content: token.content.slice(match.index, contentNext) }),
Object.assign({}, close),
Object.assign({}, token, { content: token.content.slice(contentNext) })
)
j += 3
}
}
}
}

return false
})
}
11 changes: 10 additions & 1 deletion src/tests/markdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ describe('Commonmark', () => {
const expected = entry.markdown.includes('__')
? entry.html.replace(/<strong>/g, '<u>').replace(/<\/strong>/g, '</u>')
: entry.html
expect(markdownit.render(entry.markdown)).toBe(expected)
// Ignore special markup for untouched markdown
expect(markdownit.render(entry.markdown).replace(/<span class="keep-md">([^<]+)<\/span>/g, '$1')).toBe(expected)
})
})
})
Expand Down Expand Up @@ -85,6 +86,8 @@ describe('Markdown though editor', () => {
test('links', () => {
expect(markdownThroughEditor('[test](foo)')).toBe('[test](foo)')
expect(markdownThroughEditor('[test](foo "bar")')).toBe('[test](foo "bar")')
// Issue #2703
expect(markdownThroughEditor('[bar\\\\]: /uri\n\n[bar\\\\]')).toBe('[bar\\\\](/uri)')
})
test('images', () => {
expect(markdownThroughEditor('![test](foo)')).toBe('![test](foo)')
Expand All @@ -95,6 +98,12 @@ describe('Markdown though editor', () => {
test('code block', () => {
expect(markdownThroughEditor('```\n<?php echo "Hello World";\n```')).toBe('```\n<?php echo "Hello World";\n```')
})
test('markdown untouched', () => {
// Issue #2703
expect(markdownThroughEditor('[bar\\\\]: /uri\n\n[bar\\\\]')).toBe('[bar\\\\](/uri)')
expect(markdownThroughEditor('## Test \\')).toBe('## Test \\')
expect(markdownThroughEditor('- [ [asd](sdf)')).toBe('* [ [asd](sdf)')
})
test('checkboxes', () => {
// Invalid ones but should be syntactical unchanged
expect(markdownThroughEditor('- [F] asd')).toBe('* [F] asd')
Expand Down

0 comments on commit 61c83d7

Please sign in to comment.