Wie es funktioniert?
Es funktioniert, indem ein String Stück für Stück gelesen wird, was möglicherweise nicht die beste Lösung für wirklich lange Strings ist.
Immer wenn der Parser feststellt, dass ein kritischer Block gelesen wird, d. H. '*'
ein anderes Markdown-Tag, beginnt er mit dem Parsen von Blöcken dieses Elements, bis der Parser sein schließendes Tag findet.
Es funktioniert mit mehrzeiligen Zeichenfolgen, siehe zum Beispiel den Code.
Vorsichtsmaßnahmen
Sie haben nichts angegeben, oder ich hätte Ihre Anforderungen falsch verstehen können, wenn die Notwendigkeit besteht, fett und kursiv gedruckte Tags zu analysieren funktioniert meine aktuelle Lösung in diesem Fall möglicherweise nicht.
Wenn Sie jedoch mit den oben genannten Bedingungen arbeiten müssen, kommentieren Sie einfach hier und ich werde den Code optimieren.
Erstes Update: Optimiert die Behandlung von Markdown-Tags
Tags sind nicht mehr fest codiert, sondern eine Karte, die Sie problemlos erweitern können, um sie Ihren Anforderungen anzupassen.
Die Fehler, die Sie in den Kommentaren erwähnt haben, wurden behoben. Vielen Dank, dass Sie auf diese Probleme hingewiesen haben = p
Zweites Update: Markdown-Tags mit mehreren Längen
Der einfachste Weg, dies zu erreichen: Ersetzen von Zeichen mit mehreren Längen durch einen selten verwendeten Unicode
Obwohl die Methode parseMarkdown
noch keine Tags mit mehreren Längen unterstützt, können wir diese Tags mit mehreren Längen string.replace
beim Senden unserer rawMarkdown
Requisite problemlos durch einfache Tags ersetzen .
Um ein Beispiel dafür in der Praxis zu sehen, schauen Sie sich das an ReactDOM.render
, das sich am Ende des Codes befindet.
Auch wenn Ihre Anwendung funktioniert Unterstützung mehrerer Sprachen, gibt es ungültige Unicode - Zeichen , dass JavaScript erkennt noch, ex .: "\uFFFF"
kein gültiges Unicode, wenn ich mich richtig erinnere, aber JS noch in der Lage sein , sie zu vergleichen ("\uFFFF" === "\uFFFF" = true
)
Auf den ersten Blick mag es hackig erscheinen, aber je nach Anwendungsfall sehe ich bei Verwendung dieser Route keine größeren Probleme.
Ein anderer Weg, dies zu erreichen
Nun, wir könnten leicht den letzten verfolgen N
(woN
der Länge des längsten Tags mit mehreren Längen entspricht).
Es müssten einige Änderungen am Verhalten der Schleife innerhalb der Methode vorgenommen werden
parseMarkdown
, dh es wird geprüft, ob der aktuelle Block Teil eines Tags mit mehreren Längen ist, wenn er als Tag verwendet wird. Andernfalls ``k
müssten wir es in Fällen wie markierennotMultiLength
oder ähnlichem und diesen Teil als Inhalt verschieben.
Code
// 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'));
Link zum Code (TypeScript) https://codepen.io/ludanin/pen/GRgNWPv
Link zum Code (Vanille / Babel) https://codepen.io/ludanin/pen/eYmBvXw
font _italic *and bold* then only italic_ and normal
? Was wäre das erwartete Ergebnis? Oder wird es niemals verschachtelt sein?