Skip to content

Commit 959b57b

Browse files
committed
refactoring base code
Text nodes are now always grouped in textgroup node. to fix a issue #4
1 parent be0bdda commit 959b57b

File tree

9 files changed

+200
-84
lines changed

9 files changed

+200
-84
lines changed

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
*/
55
import React, { Component, PropTypes } from 'react';
66
import { View } from 'react-native';
7-
import { parser, stringToTokens, tokensToAST } from './lib/parser';
7+
import { parser, stringToTokens } from './lib/parser';
8+
import tokensToAST from './lib/util/tokensToAST';
89
import defaultRenderFunctions from './lib/defaultRenderFunctions';
910
import AstRenderer from './lib/AstRenderer';
1011
import MarkdownIt from 'markdown-it';

lib/defaultRenderFunctions.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ const defaultRenderFunctions = {
2727
</Text>
2828
);
2929
},
30+
textgroup: (node, children, parents) => {
31+
return <Text key={AstRenderer.getUniqueID()}>{children}</Text>;
32+
},
3033
inline: (node, children, parents) => {
31-
return (
32-
<Text key={AstRenderer.getUniqueID()}>{children}</Text>
33-
);
34+
return <Text key={AstRenderer.getUniqueID()}>{children}</Text>;
3435
},
3536
span: (node, children, parents) =>
3637
<Text key={AstRenderer.getUniqueID()}>
@@ -47,14 +48,18 @@ const defaultRenderFunctions = {
4748

4849
s: (node, children, parents) => {
4950
return (
50-
<Text key={AstRenderer.getUniqueID()} style={markdownStyles.strikethrough}>
51+
<Text key={AstRenderer.getUniqueID()} style={markdownStyles.strikethrough}>
5152
{children}
5253
</Text>
5354
);
5455
},
5556
a: (node, children, parents) => {
5657
return (
57-
<Text key={AstRenderer.getUniqueID()} style={markdownStyles.a} onPress={() => AstRenderer.openUrl(node.attributes.href)}>
58+
<Text
59+
key={AstRenderer.getUniqueID()}
60+
style={markdownStyles.a}
61+
onPress={() => AstRenderer.openUrl(node.attributes.href)}
62+
>
5863
{children}
5964
</Text>
6065
);

lib/parser.js

Lines changed: 6 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,13 @@
11
import React from 'react';
22
import { View } from 'react-native';
33
import MarkdownIt from 'markdown-it';
4+
import tokensToAST from './util/tokensToAST';
5+
import removeInlineTokens from "./util/removeInlineTokens";
6+
import groupTextTokens from "./util/groupTextTokens";
47

58
let md = MarkdownIt();
69

7-
/**
8-
*
9-
* @param {{type: string, tag:string, content: string, children: *, attrs: Array}} token
10-
* @param {number} tokenIndex
11-
* @return {{type: string, content, tokenIndex: *, index: number, attributes: {}, children: *}}
12-
*/
13-
function createNode(token, tokenIndex) {
14-
let type = 'root';
15-
16-
if (token) {
17-
if (!token.tag) {
18-
type = token.type;
19-
} else {
20-
type = token.tag;
21-
}
22-
}
23-
24-
const content = token.content;
25-
let attributes = {};
26-
27-
if (token.attrs) {
28-
attributes = token.attrs.reduce((prev, curr) => {
29-
const [name, value] = curr;
30-
return { ...prev, [name]: value };
31-
}, {});
32-
}
33-
34-
return {
35-
type,
36-
content,
37-
tokenIndex,
38-
index: 0,
39-
attributes,
40-
children: tokensToAST(token.children),
41-
};
42-
}
43-
44-
/**
45-
*
46-
* @param {Array<{type: string, tag:string, content: string, children: *, attrs: Array}>}tokens
47-
* @return {Array}
48-
*/
49-
function tokensToAST(tokens) {
50-
const stack = [];
51-
const stackText = [];
52-
let children = [];
53-
54-
if (!tokens || tokens.length === 0) {
55-
return [];
56-
}
57-
58-
for (let i = 0; i < tokens.length; i++) {
59-
const token = tokens[i];
60-
const astNode = createNode(token, i);
61-
62-
if (!(astNode.type === 'text' && astNode.children.length === 0 && astNode.content === '')) {
63-
astNode.index = children.length;
64-
65-
if (token.nesting === 1) {
66-
children.push(astNode);
67-
stack.push(children);
68-
children = astNode.children;
69-
} else if (token.nesting === -1) {
70-
children = stack.pop();
71-
} else if (token.nesting === 0) {
72-
children.push(astNode);
73-
}
74-
}
75-
}
76-
77-
return children;
78-
}
79-
80-
function stringToTokens(source, markdownIt = md) {
10+
export function stringToTokens(source, markdownIt = md) {
8111
let result = [];
8212
try {
8313
result = md.parse(source, {});
@@ -92,11 +22,9 @@ function stringToTokens(source, markdownIt = md) {
9222
* @param {AstRenderer} [markdownIt]
9323
* @return {View}
9424
*/
95-
function parser(source, renderer, markdownIt = md) {
96-
const tokens = stringToTokens(source, markdownIt);
25+
export function parser(source, renderer, markdownIt = md) {
26+
const tokens = groupTextTokens(removeInlineTokens(stringToTokens(source, markdownIt)));
9727
const asttree = tokensToAST(tokens);
9828

9929
return <View>{renderer.render(asttree)}</View>;
10030
}
101-
102-
export { tokensToAST, stringToTokens, parser };

lib/util/Token.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default class Token {
2+
constructor(type, nesting = 0, children = null) {
3+
this.tag = type;
4+
this.nesting = nesting;
5+
this.children = children;
6+
}
7+
}

lib/util/createToken.js

Whitespace-only changes.

lib/util/getIsTextType.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const textTypes = [
2+
'text',
3+
'span',
4+
'strong',
5+
'a',
6+
's',
7+
'em',
8+
'h1',
9+
'h2',
10+
'h3',
11+
'h4',
12+
'h5',
13+
'h6',
14+
'h7',
15+
'h8',
16+
'h9',
17+
'br',
18+
];
19+
20+
/**
21+
*
22+
* @param node
23+
* @return {boolean}
24+
*/
25+
function getIsTextType(type) {
26+
return textTypes.indexOf(type) > -1;
27+
}

lib/util/groupTextTokens.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
class Stack {
2+
constructor() {
3+
this.data = [];
4+
this.count = 0;
5+
}
6+
7+
add(token) {
8+
this.count += 1 + token.nesting;
9+
this.data.push(token);
10+
}
11+
}
12+
13+
export default function groupTextTokens(tokens) {
14+
const result = [];
15+
16+
if (!tokens) {
17+
return result;
18+
}
19+
20+
let currentStack = new Stack();
21+
const stacks = [];
22+
23+
for (var i = 0; i < tokens.length; i++) {
24+
const token = tokens[i];
25+
26+
if (getIsTextType(token.tag || token.type)) {
27+
if (token.nesting === 1) {
28+
currentStack.add(token);
29+
stacks.push(currentStack);
30+
currentStack = new Stack();
31+
} else if (token.nesting === -1) {
32+
currentStack.add(token);
33+
currentStack = stacks.pop();
34+
} else if (token.nesting === 0) {
35+
currentStack.add(token);
36+
}
37+
} else {
38+
if (currentStack.count > 1) {
39+
result.push(createToken('textgroup', 1));
40+
41+
while (currentStack.data.length) {
42+
result.push(currentStack.data.shift());
43+
}
44+
45+
result.push(createToken('textgroup', -1));
46+
} else if (currentStack.count === 1) {
47+
while (currentStack.data.length) {
48+
result.push(currentStack.data.shift());
49+
}
50+
}
51+
52+
result.push(token);
53+
}
54+
}
55+
56+
return result;
57+
}

lib/util/removeInlineTokens.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export default function removeInlineTokens(tokens) {
2+
const result = [];
3+
if (!tokens) {
4+
return result;
5+
}
6+
7+
for (let i = 0; i < tokens.length; i++) {
8+
const token = tokens[i];
9+
10+
if (token.type === 'inline') {
11+
if (token.children && token.children.length > 0) {
12+
removeInlineTokens(token.children).forEach(item => result.push(item));
13+
}
14+
} else {
15+
result.push(token);
16+
}
17+
}
18+
19+
return result;
20+
}

lib/util/tokensToAST.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
*
3+
* @param {{type: string, tag:string, content: string, children: *, attrs: Array}} token
4+
* @param {number} tokenIndex
5+
* @return {{type: string, content, tokenIndex: *, index: number, attributes: {}, children: *}}
6+
*/
7+
function createNode(token, tokenIndex) {
8+
let type = 'root';
9+
10+
if (token) {
11+
if (!token.tag) {
12+
type = token.type;
13+
} else {
14+
type = token.tag;
15+
}
16+
}
17+
18+
const content = token.content;
19+
let attributes = {};
20+
21+
if (token.attrs) {
22+
attributes = token.attrs.reduce((prev, curr) => {
23+
const [name, value] = curr;
24+
return { ...prev, [name]: value };
25+
}, {});
26+
}
27+
28+
return {
29+
type,
30+
content,
31+
tokenIndex,
32+
index: 0,
33+
attributes,
34+
children: tokensToAST(token.children),
35+
};
36+
}
37+
38+
/**
39+
*
40+
* @param {Array<{type: string, tag:string, content: string, children: *, attrs: Array}>}tokens
41+
* @return {Array}
42+
*/
43+
export default function tokensToAST(tokens) {
44+
let stack = [];
45+
let children = [];
46+
47+
if (!tokens || tokens.length === 0) {
48+
return [];
49+
}
50+
51+
for (let i = 0; i < tokens.length; i++) {
52+
const token = tokens[i];
53+
const astNode = createNode(token, i);
54+
55+
if (!(astNode.type === 'text' && astNode.children.length === 0 && astNode.content === '')) {
56+
astNode.index = children.length;
57+
58+
if (token.nesting === 1) {
59+
children.push(astNode);
60+
stack.push(children);
61+
children = astNode.children;
62+
} else if (token.nesting === -1) {
63+
children = stack.pop();
64+
} else if (token.nesting === 0) {
65+
children.push(astNode);
66+
}
67+
}
68+
}
69+
70+
return children;
71+
}

0 commit comments

Comments
 (0)