<commit_before>
exports = module.exports = cache
exports.read = read
exports.clean = clean
exports.unpack = unpack

var mkdir = require("./utils/mkdir-p")
  , exec = require("./utils/exec")
  , pipe = exec.pipe
  , spawn = exec.spawn
  , fetch = require("./utils/fetch")
  , npm = require("../npm")
  , fs = require("./utils/graceful-fs")
  , rm = require("./utils/rm-rf")
  , readJson = require("./utils/read-json")
  , registry = require("./utils/registry")
  , log = require("./utils/log")
  , path = require("path")
  , sys = require("sys")

function cache (args, cb) {
  var cmd = args.shift()
  switch (cmd) {
    case "clean": return clean(args, cb)
    case "ls": return ls(args, cb)
    case "add": return add(args, cb)
    default: return cb(new Error(
      "Invalid cache action: "+cmd))
  }
}

// if the pkg and ver are in the cache, then
// just do a readJson and return.
// if they're not, then fetch them from the registry.
var cacheSeen = {}
function read (name, ver, cb) {
  var jsonFile = path.join(npm.cache, name, ver, "package.json")
  function c (er, data) {
    if (!er) cacheSeen[data._id] = data
    return cb(er, data)
  }
  if (name+"-"+ver in cacheSeen) {
    return cb(null, cacheSeen[name+"-"+ver])
  }
  readJson(jsonFile, function (er, data) {
    if (er) return addNameVersion(name, ver, c)
    c(er, data)
  })
}

// npm cache ls [<pkg> [<ver>]]
function ls (args, cb) {
  var show = path.join.apply(path, args)
    , read = npm.cache
    , msg = "cache ls"+(show?" "+show:"")
  read = path.join(read, show)
  mkdir(npm.cache, function (er) {
    if (er) return log.er(cb, "no cache dir")(er)
    fs.readdir(read, function (er, files) {
      if (er) {
        log("nothing found", "cache")
        return cb()
      }
      files = files.filter(function (f) { return f !== "." && f !== ".." })
      if (!files.length) {
        log("nothing found", "cache")
        return cb()
      }
      cb(null, log(files.join("\n"), msg))
    })
  })
}

// npm cache clean [<pkg> [<ver>]]
function clean (args, cb) {
  if (!cb) cb = args, args = []
  if (!args) args = []
  var pkg = args.shift()
    , ver = args.shift()
    , clean = npm.cache
  if (pkg) clean = path.join(clean, pkg)
  if (ver) clean = path.join(clean, ver)
  cb = log.er(cb, "Could not remove "+clean)
  rm(clean, cb)
}

// npm cache add <tarball-url>
// npm cache add <pkg> <ver>
// npm cache add <tarball>
// npm cache add <folder>
exports.add = function (pkg, ver, cb) {
  if (!cb && typeof ver === "function") cb = ver, ver = null
  return add([pkg, ver], cb)
}
function add (args, cb) {
  var pkg = args.shift()
    , ver = args.shift()
  if (!pkg && !ver) {
    var usage = "Usage:\n"
              + "    npm cache add <tarball-url>\n"
              + "    npm cache add <pkg> <ver>\n"
              + "    npm cache add <tarball>\n"
              + "    npm cache add <folder>\n"
    return cb(new Error(usage))
  }
  if (pkg && ver) return addNameVersion(pkg, ver, cb)
  if (pkg.match(/^https?:\/\//)) return addRemoteTarball(pkg, cb)
  else addLocal(pkg, cb)
}
function addRemoteTarball (url, cb) {
  var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random()+".tgz")
  fetch(url, tmp, function (er) {
    if (er) return log.er(cb, "failed to fetch "+url)(er)
    addLocalTarball(tmp, cb)
  })
}
function addNameVersion (name, ver, cb) {
  registry.get(name, ver, function (er, data, json) {
    if (er) return cb(er)
    var folder = path.join(npm.cache, data.name, data.version)
      , tgz = path.join(folder, "package.tgz")
    mkdir(folder, function (er) {
      if (er) return cb(er)
      if (!data.dist || !data.dist.tarball) return cb(new Error(
        "No dist.tarball in package data"))
      fetch(data.dist.tarball, tgz, function (er) {
        if (er) return cb(er)
        addLocalTarball(tgz, cb)
      })
    })
  })
}

function addLocal (p, cb) {
  // figure out if this is a folder or file.
  fs.stat(p, function (er, s) {
    if (er) return log.er(cb, "Doesn't exist: "+p)(er)
    if (s.isDirectory()) addLocalDirectory(p, cb)
    else addLocalTarball(p, cb)
  })
}
function addLocalTarball (p, cb) {
  // if it's a tar, and not in place,
  // then unzip to .tmp, add the tmp folder, and clean up tmp
  var tmp = path.join(npm.tmp, Date.now() + "-" + Math.random())
  if (p.indexOf(npm.cache) !== 0) {
    return unpackTar(p, tmp, function (er) {
      if (er) return log.er(cb, "Failed to unpack "+p)(er)
      addLocalDirectory(tmp, function (er, data) {
        if (er) cb(er)
        rm(tmp, function (er) {
          if (er) return log.er(cb, "Could not remove "+tmp)
          cb(er, data)
        })
      })
    })
  }
  if (path.basename(p) !== "package.tgz") return cb(new Error(
    "Not a valid cache tarball name: "+p))
  addPlacedTarball(p, cb)
}
function addPlacedTarball (p, cb) {
  // now we know it's in place already as .cache/name/ver/package.tgz
  // unpack to .cache/name/ver/package/, read the package.json,
  // and fire cb with the json data.
  var target = path.dirname(p)
    , folder = path.join(target, "package")
    , json = path.join(target, "package.json")
  rm(folder, function (e) {
    if (e) return log.er(cb, "Could not remove "+folder)(er)
    unpackTar(p, folder, function (er) {
      if (er) return cb(er)
      readJson(path.join(folder, "package.json"), function (er, data) {
        if (er) return cb(er)
        fs.writeFile(json, JSON.stringify(data), function (er) {
          if (er) return log.er(cb, "Could not write to "+json)(er)
          cb(null, data)
        })
      })
    })
  })
}
function addLocalDirectory (p, cb) {
  // if it's a folder, then read the package.json,
  // tar it to the proper place, and add the cache tar
  if (p.indexOf(npm.cache) === 0) return cb(new Error(
    "Adding a cache directory to the cache will make the world implode."))
  readJson(path.join(p, "package.json"), function (er, data) {
    if (er) return cb(er)
    var tgz = path.join(npm.cache, data.name, data.version, "package.tgz")
    packTar(tgz, p, function (er) {
      if (er) return cb(er)
      addLocalTarball(tgz, cb)
    })
  })
}

function unpack (pkg, ver, unpackTarget, cb) {
  read(pkg, ver, function (er, data) {
    if (er) return log.er(cb, "Could not read data for "+pkg+"@"+ver)(er)
    unpackTar( path.join(npm.cache, pkg, ver, "package.tgz")
             , unpackTarget
             , cb
             )
  })
}

function unpackTar (tarball, unpackTarget, cb) {
  mkdir(unpackTarget, function (er) {
    if (er) return log.er(cb, "Could not create "+unpackTarget)(er)
    // cp the gzip of the tarball, pipe the stdout into tar's stdin
    // gzip {tarball} --decompress --stdout | tar xf - --strip-components=1 -C {unpackTarget}
    pipe( spawn(npm.config.get("gzip"), ["--decompress", "--stdout", tarball])
        , spawn( npm.config.get("tar")
               , ["vx", "--strip-components=1", "-C", unpackTarget]
               )
        , cb
        )
  })
}
function packTar (targetTarball, folder, cb) {
  log.verbose(folder+" "+targetTarball, "packTar")
  if (folder.charAt(0) !== "/") folder = path.join(process.cwd(), folder)
  if (folder.slice(-1) === "/") folder = folder.slice(0, -1)
  var cwd = process.cwd()
    , parent = path.dirname(folder)
    , addFolder = path.basename(folder)
    , ignore = path.join(folder, ".npmignore")
    , defaultIgnore = path.join(__dirname, "utils", "default.npmignore")
    , include = path.join(folder, ".npminclude")

  fs.stat(ignore, function (er) {
    if (er) ignore = defaultIgnore
    fs.stat(include, function (er) {
      if (er) include = false
      mkdir(path.dirname(targetTarball), function (er) {
        if (er) return log.er(cb, "Could not create "+targetTarball)(er)
        process.chdir(parent)
        // tar xf - --strip-components=1 -C {unpackTarget} | gzip {tarball} > targetTarball
        var target = fs.createWriteStream(targetTarball)
          , unPacked = false
          , args = [ "cv", "--exclude", ".git", "-X", ignore]
        if (include) args.push("-T", include)
        args.push(addFolder)
        var tar = spawn(npm.config.get("tar"), args)
          , gzip = spawn(npm.config.get("gzip"), ["--stdout"])
          , errState
        pipe(tar, gzip, function (er) {
          if (errState) return
          if (er) return cb(errState = er)
        })
        sys.pump(gzip.stdout, target, function (er, ok) {
          if (errState) return
          if (er) return cb(errState = er)
          process.chdir(cwd)
          cb()
        })
      })
    })
  })
}
<commit_msg>Log an error about configs if tar or gzip don't work.<commit_after>
exports = module.exports = cache
exports.read = read
exports.clean = clean
exports.unpack = unpack

var mkdir = require("./utils/mkdir-p")
  , exec = require("./utils/exec")
  , pipe = exec.pipe
  , spawn = exec.spawn
  , fetch = require("./utils/fetch")
  , npm = require("../npm")
  , fs = require("./utils/graceful-fs")
  , rm = require("./utils/rm-rf")
  , readJson = require("./utils/read-json")
  , registry = require("./utils/registry")
  , log = require("./utils/log")
  , path = require("path")
  , sys = require("sys")

function cache (args, cb) {
  var cmd = args.shift()
  switch (cmd) {
    case "clean": return clean(args, cb)
    case "ls": return ls(args, cb)
    case "add": return add(args, cb)
    default: return cb(new Error(
      "Invalid cache action: "+cmd))
  }
}

// if the pkg and ver are in the cache, then
// just do a readJson and return.
// if they're not, then fetch them from the registry.
var cacheSeen = {}
function read (name, ver, cb) {
  var jsonFile = path.join(npm.cache, name, ver, "package.json")
  function c (er, data) {
    if (!er) cacheSeen[data._id] = data
    return cb(er, data)
  }
  if (name+"-"+ver in cacheSeen) {
    return cb(null, cacheSeen[name+"-"+ver])
  }
  readJson(jsonFile, function (er, data) {
    if (er) return addNameVersion(name, ver, c)
    c(er, data)
  })
}

// npm cache ls [<pkg> [<ver>]]
function ls (args, cb) {
  var show = path.join.apply(path, args)
    , read = npm.cache
    , msg = "cache ls"+(show?" "+show:"")
  read = path.join(read, show)
  mkdir(npm.cache, function (er) {
    if (er) return log.er(cb, "no cache dir")(er)
    fs.readdir(read, function (er, files) {
      if (er) {
        log("nothing found", "cache")
        return cb()
      }
      files = files.filter(function (f) { return f !== "." && f !== ".." })
      if (!files.length) {
        log("nothing found", "cache")
        return cb()
      }
      cb(null, log(files.join("\n"), msg))
    })
  })
}

// npm cache clean [<pkg> [<ver>]]
function clean (args, cb) {
  if (!cb) cb = args, args = []
  if (!args) args = []
  var pkg = args.shift()
    , ver = args.shift()
    , clean = npm.cache
  if (pkg) clean = path.join(clean, pkg)
  if (ver) clean = path.join(clean, ver)
  cb = log.er(cb, "Could not remove "+clean)
  rm(clean, cb)
}

// npm cache add <tarball-url>
// npm cache add <pkg> <ver>
// npm cache add <tarball>
// npm cache add <folder>
exports.add = function (pkg, ver, cb) {
  if (!cb && typeof ver === "function") cb = ver, ver = null
  return add([pkg, ver], cb)
}
function add (args, cb) {
  var pkg = args.shift()
    , ver = args.shift()
  if (!pkg && !ver) {
    var usage = "Usage:\n"
              + "    npm cache add <tarball-url>\n"
              + "    npm cache add <pkg> <ver>\n"
              + "    npm cache add <tarball>\n"
              + "    npm cache add <folder>\n"
    return cb(new Error(usage))
  }
  if (pkg && ver) return addNameVersion(pkg, ver, cb)
  if (pkg.match(/^https?:\/\//)) return addRemoteTarball(pkg, cb)
  else addLocal(pkg, cb)
}
function addRemoteTarball (url, cb) {
  var tmp = path.join(npm.tmp, Date.now()+"-"+Math.random()+".tgz")
  fetch(url, tmp, function (er) {
    if (er) return log.er(cb, "failed to fetch "+url)(er)
    addLocalTarball(tmp, cb)
  })
}
function addNameVersion (name, ver, cb) {
  registry.get(name, ver, function (er, data, json) {
    if (er) return cb(er)
    var folder = path.join(npm.cache, data.name, data.version)
      , tgz = path.join(folder, "package.tgz")
    mkdir(folder, function (er) {
      if (er) return cb(er)
      if (!data.dist || !data.dist.tarball) return cb(new Error(
        "No dist.tarball in package data"))
      fetch(data.dist.tarball, tgz, function (er) {
        if (er) return cb(er)
        addLocalTarball(tgz, cb)
      })
    })
  })
}

function addLocal (p, cb) {
  // figure out if this is a folder or file.
  fs.stat(p, function (er, s) {
    if (er) return log.er(cb, "Doesn't exist: "+p)(er)
    if (s.isDirectory()) addLocalDirectory(p, cb)
    else addLocalTarball(p, cb)
  })
}
function addLocalTarball (p, cb) {
  // if it's a tar, and not in place,
  // then unzip to .tmp, add the tmp folder, and clean up tmp
  var tmp = path.join(npm.tmp, Date.now() + "-" + Math.random())
  if (p.indexOf(npm.cache) !== 0) {
    return unpackTar(p, tmp, function (er) {
      if (er) return log.er(cb, "Failed to unpack "+p)(er)
      addLocalDirectory(tmp, function (er, data) {
        if (er) cb(er)
        rm(tmp, function (er) {
          if (er) return log.er(cb, "Could not remove "+tmp)
          cb(er, data)
        })
      })
    })
  }
  if (path.basename(p) !== "package.tgz") return cb(new Error(
    "Not a valid cache tarball name: "+p))
  addPlacedTarball(p, cb)
}
function addPlacedTarball (p, cb) {
  // now we know it's in place already as .cache/name/ver/package.tgz
  // unpack to .cache/name/ver/package/, read the package.json,
  // and fire cb with the json data.
  var target = path.dirname(p)
    , folder = path.join(target, "package")
    , json = path.join(target, "package.json")
  rm(folder, function (e) {
    if (e) return log.er(cb, "Could not remove "+folder)(er)
    unpackTar(p, folder, function (er) {
      if (er) return cb(er)
      readJson(path.join(folder, "package.json"), function (er, data) {
        if (er) return cb(er)
        fs.writeFile(json, JSON.stringify(data), function (er) {
          if (er) return log.er(cb, "Could not write to "+json)(er)
          cb(null, data)
        })
      })
    })
  })
}
function addLocalDirectory (p, cb) {
  // if it's a folder, then read the package.json,
  // tar it to the proper place, and add the cache tar
  if (p.indexOf(npm.cache) === 0) return cb(new Error(
    "Adding a cache directory to the cache will make the world implode."))
  readJson(path.join(p, "package.json"), function (er, data) {
    if (er) return cb(er)
    var tgz = path.join(npm.cache, data.name, data.version, "package.tgz")
    packTar(tgz, p, function (er) {
      if (er) return cb(er)
      addLocalTarball(tgz, cb)
    })
  })
}

function unpack (pkg, ver, unpackTarget, cb) {
  read(pkg, ver, function (er, data) {
    if (er) return log.er(cb, "Could not read data for "+pkg+"@"+ver)(er)
    unpackTar( path.join(npm.cache, pkg, ver, "package.tgz")
             , unpackTarget
             , cb
             )
  })
}

function unpackTar (tarball, unpackTarget, cb) {
  mkdir(unpackTarget, function (er) {
    if (er) return log.er(cb, "Could not create "+unpackTarget)(er)
    // cp the gzip of the tarball, pipe the stdout into tar's stdin
    // gzip {tarball} --decompress --stdout | tar xf - --strip-components=1 -C {unpackTarget}
    pipe( spawn(npm.config.get("gzip"), ["--decompress", "--stdout", tarball])
        , spawn( npm.config.get("tar")
               , ["vx", "--strip-components=1", "-C", unpackTarget]
               )
        , log.er(cb, "Failed unpacking the tarball.\n"
                   + "This is very rare. Perhaps the 'gzip' or 'tar' configs\n"
                   + "are set improperly?\n")
        )
  })
}
function packTar (targetTarball, folder, cb) {
  log.verbose(folder+" "+targetTarball, "packTar")
  if (folder.charAt(0) !== "/") folder = path.join(process.cwd(), folder)
  if (folder.slice(-1) === "/") folder = folder.slice(0, -1)
  var cwd = process.cwd()
    , parent = path.dirname(folder)
    , addFolder = path.basename(folder)
    , ignore = path.join(folder, ".npmignore")
    , defaultIgnore = path.join(__dirname, "utils", "default.npmignore")
    , include = path.join(folder, ".npminclude")
  cb = log.er(cb, "Failed unpacking the tarball.\n"
             + "This is very rare. Perhaps the 'gzip' or 'tar' configs\n"
             + "are set improperly?\n")

  fs.stat(ignore, function (er) {
    if (er) ignore = defaultIgnore
    fs.stat(include, function (er) {
      if (er) include = false
      mkdir(path.dirname(targetTarball), function (er) {
        if (er) return log.er(cb, "Could not create "+targetTarball)(er)
        process.chdir(parent)
        // tar xf - --strip-components=1 -C {unpackTarget} | gzip {tarball} > targetTarball
        var target = fs.createWriteStream(targetTarball)
          , unPacked = false
          , args = [ "cv", "--exclude", ".git", "-X", ignore]
        if (include) args.push("-T", include)
        args.push(addFolder)
        var tar = spawn(npm.config.get("tar"), args)
          , gzip = spawn(npm.config.get("gzip"), ["--stdout"])
          , errState
        pipe(tar, gzip, function (er) {
          if (errState) return
          if (er) return cb(errState = er)
        })
        sys.pump(gzip.stdout, target, function (er, ok) {
          if (errState) return
          if (er) return cb(errState = er)
          process.chdir(cwd)
          cb()
        })
      })
    })
  })
}
<|endoftext|>


<commit_before>'use babel';

import fs from 'fs';

export const config = {
  cargoPath: {
    title: 'Path to the Cargo executable',
    type: 'string',
    default: 'cargo',
    order: 1
  },
  verbose: {
    title: 'Verbose Cargo output',
    description: 'Pass the --verbose flag to cargo',
    type: 'boolean',
    default: false,
    order: 2
  },
  openDocs: {
    title: 'Open documentation in browser after \'doc\' target is built',
    type: 'boolean',
    default: false,
    order: 3
  },
  showBacktrace: {
    title: 'Show backtrace information in tests',
    description: 'Set environment variable RUST_BACKTRACE=1',
    type: 'boolean',
    default: false,
    order: 4
  },
  cargoCheck: {
    title: 'Enable `cargo check',
    description: 'Enable the `cargo check` Cargo command. Only use this if you have `cargo check` installed.',
    type: 'boolean',
    default: false,
    order: 5
  },
  cargoClippy: {
    title: 'Enable `cargo clippy',
    description: 'Enable the `cargo clippy` Cargo command to run Clippy\'s lints. \
                  Only use this if you have the `cargo clippy` package installed.',
    type: 'boolean',
    default: false,
    order: 6
  },
  jsonErrors: {
    title: 'Use json errors',
    description: 'Instead of using regex to parse the human readable output (requires rustc version 1.7)',
    type: 'boolean',
    default: false,
    order: 7
  }
};

export function provideBuilder() {
  return class CargoBuildProvider {
    constructor(cwd) {
      this.cwd = cwd;
    }

    getNiceName() {
      return 'Cargo';
    }

    isEligible() {
      return fs.existsSync(`${this.cwd}/Cargo.toml`);
    }

    settings() {
      const cargoPath = atom.config.get('build-cargo.cargoPath');
      const args = [];
      atom.config.get('build-cargo.verbose') && args.push('--verbose');

      const docArgs = [ 'doc' ];
      atom.config.get('build-cargo.openDocs') && docArgs.push('--open');

      const env = {};
      if (atom.config.get('build-cargo.jsonErrors')) {
          env["RUSTFLAGS"] = "-Z unstable-options --error-format=json";
      }
      if (atom.config.get('build-cargo.showBacktrace')) {
        env['RUST_BACKTRACE'] = '1'
      }

      const matchRelaxed = '(?<file>.+.rs):(?<line>\\d+):(?<col>\\d+):(?: (?<line_end>\\d+):(?<col_end>\\d+) )?(error):';
      const matchStrictRegex = '(?<file>.+.rs):(?<line>\\d+):(?<col>\\d+):(?: (?<line_end>\\d+):(?<col_end>\\d+) )?';
      const matchStrictFunction = function(output) {
          var array = [];
          output.split(/\n/).forEach(function(line) {
              if (line[0] != '{') {
                  return;
              }
              const json = JSON.parse(line);
              json.spans.forEach(function(span) {
                  json.file = span.file_name;
                  json.line = span.line_start;
                  json.line_end = span.line_end;
                  json.col = span.column_start;
                  json.col_end = span.column_end;
                  this.push(json);
              }.bind(this));
          }.bind(array));
          return array;
      };
      const matchStrict = atom.config.get('build-cargo.jsonErrors') ? matchStrictFunction : matchStrictRegex;
      const matchThreadPanic = 'thread \'[^\\\']+\' panicked at \'[^\\\']+\', (?<file>[^\\/][^\\:]+):(?<line>\\d+)';
      const matchBacktrace = 'at (?<file>[^.\/][^\\/][^\\:]+):(?<line>\\d+)';

      var commands = [
        {
          name: 'Cargo: build (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'build' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ]
        },
        {
          name: 'Cargo: build (release)',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--release' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic ]
        },
        {
          name: 'Cargo: bench',
          exec: cargoPath,
          env: env,
          args: [ 'bench' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ]
        },
        {
          name: 'Cargo: clean',
          exec: cargoPath,
          env: env,
          args: [ 'clean' ].concat(args),
          sh: false,
          errorMatch: []
        },
        {
          name: 'Cargo: doc',
          exec: cargoPath,
          env: env,
          args: docArgs.concat(args),
          sh: false,
          errorMatch: []
        },
        {
          name: 'Cargo: run',
          exec: cargoPath,
          env: env,
          args: [ 'run' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ]
        },
        {
          name: 'Cargo: test',
          exec: cargoPath,
          env: env,
          args: [ 'test' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ]
        },
        {
          name: 'Cargo: update',
          exec: cargoPath,
          env: env,
          args: [ 'update' ].concat(args),
          sh: false,
          errorMatch: []
        },
        {
          name: `Cargo: build example`,
          exec: cargoPath,
          env: env,
          args: [ 'build', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ]
        },
        {
          name: `Cargo: run example`,
          exec: cargoPath,
          env: env,
          args: [ 'run', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ]
        },
        {
          name: `Cargo: run bin`,
          exec: cargoPath,
          env: env,
          args: [ 'run', '--bin', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ]
        }
      ];

      if (atom.config.get('build-cargo.cargoClippy')) {
        commands.push({
          name: `Cargo: Clippy`,
          exec: cargoPath,
          env: env,
          args: ['clippy'].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ]
        })
      }

      if (atom.config.get('build-cargo.cargoCheck')) {
        commands.push({
          name: `Cargo: check`,
          exec: cargoPath,
          env: env,
          args: ['check'].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ]
        })
      }

      return commands;
    }
  };
}
<commit_msg>Add support for Atom commands<commit_after>'use babel';

import fs from 'fs';

export const config = {
  cargoPath: {
    title: 'Path to the Cargo executable',
    type: 'string',
    default: 'cargo',
    order: 1
  },
  verbose: {
    title: 'Verbose Cargo output',
    description: 'Pass the --verbose flag to cargo',
    type: 'boolean',
    default: false,
    order: 2
  },
  openDocs: {
    title: 'Open documentation in browser after \'doc\' target is built',
    type: 'boolean',
    default: false,
    order: 3
  },
  showBacktrace: {
    title: 'Show backtrace information in tests',
    description: 'Set environment variable RUST_BACKTRACE=1',
    type: 'boolean',
    default: false,
    order: 4
  },
  cargoCheck: {
    title: 'Enable `cargo check',
    description: 'Enable the `cargo check` Cargo command. Only use this if you have `cargo check` installed.',
    type: 'boolean',
    default: false,
    order: 5
  },
  cargoClippy: {
    title: 'Enable `cargo clippy',
    description: 'Enable the `cargo clippy` Cargo command to run Clippy\'s lints. \
                  Only use this if you have the `cargo clippy` package installed.',
    type: 'boolean',
    default: false,
    order: 6
  },
  jsonErrors: {
    title: 'Use json errors',
    description: 'Instead of using regex to parse the human readable output (requires rustc version 1.7)',
    type: 'boolean',
    default: false,
    order: 7
  }
};

export function provideBuilder() {
  return class CargoBuildProvider {
    constructor(cwd) {
      this.cwd = cwd;
    }

    getNiceName() {
      return 'Cargo';
    }

    isEligible() {
      return fs.existsSync(`${this.cwd}/Cargo.toml`);
    }

    settings() {
      const cargoPath = atom.config.get('build-cargo.cargoPath');
      const args = [];
      atom.config.get('build-cargo.verbose') && args.push('--verbose');

      const docArgs = [ 'doc' ];
      atom.config.get('build-cargo.openDocs') && docArgs.push('--open');

      const env = {};
      if (atom.config.get('build-cargo.jsonErrors')) {
          env["RUSTFLAGS"] = "-Z unstable-options --error-format=json";
      }
      if (atom.config.get('build-cargo.showBacktrace')) {
        env['RUST_BACKTRACE'] = '1'
      }

      const matchRelaxed = '(?<file>.+.rs):(?<line>\\d+):(?<col>\\d+):(?: (?<line_end>\\d+):(?<col_end>\\d+) )?(error):';
      const matchStrictRegex = '(?<file>.+.rs):(?<line>\\d+):(?<col>\\d+):(?: (?<line_end>\\d+):(?<col_end>\\d+) )?';
      const matchStrictFunction = function(output) {
          var array = [];
          output.split(/\n/).forEach(function(line) {
              if (line[0] != '{') {
                  return;
              }
              const json = JSON.parse(line);
              json.spans.forEach(function(span) {
                  json.file = span.file_name;
                  json.line = span.line_start;
                  json.line_end = span.line_end;
                  json.col = span.column_start;
                  json.col_end = span.column_end;
                  this.push(json);
              }.bind(this));
          }.bind(array));
          return array;
      };
      const matchStrict = atom.config.get('build-cargo.jsonErrors') ? matchStrictFunction : matchStrictRegex;
      const matchThreadPanic = 'thread \'[^\\\']+\' panicked at \'[^\\\']+\', (?<file>[^\\/][^\\:]+):(?<line>\\d+)';
      const matchBacktrace = 'at (?<file>[^.\/][^\\/][^\\:]+):(?<line>\\d+)';

      var commands = [
        {
          name: 'Cargo: build (debug)',
          exec: cargoPath,
          env: env,
          args: [ 'build' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ],
          atomCommandName: "cargo:build-debug"
        },
        {
          name: 'Cargo: build (release)',
          exec: cargoPath,
          env: env,
          args: [ 'build', '--release' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic ],
          atomCommandName: "cargo:build-release"
        },
        {
          name: 'Cargo: bench',
          exec: cargoPath,
          env: env,
          args: [ 'bench' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ],
          atomCommandName: "cargo:bench"
        },
        {
          name: 'Cargo: clean',
          exec: cargoPath,
          env: env,
          args: [ 'clean' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: "cargo:clean"
        },
        {
          name: 'Cargo: doc',
          exec: cargoPath,
          env: env,
          args: docArgs.concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: "cargo:doc"
        },
        {
          name: 'Cargo: run',
          exec: cargoPath,
          env: env,
          args: [ 'run' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ],
          atomCommandName: "cargo:run"
        },
        {
          name: 'Cargo: test',
          exec: cargoPath,
          env: env,
          args: [ 'test' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ],
          atomCommandName: "cargo:run-test"
        },
        {
          name: 'Cargo: update',
          exec: cargoPath,
          env: env,
          args: [ 'update' ].concat(args),
          sh: false,
          errorMatch: [],
          atomCommandName: "cargo:update"
        },
        {
          name: `Cargo: build example`,
          exec: cargoPath,
          env: env,
          args: [ 'build', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ],
          atomCommandName: "cargo:build-example"
        },
        {
          name: `Cargo: run example`,
          exec: cargoPath,
          env: env,
          args: [ 'run', '--example', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ],
          atomCommandName: "cargo:run-example"
        },
        {
          name: `Cargo: run bin`,
          exec: cargoPath,
          env: env,
          args: [ 'run', '--bin', '{FILE_ACTIVE_NAME_BASE}' ].concat(args),
          sh: false,
          errorMatch: [ matchStrict, matchThreadPanic, matchBacktrace ],
          atomCommandName: "cargo:run-bin"
        }
      ];

      if (atom.config.get('build-cargo.cargoClippy')) {
        commands.push({
          name: `Cargo: Clippy`,
          exec: cargoPath,
          env: env,
          args: ['clippy'].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ],
          atomCommandName: "cargo:clippy"
        })
      }

      if (atom.config.get('build-cargo.cargoCheck')) {
        commands.push({
          name: `Cargo: check`,
          exec: cargoPath,
          env: env,
          args: ['check'].concat(args),
          sh: false,
          errorMatch: [ matchRelaxed, matchThreadPanic ],
          atomCommandName: "cargo:check"
        })
      }

      return commands;
    }
  };
}
<|endoftext|>


<commit_before>/**
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const puppeteer = require('puppeteer-core');
const findChrome = require('./find_chrome');
const {rpc} = require('../rpc');
const {Color} = require('./color');
const {InterceptedRequest} = require('./intercepted_request');

const fs = require('fs');
const util = require('util');
const {URL} = require('url');
const EventEmitter = require('events');
const fsReadFile = util.promisify(fs.readFile);

class App extends EventEmitter {
  /**
   * @param {!Page} page Puppeteer page
   */
  constructor(page) {
    super();
    this.www_ = new Map();
    this.page_ = page;
    this.page_.on('close', () => this.emit(App.Events.Exit));
  }

  /**
   * @param {!Color} bgcolor
   */
  async init(bgcolor) {
    const bgcolorRGBA = bgcolor.canonicalRGBA();
    this.session_ = await this.page_.target().createCDPSession();
    return Promise.all([
      this.session_.send('Emulation.setDefaultBackgroundColorOverride',
          {color: {r: bgcolorRGBA[0], g: bgcolorRGBA[1],
            b: bgcolorRGBA[2], a: bgcolorRGBA[3] * 255}}),
      this.session_.send('Network.setRequestInterception', {patterns: [{urlPattern: '*'}]}),
      this.session_.on('Network.requestIntercepted', this.requestIntercepted_.bind(this))
    ]);
  }

  async exit() {
    await this.browser_.close();
  }

  /**
   * @param {string} name
   * @param {function} func
   * @return {!Promise}
   */
  exposeFunction(name, func) {
    return this.page_.exposeFunction(name, func);
  }

  /**
   * @param {function()|string} pageFunction
   * @param {!Array<*>} args
   * @return {!Promise<*>}
   */
  evaluate(pageFunction, ...args) {
    return this.page_.evaluate(pageFunction, ...args);
  }

  /**
   * @param {string} www Folder with the web content.
   * @param {string=} path Only serve folder for requests with given prefix.
   */
  serveFolder(folder, prefix) {
    this.www_.set(prefix || '', folder);
  }

  /**
   * Serves pages from given origin, eg `http://localhost:8080`.
   * This can be used for the fast development mode available in web frameworks.
   *
   * @param {string} origin
   */
  serveOrigin(origin) {
    this.wwwOrigin_ = origin;
  }

  /**
   * @param {string} uri
   * @param {*} params
   * @return {!Promise<*>}
   */
  load(uri, params) {
    if (!this.www_.size) {
      this.page_.close();
      throw new Error('Please call app.serveFolder(__dirname) or point to ' +
                      'other folder(s) with your web files');
    }
    this.page_.goto(`https://domain/${uri}`, {timeout: 0});
    return this.initializeRpc_(params);
  }

  /**
   * @param {*} params
   */
  async initializeRpc_(params) {
    const rpcFile = (await fsReadFile(__dirname + '/../rpc/rpc.js')).toString();
    let sendToParent;
    await this.page_.exposeFunction('receivedFromChild', data => sendToParent(data));

    await this.page_.evaluate(rpcFile => {
      const module = { exports: {} };
      eval(rpcFile);
      self.rpc = module.exports;
      function transport(receivedFromParent) {
        self.receivedFromParent = receivedFromParent;
        return receivedFromChild;
      }
      self.rpc.initWorld(transport, async params => {
        if (document.readyState === 'loading')
          await new Promise(f => document.addEventListener('DOMContentLoaded', f));
        if (self.load)
          return self.load(params.userRarams);
      });
    }, rpcFile);

    const transport = receivedFromChild => {
      sendToParent = receivedFromChild;
      return data => this.page_.evaluate(data => self.receivedFromParent(data), data);
    };
    return rpc.createWorld(transport, { userRarams: params });
  }

  /**
   * @param {!Object} request Intercepted request.
   */
  async requestIntercepted_(payload) {
    const request = new InterceptedRequest(this.session_, payload);
    const url = new URL(request.url());
    if (url.hostname !== 'domain') {
      request.continue();
      return;
    }
    if (this.wwwOrigin_) {
      request.continue({ url: this.wwwOrigin_ + url.pathname });
      return;
    }

    for (const [prefix, folder] of this.www_) {
      let pathname = url.pathname.substr(1);
      if (!pathname.startsWith(prefix))
        continue;
      pathname = pathname.substr(prefix.length);
      const fileName = folder + '/' + pathname;
      if (!fs.existsSync(fileName))
        continue;
      const buffer = await fsReadFile(fileName);
      request.fulfill(200, { 'content-type': contentType(request, fileName) }, buffer);
      return;
    }
    request.continue();
  }
}

App.Events = {
  Exit: 'exit',
};

const imageContentTypes = new Map([
  ['jpeg', 'image/jpeg'], ['jpg', 'image/jpeg'], ['svg', 'image/svg+xml'], ['gif', 'image/gif'], ['webp', 'image/webp'],
  ['png', 'image/png'], ['ico', 'image/ico'], ['tiff', 'image/tiff'], ['tif', 'image/tif'], ['bmp', 'image/bmp']
]);

const fontContentTypes = new Map([
  ['ttf', 'font/opentype'], ['otf', 'font/opentype'], ['ttc', 'font/opentype'], ['woff', 'application/font-woff']
]);

/**
 * @param {!InterceptedRequest} request
 * @param {!string} fileName
 */
function contentType(request, fileName) {
  const dotIndex = fileName.lastIndexOf('.');
  const extension = fileName.substr(dotIndex + 1);
  switch (request.resourceType()) {
    case 'Document': return 'text/html';
    case 'Script': return 'text/javascript';
    case 'Stylesheet': return 'text/css';
    case 'Image':
      return imageContentTypes.get(extension) || 'image/png';
    case 'Font':
      return fontContentTypes.get(extension) || 'application/font-woff';
  }
}

/**
 * @param {!Object=} options
 * @return {!Browser}
 */
async function launch(options = {}) {
  if (!options.bgcolor)
    options.bgcolor = '#ffffff';
  const bgcolor = Color.parse(options.bgcolor);
  const bgcolorHex = bgcolor.asString(Color.Format.HEX);

  const executablePath = options.executablePath || findChrome().pop();
  if (!executablePath) {
    console.error('Could not find Chrome installation, please make sure Chrome browser is installed from https://www.google.com/chrome/.');
    process.exit(0);
    return;
  }

  const args = [
    `--app=data:text/html,<style>html{background:${bgcolorHex};}</style>`,
    `--enable-features=NetworkService`,
  ];

  if (options.args)
    args.push(...options.args);

  if (options.width && options.height)
    args.push(`--window-size=${options.width},${options.height}`);

  const browser = await puppeteer.launch({
    executablePath,
    pipe: true,
    defaultViewport: null,
    headless: false,
    userDataDir: options.userDataDir || '.profile',
    args });

  const page = (await browser.pages()).find(page => page.url().startsWith('data:text/html,'));
  const context = browser.defaultBrowserContext();
  await context.overridePermissions('https://domain', [
    'geolocation',
    'midi',
    'notifications',
    'camera',
    'microphone',
    'clipboard-read',
    'clipboard-write']);
  const app = new App(page);
  await app.init(bgcolor);
  return app;
}

module.exports = { launch };
<commit_msg>Update load() uri with default parameter (#58)<commit_after>/**
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const puppeteer = require('puppeteer-core');
const findChrome = require('./find_chrome');
const {rpc} = require('../rpc');
const {Color} = require('./color');
const {InterceptedRequest} = require('./intercepted_request');

const fs = require('fs');
const util = require('util');
const {URL} = require('url');
const EventEmitter = require('events');
const fsReadFile = util.promisify(fs.readFile);

class App extends EventEmitter {
  /**
   * @param {!Page} page Puppeteer page
   */
  constructor(page) {
    super();
    this.www_ = new Map();
    this.page_ = page;
    this.page_.on('close', () => this.emit(App.Events.Exit));
  }

  /**
   * @param {!Color} bgcolor
   */
  async init(bgcolor) {
    const bgcolorRGBA = bgcolor.canonicalRGBA();
    this.session_ = await this.page_.target().createCDPSession();
    return Promise.all([
      this.session_.send('Emulation.setDefaultBackgroundColorOverride',
          {color: {r: bgcolorRGBA[0], g: bgcolorRGBA[1],
            b: bgcolorRGBA[2], a: bgcolorRGBA[3] * 255}}),
      this.session_.send('Network.setRequestInterception', {patterns: [{urlPattern: '*'}]}),
      this.session_.on('Network.requestIntercepted', this.requestIntercepted_.bind(this))
    ]);
  }

  async exit() {
    await this.browser_.close();
  }

  /**
   * @param {string} name
   * @param {function} func
   * @return {!Promise}
   */
  exposeFunction(name, func) {
    return this.page_.exposeFunction(name, func);
  }

  /**
   * @param {function()|string} pageFunction
   * @param {!Array<*>} args
   * @return {!Promise<*>}
   */
  evaluate(pageFunction, ...args) {
    return this.page_.evaluate(pageFunction, ...args);
  }

  /**
   * @param {string} www Folder with the web content.
   * @param {string=} path Only serve folder for requests with given prefix.
   */
  serveFolder(folder, prefix) {
    this.www_.set(prefix || '', folder);
  }

  /**
   * Serves pages from given origin, eg `http://localhost:8080`.
   * This can be used for the fast development mode available in web frameworks.
   *
   * @param {string} origin
   */
  serveOrigin(origin) {
    this.wwwOrigin_ = origin;
  }

  /**
   * @param {string} uri
   * @param {*} params
   * @return {!Promise<*>}
   */
  load(uri, params) {
    if (!this.www_.size) {
      this.page_.close();
      throw new Error('Please call app.serveFolder(__dirname) or point to ' +
                      'other folder(s) with your web files');
    }
    this.page_.goto(`https://domain/${uri||''}`, {timeout: 0});
    return this.initializeRpc_(params);
  }

  /**
   * @param {*} params
   */
  async initializeRpc_(params) {
    const rpcFile = (await fsReadFile(__dirname + '/../rpc/rpc.js')).toString();
    let sendToParent;
    await this.page_.exposeFunction('receivedFromChild', data => sendToParent(data));

    await this.page_.evaluate(rpcFile => {
      const module = { exports: {} };
      eval(rpcFile);
      self.rpc = module.exports;
      function transport(receivedFromParent) {
        self.receivedFromParent = receivedFromParent;
        return receivedFromChild;
      }
      self.rpc.initWorld(transport, async params => {
        if (document.readyState === 'loading')
          await new Promise(f => document.addEventListener('DOMContentLoaded', f));
        if (self.load)
          return self.load(params.userRarams);
      });
    }, rpcFile);

    const transport = receivedFromChild => {
      sendToParent = receivedFromChild;
      return data => this.page_.evaluate(data => self.receivedFromParent(data), data);
    };
    return rpc.createWorld(transport, { userRarams: params });
  }

  /**
   * @param {!Object} request Intercepted request.
   */
  async requestIntercepted_(payload) {
    const request = new InterceptedRequest(this.session_, payload);
    const url = new URL(request.url());
    if (url.hostname !== 'domain') {
      request.continue();
      return;
    }
    if (this.wwwOrigin_) {
      request.continue({ url: this.wwwOrigin_ + url.pathname });
      return;
    }

    for (const [prefix, folder] of this.www_) {
      let pathname = url.pathname.substr(1);
      if (!pathname.startsWith(prefix))
        continue;
      pathname = pathname.substr(prefix.length);
      const fileName = folder + '/' + pathname;
      if (!fs.existsSync(fileName))
        continue;
      const buffer = await fsReadFile(fileName);
      request.fulfill(200, { 'content-type': contentType(request, fileName) }, buffer);
      return;
    }
    request.continue();
  }
}

App.Events = {
  Exit: 'exit',
};

const imageContentTypes = new Map([
  ['jpeg', 'image/jpeg'], ['jpg', 'image/jpeg'], ['svg', 'image/svg+xml'], ['gif', 'image/gif'], ['webp', 'image/webp'],
  ['png', 'image/png'], ['ico', 'image/ico'], ['tiff', 'image/tiff'], ['tif', 'image/tif'], ['bmp', 'image/bmp']
]);

const fontContentTypes = new Map([
  ['ttf', 'font/opentype'], ['otf', 'font/opentype'], ['ttc', 'font/opentype'], ['woff', 'application/font-woff']
]);

/**
 * @param {!InterceptedRequest} request
 * @param {!string} fileName
 */
function contentType(request, fileName) {
  const dotIndex = fileName.lastIndexOf('.');
  const extension = fileName.substr(dotIndex + 1);
  switch (request.resourceType()) {
    case 'Document': return 'text/html';
    case 'Script': return 'text/javascript';
    case 'Stylesheet': return 'text/css';
    case 'Image':
      return imageContentTypes.get(extension) || 'image/png';
    case 'Font':
      return fontContentTypes.get(extension) || 'application/font-woff';
  }
}

/**
 * @param {!Object=} options
 * @return {!Browser}
 */
async function launch(options = {}) {
  if (!options.bgcolor)
    options.bgcolor = '#ffffff';
  const bgcolor = Color.parse(options.bgcolor);
  const bgcolorHex = bgcolor.asString(Color.Format.HEX);

  const executablePath = options.executablePath || findChrome().pop();
  if (!executablePath) {
    console.error('Could not find Chrome installation, please make sure Chrome browser is installed from https://www.google.com/chrome/.');
    process.exit(0);
    return;
  }

  const args = [
    `--app=data:text/html,<style>html{background:${bgcolorHex};}</style>`,
    `--enable-features=NetworkService`,
  ];

  if (options.args)
    args.push(...options.args);

  if (options.width && options.height)
    args.push(`--window-size=${options.width},${options.height}`);

  const browser = await puppeteer.launch({
    executablePath,
    pipe: true,
    defaultViewport: null,
    headless: false,
    userDataDir: options.userDataDir || '.profile',
    args });

  const page = (await browser.pages()).find(page => page.url().startsWith('data:text/html,'));
  const context = browser.defaultBrowserContext();
  await context.overridePermissions('https://domain', [
    'geolocation',
    'midi',
    'notifications',
    'camera',
    'microphone',
    'clipboard-read',
    'clipboard-write']);
  const app = new App(page);
  await app.init(bgcolor);
  return app;
}

module.exports = { launch };
<|endoftext|>


<commit_before>(function (global) {
  'use strict';

  // ImageDataに描画された文字の位置とサイズを取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charRect(data) {
    var
    t = Infinity, b = 0,
    l = Infinity, r = 0,
    x, y;

    for (y = 0; y < data.height; y++) {
      for (x = 0; x < data.width; x++) {
        if (data.data[(y * data.width + x) * 4 + 3] !== 0) {
          t = Math.min(t, y);
          b = Math.max(b, y);
          l = Math.min(l, x);
          r = Math.max(r, x);
        }
      }
    }

    return [l, t, r - l, b - t];
  }

  // ImageDataに描画された文字の位置とサイズを正方形で取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charSquare(data) {
    var
    rect = charRect(data),
    l = Math.max(rect[2], rect[3]);

    return [rect[0] - Math.floor(rect[2] / 2) + Math.floor(l / 2),
            rect[1] - Math.floor(rect[3] / 2) + Math.floor(l / 2),
            l, l];
  }

  // rectの領域をn×nに区切って、その中の文字の割合がpより大きいかどうかの入った配列（mesh）を返す
  // 配列の要素が1のときpより大きくて、0のとき小さい
  function charMesh(data, rect, n, p) {
    var
    sx = rect[0], sy = rect[1],
    w = rect[2], h = rect[3],
    x, y,
    i, j, k,
    mesh = [];

    for (y = 0; y < h; y++) {
      i = Math.floor(y / h * n);
      for (x = 0; x < w; x++) {
        j = Math.floor(x / w * n);
        k = i * n + j;
        mesh[k] = mesh[k] || 0;
        if (y + sy < 0 || x + sx < 0) continue;
        if (data.data[((y + sy) * data.width + (x + sx)) * 4 + 3] !== 0) {
          mesh[k] += 1;
        }
      }
    }

    for (y = 0; y < n; y++) {
      for (x = 0; x < n; x++) {
        k = y * n + x;
        mesh[k] /= w/n*h/n;
        mesh[k] = mesh[k] > p ? 1 : 0;
      }
    }

    return mesh;
  }

  // meshを整数値にする。
  // meshの長さが32より大きいと死ぬ。
  function meshToInt(mesh) {
    return parseInt(mesh.join(''), 2);
  }

  // 公開
  var
  chars = {
    charRect: charRect,
    charSquare: charSquare,
    charMesh: charMesh,
    meshToInt: meshToInt,
  };
  if (typeof module !== 'undefined') {
    module.exports = chars;
  }
  global.chars = chars;
})((this || 0).self || global);
<commit_msg>fix charMesh<commit_after>(function (global) {
  'use strict';

  // ImageDataに描画された文字の位置とサイズを取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charRect(data) {
    var
    t = Infinity, b = 0,
    l = Infinity, r = 0,
    x, y;

    for (y = 0; y < data.height; y++) {
      for (x = 0; x < data.width; x++) {
        if (data.data[(y * data.width + x) * 4 + 3] !== 0) {
          t = Math.min(t, y);
          b = Math.max(b, y);
          l = Math.min(l, x);
          r = Math.max(r, x);
        }
      }
    }

    return [l, t, r - l, b - t];
  }

  // ImageDataに描画された文字の位置とサイズを正方形で取得する。
  // 返り値は`[x, y, w, h]`の形式
  function charSquare(data) {
    var
    rect = charRect(data),
    l = Math.max(rect[2], rect[3]);

    return [rect[0] + Math.floor(rect[2] / 2) - Math.floor(l / 2),
            rect[1] + Math.floor(rect[3] / 2) - Math.floor(l / 2),
            l, l];
  }

  // rectの領域をn×nに区切って、その中の文字の割合がpより大きいかどうかの入った配列（mesh）を返す
  // 配列の要素が1のときpより大きくて、0のとき小さい
  function charMesh(data, rect, n, p) {
    var
    sx = rect[0], sy = rect[1],
    w = rect[2], h = rect[3],
    x, y,
    i, j, k,
    mesh = [];

    for (y = 0; y < h; y++) {
      i = Math.floor(y / h * n);
      for (x = 0; x < w; x++) {
        j = Math.floor(x / w * n);
        k = i * n + j;
        mesh[k] = mesh[k] || 0;
        if (y + sy < 0 || x + sx < 0 ||
            data.height <= y + sy || data.width <= x + sx) continue;
        if (data.data[((y + sy) * data.width + (x + sx)) * 4 + 3]) {
          mesh[k] += 1;
        }
      }
    }

    for (y = 0; y < n; y++) {
      for (x = 0; x < n; x++) {
        k = y * n + x;
        mesh[k] /= w/n*h/n;
        mesh[k] = mesh[k] > p ? 1 : 0;
      }
    }

    return mesh;
  }

  // meshを整数値にする。
  // meshの長さが32より大きいと死ぬ。
  function meshToInt(mesh) {
    return parseInt(mesh.join(''), 2);
  }

  // 公開
  var
  chars = {
    charRect: charRect,
    charSquare: charSquare,
    charMesh: charMesh,
    meshToInt: meshToInt,
  };
  if (typeof module !== 'undefined') {
    module.exports = chars;
  }
  global.chars = chars;
})((this || 0).self || global);
<|endoftext|>


<commit_before>/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License.
See LICENSE file.
*/
var fs = require('graceful-fs'),
    path = require('path'),
    args = require('./args'),
    async = require('async'),
    crypto = require('crypto'),
    url = require('url'),
    log = require('davlog'),
    timethat = require('timethat').calc,
    verify = require('./verify.js').verify,
    options = args();

var check = function(dir, callback) {
    var json = path.join(options.dir, dir, 'index.json');
    fs.readFile(json, 'utf8', function(err, data) {
        if (err) {
            return callback();
        }
        var o = JSON.parse(data);
        if (!o.versions) {
            return callback();
        }
        var tarballs = [];
        Object.keys(o.versions).forEach(function(version) {
            var info = o.versions[version];
            if (info.dist && info.dist.tarball && info.dist.shasum) {
                var u = url.parse(info.dist.tarball);
                tarballs.push({
                    path: u.pathname,
                    tarball: path.join(options.dir, u.pathname),
                    shasum: info.dist.shasum
                });
            }
        });
        if (!tarballs.length) {
            return callback();
        }
        async.eachLimit(tarballs, options.limit, verify, function() {
            callback(null, tarballs);
        });
    });
};
fs.readdir(options.dir, function(err, dirs) {
    var start = new Date();
    async.eachLimit(dirs, options.limit, check, function(err, res) {
        log.info('finished checking tarballs in', timethat(start));
        process.exit(250);
    });
});
<commit_msg>check: set log.quiet() to mute info notices on check<commit_after>/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License.
See LICENSE file.
*/
var fs = require('graceful-fs'),
    path = require('path'),
    args = require('./args'),
    async = require('async'),
    crypto = require('crypto'),
    url = require('url'),
    log = require('davlog'),
    timethat = require('timethat').calc,
    verify = require('./verify.js').verify,
    options = args();

//kill the info logging on check
//only warn and err should be printed.
log.quiet();

var check = function(dir, callback) {
    var json = path.join(options.dir, dir, 'index.json');
    fs.readFile(json, 'utf8', function(err, data) {
        if (err) {
            return callback();
        }
        var o = JSON.parse(data);
        if (!o.versions) {
            return callback();
        }
        var tarballs = [];
        Object.keys(o.versions).forEach(function(version) {
            var info = o.versions[version];
            if (info.dist && info.dist.tarball && info.dist.shasum) {
                var u = url.parse(info.dist.tarball);
                tarballs.push({
                    path: u.pathname,
                    tarball: path.join(options.dir, u.pathname),
                    shasum: info.dist.shasum
                });
            }
        });
        if (!tarballs.length) {
            return callback();
        }
        async.eachLimit(tarballs, options.limit, verify, function() {
            callback(null, tarballs);
        });
    });
};
fs.readdir(options.dir, function(err, dirs) {
    var start = new Date();
    async.eachLimit(dirs, options.limit, check, function(err, res) {
        log.info('finished checking tarballs in', timethat(start));
        process.exit(250);
    });
});
<|endoftext|>


<commit_before>'use strict';

var crypto = require('crypto');
var request = require('request');
var cheerio = require('cheerio');
var db = require('./db');

var fingerprint = function (data) {
  var shasum = crypto.createHash('sha1');
  shasum.update(data || '');
  return shasum.digest('hex');
};

var getExcerpt = function (data, query) {
  if (!data) return null;
  var $ = cheerio.load(data);
  var excerpt = $(query).html();
  if (typeof excerpt !== 'string') return null;
  return new Buffer(excerpt);
};

var getNewData = function (url, cb) {
  console.log('Fetching:', url);
  request(url, function (err, res, data) {
    if (err) return cb(err);
    if (res.statusCode >= 300) return cb(new Error('Unexpected HTTP status code: ' + res.statusCode));
    cb(null, new Buffer(data));
  });
};

var getOldData = function (url, cb) {
  db.log.find({ url: url }).sort({ when: -1 }).limit(1, function (err, docs) {
    if (err) return cb(err);
    if (!docs.length) return cb();
    cb(null, docs[0].data.buffer, docs[0].when);
  });
};

module.exports = function (url, query, cb) {
  getNewData(url, function (err, data) {
    if (err) return cb(err);
    getOldData(url, function (err, oldData, when) {
      if (err) return cb(err);
      if (query) {
        var excerpt = getExcerpt(data, query);
        var oldExcerpt = getExcerpt(oldData, query);
        if (!excerpt) return cb(new Error('Could not get excerpt from new data'));
        if (oldData && !oldExcerpt) return cb(new Error('Could not get excerpt from old data'));
      }

      var hash = fingerprint(excerpt || data);
      var oldHash = fingerprint(oldExcerpt || oldData);
      if (hash === oldHash) return cb();

      console.log('Detected change:', url);
      var doc = { url: url, hash: hash, when: new Date(), data: data };
      db.log.insert(doc, function (err) {
        cb(err, {
          url: url,
          old: {
            data: (oldData || '').toString(),
            when: when || new Date(0)
          },
          new: {
            data: data.toString(),
            when: doc.when
          }
        });
      });
    });
  });
};
<commit_msg>Add debugging data to error<commit_after>'use strict';

var crypto = require('crypto');
var request = require('request');
var cheerio = require('cheerio');
var db = require('./db');

var fingerprint = function (data) {
  var shasum = crypto.createHash('sha1');
  shasum.update(data || '');
  return shasum.digest('hex');
};

var getExcerpt = function (data, query) {
  if (!data) return null;
  var $ = cheerio.load(data);
  var excerpt = $(query).html();
  if (typeof excerpt !== 'string') return null;
  return new Buffer(excerpt);
};

var getNewData = function (url, cb) {
  console.log('Fetching:', url);
  request(url, function (err, res, data) {
    if (err) return cb(err);
    if (res.statusCode >= 300) return cb(new Error('Unexpected HTTP status code: ' + res.statusCode));
    cb(null, new Buffer(data));
  });
};

var getOldData = function (url, cb) {
  db.log.find({ url: url }).sort({ when: -1 }).limit(1, function (err, docs) {
    if (err) return cb(err);
    if (!docs.length) return cb();
    cb(null, docs[0].data.buffer, docs[0].when);
  });
};

module.exports = function (url, query, cb) {
  getNewData(url, function (err, data) {
    if (err) return cb(err);
    getOldData(url, function (err, oldData, when) {
      if (err) return cb(err);
      if (query) {
        var excerpt = getExcerpt(data, query);
        var oldExcerpt = getExcerpt(oldData, query);
        if (!excerpt) {
          err = new Error('Could not get excerpt from new data');
          err.bodySize = data.length;
          cb(err);
          return;
        }
        if (oldData && !oldExcerpt) return cb(new Error('Could not get excerpt from old data'));
      }

      var hash = fingerprint(excerpt || data);
      var oldHash = fingerprint(oldExcerpt || oldData);
      if (hash === oldHash) return cb();

      console.log('Detected change:', url);
      var doc = { url: url, hash: hash, when: new Date(), data: data };
      db.log.insert(doc, function (err) {
        cb(err, {
          url: url,
          old: {
            data: (oldData || '').toString(),
            when: when || new Date(0)
          },
          new: {
            data: data.toString(),
            when: doc.when
          }
        });
      });
    });
  });
};
<|endoftext|>


<commit_before>
function Machine() {
    this.reset();
}

Machine.prototype.reset = function () {
    this.pc = 0;
    this.memory = new Array(4096);
    this.stack = new Array(16);
    this.sp = 0;
    this.v = createArray(16);
    this.i = 0;
}

function loadAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.i = addr;
}

function jumpToLocation(self, code, nv) {
    var addr = code & 0x0fff;
    self.pc = self.v[0] + addr;
}

function jumpToAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.pc = addr;
}

function callAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.stack[self.sp++] = self.pc;
    self.pc = addr;
}

function skipIfEqual(self, code, nv) {
    var value = code & 0x00ff;

    if (self.v[nv] == value)
        self.pc += 2;
}

function skipIfNotEqual(self, code, nv) {
    var value = code & 0x00ff;

    if (self.v[nv] != value)
        self.pc += 2;
}

function skipIfEqualRegister(self, code, nv) {
    var nv0 = (code & 0x00f0) >> 4;

    if (self.v[nv] == self.v[nv0])
        self.pc += 2;
}

function loadAndOpers(self, code, nv) {
    var nv0 = (code & 0x00f0) >> 4;

    var rightcode = code & 0x0f;

    if (rightcode == 1)
        self.v[nv] |= self.v[nv0];
    else if (rightcode == 2)
        self.v[nv] &= self.v[nv0];
    else if (rightcode == 3)
        self.v[nv] ^= self.v[nv0];
    else if (rightcode == 4) {
        var result = self.v[nv] + self.v[nv0];

        if ((result & 0x00ff) != result) {
            result = result & 0x00ff;
            self.vf = 1;
        }
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 5) {
        var result = self.v[nv] - self.v[nv0];
        result &= 0x00ff;

        if (self.v[nv] > self.v[nv0])
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 7) {
        var result = self.v[nv0] - self.v[nv];
        result &= 0x00ff;

        if (self.v[nv0] > self.v[nv])
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 6) {
        if (self.v[nv] & 0x01)
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] >>= 1;
    }
    else if (rightcode == 0x0e) {
        if (self.v[nv] & 0x80)
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] <<= 1;
    }
    else
        self.v[nv] = self.v[nv0];
}

var dispatch = [];
dispatch[0x01] = jumpToAddress;
dispatch[0x02] = callAddress;
dispatch[0x03] = skipIfEqual;
dispatch[0x04] = skipIfNotEqual;
dispatch[0x05] = skipIfEqualRegister;
dispatch[0x08] = loadAndOpers;
dispatch[0x0a] = loadAddress;
dispatch[0x0b] = jumpToLocation;

Machine.prototype.execute = function (code) {
    if (code == 0x00ee) {
        var addr = this.stack[this.sp - 1];
        this.sp--;
        this.pc = addr;
        return;
    }

    var nv = (code & 0x0f00) >> 8;
    var leftcode = (code & 0xf000) >> 12;

    if (dispatch[leftcode]) {
        dispatch[leftcode](this, code, nv);
        return;
    }

    if (leftcode === 0x0f) {
        if ((code & 0x00ff) == 0x001e)
            this.i += this.v[nv];

        if ((code & 0x00ff) == 0x0033) {
            var value = this.v[nv];
            var value1 = value % 10;
            var value2 = Math.floor((value % 100) / 10);
            var value3 = Math.floor((value % 1000) / 100);

            this.memory[this.i] = value3;
            this.memory[this.i + 1] = value2;
            this.memory[this.i + 2] = value1;
        }
    }
    else
        this.v[nv] = code & 0xff;
}

function createArray(size) {
    var array = new Array(size);

    for (var k = 0; k < size; k++)
        array[k] = 0;

    return array;
}

module.exports = {
    machine: function () { return new Machine(); }
}<commit_msg>Refactor operation dispatch<commit_after>
function Machine() {
    this.reset();
}

Machine.prototype.reset = function () {
    this.pc = 0;
    this.memory = new Array(4096);
    this.stack = new Array(16);
    this.sp = 0;
    this.v = createArray(16);
    this.i = 0;
}

function loadAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.i = addr;
}

function jumpToLocation(self, code, nv) {
    var addr = code & 0x0fff;
    self.pc = self.v[0] + addr;
}

function jumpToAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.pc = addr;
}

function callAddress(self, code, nv) {
    var addr = code & 0x0fff;
    self.stack[self.sp++] = self.pc;
    self.pc = addr;
}

function skipIfEqual(self, code, nv) {
    var value = code & 0x00ff;

    if (self.v[nv] == value)
        self.pc += 2;
}

function skipIfNotEqual(self, code, nv) {
    var value = code & 0x00ff;

    if (self.v[nv] != value)
        self.pc += 2;
}

function skipIfEqualRegister(self, code, nv) {
    var nv0 = (code & 0x00f0) >> 4;

    if (self.v[nv] == self.v[nv0])
        self.pc += 2;
}

function loadAndOpers(self, code, nv) {
    var nv0 = (code & 0x00f0) >> 4;

    var rightcode = code & 0x0f;

    if (rightcode == 1)
        self.v[nv] |= self.v[nv0];
    else if (rightcode == 2)
        self.v[nv] &= self.v[nv0];
    else if (rightcode == 3)
        self.v[nv] ^= self.v[nv0];
    else if (rightcode == 4) {
        var result = self.v[nv] + self.v[nv0];

        if ((result & 0x00ff) != result) {
            result = result & 0x00ff;
            self.vf = 1;
        }
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 5) {
        var result = self.v[nv] - self.v[nv0];
        result &= 0x00ff;

        if (self.v[nv] > self.v[nv0])
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 7) {
        var result = self.v[nv0] - self.v[nv];
        result &= 0x00ff;

        if (self.v[nv0] > self.v[nv])
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] = result;
    }
    else if (rightcode == 6) {
        if (self.v[nv] & 0x01)
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] >>= 1;
    }
    else if (rightcode == 0x0e) {
        if (self.v[nv] & 0x80)
            self.vf = 1;
        else
            self.vf = 0;

        self.v[nv] <<= 1;
    }
    else
        self.v[nv] = self.v[nv0];
}

function loadsAndAdd(self, code, nv) {
    if ((code & 0x00ff) == 0x001e)
        self.i += self.v[nv];

    if ((code & 0x00ff) == 0x0033) {
        var value = self.v[nv];
        var value1 = value % 10;
        var value2 = Math.floor((value % 100) / 10);
        var value3 = Math.floor((value % 1000) / 100);

        self.memory[self.i] = value3;
        self.memory[self.i + 1] = value2;
        self.memory[self.i + 2] = value1;
    }
}

var dispatch = [];
dispatch[0x01] = jumpToAddress;
dispatch[0x02] = callAddress;
dispatch[0x03] = skipIfEqual;
dispatch[0x04] = skipIfNotEqual;
dispatch[0x05] = skipIfEqualRegister;
dispatch[0x08] = loadAndOpers;
dispatch[0x0a] = loadAddress;
dispatch[0x0b] = jumpToLocation;
dispatch[0x0f] = loadsAndAdd;

Machine.prototype.execute = function (code) {
    if (code == 0x00ee) {
        var addr = this.stack[this.sp - 1];
        this.sp--;
        this.pc = addr;
        return;
    }

    var nv = (code & 0x0f00) >> 8;
    var leftcode = (code & 0xf000) >> 12;

    if (dispatch[leftcode]) {
        dispatch[leftcode](this, code, nv);
        return;
    }

    this.v[nv] = code & 0xff;
}

function createArray(size) {
    var array = new Array(size);

    for (var k = 0; k < size; k++)
        array[k] = 0;

    return array;
}

module.exports = {
    machine: function () { return new Machine(); }
}<|endoftext|>


<commit_before>'use babel';

import EventEmitter from 'events';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { execSync } from 'child_process';
import voucher from 'voucher';
import glob from 'glob';

export const config = require('./config');

export function providingFunction() {
    const generateErrorMatch = [
        "CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const compileErrorMatch = [
        "(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?error:\\s+(?<message>.+)", // GCC/Clang Error
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?error\\s+(C\\d+):(?<message>.*)" // Visual Studio Error
    ];
    return class CMakeBuildProvider extends EventEmitter {
        constructor(source_dir) {
            super();
            // TODO allow the source directory to be selected.
            this.source_dir = source_dir;

            atom.config.observe('build-cmake.build_suffix', (suffix) => {
                this.build_dir = this.source_dir + suffix;
                this.cache_path = path.join(this.build_dir, 'CMakeCache.txt');
            });
            atom.config.observe('build-cmake.generator', (generator) => {
                // TODO: Validate generator exists and show feedback to user.
                this.generator = (!!generator) ? generator.trim() : '';
            });
            atom.config.observe('build-cmake.executable', (executable) => {
                // TODO: Validate executable is on path/exists and show feedback to user.
                this.executable = executable;
            });
            atom.config.observe('build-cmake.custom_args', (c_args) => {
                this.custom_args = c_args;
            });
            atom.config.onDidChange('build-cmake.build_suffix', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.executable', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.generator', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.custom_args', () => {this.emit('refresh');});
        }

        destructor() {
        }

        getNiceName() {
            return 'cmake';
        }

        isEligible() {
            return fs.existsSync(path.join(this.source_dir, 'CMakeLists.txt')) || fs.existsSync(this.cache_path);
        }

        createVisualStudioTarget(target_name) {
            return {
                atomCommandName : 'cmake:'+target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.source_dir,
                args : [this.custom_args, '--build', this.build_dir, '--target',target_name,'--','/maxcpucount','/clp:NoSummary;ErrorsOnly;Verbosity=quiet'],
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                sh : false
            };
        }

        visualStudioTargets() {
            return Array.from(new Set(glob.sync("**/*.vcxproj", {cwd:this.build_dir, nodir:true, ignore:"CMakeFiles/**"})
            .map(target => path.basename(target, '.vcxproj'))))
            .map(target => this.createVisualStudioTarget(target))
            .concat([this.createVisualStudioTarget('clean')]);
        }

        createMakeFileTarget(target_name) {
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.source_dir,
                args : [this.custom_args, '--build', this.build_dir, '--target',target_name,'--','-j'+os.cpus().length],
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                sh : false
            };
        }

        makeFileTargets() {
            output = execSync(
              this.executable + ' --build ' + this.build_dir + ' --target help', { cwd: this.build_dir });
            return output.toString('utf8')
            .split(/[\r\n]{1,2}/)
            .filter(line => line.startsWith('...'))
            .map((line) => this.createMakeFileTarget(line.replace('... ','').split(' ')[0]));
        }

        settings() {
            fs.unwatchFile(this.cache_path);

            fs.watchFile(this.cache_path, (curr, prev) => {
                if(fs.existsSync(this.cache_path) && curr.mtime != prev.mtime)
                    this.emit('refresh');
            });

            var args = ['-B' + this.build_dir, '-H' + this.source_dir, '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON'];
            // Add custom generator if specified.
            if (!!this.generator) {
                args.unshift('-G' + this.generator);
            }
            const generateTarget = {
                atomCommandName : 'cmake:generate',
                name : 'generate',
                exec : this.executable,
                cwd : this.source_dir,
                args : args,
                errorMatch:generateErrorMatch,
                sh : false
            };

            return voucher(fs.readFile, this.cache_path, { encoding: 'utf8' })
            .then(cache => {
                var generator = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/)[1];
                if(generator.match('Visual Studio'))
                    return [ generateTarget ].concat(this.visualStudioTargets());
                else
                    return [ generateTarget ].concat(this.makeFileTargets());
            }).catch(e => {
                return [ generateTarget ];
            });
        }
    };
}
<commit_msg>Fixed slight error.<commit_after>'use babel';

import EventEmitter from 'events';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { execSync } from 'child_process';
import voucher from 'voucher';
import glob from 'glob';

export const config = require('./config');

export function providingFunction() {
    const generateErrorMatch = [
        "CMake Error at (?<file>[\\/0-9a-zA-Z\\._-]+):(?<line>\\d+)"
    ];
    const compileErrorMatch = [
        "(?<file>.+):(?<line>\\d+):(?<column>\\d+):\\s+(.*\\s+)?error:\\s+(?<message>.+)", // GCC/Clang Error
        "(.*>)?(?<file>.+)\\((?<line>\\d+)\\):\\s+(.*\\s+)?error\\s+(C\\d+):(?<message>.*)" // Visual Studio Error
    ];
    return class CMakeBuildProvider extends EventEmitter {
        constructor(source_dir) {
            super();
            // TODO allow the source directory to be selected.
            this.source_dir = source_dir;

            atom.config.observe('build-cmake.build_suffix', (suffix) => {
                this.build_dir = this.source_dir + suffix;
                this.cache_path = path.join(this.build_dir, 'CMakeCache.txt');
            });
            atom.config.observe('build-cmake.generator', (generator) => {
                // TODO: Validate generator exists and show feedback to user.
                this.generator = (!!generator) ? generator.trim() : '';
            });
            atom.config.observe('build-cmake.executable', (executable) => {
                // TODO: Validate executable is on path/exists and show feedback to user.
                this.executable = executable;
            });
            atom.config.observe('build-cmake.custom_args', (c_args) => {
                this.custom_args = c_args;
            });
            atom.config.onDidChange('build-cmake.build_suffix', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.executable', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.generator', () => {this.emit('refresh');});
            atom.config.onDidChange('build-cmake.custom_args', () => {this.emit('refresh');});
        }

        destructor() {
        }

        getNiceName() {
            return 'cmake';
        }

        isEligible() {
            return fs.existsSync(path.join(this.source_dir, 'CMakeLists.txt')) || fs.existsSync(this.cache_path);
        }

        createVisualStudioTarget(target_name) {
            return {
                atomCommandName : 'cmake:'+target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.source_dir,
                args : ['--build', this.build_dir, '--target',target_name,'--','/maxcpucount','/clp:NoSummary;ErrorsOnly;Verbosity=quiet'],
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                sh : false
            };
        }

        visualStudioTargets() {
            return Array.from(new Set(glob.sync("**/*.vcxproj", {cwd:this.build_dir, nodir:true, ignore:"CMakeFiles/**"})
            .map(target => path.basename(target, '.vcxproj'))))
            .map(target => this.createVisualStudioTarget(target))
            .concat([this.createVisualStudioTarget('clean')]);
        }

        createMakeFileTarget(target_name) {
            return {
                atomCommandName : 'cmake:' + target_name,
                name : target_name,
                exec : this.executable,
                cwd : this.source_dir,
                args : ['--build', this.build_dir, '--target',target_name,'--','-j'+os.cpus().length],
                errorMatch : compileErrorMatch.concat(generateErrorMatch),
                sh : false
            };
        }

        makeFileTargets() {
            output = execSync(
              this.executable + ' --build ' + this.build_dir + ' --target help', { cwd: this.build_dir });
            return output.toString('utf8')
            .split(/[\r\n]{1,2}/)
            .filter(line => line.startsWith('...'))
            .map((line) => this.createMakeFileTarget(line.replace('... ','').split(' ')[0]));
        }

        settings() {
            fs.unwatchFile(this.cache_path);

            fs.watchFile(this.cache_path, (curr, prev) => {
                if(fs.existsSync(this.cache_path) && curr.mtime != prev.mtime)
                    this.emit('refresh');
            });

            var args = [this.custom_args, '-B' + this.build_dir, '-H' + this.source_dir, '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON'];
            // Add custom generator if specified.
            if (!!this.generator) {
                args.unshift('-G' + this.generator);
            }
            const generateTarget = {
                atomCommandName : 'cmake:generate',
                name : 'generate',
                exec : this.executable,
                cwd : this.source_dir,
                args : args,
                errorMatch:generateErrorMatch,
                sh : false
            };

            return voucher(fs.readFile, this.cache_path, { encoding: 'utf8' })
            .then(cache => {
                var generator = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/)[1];
                if(generator.match('Visual Studio'))
                    return [ generateTarget ].concat(this.visualStudioTargets());
                else
                    return [ generateTarget ].concat(this.makeFileTargets());
            }).catch(e => {
                return [ generateTarget ];
            });
        }
    };
}
<|endoftext|>