Discourse verwendet einen Markdown-Engine namens Markdown-it.
Hier sind einige Entwicklungshinweise, die dir helfen, Fehler im Kern zu beheben oder neue Plugins zu erstellen.
Die Grundlagen
Discourse enthält nur wenige Helfer auf Basis der Engine. Der Großteil des Lernens besteht daher darin, Markdown It zu verstehen.
Das Dokumentationsverzeichnis enthält die aktuelle Dokumentation.
Ich empfehle dringend, Folgendes zu lesen:
-
Das Architektur-Dokument, um auf hoher Ebene zu verstehen, wie die Engine funktioniert.
-
Entwicklung für grundlegende Entwicklungshinweise
-
API-Dokumentation für eine sehr detaillierte Referenz
-
Und schließlich der Quellcode, der sehr gut dokumentiert und klar strukturiert ist.
Während ich Erweiterungen für die Engine entwickle, öffne ich normalerweise einen zweiten Editor, um mir vorhandene Regeln anzusehen. Die Engine besteht aus einer langen Liste von Regeln, und jede Regel befindet sich in einer eigenen Datei, die sich gut nachvollziehen lässt.
Wenn ich an einer Inline-Regel arbeite, überlege ich, welche vorhandene Inline-Regel mehr oder weniger ähnlich funktioniert, und baue meine Arbeit darauf auf.
Denke daran, dass man manchmal auskommt, indem man nur einen Renderer ändert, um die gewünschte Funktionalität zu erhalten. Das ist in der Regel viel einfacher.
Wie strukturiert man eine Erweiterung?
Wenn die Markdown-Engine initialisiert wird, durchsucht sie alle Module.
Wenn ein Modul /discourse-markdown\/|markdown-it\// heißt (was bedeutet, dass es sich in einem discourse-markdown- oder markdown-it-Verzeichnis befindet), ist es ein Kandidat für die Initialisierung.
Wenn das Modul eine Methode namens setup exportiert, wird diese von der Engine während der Initialisierung aufgerufen.
Das Setup-Protokoll
/my-plugins/assets/javascripts/discourse-markdown/awesome-extension.js
export function setup(helper) {
// ... dein Code kommt hier hin
}
Eine setup-Methode erhält Zugriff auf ein Helper-Objekt, das für die Initialisierung verwendet werden kann. Dieses enthält die folgenden Methoden und Variablen:
-
bool markdownIt: Diese Eigenschaft wird auftruegesetzt, wenn die neue Engine verwendet wird. Für eine korrekte Abwärtskompatibilität solltest du dies prüfen. -
registerOptions(cb(opts, siteSettings, state)): Die übergebene Funktion wird vor der Initialisierung der Markdown-Engine aufgerufen. Du kannst sie verwenden, um zu bestimmen, ob die Engine aktiviert oder deaktiviert werden soll. -
allowList([spec, ...]): Diese Methode wird verwendet, um HTML mit unserem Sanitizer zu erlauben (Allowlist). -
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 hier hin.
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 zwei Regler (Rulers), die du für eigene BBCode-Tags verwenden kannst. Einen für Inline-Ebene und einen für Block-Ebene.
Inline-BBCode-Regeln sind solche, die in einem Inline-Absatz stehen wie [b]fett[/b]
Block-Regeln gelten für mehrere Zeilen Text wie:
[poll]
- Option 1
- Option 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-Regeln
Es gibt viele Beispiele für Inline-Regeln unter: bbcode-inline.js
Zitate und Umfragen sind gute Beispiele für BBCode-Block-Regeln.
Inline-BBCode-Regeln
Inline-BBCode-Regeln sind ein Objekt, das Informationen darüber enthält, wie ein Tag verarbeitet werden soll.
Zum Beispiel:
md.inline.bbcode.ruler.push("underline", {
tag: "u",
wrap: "span.bbcode-u",
});
Verursacht, dass
test [u]test[/u]
umgewandelt wird in:
test <span class="bbcode-u">test</span>
Inline-Regeln können Text entweder einhüllen (wrap) oder ersetzen (replace). Beim Einhüllen kannst du 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 {
// entferne einfach das bbcode-Tag
endToken.content = "";
startToken.content = "";
// Randfall, wir wollen nicht, dass dies als Onebox erkannt wird, wenn automatisch verlinkt
// dies stellt sicher, dass es nicht entfernt wird
startToken.type = "html_inline";
}
return false;
},
});
Die Einhüllungs-Funktion bietet Zugriff auf:
-
Die tagInfo, die ein Wörterbuch von Schlüssel/Wert-Paaren ist, die über BBCode angegeben wurden.
[test=testing]→{_default: "testing"}
[test a=1]→{a: "1"} -
Das Token, das das Inline-Element beginnt
-
Das Token, das das Inline-Element beendet
-
Den Inhalt des BBCode-Inline-Elements
Mit diesen Informationen kannst du alle Arten von Einhüllungsbedürfnissen verarbeiten.
Gelegentlich möchtest du möglicherweise den gesamten BBCode-Block ersetzen. Dafür kannst du 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]code block[code] durch ein einzelnes code_inline-Token.
Block-BBCode-Regeln
Block-BBCode-Regeln erlauben es dir, einen gesamten Block zu ersetzen. Die Block-APIs sind für einfache Fälle die gleichen:
md.block.bbcode.ruler.push("happy", {
tag: "happy",
wrap: "div.happy",
});
[happy]
hello
[/happy]
wird zu
<div class="happy">hello</div>
Die Funktions-Hülle hat eine leicht andere API, da es keine Einhüllungs-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>
Mit den Regeln before und after kannst du die volle Kontrolle über die Block-Renderung erhalten. Damit kannst du zum Beispiel Tags doppelt verschachteln und so weiter.
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>
Verarbeitung von Textersetzungen
Discourse bringt eine besondere Kernregel mit, um reguläre Ausdrücke auf Text anzuwenden.
md.core.textPostProcess.ruler
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 – Vorschläge für Änderungen auf github.