From 8cbb9614932ab684f2cf85624739369ed9e7757e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 22 Nov 2018 05:02:38 +0900 Subject: [PATCH] [MFM] Improve URL parsing Fix #3368 --- src/mfm/parser.ts | 20 +++++++++++++++++--- test/mfm.ts | 36 +++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/mfm/parser.ts b/src/mfm/parser.ts index 5acba7867..047124e58 100644 --- a/src/mfm/parser.ts +++ b/src/mfm/parser.ts @@ -245,11 +245,25 @@ const mfm = P.createLanguage({ const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/); if (!match) return P.makeFailure(i, 'not a url'); let url = match[0]; - const before = input[i - 1]; + let pendingBracket = 0; + const end = url.split('').findIndex(char => { + if (char == ')') { + if (pendingBracket > 0) { + pendingBracket--; + return false; + } else { + return true; + } + } else if (char == '(') { + pendingBracket++; + return false; + } else { + return false; + } + }); + if (end > 0) url = url.substr(0, end); if (url.endsWith('.')) url = url.substr(0, url.lastIndexOf('.')); if (url.endsWith(',')) url = url.substr(0, url.lastIndexOf(',')); - if (url.endsWith(')') && before == '(') url = url.substr(0, url.lastIndexOf(')')); - if (url.endsWith(']') && before == '[') url = url.substr(0, url.lastIndexOf(']')); return P.makeSuccess(i + url.length, url); }) .map(x => makeNode('url', { url: x })), diff --git a/test/mfm.ts b/test/mfm.ts index d99bb2bac..8447e798e 100644 --- a/test/mfm.ts +++ b/test/mfm.ts @@ -381,6 +381,15 @@ describe('Text', () => { ], tokens); }); + it('ignore parent brackets 2', () => { + const tokens = analyze('(foo https://example.com/foo)'); + assert.deepEqual([ + text('(foo '), + node('url', { url: 'https://example.com/foo' }), + text(')') + ], tokens); + }); + it('ignore parent brackets with internal brackets', () => { const tokens = analyze('(https://example.com/foo(bar))'); assert.deepEqual([ @@ -391,13 +400,26 @@ describe('Text', () => { }); }); - it('link', () => { - const tokens = analyze('[foo](https://example.com)'); - assert.deepEqual([ - nodeWithChildren('link', [ - text('foo') - ], { url: 'https://example.com', silent: false }) - ], tokens); + describe('link', () => { + it('simple', () => { + const tokens = analyze('[foo](https://example.com)'); + assert.deepEqual([ + nodeWithChildren('link', [ + text('foo') + ], { url: 'https://example.com', silent: false }) + ], tokens); + }); + + it('in text', () => { + const tokens = analyze('before[foo](https://example.com)after'); + assert.deepEqual([ + text('before'), + nodeWithChildren('link', [ + text('foo') + ], { url: 'https://example.com', silent: false }), + text('after'), + ], tokens); + }); }); it('emoji', () => {