Changeset View
Standalone View
src/workingcopy/ArcanistWorkingCopyPath.php
- This file was added.
<?php | |||||
final class ArcanistWorkingCopyPath | |||||
extends Phobject { | |||||
private $path; | |||||
private $mode; | |||||
private $data; | |||||
private $binary; | |||||
private $dataAsLines; | |||||
private $charMap; | |||||
private $lineMap; | |||||
public function setPath($path) { | |||||
$this->path = $path; | |||||
return $this; | |||||
} | |||||
public function getPath() { | |||||
return $this->path; | |||||
} | |||||
public function setData($data) { | |||||
$this->data = $data; | |||||
return $this; | |||||
} | |||||
public function getData() { | |||||
if ($this->data === null) { | |||||
throw new Exception( | |||||
pht( | |||||
'No data provided for path "%s".', | |||||
$this->getDescription())); | |||||
} | |||||
return $this->data; | |||||
} | |||||
public function getDataAsLines() { | |||||
if ($this->dataAsLines === null) { | |||||
$lines = phutil_split_lines($this->getData()); | |||||
$this->dataAsLines = $lines; | |||||
} | |||||
return $this->dataAsLines; | |||||
} | |||||
public function setMode($mode) { | |||||
$this->mode = $mode; | |||||
return $this; | |||||
} | |||||
public function getMode() { | |||||
if ($this->mode === null) { | |||||
throw new Exception( | |||||
pht( | |||||
'No mode provided for path "%s".', | |||||
$this->getDescription())); | |||||
} | |||||
return $this->mode; | |||||
} | |||||
public function isExecutable() { | |||||
$mode = $this->getMode(); | |||||
return (bool)($mode & 0111); | |||||
} | |||||
public function isBinary() { | |||||
if ($this->binary === null) { | |||||
$data = $this->getData(); | |||||
$is_binary = ArcanistDiffUtils::isHeuristicBinaryFile($data); | |||||
$this->binary = $is_binary; | |||||
} | |||||
return $this->binary; | |||||
} | |||||
public function getMimeType() { | |||||
if ($this->mimeType === null) { | |||||
// TOOLSETS: This is not terribly efficient on real repositories since | |||||
// it re-writes files which are often already on disk, but is good for | |||||
// unit tests. | |||||
$tmp = new TempFile(); | |||||
Filesystem::writeFile($tmp, $this->getData()); | |||||
$mime = Filesystem::getMimeType($tmp); | |||||
amckinley: Why can't we just invoke `Filesystem::getMimeType()` on the real file instead of copying it? | |||||
epriestleyAuthorUnsubmitted Done Inline ActionsI'd ideally like (most of?) the unit tests to be able to work without having to touch disk, or even have a real file on disk at all. This change moves us some of the way toward that: we're still writing files to disk but most of (none of?) the current linters actually touch them, and we could probably stop soon or in the future without too much effort. Today, some other tests work by building an actual git repository in tmp/ and effectively running arc inside it. Although this is the only realistic way to do some things, I think way more tests than necessary currently rely on a lot more external state than they need. Later, I expect WorkingCopyPath to also be able to represent a real file on disk, and obviously we need to actually write to a real file when patching working copies. I think Filesystem::getMimeType() could probably also be changed to have a "$bytes" variant, although this might be somewhat tricky. It looks like echo ... | file - works when we fall back to the file binary, and it looks like finfo_file($resource, ...) might be able to take some kind of clever PHP stream reference for the second argument and not require a write to disk. Historically, we had (and perhaps still have?) some linters which really acted on a whole repository state. These may be trickier, but maybe they really make more sense to decompose as "build + lint" or turn into beautifiers instead of linters. This is kind of meandering but basically: I do expect to add some code to just use the real file on disk when we have one later on, but I'm also trying to make stuff need less state/context to run. epriestley: I'd ideally like (most of?) the unit tests to be able to work without having to touch disk, or… | |||||
$this->mimeType = $mime; | |||||
} | |||||
return $this->mimeType; | |||||
} | |||||
public function getBasename() { | |||||
return basename($this->getPath()); | |||||
} | |||||
public function getLineAndCharFromOffset($offset) { | |||||
if ($this->charMap === null) { | |||||
$char_map = array(); | |||||
$line_map = array(); | |||||
$lines = $this->getDataAsLines(); | |||||
$line_number = 0; | |||||
$line_start = 0; | |||||
foreach ($lines as $line) { | |||||
$len = strlen($line); | |||||
$line_map[] = $line_start; | |||||
$line_start += $len; | |||||
for ($ii = 0; $ii < $len; $ii++) { | |||||
$char_map[] = $line_number; | |||||
} | |||||
$line_number++; | |||||
} | |||||
$this->charMap = $char_map; | |||||
$this->lineMap = $line_map; | |||||
} | |||||
$line = $this->charMap[$offset]; | |||||
$char = $offset - $this->lineMap[$line]; | |||||
return array($line, $char); | |||||
} | |||||
} |
Why can't we just invoke Filesystem::getMimeType() on the real file instead of copying it? And if we do have to copy it, maybe we should only copy the first couple hundred bytes?