Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20140305.diviner.1.slugcol.sql b/resources/sql/autopatches/20140305.diviner.1.slugcol.sql
new file mode 100644
index 0000000000..741cd8df45
--- /dev/null
+++ b/resources/sql/autopatches/20140305.diviner.1.slugcol.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol
+ ADD titleSlugHash CHAR(12) COLLATE latin1_bin AFTER title;
diff --git a/resources/sql/autopatches/20140305.diviner.2.slugkey.sql b/resources/sql/autopatches/20140305.diviner.2.slugkey.sql
new file mode 100644
index 0000000000..1316a69ccc
--- /dev/null
+++ b/resources/sql/autopatches/20140305.diviner.2.slugkey.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol
+ ADD KEY `key_slug` (titleSlugHash);
diff --git a/src/applications/diviner/atom/DivinerAtom.php b/src/applications/diviner/atom/DivinerAtom.php
index c4405fd98d..b224dde4d7 100644
--- a/src/applications/diviner/atom/DivinerAtom.php
+++ b/src/applications/diviner/atom/DivinerAtom.php
@@ -1,425 +1,426 @@
<?php
final class DivinerAtom {
const TYPE_FILE = 'file';
const TYPE_ARTICLE = 'article';
const TYPE_METHOD = 'method';
const TYPE_CLASS = 'class';
const TYPE_FUNCTION = 'function';
const TYPE_INTERFACE = 'interface';
private $type;
private $name;
private $file;
private $line;
private $hash;
private $contentRaw;
private $length;
private $language;
private $docblockRaw;
private $docblockText;
private $docblockMeta;
private $warnings = array();
private $parent;
private $parentHash;
private $children = array();
private $childHashes = array();
private $context;
private $extends = array();
private $links = array();
private $book;
private $properties = array();
/**
* Returns a sorting key which imposes an unambiguous, stable order on atoms.
*/
public function getSortKey() {
return implode(
"\0",
array(
$this->getBook(),
$this->getType(),
$this->getContext(),
$this->getName(),
$this->getFile(),
sprintf('%08', $this->getLine()),
));
}
public function setBook($book) {
$this->book = $book;
return $this;
}
public function getBook() {
return $this->book;
}
public function setContext($context) {
$this->context = $context;
return $this;
}
public function getContext() {
return $this->context;
}
public static function getAtomSerializationVersion() {
return 2;
}
public function addWarning($warning) {
$this->warnings[] = $warning;
return $this;
}
public function getWarnings() {
return $this->warnings;
}
public function setDocblockRaw($docblock_raw) {
$this->docblockRaw = $docblock_raw;
$parser = new PhutilDocblockParser();
list($text, $meta) = $parser->parse($docblock_raw);
$this->docblockText = $text;
$this->docblockMeta = $meta;
return $this;
}
public function getDocblockRaw() {
return $this->docblockRaw;
}
public function getDocblockText() {
if ($this->docblockText === null) {
throw new Exception("Call setDocblockRaw() before getDocblockText()!");
}
return $this->docblockText;
}
public function getDocblockMeta() {
if ($this->docblockMeta === null) {
throw new Exception("Call setDocblockRaw() before getDocblockMeta()!");
}
return $this->docblockMeta;
}
public function getDocblockMetaValue($key, $default = null) {
$meta = $this->getDocblockMeta();
return idx($meta, $key, $default);
}
public function setDocblockMetaValue($key, $value) {
$meta = $this->getDocblockMeta();
$meta[$key] = $value;
$this->docblockMeta = $meta;
return $this;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getType() {
return $this->type;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setFile($file) {
$this->file = $file;
return $this;
}
public function getFile() {
return $this->file;
}
public function setLine($line) {
$this->line = $line;
return $this;
}
public function getLine() {
return $this->line;
}
public function setContentRaw($content_raw) {
$this->contentRaw = $content_raw;
return $this;
}
public function getContentRaw() {
return $this->contentRaw;
}
public function setHash($hash) {
$this->hash = $hash;
return $this;
}
public function addLink(DivinerAtomRef $ref) {
$this->links[] = $ref;
return $this;
}
public function addExtends(DivinerAtomRef $ref) {
$this->extends[] = $ref;
return $this;
}
public function getLinkDictionaries() {
return mpull($this->links, 'toDictionary');
}
public function getExtendsDictionaries() {
return mpull($this->extends, 'toDictionary');
}
public function getExtends() {
return $this->extends;
}
public function getHash() {
if ($this->hash) {
return $this->hash;
}
$parts = array(
+ $this->getBook(),
$this->getType(),
$this->getName(),
$this->getFile(),
$this->getLine(),
$this->getLength(),
$this->getLanguage(),
$this->getContentRaw(),
$this->getDocblockRaw(),
$this->getProperties(),
$this->getChildHashes(),
mpull($this->extends, 'toHash'),
mpull($this->links, 'toHash'),
);
$this->hash = md5(serialize($parts)).'N';
return $this->hash;
}
public function setLength($length) {
$this->length = $length;
return $this;
}
public function getLength() {
return $this->length;
}
public function setLanguage($language) {
$this->language = $language;
return $this;
}
public function getLanguage() {
return $this->language;
}
public function addChildHash($child_hash) {
$this->childHashes[] = $child_hash;
return $this;
}
public function getChildHashes() {
if (!$this->childHashes && $this->children) {
$this->childHashes = mpull($this->children, 'getHash');
}
return $this->childHashes;
}
public function setParentHash($parent_hash) {
if ($this->parentHash) {
throw new Exception("Atom already has a parent!");
}
$this->parentHash = $parent_hash;
return $this;
}
public function hasParent() {
return $this->parent || $this->parentHash;
}
public function setParent(DivinerAtom $atom) {
if ($this->parentHash) {
throw new Exception("Parent hash has already been computed!");
}
$this->parent = $atom;
return $this;
}
public function getParentHash() {
if ($this->parent && !$this->parentHash) {
$this->parentHash = $this->parent->getHash();
}
return $this->parentHash;
}
public function addChild(DivinerAtom $atom) {
if ($this->childHashes) {
throw new Exception("Child hashes have already been computed!");
}
$atom->setParent($this);
$this->children[] = $atom;
return $this;
}
public function getURI() {
$parts = array();
$parts[] = phutil_escape_uri_path_component($this->getType());
if ($this->getContext()) {
$parts[] = phutil_escape_uri_path_component($this->getContext());
}
$parts[] = phutil_escape_uri_path_component($this->getName());
$parts[] = null;
return implode('/', $parts);
}
public function toDictionary() {
// NOTE: If you change this format, bump the format version in
// getAtomSerializationVersion().
return array(
'book' => $this->getBook(),
'type' => $this->getType(),
'name' => $this->getName(),
'file' => $this->getFile(),
'line' => $this->getLine(),
'hash' => $this->getHash(),
'uri' => $this->getURI(),
'length' => $this->getLength(),
'context' => $this->getContext(),
'language' => $this->getLanguage(),
'docblockRaw' => $this->getDocblockRaw(),
'warnings' => $this->getWarnings(),
'parentHash' => $this->getParentHash(),
'childHashes' => $this->getChildHashes(),
'extends' => $this->getExtendsDictionaries(),
'links' => $this->getLinkDictionaries(),
'ref' => $this->getRef()->toDictionary(),
'properties' => $this->getProperties(),
);
}
public function getRef() {
$title = null;
if ($this->docblockMeta) {
$title = $this->getDocblockMetaValue('title');
}
return id(new DivinerAtomRef())
->setBook($this->getBook())
->setContext($this->getContext())
->setType($this->getType())
->setName($this->getName())
->setTitle($title)
->setGroup($this->getProperty('group'));
}
public static function newFromDictionary(array $dictionary) {
$atom = id(new DivinerAtom())
->setBook(idx($dictionary, 'book'))
->setType(idx($dictionary, 'type'))
->setName(idx($dictionary, 'name'))
->setFile(idx($dictionary, 'file'))
->setLine(idx($dictionary, 'line'))
->setHash(idx($dictionary, 'hash'))
->setLength(idx($dictionary, 'length'))
->setContext(idx($dictionary, 'context'))
->setLanguage(idx($dictionary, 'language'))
->setParentHash(idx($dictionary, 'parentHash'))
->setDocblockRaw(idx($dictionary, 'docblockRaw'))
->setProperties(idx($dictionary, 'properties'));
foreach (idx($dictionary, 'warnings', array()) as $warning) {
$atom->addWarning($warning);
}
foreach (idx($dictionary, 'childHashes', array()) as $child) {
$atom->addChildHash($child);
}
foreach (idx($dictionary, 'extends', array()) as $extends) {
$atom->addExtends(DivinerAtomRef::newFromDictionary($extends));
}
return $atom;
}
public function getProperty($key, $default = null) {
return idx($this->properties, $key, $default);
}
public function setProperty($key, $value) {
$this->properties[$key] = $value;
}
public function getProperties() {
return $this->properties;
}
public function setProperties(array $properties) {
$this->properties = $properties;
return $this;
}
public static function getThisAtomIsNotDocumentedString($type) {
switch ($type) {
case self::TYPE_FILE:
return pht('This file is not documented.');
case self::TYPE_FUNCTION:
return pht('This function is not documented.');
case self::TYPE_CLASS:
return pht('This class is not documented.');
case self::TYPE_ARTICLE:
return pht('This article is not documented.');
case self::TYPE_METHOD:
return pht('This method is not documented.');
case self::TYPE_INTERFACE:
return pht('This interface is not documented.');
default:
phlog("Need translation for '{$type}'.");
return pht('This %s is not documented.', $type);
}
}
public static function getAtomTypeNameString($type) {
switch ($type) {
case self::TYPE_FILE:
return pht('File');
case self::TYPE_FUNCTION:
return pht('Function');
case self::TYPE_CLASS:
return pht('Class');
case self::TYPE_ARTICLE:
return pht('Article');
case self::TYPE_METHOD:
return pht('Method');
case self::TYPE_INTERFACE:
return pht('Interface');
default:
phlog("Need translation for '{$type}'.");
return ucwords($type);
}
}
}
diff --git a/src/applications/diviner/atom/DivinerAtomRef.php b/src/applications/diviner/atom/DivinerAtomRef.php
index f1bd344b75..b536ed6223 100644
--- a/src/applications/diviner/atom/DivinerAtomRef.php
+++ b/src/applications/diviner/atom/DivinerAtomRef.php
@@ -1,196 +1,206 @@
<?php
final class DivinerAtomRef {
private $book;
private $context;
private $type;
private $name;
private $group;
private $summary;
private $index;
private $title;
public function getSortKey() {
return implode(
"\0",
array(
$this->getName(),
$this->getType(),
$this->getContext(),
$this->getBook(),
$this->getIndex(),
));
}
public function setIndex($index) {
$this->index = $index;
return $this;
}
public function getIndex() {
return $this->index;
}
public function setSummary($summary) {
$this->summary = $summary;
return $this;
}
public function getSummary() {
return $this->summary;
}
public function setName($name) {
$normal_name = self::normalizeString($name);
if (preg_match('/^@[0-9]+$/', $normal_name)) {
throw new Exception(
"Atom names must not be in the form '/@\d+/'. This pattern is ".
"reserved for disambiguating atoms with similar names.");
}
$this->name = $normal_name;
return $this;
}
public function getName() {
return $this->name;
}
public function setType($type) {
$this->type = self::normalizeString($type);
return $this;
}
public function getType() {
return $this->type;
}
public function setContext($context) {
if ($context === null) {
$this->context = $context;
} else {
$this->context = self::normalizeString($context);
}
return $this;
}
public function getContext() {
return $this->context;
}
public function setBook($book) {
if ($book === null) {
$this->book = $book;
} else {
$this->book = self::normalizeString($book);
}
return $this;
}
public function getBook() {
return $this->book;
}
public function setGroup($group) {
$this->group = $group;
return $this;
}
public function getGroup() {
return $this->group;
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function getTitle() {
return $this->title;
}
+ public function getTitleSlug() {
+ return self::normalizeTitleString($this->getTitle());
+ }
+
public function toDictionary() {
return array(
'book' => $this->getBook(),
'context' => $this->getContext(),
'type' => $this->getType(),
'name' => $this->getName(),
'group' => $this->getGroup(),
'index' => $this->getIndex(),
'summary' => $this->getSummary(),
'title' => $this->getTitle(),
);
}
public function toHash() {
$dict = $this->toDictionary();
unset($dict['group']);
unset($dict['index']);
unset($dict['summary']);
unset($dict['title']);
ksort($dict);
return md5(serialize($dict)).'S';
}
public static function newFromDictionary(array $dict) {
$obj = new DivinerAtomRef();
$obj->setBook(idx($dict, 'book'));
$obj->setContext(idx($dict, 'context'));
$obj->setType(idx($dict, 'type'));
$obj->setName(idx($dict, 'name'));
$obj->group = idx($dict, 'group');
$obj->index = idx($dict, 'index');
$obj->summary = idx($dict, 'summary');
$obj->title = idx($dict, 'title');
+
return $obj;
}
public static function normalizeString($str) {
// These characters create problems on the filesystem or in URIs. Replace
// them with non-problematic appoximations (instead of simply removing them)
// to keep the URIs fairly useful and avoid unnecessary collisions. These
// approximations are selected based on some domain knowledge of common
// languages: where a character is used as a delimiter, it is more helpful
// to replace it with a "." or a ":" or similar, while it's better if
// operator overloads read as, e.g., "operator_div".
$map = array(
// Hopefully not used anywhere by anything.
'#' => '.',
// Used in Ruby methods.
'?' => 'Q',
// Used in PHP namespaces.
'\\' => '.',
// Used in "operator +" in C++.
'+' => 'plus',
// Used in "operator %" in C++.
'%' => 'mod',
// Used in "operator /" in C++.
'/' => 'div',
);
$str = str_replace(array_keys($map), array_values($map), $str);
// Replace all spaces with underscores.
$str = preg_replace('/ +/', '_', $str);
// Replace control characters with "X".
$str = preg_replace('/[\x00-\x19]/', 'X', $str);
// Replace specific problematic names with alternative names.
$alternates = array(
'.' => 'dot',
'..' => 'dotdot',
'' => 'null',
);
return idx($alternates, $str, $str);
}
+ public static function normalizeTitleString($str) {
+ $str = self::normalizeString($str);
+ return phutil_utf8_strtolower($str);
+ }
+
}
diff --git a/src/applications/diviner/controller/DivinerFindController.php b/src/applications/diviner/controller/DivinerFindController.php
index a84e734642..e672a21f4b 100644
--- a/src/applications/diviner/controller/DivinerFindController.php
+++ b/src/applications/diviner/controller/DivinerFindController.php
@@ -1,71 +1,80 @@
<?php
final class DivinerFindController extends DivinerController {
public function shouldAllowPublic() {
return true;
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$book_name = $request->getStr('book');
$book = null;
if ($book_name) {
$book = id(new DivinerBookQuery())
->setViewer($viewer)
->withNames(array($book_name))
->executeOne();
if (!$book) {
return new Aphront404Response();
}
}
$query = id(new DivinerAtomQuery())
- ->setViewer($viewer)
- ->withNames(
- array(
- $request->getStr('name'),
- // TODO: This could probably be more smartly normalized in the DB,
- // but just fake it for now.
- phutil_utf8_strtolower($request->getStr('name')),
- ));
+ ->setViewer($viewer);
if ($book) {
$query->withBookPHIDs(array($book->getPHID()));
}
$context = $request->getStr('context');
if (strlen($context)) {
$query->withContexts(array($context));
}
$type = $request->getStr('type');
if (strlen($type)) {
$query->withTypes(array($type));
}
- $atoms = $query->execute();
+ $name_query = clone $query;
+
+ $name_query->withNames(
+ array(
+ $request->getStr('name'),
+ // TODO: This could probably be more smartly normalized in the DB,
+ // but just fake it for now.
+ phutil_utf8_strtolower($request->getStr('name')),
+ ));
+
+ $atoms = $name_query->execute();
+
+ if (!$atoms) {
+ $title_query = clone $query;
+ $title_query->withTitles(array($request->getStr('name')));
+ $atoms = $title_query->execute();
+ }
if (!$atoms) {
return new Aphront404Response();
}
if (count($atoms) == 1 && $request->getBool('jump')) {
$atom_uri = head($atoms)->getURI();
return id(new AphrontRedirectResponse())->setURI($atom_uri);
}
$list = $this->renderAtomList($atoms);
return $this->buildApplicationPage(
$list,
array(
'title' => 'derp',
'device' => true,
));
}
}
diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php
index 45986b7674..df6124293f 100644
--- a/src/applications/diviner/query/DivinerAtomQuery.php
+++ b/src/applications/diviner/query/DivinerAtomQuery.php
@@ -1,412 +1,432 @@
<?php
final class DivinerAtomQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $bookPHIDs;
private $names;
private $types;
private $contexts;
private $indexes;
private $includeUndocumentable;
private $includeGhosts;
private $nodeHashes;
+ private $titles;
private $needAtoms;
private $needExtends;
private $needChildren;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withBookPHIDs(array $phids) {
$this->bookPHIDs = $phids;
return $this;
}
public function withTypes(array $types) {
$this->types = $types;
return $this;
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withContexts(array $contexts) {
$this->contexts = $contexts;
return $this;
}
public function withIndexes(array $indexes) {
$this->indexes = $indexes;
return $this;
}
public function withNodeHashes(array $hashes) {
$this->nodeHashes = $hashes;
return $this;
}
+ public function withTitles($titles) {
+ $this->titles = $titles;
+ return $this;
+ }
+
public function needAtoms($need) {
$this->needAtoms = $need;
return $this;
}
public function needChildren($need) {
$this->needChildren = $need;
return $this;
}
/**
* Include "ghosts", which are symbols which used to exist but do not exist
* currently (for example, a function which existed in an older version of
* the codebase but was deleted).
*
* These symbols had PHIDs assigned to them, and may have other sorts of
* metadata that we don't want to lose (like comments or flags), so we don't
* delete them outright. They might also come back in the future: the change
* which deleted the symbol might be reverted, or the documentation might
* have been generated incorrectly by accident. In these cases, we can
* restore the original data.
*
* However, most callers are not interested in these symbols, so they are
* excluded by default. You can use this method to include them in results.
*
* @param bool True to include ghosts.
* @return this
*/
public function withIncludeGhosts($include) {
$this->includeGhosts = $include;
return $this;
}
public function needExtends($need) {
$this->needExtends = $need;
return $this;
}
public function withIncludeUndocumentable($include) {
$this->includeUndocumentable = $include;
return $this;
}
protected function loadPage() {
$table = new DivinerLiveSymbol();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $atoms) {
$books = array_unique(mpull($atoms, 'getBookPHID'));
$books = id(new DivinerBookQuery())
->setViewer($this->getViewer())
->withPHIDs($books)
->execute();
$books = mpull($books, null, 'getPHID');
foreach ($atoms as $key => $atom) {
$book = idx($books, $atom->getBookPHID());
if (!$book) {
unset($atoms[$key]);
continue;
}
$atom->attachBook($book);
}
if ($this->needAtoms) {
$atom_data = id(new DivinerLiveAtom())->loadAllWhere(
'symbolPHID IN (%Ls)',
mpull($atoms, 'getPHID'));
$atom_data = mpull($atom_data, null, 'getSymbolPHID');
foreach ($atoms as $key => $atom) {
$data = idx($atom_data, $atom->getPHID());
if (!$data) {
unset($atoms[$key]);
continue;
}
$atom->attachAtom($data);
}
}
// Load all of the symbols this symbol extends, recursively. Commonly,
// this means all the ancestor classes and interfaces it extends and
// implements.
if ($this->needExtends) {
// First, load all the matching symbols by name. This does 99% of the
// work in most cases, assuming things are named at all reasonably.
$names = array();
foreach ($atoms as $atom) {
foreach ($atom->getAtom()->getExtends() as $xref) {
$names[] = $xref->getName();
}
}
if ($names) {
$xatoms = id(new DivinerAtomQuery())
->setViewer($this->getViewer())
->withNames($names)
->needExtends(true)
->needAtoms(true)
->needChildren($this->needChildren)
->execute();
$xatoms = mgroup($xatoms, 'getName', 'getType', 'getBookPHID');
} else {
$xatoms = array();
}
foreach ($atoms as $atom) {
$alang = $atom->getAtom()->getLanguage();
$extends = array();
foreach ($atom->getAtom()->getExtends() as $xref) {
// If there are no symbols of the matching name and type, we can't
// resolve this.
if (empty($xatoms[$xref->getName()][$xref->getType()])) {
continue;
}
// If we found matches in the same documentation book, prefer them
// over other matches. Otherwise, look at all the the matches.
$matches = $xatoms[$xref->getName()][$xref->getType()];
if (isset($matches[$atom->getBookPHID()])) {
$maybe = $matches[$atom->getBookPHID()];
} else {
$maybe = array_mergev($matches);
}
if (!$maybe) {
continue;
}
// Filter out matches in a different language, since, e.g., PHP
// classes can not implement JS classes.
$same_lang = array();
foreach ($maybe as $xatom) {
if ($xatom->getAtom()->getLanguage() == $alang) {
$same_lang[] = $xatom;
}
}
if (!$same_lang) {
continue;
}
// If we have duplicates remaining, just pick the first one. There's
// nothing more we can do to figure out which is the real one.
$extends[] = head($same_lang);
}
$atom->attachExtends($extends);
}
}
if ($this->needChildren) {
$child_hashes = $this->getAllChildHashes($atoms, $this->needExtends);
if ($child_hashes) {
$children = id(new DivinerAtomQuery())
->setViewer($this->getViewer())
->withIncludeUndocumentable(true)
->withNodeHashes($child_hashes)
->needAtoms($this->needAtoms)
->execute();
$children = mpull($children, null, 'getNodeHash');
} else {
$children = array();
}
$this->attachAllChildren($atoms, $children, $this->needExtends);
}
return $atoms;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->bookPHIDs) {
$where[] = qsprintf(
$conn_r,
'bookPHID IN (%Ls)',
$this->bookPHIDs);
}
if ($this->types) {
$where[] = qsprintf(
$conn_r,
'type IN (%Ls)',
$this->types);
}
if ($this->names) {
$where[] = qsprintf(
$conn_r,
'name IN (%Ls)',
$this->names);
}
+ if ($this->titles) {
+ $hashes = array();
+ foreach ($this->titles as $title) {
+ $slug = DivinerAtomRef::normalizeTitleString($title);
+ $hash = PhabricatorHash::digestForIndex($slug);
+ $hashes[] = $hash;
+ }
+
+ $where[] = qsprintf(
+ $conn_r,
+ 'titleSlugHash in (%Ls)',
+ $hashes);
+ }
+
if ($this->contexts) {
$with_null = false;
$contexts = $this->contexts;
foreach ($contexts as $key => $value) {
if ($value === null) {
unset($contexts[$key]);
$with_null = true;
continue;
}
}
if ($contexts && $with_null) {
$where[] = qsprintf(
$conn_r,
'context IN (%Ls) OR context IS NULL',
$contexts);
} else if ($contexts) {
$where[] = qsprintf(
$conn_r,
'context IN (%Ls)',
$contexts);
} else if ($with_null) {
$where[] = qsprintf(
$conn_r,
'context IS NULL');
}
}
if ($this->indexes) {
$where[] = qsprintf(
$conn_r,
'atomIndex IN (%Ld)',
$this->indexes);
}
if (!$this->includeUndocumentable) {
$where[] = qsprintf(
$conn_r,
'isDocumentable = 1');
}
if (!$this->includeGhosts) {
$where[] = qsprintf(
$conn_r,
'graphHash IS NOT NULL');
}
if ($this->nodeHashes) {
$where[] = qsprintf(
$conn_r,
'nodeHash IN (%Ls)',
$this->nodeHashes);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
/**
* Walk a list of atoms and collect all the node hashes of the atoms'
* children. When recursing, also walk up the tree and collect children of
* atoms they extend.
*
* @param list<DivinerLiveSymbol> List of symbols to collect child hashes of.
* @param bool True to collect children of extended atoms,
* as well.
* @return map<string, string> Hashes of atoms' children.
*/
private function getAllChildHashes(array $symbols, $recurse_up) {
assert_instances_of($symbols, 'DivinerLiveSymbol');
$hashes = array();
foreach ($symbols as $symbol) {
foreach ($symbol->getAtom()->getChildHashes() as $hash) {
$hashes[$hash] = $hash;
}
if ($recurse_up) {
$hashes += $this->getAllChildHashes($symbol->getExtends(), true);
}
}
return $hashes;
}
/**
* Attach child atoms to existing atoms. In recursive mode, also attach child
* atoms to atoms that these atoms extend.
*
* @param list<DivinerLiveSymbol> List of symbols to attach childeren to.
* @param map<string, DivinerLiveSymbol> Map of symbols, keyed by node hash.
* @param bool True to attach children to extended atoms, as well.
* @return void
*/
private function attachAllChildren(
array $symbols,
array $children,
$recurse_up) {
assert_instances_of($symbols, 'DivinerLiveSymbol');
assert_instances_of($children, 'DivinerLiveSymbol');
foreach ($symbols as $symbol) {
$symbol_children = array();
foreach ($symbol->getAtom()->getChildHashes() as $hash) {
if (isset($children[$hash])) {
$symbol_children[] = $children[$hash];
}
}
$symbol->attachChildren($symbol_children);
if ($recurse_up) {
$this->attachAllChildren($symbol->getExtends(), $children, true);
}
}
}
public function getQueryApplicationClass() {
return 'PhabricatorApplicationDiviner';
}
}
diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php
index 2e873808ab..10c22e21f6 100644
--- a/src/applications/diviner/storage/DivinerLiveSymbol.php
+++ b/src/applications/diviner/storage/DivinerLiveSymbol.php
@@ -1,180 +1,193 @@
<?php
final class DivinerLiveSymbol extends DivinerDAO
implements PhabricatorPolicyInterface, PhabricatorMarkupInterface {
protected $bookPHID;
protected $context;
protected $type;
protected $name;
protected $atomIndex;
protected $graphHash;
protected $identityHash;
protected $nodeHash;
protected $title;
+ protected $titleSlugHash;
protected $groupName;
protected $summary;
protected $isDocumentable = 0;
private $book = self::ATTACHABLE;
private $atom = self::ATTACHABLE;
private $extends = self::ATTACHABLE;
private $children = self::ATTACHABLE;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_TIMESTAMPS => false,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
DivinerPHIDTypeAtom::TYPECONST);
}
public function getBook() {
return $this->assertAttached($this->book);
}
public function attachBook(DivinerLiveBook $book) {
$this->book = $book;
return $this;
}
public function getAtom() {
return $this->assertAttached($this->atom);
}
public function attachAtom(DivinerLiveAtom $atom) {
$this->atom = DivinerAtom::newFromDictionary($atom->getAtomData());
return $this;
}
public function getURI() {
$parts = array(
'book',
$this->getBook()->getName(),
$this->getType(),
);
if ($this->getContext()) {
$parts[] = $this->getContext();
}
$parts[] = $this->getName();
if ($this->getAtomIndex()) {
$parts[] = $this->getAtomIndex();
}
return '/'.implode('/', $parts).'/';
}
public function getSortKey() {
return $this->getTitle();
}
public function save() {
// NOTE: The identity hash is just a sanity check because the unique tuple
// on this table is way way too long to fit into a normal UNIQUE KEY. We
// don't use it directly, but its existence prevents duplicate records.
if (!$this->identityHash) {
$this->identityHash = PhabricatorHash::digestForIndex(
serialize(
array(
'bookPHID' => $this->getBookPHID(),
'context' => $this->getContext(),
'type' => $this->getType(),
'name' => $this->getName(),
'index' => $this->getAtomIndex(),
)));
}
return parent::save();
}
public function getTitle() {
$title = parent::getTitle();
if (!strlen($title)) {
$title = $this->getName();
}
return $title;
}
+ public function setTitle($value) {
+ $this->writeField('title', $value);
+ if (strlen($value)) {
+ $slug = DivinerAtomRef::normalizeTitleString($value);
+ $hash = PhabricatorHash::digestForIndex($slug);
+ $this->titleSlugHash = $hash;
+ } else {
+ $this->titleSlugHash = null;
+ }
+ return $this;
+ }
+
public function attachExtends(array $extends) {
assert_instances_of($extends, 'DivinerLiveSymbol');
$this->extends = $extends;
return $this;
}
public function getExtends() {
return $this->assertAttached($this->extends);
}
public function attachChildren(array $children) {
assert_instances_of($children, 'DivinerLiveSymbol');
$this->children = $children;
return $this;
}
public function getChildren() {
return $this->assertAttached($this->children);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return $this->getBook()->getCapabilities();
}
public function getPolicy($capability) {
return $this->getBook()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBook()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht('Atoms inherit the policies of the books they are part of.');
}
/* -( Markup Interface )--------------------------------------------------- */
public function getMarkupFieldKey($field) {
return $this->getPHID().':'.$field.':'.$this->getGraphHash();
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::getEngine('diviner');
}
public function getMarkupText($field) {
return $this->getAtom()->getDocblockText();
}
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return false;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Sep 7, 8:00 AM (5 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
222659
Default Alt Text
(36 KB)

Event Timeline