Différences entre les versions de « Module:Flowchart »
(Forme par défaut des nœuds "rectangle" → "rounded") |
(Correctif pour la génération de nœuds invisibles) |
||
Ligne 218 : | Ligne 218 : | ||
-- Génère les liens | -- Génère les liens | ||
local link_number = 0 | local link_number = 0 | ||
+ | |||
+ | -- Classe CSS pour les noeuds invisibles | ||
+ | table.insert(lines, "classDef SkipLevel width:0px;") | ||
for key, properties in pairs(links) do | for key, properties in pairs(links) do | ||
Ligne 233 : | Ligne 236 : | ||
end | end | ||
− | + | -- Définit les noeuds permettant de sauter de niveau. | |
− | + | -- Voir https://github.com/mermaid-js/mermaid/issues/637 pour la technique | |
− | + | -- qui consiste en rajouter des noeuds invisibles. | |
− | local | + | local level = nodes[to].level |
− | |||
− | |||
− | if | + | if level ~= '-' and level ~= nil then |
level = tonumber(level) | level = tonumber(level) | ||
− | if | + | if level == nil or level < 1 then |
error(string.format('Le niveau du noeud %s doit être un nombre entier > 0.', to), 0) | error(string.format('Le niveau du noeud %s doit être un nombre entier > 0.', to), 0) | ||
end | end | ||
− | + | local prev_id = from_id | |
− | |||
− | |||
local class = "class " .. from_id .. '-0' | local class = "class " .. from_id .. '-0' | ||
− | for i = 1, level do | + | for i = 1, level - 1 do |
− | + | -- Ajoute un noeud invisible | |
− | + | local next_id = from_id .. '-' .. i | |
− | table.insert(lines, | + | class = class .. ',' .. next_id |
+ | table.insert(lines, next_id .. '( )') | ||
− | + | -- Ajoute une connexion entre les noeuds invisibles | |
− | table.insert(lines, | + | table.insert(lines, prev_id .. " --- " .. next_id) |
+ | prev_id = next_id | ||
end | end | ||
− | |||
table.insert(lines, class .. " SkipLevel") | table.insert(lines, class .. " SkipLevel") | ||
− | |||
− | + | -- La dernière connexion sera créée normalement | |
+ | from_id = prev_id | ||
end | end | ||
Version du 15 août 2020 à 13:40
1 Utilisation[w]
Permet d'afficher un digramme de type "Flowchart" (algorithme). L'utilisation de ce modèle est différente car il ne possède pas de nombre de paramètres définis et les paramètres sont nommés selon la fonction qu'ils occupent dans le flowchart. Seuls les paramètres débutant par $
sont fixes.
Il est conseillé de passer le wikicode pour faire l'édition de ce modèle. L'éditeur visuel n'étant pas adapté à ce cas d'utilisation.
1.1 Nœuds[w]
Sous la forme id nœud=Étiquette à afficher pour le nœud
, quasiment tous les caractères sont autorisés dans l’identifiant du nœud, sauf ceux utilisés pour déclarer un lien, un paramètre général ou un groupe. On peut en plus définir des propriétés pour les nœuds, telles que :
id nœud.shape=...
spécifie la forme du nœud, parmirounded
(par défaut),rectangle
,circle
,flag
,diamond
.id nœud.group=...
définit le groupe de nœuds auquel appartient ce nœud, par défaut aucun.id nœud.style.fill=...
couleur de remplissage du nœud, par défaut c’est celle du thème Mermaid actif.id nœud.style.stroke=...
idem pour la bordure du nœud ( exemple:red,stroke-width:4px,stroke-dasharray: 5 5
)
1.2 Liens[w]
Ajout d’un lien entre deux nœuds. Sous la forme id nœud d’origine -> id nœud d’arrivée
, ou id nœud d’origine -> id nœud d’arrivée=Étiquette du lien
pour ajouter une étiquette. Comme pour les définitions de nœuds, on peut définir des propriétés pour le lien, telles que :
id nœud d’origine -> id nœud d’arrivée.shape=...
spécifie la forme du lien, parmicurve
(par défaut),linear
,step
,step before
,step after
.id nœud d’origine -> id nœud d’arrivée.ending=...
peut valoirarrow
pour ajouter une flèche au bout du lien (par défaut), ouplain
pour ne rien y mettre.
1.3 Groupes[w]
Définition d’un groupe de nœuds. Les nœuds sont ajoutés à un groupe en affectant leur propriété .group=id du groupe
. On peut en plus spécifier l’étiquette du groupe avec la syntaxe group id du groupe=Étiquette du groupe
(préfixé de group
).
2 Exemple[w]
{{Flowchart
| $orientation = to right
| Start = Boîte de début
| Start -> A
| A = Lien vers une [[AAA|maladie]]
| A -> End
| End.group = groupe
| group groupe = Un groupe
| Start -> B
| B = Texte en '''gras'''<br>Avec une autre ligne.<br> Et une autre.
| B -> B2
| B2 = Une condition?
| B2.shape = diamond
| B2.style.fill = #ff6666
| B2 -> B3 = Oui
| B3.group = groupe
| B2 -> End = Non
| Start -> C
| C -> Fin 2
| Fin 2.level = 2
| Fin 2.shape = rectangle
| Fin 2.style.fill = #ff9900
}}
3 Paramètres[w]
Génère un diagramme de type Flowchart (algorithme)
Paramètre | Description | Type | Statut | |
---|---|---|---|---|
titre | $titre | Titre du graphique | Contenu | facultatif |
Orientation | $orientation | Orientation du flowchart, peut être to bottom, to right, to top, to left.
| Chaîne | facultatif |
Déboggage | $debug | Active le mode déboggage
| Booléen | facultatif |
4 Notes techniques[w]
- En raison d'un conflit avec l'extension Flow, l'affichage des Flowcharts dans les discussions ne fonctionne pas car la librairie mermaid n'est pas chargée. Un patch a été ajouté dans MediaWiki:Common.js pour pallier à ce problème. Un bogue a été soumis auprès des développeurs.
- De base, Lua ne parse pas de Wikicode à l'interne, une fonction de parsing a donc été rajoutée à LocalSettings.js
local p = {} --[======[ Fonctions utilitaires ]======]-- local getArgs = require("Module:Arguments").getArgs --- Récupère la liste des clés d’un tableau associatif local function get_table_keys(t) local keys = {} for key, _ in pairs(t) do table.insert(keys, '"' .. key .. '"') end return table.concat(keys, ", ") end --- Convertit un identifiant d’objet en identifiant utilisé en syntaxe Mermaid local function generate_id(id) -- On utilise une version hachée de l’identifiant de chaque objet (avec un -- algorithme de hachage faible, acceptable ici car nous n’en faisons pas -- une utilisation cryptographique) afin de permettre l’utilisation de -- caractères arbitraires dans le Wikicode sans poser de problèmes au -- parseur Mermaid return mw.hash.hashValue("md5", id) end --- Échappe les guillemets droits utilisés dans une étiquette local function escape_quotes(label) return string.gsub(label, '"', "#quot;") end --- Découpe une chaîne autour de la première occurrence d’un délimiteur local function split_delimiter(s, delim) local d_start, d_end = string.find(s, delim, 1, true) if d_start and d_end then local left = mw.text.trim(string.sub(s, 1, d_start - 1)) local right = mw.text.trim(string.sub(s, d_end + 1)) return left, right else return s end end --- Affecte une valeur dans une clé d’une table, en utilisant un chemin en --- notation pointée local function set_key_path(t, key, default_key, value) local left, right = split_delimiter(key, ".") if not right and default_key then right = default_key end if not right then t[left] = value else if not t[left] then t[left] = {} end set_key_path(t[left], right, nil, value) end end --[======[ Représentation intermédiaire du flowchart ]======]-- local link_separator = "->" local group_prefix = "group " --- Lit la définition d’un flowchart function p.read_flowchart(args) local nodes = {} local groups = {} local links = {} for key, value in pairs(args) do if type(key) == "number" then key = value value = nil end local is_group = string.find(key, group_prefix, 1, true) == 1 local is_link = string.find(key, link_separator, 1, true) if is_group then -- Concerne un groupe de nœuds local group_key = string.sub(key, #group_prefix + 1) set_key_path(groups, group_key, "name", value) elseif is_link then -- Concerne un lien local base, rest = split_delimiter(key, ".") local from, to = split_delimiter(base, link_separator) local link_key = nil if not rest then link_key = string.format("%s->%s", from, to) else link_key = string.format("%s->%s.%s", from, to, rest) end if not nodes[from] then nodes[from] = {} end if not nodes[to] then nodes[to] = {} end set_key_path(links, link_key, "name", value) else -- Concerne un nœud set_key_path(nodes, key, "name", value) end end return nodes, groups, links end --[======[ Génération de code Mermaid ]======]-- local orientations = { ["to bottom"] = "TB", ["to top"] = "BT", ["to right"] = "LR", ["to left"] = "RL", } local nodes_shapes = { rectangle = '%s["%s"]', rounded = '%s("%s")', circle = '%s(("%s"))', flag = '%s>"%s"]', diamond = '%s{"%s"}', } local endings = { plain = "---", arrow = "-->", } local links_shapes = { curve = "basis", linear = "linear", ["step before"] = "stepBefore", ["step after"] = "stepAfter", step = "step", } --- Génère le code Mermaid pour un flowchart function p.flowchart_to_mermaid(params, nodes, groups, links) local lines = {} local orientation = params.orientation or "to bottom" if not orientations[orientation] then error(string.format( 'Orientation "%s" inconnue pour le flowchart. Les orientations possibles sont %s.', orientation, get_table_keys(orientations) ), 0) end table.insert(lines, string.format("graph %s", orientations[orientation])) -- Liste des nœuds membres de chaque groupe, construite en même temps -- qu’on itère sur les nœuds pour les insérer dans la sortie local groups_members = {} for key, _ in pairs(groups) do groups_members[key] = {} end -- Génère les nœuds for key, properties in pairs(nodes) do local id = generate_id(key) local name = properties.name or key local shape = properties.shape or "rounded" if properties.group then table.insert(groups_members[properties.group], id) end if not nodes_shapes[shape] then error(string.format( 'Forme "%s" inconnue pour le nœud "%s". Les formes possibles sont %s.', shape, name, get_table_keys(nodes_shapes) ), 0) end -- Forme et étiquette du nœud table.insert(lines, string.format(nodes_shapes[shape], id, escape_quotes(name))) -- Style if properties.style then local rules = {} for property, value in pairs(properties.style) do table.insert(rules, string.format("%s:%s", property, value)) end table.insert(lines, string.format("style %s %s", id, table.concat(rules, ","))) end end -- Génère les groupes for key, properties in pairs(groups) do local name = properties.name or key table.insert(lines, string.format('subgraph %s', name)) for _, node in pairs(groups_members[key]) do table.insert(lines, node) end table.insert(lines, "end") end -- Génère les liens local link_number = 0 -- Classe CSS pour les noeuds invisibles table.insert(lines, "classDef SkipLevel width:0px;") for key, properties in pairs(links) do local from, to = split_delimiter(key, link_separator) local from_id = generate_id(from) local to_id = generate_id(to) local ending = properties.ending or "arrow" if not endings[ending] then error(string.format( 'Terminaison "%s" inconnue pour le lien "%s -> %s". Les terminaisons possibles sont %s.', shape, from, to, get_table_keys(endings) ), 0) end -- Définit les noeuds permettant de sauter de niveau. -- Voir https://github.com/mermaid-js/mermaid/issues/637 pour la technique -- qui consiste en rajouter des noeuds invisibles. local level = nodes[to].level if level ~= '-' and level ~= nil then level = tonumber(level) if level == nil or level < 1 then error(string.format('Le niveau du noeud %s doit être un nombre entier > 0.', to), 0) end local prev_id = from_id local class = "class " .. from_id .. '-0' for i = 1, level - 1 do -- Ajoute un noeud invisible local next_id = from_id .. '-' .. i class = class .. ',' .. next_id table.insert(lines, next_id .. '( )') -- Ajoute une connexion entre les noeuds invisibles table.insert(lines, prev_id .. " --- " .. next_id) prev_id = next_id end table.insert(lines, class .. " SkipLevel") -- La dernière connexion sera créée normalement from_id = prev_id end if properties.name then table.insert(lines, string.format('%s -- "%s" %s %s', from_id, escape_quotes(properties.name), endings[ending], to_id)) else table.insert(lines, string.format("%s %s %s", from_id, endings[ending], to_id)) end local shape = properties.shape or "curve" if not links_shapes[shape] then error(string.format( 'Forme "%s" inconnue pour le lien "%s -> %s". Les formes possibles sont %s.', shape, from, to, get_table_keys(links_shapes) ), 0) end if shape ~= "linear" then table.insert(lines, string.format("linkStyle %d interpolate %s", link_number, links_shapes[shape])) end link_number = link_number + 1 end return table.concat(lines, "\n") end local themes = { default = true, neutral = true, forest = true, dark = true, } --- Appelle l’extension Mermaid function p.render_mermaid(frame, params, nodes, groups, links) local mermaid = p.flowchart_to_mermaid(params, nodes, groups, links) local theme = params.theme or "neutral" if not themes[theme] then error(string.format( 'Thème "%s" inconnu pour le flowchart. Les thèmes possibles sont %s.', theme, get_table_keys(themes) ), 0) end local rendered_mermaid = frame:callParserFunction( "#mermaid", mermaid, "config.theme = " .. theme ) if params.debug then dump_params = mw.text.nowiki(mw.dumpObject(params)) dump_nodes = mw.text.nowiki(mw.dumpObject(nodes)) dump_groups = mw.text.nowiki(mw.dumpObject(groups)) dump_links = mw.text.nowiki(mw.dumpObject(links)) dump_mermaid = mw.text.nowiki(mermaid) return string.format([[ <h4>Représentation interne</h4> <table> <tr> <th>params</th> <th>nodes</th> <th>groups</th> <th>links</th> </tr> <tr> <td><pre>%s</pre></td> <td><pre>%s</pre></td> <td><pre>%s</pre></td> <td><pre>%s</pre></td> </tr> </table> <h4>Code Mermaid</h4> <pre>%s</pre> <h4>Résultat</h4> %s ]], dump_params, dump_nodes, dump_groups, dump_links, dump_mermaid, rendered_mermaid) else return rendered_mermaid end end --[======[ Interface ]======]-- function p.render(frame) -- Normalise les arguments local args = getArgs(frame, { wrappers = 'Modèle:Flowchart' }) -- Lit et consomme les paramètres généraux du flowchart préfixés par "$" local params = {} for args_key, value in pairs(args) do local key = args_key if type(args_key) == "number" then key = value value = "true" end if string.sub(key, 1, 1) == "$" then params[string.sub(key, 2)] = value args[args_key] = nil end end local nodes, groups, links = p.read_flowchart(args) return p.render_mermaid(frame, params, nodes, groups, links) end return p