sourcecamembert::FileUtil.fan

//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   24 Apr 12  Brian Frank  Creation
//

using concurrent

**
** FileUtil
**
const class FileUtil
{
  static Str pathDis(File file)
  {
    names := file.path.dup
    if (names.first.endsWith(":")) names.removeAt(0)
    return "/" + names.join("/")
  }

  static Bool contains(File dir, File? x)
  {
    if (x == null) return false
    return x.normalize.uri.toStr.startsWith(dir.normalize.uri.toStr)
  }

  static Uri pathIn(File dir, File x)
  {
    if(dir.uri.toStr.size >= x.uri.toStr.size)
      return ``
    return x.uri.toStr[dir.uri.toStr.size..-1].toUri
  }

  ** Replace found items with new text
  static Void replaceAll(Item[] items, Str oldText, Str newText, Str delimiter)
  {
    // file currently worked on
    File? curFile
    //  text lines of file currently worked on
    Str[]? lines
    // Index of current line we are working on
    Int curLine
    // current offset in line (because item spans are "off" after mmultiple replaces in same line)
    offset := 0
    step := newText.size - oldText.size

    items.each |a|
    {
      if(! (a is FileItem)) return
      item := a as FileItem
      if(item.file != curFile)
      {
        if(curFile != null)
          saveLines(curFile, lines, delimiter)
        curFile = item.file
        try
          lines = curFile.readAllLines
        catch(Err e) {e.trace; lines=null}
        offset = 0
      }
      if(item.loc.span.start.line != item.loc.span.end.line)
        throw Err("Replace only supported within a single line.")
      line := item.loc.span.start.line
      if(line != curLine)
      {
        offset = 0
        curLine = line
      }
      if(lines != null)
      {
        l := lines[line]
        start :=  item.loc.span.start.col + offset
        end := item.loc.span.end.col + offset
        lines[line] = l[0 ..< start] + newText + l[end .. -1]
        offset += step
      }
    }
    // last file
    if(curFile!=null)
      saveLines(curFile, lines, delimiter)
  }

  static internal Void saveLines(File file, Str[] lines, Str delimiter)
  {
    lastLine := lines.size-1
    out := file.out
    try
    {
      lines.each |Str line, Int i|
      {
        out.print(line)
        if (i != lines.size-1 || line.isEmpty)
          out.print(delimiter)
      }
    }
    catch(Err e)
      e.trace
    finally
      out.close
  }
}