Question: Is there an automatic way to add the line numbers of the original R Markdown source code to the formatted code portions of the HTML output produced by knitr?
Purpose: My ultimate goal is to be able to quickly move to parts of my source R Markdown code that I identify need editing while reviewing the HTML output. Using line numbers is the fastest way I know to do this, but I welcome hearing others' strategies.
Solutions I've tried:
attr.source = '.numberLines'
will attractively add line numbers to the code parts of the HTML output, that option doesn't provide the source-code line numbers automatically (you must force that manually using .startFrom
) -- instead, the lines are renumbered at the beginning of each chunk and after each piece of output. In the following illustration, I've included .startFrom
to force the line numbering to start at 10, to match the line number for test_data <- rnorm(10)
which is the line number I want to see. A practical solution, however, needs the starting number to be automatic. Also, in the HTML output (shown beneath the code) the hist(test_data)
line is renumbered starting with the same starting number, 10. I would want that to be 12, as in the source code. Considered option: I've considered preprocessing my code by running an initial script that will add line numbers as comments at the end of lines, but I'd prefer a solution that is contained within the main knitr file.
This version uses the line numbers you'll see in the source pane of RStudio. You must use RStudio for this to work. The following changes to the RMD are necessary:
library(jsonlite)
and library(dplyr)
The R chunk and R script need to be placed at the end of your RMD. The JS chunk can be placed anywhere.
Put me at the end of the RMD.
```{r ignoreMe,include=F,echo=F}
# get all lines of RMD into object for numbering; R => JS object
cx <- rstudioapi::getSourceEditorContext()$contents
cxt <- data.frame(rws = cx, id = 1:length(cx)) %>% toJSON()
```
<script id='dat'>`r cxt`</script>
This collects the R object that you made in the R chunk, but its placement does not matter. All of the R code will execute before this regardless of where you place it in your RMD.
```{r gimme,engine="js",results="as-is",echo=F}
setTimeout(function(){
scrCx = document.querySelector("#dat"); // call R created JSON*
cxt = JSON.parse(scrCx.innerHTML);
echoes = document.querySelectorAll('pre > code'); // capture echoes to #
j = 0;
for(i=0; i < echoes.length; i++){ // for each chunk
txt = echoes[i].innerText;
ix = finder(txt, cxt, j); // call finder, which calls looker
stxt = txt.replace(/^/gm, () => `${ix++} `); // for each line
echoes[i].innerText = stxt; // replace with numbered lines
j = ix; // all indices should be bigger than previous numbering
}
}, 300)
function looker(str) { //get the first string in chunk echo
k = 0;
ind = str.indexOf("\n");
sret = str.substring(0, ind);
oind = ind; // start where left off
while(sret === null || sret === "" || sret === " "){
nInd = str.indexOf("\n", oind + 1); // loop if string is blank!
sret = str.substring(oind + 1, nInd);
k++;
ind = oind;
oind = nInd;
}
return {sret, k}; // return string AND how many rows were blank/offset
}
function finder(txt, jstr, j) {
txsp = looker(txt);
xi = jstr.findIndex(function(item, j){ // search JSON match
return item.rws === txsp.sret; // search after last index
})
xx = xi - txsp.k + 1; // minus # of blank lines; add 1 (JS starts at 0)
return xx;
}
```
If you wanted to validate the line numbers, you can use the object
cx
, likecx[102]
should match the 102 in the HTML and the 102 in the source pane.
I've added comments so that you're able to understand the purpose of the code. However, if something's not clear, let me know.
What I think you're looking for is a line number for each line of the echoes, not necessarily anything else. If that's the case, add this to your RMD. If there are any chunks that you don't want to be numbered, add the chunk option include=F
. The code still runs, but it won't show the content in the output. You may want to add that chunk option to this JS chunk.
```{r gimme,engine="js",results="as-is"}
setTimeout(function(){
// number all lines that reflect echoes
echoes = document.querySelectorAll('pre > code');
j = 1;
for(i=0; i < echoes.length; i++){ // for each chunk
txt = echoes[i].innerText.replace(/^/gm, () => `${j++} `); // for each line
echoes[i].innerText = txt; // replace with numbered lines
}
}, 300)
```
It doesn't matter where you put this (at the end, at the beginning). You won't get anything from this chunk if you try to run it inline. You have to knit for it to work.
I assembled some arbitrary code to number with this chunk.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.