1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-04 04:32:43 +01:00
phorge-phorge/src/applications/diffusion/remarkup/DiffusionSourceLinkRemarkupRule.php
epriestley 4180b337cf Add a "{src ...}" Remarkup rule to provide a more flexible way to reference source files in Diffusion
Summary: Depends on D20538. Ref T13291. We now recognize full source URIs, but encoding full URIs isn't super human-friendly and we can't do stuff like relative links with them. Add `{src ...}` as a way to get to this behavior that supports options and more flexible syntax.

Test Plan: {F6463607}

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13291

Differential Revision: https://secure.phabricator.com/D20539
2019-05-21 13:12:28 -07:00

221 lines
5.3 KiB
PHP

<?php
final class DiffusionSourceLinkRemarkupRule
extends PhutilRemarkupRule {
const KEY_SOURCELINKS = 'diffusion.links';
public function getPriority() {
return 200.0;
}
public function apply($text) {
return preg_replace_callback(
'@{(?:src|source)\b((?:[^}\\\\]+|\\\\.)*)}@m',
array($this, 'markupSourceLink'),
$text);
}
public function markupSourceLink(array $matches) {
$engine = $this->getEngine();
$text_mode = $engine->isTextMode();
$mail_mode = $engine->isHTMLMailMode();
if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
// We could do better than this in text mode and mail mode, but focus
// on web mode first.
return $matches[0];
}
$metadata_key = self::KEY_SOURCELINKS;
$metadata = $engine->getTextMetadata($metadata_key, array());
$token = $engine->storeText($matches[0]);
$metadata[] = array(
'token' => $token,
'raw' => $matches[0],
'input' => $matches[1],
);
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$metadata_key = self::KEY_SOURCELINKS;
$metadata = $engine->getTextMetadata($metadata_key, array());
if (!$metadata) {
return;
}
$viewer = $engine->getConfig('viewer');
if (!$viewer) {
return;
}
$defaults = array(
'repository' => null,
'line' => null,
'commit' => null,
'ref' => null,
);
$tags = array();
foreach ($metadata as $ref) {
$token = $ref['token'];
$raw = $ref['raw'];
$input = $ref['input'];
$pattern =
'(^'.
'[\s,]*'.
'(?:"(?P<quotedpath>(?:[^\\\\"]+|\\.)+)"|(?P<rawpath>[^\s,]+))'.
'[\s,]*'.
'(?P<options>.*)'.
'\z)';
$matches = null;
if (!preg_match($pattern, $input, $matches)) {
$hint_text = pht(
'Missing path, expected "{src path ...}" in: %s',
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue;
}
$path = idx($matches, 'rawpath');
if (!strlen($path)) {
$path = idx($matches, 'quotedpath');
$path = stripcslashes($path);
}
$parts = explode(':', $path, 2);
if (count($parts) == 2) {
$repository = nonempty($parts[0], null);
$path = $parts[1];
} else {
$repository = null;
$path = $parts[0];
}
$options = $matches['options'];
$parser = new PhutilSimpleOptions();
$options = $parser->parse($options) + $defaults;
foreach ($options as $key => $value) {
if (!array_key_exists($key, $defaults)) {
$hint_text = pht(
'Unknown option "%s" in: %s',
$key,
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue 2;
}
}
if ($options['repository'] !== null) {
$repository = $options['repository'];
}
if ($repository === null) {
$hint_text = pht(
'Missing repository, expected "{src repository:path ...}" '.
'or "{src path repository=...}" in: %s',
$raw);
$hint = $this->newSyntaxHint($hint_text);
$engine->overwriteStoredText($token, $hint);
continue;
}
$tags[] = array(
'token' => $token,
'raw' => $raw,
'identifier' => $repository,
'path' => $path,
'options' => $options,
);
}
if (!$tags) {
return;
}
$query = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(ipull($tags, 'identifier'));
$query->execute();
$repository_map = $query->getIdentifierMap();
foreach ($tags as $tag) {
$token = $tag['token'];
$identifier = $tag['identifier'];
$repository = idx($repository_map, $identifier);
if (!$repository) {
// For now, just bail out here. Ideally, we should distingiush between
// restricted and invalid repositories.
continue;
}
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $viewer,
'repository' => $repository,
));
$options = $tag['options'];
$line = $options['line'];
$commit = $options['commit'];
$ref_name = $options['ref'];
$link_uri = $drequest->generateURI(
array(
'action' => 'browse',
'path' => $tag['path'],
'commit' => $commit,
'line' => $line,
'branch' => $ref_name,
));
$view = id(new DiffusionSourceLinkView())
->setRepository($repository)
->setPath($tag['path'])
->setURI($link_uri);
if ($line !== null) {
$view->setLine($line);
}
if ($commit !== null) {
$view->setCommit($commit);
}
if ($ref_name !== null) {
$view->setRefName($ref_name);
}
$engine->overwriteStoredText($token, $view);
}
}
private function newSyntaxHint($text) {
return id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor('red')
->setIcon('fa-exclamation-triangle')
->setName($text);
}
}