From c96634e00b409aeb203f6234e9a319ed8d05a9e2 Mon Sep 17 00:00:00 2001 From: eternal-flame-AD Date: Wed, 12 Nov 2025 17:55:42 -0600 Subject: [PATCH] enhance: support text escaping (#152) Signed-off-by: eternal-flame-AD --- src/internal/parser.ts | 8 +++++++- test/parser.ts | 26 ++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/internal/parser.ts b/src/internal/parser.ts index 2eaf89c..286b1ed 100644 --- a/src/internal/parser.ts +++ b/src/internal/parser.ts @@ -16,6 +16,7 @@ type Args = Record; const space = P.regexp(/[\u0020\u3000\t]/); const alphaAndNum = P.regexp(/[a-z0-9]/i); const newLine = P.alt([P.crlf, P.cr, P.lf]); +const asciiPunctuation = P.regexp(/[!#$%&'()*+,-./:;<=>?@\[\\\]^_`{|}~]/); function seqOrText[]>(...parsers: Parsers): P.Parser | string> { return new P.Parser | string>((input, index, state) => { @@ -794,5 +795,10 @@ export const language = P.createLanguage({ }); }, - text: () => P.char, + text: () => { + return P.alt([ + P.seq(P.str('\\'), asciiPunctuation).select(1), + P.char, + ]); + }, }); diff --git a/test/parser.ts b/test/parser.ts index 0f7575a..56c9790 100644 --- a/test/parser.ts +++ b/test/parser.ts @@ -303,14 +303,14 @@ hoge`; test('行末以外に閉じタグがある場合はマッチしない', () => { const input = '\\[aaa\\]after'; const output = [ - TEXT('\\[aaa\\]after') + TEXT('[aaa]after') ]; assert.deepStrictEqual(mfm.parse(input), output); }); test('行頭以外に開始タグがある場合はマッチしない', () => { const input = 'before\\[aaa\\]'; const output = [ - TEXT('before\\[aaa\\]') + TEXT('before[aaa]') ]; assert.deepStrictEqual(mfm.parse(input), output); }); @@ -1086,6 +1086,28 @@ hoge`; assert.deepStrictEqual(mfm.parse(input), output); }); + test('with escaped text', () => { + const input = 'Ai said: ["Misskey.io \\[is\\] the official instance"](https://xn--931a.moe/).'; + const output = [ + TEXT('Ai said: '), + LINK(false, 'https://xn--931a.moe/', [ + TEXT('"Misskey.io [is] the official instance"') + ]), + TEXT('.') + ]; + assert.deepStrictEqual(mfm.parse(input), output); + }); + + test('with double escaped text', () => { + const input = 'Ai said: ["Misskey.io \\\\[is\\\\] the official instance"](https://xn--931a.moe/).'; + const output = [ + TEXT('Ai said: '), + LINK(false, 'https://xn--931a.moe/', [ + TEXT('"Misskey.io \\[is\\] the official instance"') + ]), + ]; + }); + test('with angle brackets url', () => { const input = '[official instance]().'; const output = [