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 größte Teil des Lernaufwands 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 klar 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 dedizierten 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 ähnelt, und basiere meine Arbeit darauf.
Beachten Sie, dass Sie manchmal damit durchkommen, nur einen Renderer zu ändern, um die gewünschte Funktionalität zu erhalten, was normalerweise viel einfacher ist.
Wie strukturiere ich eine Erweiterung?
Wenn die Markdown-Engine initialisiert wird, sucht sie alle Module durch.
Wenn ein Modul /discourse-markdown\\/|markdown-it\\// genannt wird (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 Helper-Objekt, 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 sie ü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 extension goes here.
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 sich in einem Inline-Absatz befinden, wie [b]fett[/b]
Block-Level-Regeln gelten für mehrere Textzeilen, wie zum Beispiel:
[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 in:
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 {
// just strip the bbcode tag
endToken.content = "";
startToken.content = "";
// edge case, we don't want this detected as a onebox if auto linked
// this ensures it is not stripped
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 beginnt
-
Das Token, das die Inline-Struktur beendet
-
Den Inhalt des BBCode-Inlines
Mithilfe dieser Informationen können Sie alle Arten von Wrapping-Anforderungen verarbeiten.
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 einziges 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 class="happy">hello</div>
Die Funktions-Wrapper hat eine leicht 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 Text-Ersetzungen
Discourse liefert eine zusätzliche spezielle Kernregel zur Anwendung von regulären Ausdrücken auf Text.
md.core.textPostProcess.ruler
Verwendung:
md.core.textPostProcess.ruler.push("onlyfastcars", {
matcher: /(car)|(bus)/, //regex flags are NOT supported
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.