怎么运行的?
它通过逐块读取一个字符串来工作,这可能不是真正长字符串的最佳解决方案。
每当解析器检测到正在读取关键块(即'*'
任何其他markdown标记)时,它将开始解析该元素的块,直到解析器找到其结束标记为止。
它适用于多行字符串,请参见代码。
注意事项
您尚未指定,否则我可能会误解您的需求,如果有必要解析粗体和斜体的标记,则当前的解决方案在这种情况下可能无法正常工作。
但是,如果需要使用上述条件,请在此处注释,然后我将对代码进行调整。
第一次更新:调整如何处理降价标签
标签不再是硬编码的,它们是一张地图,您可以在其中轻松扩展以满足您的需求。
修复了您在评论中提到的错误,感谢您指出此问题= p
第二次更新:多长减价标签
实现此目的的最简单方法:用很少使用的unicode替换多长度字符
尽管该方法parseMarkdown
尚不支持多长度标签,但string.replace
在发送rawMarkdown
道具时,我们可以轻松地用一个简单的替换这些多长度标签。
要在实践中查看此示例,请查看ReactDOM.render
代码末尾的。
即使您的应用程序确实支持多种语言,"\uFFFF"
如果我没有记错的话,JavaScript仍然会检测到无效的unicode字符,例如:不是有效的unicode,但是JS仍然可以对其进行比较("\uFFFF" === "\uFFFF" = true
)
乍一看似乎很麻烦,但是根据您的用例,使用此路由我看不到任何重大问题。
实现这一目标的另一种方法
好吧,我们可以轻松地跟踪最后一个N
(N
对应于最长的多长度标签的长度)块。
将对循环内部方法的parseMarkdown
行为方式进行一些调整
,即检查当前块是否为多长度标签的一部分(如果将其用作标签);否则,在类似的情况下``k
,我们需要将其标记为notMultiLength
或类似名称并将其作为内容推送。
码
// Instead of creating hardcoded variables, we can make the code more extendable
// by storing all the possible tags we'll work with in a Map. Thus, creating
// more tags will not require additional logic in our code.
const tags = new Map(Object.entries({
"*": "strong", // bold
"!": "button", // action
"_": "em", // emphasis
"\uFFFF": "pre", // Just use a very unlikely to happen unicode character,
// We'll replace our multi-length symbols with that one.
}));
// Might be useful if we need to discover the symbol of a tag
const tagSymbols = new Map();
tags.forEach((v, k) => { tagSymbols.set(v, k ); })
const rawMarkdown = `
This must be *bold*,
This also must be *bo_ld*,
this _entire block must be
emphasized even if it's comprised of multiple lines_,
This is an !action! it should be a button,
\`\`\`
beep, boop, this is code
\`\`\`
This is an asterisk\\*
`;
class App extends React.Component {
parseMarkdown(source) {
let currentTag = "";
let currentContent = "";
const parsedMarkdown = [];
// We create this variable to track possible escape characters, eg. "\"
let before = "";
const pushContent = (
content,
tagValue,
props,
) => {
let children = undefined;
// There's the need to parse for empty lines
if (content.indexOf("\n\n") >= 0) {
let before = "";
const contentJSX = [];
let chunk = "";
for (let i = 0; i < content.length; i++) {
if (i !== 0) before = content[i - 1];
chunk += content[i];
if (before === "\n" && content[i] === "\n") {
contentJSX.push(chunk);
contentJSX.push(<br />);
chunk = "";
}
if (chunk !== "" && i === content.length - 1) {
contentJSX.push(chunk);
}
}
children = contentJSX;
} else {
children = [content];
}
parsedMarkdown.push(React.createElement(tagValue, props, children))
};
for (let i = 0; i < source.length; i++) {
const chunk = source[i];
if (i !== 0) {
before = source[i - 1];
}
// Does our current chunk needs to be treated as a escaped char?
const escaped = before === "\\";
// Detect if we need to start/finish parsing our tags
// We are not parsing anything, however, that could change at current
// chunk
if (currentTag === "" && escaped === false) {
// If our tags array has the chunk, this means a markdown tag has
// just been found. We'll change our current state to reflect this.
if (tags.has(chunk)) {
currentTag = tags.get(chunk);
// We have simple content to push
if (currentContent !== "") {
pushContent(currentContent, "span");
}
currentContent = "";
}
} else if (currentTag !== "" && escaped === false) {
// We'll look if we can finish parsing our tag
if (tags.has(chunk)) {
const symbolValue = tags.get(chunk);
// Just because the current chunk is a symbol it doesn't mean we
// can already finish our currentTag.
//
// We'll need to see if the symbol's value corresponds to the
// value of our currentTag. In case it does, we'll finish parsing it.
if (symbolValue === currentTag) {
pushContent(
currentContent,
currentTag,
undefined, // you could pass props here
);
currentTag = "";
currentContent = "";
}
}
}
// Increment our currentContent
//
// Ideally, we don't want our rendered markdown to contain any '\'
// or undesired '*' or '_' or '!'.
//
// Users can still escape '*', '_', '!' by prefixing them with '\'
if (tags.has(chunk) === false || escaped) {
if (chunk !== "\\" || escaped) {
currentContent += chunk;
}
}
// In case an erroneous, i.e. unfinished tag, is present and the we've
// reached the end of our source (rawMarkdown), we want to make sure
// all our currentContent is pushed as a simple string
if (currentContent !== "" && i === source.length - 1) {
pushContent(
currentContent,
"span",
undefined,
);
}
}
return parsedMarkdown;
}
render() {
return (
<div className="App">
<div>{this.parseMarkdown(this.props.rawMarkdown)}</div>
</div>
);
}
}
ReactDOM.render(<App rawMarkdown={rawMarkdown.replace(/```/g, "\uFFFF")} />, document.getElementById('app'));
链接到代码(TypeScript)https://codepen.io/ludanin/pen/GRgNWPv
链接到代码(vanilla / babel)https://codepen.io/ludanin/pen/eYmBvXw
font _italic *and bold* then only italic_ and normal
怎么办?预期的结果是什么?还是永远不会嵌套?