Discourse utilizza un motore Markdown chiamato Markdown-it.
Ecco alcune note di sviluppo che ti aiuteranno a correggere bug nel core o a creare i tuoi nuovi plugin.
Le basi
Discourse contiene solo pochi helper sopra il motore, quindi la maggior parte dell’apprendimento necessario consiste nel comprendere Markdown It.
La directory docs contiene la documentazione attuale.
Consiglio vivamente di leggere:
-
Il documento sull’architettura per capire a livello generale come funziona il motore.
-
Sviluppo per le linee guida di sviluppo di base
-
La documentazione API per un riferimento molto dettagliato
-
E infine, il codice sorgente che è molto ben documentato e chiaro.
Mentre sviluppo estensioni per il motore, apro di solito un secondo editor per guardare le regole esistenti. Il motore consiste in una lunga lista di regole e ogni regola è in un file dedicato che è ragionevolmente facile da seguire.
Se sto lavorando su una regola inline, penso a quale regola inline esistente funzioni più o meno in modo simile e baso il mio lavoro su di essa.
Tieni presente che a volte puoi cavartela semplicemente modificando un renderer per ottenere la funzionalità desiderata, il che è di solito molto più facile.
Come strutturare un’estensione?
Quando il motore markdown si inizializza, cerca attraverso tutti i moduli.
Se un modulo corrisponde a /discourse-markdown\/|markdown-it\// (cioè si trova in una directory discourse-markdown o markdown-it), sarà un candidato per l’inizializzazione.
Se il modulo esporta un metodo chiamato setup, verrà chiamato dal motore durante l’inizializzazione.
Il protocollo setup
/my-plugins/assets/javascripts/discourse-markdown/awesome-extension.js
export function setup(helper) {
// ... il tuo codice va qui
}
Un metodo setup ottiene accesso a un oggetto helper che può essere usato per l’inizializzazione. Questo contiene i seguenti metodi e variabili:
-
bool markdownIt: questa proprietà è impostata sutruequando il nuovo motore è in uso. Per una corretta compatibilità con le versioni precedenti, è consigliabile verificarla. -
registerOptions(cb(opts, siteSettings, state)): la funzione fornita viene chiamata prima che il motore markdown venga inizializzato; puoi usarla per determinare se abilitare o disabilitare il motore. -
allowList([spec, ...]): questo metodo viene usato per creare una lista di approvazione (allowlist) per l’HTML con il nostro sanitizzatore. -
registerPlugin(func(md)): questo metodo viene usato per registrare un plugin Markdown It.
Mettere tutto insieme
function amazingMarkdownItInline(state, silent) {
// qui va l'estensione inline standard di markdown it.
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);
});
}
Estensioni specifiche di Discourse
Generare riferimenti hashtag in Ruby
Usa HashtagAutocompleteService#hashtags_for per costruire riferimenti per il Markdown generato:
HashtagAutocompleteService.new(user.guardian).hashtags_for("tag", ["bug", "support"]).join(" ")
Se una categoria visibile ha lo slug bug, il risultato è #bug::tag #support.
Passa nomi già filtrati per la visibilità dello spettatore. Ordine e maiuscole/minuscole sono
preservati, i conflitti vengono controllati rispetto ai tipi di priorità superiore e un nome che
la regola hashtag non riesce a corrispondere ricade sul testo normale.
BBCode
Discourse contiene 2 righelli (rulers) che puoi usare per tag BBCode personalizzati. Un righello inline e uno a livello di blocco.
Le regole bbcode inline sono quelle che si trovano in un paragrafo inline come [b]bold[/b]
Le regole a livello di blocco si applicano a più righe di testo come:
[poll]
- opzione 1
- opzioni 2
[/poll]
md.inline.bbcode.ruler contiene una lista di regole inline applicate in ordine.
md.block.bbcode.ruler contiene una lista di regole a livello di blocco
Ci sono molti esempi di regole inline in: bbcode-inline.js
I Citazioni e i sondaggi sono buoni esempi di regole bbcode a blocco.
Regole BBCode inline
Le regole BBCode inline sono un oggetto contenente informazioni su come gestire un tag.
Ad esempio:
md.inline.bbcode.ruler.push("underline", {
tag: "u",
wrap: "span.bbcode-u",
});
Causerà la conversione di
test [u]test[/u]
in:
test <span class="bbcode-u">test</span>
Le regole inline possono avvolgere (wrap) o sostituire il testo. Quando si avvolge, si può anche passare una funzione per ottenere maggiore flessibilità.
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 {
// rimuovi semplicemente il tag bbcode
endToken.content = "";
startToken.content = "";
// caso limite, non vogliamo che questo venga rilevato come un onebox se collegato automaticamente
// questo assicura che non venga rimosso
startToken.type = "html_inline";
}
return false;
},
});
La funzione di avvolgimento fornisce accesso a:
-
Il tagInfo, che è un dizionario di chiavi/valori specificati tramite bbcode.
[test=testing]→{_default: "testing"}
[test a=1]→{a: "1"} -
Il token che inizia l’inline
-
Il token che termina l’inline
-
Il contenuto dell’inline bbcode
Usando queste informazioni puoi gestire ogni sorta di esigenze di avvolgimento.
A volte potresti voler sostituire l’intero blocco BBCode, per questo puoi usare replace
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 questo caso stiamo sostituendo un intero [code]code block[code] con un singolo token code_inline.
Regole BBCode a blocco
Le regole bbcode a blocco ti permettono di sostituire un intero blocco. Le API dei blocchi sono le stesse per i casi semplici:
md.block.bbcode.ruler.push("happy", {
tag: "happy",
wrap: "div.happy",
});
[happy]
hello
[/happy]
diventerà
<div class="happy">hello</div>
La funzione wrapper ha un’API leggermente diversa perché non ci sono token di avvolgimento.
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]
Diventerà
<div data-money="100">
<b>test</b>
</div>
Puoi ottenere il controllo completo sulla rendering dei blocchi con le regole before e after, questo ti permette di fare cose come annidare doppiamente un tag e così via.
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]
diventerà
<div>
<div>test</div>
</div>
Gestione delle sostituzioni di testo
Discourse è fornito con una regola core extra e speciale per applicare espressioni regolari al testo.
md.core.textPostProcess.ruler
Per usare:
md.core.textPostProcess.ruler.push("onlyfastcars", {
matcher: /(car)|(bus)/, // le flag regex NON sono supportate
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
Diventerà
<p>I like fast cars and fast buses</p>
Questo documento è versionato - suggerisci modifiche su github.