mirror of
https://activitypub.software/TransFem-org/sfm-js
synced 2024-11-26 16:03:02 +00:00
Merge branch 'develop' into master
This commit is contained in:
commit
d238c1dad4
4 changed files with 210 additions and 14 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mfm-js",
|
||||
"version": "0.14.1",
|
||||
"version": "0.15.0",
|
||||
"description": "An MFM parser implementation with PEG.js",
|
||||
"main": "./built/index.js",
|
||||
"types": "./built/index.d.ts",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"tsc": "tsc",
|
||||
"tsd": "tsd",
|
||||
"parse": "node ./built/cli/parse",
|
||||
"parse-plain": "node ./built/cli/parsePlain",
|
||||
"test": "mocha -r ts-node/register 'test/**/*.ts' && npm run tsd"
|
||||
},
|
||||
"repository": {
|
||||
|
|
46
src/cli/parsePlain.ts
Normal file
46
src/cli/parsePlain.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import { performance } from 'perf_hooks';
|
||||
import inputLine, { InputCanceledError } from './misc/inputLine';
|
||||
import { parsePlain } from '..';
|
||||
|
||||
async function entryPoint() {
|
||||
console.log('intaractive plain parser');
|
||||
|
||||
while (true) {
|
||||
let input: string;
|
||||
try {
|
||||
input = await inputLine('> ');
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof InputCanceledError) {
|
||||
console.log('bye.');
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// replace special chars
|
||||
input = input
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\t/g, '\t')
|
||||
.replace(/\\u00a0/g, '\u00a0');
|
||||
|
||||
try {
|
||||
const parseTimeStart = performance.now();
|
||||
const result = parsePlain(input);
|
||||
const parseTimeEnd = performance.now();
|
||||
console.log(JSON.stringify(result));
|
||||
const parseTime = (parseTimeEnd - parseTimeStart).toFixed(3);
|
||||
console.log(`parsing time: ${parseTime}ms`);
|
||||
}
|
||||
catch (err) {
|
||||
console.log('parsing error:');
|
||||
console.log(err);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
entryPoint()
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
});
|
|
@ -66,7 +66,7 @@ fullParser
|
|||
= nodes:(&. n:(block / inline) { return n; })* { return mergeText(nodes); }
|
||||
|
||||
plainParser
|
||||
= nodes:(&. n:(emojiCode / unicodeEmoji / text) { return n; })* { return mergeText(nodes); }
|
||||
= nodes:(&. n:(emojiCode / unicodeEmoji / plainText) { return n; })* { return mergeText(nodes); }
|
||||
|
||||
inlineParser
|
||||
= nodes:(&. n:inline { return n; })* { return mergeText(nodes); }
|
||||
|
@ -165,7 +165,7 @@ inline
|
|||
/ url
|
||||
/ link
|
||||
/ fn
|
||||
/ text
|
||||
/ inlineText
|
||||
|
||||
// inline: emoji code
|
||||
|
||||
|
@ -219,16 +219,22 @@ small
|
|||
// inline: italic
|
||||
|
||||
italic
|
||||
= italicTag
|
||||
/ italicAlt
|
||||
|
||||
italicTag
|
||||
= "<i>" content:(!"</i>" i:inline { return i; })+ "</i>"
|
||||
{
|
||||
return ITALIC(mergeText(content));
|
||||
}
|
||||
/ "*" content:$(!"*" ([a-z0-9]i / _))+ "*"
|
||||
|
||||
italicAlt
|
||||
= "*" content:$(!"*" ([a-z0-9]i / _))+ "*" &(EOF / LF / _)
|
||||
{
|
||||
const parsedContent = applyParser(content, 'inlineParser');
|
||||
return ITALIC(parsedContent);
|
||||
}
|
||||
/ "_" content:$(!"_" ([a-z0-9]i / _))+ "_"
|
||||
/ "_" content:$(!"_" ([a-z0-9]i / _))+ "_" &(EOF / LF / _)
|
||||
{
|
||||
const parsedContent = applyParser(content, 'inlineParser');
|
||||
return ITALIC(parsedContent);
|
||||
|
@ -289,7 +295,7 @@ mentionHostPart
|
|||
// inline: hashtag
|
||||
|
||||
hashtag
|
||||
= "#" content:hashtagContent
|
||||
= "#" !("\uFE0F"? "\u20E3") content:hashtagContent
|
||||
{
|
||||
return HASHTAG(content);
|
||||
}
|
||||
|
@ -382,7 +388,13 @@ fnArg
|
|||
|
||||
// inline: text
|
||||
|
||||
text
|
||||
inlineText
|
||||
= !(LF / _) . &(hashtag / mention / italicAlt) . { return text(); } // hashtag, mention, italic ignore
|
||||
/ . /* text node */
|
||||
|
||||
// inline: text (for plainParser)
|
||||
|
||||
plainText
|
||||
= . /* text node */
|
||||
|
||||
//
|
||||
|
|
151
test/parser.ts
151
test/parser.ts
|
@ -4,7 +4,29 @@ import {
|
|||
TEXT, CENTER, FN, UNI_EMOJI, MENTION, EMOJI_CODE, HASHTAG, N_URL, BOLD, SMALL, ITALIC, STRIKE, QUOTE, MATH_BLOCK, SEARCH, CODE_BLOCK, LINK
|
||||
} from '../built/index';
|
||||
|
||||
describe('parser', () => {
|
||||
describe('PlainParser', () => {
|
||||
describe('text', () => {
|
||||
it('basic', () => {
|
||||
const input = 'abc';
|
||||
const output = [TEXT('abc')];
|
||||
assert.deepStrictEqual(mfm.parsePlain(input), output);
|
||||
});
|
||||
|
||||
it('ignore hashtag', () => {
|
||||
const input = 'abc#abc';
|
||||
const output = [TEXT('abc#abc')];
|
||||
assert.deepStrictEqual(mfm.parsePlain(input), output);
|
||||
});
|
||||
|
||||
it('keycap number sign', () => {
|
||||
const input = 'abc#️⃣abc';
|
||||
const output = [TEXT('abc'), UNI_EMOJI('#️⃣'), TEXT('abc')];
|
||||
assert.deepStrictEqual(mfm.parsePlain(input), output);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('FullParser', () => {
|
||||
describe('text', () => {
|
||||
it('普通のテキストを入力すると1つのテキストノードが返される', () => {
|
||||
const input = 'abc';
|
||||
|
@ -222,6 +244,12 @@ describe('parser', () => {
|
|||
const output = [TEXT('今起きた'), UNI_EMOJI('😇')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('keycap number sign', () => {
|
||||
const input = 'abc#️⃣123';
|
||||
const output = [TEXT('abc'), UNI_EMOJI('#️⃣'), TEXT('123')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('big', () => {
|
||||
|
@ -338,7 +366,7 @@ describe('parser', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('italic 1', () => {
|
||||
describe('italic tag', () => {
|
||||
it('basic', () => {
|
||||
const input = '<i>abc</i>';
|
||||
const output = [
|
||||
|
@ -376,7 +404,7 @@ describe('parser', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('italic 2', () => {
|
||||
describe('italic alt 1', () => {
|
||||
it('basic', () => {
|
||||
const input = '*abc*';
|
||||
const output = [
|
||||
|
@ -386,6 +414,54 @@ describe('parser', () => {
|
|||
];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic 2', () => {
|
||||
const input = 'before *abc* after';
|
||||
const output = [
|
||||
TEXT('before '),
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT(' after')
|
||||
];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('ignore a italic syntax if the before char is neither a space nor an LF', () => {
|
||||
const input = 'before*abc*after';
|
||||
const output = [TEXT('before*abc*after')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('italic alt 2', () => {
|
||||
it('basic', () => {
|
||||
const input = '_abc_';
|
||||
const output = [
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
])
|
||||
];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic 2', () => {
|
||||
const input = 'before _abc_ after';
|
||||
const output = [
|
||||
TEXT('before '),
|
||||
ITALIC([
|
||||
TEXT('abc')
|
||||
]),
|
||||
TEXT(' after')
|
||||
];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('ignore a italic syntax if the before char is neither a space nor an LF', () => {
|
||||
const input = 'before_abc_after';
|
||||
const output = [TEXT('before_abc_after')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
// strike
|
||||
|
@ -394,12 +470,73 @@ describe('parser', () => {
|
|||
|
||||
// mathInline
|
||||
|
||||
// mention
|
||||
describe('mention', () => {
|
||||
it('basic', () => {
|
||||
const input = '@abc';
|
||||
const output = [MENTION('abc', null, '@abc')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic 2', () => {
|
||||
const input = 'before @abc after';
|
||||
const output = [TEXT('before '), MENTION('abc', null, '@abc'), TEXT(' after')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic remote', () => {
|
||||
const input = '@abc@misskey.io';
|
||||
const output = [MENTION('abc', 'misskey.io', '@abc@misskey.io')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic remote 2', () => {
|
||||
const input = 'before @abc@misskey.io after';
|
||||
const output = [TEXT('before '), MENTION('abc', 'misskey.io', '@abc@misskey.io'), TEXT(' after')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic remote 3', () => {
|
||||
const input = 'before\n@abc@misskey.io\nafter';
|
||||
const output = [TEXT('before\n'), MENTION('abc', 'misskey.io', '@abc@misskey.io'), TEXT('\nafter')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('ignore format of mail address', () => {
|
||||
const input = 'abc@example.com';
|
||||
const output = [TEXT('abc@example.com')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hashtag', () => {
|
||||
it('and unicode emoji', () => {
|
||||
const input = '#️⃣abc123#abc';
|
||||
const output = [UNI_EMOJI('#️⃣'), TEXT('abc123'), HASHTAG('abc')];
|
||||
it('basic', () => {
|
||||
const input = '#abc';
|
||||
const output = [HASHTAG('abc')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('basic 2', () => {
|
||||
const input = 'before #abc after';
|
||||
const output = [TEXT('before '), HASHTAG('abc'), TEXT(' after')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('with keycap number sign', () => {
|
||||
const input = '#️⃣abc123 #abc';
|
||||
const output = [UNI_EMOJI('#️⃣'), TEXT('abc123 '), HASHTAG('abc')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('with keycap number sign 2', () => {
|
||||
const input = `abc
|
||||
#️⃣abc`;
|
||||
const output = [TEXT('abc\n'), UNI_EMOJI('#️⃣'), TEXT('abc')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
|
||||
it('ignore a hashtag if the before char is neither a space nor an LF', () => {
|
||||
const input = 'abc#abc';
|
||||
const output = [TEXT('abc#abc')];
|
||||
assert.deepStrictEqual(mfm.parse(input), output);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue