Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement plain syntax #114

Merged
merged 8 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ I'm @ai, A bot of misskey!
https://github.com/syuilo/ai
</center>`;

// Generate a MFM tree from the MFM text.
// Generate a MFM tree from the full MFM text.
const mfmTree = mfm.parse(inputText);

// Generate a MFM tree from the MFM plain text.
const plainMfmTree = mfm.parsePlain('I like the hot soup :soup:​');
// Generate a MFM tree from the simple MFM text.
const simpleMfmTree = mfm.parseSimple('I like the hot soup :soup:​');

// Reverse to a MFM text from the MFM tree.
const text = mfm.toString(mfmTree);
Expand Down Expand Up @@ -62,9 +62,9 @@ full parser:
npm run parse
```

plain parser:
simple parser:
```
npm run parse-plain
npm run parse-simple
```

## License
Expand Down
4 changes: 2 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ console.log(JSON.stringify(nodes));
// => [{"type":"bold","children":[{"type":"text","props":{"text":"<s>cannot nest</s>"}}]}]
```

## parsePlain API
## parseSimple API
入力文字列からノードツリーを生成します。
絵文字コードとUnicode絵文字を利用可能です。

例:
```ts
const nodes = mfm.parsePlain('Hello :surprised_ai:');
const nodes = mfm.parseSimple('Hello :surprised_ai:');
console.log(JSON.stringify(nodes));
// => [{"type":"text","props":{"text":"Hello "}},{"type":"emojiCode","props":{"name":"surprised_ai"}}]
```
Expand Down
21 changes: 16 additions & 5 deletions etc/mfm-js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export type MfmHashtag = {
};

// @public (undocumented)
export type MfmInline = MfmUnicodeEmoji | MfmEmojiCode | MfmBold | MfmSmall | MfmItalic | MfmStrike | MfmInlineCode | MfmMathInline | MfmMention | MfmHashtag | MfmUrl | MfmLink | MfmFn | MfmText;
export type MfmInline = MfmUnicodeEmoji | MfmEmojiCode | MfmBold | MfmSmall | MfmItalic | MfmStrike | MfmInlineCode | MfmMathInline | MfmMention | MfmHashtag | MfmUrl | MfmLink | MfmFn | MfmPlain | MfmText;

// @public (undocumented)
export type MfmInlineCode = {
Expand Down Expand Up @@ -165,6 +165,13 @@ export type MfmMention = {
// @public (undocumented)
export type MfmNode = MfmBlock | MfmInline;

// @public (undocumented)
export type MfmPlain = {
type: 'plain';
props?: Record<string, unknown>;
children: MfmText[];
};

// @public (undocumented)
export type MfmQuote = {
type: 'quote';
Expand All @@ -182,6 +189,9 @@ export type MfmSearch = {
children?: [];
};

// @public (undocumented)
export type MfmSimpleNode = MfmUnicodeEmoji | MfmEmojiCode | MfmText;

// @public (undocumented)
export type MfmSmall = {
type: 'small';
Expand Down Expand Up @@ -228,18 +238,19 @@ export type MfmUrl = {
export const N_URL: (value: string, brackets?: boolean | undefined) => NodeType<'url'>;

// @public (undocumented)
export type NodeType<T extends MfmNode['type']> = T extends 'quote' ? MfmQuote : T extends 'search' ? MfmSearch : T extends 'blockCode' ? MfmCodeBlock : T extends 'mathBlock' ? MfmMathBlock : T extends 'center' ? MfmCenter : T extends 'unicodeEmoji' ? MfmUnicodeEmoji : T extends 'emojiCode' ? MfmEmojiCode : T extends 'bold' ? MfmBold : T extends 'small' ? MfmSmall : T extends 'italic' ? MfmItalic : T extends 'strike' ? MfmStrike : T extends 'inlineCode' ? MfmInlineCode : T extends 'mathInline' ? MfmMathInline : T extends 'mention' ? MfmMention : T extends 'hashtag' ? MfmHashtag : T extends 'url' ? MfmUrl : T extends 'link' ? MfmLink : T extends 'fn' ? MfmFn : T extends 'text' ? MfmText : never;
export type NodeType<T extends MfmNode['type']> = T extends 'quote' ? MfmQuote : T extends 'search' ? MfmSearch : T extends 'blockCode' ? MfmCodeBlock : T extends 'mathBlock' ? MfmMathBlock : T extends 'center' ? MfmCenter : T extends 'unicodeEmoji' ? MfmUnicodeEmoji : T extends 'emojiCode' ? MfmEmojiCode : T extends 'bold' ? MfmBold : T extends 'small' ? MfmSmall : T extends 'italic' ? MfmItalic : T extends 'strike' ? MfmStrike : T extends 'inlineCode' ? MfmInlineCode : T extends 'mathInline' ? MfmMathInline : T extends 'mention' ? MfmMention : T extends 'hashtag' ? MfmHashtag : T extends 'url' ? MfmUrl : T extends 'link' ? MfmLink : T extends 'fn' ? MfmFn : T extends 'plain' ? MfmPlain : T extends 'text' ? MfmText : never;

// @public (undocumented)
export function parse(input: string, opts?: Partial<{
fnNameList: string[];
nestLimit: number;
}>): MfmNode[];

// Warning: (ae-forgotten-export) The symbol "MfmPlainNode" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function parsePlain(input: string): MfmPlainNode[];
export function parseSimple(input: string): MfmSimpleNode[];

// @public (undocumented)
export const PLAIN: (text: string) => NodeType<'plain'>;

// @public (undocumented)
export const QUOTE: (children: MfmNode[]) => NodeType<'quote'>;
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
"scripts": {
"build": "npm run tsc && npm run peg",
"build-debug": "npm run tsc && npm run peg-debug",
"peg": "peggy --cache -o src/internal/parser.js --allowed-start-rules fullParser,plainParser src/internal/parser.pegjs && npm run peg-copy",
"peg-debug": "peggy --cache -o src/internal/parser.js --allowed-start-rules fullParser,inlineParser,plainParser --trace src/internal/parser.pegjs && npm run peg-copy",
"peg": "peggy --cache -o src/internal/parser.js --allowed-start-rules fullParser,simpleParser src/internal/parser.pegjs && npm run peg-copy",
"peg-debug": "peggy --cache -o src/internal/parser.js --allowed-start-rules fullParser,inlineParser,simpleParser --trace src/internal/parser.pegjs && npm run peg-copy",
"peg-copy": "copyfiles -f src/internal/parser.js built/internal/",
"tsc": "tsc",
"tsd": "tsd",
"parse": "node ./built/cli/parse",
"parse-plain": "node ./built/cli/parsePlain",
"parse-simple": "node ./built/cli/parseSimple",
"api": "npx api-extractor run --local --verbose",
"api-prod": "npx api-extractor run --verbose",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
Expand Down
8 changes: 4 additions & 4 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import peg from 'peggy';
import { MfmNode, MfmPlainNode } from './node';
import { MfmNode, MfmSimpleNode } from './node';
import { stringifyNode, stringifyTree, inspectOne } from './internal/util';

// eslint-disable-next-line @typescript-eslint/no-var-requires
Expand All @@ -18,10 +18,10 @@ export function parse(input: string, opts: Partial<{ fnNameList: string[]; nestL
}

/**
* Generates a MfmNode tree of plain from the MFM string.
* Generates a MfmSimpleNode tree from the MFM string.
*/
export function parsePlain(input: string): MfmPlainNode[] {
const nodes = parser.parse(input, { startRule: 'plainParser' });
export function parseSimple(input: string): MfmSimpleNode[] {
const nodes = parser.parse(input, { startRule: 'simpleParser' });
return nodes;
}

Expand Down
6 changes: 3 additions & 3 deletions src/cli/parsePlain.ts → src/cli/parseSimple.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { performance } from 'perf_hooks';
import inputLine, { InputCanceledError } from './misc/inputLine';
import { parsePlain } from '..';
import { parseSimple } from '..';

async function entryPoint() {
console.log('intaractive plain parser');
console.log('intaractive simple parser');

while (true) {
let input: string;
Expand All @@ -26,7 +26,7 @@ async function entryPoint() {

try {
const parseTimeStart = performance.now();
const result = parsePlain(input);
const result = parseSimple(input);
const parseTimeEnd = performance.now();
console.log(JSON.stringify(result));
const parseTime = (parseTimeEnd - parseTimeStart).toFixed(3);
Expand Down
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export {
parse,
parsePlain,
parseSimple,
toString,
inspect,
extract,
Expand All @@ -9,6 +9,7 @@ export {
export {
NodeType,
MfmNode,
MfmSimpleNode,
MfmBlock,
MfmInline,
} from './node';
Expand All @@ -35,6 +36,7 @@ export {
MfmUrl,
MfmLink,
MfmFn,
MfmPlain,
MfmText,
} from './node';

Expand All @@ -60,5 +62,6 @@ export {
N_URL,
LINK,
FN,
PLAIN,
TEXT,
} from './node';
30 changes: 24 additions & 6 deletions src/internal/parser.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
N_URL,
LINK,
FN,
PLAIN,
TEXT
} = require('../node');

Expand Down Expand Up @@ -101,8 +102,8 @@
fullParser
= nodes:(&. @full)* { return mergeText(nodes); }

plainParser
= nodes:(&. @plain)* { return mergeText(nodes); }
simpleParser
= nodes:(&. @simple)* { return mergeText(nodes); }

//
// syntax list
Expand All @@ -126,6 +127,7 @@ full
/ hashtag
/ url
/ fn
/ plain
/ link
/ search // block
/ inlineText
Expand All @@ -144,6 +146,7 @@ inline
/ hashtag
/ url
/ fn
/ plain
/ link
/ inlineText

Expand All @@ -158,12 +161,13 @@ L_inline
/ inlineCode
/ mathInline
/ L_fn
/ plain
/ L_inlineText

plain
simple
= emojiCode
/ unicodeEmoji
/ plainText
/ simpleText

//
// block rules
Expand Down Expand Up @@ -553,6 +557,20 @@ fnArg
return { k: k, v: true };
}

// inline: plain

plain
= "<plain>" LF? content:plainContent LF? "</plain>"
{
return PLAIN(content);
}

plainContent
= (!(LF? "</plain>") .)+
{
return text();
}

// inline: text

inlineText
Expand All @@ -563,9 +581,9 @@ L_inlineText
= !(LF / _) [a-z0-9]i &italicAlt . { return text(); } // italic ignore
/ . /* text node */

// inline: text (for plainParser)
// inline: text (for simpleParser)

plainText
simpleText
= . /* text node */

//
Expand Down
3 changes: 3 additions & 0 deletions src/internal/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ export function stringifyNode(node: MfmNode): string {
const args = (argFields.length > 0) ? '.' + argFields.join(',') : '';
return `$[${ node.props.name }${ args } ${ stringifyTree(node.children) }]`;
}
case 'plain': {
return `<plain>\n${ stringifyTree(node.children) }\n</plain>`;
}
case 'text': {
return node.props.text;
}
Expand Down
12 changes: 10 additions & 2 deletions src/node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type MfmNode = MfmBlock | MfmInline;

export type MfmPlainNode = MfmUnicodeEmoji | MfmEmojiCode | MfmText;
export type MfmSimpleNode = MfmUnicodeEmoji | MfmEmojiCode | MfmText;

export type MfmBlock = MfmQuote | MfmSearch | MfmCodeBlock | MfmMathBlock | MfmCenter;

Expand Down Expand Up @@ -53,7 +53,7 @@ export type MfmCenter = {
export const CENTER = (children: MfmInline[]): NodeType<'center'> => { return { type: 'center', children }; };

export type MfmInline = MfmUnicodeEmoji | MfmEmojiCode | MfmBold | MfmSmall | MfmItalic | MfmStrike |
MfmInlineCode | MfmMathInline | MfmMention | MfmHashtag | MfmUrl | MfmLink | MfmFn | MfmText;
MfmInlineCode | MfmMathInline | MfmMention | MfmHashtag | MfmUrl | MfmLink | MfmFn | MfmPlain | MfmText;

export type MfmUnicodeEmoji = {
type: 'unicodeEmoji';
Expand Down Expand Up @@ -173,6 +173,13 @@ export type MfmFn = {
};
export const FN = (name: string, args: MfmFn['props']['args'], children: MfmFn['children']): NodeType<'fn'> => { return { type: 'fn', props: { name, args }, children }; };

export type MfmPlain = {
type: 'plain';
props?: Record<string, unknown>;
children: MfmText[];
};
export const PLAIN = (text: string): NodeType<'plain'> => { return { type: 'plain', children: [TEXT(text)] }; };

export type MfmText = {
type: 'text';
props: {
Expand Down Expand Up @@ -201,5 +208,6 @@ export type NodeType<T extends MfmNode['type']> =
T extends 'url' ? MfmUrl :
T extends 'link' ? MfmLink :
T extends 'fn' ? MfmFn :
T extends 'plain' ? MfmPlain :
T extends 'text' ? MfmText :
never;
10 changes: 10 additions & 0 deletions test/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ after`;
assert.strictEqual(mfm.toString(mfm.parse(input)), '$[spin.speed=1s,alternate Hello]');
});

it('plain', () => {
const input = 'a\n<plain>\nHello\nworld\n</plain>\nb';
assert.strictEqual(mfm.toString(mfm.parse(input)), 'a\n<plain>\nHello\nworld\n</plain>\nb');
});

it('1 line plain', () => {
const input = 'a\n<plain>Hello</plain>\nb';
assert.strictEqual(mfm.toString(mfm.parse(input)), 'a\n<plain>\nHello\n</plain>\nb');
});

it('preserve url brackets', () => {
const input1 = 'https://github.com/syuilo/ai';
assert.strictEqual(mfm.toString(mfm.parse(input1)), input1);
Expand Down
Loading