diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -189,9 +189,7 @@ 'ArcanistFileConfigurationSource' => 'config/source/ArcanistFileConfigurationSource.php', 'ArcanistFileDataRef' => 'upload/ArcanistFileDataRef.php', 'ArcanistFileRef' => 'ref/file/ArcanistFileRef.php', - 'ArcanistFileSymbolHardpointQuery' => 'ref/file/ArcanistFileSymbolHardpointQuery.php', 'ArcanistFileSymbolRef' => 'ref/file/ArcanistFileSymbolRef.php', - 'ArcanistFileSymbolRefInspector' => 'ref/file/ArcanistFileSymbolRefInspector.php', 'ArcanistFileUploader' => 'upload/ArcanistFileUploader.php', 'ArcanistFilenameLinter' => 'lint/linter/ArcanistFilenameLinter.php', 'ArcanistFilenameLinterTestCase' => 'lint/linter/__tests__/ArcanistFilenameLinterTestCase.php', @@ -361,6 +359,8 @@ 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParenthesesSpacingXHPASTLinterRuleTestCase.php', 'ArcanistParseStrUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParseStrUseXHPASTLinterRule.php', 'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParseStrUseXHPASTLinterRuleTestCase.php', + 'ArcanistPasteRef' => 'ref/paste/ArcanistPasteRef.php', + 'ArcanistPasteSymbolRef' => 'ref/paste/ArcanistPasteSymbolRef.php', 'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php', 'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php', 'ArcanistPhpLinter' => 'lint/linter/ArcanistPhpLinter.php', @@ -401,9 +401,7 @@ 'ArcanistRevisionCommitMessageHardpointQuery' => 'ref/revision/ArcanistRevisionCommitMessageHardpointQuery.php', 'ArcanistRevisionRef' => 'ref/revision/ArcanistRevisionRef.php', 'ArcanistRevisionRefSource' => 'ref/ArcanistRevisionRefSource.php', - 'ArcanistRevisionSymbolHardpointQuery' => 'ref/revision/ArcanistRevisionSymbolHardpointQuery.php', 'ArcanistRevisionSymbolRef' => 'ref/revision/ArcanistRevisionSymbolRef.php', - 'ArcanistRevisionSymbolRefInspector' => 'ref/revision/ArcanistRevisionSymbolRefInspector.php', 'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php', 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', @@ -424,6 +422,9 @@ 'ArcanistSetting' => 'configuration/ArcanistSetting.php', 'ArcanistSettings' => 'configuration/ArcanistSettings.php', 'ArcanistShellCompleteWorkflow' => 'toolset/workflow/ArcanistShellCompleteWorkflow.php', + 'ArcanistSimpleSymbolHardpointQuery' => 'ref/simple/ArcanistSimpleSymbolHardpointQuery.php', + 'ArcanistSimpleSymbolRef' => 'ref/simple/ArcanistSimpleSymbolRef.php', + 'ArcanistSimpleSymbolRefInspector' => 'ref/simple/ArcanistSimpleSymbolRefInspector.php', 'ArcanistSingleLintEngine' => 'lint/engine/ArcanistSingleLintEngine.php', 'ArcanistSlownessXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSlownessXHPASTLinterRule.php', 'ArcanistSlownessXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistSlownessXHPASTLinterRuleTestCase.php', @@ -439,6 +440,8 @@ 'ArcanistSymbolRef' => 'ref/symbol/ArcanistSymbolRef.php', 'ArcanistSyntaxErrorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistSyntaxErrorXHPASTLinterRule.php', 'ArcanistSystemConfigurationSource' => 'config/source/ArcanistSystemConfigurationSource.php', + 'ArcanistTaskRef' => 'ref/task/ArcanistTaskRef.php', + 'ArcanistTaskSymbolRef' => 'ref/task/ArcanistTaskSymbolRef.php', 'ArcanistTasksWorkflow' => 'workflow/ArcanistTasksWorkflow.php', 'ArcanistTautologicalExpressionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistTautologicalExpressionXHPASTLinterRule.php', 'ArcanistTautologicalExpressionXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistTautologicalExpressionXHPASTLinterRuleTestCase.php', @@ -1171,9 +1174,7 @@ 'ArcanistRef', 'ArcanistDisplayRefInterface', ), - 'ArcanistFileSymbolHardpointQuery' => 'ArcanistRuntimeHardpointQuery', - 'ArcanistFileSymbolRef' => 'ArcanistSymbolRef', - 'ArcanistFileSymbolRefInspector' => 'ArcanistRefInspector', + 'ArcanistFileSymbolRef' => 'ArcanistSimpleSymbolRef', 'ArcanistFileUploader' => 'Phobject', 'ArcanistFilenameLinter' => 'ArcanistLinter', 'ArcanistFilenameLinterTestCase' => 'ArcanistLinterTestCase', @@ -1343,7 +1344,12 @@ 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistParseStrUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', - 'ArcanistPasteWorkflow' => 'ArcanistWorkflow', + 'ArcanistPasteRef' => array( + 'ArcanistRef', + 'ArcanistDisplayRefInterface', + ), + 'ArcanistPasteSymbolRef' => 'ArcanistSimpleSymbolRef', + 'ArcanistPasteWorkflow' => 'ArcanistArcWorkflow', 'ArcanistPatchWorkflow' => 'ArcanistWorkflow', 'ArcanistPhpLinter' => 'ArcanistExternalLinter', 'ArcanistPhpLinterTestCase' => 'ArcanistExternalLinterTestCase', @@ -1386,9 +1392,7 @@ 'ArcanistDisplayRefInterface', ), 'ArcanistRevisionRefSource' => 'Phobject', - 'ArcanistRevisionSymbolHardpointQuery' => 'ArcanistRuntimeHardpointQuery', - 'ArcanistRevisionSymbolRef' => 'ArcanistSymbolRef', - 'ArcanistRevisionSymbolRefInspector' => 'ArcanistRefInspector', + 'ArcanistRevisionSymbolRef' => 'ArcanistSimpleSymbolRef', 'ArcanistRuboCopLinter' => 'ArcanistExternalLinter', 'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistRubyLinter' => 'ArcanistExternalLinter', @@ -1408,6 +1412,9 @@ 'ArcanistSetting' => 'Phobject', 'ArcanistSettings' => 'Phobject', 'ArcanistShellCompleteWorkflow' => 'ArcanistWorkflow', + 'ArcanistSimpleSymbolHardpointQuery' => 'ArcanistRuntimeHardpointQuery', + 'ArcanistSimpleSymbolRef' => 'ArcanistSymbolRef', + 'ArcanistSimpleSymbolRefInspector' => 'ArcanistRefInspector', 'ArcanistSingleLintEngine' => 'ArcanistLintEngine', 'ArcanistSlownessXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistSlownessXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', @@ -1423,6 +1430,11 @@ 'ArcanistSymbolRef' => 'ArcanistRef', 'ArcanistSyntaxErrorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistSystemConfigurationSource' => 'ArcanistFilesystemConfigurationSource', + 'ArcanistTaskRef' => array( + 'ArcanistRef', + 'ArcanistDisplayRefInterface', + ), + 'ArcanistTaskSymbolRef' => 'ArcanistSimpleSymbolRef', 'ArcanistTasksWorkflow' => 'ArcanistWorkflow', 'ArcanistTautologicalExpressionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistTautologicalExpressionXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', diff --git a/src/conduit/ConduitSearchFuture.php b/src/conduit/ConduitSearchFuture.php --- a/src/conduit/ConduitSearchFuture.php +++ b/src/conduit/ConduitSearchFuture.php @@ -6,6 +6,7 @@ private $conduitEngine; private $method; private $constraints; + private $attachments; private $objects = array(); private $cursor; @@ -28,7 +29,7 @@ return $this->method; } - public function setConstraints($constraints) { + public function setConstraints(array $constraints) { $this->constraints = $constraints; return $this; } @@ -37,6 +38,15 @@ return $this->constraints; } + public function setAttachments(array $attachments) { + $this->attachments = $attachments; + return $this; + } + + public function getAttachments() { + return $this->attachments; + } + public function isReady() { if ($this->hasResult()) { return true; @@ -86,6 +96,10 @@ 'constraints' => $constraints, ); + if ($this->attachments) { + $parameters['attachments'] = $this->attachments; + } + if ($this->cursor !== null) { $parameters['after'] = (string)$this->cursor; } diff --git a/src/inspector/ArcanistRefInspector.php b/src/inspector/ArcanistRefInspector.php --- a/src/inspector/ArcanistRefInspector.php +++ b/src/inspector/ArcanistRefInspector.php @@ -6,11 +6,24 @@ abstract public function getInspectFunctionName(); abstract public function newInspectRef(array $argv); + protected function newInspectors() { + return array($this); + } + final public static function getAllInspectors() { - return id(new PhutilClassMapQuery()) + $base_inspectors = id(new PhutilClassMapQuery()) ->setAncestorClass(__CLASS__) - ->setUniqueMethod('getInspectFunctionName') ->execute(); + + $results = array(); + + foreach ($base_inspectors as $base_inspector) { + foreach ($base_inspector->newInspectors() as $inspector) { + $results[] = $inspector; + } + } + + return mpull($results, null, 'getInspectFunctionName'); } } diff --git a/src/log/ArcanistLogEngine.php b/src/log/ArcanistLogEngine.php --- a/src/log/ArcanistLogEngine.php +++ b/src/log/ArcanistLogEngine.php @@ -92,4 +92,14 @@ ->setMessage($message)); } + public function writeWaitingForInput() { + if (!phutil_is_interactive()) { + return; + } + + $this->writeStatus( + pht('INPUT'), + pht('Waiting for input on stdin...')); + } + } diff --git a/src/ref/ArcanistDisplayRef.php b/src/ref/ArcanistDisplayRef.php --- a/src/ref/ArcanistDisplayRef.php +++ b/src/ref/ArcanistDisplayRef.php @@ -6,6 +6,7 @@ ArcanistTerminalStringInterface { private $ref; + private $uri; public function setRef(ArcanistRef $ref) { $this->ref = $ref; @@ -16,6 +17,15 @@ return $this->ref; } + public function setURI($uri) { + $this->uri = $uri; + return $this; + } + + public function getURI() { + return $this->uri; + } + public function newTerminalString() { $ref = $this->getRef(); @@ -60,9 +70,20 @@ } $ref = $this->getRef(); - return tsprintf( + $output = array(); + + $output[] = tsprintf( "** * ** %s\n", $display_text); + + $uri = $this->getURI(); + if ($uri !== null) { + $output[] = tsprintf( + "** :// ** __%s__\n", + $uri); + } + + return $output; } } diff --git a/src/ref/file/ArcanistFileSymbolRef.php b/src/ref/file/ArcanistFileSymbolRef.php --- a/src/ref/file/ArcanistFileSymbolRef.php +++ b/src/ref/file/ArcanistFileSymbolRef.php @@ -1,47 +1,30 @@ getSymbol()); } - protected function newCacheKeyParts() { - return array( - sprintf('type(%s)', $this->type), - ); + protected function getSimpleSymbolPrefixPattern() { + return '[Ff]?'; + } + + protected function getSimpleSymbolPHIDType() { + return 'FILE'; + } + + public function getSimpleSymbolConduitSearchMethodName() { + return 'file.search'; } - public function getSymbolType() { - return $this->type; + public function getSimpleSymbolInspectFunctionName() { + return 'file'; } - protected function resolveSymbol($symbol) { - $matches = null; - - $is_id = preg_match('/^[Ff]?([1-9]\d*)\z/', $symbol, $matches); - if ($is_id) { - $this->type = self::TYPE_ID; - return (int)$matches[1]; - } - - $is_phid = preg_match('/^PHID-FILE-\S+\z/', $symbol, $matches); - if ($is_phid) { - $this->type = self::TYPE_PHID; - return $matches[0]; - } - - throw new PhutilArgumentUsageException( - pht( - 'The format of file symbol "%s" is unrecognized. Expected a '. - 'monogram like "F123", or an ID like "123", or a file PHID.', - $symbol)); + public function newSimpleSymbolObjectRef() { + return new ArcanistFileRef(); } } diff --git a/src/ref/file/ArcanistFileSymbolRefInspector.php b/src/ref/file/ArcanistFileSymbolRefInspector.php deleted file mode 100644 --- a/src/ref/file/ArcanistFileSymbolRefInspector.php +++ /dev/null @@ -1,22 +0,0 @@ -setSymbol($argv[0]); - } - -} diff --git a/src/ref/paste/ArcanistPasteRef.php b/src/ref/paste/ArcanistPasteRef.php new file mode 100644 --- /dev/null +++ b/src/ref/paste/ArcanistPasteRef.php @@ -0,0 +1,52 @@ +getMonogram()); + } + + public static function newFromConduit(array $parameters) { + $ref = new self(); + $ref->parameters = $parameters; + return $ref; + } + + public function getID() { + return idx($this->parameters, 'id'); + } + + public function getPHID() { + return idx($this->parameters, 'phid'); + } + + public function getTitle() { + return idxv($this->parameters, array('fields', 'title')); + } + + public function getURI() { + return idxv($this->parameters, array('fields', 'uri')); + } + + public function getContent() { + return idxv($this->parameters, array('attachments', 'content', 'content')); + } + + public function getMonogram() { + return 'P'.$this->getID(); + } + + public function getDisplayRefObjectName() { + return $this->getMonogram(); + } + + public function getDisplayRefTitle() { + return $this->getTitle(); + } + +} diff --git a/src/ref/paste/ArcanistPasteSymbolRef.php b/src/ref/paste/ArcanistPasteSymbolRef.php new file mode 100644 --- /dev/null +++ b/src/ref/paste/ArcanistPasteSymbolRef.php @@ -0,0 +1,36 @@ +getSymbol()); + } + + protected function getSimpleSymbolPrefixPattern() { + return '[Pp]?'; + } + + protected function getSimpleSymbolPHIDType() { + return 'PSTE'; + } + + public function getSimpleSymbolConduitSearchMethodName() { + return 'paste.search'; + } + + public function getSimpleSymbolConduitSearchAttachments() { + return array( + 'content' => true, + ); + } + + public function getSimpleSymbolInspectFunctionName() { + return 'paste'; + } + + public function newSimpleSymbolObjectRef() { + return new ArcanistPasteRef(); + } + +} diff --git a/src/ref/revision/ArcanistRevisionSymbolHardpointQuery.php b/src/ref/revision/ArcanistRevisionSymbolHardpointQuery.php deleted file mode 100644 --- a/src/ref/revision/ArcanistRevisionSymbolHardpointQuery.php +++ /dev/null @@ -1,40 +0,0 @@ -yieldConduitSearch( - 'differential.revision.search', - array( - 'ids' => array_values($id_set), - ))); - - $refs = array(); - foreach ($revisions as $revision) { - $ref = ArcanistRevisionRef::newFromConduit($revision); - $refs[$ref->getID()] = $ref; - } - - $results = array(); - foreach ($id_map as $key => $id) { - $results[$key] = idx($refs, $id); - } - - yield $this->yieldMap($results); - } - -} diff --git a/src/ref/revision/ArcanistRevisionSymbolRef.php b/src/ref/revision/ArcanistRevisionSymbolRef.php --- a/src/ref/revision/ArcanistRevisionSymbolRef.php +++ b/src/ref/revision/ArcanistRevisionSymbolRef.php @@ -1,25 +1,30 @@ getSymbol()); } - protected function resolveSymbol($symbol) { - $matches = null; + protected function getSimpleSymbolPrefixPattern() { + return '[Dd]?'; + } + + protected function getSimpleSymbolPHIDType() { + return 'DREV'; + } - if (!preg_match('/^[Dd]?([1-9]\d*)\z/', $symbol, $matches)) { - throw new PhutilArgumentUsageException( - pht( - 'The format of revision symbol "%s" is unrecognized. '. - 'Expected a revision monogram like "D123", or a '. - 'revision ID like "123".', - $symbol)); - } + public function getSimpleSymbolConduitSearchMethodName() { + return 'differential.revision.search'; + } + + public function getSimpleSymbolInspectFunctionName() { + return 'revision'; + } - return (int)$matches[1]; + public function newSimpleSymbolObjectRef() { + return new ArcanistRevisionRef(); } } diff --git a/src/ref/revision/ArcanistRevisionSymbolRefInspector.php b/src/ref/revision/ArcanistRevisionSymbolRefInspector.php deleted file mode 100644 --- a/src/ref/revision/ArcanistRevisionSymbolRefInspector.php +++ /dev/null @@ -1,22 +0,0 @@ -setSymbol($argv[0]); - } - -} diff --git a/src/ref/file/ArcanistFileSymbolHardpointQuery.php b/src/ref/simple/ArcanistSimpleSymbolHardpointQuery.php rename from src/ref/file/ArcanistFileSymbolHardpointQuery.php rename to src/ref/simple/ArcanistSimpleSymbolHardpointQuery.php --- a/src/ref/file/ArcanistFileSymbolHardpointQuery.php +++ b/src/ref/simple/ArcanistSimpleSymbolHardpointQuery.php @@ -1,6 +1,6 @@ $ref) { switch ($ref->getSymbolType()) { - case ArcanistFileSymbolRef::TYPE_ID: + case ArcanistSimpleSymbolRef::TYPE_ID: $id_map[$key] = $ref->getSymbol(); break; - case ArcanistFileSymbolRef::TYPE_PHID: + case ArcanistSimpleSymbolRef::TYPE_PHID: $phid_map[$key] = $ref->getSymbol(); break; } } + $template_ref = head($refs); + + $conduit_method = + $template_ref->getSimpleSymbolConduitSearchMethodName(); + $conduit_attachments = + $template_ref->getSimpleSymbolConduitSearchAttachments(); + $futures = array(); if ($id_map) { $id_future = $this->newConduitSearch( - 'file.search', + $conduit_method, array( 'ids' => array_values(array_fuse($id_map)), - )); + ), + $conduit_attachments); $futures[] = $id_future; } else { @@ -44,10 +52,11 @@ if ($phid_map) { $phid_future = $this->newConduitSearch( - 'file.search', + $ref->getSimpleSymbolConduitSearchMethodName(), array( 'phids' => array_values(array_fuse($phid_map)), - )); + ), + $conduit_attachments); $futures[] = $phid_future; } else { @@ -76,12 +85,16 @@ } } + $object_ref = $template_ref->newSimpleSymbolObjectRef(); + foreach ($result_map as $key => $raw_result) { if ($raw_result === null) { continue; } - $result_map[$key] = ArcanistFileRef::newFromConduit($raw_result); + $result_map[$key] = call_user_func_array( + array(get_class($object_ref), 'newFromConduit'), + array($raw_result)); } yield $this->yieldMap($result_map); diff --git a/src/ref/simple/ArcanistSimpleSymbolRef.php b/src/ref/simple/ArcanistSimpleSymbolRef.php new file mode 100644 --- /dev/null +++ b/src/ref/simple/ArcanistSimpleSymbolRef.php @@ -0,0 +1,58 @@ +type), + ); + } + + final public function getSymbolType() { + return $this->type; + } + + final protected function resolveSymbol($symbol) { + $matches = null; + + $prefix_pattern = $this->getSimpleSymbolPrefixPattern(); + $id_pattern = '(^'.$prefix_pattern.'([1-9]\d*)\z)'; + + $is_id = preg_match($id_pattern, $symbol, $matches); + if ($is_id) { + $this->type = self::TYPE_ID; + return (int)$matches[1]; + } + + $phid_type = $this->getSimpleSymbolPHIDType(); + $phid_type = preg_quote($phid_type); + $phid_pattern = '(^PHID-'.$phid_type.'-\S+\z)'; + $is_phid = preg_match($phid_pattern, $symbol, $matches); + if ($is_phid) { + $this->type = self::TYPE_PHID; + return $matches[0]; + } + + throw new PhutilArgumentUsageException( + pht( + 'The format of symbol "%s" is unrecognized. Expected a '. + 'monogram like "X123", or an ID like "123", or a PHID.', + $symbol)); + } + + abstract protected function getSimpleSymbolPrefixPattern(); + abstract protected function getSimpleSymbolPHIDType(); + abstract public function getSimpleSymbolConduitSearchMethodName(); + abstract public function getSimpleSymbolInspectFunctionName(); + + public function getSimpleSymbolConduitSearchAttachments() { + return array(); + } + +} diff --git a/src/ref/simple/ArcanistSimpleSymbolRefInspector.php b/src/ref/simple/ArcanistSimpleSymbolRefInspector.php new file mode 100644 --- /dev/null +++ b/src/ref/simple/ArcanistSimpleSymbolRefInspector.php @@ -0,0 +1,47 @@ +setAncestorClass('ArcanistSimpleSymbolRef') + ->execute(); + + $inspectors = array(); + foreach ($refs as $ref) { + $inspectors[] = id(new self()) + ->setTemplateRef($ref); + } + + return $inspectors; + } + + public function setTemplateRef(ArcanistSimpleSymbolRef $template_ref) { + $this->templateRef = $template_ref; + return $this; + } + + public function getTemplateRef() { + return $this->templateRef; + } + + public function getInspectFunctionName() { + return $this->getTemplateRef()->getSimpleSymbolInspectFunctionName(); + } + + public function newInspectRef(array $argv) { + if (count($argv) !== 1) { + throw new PhutilArgumentUsageException( + pht( + 'Expected exactly one argument to "%s(...)" with a symbol.', + $this->getInspectFunctionName())); + } + + return id(clone $this->getTemplateRef()) + ->setSymbol($argv[0]); + } + +} diff --git a/src/ref/symbol/ArcanistSymbolEngine.php b/src/ref/symbol/ArcanistSymbolEngine.php --- a/src/ref/symbol/ArcanistSymbolEngine.php +++ b/src/ref/symbol/ArcanistSymbolEngine.php @@ -59,6 +59,17 @@ $symbols); } + public function loadPasteForSymbol($symbol) { + $refs = $this->loadPastesForSymbols(array($symbol)); + return head($refs)->getObject(); + } + + public function loadPastesForSymbols(array $symbols) { + return $this->loadRefsForSymbols( + new ArcanistPasteSymbolRef(), + $symbols); + } + public function loadRefsForSymbols( ArcanistSymbolRef $template, array $symbols) { diff --git a/src/ref/task/ArcanistTaskRef.php b/src/ref/task/ArcanistTaskRef.php new file mode 100644 --- /dev/null +++ b/src/ref/task/ArcanistTaskRef.php @@ -0,0 +1,44 @@ +getMonogram()); + } + + public static function newFromConduit(array $parameters) { + $ref = new self(); + $ref->parameters = $parameters; + return $ref; + } + + public function getID() { + return idx($this->parameters, 'id'); + } + + public function getPHID() { + return idx($this->parameters, 'phid'); + } + + public function getName() { + return idxv($this->parameters, array('fields', 'name')); + } + + public function getMonogram() { + return 'T'.$this->getID(); + } + + public function getDisplayRefObjectName() { + return $this->getMonogram(); + } + + public function getDisplayRefTitle() { + return $this->getName(); + } + +} diff --git a/src/ref/task/ArcanistTaskSymbolRef.php b/src/ref/task/ArcanistTaskSymbolRef.php new file mode 100644 --- /dev/null +++ b/src/ref/task/ArcanistTaskSymbolRef.php @@ -0,0 +1,30 @@ +getSymbol()); + } + + protected function getSimpleSymbolPrefixPattern() { + return '[Tt]?'; + } + + protected function getSimpleSymbolPHIDType() { + return 'TASK'; + } + + public function getSimpleSymbolConduitSearchMethodName() { + return 'maniphest.search'; + } + + public function getSimpleSymbolInspectFunctionName() { + return 'task'; + } + + public function newSimpleSymbolObjectRef() { + return new ArcanistTaskRef(); + } + +} diff --git a/src/toolset/query/ArcanistRuntimeHardpointQuery.php b/src/toolset/query/ArcanistRuntimeHardpointQuery.php --- a/src/toolset/query/ArcanistRuntimeHardpointQuery.php +++ b/src/toolset/query/ArcanistRuntimeHardpointQuery.php @@ -51,14 +51,19 @@ abstract protected function canLoadRef(ArcanistRef $ref); - final public function newConduitSearch($method, $constraints) { + final public function newConduitSearch( + $method, + $constraints, + $attachments = array()) { + $conduit_engine = $this->getRuntime() ->getConduitEngine(); $conduit_future = id(new ConduitSearchFuture()) ->setConduitEngine($conduit_engine) ->setMethod($method) - ->setConstraints($constraints); + ->setConstraints($constraints) + ->setAttachments($attachments); return $conduit_future; } diff --git a/src/workflow/ArcanistCallConduitWorkflow.php b/src/workflow/ArcanistCallConduitWorkflow.php --- a/src/workflow/ArcanistCallConduitWorkflow.php +++ b/src/workflow/ArcanistCallConduitWorkflow.php @@ -44,13 +44,7 @@ } $method = head($method); - if (phutil_is_interactive()) { - echo tsprintf( - "%s\n", - pht('Waiting for JSON parameters on stdin...')); - } - - $params = @file_get_contents('php://stdin'); + $params = $this->readStdin(); try { $params = phutil_json_decode($params); } catch (PhutilJSONParserException $ex) { diff --git a/src/workflow/ArcanistPasteWorkflow.php b/src/workflow/ArcanistPasteWorkflow.php --- a/src/workflow/ArcanistPasteWorkflow.php +++ b/src/workflow/ArcanistPasteWorkflow.php @@ -1,145 +1,137 @@ newWorkflowInformation() + ->addExample('**paste** [__options__] --') + ->addExample('**paste** [__options__] -- __object__') + ->setHelp($help); } - public function getArguments() { + public function getWorkflowArguments() { return array( - 'title' => array( - 'param' => 'title', - 'help' => pht('Title for the paste.'), - ), - 'lang' => array( - 'param' => 'language', - 'help' => pht('Language for syntax highlighting.'), - ), - 'json' => array( - 'help' => pht('Output in JSON format.'), - ), - '*' => 'argv', + $this->newWorkflowArgument('title') + ->setParameter('title') + ->setHelp(pht('Title for the paste.')), + $this->newWorkflowArgument('lang') + ->setParameter('language') + ->setHelp(pht('Language for the paste.')), + $this->newWorkflowArgument('json') + ->setHelp(pht('Output in JSON format.')), + $this->newWorkflowArgument('argv') + ->setWildcard(true), ); } - public function requiresAuthentication() { - return true; - } - - protected function didParseArguments() { - $this->json = $this->getArgument('json'); - $this->language = $this->getArgument('lang'); - $this->title = $this->getArgument('title'); + public function runWorkflow() { + $set_language = $this->getArgument('lang'); + $set_title = $this->getArgument('title'); $argv = $this->getArgument('argv'); if (count($argv) > 1) { - throw new ArcanistUsageException( - pht('Specify only one paste to retrieve.')); - } else if (count($argv) == 1) { - $id = $argv[0]; - if (!preg_match('/^P?\d+/', $id)) { - throw new ArcanistUsageException( + throw new PhutilArgumentUsageException( + pht( + 'Specify only one paste to retrieve.')); + } + + $symbols = $this->getSymbolEngine(); + + if (count($argv) === 1) { + if ($set_language !== null) { + throw new PhutilArgumentUsageException( pht( - 'Specify a paste ID, like %s.', - 'P123')); + 'Flag "--lang" is not supported when reading pastes.')); } - $this->id = (int)ltrim($id, 'P'); - if ($this->language || $this->title) { - throw new ArcanistUsageException( + if ($set_title !== null) { + throw new PhutilArgumentUsageException( pht( - 'Use options %s and %s only when creating pastes.', - '--lang', - '--title')); + 'Flag "--title" is not supported when reading pastes.')); } - } - } - public function run() { - if ($this->id) { - return $this->getPaste(); - } else { - return $this->createPaste(); - } - } + $paste_symbol = $argv[0]; - private function getPaste() { - $conduit = $this->getConduit(); - - $info = $conduit->callMethodSynchronous( - 'paste.query', - array( - 'ids' => array($this->id), - )); - $info = head($info); - - if ($this->json) { - echo json_encode($info)."\n"; - } else { - echo $info['content']; - if (!preg_match('/\\n$/', $info['content'])) { - // If there's no newline, add one, since it looks stupid otherwise. If - // you want byte-for-byte equivalence you can use `--json`. - echo "\n"; + $paste_ref = $symbols->loadPasteForSymbol($paste_symbol); + if (!$paste_ref) { + throw new PhutilArgumentUsageException( + pht( + 'Paste "%s" does not exist, or you do not have access '. + 'to see it.')); } + + echo $paste_ref->getContent(); + + return 0; } - return 0; - } + $content = $this->readStdin(); - private function createPaste() { - $conduit = $this->getConduit(); + $xactions = array(); - if (!function_exists('posix_isatty') || posix_isatty(STDIN)) { - $this->writeStatusMessage(pht('Reading paste from stdin...')."\n"); + if ($set_title === null) { + $set_title = pht('Command-Line Input'); } - $info = $conduit->callMethodSynchronous( - 'paste.create', - array( - 'content' => file_get_contents('php://stdin'), - 'title' => $this->title, - 'language' => $this->language, - )); - - if ($this->getArgument('json')) { - echo json_encode($info)."\n"; - } else { - echo $info['objectName'].': '.$info['uri']."\n"; + $xactions[] = array( + 'type' => 'title', + 'value' => $set_title, + ); + + if ($set_language !== null) { + $xactions[] = array( + 'type' => 'language', + 'value' => $set_language, + ); } + $xactions[] = array( + 'type' => 'text', + 'value' => $content, + ); + + $method = 'paste.edit'; + + $parameters = array( + 'transactions' => $xactions, + ); + + $conduit_engine = $this->getConduitEngine(); + $conduit_call = $conduit_engine->newCall($method, $parameters); + $conduit_future = $conduit_engine->newFuture($conduit_call); + $result = $conduit_future->resolve(); + + $paste_phid = idxv($result, array('object', 'phid')); + $paste_ref = $symbols->loadPasteForSymbol($paste_phid); + + $log = $this->getLogEngine(); + + $log->writeSuccess( + pht('DONE'), + pht('Created a new paste.')); + + echo tsprintf( + '%s', + $paste_ref->newDisplayRef() + ->setURI($paste_ref->getURI())); + return 0; } diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php --- a/src/workflow/ArcanistWorkflow.php +++ b/src/workflow/ArcanistWorkflow.php @@ -52,7 +52,6 @@ private $repositoryAPI; private $configurationManager; private $arguments = array(); - private $passedArguments = array(); private $command; private $stashed; @@ -777,10 +776,6 @@ return $this->arguments->getArg($key); } - final public function getPassedArguments() { - return $this->passedArguments; - } - final public function getCompleteArgumentSpecification() { $spec = $this->getArguments(); $arc_config = $this->getArcanistConfiguration(); @@ -791,8 +786,6 @@ } final public function parseArguments(array $args) { - $this->passedArguments = $args; - $spec = $this->getCompleteArgumentSpecification(); $dict = array(); @@ -2411,4 +2404,21 @@ return $this->getRuntime()->getViewer(); } + final protected function readStdin() { + $log = $this->getLogEngine(); + $log->writeWaitingForInput(); + + // NOTE: We can't just "file_get_contents()" here because signals don't + // interrupt it. If the user types "^C", we want to interrupt the read. + + $raw_handle = fopen('php://stdin', 'rb'); + $stdin = new PhutilSocketChannel($raw_handle); + + while ($stdin->update()) { + PhutilChannel::waitForAny(array($stdin)); + } + + return $stdin->read(); + } + }