如何在 Rmarkdown 中添加跨块的行号?

How can I add line numbers that go across chunks in Rmarkdown?

我想将代码行编号添加到我的 Rmarkdown 文件的 HTML 输出中。我很高兴 唯一地 识别输出中每一行代码的任何方法(例如,在整个文档中递增的连续行号,或者代码块由它们自己的索引标识,并且在这些代码块中,行号从 1) 开始。

我一直无法做到这一点。

使用其他 Whosebug 答案和 this blog post 我得到了行号,但它们会随着每个新块而重置。

我有以下 Rmd 文件:

---
output:
  html_document:
    highlight: kate
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  class.source = "numberLines lineAnchors"
  )
```

```{r}
5 + 5
3 * 9
```

```{r}
x <- 5
x * 3
```

编译为:

如您所见,当一个块被输出拆分或一个新块开始时,行号会重置。我希望看到行号为 1、2、3、4,而不是 1、1、1、2。

有人知道如何让它工作吗?

这是我可以建议的:使用行自动编号功能 + 使用 CSS 计数器在右侧显示块编号 ("cell")。然后你可以参考一个块+行。

---
title: "ChunkLine numbering"
author: "WeLoveDataScience"
output:
  html_document:
    highlight: pygments
---

```{css, echo=FALSE}
body {
  counter-reset: nchunk;
}


pre.r {
  counter-increment: nchunk;
  position: relative;
  overflow: visible;
}

pre.r::before {
  content: 'C[' counter(nchunk) ']';
  display: inline-block;
  position: absolute;
  right: 0em;
  color: rgb(50, 60, 160);
}
```

```{r cars, class.source = c("numCode", "r", "numberLines")}
summary(cars)
head(cars)
foo=function(x){
  2*x
}
```

```{r other, class.source = c("numCode", "r", "numberLines")}
1+1
```

我设法通过在 Rmd 文档末尾添加一些 jquery 来获得 HTML 文档的连续行号:

---
output:
  rmdformats::html_clean:
    highlight: kate
    toc: true
    use_bookdown: true
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  class.source = "numberLines lineAnchors"
  )
```

```{r}
5 + 5
3 * 9
```

```{r}
x <- 5
x * 3
```

<!-- 
The javascript below will reset the line numbers when
the document loads. 
-->

<script>
$(function() {
  $(".sourceLine").each( function( index ){
    $( this ).attr( "data-line-number", index + 1 );
  });
});
</script>

生成的文档具有连续的行号,如下所示:

它有点老套,因为它需要 jquery(可能是通过包含一些覆盖现有的 css 来做到这一点的方法),但它可以工作并且看起来很整洁。

2020 年 8 月 4 日更新

他们改变了行编号的工作方式(现在它基于 CSS 计数器)。为了让它现在在整个文档中按顺序工作,您需要插入一些 CSS。下面是一个完整的工作示例。重要的代码在 <style> 块中。它在 body(文档)级别重置相关计数器,并防止为每个代码块重置计数器。

---
output:
  rmdformats::html_clean:
    highlight: kate
    toc: true
    use_bookdown: true
---

<style>
body
  { counter-reset: source-line 0; }
pre.numberSource code
  { counter-reset: none; }
</style>

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  class.source = "numberLines lineAnchors"
  )
```

```{r}
5 + 5
3 * 9
```

```{r}
x <- 5
x * 3
```

正如@mb21 在评论中指出的那样,可以通过添加 startFrom 属性来控制代码块的第一行号。但是,这不能手动完成,因为 knitr 可以根据内容将代码块拆分为多个块。我们希望以编程方式添加此属性。

我知道的最简单的方法是让 pandoc 修改结构,因为在 pandoc 看到它们时所有的块都已经被评估过。我们将使用 Lua instead of R for scripting, as that is the most efficient when working with pandoc filters.

该脚本将跟踪它到目前为止看到的代码行数,并将正确的 startFrom 属性添加到源代码块。我们可以通过检查 numberLines class 来区分源块和结果块:只有前者有 class.

-- Number of code lines seen so far.
local total_lines_count = 0

-- Count the number of newlines in a string.
function count_lines (text)
  local count = 0
  local last_pos = 0
  repeat
    last_pos = string.find(text, '\n', last_pos + 1, true)
    count = count + 1
  until not last_pos
  return count
end

function CodeBlock (cb)
  -- do nothing for result blocks
  if not cb.classes:includes 'numberLines' then return nil end

  cb.attributes['startFrom'] = total_lines_count + 1
  total_lines_count = total_lines_count + count_lines(cb.text)
  return cb
end

现在唯一剩下的就是告诉 pandoc 在转换期间调用过滤器。这可以通过将 --lua-filter 选项添加到 pandoc_args:

来完成
---
output:
  html_document:
    highlight: kate
    pandoc_args: ['--lua-filter=number-lines.lua']
---

文件 number-lines.lua 应包含上面的 Lua 代码并放在与您的文档相同的文件夹中,或者放在 pandoc 数据目录下的 filters 文件夹中(参见 pandoc -v).

这种方法的优点是它适用于 HTML 以及 PDF 输出。