Pliage de code dans bookdown et blogdown
L’option code_folding: true, comme dans les documents rmarkdown classiques, ne fonctionne pas avec bookdown ou blogdown. Il est quand même possible de l’activer avec quelques astuces. Tous les fichiers présentés ici, les fichiers javascript et Rmd nécessaires à bookdown et les fichiers html nécessaires à blogdown, pour activer le pliage de code, sont disponibles sur mon github (blog tips).
Comme pour l’article de blog que vous êtes en train de lire, ces codes permettent:
- un bouton sur chaque bloc de code pour afficher/cacher
- un bouton global avec un menu déroulant pour afficher/masquer tous les codes sur la page
J’ai passé des thèmes Hugo avec du code folding, retrouvez la liste ici.
Pliage du code dans bookdown
Il y a quelque temps, j’ai répondu à une question sur stackoverflow pour activer le pliage de code avec bookdown. Bien que cette réponse fonctionne, je n’étais pas totalement satisfait du comportement du bouton “Afficher/Masquer Global”. Aujourd’hui, je l’ai corrigé et il fonctionne proprement ! J’ai fait de “l’ingénierie inverse” pour trouver ce qui fonctionnait dans le rmarkdown classique et j’ai redéfini le css approprié pour bookdown.
Quelques codes javascript
La fonction principale de javascript qui sera appelée codefolding.js doit trouver le tag .sourceCode pour fonctionner avec bookdown. Cela nécessite également des fonctions javascript complémentaires de la librairie bootstrap, mais pas toutes.
Voici les étapes:
- Créez un dossier
jsdans le même répertoire que votre fichierRmd. - Téléchargez les fonctions javascript
transition.js,collapse.jsetdropdown.jsici par exemple: https://github.com/twbs/bootstrap/tree/v3.3.7/js et stockez-les dans votre dossierjs. - Créez un nouveau fichier dans le dossier
jsappelécodefolding.jsavec le code suivant. C’est la même chose que pour l’optioncode_foldingdermarkdownmais avecpre.sourceCodeajouté pour que la fonction trouve les morceaux de code R. Vous pouvez ajouter d’autres langage de code avecpre.langdans ce fichier.
Le fichier codefolding.js:
window.initializeCodeFolding = function(show) {
// handlers for show-all and hide all
$("#rmd-show-all-code").click(function() {
$('div.r-code-collapse').each(function() {
$(this).collapse('show');
});
});
$("#rmd-hide-all-code").click(function() {
$('div.r-code-collapse').each(function() {
$(this).collapse('hide');
});
});
// index for unique code element ids
var currentIndex = 1;
// select all R code blocks
var rCodeBlocks = $('pre.sourceCode, pre.r, pre.python, pre.bash, pre.sql, pre.cpp, pre.stan, pre.js');
rCodeBlocks.each(function() {
// create a collapsable div to wrap the code in
var div = $('<div class="collapse r-code-collapse"></div>');
if (show)
div.addClass('in');
var id = 'rcode-643E0F36' + currentIndex++;
div.attr('id', id);
$(this).before(div);
$(this).detach().appendTo(div);
// add a show code button right above
var showCodeText = $('<span>' + (show ? 'Hide' : 'Code') + '</span>');
var showCodeButton = $('<button type="button" class="btn btn-default btn-xs code-folding-btn pull-right"></button>');
showCodeButton.append(showCodeText);
showCodeButton
.attr('data-toggle', 'collapse')
.attr('data-target', '#' + id)
.attr('aria-expanded', show)
.attr('aria-controls', id);
var buttonRow = $('<div class="row"></div>');
var buttonCol = $('<div class="col-md-12"></div>');
buttonCol.append(showCodeButton);
buttonRow.append(buttonCol);
div.before(buttonRow);
// update state of button on show/hide
div.on('hidden.bs.collapse', function () {
showCodeText.text('Code');
});
div.on('show.bs.collapse', function () {
showCodeText.text('Hide');
});
});
}
Le fichier Rmd
Le script Rmd pour bookdown inclut toutes les fonctions js directement écrites dans l’en-tête, de sorte que le dossier js n’est pas utile pour le document final lui-même. Dans le bloc setup, j’ai défini l’option pour afficher tous les blocs de code par défaut dans le document, mais vous pouvez choisir de les masquer par défaut avec 'show' === 'hide'. Le fichier Rmd complet à utiliser comme base pour un document bookdown se trouve sur mon répertoire “blog tips” sur github.
- L’en-tête YAML suivant lit un fichier
header.htmlqui est construit en “knittant” le fichier Rmd.
---
title: "Toggle R code"
author: "StatnMap"
date: '06 mai, 2020'
output:
bookdown::html_document2:
includes:
in_header: header.html
bookdown::gitbook:
includes:
in_header: header.html
---
- Le code R ci-dessous doit être inclus dans un bloc avec
echo=FALSE. Ce code lit les fichiersjset les inclut dans un fichierheader.htmlexterne avec lecssassocié. Ceheader.htmlest ensuite directement inclus dans les fichiers bookdown grâce à la configurationyamlci-dessus.
Vous verrez qu’il y a quelquescssdans ce code pour gérer pour la position, les couleurs, … des boutons à cliquer.
codejs <- readr::read_lines("/mnt/Data/autoentrepreneur/js/codefolding.js")
collapsejs <- readr::read_lines("/mnt/Data/autoentrepreneur/js/collapse.js")
transitionjs <- readr::read_lines("/mnt/Data/autoentrepreneur/js/transition.js")
dropdownjs <- readr::read_lines("/mnt/Data/autoentrepreneur/js/dropdown.js")
htmlhead <- c(
paste('
<script>',
paste(transitionjs, collapse = "\n"),
'</script>
<script>',
paste(collapsejs, collapse = "\n"),
'</script>
<script>',
paste(codejs, collapse = "\n"),
'</script>
<script>',
paste(dropdownjs, collapse = "\n"),
'</script>
<style type="text/css">
.code-folding-btn { margin-bottom: 4px; }
.row { display: flex; }
.collapse { display: none; }
.in { display:block }
.pull-right > .dropdown-menu {
right: 0;
left: auto;
}
.open > .dropdown-menu {
display: block;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
list-style: none;
background-color: #fff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}
</style>
<script>
$(document).ready(function () {
window.initializeCodeFolding("show" === "show");
});
</script>
', sep = "\n"),
paste0('
<script>
document.write(\'<div class="btn-group pull-right" style="position: absolute; top: 20%; right: 2%; z-index: 200"><button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" data-_extension-text-contrast=""><span>Code</span> <span class="caret"></span></button><ul class="dropdown-menu" style="min-width: 50px;"><li><a id="rmd-show-all-code" href="#">Show All Code</a></li><li><a id="rmd-hide-all-code" href="#">Hide All Code</a></li></ul></div>\')
</script>
')
)
readr::write_lines(htmlhead, path = "/mnt/Data/autoentrepreneur/header.html")
Pliage du code en blogdown
Comme j’ai récemment migré vers hugo, j’ai décidé que je pouvais inclure cette astuce de pliage de code avec blogdown. Comme vous pouvez le voir sur l’article que vous lisez actuellement, le pliage de code est activé. Vous pouvez récupérer tous les codes qui vont suivre dans mon dossier “blog tips” sur github. Si vous voulez voir toutes les modifications que j’ai ajoutées à mon template de site Internet “hugo-statnmap-template” pour cette question de pliage de code, vous pouvez allez voir le “pull request” associé sur le github de mon template. Notez que mon template est multilingue, donc le code associé au pliage de code l’est aussi.
Le fonctionnement dépend des mêmes codes que pour bookdown, sauf que nous pouvons le configurer une seule fois pour l’ensemble du site en utilisant le thème.
Quelques bibliothèques javascripts
Les mêmes bibliothèques javascript sont nécessaires.
- Créez un dossier
jsdans le répertoirestaticde votre thème hugo. - Ajoutez les fonctions javascript complémentaires de bootstrap:
transition.js,collapse.jsetdropdown.js(ici par exemple) dans le répertoirejs. - Dans ce même répertoire
js, créez un nouveau fichier appelécodefolding.js. Le code est exactement le même que pour bookdown.
Le fichier codefolding.js:
window.initializeCodeFolding = function(show) {
// handlers for show-all and hide all
$("#rmd-show-all-code").click(function() {
$('div.r-code-collapse').each(function() {
$(this).collapse('show');
});
});
$("#rmd-hide-all-code").click(function() {
$('div.r-code-collapse').each(function() {
$(this).collapse('hide');
});
});
// index for unique code element ids
var currentIndex = 1;
// select all R code blocks
var rCodeBlocks = $('pre.sourceCode, pre.r, pre.python, pre.bash, pre.sql, pre.cpp, pre.stan, pre.js');
rCodeBlocks.each(function() {
// create a collapsable div to wrap the code in
var div = $('<div class="collapse r-code-collapse"></div>');
if (show)
div.addClass('in');
var id = 'rcode-643E0F36' + currentIndex++;
div.attr('id', id);
$(this).before(div);
$(this).detach().appendTo(div);
// add a show code button right above
var showCodeText = $('<span>' + (show ? 'Hide' : 'Code') + '</span>');
var showCodeButton = $('<button type="button" class="btn btn-default btn-xs code-folding-btn pull-right"></button>');
showCodeButton.append(showCodeText);
showCodeButton
.attr('data-toggle', 'collapse')
.attr('data-target', '#' + id)
.attr('aria-expanded', show)
.attr('aria-controls', id);
var buttonRow = $('<div class="row"></div>');
var buttonCol = $('<div class="col-md-12"></div>');
buttonCol.append(showCodeButton);
buttonRow.append(buttonCol);
div.before(buttonRow);
// update state of button on show/hide
div.on('hidden.bs.collapse', function () {
showCodeText.text('Code');
});
div.on('show.bs.collapse', function () {
showCodeText.text('Hide');
});
});
}
Modifiez vos modèles de templates Hugo
- Dans votre fichier de configuration de thème, vous pouvez ajouter les paramètres suivants pour activer ou désactiver totalement le pliage de code dans le site Web et définir s’il est affiché ou non par défaut. Notez que ceci peut également être défini dans chaque article du blog (voir ci-dessous).
# Set to true to disable code folding
disable_codefolding = false
# Set to "hide" or "show" all codes by default
codefolding_show = "hide"
- Dans le pied de page principal (footer) ou l’en-tête (header) de votre thème, vous devez charger les libraries javascript. Vous pouvez utiliser le code suivant (
footer_js.html).
{{ if not .Site.Params.disable_codefolding }}
<script src="{{ "js/collapse.js" | relURL }}"></script>
<script src="{{ "js/dropdown.js" | relURL }}"></script>
<script src="{{ "js/transition.js" | relURL }}"></script>
{{ end }}
- Pour pouvoir activer ou non le pliage de code dans chaque article, vous devez charger
codefolding.jsdans le pied de page (ou en-tête) de l’article directement. Vous pouvez également laisser la possibilité de choisir pour chaque article si tous les codes sont affichés ou masqués lors du chargement de l’article. C’est la même partie de code que pour bookdown avec"show" === "show", mais ici on utilise.Site.Paramsou.Paramspour le définir. Par exemple, sur mon site, vous pouvez aller sur les autres articles du blog et vous verrez que tous les codes sont cachés par défaut ("show" === "hide"). En revanche, lorsque vous arrivez sur l’article actuel sur le pliage de code, tous les codes ont été ouverts. C’est parce que j’ai miscodefolding_show: 'show'dans l’en-têteyamlde mon fichierRmd. De même pour le paramètredisable_codefolding: falseque vous pouvez paramétrer dans votre fichier de configuration du site principal ainsi que dans chaque article du blog. Pour ça, vous devez ajouter cette partie de code directement dans votre modèle de pied de page (ou en-tête) d’article (article_footer_js.html).
{{ if and (not .Site.Params.disable_codefolding) (not .Params.disable_codefolding) (in (string .Content) "</pre>") }}
<script>
$(document).ready(function () {
window.initializeCodeFolding("show" === {{ if isset .Params "codefolding_show" }}{{ .Params.codefolding_show }}{{ else }}{{ default ("hide") .Site.Params.codefolding_show }}{{ end }});
});
</script>
<script src="{{ "js/codefolding.js" | relURL }}"></script>
{{ end }}
- Vous devez trouver l’endroit approprié pour ajouter le code suivant dans le template de vos articles, dans l’en-tête de l’article par exemple. Ce code permet de créer un bouton principal pour afficher/masquer tous les blocs de code en même temps. Dans mon cas, j’ai stocké le code suivant dans
header_maincodefolding.htmldans le répertoiretemplate/partials. J’ai ensuite ajouté{{ partial "header_maincodefolding" . }}dans l’en-tête de mon article.
{{ if and (not .Site.Params.disable_codefolding) (not .Params.disable_codefolding) (in (string .Content) "</pre>") }}
<div id="code-folding-buttons" class="btn-group pull-right">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true" data-_extension-text-contrast="">
<span>Show/Hide all code</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" style="min-width: 50px;">
<li><a id="rmd-show-all-code" href="#">Show All Code</a>
</li><li><a id="rmd-hide-all-code" href="#">Hide All Code</a></li>
</ul>
</div>
{{ end }}
Ajouter quelques css
Pour que tous les boutons apparaissent correctement sur votre page web, vous devez les placer avec un peu de css. Voici les codes css que j’ai utilisés.
- Code pour le bouton principal “Afficher/masquer” tous les codes
#code-folding-buttons {float: right;}
#code-folding-buttons .pull-right > .dropdown-menu {
right: 0;
left: auto;
}
#code-folding-buttons .btn.btn-default.btn-xs.dropdown-toggle {
float: right;
}
#code-folding-buttons.open > .dropdown-menu {
display: block;
}
#code-folding-buttons .dropdown-menu {
top: 100%;
left: 0;
z-index: 1000;
display: none;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
text-align: left;
list-style: none;
background-color: #fff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
box-shadow: 0 6px 12px rgba(0,0,0,.175);
}
#code-folding-buttons .dropdown-menu li {
padding: 5px;
}
- Code pour le bouton au-dessus de chaque bloc de code
.code-folding-btn {
margin-bottom: 4px;
}
.row {
display: flex;
border-bottom: solid 1px #d7d7d7;
}
.collapse {
display: none;
}
.in {
display: block ;
border: solid 1px #d7d7d7;
border-radius: 5px;
}
.col-md-12 {
margin: 0 0 0 auto;
}
Une fois encore, je rappelle que tous ces scripts sont disponibles dans mon dossier “blog tips” sur github
Citation :
Merci de citer ce travail avec :
Rochette Sébastien. (2017, nov.. 13). "Afficher / cacher les zones de code avec bookdown et blogdown". Retrieved from https://statnmap.com/fr/2017-11-13-afficher-cacher-les-zones-de-code-avec-bookdown-et-blogdown/.
Citation BibTex :
@misc{Roche2017Affic,
author = {Rochette Sébastien},
title = {Afficher / cacher les zones de code avec bookdown et blogdown},
url = {https://statnmap.com/fr/2017-11-13-afficher-cacher-les-zones-de-code-avec-bookdown-et-blogdown/},
year = {2017}
}