Как добавить сворачивание кода в выходные фрагменты в rmarkdown html-документах

Я очень ценю функцию "code_folding" в RMarkdown. Тем не менее, мне действительно нужно, чтобы код отображался все время и переключал отображение на выходе.

---
title: "test file"
author: "dayne"
date: "June 10, 2016"
output: 
  html_document:
    code_folding: hide
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

Here is a basic example.

```{r}
3 + 4
```

Есть ли способ переключить вывод, а не код? Лучшее (но не идеальное) решение, о котором я думал, состоит в том, чтобы добавить collapse=TRUE в куски, но тогда код и вывод все же отображаются одновременно.

Ссылка на скомпилированный документ: http://rpubs.com/daynefiler/188408

Ответы

Ответ 1

TOC:

  • Полный контроль над тем, какие куски должны быть сложены

  • Сложите все куски, содержащие более одной строки кода/вывода


1. Полный контроль над тем, какие куски должны быть сложены

Я хотел иметь такую ​​же функциональность, и сделал следующее:

Я создал JavaScript, который выглядит следующим образом:

$(document).ready(function() {

  $chunks = $('.fold');

  $chunks.each(function () {

    // add button to source code chunks
    if ( $(this).hasClass('s') ) {
      $('pre.r', this).prepend("<div class=\"showopt\">Show Source</div><br style=\"line-height:22px;\"/>");
      $('pre.r', this).children('code').attr('class', 'folded');
    }

    // add button to output chunks
    if ( $(this).hasClass('o') ) {
      $('pre:not(.r)', this).has('code').prepend("<div class=\"showopt\">Show Output</div><br style=\"line-height:22px;\"/>");
      $('pre:not(.r)', this).children('code:not(r)').addClass('folded');

      // add button to plots
      $(this).find('img').wrap('<pre class=\"plot\"></pre>');
      $('pre.plot', this).prepend("<div class=\"showopt\">Show Plot</div><br style=\"line-height:22px;\"/>");
      $('pre.plot', this).children('img').addClass('folded');

    }
  });

  // hide all chunks when document is loaded
  $('.folded').css('display', 'none')

  // function to toggle the visibility
  $('.showopt').click(function() {
    var label = $(this).html();
    if (label.indexOf("Show") >= 0) {
      $(this).html(label.replace("Show", "Hide"));
    } else {
      $(this).html(label.replace("Hide", "Show"));
    }
    $(this).siblings('code, img').slideToggle('fast', 'swing');
  });
});

Так как я не трещина JS, это может быть не идеально, но он делает то, что предполагается. Включите его в свой файл Rmd:

<script src="js/hideOutput.js"></script>

Я также написал некоторые определения CSS для стилизации кнопки:

.showopt {
  background-color: #004c93;
  color: #FFFFFF; 
  width: 100px;
  height: 20px;
  text-align: center;
  vertical-align: middle !important;
  float: right;
  font-family: sans-serif;
  border-radius: 8px;
}

.showopt:hover {
    background-color: #dfe4f2;
    color: #004c93;
}

pre.plot {
  background-color: white !important;
}

После включения обоих файлов JS и таблицы стилей вы можете скрыть куски, обернув вокруг них контейнер div одним из следующих классов:

Скрыть только вывод

<div class="fold o">
```{r}
  ...
```
</div>

Скрыть исходный код

<div class="fold s">
```{r}
  ...
```
</div>

Скрыть оба

<div class="fold s o">
```{r}
  ...
```
</div>

script определяет тип каждого фрагмента (например, исходный код, текстовый вывод или вывод графика) и соответствующим образом намечает кнопки.

Результат выглядит следующим образом:

введите описание изображения здесь

введите описание изображения здесь


2. Сложите все куски, содержащие более одной строки кода/вывода

Вот версия script, которая добавляет функцию сгибания ко всем фрагментам длиной более одной строки:

$(document).ready(function() {
  $plots = $('img.plot');
  $chunks = $('pre').has('code');
  $chunks = $chunks.filter(function(idx) {
    return $(this).children('code').outerHeight(false) > parseInt($(this).css('line-height'));
  });

  $chunks.each(function () {
    if($(this).hasClass('r')) {
      $(this).append("<div class=\"showopt\">Show Source</div><br style=\"line-height:22px;\"/>");
    } else {
      $(this).append("<div class=\"showopt\">Show Output</div><br style=\"line-height:22px;\"/>");
    }
  });

  $plots.each(function () {
    $(this).wrap('<pre class=\"plot\"></pre>');
    $(this).parent('pre.plot').prepend("<div class=\"showopt\">Show Plot</div><br style=\"line-height:22px;\"/>");
  });

  // hide all chunks when document is loaded
  $chunks.children('code').toggle();
  $('pre.plot').children('img').toggle();
  // function to toggle the visibility
  $('.showopt').click(function() {
    var label = $(this).html();
    if (label.indexOf("Show") >= 0) {
      $(this).html(label.replace("Show", "Hide"));
    } else {
      $(this).html(label.replace("Hide", "Show"));
    }
    $(this).siblings('code, img').slideToggle('fast', 'swing');
  });
});

Просто включите его с помощью <script src="js/hideAll.js"></script>, и вам не нужно обертывать контейнеры div вокруг ваших фрагментов кода. Одна вещь, которую вы должны добавить в свой документ Rmd, является следующей глобальной опцией:

```{r, echo = F}
knitr::opts_chunk$set(out.extra = 'class="plot"')
```

Необходимо определить графический вывод.

Ответ 2

Я прямо скопировал javascript из источника другого документа Rmarkdown, где я установил code_folding: show. Я сохранил javascript как py_code_folding.js и добавил <script src="py_code_folding.js"></script> в начало моего документа Rmarkdown. Единственным ограничением является то, что мне пришлось жестко кодировать в своем javascript, хочу ли я, чтобы блоки были первоначально показаны или скрыты.

window.initializePythonCodeFolding = 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 = 10000;

  // select all R code blocks
  var rCodeBlocks = $('pre.python');
  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');
    });
  });

}

$(document).ready(function () {
  window.initializePythonCodeFolding("show" === "show");
});

Ответ 3

Быстрый хакерский способ переключения раздела (не обязательно кода):

Включите разделы для переключения с <div class="toggle"><button>TOGGLE_TEXT</button> и <\div> в ваш .Rmd файл

1. How many users are in the second, but not the first, user table?

    <div class="toggle"><button>Solution</button>
    ```{r}
    setdiff(user2, user) %>% nrow()
    ```
    </div>

Поместите это внизу файла .Rmd(или, в идеале, в файл .js, связанный со всеми вашими страницами).

<script>
  $(".toggle").click(function() {
    $(this).toggleClass("open");
  });
</script>

Поместите это в ваш .css файл (вам нужно будет играть с высотой для вашей кнопки).

.toggle {
  height: 1.55em;
  overflow-y: hidden;
}
.toggle.open {
  height: auto;
}