diff --git a/src/mfm/parse.ts b/src/mfm/parse.ts index ee434d8dd9..01c8cc7d7a 100644 --- a/src/mfm/parse.ts +++ b/src/mfm/parse.ts @@ -26,45 +26,6 @@ export default (source: string): Node[] => { nodes = concatText(nodes); concatTextRecursive(nodes); - function getBeforeTextNode(node: Node): Node { - if (node == null) return null; - if (node.name == 'text') return node; - if (node.children) return getBeforeTextNode(node.children[node.children.length - 1]); - return null; - } - - function getAfterTextNode(node: Node): Node { - if (node == null) return null; - if (node.name == 'text') return node; - if (node.children) return getBeforeTextNode(node.children[0]); - return null; - } - - function isBlockNode(node: Node): boolean { - return ['blockCode', 'center', 'quote', 'title'].includes(node.name); - } - - /** - * ブロック要素の前後にある改行を削除します - * (ブロック要素自体が改行の役割を果たすため、余計に改行されてしまう) - * @param nodes - */ - const removeNeedlessLineBreaks = (nodes: Node[]) => { - nodes.forEach((node, i) => { - if (node.children) removeNeedlessLineBreaks(node.children); - if (isBlockNode(node)) { - const before = getBeforeTextNode(nodes[i - 1]); - const after = getAfterTextNode(nodes[i + 1]); - if (before && before.props.text.endsWith('\n')) { - before.props.text = before.props.text.substring(0, before.props.text.length - 1); - } - if (after && after.props.text.startsWith('\n')) { - after.props.text = after.props.text.substring(1); - } - } - }); - }; - const removeEmptyTextNodes = (nodes: Node[]) => { nodes.forEach(n => { if (n.children) { @@ -74,8 +35,6 @@ export default (source: string): Node[] => { return nodes.filter(n => !(n.name == 'text' && n.props.text == '')); }; - removeNeedlessLineBreaks(nodes); - nodes = removeEmptyTextNodes(nodes); return nodes; diff --git a/src/mfm/parser.ts b/src/mfm/parser.ts index 9e47baf893..467097d4f1 100644 --- a/src/mfm/parser.ts +++ b/src/mfm/parser.ts @@ -254,7 +254,7 @@ const mfm = P.createLanguage({ const qInner = quote.join('\n').replace(/^>/gm, '').replace(/^ /gm, ''); if (qInner == '') return P.makeFailure(i, 'not a quote'); const contents = r.root.tryParse(qInner); - return P.makeSuccess(i + quote.join('\n').length, makeNodeWithChildren('quote', contents)); + return P.makeSuccess(i + quote.join('\n').length + 1, makeNodeWithChildren('quote', contents)); })), //#endregion diff --git a/test/mfm.ts b/test/mfm.ts index c89152f8a2..bfbf595f1c 100644 --- a/test/mfm.ts +++ b/test/mfm.ts @@ -299,6 +299,7 @@ describe('Text', () => { nodeWithChildren('quote', [ text('foo') ]), + text('\n'), nodeWithChildren('quote', [ text('bar') ]), @@ -358,7 +359,7 @@ describe('Text', () => { it('with before and after texts', () => { const tokens = analyze('before\n> foo\nafter'); assert.deepEqual([ - text('before'), + text('before\n'), nodeWithChildren('quote', [ text('foo') ]), @@ -366,6 +367,24 @@ describe('Text', () => { ], tokens); }); + it('multiple quotes', () => { + const tokens = analyze('> foo\nbar\n\n> foo\nbar\n\n> foo\nbar'); + assert.deepEqual([ + nodeWithChildren('quote', [ + text('foo') + ]), + text('bar\n\n'), + nodeWithChildren('quote', [ + text('foo') + ]), + text('bar\n\n'), + nodeWithChildren('quote', [ + text('foo') + ]), + text('bar'), + ], tokens); + }); + it('require line break before ">"', () => { const tokens = analyze('foo>bar'); assert.deepEqual([ @@ -388,11 +407,11 @@ describe('Text', () => { it('trim line breaks', () => { const tokens = analyze('foo\n\n>a\n>>b\n>>\n>>>\n>>>c\n>>>\n>d\n\n'); assert.deepEqual([ - text('foo\n'), + text('foo\n\n'), nodeWithChildren('quote', [ - text('a'), + text('a\n'), nodeWithChildren('quote', [ - text('b\n'), + text('b\n\n'), nodeWithChildren('quote', [ text('\nc\n') ]) @@ -664,7 +683,7 @@ describe('Text', () => { it('with before and after texts', () => { const tokens = analyze('before\n【foo】\nafter'); assert.deepEqual([ - text('before'), + text('before\n'), nodeWithChildren('title', [ text('foo') ]),