R/patchKnitrSynctex.R

#' @export
#' @title Patch synctex file for knitr
#' @description Fixes up a synctex file so that forward and backward search works
#' @param texfile path to base tex file
#' @return None - just some messages. 
#' @details Written by Jan Gleixner and put in this package by Kendon Bell with some minor adjustments.
#' Jan's notes are: it does use the "blubb-concordance.tex" 
#' file generated by knitr to patch the blubb.synctex[.gz] file generated by latex in order to
#' fix forward and reverse seach between blubb.pdf and blubb.Rnw
#' tested only under windows, with one single utf8 Rnw file.
#' Author: Jan Gleixner <jan.gleixner at gmail>
###############################################################################
patchKnitrSynctex <- function (texfile){
  require(tools)
  path <- dirname(texfile)
  fileCon <- paste0(tools::file_path_sans_ext(texfile), "-concordance.tex")
  if (!file.exists(fileCon)) 
    stop(paste(fileCon,"does not exist! Did you set 'opts_knit$set(concordance = TRUE);'?"))
  text<-readChar(fileCon, file.info(fileCon)$size);
  require(stringr)
  re <- "\\\\Sconcordance\\{concordance:([^:]*):([^\\%]*):\\%\\r?\\n(\\d+)(( \\d+ \\d+)*)\\}";
  parsed <- str_match_all(text,re);
  for(i in seq(1,nrow(parsed[[1]]))){  	
    texF=parsed[[1]][i,2];
    rnwF=parsed[[1]][i,3];
    startLine=as.integer(parsed[[1]][i,4]);
    rleValues <- read.table(textConnection(parsed[[1]][i,5]));
    rleO = rle(0);
    rleO$values=as.numeric(rleValues[seq(2,length(rleValues),2)]);
    rleO$lengths=as.integer(rleValues[seq(1,length(rleValues),2)]);
    diffs=inverse.rle(rleO);
    mapping_=c(startLine,startLine+cumsum(diffs[-1]));
    
    basename <- tools::file_path_sans_ext(rnwF);		
    syncF = file.path(path, paste0(basename,".synctex"))
    
    compressed <- FALSE
    if (file.exists(syncF)) {
      sf=file(syncF);
    } else{
      syncF <- paste(syncF, ".gz", sep = "")
      if (file.exists(syncF)) {
        compressed <- TRUE
        sf <- gzfile(syncF);
      }
    }
    lines <- try(readLines(syncF, warn = FALSE), silent = TRUE)
    if (inherits(lines, "try-error")) 
      stop(paste(f, "cannot be read, no patching done."))
    close(sf)
    postemble =grep("^Postamble:",lines,perl=T)
    re=paste0("^Input:([^:]+):(.*", basename(texF), ")");		
    toRepl=grep(re,lines[seq(1,postemble)],perl=T);
    inputs=str_match(lines[toRepl],re);
    if (length(inputs)>0){
      tag=inputs[,2];
      inputs[,3]=paste0(tools::file_path_sans_ext(inputs[,3]), ".Rnw");
      lines[toRepl]=paste0("Input:",inputs[,2],":",inputs[,3]);
      re=paste0("^([xkgvh\\$\\(\\[]", tag ,"\\,)(\\d+)([\\,:].*)");
      needReplacment=grep(re,lines[seq(1,postemble)],perl=T);
      toRepl=str_match(lines[needReplacment],re);
      toRepl[,3]=as.character(mapping_[as.integer(toRepl[,3])]);
      newlines=paste0(toRepl[,2],toRepl[,3],toRepl[,4]);
      lines[needReplacment]=newlines;
      if (!compressed) {
        sf=file(syncF,"wb");
      } else{
        sf <- gzfile(syncF,"wb");
      }
      writeLines(lines, sf, sep = "\n")
      close(sf);
      message(paste(length(needReplacment), "patches made to",syncF));
    }else{
      message(paste("No patches made to",syncF));
    }
  }
}
kendonB/bellmisc documentation built on May 20, 2019, 9:03 a.m.