
  # -------------------------------------------------------------------------------------------------------------------------  #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #           File Name              :  findFnsInFile.R                                                                        #
  #           Last Updated Funclist  :  19 Feb 2015, 12:52 PM (Thursday)                                                       #
  #                                                                                                                            #
  #           Author Name            :  Rick Saporta                                                                           #
  #           Author Email           :  RickSaporta@gmail.com                                                                  #
  #           Author URL             :  www.github.com/rsaporta                                                                #
  #                                                                                                                            #
  #           Packages Called        :  data.table, stringr                                                                    #
  #           Packages Used via NS   :  formatR                                                                                #
  #                                                                                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  #                                                                                                                            #
  #   splitAtArgs        ( x, width=120, space=NULL, collapse="\n", arg.line2.pad=2, showWarnings=TRUE                         #
  #                        , simplify=!is.null(collapse) )                                                                     #
  #   mkFunctHeader      ( f, collapse="\n", pad=10, verbose=FALSE, drop.blank.author.details=TRUE                             #
  #                        , space.out.sections=TRUE, use.work.email=grepl("/orch", f, ignore.case=TRUE) )                     #
  #   printFunctList     ( fnlist, pre=" # ", copy=FALSE, outToScreen=TRUE, width=120, func.min.char=18                        #
  #                        , func.max.char=35, arg.line2.pad=2, endmarker="<END FUNCS>" )                                      #
  #   insertIntoFolder_  ( folderName, recursive=FALSE, all.files=TRUE                                                         #
  #                        , exts=c("r", "R", "s", "S", "Rprofile"), showWarnings=TRUE, verbose.file=verbose                   #
  #                        , verbose=TRUE )                                                                                    #
  #   insertIntoFile_    ( f, endmarker="<END FUNCS>", skip="## noheader", func.max.char=30, verbose=TRUE )                    #
  #   findFnsInFile      ( f, func.min.char=18, func.max.char=35 )                                                             #
  #   findLastBracket    ( string, start, nchars=100, showWarnings=TRUE )                                                      #
  #   findPkgsInFile     ( f, recursive=TRUE, verbose=FALSE, blanks=character(0) )                                             #
  #                                                                                                                            #
  #                                                                                                                            #
  #                                                     <END FUNCS>                                                            #
  #  -----------------------------------------------------------------------------------------------------------------------   #
  # -------------------------------------------------------------------------------------------------------------------------  #


## 2015-01-23 TODO: 
# in findPkgsInFile() the pat.func  was missing the opening paren for function()
# Check that that same pattern is not in the other functions and similar modification needed


# These functions are used to read my .r files and 
# produce a list of all the functions in that file


#-----------------------------------------------------------------#
  # EXAMPLE OF USAGE
  #----------------#
    # f <- "~/git/misc/rscripts/utilsRS.r"
    # f <- "~/git/misc/rscripts/Confusion Matrix.R"
    # f <- "~/git/misc/rscripts/mini.R"
    # folder <- "~/git/misc/rscripts/utils/"
  
    # findFnsInFile(f)
    # printFunctList(f, pre="")

    ## To auto-modify a file with its function info, use
    # insertIntoFile_(f)
    # insertIntoFolder_(folder)

    ## Uncomment this to run on utils folder
    # folder <- "~/git/misc/rscripts/utils/"
    # insertIntoFolder_(folder)
#-----------------------------------------------------------------#


options(author.name="Rick Saporta")
options(author.email.home="RickSaporta@gmail.com")
options(author.email.work="RSaporta@TheOrchard.com")
options(author.url="www.github.com/rsaporta")


splitAtArgs <- function(x, width=120, space=NULL, collapse="\n", arg.line2.pad=2, showWarnings=TRUE, simplify=!is.null(collapse)) {

  if (!length(x))
    return(x)

  if (length(x) > 1) 
    return(sapply(x, splitAtArgs, space=space, width=width, collapse=collapse, showWarnings=showWarnings, simplify=simplify, USE.NAMES=TRUE))

  # Capture the names, we will put it back later
  nm <- names(x)

  pat.paren.open  <- "\\("
  pat.paren.close <- "\\)"
  pat.arg <- ",\\s*.+?\\="  # The '?' on the '+' makes it non-greedy

  ## The commas will line up with the first parens

  ## ---- DETERMINE PADDING ----- ##
    ## IF SET MANUALLY
    if(!is.null(space)) {
      if (is.numeric(space)) {
        paren1 <- space
        space <- NULL
      } else {
        space  <- as.character(space)
        paren1 <- nchar(space)
      }
    ## IF NOT SET MANUALLY
    } else {
    
      ## Returns the nchar of the first paren OF EACH X
      paren1 <- regexpr(pat.paren.open, x)

      ## If not found
      if (!length(paren1) || paren1 < 1)
        paren1 <- 12
    }

    if (is.null(space))
      space <- pasteR(" ", paren1-1)

  ## ---- END DETERMINE PADDING ----- ##


  ## TODO: ignore commas inside quotes or parens. (the latter more common than the former)
  commas <- gregexpr(pat.arg, x)[[1]]

  ## 
  parens.open  <- gregexpr(pat.paren.open, x)[[1]]   
  parens.close <- gregexpr(pat.paren.close, x)[[1]]
  # ignore the first and last, which belong to 
  parens.close <- parens.close[-length(parens.close)]
  parens.open <- parens.open[-1L]

  if (length(parens.open) != length(parens.close)) {
    if (showWarnings)
      warning(sprintf("unequal parens-pairs in  %s()\nFormatting may be off", substr(x, 1, paren1-1)))
  } else { 
    commas <- setdiff(commas, unlist(mapply(seq, parens.open, parens.close, USE.NAMES=FALSE)))
  }

  commaStops <- c(commas-1, nchar(x))[-1L]

  ## Initialize for iterations
  line2.width <- sum(paren1, arg.line2.pad)
  space <- paste0(space, pasteR(" ", arg.line2.pad))
  i <- 0
  split <- c()

  while (max(split, 0) < nchar(x)) {

    i <- i + 1
    split[[i]] <- suppressWarnings(min(commas[ commaStops >= sum(width-ifelse(i>1, line2.width, 0), split[[i-1]] ) ]) )

    ## for any iteration after the 2nd, if the new split is the same as the last one, just move on to the next one. 
    ##   ... this will happen when an argument's default value is too long
    if (i > 1   &&  split[[i]] == split[[i-1]])
      split[[i]] <- suppressWarnings(min(commas[commas>split[[i]]]))

    ## Infinite-loop safety check 
    if (i > 300) {
      if (showWarnings)
        warning(sprintf("Iteration exceeded for %s()\nFormatting may be off", substr(x, 1, paren1-1)))
      return(x)  ## Only returning if error
    }
  }

  # Remove infinite vals
  split <- split[is.finite(split)]

  start <- c(1, split)
  stop  <- c(split-1, nchar(x))

  splat <- mapply(substr, start=start, stop=stop, x=x)
  splat[-1L] <- paste0(space, splat[-1L])

  ## TODO:  Figure out a
  splat[(nchar(splat) > width)]

  ret <- paste0(splat, collapse=collapse)

  ## We use the names of the vectors in printFunctList() to preserve correct order
  # put names back
  if (!is.null(nm)) {
    sf <- sprintf("%%s.%%0.%ii", 1+nchar(length(ret)))
    names(ret) <- sprintf(sf, nm, seq_along(ret))
  }

  return(ret)
}

mkFunctHeader <- function(f, collapse="\n", pad=10, verbose=FALSE, drop.blank.author.details=TRUE, space.out.sections=TRUE, use.work.email=grepl("/orch", f, ignore.case=TRUE)) {
  fname <- basename(f)
  pkgs <- findPkgsInFile(f, recursive=FALSE, verbose=verbose, blanks=NA_character_)

  opt.email <- ifelse(use.work.email, "author.email.work", "author.email.home")

  info <- list( 'File Name' = fname
               , 'Last Updated Funclist' = format(Sys.time(), format="%d %b %Y, %l:%M %p (%A)")
               , 'Author Name'  = getOption("author.name", default="")
               , 'Author Email' = getOption(opt.email, default="")
               , 'Author URL'   =  getOption("author.url", default="")
               , 'Packages Called' = paste(pkgs$attach, collapse=", ")
               , 'Packages Used via NS' = paste(pkgs$unattach, collapse=", ")
              )

  if (drop.blank.author.details) {
    ## only keep those where NOT value is blank and name contains "Author"
    keep <- !(info == "" & grepl("Author ", names(info)) & names(info) != "Author Name")
    info <- info[keep]
  }

  padding <- sprintf(sprintf("%%%is", pad), "")
  if (!length(padding))
    padding <- ""

  info.pad <- sprintf("%%s%%-%is  :  %%s", max(nchar(names(info))))

  ## Combine into neat rows
  ret <- sprintf(info.pad, padding, names(info), info)

  ## Optionally, add spacing by inserting blank lines in appropriate places
  if (space.out.sections) {
    space.before <- c('Author Name', 'Packages Called')
    ordering <- seq(ret)
    blank.lines <- setNames(nm=ordering[names(info) %in% space.before] - .1)
    blank.lines[] <- ""
    ret <- c(ret, blank.lines)[order(c(ordering, names(blank.lines)))]
  }

  ## Collapse and return
  paste(ret, collapse=collapse)
}



printFunctList <- function(fnlist, pre="  # ", copy=FALSE, outToScreen=TRUE, width=120, func.min.char=18, func.max.char=35, arg.line2.pad=2, endmarker="<END FUNCS>") {
## fnlist can be the text of a file or a filename itself


  # defaults to utils file
  if (missing(fnlist)) 
    fnlist <- path.expand("~/git/misc/rscripts/utilsRS.r")

  # read in the functions. Also set the header
  if (inherits(fnlist, "file") || (length(fnlist)==1 && grepl("\\.[rR]$", fnlist))) {
    header <- mkFunctHeader(fnlist, collapse=NULL)
    fnlist <- findFnsInFile(fnlist, func.min.char=func.min.char)  
  } else {
    fnlist <- pasteC(fnlist, C=" ")
    header <- NULL
  }

  browser(expr=inDebugMode("printFunctList"), text="in printFunctList() near top")

  ## calculate post-side
  (post.spaces <- max(0, attr(regexpr("\\s+$", pre), "match.length")))
  post <- gsub("^\\s*|\\s*$", "", pre)
  # reverse it
  post <- paste(rev(strsplit(post, "")[[1]]), collapse="")
  post <- paste0(pasteR(" ", post.spaces), post)
  len.pre_post <- sum(nchar(c(pre, post)))
  
  ## Chop up any function that is too long
  data.table::setattr(fnlist, "names", seq_along(fnlist))
  large <- nchar(fnlist) > width
  if (any(large)) {
    toinsert <-  splitAtArgs(fnlist[large], collapse=NULL, width=(width-len.pre_post-1)) # the (-1) just to be safe
    tmp      <- c(fnlist[!large], unlist(toinsert, use.names=TRUE))
    fnlist   <- unname(tmp[order(names(tmp))])
  }
  
  ## Add info at the top
  width.using <- (max(1+nchar(fnlist), width)) + 1
  hr     <- paste0(" ", pasteR("-", width.using-2), " ")
  hr.top <- pasteR("-", width.using)

  ## Format rows
  endmarker <- paste0(pasteR(" ", (width.using - nchar(endmarker) - 5)/2), endmarker)
  fnlist <- c(hr.top, hr, "", header, "", hr, "", paste(" ", fnlist), "", "", endmarker, hr, hr.top)

  ## pad with spaces
  spaces <- width.using - (nchar(fnlist) - 1)
  fnlist <- paste0( fnlist, pasteR(" ", spaces))
  
  # output
  out <- paste0(pre, fnlist, post, collapse="\n")

  # copy to clipboard
  if (copy & exists("clipCopy"))
    clipCopy(out)

  # print to screen
  if (outToScreen) {
    cls()
    cat("\n", out, "\n", sep="")
  } 

  return(invisible(out))
}

# ------------------------------------------------------------------------------------ #

insertIntoFolder_ <- function(folderName, recursive=FALSE, all.files=TRUE, exts=c("r", "R", "s", "S", "Rprofile"), showWarnings=TRUE, verbose.file=verbose, verbose=TRUE) {

  # stop("You need to put in a catch in case there is an error, that the existing info is not erased")

  files <- getRFilesFromFolder(folderName, recursive=recursive, all.files=all.files, exts=exts, fail.on.folder.missing=TRUE)

  if (!length(files) && showWarnings) 
    warning("No R files in ", folderName)

  for (f in files) {
    if (verbose)
      cat("Modifying: ", f, "\n")

    ## take a backup in case of error, we will write that back to the file
    raw.bak <- readLines(f)
    caught <- try(insertIntoFile_(f, verbose=verbose.file), silent=FALSE)
    if (isErr(caught)) {
      warning ("insertIntoFile_() failed for '", f, "'")
      write(raw.bak, file=f, append=FALSE, sep="\n")
    }
  }
  return(invisible(TRUE))
}

# ------- #

insertIntoFile_ <- function(f, endmarker="<END FUNCS>", skip="## noheader", func.max.char=30, verbose=TRUE) {
  # stop("You need to put in a catch in case there is an error, that the existing info is not erased")
  
  # if (grepl("paste functions\\.r$", f))
  #   stop (pasteR("!", 100), "\nDo not call insertIntoFile_() on  'paste functions.r' -- something goes very wrong in the ")
  
  raw <- readLines(f)

  ## if the skip char is present, then do not process this file
  skip <- gsub("# ", "#\\\\s+", skip)
  skip <- paste0("^\\s*", skip, "\\s*$")
  if (any(grepl(skip, raw))) {
    verboseMsg(verbose, "Skipping  '", f, "'", sep="", time=FALSE) 
    return(invisible(FALSE))
  }


  pat.hr <- "^\\s*#\\s*(-)+\\s*#\\s*$"
  hrl <- grep(pat.hr, raw)
  endmarker <- paste0("  ", endmarker, "  ") ## to distinguish it from it being an argument, namely in this file.
  endl <- grep(endmarker, raw)

  ## if not found, endl should be set to 1. Otherwise, make sure to jsut take the first element, then add 2
  if (!length(endl)) {
    endl <- 1
  } else 
    endl <- endl[[1]] + 2 ## upto 2 hr lines after the marker

  ## There should be exactly three HRs and the final one should be the same as endl
  if (any((endl+(0:1)) %in% hrl) && (sum(hrl <= endl) %in% c(3, 5)) && all(hrl[1:3] <= endl)) {
    raw <- raw[-seq(hrl[[1]], endl)]
    insert.in.middle <- TRUE
  } else {
    insert.in.middle <- FALSE
  }

  browser(expr=inDebugMode(c("insertIntoFile_")), text="in insertIntoFile_() right before calling printFunctList(f)")

  funcs <- printFunctList(f, copy=FALSE, outToScreen=FALSE, func.max.char=func.max.char)
  funcs <- capture.output(cat(funcs))

  # OLD:  if (length(hrl) &&  hrl[[1]] > 1) ## ... this was picking up random '# ---- #' when no previous header info was present
  if (insert.in.middle) {
    h1 <- hrl[[1]]
    top <- raw[1:(h1-1)]
    bottom <- raw[h1:length(raw)]

    if (!isBlankLine(tail(top, 1)))
      top <- c(top, "")
    if (!isBlankLine(head(bottom, 1)))
      bottom <- c("", bottom)

    raw <- c(top, funcs, bottom)
  } else {
    if (!isBlankLine(raw[[1]]))
      raw <- c("", raw)
    raw <- c(funcs, raw)
  }

  write(raw, file=f, append=FALSE, sep="\n")
}

# ------------------------------------------------------------ #

# findFnsInFile(f)
findFnsInFile <- function(f, func.min.char=18, func.max.char=35) {
# find any named (non-annonymous) functions in a file

  require(stringr)  # for findLastBracket
  require(data.table)

  ## TRY USING formatR
  if (require(formatR)) {
    tlines <- try({
                    formatted <- formatR::tidy_source(f, comment=FALSE, blank=FALSE, arrow=FALSE, brace.newline=FALSE, output=FALSE, indent=2, width.cutoff=500)[["text.tidy"]]
                    # fix the equal signs spacing
                    formatted <- gsub(" = ", "=", formatted)
                    ## formatR breaks lines adds some line breaks. Doing a gsub might replace some strings, so instead, capture the cat() of it. 
                    capture.output(cat(formatted, sep="\n"))
                  }, silent=TRUE)
    if (isErr(tlines)) {
      message(sprintf("The following error was caught when calling\n  formatR::tidy_source(f='%s')\n%s\n%s\n%2$s", f, pasteR("-", 65), gettext(tlines)))
    }
  }

  ## If no formatR, or if it failed, import by manually reading the raw lines
  if (!require(formatR) || inherits(tlines, "try-error")) {
    # If f is not a connection (ie a string of file path), open file connection
    if (! inherits(f, "file"))
      f <- file(as.path(f), open="rt")

    # read in individual lines, collapse into a single string
    tlines <- readLines(f)
    close(f)

    # remove all comments
    tlines <- gsub("(^|\\s+)#.*", "", tlines)
  }

  browser(expr=inDebugMode(c("findFnsInFile", "parse")), text="In findFnsInFile() in the utils file findFnsInFile.r\n Right before collapsing tlines (you might want to take a backup)")

  # remove blank lines, 
  #  .. but add ONE blank line at the end. (This is so that any final `}` gets counted. But could be better programmed.)
  tlines <-  c(tlines[tlines != ""], "")
  # collapse into a single string
  ## 2015-02-07 changing collapse from " " to "\n"
  # tlines.bak <- paste0(tlines, collapse=" ") 
  tlines <- pasteC(tlines, C="\n")

    # SOME EXAMPLE OF WHAT I NEED TO FIND
    # --------------------------------- #
    # testString <- c("<- function(",
    # "<- function (",
    # "<- function 
    # (",
    # "NOT < -function(")

  # THESE ARE THE REGEX PATTERNS TO FIND FUNCTIONS
  #               first var name      (s  <- s second var name)*  s  <- s
  vname <- "\\b[\\.A-Za-z][\\.A-Za-z0-9_$]*(\\s*<-\\s*[\\.A-Za-z0-9_$]*)*\\s*<-\\s*"
  funct <- "function\\s*\\(" 
  # TODO find `excep %=% tions`  ie  quoted special characters, possibly: "^'{1}[pattern1]'$" 
  # TODO add in  equal sign


      # using regexc instead of stringr
      # inds <- sapply(regexec(paste0(vname, funct), tlines), function(x) c(st=x[[1]], en=abs(x[[1]])-1+attr(x,"match.length")[[1]])); 

  # grab the nchar location of the start of each function name(s) 
  inds <- data.table(str_locate_all(pattern=paste0(vname, funct), string=tlines)[[1]], key="start")
  setnames(inds, "end", "parenStart")

  if (!nrow(inds)) {
    warning("file '", path.unexpand(f), "' has no functions", call.=FALSE)
    return(character())
  }

  # find the closing bracket for each
  inds[, end:=findLastBracket(tlines, parenStart), by=start]
  #  TODO:  instead of calling 'findLastBracket' 20+times, 
  #         call it once, create a list of all closing brackets
  #         then for each "start" grab the min(which(closingBracket > start))

  allFuncs <- str_sub(tlines, inds$start, inds$end)

  ## Clean all double-spaces to single space. 
  ## TODO:  Avoid removing double spaces from strings
  allFuncs <- gsub(" ( )+", " ", allFuncs)

  ## remove the "<- function" portion
  pat.func_assign <- "\\s*(<-|=)\\s*function\\s*\\(\\s*"

  # add a space before last bracket then put the paren back
  splat <- strsplit(allFuncs, pat.func_assign)

  ## Set sprintf fmt based on nchar of each function
  nc <- min(max(mnchar(sapply(splat, "[[", 1)), func.min.char), func.max.char)
  fmt <- paste0("%-", nc, "s ( %s")

  # piece back together with proper spacing
  Names_N_Args <- sapply(splat, function(x) {
                    sprintf(fmt, x[[1]], x[[2]])
                  })

  ## put a space before the last paren
  Names_N_Args <- gsub("\\)$", " )", Names_N_Args)

  return(Names_N_Args)
}

# ------------------------------------------------------------ #
findLastBracket <- function(string, start, nchars=100, showWarnings=TRUE) {
  # not using argument nchars for now
  # this function depends on library(stringr), which is called in the previous env.
  # library(stringr)

  if (length(string) != 1)
    stop ("object 'string' should be a character of length exactl one. Try using pasteC() ")

  # ditch everything prior to start, then count brackets
  string        <- str_sub(string, start, -1);

  openBrackets  <- str_locate_all(string, "\\(")[[1]][,"start"]
  closeBrackets <- str_locate_all(string, "\\)")[[1]][,"start"]
  #

  #####  THESE ARE THE BRACKETS TO IGNORE   #####
  ## A note on "\\\\\\("
  ##     "\\ \\ \\("    <~~  The six brackets
  ##     "\  \  \ ("    <~~  This is what R sends to the Regex
  ##     "   \\  \("    <~~  The three brackets regex receives
  ##     "   \(    "    <~~  What regex is searching for
  ##
  ######       REMIINDER TO SELF
  ## Why do i neeed to find standalone ")" ? 
  ## I do not. Rather, there are times where I have standalone open braces, eg  pasteQ(X, w="(")
  ## I need to exclude those. 
  ## However, there are times, where I also have matching closing braces, eg   paste("(", x, ")")
  ## In which case, if I remove just the starting brace, I am left with the closing brace by itself.

  # toIgnore <- c('[\\"\\\']\\("', "\\\\\\(", '["\\\']\\)["\\\']', "\\\\\\)", '["\\\']\\s* \\(', '\\)\\s*["\\\']')
  toIgnore <- c('"\\("', "\\\\\\(", '"\\)"', "\\\\\\)", '" \\(', '"\\s* \\(', "`\\(`")
  ## Specific lines from file
  toIgnore2 <- c("file at a time. \\(ie, ", "must have length 1\\)", '"\\) objects were NOT saved:', add_1='paste."message\\(\\\\"'
               , 'save is empty \\(meaning', '":  \\("', add_1='as) \\\\n"', 'exceeds max for .BY=list\\("', ',3})",'
                , '"function \\("', "'as.Date\\(',", '"as.Date\\(",'
                , "s   \\(", "s   \\(Lines: ", add_1="in \\(0, 1000\\]"
                # , 'sample size \\("',  '"\\) exceeds'
                , '\\(", pat', '%s ago\\)', 'a superset\\)"', 'DT \\(eg in'
                , ' dayid in \\("', 'if present. \\('
                # , "(" = 
                
                # This is from makeSQLtable.r
                , add_1="\"\\('\\|"
                  )
  toIgnore <- c(toIgnore, toIgnore2)

  ## TODO:  Line 343 in jesus.r
  ##    file.counts <- paste0(file.counts, " (out of ", length(fileWithPath), ")")

  ## The results of 'ignoreThese' will be the start of the string in toIgnore.
  ##    however the values in open/closeBrackets will be the start of the actual paren, 
  ##    which can be a few characters in from those values in ignoreThese.

  # Find all matches of 'toIgnore' in the string
  (ignoreThese <- sapply(toIgnore, gregexpr, string))

  ## nchar(.) is thrown off by the extra escapes in certain patterns. 
  ## This is only an issue if the escapes are after the bracket, as in 'as) \\\\n"' so we add 1
  wh.add_1 <- which(names(ignoreThese) == "add_1")
  for (w in wh.add_1) 
    if (any(ignoreThese[w] > 0))
      ignoreThese[[w]] <- ignoreThese[[w]] + 1

  ## Find the number of chars after the paren.
  trailingChars <- nchar(toIgnore) - unlist(gregexpr("\\(|\\)", toIgnore))
  ## x will be the START of the match.  x + ml -1 is the END of the match. 
  ##   We don't want the end, we want the parenthesis, which is 'b' in from the end, 
  ##   where b is the number of chars after x.  
  ##   Note that if x is (-1), ie no match, the output will be NULL and so dropped when unlisted
  ignoreThese  <-  mapply(function(x, b) if (x[[1]] > 0) x + (attr(x, "match.length") - b) -1, ignoreThese, trailingChars)
  ignoreThese  <- unique(unlist(ignoreThese))
  if (!is.null(ignoreThese))
      ignoreThese  <- sort(ignoreThese)

  browser(expr=inDebugMode(c("findLastBracket")), text="In findLastBracket() in file findFnsInFile.r\n Right before taking setdiff of openBrackets & closeBrackets.")
  if (FALSE) ## for debugging
  ##  DEBUGGING TIPS
  ## Look for which indecies will be dropped.
  ## If a specific is pattern found, add it to toIgnore2, above
  #    findFnsInFile("~/Desktop/temp_file.r")
  {
    c
    print(length(openBrackets))
    print(length(closeBrackets))
    cat("These will be dropped from openBrackets:  ", if (length(intersect(ignoreThese, openBrackets)))  pasteC(intersect(ignoreThese, openBrackets), C=", ")  else "[none]", fill=TRUE)
    cat("These will be dropped from closeBrackets: ", if (length(intersect(ignoreThese, closeBrackets))) pasteC(intersect(ignoreThese, closeBrackets), C=", ") else "[none]", fill=TRUE)
    cat("\n")

    extra <- 12
    for (ind in seq(openBrackets)) {
      # cat(ind, ": [", openBrackets[ind], "-", closeBrackets[ind], "]:", substr(string, openBrackets[ind] - extra, removeNA(closeBrackets[ind] + extra, repl=nchar(string))), "\n")
      ob <- openBrackets[ind]
      cb <- minn(closeBrackets[ind], nchar(string))
      frmt <- paste0("%4i : [%5i-%5i]: %s\n%22s|%", extra, "s%", cb - ob, "s\n")
      cat(sprintf(frmt, ind, ob, cb, gsub("\n", " ", substr(string, ob-extra, minn(cb+extra, nchar(string)))), "", "^", "^" ), fill=FALSE)
    }
  }


  ## CHECKING THAT WE GOT THEM ALL
  match.check <- c(intersect(openBrackets,  ignoreThese), intersect(closeBrackets,  ignoreThese))
  if (length(c(match.check, ignoreThese)) &&  !identical(sort(unique(match.check)), sort(unique(ignoreThese))))
    warning("Some 'ignoreThese' brackets did not match\n  with 'openBrackets' or 'closeBrackets', which is strange.\n\n  HINT: Investigate by setting    debugOn('findLastBracket') \n")

  ## Remove Them
  openBrackets  <- setdiff(openBrackets, ignoreThese)
  closeBrackets <- setdiff(closeBrackets, ignoreThese)

  # whenever CL[n] > OP[n+1], then n is the index of a closing paren 
  #  We simply want to find the lowest such n. 

  # things to watch out for:  uneven sizes in OP & CL
  len.OP <- length(openBrackets)
  len.CL <- length(closeBrackets)

  if (len.OP < len.CL) 
    warning("Too MANY closing parens following '", substr(string, 1, 13), "'.")

  # We can deal with too many closing parens, but not with too few. 
  if (len.OP - len.CL > 2)
    stop("There are too many missing closing parens. Cannot continue.\n\n  HINT: This can be due to the 'toIgnore2' argument in findLastBracket() which has specific\n        patterns previously encountered in other files.\n        It is possible that an opening pattern was matched but not its closing counterpart")

  if (len.OP - len.CL == 1) {
    warning("A closing paren is missing following '", substr(string, 1, 13), "'.")
    closeBrackets <- c(closeBrackets, nchar(string))
  }

    # [58] "allPosCombsList ( dat, choose=seq(ncol(dat)), yName=\"y\" )"
    # [59] "allPosCombsMatrix.TakesTooLong ( dat, choose=-1 )" 
    # Here is an example of two lines from a file. Notice that the location of 
    #   of the last paren in [58] is greater than all the open parens in [58]
    #   but it is less than the next opening paren, ie in line [59] (but not
    #   neccessary for them to be on separate lines. This all will be collapsed
    #   into one string, anyway)
    # Hence, by comparing open[i] to close[i-1], the latter will be less than 
    #   the former only when the latter is the last paren in a series.  
    diffs <- closeBrackets - c(openBrackets[-1], nchar(string)+1)
    closeParen <- min(which(diffs < 0))
 
 # OLD HAD THIS EXPRESSION WITH nchar(...)... 
 #  I dont think this is necessary.  (I think i only had it in for when I Was trying to read just a few lines at a time.)
 #  closeParen <- min(which(closeBrackets - c(openBrackets[-1], nchar(string)+1) < 0))

  if (identical(closeParen, integer(0))) {
    warning("Could not match up the parens.")
    return(NULL)
  }

  return(closeBrackets[closeParen]+start-1)
  # if closeParen is empty, then either (a) closing bracket doesnt exist [ie impromper input]
  # (b) we need to read more lines
  # (c) max(CL) is the last 

}

# ------------------------------------------------------------------------ #

# findPkgsInFile(f)
findPkgsInFile <- function(f, recursive=TRUE, verbose=FALSE, blanks=character(0)) {
# finds packages used in file, by searching for
#   require(pkg) &  library(pkg)

  if (isTRUE(file.info(f)[["isdir"]])) {
        if (verbose)
          cat("'", f, "' IS A DIRECTORY. FILES ARE\n  ", sep="")
        f <- getRFilesFromFolder(f, recursive=recursive)
        if (verbose)
          cat(paste(f, collapse="\n  "), "\n", sep="")
  } else if (verbose)
        cat("Processing single file '", f, "'\n", sep="")


  ## Check if a single file
  if (!length(f))
    return(c())
  if (length(f) > 1L)
    return(sort(unique(unlist(lapply(f, findPkgsInFile)), use.names=FALSE)))

  ## --- BELOW HERE, f is a single file ---- ### 

  ## Not used, I dont think
  # f.nm <- capture.output(f)

  require(stringr)  # for findLastBracket
  require(data.table)


  # If f is not a connection (ie a string of file path), open file connection
  if (! inherits(f, "file"))
    f <- file(as.path(f), open="rt")

  # read in individual lines, collapse into a single string
  tlines <- readLines(f)
  close(f)

  # remove all comments
  tlines <- gsub("(^|\\s+)#.*", "", tlines)

  # remove blank lines, 
  #  .. but add ONE blank line at the end. (This is so that any final `}` gets counted.)
  tlines <-  c(tlines[tlines != ""], "")
  # collapse into a single string
  # tlines <- pasteC(tlines, C="\n")  ## changed C=" " to C="\n"
  tlines <- pasteC(tlines, C=" ")


    # SOME EXAMPLE OF WHAT I NEED TO FIND
    # --------------------------------- #
    # require
    # library
    # lib
    # pkgName::functino()
    # TODO:  capture when using an object containing multiple package names

  # THESE ARE THE REGEX PATTERNS TO FIND FUNCTIONS
  pat.outter <- "(\\s|^|;|\\{|\\}|,)(library|lib|require)\\(\\s*[\\\"']?%s[\\\"']?\\s*\\)"
  pat.pkg    <- "(([\\.A-za-z0-9_]*)+)"
  pat <- sprintf(pat.outter, pat.pkg)

  (mat <- gregexpr(pat, tlines))
  ret <- regmatches(tlines, mat) [[1L]]
  ret <- gsub(pat, "\\3", ret)

#  (matches <- gregexpr(pat, tlines)[[1L]])
#  # `matches` will be negative if no match
#  ret <- 
#    if (matches[[1]] > 0L) {
#      mapply(function(from, to) gsub(pat, "\\2", substr(tlines, from, to), perl=TRUE), 
#              matches, matches + attr(matches, "match.length")-1)
#    } else {
#      c()
#    }

  # ---- #
  ## Search for packages called by namespace
  ##  eg:  Hmisc::cut(.)
  pat.func   <- "(([\\.A-za-z0-9_'`\"]*)+) *\\(";  "\\)"  # <~~~ closing bracket or file parser
  pat.nmsp <- paste0(pat.pkg, "::", pat.func)
  mat.nmsp <- gregexpr(pat.nmsp, tlines)
  extr.nmsp <- regmatches(tlines, mat.nmsp) [[1]]
  ## now split on "::" and take the first of each split
  ret.nmsp <- sapply(strsplit(extr.nmsp, "::"), "[[", 1)
  ## REMOVE those which are being attached
  ret.nmsp <- setdiff(ret.nmsp, ret)
  ## TODO: avoid strings, for now, remove blanks
  ret.nmsp <- ret.nmsp[ret.nmsp != ""]

  if (verbose)
    cat("\n\n")

  ## Sort and uniquify, but only if non-empty [sorting an empty vector throws error]
  ret      <- if (length(ret))      sort(unique(ret))      else  blanks
  ret.nmsp <- if (length(ret.nmsp)) sort(unique(ret.nmsp)) else  blanks
  
  ret.list <- list(attach=ret, unattach=ret.nmsp)

  ## Commented lines earch for incorrect package names that had remained in the files
  # lookFor <- c("Rcurl", "microbmenchmark", "stat")
  # selfname_(lookFor)
  # if (any(wh <- sapply(lookFor, function(w) any(grepl(paste0(w, "($|\\)|\\b)"), unlist(ret.list), ignore.case=FALSE)))))
  #   message("'", pasteC(lookFor[wh], C=","), "found in file  '", f.nm, "")

  return(ret.list)
}




