public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $phid = head($request->getArr('macro')); $above = $request->getStr('above'); $below = $request->getStr('below'); $e_macro = true; $errors = array(); if ($request->isDialogFormPost()) { if (!$phid) { $e_macro = pht('Required'); $errors[] = pht('Macro name is required.'); } else { $macro = id(new PhabricatorMacroQuery())->setViewer($user)->withPHIDs(array($phid))->executeOne(); if (!$macro) { $e_macro = pht('Invalid'); $errors[] = pht('No such macro.'); } } if (!$errors) { $options = new PhutilSimpleOptions(); $data = array('src' => $macro->getName(), 'above' => $above, 'below' => $below); $string = $options->unparse($data, $escape = '}'); $result = array('text' => "{meme, {$string}}"); return id(new AphrontAjaxResponse())->setContent($result); } } $view = id(new AphrontFormView())->setUser($user)->appendControl(id(new AphrontFormTokenizerControl())->setLabel(pht('Macro'))->setName('macro')->setLimit(1)->setDatasource(new PhabricatorMacroDatasource())->setError($e_macro))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Above'))->setName('above')->setValue($above))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Below'))->setName('below')->setValue($below)); $dialog = id(new AphrontDialogView())->setUser($user)->setTitle(pht('Create Meme'))->appendForm($view)->addCancelButton('/')->addSubmitButton(pht('Llama Diorama')); return id(new AphrontDialogResponse())->setDialog($dialog); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $resource_type = $args->getArg('type'); if (!$resource_type) { throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type')); } $until = $args->getArg('until'); if (strlen($until)) { $until = strtotime($until); if ($until <= 0) { throw new PhutilArgumentUsageException(pht('Unable to parse argument to "%s".', '--until')); } } $attributes = $args->getArg('attributes'); if ($attributes) { $options = new PhutilSimpleOptions(); $options->setCaseSensitive(true); $attributes = $options->parse($attributes); } $lease = id(new DrydockLease())->setResourceType($resource_type); if ($attributes) { $lease->setAttributes($attributes); } if ($until) { $lease->setUntil($until); } $lease->queueForActivation(); echo tsprintf("%s\n", pht('Waiting for daemons to activate lease...')); $lease->waitUntilActive(); echo tsprintf("%s\n", pht('Activated lease "%s".', $lease->getID())); return 0; }
protected function renderObjectEmbed($object, $handle, $options) { $embed_paste = id(new PasteEmbedView())->setPaste($object)->setHandle($handle); if (strlen($options)) { $parser = new PhutilSimpleOptions(); $opts = $parser->parse(substr($options, 1)); foreach ($opts as $key => $value) { if ($key == 'lines') { $embed_paste->setLines(preg_replace('/[^0-9]/', '', $value)); } else { if ($key == 'highlight') { $highlights = preg_split('/,|&/', preg_replace('/\\s+/', '', $value)); $to_highlight = array(); foreach ($highlights as $highlight) { $highlight = explode('-', $highlight); if (!empty($highlight)) { sort($highlight); $to_highlight = array_merge($to_highlight, range(head($highlight), last($highlight))); } } $embed_paste->setHighlights(array_unique($to_highlight)); } } } } return $embed_paste; }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $resource_name = $args->getArg('name'); if (!$resource_name) { throw new PhutilArgumentUsageException('Specify a resource name with `--name`.'); } $blueprint_id = $args->getArg('blueprint'); if (!$blueprint_id) { throw new PhutilArgumentUsageException('Specify a blueprint ID with `--blueprint`.'); } $attributes = $args->getArg('attributes'); if ($attributes) { $options = new PhutilSimpleOptions(); $options->setCaseSensitive(true); $attributes = $options->parse($attributes); } $viewer = $this->getViewer(); $blueprint = id(new DrydockBlueprintQuery())->setViewer($viewer)->withIDs(array($blueprint_id))->executeOne(); if (!$blueprint) { throw new PhutilArgumentUsageException('Specified blueprint does not exist.'); } $resource = id(new DrydockResource())->setBlueprintPHID($blueprint->getPHID())->setType($blueprint->getImplementation()->getType())->setName($resource_name)->setStatus(DrydockResourceStatus::STATUS_OPEN); if ($attributes) { $resource->setAttributes($attributes); } $resource->save(); $console->writeOut("Created Resource %s\n", $resource->getID()); return 0; }
public function markupNavigation(array $matches) { if (!$this->isFlatText($matches[0])) { return $matches[0]; } $elements = ltrim($matches[1], ", \n"); $elements = explode('>', $elements); $defaults = array('name' => null, 'type' => 'link', 'href' => null, 'icon' => null); $sequence = array(); $parser = new PhutilSimpleOptions(); foreach ($elements as $element) { if (strpos($element, '=') === false) { $sequence[] = array('name' => trim($element)) + $defaults; } else { $sequence[] = $parser->parse($element) + $defaults; } } if ($this->getEngine()->isTextMode()) { return implode(' > ', ipull($sequence, 'name')); } static $icon_names; if (!$icon_names) { $icon_names = array_fuse(PHUIIconView::getIcons()); } $out = array(); foreach ($sequence as $item) { $item_name = $item['name']; $item_color = PHUITagView::COLOR_GREY; if ($item['type'] == 'instructions') { $item_name = phutil_tag('em', array(), $item_name); $item_color = PHUITagView::COLOR_INDIGO; } $tag = id(new PHUITagView())->setType(PHUITagView::TYPE_SHADE)->setShade($item_color)->setName($item_name); if ($item['icon']) { $icon_name = 'fa-' . $item['icon']; if (isset($icon_names[$icon_name])) { $tag->setIcon($icon_name); } } if ($item['href'] !== null) { if (PhabricatorEnv::isValidRemoteURIForLink($item['href'])) { $tag->setHref($item['href']); $tag->setExternal(true); } } $out[] = $tag; } if ($this->getEngine()->isHTMLMailMode()) { $arrow_attr = array('style' => 'color: #92969D;'); $nav_attr = array(); } else { $arrow_attr = array('class' => 'remarkup-nav-sequence-arrow'); $nav_attr = array('class' => 'remarkup-nav-sequence'); } $joiner = phutil_tag('span', $arrow_attr, " → "); $out = phutil_implode_html($joiner, $out); $out = phutil_tag('span', $nav_attr, $out); return $this->getEngine()->storeText($out); }
private function getFileOptions($option_string) { $options = array('size' => null, 'layout' => 'left', 'float' => false, 'width' => null, 'height' => null, 'alt' => null); if ($option_string) { $option_string = trim($option_string, ', '); $parser = new PhutilSimpleOptions(); $options = $parser->parse($option_string) + $options; } return $options; }
public function markupIcon(array $matches) { $engine = $this->getEngine(); $text_mode = $engine->isTextMode(); $mail_mode = $engine->isHTMLMailMode(); if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) { return $matches[0]; } $extra = idx($matches, 1); // We allow various forms, like these: // // {icon} // {icon camera} // {icon,camera} // {icon camera color=red} // {icon, camera, color=red} $extra = ltrim($extra, ", \n"); $extra = preg_split('/[\\s,]+/', $extra, 2); // Choose some arbitrary default icon so that previews render in a mostly // reasonable way as you're typing the syntax. $icon = idx($extra, 0, 'paw'); $defaults = array('color' => null, 'spin' => false); $options = idx($extra, 1, ''); $parser = new PhutilSimpleOptions(); $options = $parser->parse($options) + $defaults; // NOTE: We're validating icon and color names to prevent users from // adding arbitrary CSS classes to the document. Although this probably // isn't dangerous, it's safer to validate. static $icon_names; if (!$icon_names) { $icon_names = array_fuse(PHUIIconView::getFontIcons()); } static $color_names; if (!$color_names) { $color_names = array_fuse(PHUIIconView::getFontIconColors()); } if (empty($icon_names['fa-' . $icon])) { $icon = 'paw'; } $color = $options['color']; if (empty($color_names[$color])) { $color = null; } $classes = array(); $classes[] = $color; $spin = $options['spin']; if ($spin) { $classes[] = 'ph-spin'; } $icon_view = id(new PHUIIconView())->setIconFont('fa-' . $icon, implode(' ', $classes)); return $this->getEngine()->storeText($icon_view); }
protected function renderObjectEmbed($object, $handle, $options) { $embed_mock = id(new PholioMockEmbedView())->setMock($object); if (strlen($options)) { $parser = new PhutilSimpleOptions(); $opts = $parser->parse(substr($options, 1)); if (isset($opts['image'])) { $images = array_unique(explode('&', preg_replace('/\\s+/', '', $opts['image']))); $embed_mock->setImages($images); } } return $embed_mock->render(); }
protected final function applyRules($input, &$count, array $rules) { if (++$count > $this->limit) { throw new Exception("Token replacement count exceeded limit!"); } $matches = null; preg_match_all('/(\\[[^\\]]+\\])/', $input, $matches, PREG_OFFSET_CAPTURE); foreach (array_reverse($matches[1]) as $token_spec) { list($token, $offset) = $token_spec; $token_name = substr($token, 1, -1); $options = array(); if ($name_end = strpos($token_name, ',')) { $options_parser = new PhutilSimpleOptions(); $options = $options_parser->parse($token_name); $token_name = substr($token_name, 0, $name_end); } if (empty($rules[$token_name])) { throw new Exception("Invalid token '{$token_name}' in grammar."); } $key = array_rand($rules[$token_name]); $replacement = $this->applyRules($rules[$token_name][$key], $count, $rules); if (isset($options['indent'])) { if (is_numeric($options['indent'])) { $replacement = self::strPadLines($replacement, $options['indent']); } else { $replacement = self::strPadLines($replacement); } } if (isset($options['trim'])) { switch ($options['trim']) { case 'left': $replacement = ltrim($replacement); break; case 'right': $replacement = rtrim($replacement); break; default: case 'both': $replacement = trim($replacement); break; } } if (isset($options['block'])) { $replacement = "\n" . $replacement . "\n"; } $input = substr_replace($input, $replacement, $offset, strlen($token)); } return $input; }
public function markupMeme($matches) { if (!$this->isFlatText($matches[0])) { return $matches[0]; } $options = array('src' => null, 'above' => null, 'below' => null); $parser = new PhutilSimpleOptions(); $options = $parser->parse($matches[1]) + $options; $uri = id(new PhutilURI('/macro/meme/'))->alter('macro', $options['src'])->alter('uppertext', $options['above'])->alter('lowertext', $options['below']); if ($this->getEngine()->isTextMode()) { $img = ($options['above'] != '' ? "\"{$options['above']}\"\n" : '') . $options['src'] . ' <' . PhabricatorEnv::getProductionURI($uri) . '>' . ($options['below'] != '' ? "\n\"{$options['below']}\"" : ''); } else { $alt_text = pht('Macro %s: %s %s', $options['src'], $options['above'], $options['below']); $img = $this->newTag('img', array('src' => $uri, 'alt' => $alt_text)); } return $this->getEngine()->storeText($img); }
public function testTransformation() { $files = dirname(__FILE__) . '/transformer/'; foreach (Filesystem::listDirectory($files) as $file) { $name = basename($file); $data = Filesystem::readFile($files . '/' . $file); $parts = preg_split('/^~~~+\\n/m', $data); $parts = array_merge($parts, array(null)); list($options, $in, $expect) = $parts; $parser = new PhutilSimpleOptions(); $options = $parser->parse($options) + array('minify' => false, 'name' => $name); $xformer = new CelerityResourceTransformer(); $xformer->setRawURIMap(array('/rsrc/example.png' => '/res/hash/example.png')); $xformer->setMinify($options['minify']); $result = $xformer->transformResource($options['name'], $in); $this->assertEqual(rtrim($expect), rtrim($result), $file); } }
public function execute(PhutilArgumentParser $args) { $viewer = $this->getViewer(); $resource_type = $args->getArg('type'); if (!$resource_type) { throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type')); } $until = $args->getArg('until'); if (strlen($until)) { $until = strtotime($until); if ($until <= 0) { throw new PhutilArgumentUsageException(pht('Unable to parse argument to "%s".', '--until')); } } $attributes = $args->getArg('attributes'); if ($attributes) { $options = new PhutilSimpleOptions(); $options->setCaseSensitive(true); $attributes = $options->parse($attributes); } $lease = id(new DrydockLease())->setResourceType($resource_type); $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID(); $lease->setAuthorizingPHID($drydock_phid); // TODO: This is not hugely scalable, although this is a debugging workflow // so maybe it's fine. Do we even need `bin/drydock lease` in the long run? $all_blueprints = id(new DrydockBlueprintQuery())->setViewer($viewer)->execute(); $allowed_phids = mpull($all_blueprints, 'getPHID'); if (!$allowed_phids) { throw new Exception(pht('No blueprints exist which can plausibly allocate resources to ' . 'satisfy the requested lease.')); } $lease->setAllowedBlueprintPHIDs($allowed_phids); if ($attributes) { $lease->setAttributes($attributes); } if ($until) { $lease->setUntil($until); } $lease->queueForActivation(); echo tsprintf("%s\n", pht('Waiting for daemons to activate lease...')); $lease->waitUntilActive(); echo tsprintf("%s\n", pht('Activated lease "%s".', $lease->getID())); return 0; }
public function testSimpleOptionsUnparse() { $map = array('' => array(), 'legs=4' => array('legs' => '4'), 'legs=4, eyes=2' => array('legs' => '4', 'eyes' => '2'), 'eyes=2, legs=4' => array('eyes' => '2', 'legs' => '4'), 'legs=4, head' => array('legs' => '4', 'head' => true), 'eyes=2' => array('legs' => '', 'eyes' => '2'), '"thousands separator"=","' => array('thousands separator' => ',')); foreach ($map as $expect => $dict) { $parser = new PhutilSimpleOptions(); $this->assertEqual($expect, $parser->unparse($dict), "Correct unparse of " . print_r($dict, true)); } $bogus = array(array('' => ''), array('' => 'x')); foreach ($bogus as $bad_input) { $caught = null; try { $parser = new PhutilSimpleOptions(); $parser->unparse($bad_input); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue($caught instanceof Exception, "Correct throw on unparse of bad input."); } $parser = new PhutilSimpleOptions(); $this->assertEqual('a="\\}"', $parser->unparse(array('a' => '}'), '}'), "Unparse with extra escape."); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $resource_type = $args->getArg('type'); if (!$resource_type) { throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type')); } $attributes = $args->getArg('attributes'); if ($attributes) { $options = new PhutilSimpleOptions(); $options->setCaseSensitive(true); $attributes = $options->parse($attributes); } $lease = id(new DrydockLease())->setResourceType($resource_type); if ($attributes) { $lease->setAttributes($attributes); } $lease->queueForActivation()->waitUntilActive(); $console->writeOut("%s\n", pht('Acquired Lease %s', $lease->getID())); return 0; }
public function markupImage(array $matches) { if (!$this->isFlatText($matches[0])) { return $matches[0]; } $args = array(); $defaults = array('uri' => null, 'alt' => null, 'href' => null, 'width' => null, 'height' => null); $trimmed_match = trim($matches[2]); if ($this->isURI($trimmed_match)) { $args['uri'] = new PhutilURI($trimmed_match); } else { $parser = new PhutilSimpleOptions(); $keys = $parser->parse($trimmed_match); $uri_key = ''; foreach (array('src', 'uri', 'url') as $key) { if (array_key_exists($key, $keys)) { $uri_key = $key; } } if ($uri_key) { $args['uri'] = new PhutilURI($keys[$uri_key]); } $args += $keys; } $args += $defaults; if ($args['href'] && !PhabricatorEnv::isValidURIForLink($args['href'])) { $args['href'] = null; } if ($args['uri']) { $src_uri = id(new PhutilURI('/file/imageproxy/'))->setQueryParam('uri', (string) $args['uri']); $img = $this->newTag('img', array('src' => $src_uri, 'alt' => $args['alt'], 'href' => $args['href'], 'width' => $args['width'], 'height' => $args['height'])); return $this->getEngine()->storeText($img); } else { return $matches[0]; } }
public function testSimpleOptionsUnparse() { $map = array('' => array(), 'legs=4' => array('legs' => '4'), 'legs=4, eyes=2' => array('legs' => '4', 'eyes' => '2'), 'eyes=2, legs=4' => array('eyes' => '2', 'legs' => '4'), 'legs=4, head' => array('legs' => '4', 'head' => true), 'eyes=2' => array('legs' => '', 'eyes' => '2')); foreach ($map as $expect => $dict) { $this->assertEqual($expect, PhutilSimpleOptions::unparse($dict), "Correct unparse of " . print_r($dict, true)); } $bogus = array(array('LEGS' => true), array('LEGS' => 4), array('!' => '!'), array('' => '2')); foreach ($bogus as $bad_input) { $caught = null; try { PhutilSimpleOptions::unparse($bad_input); } catch (Exception $ex) { $caught = $ex; } $this->assertEqual(true, $caught instanceof Exception, "Correct throw on unparse of bad input."); } }
public function markupText($text, $children) { if (preg_match('/^\\s*```/', $text)) { // If this is a ```-style block, trim off the backticks and any leading // blank line. $text = preg_replace('/^\\s*```(\\s*\\n)?/', '', $text); $text = preg_replace('/```\\s*$/', '', $text); } $lines = explode("\n", $text); while ($lines && !strlen(last($lines))) { unset($lines[last_key($lines)]); } $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null); $parser = new PhutilSimpleOptions(); $custom = $parser->parse(head($lines)); if ($custom) { $valid = true; foreach ($custom as $key => $value) { if (!array_key_exists($key, $options)) { $valid = false; break; } } if ($valid) { array_shift($lines); $options = $custom + $options; } } // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { for ($ii = 0; $ii < strlen($line); $ii++) { if ($line[$ii] != ' ') { $min_indent = min($ii, $min_indent); break; } } } $text = implode("\n", $lines); if ($min_indent) { $indent_string = str_repeat(' ', $min_indent); $text = preg_replace('/^' . $indent_string . '/m', '', $text); } if ($this->getEngine()->isTextMode()) { $out = array(); $header = array(); if ($options['counterexample']) { $header[] = 'counterexample'; } if ($options['name'] != '') { $header[] = 'name=' . $options['name']; } if ($header) { $out[] = implode(', ', $header); } $text = preg_replace('/^/m', ' ', $text); $out[] = $text; return implode("\n", $out); } if (empty($options['lang'])) { // If the user hasn't specified "lang=..." explicitly, try to guess the // language. If we fail, fall back to configured defaults. $lang = PhutilLanguageGuesser::guessLanguage($text); if (!$lang) { $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'text'); } $options['lang'] = $lang; } $code_body = $this->highlightSource($text, $options); $name_header = null; if ($this->getEngine()->isHTMLMailMode()) { $header_attributes = array('style' => 'padding: 6px 8px; background: #fdf5d4; color: rgba(0,0,0,.75); font-weight: bold; display: inline-block; border-top: 1px solid #f1c40f; border-left: 1px solid #f1c40f; border-right: 1px solid #f1c40f; margin-bottom: -1px;'); } else { $header_attributes = array('class' => 'remarkup-code-header'); } if ($options['name']) { $name_header = phutil_tag('div', $header_attributes, $options['name']); } $class = 'remarkup-code-block'; if ($options['counterexample']) { $class = 'remarkup-code-block code-block-counterexample'; } $attributes = array('class' => $class, 'data-code-lang' => $options['lang'], 'data-sigil' => 'remarkup-code-block'); return phutil_tag('div', $attributes, array($name_header, $code_body)); }
public function markupText($text) { if (preg_match('/^```/', $text)) { // If this is a ```-style block, trim off the backticks. $text = preg_replace('/```\\s*$/', '', substr($text, 3)); } $lines = explode("\n", $text); $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null); $parser = new PhutilSimpleOptions(); $custom = $parser->parse(head($lines)); if ($custom) { $valid = true; foreach ($custom as $key => $value) { if (!array_key_exists($key, $options)) { $valid = false; break; } } if ($valid) { array_shift($lines); $options = $custom + $options; } } // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { for ($ii = 0; $ii < strlen($line); $ii++) { if ($line[$ii] != ' ') { $min_indent = min($ii, $min_indent); break; } } } $text = implode("\n", $lines); if ($min_indent) { $indent_string = str_repeat(' ', $min_indent); $text = preg_replace('/^' . $indent_string . '/m', '', $text); } if ($this->getEngine()->isTextMode()) { $out = array(); $header = array(); if ($options['counterexample']) { $header[] = 'counterexample'; } if ($options['name'] != '') { $header[] = 'name=' . $options['name']; } if ($header) { $out[] = implode(', ', $header); } $text = preg_replace('/^/m', ' ', $text); $text = preg_replace('/^\\s+$/m', '', $text); $out[] = $text; return implode("\n", $out); } if (empty($options['lang'])) { // If the user hasn't specified "lang=..." explicitly, try to guess the // language. If we fail, fall back to configured defaults. $lang = PhutilLanguageGuesser::guessLanguage($text); if (!$lang) { $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'php'); } $options['lang'] = $lang; } $code_body = $this->highlightSource($text, $options); $name_header = null; if ($options['name']) { $name_header = phutil_tag('div', array('class' => 'remarkup-code-header'), $options['name']); } return phutil_tag('div', array('class' => 'remarkup-code-block', 'data-code-lang' => $options['lang']), array($name_header, $code_body)); }
public function markupEmbedFile($matches) { $file = null; if ($matches[1]) { // TODO: This is pretty inefficient if there are a bunch of files. $file = id(new PhabricatorFile())->load($matches[1]); } if (!$file) { return $matches[0]; } $options = array('size' => 'thumb', 'layout' => 'left', 'float' => false, 'name' => null); if (!empty($matches[2])) { $matches[2] = trim($matches[2], ', '); $options = PhutilSimpleOptions::parse($matches[2]) + $options; } $file_name = coalesce($options['name'], $file->getName()); if (!$file->isViewableImage() || $options['layout'] == 'link') { // If a file isn't in image, just render a link to it. $link = phutil_render_tag('a', array('href' => $file->getBestURI(), 'target' => '_blank', 'class' => 'phabricator-remarkup-embed-layout-link'), phutil_escape_html($file_name)); return $this->getEngine()->storeText($link); } $attrs = array(); switch ($options['size']) { case 'full': $attrs['src'] = $file->getBestURI(); $link = null; break; case 'thumb': default: $attrs['src'] = $file->getPreview220URI(); $link = $file->getBestURI(); break; } $embed = phutil_render_tag('img', $attrs); if ($link) { $embed = phutil_render_tag('a', array('href' => $link, 'class' => 'phabricator-remarkup-embed-image', 'target' => '_blank'), $embed); } $layout_class = null; switch ($options['layout']) { case 'right': case 'center': case 'inline': case 'left': $layout_class = 'phabricator-remarkup-embed-layout-' . $options['layout']; break; default: $layout_class = 'phabricator-remarkup-embed-layout-left'; break; } if ($options['float']) { switch ($options['layout']) { case 'center': case 'inline': break; case 'right': $layout_class .= ' phabricator-remarkup-embed-float-right'; break; case 'left': default: $layout_class .= ' phabricator-remarkup-embed-float-left'; break; } } if ($layout_class) { $embed = phutil_render_tag('div', array('class' => $layout_class), $embed); } return $this->getEngine()->storeText($embed); }
public function markupText($text) { if (preg_match('/^```/', $text)) { // If this is a ```-style block, trim off the backticks. $text = preg_replace('/```\\s*$/', '', substr($text, 3)); } $lines = explode("\n", $text); $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null); $custom = PhutilSimpleOptions::parse(head($lines)); if ($custom) { $valid = true; foreach ($custom as $key => $value) { if (!array_key_exists($key, $options)) { $valid = false; break; } } if ($valid) { array_shift($lines); $options = $custom + $options; } } if ($options['counterexample']) { $aux_class = ' remarkup-counterexample'; } else { $aux_class = null; } // Normalize the text back to a 0-level indent. $min_indent = 80; foreach ($lines as $line) { for ($ii = 0; $ii < strlen($line); $ii++) { if ($line[$ii] != ' ') { $min_indent = min($ii, $min_indent); break; } } } if ($min_indent) { $indent_string = str_repeat(' ', $min_indent); $text = preg_replace('/^' . $indent_string . '/m', '', implode("\n", $lines)); } else { $text = implode("\n", $lines); } if (empty($options['lang'])) { // If the user hasn't specified "lang=..." explicitly, try to guess the // language. If we fail, fall back to configured defaults. $lang = PhutilLanguageGuesser::guessLanguage($text); if (!$lang) { $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'php'); } $options['lang'] = $lang; } $name_header = null; if ($options['name']) { $name_header = phutil_render_tag('div', array('class' => 'remarkup-code-header'), phutil_escape_html($options['name'])); } $aux_style = null; if ($options['lines']) { // Put a minimum size on this because the scrollbar is otherwise // unusable. $height = max(6, (int) $options['lines']); $aux_style = 'max-height: ' . 2 * $height . 'em;'; } $engine = $this->getEngine()->getConfig('syntax-highlighter.engine'); if (!$engine) { $engine = 'PhutilDefaultSyntaxHighlighterEngine'; } $engine = newv($engine, array()); $engine->setConfig('pygments.enabled', $this->getEngine()->getConfig('pygments.enabled')); $code_body = phutil_render_tag('pre', array('class' => 'remarkup-code' . $aux_class, 'style' => $aux_style), $engine->highlightSource($options['lang'], $text)); return phutil_render_tag('div', array('class' => 'remarkup-code-block', 'data-code-lang' => $options['lang']), $name_header . $code_body); }
private function executeParserTest($name, $data) { $data = explode("\n", $data, 2); if (count($data) !== 2) { throw new Exception(pht('Expected multiple lines in parser test file "%s".', $name)); } $head = head($data); $body = last($data); if (!preg_match('/^#/', $head)) { throw new Exception(pht('Expected first line of parser test file "%s" to begin with "#" ' . 'and specify test options.', $name)); } $head = preg_replace('/^#\\s*/', '', $head); $options_parser = new PhutilSimpleOptions(); $options = $options_parser->parse($head); $type = null; foreach ($options as $key => $value) { switch ($key) { case 'pass': case 'fail-syntax': case 'fail-parse': if ($type !== null) { throw new Exception(pht('Test file "%s" unexpectedly specifies multiple expected ', 'test outcomes.', $name)); } $type = $key; break; case 'comment': // Human readable comment providing test case information. break; case 'rtrim': // Allows construction of tests which rely on EOF without newlines. $body = rtrim($body); break; default: throw new Exception(pht('Test file "%s" has unknown option "%s" in its options ' . 'string.', $name, $key)); } } if ($type === null) { throw new Exception(pht('Test file "%s" does not specify a test result (like "pass") in ' . 'its options string.', $name)); } $future = xhpast_get_parser_future($body); list($err, $stdout, $stderr) = $future->resolve(); switch ($type) { case 'pass': case 'fail-parse': $this->assertEqual(0, $err, pht('Exit code for "%s".', $name)); $expect_name = preg_replace('/\\.test$/', '.expect', $name); $dir = dirname(__FILE__) . '/data/'; $expect = Filesystem::readFile($dir . $expect_name); $expect = json_decode($expect, true); if (!is_array($expect)) { throw new Exception(pht('Test ".expect" file "%s" for test "%s" is not valid JSON.', $expect_name, $name)); } $stdout = json_decode($stdout, true); if (!is_array($stdout)) { throw new Exception(pht('Output for test file "%s" is not valid JSON.', $name)); } $json = new PhutilJSON(); $expect_nice = $json->encodeFormatted($expect); $stdout_nice = $json->encodeFormatted($stdout); if ($type == 'pass') { $this->assertEqual($expect_nice, $stdout_nice, pht('Parser output for "%s".', $name)); } else { $this->assertFalse($expect_nice == $stdout_nice, pht('Expected parser to parse "%s" incorrectly.', $name)); } break; case 'fail-syntax': $this->assertEqual(1, $err, pht('Exit code for "%s".', $name)); $this->assertTrue((bool) preg_match('/syntax error/', $stderr), pht('Expect "syntax error" in stderr or "%s".', $name)); break; } }