Discourse verwendet eine Markdown-Engine namens Markdown-it.
Hier sind einige Entwicklerhinweise, die Ihnen helfen, Fehler im Kern zu beheben oder neue Plugins zu erstellen.
Die Grundlagen
Discourse enthält nur einige Hilfsprogramme über der Engine, daher besteht der Großteil des zu erlernenden Wissens darin, Markdown It zu verstehen.
Das docs-Verzeichnis enthält die aktuelle Dokumentation.
Ich empfehle dringend, Folgendes zu lesen:
-
Das Architekturdokument, um auf hoher Ebene zu verstehen, wie die Engine funktioniert.
-
Entwicklung für grundlegende Entwicklungsrichtlinien
-
API-Dokumentation für eine sehr detaillierte Referenz
-
Und schließlich den Quellcode, der sehr gut dokumentiert und übersichtlich ist.
Wenn ich Erweiterungen für die Engine entwickle, öffne ich normalerweise einen zweiten Editor und schaue mir bestehende Regeln an. Die Engine besteht aus einer langen Liste von Regeln, und jede Regel befindet sich in einer eigenen Datei, die einigermaßen leicht zu verfolgen ist.
Wenn ich an einer Inline-Regel arbeite, überlege ich, welche bestehende Inline-Regel ihr mehr oder weniger ähnlich ist, und basiere meine Arbeit darauf.
Denken Sie daran, dass Sie manchmal damit durchkommen, nur einen Renderer zu ändern, um die gewünschte Funktionalität zu erhalten, was normalerweise viel einfacher ist.
Wie strukturiert man eine Erweiterung?
Wenn die Markdown-Engine initialisiert wird, durchsucht sie alle Module.
Wenn ein Modul /discourse-markdown\\/|markdown-it\\// heißt (d. h. es befindet sich in einem discourse-markdown- oder markdown-it-Verzeichnis), ist es ein Kandidat für die Initialisierung.
Wenn das Modul eine Methode namens setup exportiert, wird diese während der Initialisierung von der Engine aufgerufen.
Das Setup-Protokoll
/my-plugins/assets/javascripts/discourse-markdown/awesome-extension.js
export function setup(helper) {
// ... Ihr Code kommt hierher
}
Eine setup-Methode erhält Zugriff auf ein Hilfsobjekt, das sie zur Initialisierung verwenden kann. Dieses enthält die folgenden Methoden und Variablen:
-
bool markdownIt: Diese Eigenschaft wird auftruegesetzt, wenn die neue Engine verwendet wird. Für eine ordnungsgemäße Abwärtskompatibilität sollten Sie dies überprüfen. -
registerOptions(cb(opts,siteSettings,state)): Die bereitgestellte Funktion wird aufgerufen, bevor die Markdown-Engine initialisiert wird. Sie können sie verwenden, um zu bestimmen, ob die Engine aktiviert oder deaktiviert werden soll. -
allowList([spec, ...]): Diese Methode wird verwendet, um HTML mit unserem Sanitizer auf die Whitelist zu setzen. -
registerPlugin(func(md)): Diese Methode wird verwendet, um ein Markdown It Plugin zu registrieren.
Alles zusammenfügen
function amazingMarkdownItInline(state, silent) {
// Standard markdown it inline-Erweiterung kommt hierher.
return false;
}
export function setup(helper) {
if(!helper.markdownIt) { return; }
helper.registerOptions((opts,siteSettings)=>{
opts.features.['my_extension'] = !!siteSettings.my_extension_enabled;
});
helper.allowList(['span.amazing', 'div.amazing']);
helper.registerPlugin(md=>{
md.inline.push('amazing', amazingMarkdownItInline);
});
}
Discourse-spezifische Erweiterungen
BBCode
Discourse enthält 2 Ruler, die Sie für benutzerdefinierte BBCode-Tags verwenden können. Einen Inline- und einen Block-Level-Ruler.
Inline-BBCode-Regeln sind solche, die in einem Inline-Absatz wie [b]fett[/b] stehen.
Block-Level-Regeln gelten für mehrere Textzeilen wie:
[poll]
- option 1
- options 2
[/poll]
md.inline.bbcode.ruler enthält eine Liste von Inline-Regeln, die in der Reihenfolge angewendet werden.
md.block.bbcode.ruler enthält eine Liste von Block-Level-Regeln.
Es gibt viele Beispiele für Inline-Regeln unter: bbcode-inline.js
Zitate und Umfragen sind gute Beispiele für BBCode-Blockregeln.
Inline BBCode-Regeln
Inline BBCode-Regeln sind ein Objekt, das Informationen darüber enthält, wie ein Tag zu behandeln ist.
Zum Beispiel:
md.inline.bbcode.ruler.push("underline", {
tag: "u",
wrap: "span.bbcode-u",
});
Bewirkt, dass
test [u]test[/u]
konvertiert wird zu:
test <span>test</span>
Inline-Regeln können Text entweder umschließen oder ersetzen. Beim Umschließen können Sie auch eine Funktion übergeben, um zusätzliche Flexibilität zu erhalten.
md.inline.bbcode.ruler.push("url", {
tag: "url",
wrap: function (startToken, endToken, tagInfo, content) {
const url = (tagInfo.attrs["_default"] || content).trim();
if (simpleUrlRegex.test(url)) {
startToken.type = "link_open";
startToken.tag = "a";
startToken.attrs = [
["href", url],
["data-bbcode", "true"],
];
startToken.content = "";
startToken.nesting = 1;
endToken.type = "link_close";
endToken.tag = "a";
endToken.content = "";
endToken.nesting = -1;
} else {
// einfach den bbcode-tag entfernen
endToken.content = "";
startToken.content = "";
// sonderfall, wir wollen nicht, dass dies als onebox erkannt wird, wenn es automatisch verlinkt ist
// dies stellt sicher, dass es nicht entfernt wird
startToken.type = "html_inline";
}
return false;
},
});
Die Wrapping-Funktion bietet Zugriff auf:
-
Die
tagInfo, ein Wörterbuch von Schlüssel/Werten, die über BBCode angegeben werden.[test=testing]→{_default: "testing"}
[test a=1]→{a: "1"} -
Das Token, das die Inline-Struktur startet
-
Das Token, das die Inline-Struktur beendet
-
Den Inhalt des BBCode-Inlines
Mithilfe dieser Informationen können Sie alle Arten von Wrapping-Anforderungen behandeln.
Gelegentlich möchten Sie den gesamten BBCode-Block ersetzen. Dafür können Sie replace verwenden.
md.inline.bbcode.ruler.push("code", {
tag: "code",
replace: function (state, tagInfo, content) {
let token;
token = state.push("code_inline", "code", 0);
token.content = content;
return true;
},
});
In diesem Fall ersetzen wir einen gesamten [code]Codeblock[code] durch ein einzelnes code_inline-Token.
Block BBCode-Regeln
Block-BBCode-Regeln ermöglichen es Ihnen, einen gesamten Block zu ersetzen. Die Block-APIs sind für einfache Fälle gleich:
md.block.bbcode.ruler.push("happy", {
tag: "happy",
wrap: "div.happy",
});
[happy]
hello
[/happy]
wird zu
<div>hello</div>
Die Funktions-Wrapper hat eine etwas andere API, da es keine umgebenden Tokens gibt.
md.block.bbcode.ruler.push("money", {
tag: "money",
wrap: function (token, tagInfo) {
token.attrs = [["data-money", tagInfo.attrs["_default"]]];
return true;
},
});
[money=100]
**test**
[/money]
wird zu
<div data-money="100">
<b>test</b>
</div>
Sie können die vollständige Kontrolle über das Block-Rendering mit den Regeln before und after erhalten. Dies ermöglicht es Ihnen, Dinge wie das doppelte Verschachteln eines Tags usw. zu tun.
md.block.bbcode.ruler.push("ddiv", {
tag: "ddiv",
before: function (state, tagInfo) {
state.push("div_open", "div", 1);
state.push("div_open", "div", 1);
},
after: function (state) {
state.push("div_close", "div", -1);
state.push("div_close", "div", -1);
},
});
[ddiv]
test
[/ddiv]
wird zu
<div>
<div>test</div>
</div>
Umgang mit Textersetzungen
Discourse liefert eine zusätzliche spezielle Kernregel zur Anwendung von regulären Ausdrücken auf Text.
md.core.textPostProcess.ruler
Zur Verwendung:
md.core.textPostProcess.ruler.push("onlyfastcars", {
matcher: /(car)|(bus)/, //regex flags werden NICHT unterstützt
onMatch: function (buffer, matches, state) {
let token = new state.Token("text", "", 0);
token.content = "fast " + matches[0];
buffer.push(token);
},
});
I like cars and buses
wird zu
<p>I like fast cars and fast buses</p>
Dieses Dokument wird versioniert – schlagen Sie Änderungen auf github vor.