refactor: refactor code tab split (#1369)

* refactor: refactor code tab split

* Add split tab unit test

* Fix code tab list bug
This commit is contained in:
Zach Badgett 2019-05-01 21:57:13 -06:00 committed by Yangshun Tay
parent 5030952a8f
commit 0813920349
9 changed files with 671 additions and 43 deletions

View file

@ -19,20 +19,120 @@ const translateThisDoc = translate(
'Translate this Doc|recruitment message asking to translate the docs',
);
const splitTabsToTitleAndContent = content => {
const titles = content.match(/<!--(.*?)-->/gms);
const tabs = content.split(/<!--.*?-->/gms);
if (!titles || !tabs || !titles.length || !tabs.length) {
return [];
const splitTabsToTitleAndContent = (lines, indents) => {
let first = false;
let inBlock = false;
let whitespace = false;
const tc = [];
let current = {
content: [],
};
lines.forEach(line => {
if (indents) {
line = line.replace(new RegExp(`^((\\t|\\s{4}){${indents}})`, 'g'), '');
}
let pos = 0;
const end = line.length;
const isToken = (cline, cpos, ...chars) => {
for (let i = 0; i < chars.length; i++) {
if (cline.charCodeAt(cpos) !== chars[i]) {
return false;
}
cpos++;
}
return true;
};
while (pos + 1 < end) {
// Skip all the whitespace when we first start the scan.
for (let max = end; pos < max; pos++) {
if (line.charCodeAt(pos) !== 0x20 && line.charCodeAt(pos) !== 0x0a) {
break;
}
whitespace = true;
}
// Check for the start of a comment: <!--
// If we're in a code block we skip it.
if (
isToken(
line,
pos,
0x3c /* < */,
0x21 /* ! */,
0x2d /* - */,
0x2d /* - */,
) &&
!inBlock
) {
if (current !== null && current.title !== undefined) {
tc.push({
title: current.title,
content: current.content.join('\n'),
});
current = {
content: [],
};
}
first = true;
pos += 4;
let b0;
let b1;
const buf = [];
// Add all characters to the title buffer until
// we reach the end marker: -->
for (let max = end; pos < max; pos++) {
const b = line.charCodeAt(pos);
if (b0 === 0x2d /* - */ && b1 === 0x2d /* - */) {
if (b !== 0x3e /* > */) {
throw new Error(`Invalid comment sequence "--"`);
}
break;
}
buf.push(b);
b0 = b1;
b1 = b;
}
// Clear the line out before we add it to content.
// This also means tabs can only be defined on a line by itself.
line = '\n';
// Trim the last 2 characters: --
current.title = String.fromCharCode(...buf)
.substring(0, buf.length - 2)
.trim();
}
// If the first thing in a code tab is not a title it's invalid.
if (!first) {
throw new Error(`Invalid code tab markdown`);
}
// Check for code block: ```
// If the line begins with whitespace we don't consider it a code block.
if (
isToken(line, pos, 0x60 /* ` */, 0x60 /* ` */, 0x60 /* ` */) &&
!whitespace
) {
pos += 3;
inBlock = !inBlock;
}
pos++;
whitespace = false;
}
current.content.push(line);
});
if (current !== null && current.title !== undefined) {
tc.push({
title: current.title,
content: current.content.join('\n'),
});
}
tabs.shift();
return titles.map((title, idx) => ({
title: title.substring(4, title.length - 3).trim(),
content: tabs[idx],
}));
return tc;
};
const cleanTheCodeTag = content => {
const cleanTheCodeTag = (content, indents) => {
const prepend = (line, indent) => {
if (indent) {
return ' '.repeat(indent) + line;
}
return line;
};
const contents = content.split(/(<pre>)(.*?)(<\/pre>)/gms);
let inCodeBlock = false;
const cleanContents = contents.map(c => {
@ -47,7 +147,7 @@ const cleanTheCodeTag = content => {
if (inCodeBlock) {
return c.replace(/\n/g, '<br />');
}
return c;
return prepend(c, indents);
});
return cleanContents.join('');
};
@ -56,32 +156,43 @@ const cleanTheCodeTag = content => {
class Doc extends React.Component {
renderContent() {
const {content} = this.props;
let inCodeTabs = false;
const contents = content.split(
/(<!--DOCUSAURUS_CODE_TABS-->\n)(.*?)(\n<!--END_DOCUSAURUS_CODE_TABS-->)/gms,
);
const renderResult = contents.map(c => {
if (c === '<!--DOCUSAURUS_CODE_TABS-->\n') {
inCodeTabs = true;
return '';
}
if (c === '\n<!--END_DOCUSAURUS_CODE_TABS-->') {
inCodeTabs = false;
return '';
}
if (inCodeTabs) {
let indents = 0;
return content.replace(
/(\t|\s{4})*?(<!--DOCUSAURUS_CODE_TABS-->\n)(.*?)((\n|\t|\s{4})<!--END_DOCUSAURUS_CODE_TABS-->)/gms,
m => {
const contents = m.split('\n').filter(c => {
if (!indents) {
indents = (
c.match(/((\t|\s{4})+)<!--DOCUSAURUS_CODE_TABS-->/) || []
).length;
}
if (c.match(/(\t|\s{4})+<!--DOCUSAURUS_CODE_TABS-->/)) {
return false;
}
if (
c.match(
/<!--END_DOCUSAURUS_CODE_TABS-->|<!--DOCUSAURUS_CODE_TABS-->/,
) ||
(indents > 0 &&
c.match(
/(\t|\s{4})+(<!--END_DOCUSAURUS_CODE_TABS-->|<!--DOCUSAURUS_CODE_TABS-->)/,
))
) {
return false;
}
return true;
});
if (indents) {
indents -= 1;
}
const codeTabsMarkdownBlock = renderToStaticMarkup(
<CodeTabsMarkdownBlock>
{splitTabsToTitleAndContent(c)}
{splitTabsToTitleAndContent(contents, indents)}
</CodeTabsMarkdownBlock>,
);
return cleanTheCodeTag(codeTabsMarkdownBlock);
}
return c;
});
return renderResult.join('');
return cleanTheCodeTag(codeTabsMarkdownBlock, indents);
},
);
}
render() {