Code folding is an interesting feature in Rmarkdown documents. Find out how to realise it in bookdown documents and blogdown websites.
Code folding in bookdown and blogdown
Option code_folding: true
, like in classical rmarkdown documents, is not working in bookdown or blogdown but it is possible to enable it with some tricks. All files presented here, the javascript
and Rmd
files necessary for bookdown and the html
files necessary for blogdown, to enable code folding are available on my github blog tips repository.
As in the present blog post, these codes allow for:
- a button on each code chunk to show/hide it
- a global button with a dropdown menu to show/hide all codes on your page
I modified Hugo themes with code folding, find out the list here.
Code folding in bookdown
Some time ago, I answered a question on stackoverflow to make code folding working with bookdown. Although this answer works, I was not totally satisfied with the behavior of the “Show/Hide Global” button. Today, I finally made it work properly ! I did “reverse engineering” to find what worked in the classical rmarkdown
and defined the appropriate css
for bookdown.
Some javascript codes
The main javascript function that will be called codefolding.js
needs to find .sourceCode
class divisions to work with bookdown. This also requires complementary javascript functions of bootstrap, but not all.
Here are the steps:
- Create a
js
folder in the same directory than yourRmd
file - Download javascript functions
transition.js
,collapse.js
anddropdown.js
here for instance: https://github.com/twbs/bootstrap/tree/v3.3.7/js and store them in yourjs
folder - Create a new file in the
js
folder calledcodefolding.js
with the following code. This is the same as for rmarkdowncode_folding
option but withpre.sourceCode
added to find R-code chunks. You can had any other code language withpre.lang
in this file.
The codefolding.js
file:
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');
});
});
}
The Rmd file
The bookdown Rmd
script includes all js
functions directly written in the header, so that the js
folder is not useful for the final document itself. When creating the setup
chunk, the option is set to show all code blocks by default, but you can choose to hide them by default with 'show' === 'hide'
. The complete Rmd file to be used as a basis can be found on my blog tips github repository.
- YAML header reads a
header.html
that is built when knitting the Rmd file.
---
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
---
- The R-code below is to be included in a chunk with
echo=FALSE
. This readsjs
files and includes them in an externalheader.html
with thecss
asociated. Thisheader.html
will then be directly included in the bookdown files thanks to the aboveyaml
configuration.
You’ll see that there is somecss
in this code that you can modify for the position, colors and whatever you want on these buttons with some morecss
.
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")
Code folding in blogdown
As I recently migrated to hugo, I decided I could include this trick with blogdown. As you can see on the present article, code folding is enabled. You can retrieve all following codes in my github blog tips repository. If you want to see all modifications that I added to my “hugo-statnmap-template” for code folding, I recommend you to refer to this code-folding pull request in my hugo template on github. Note that my template is multilingual, so is the code-folding code on github too.
This relies on the same codes than for bookdown, except that we can set it up once for the entire website using your theme.
Some javascript libraries
The same javascript libraries are necessary.
- Create a
js
folder in thestatic
directory of your hugo theme. - Add the complementary javascript functions of bootstrap:
transition.js
,collapse.js
anddropdown.js
(here for instance) in thejs
directory. - In this same
js
directory, create a new file calledcodefolding.js
. The code is exactly the same as for bookdown.
The codefolding.js
file:
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');
});
});
}
Modify your hugo templates
- In your theme config file, you can add the following parameters to enable or disable totally codefolding in the website and to define if it is shown or not by default. Note that this can also be defined in each blog article (see below).
# Set to true to disable code folding
disable_codefolding = false
# Set to "hide" or "show" all codes by default
codefolding_show = "hide"
- In the main footer (or header) of your website, you need to load the javascript libraries. You can use the following code (
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 }}
- To be able to allow or not code_folding in each article, you need to load
codefolding.js
in the article footer (or header) directly. You can also let the possibility to choose for each article if all codes are shown or hidden when loading the article. This is the same part of code as for bookdown with"show" === "show"
, but here we use.Site.Params
or.Params
to define it. For instance, in the present website, you can go on the other blog posts and you will see that all codes are hidden by default"show" === "hide"
. When you arrive on the present article on code-folding, all codes where opened. This is because I setcodefolding_show: 'show'
in theyaml
header of myRmd
file. Same is for parameterdisable_codefolding: false
that you can set in your main website config file as well as in each blog article. Thus, you need to add this part of code in your article footer (or header) template directly (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 }}
- You need to find the appropriate place to add the following code in the template of your articles, maybe in article header. This code creates the main button that will enable “show/hide all code blocks” at the same time. In my case, I stored the following code in
header_maincodefolding.html
in thetemplate/partials
directory. I then added{{ partial "header_maincodefolding" . }}
in my article header.
{{ 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 }}
Add some css
For all the buttons to appear correctly on your webpage, you need to place them with some css. Here are the css
codes I used.
- Code for the main “show/hide all codes” button
#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 for buttons over each code block
.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;
}
Again, all these codes are available on my github blog tips repository
Citation:
For attribution, please cite this work as:
Rochette S. (2017, Nov. 13). "Enable code folding in bookdown and blogdown". Retrieved from https://statnmap.com/2017-11-13-enable-code-folding-in-bookdown-and-blogdown/.
BibTex citation:
@misc{Roche2017Enabl,
author = {Rochette S},
title = {Enable code folding in bookdown and blogdown},
url = {https://statnmap.com/2017-11-13-enable-code-folding-in-bookdown-and-blogdown/},
year = {2017}
}