diff --git a/.divinerconfig b/.divinerconfig index 42f3283..e746f43 100644 --- a/.divinerconfig +++ b/.divinerconfig @@ -1,19 +1,21 @@ { "name" : "libphutil", "src_base" : "https://github.com/facebook/libphutil/blob/master", "groups" : { "overview" : "Overview", "contrib" : "Contributing to libphutil", "working" : "Working with libphutil", "util" : "Core Utilities", "library" : "Phutil Module System", "filesystem" : "Filesystem", "exec" : "Command Execution", "futures" : "Futures", "markup" : "Markup", "console" : "Console Utilities", "xhpast" : "XHPAST (PHP/XHP Parser)", - "conduit" : "Conduit (Service API)" + "conduit" : "Conduit (Service API)", + "parser" : "Other Parsers", + "testcase" : "Test Cases" } } diff --git a/resources/git/commit-template.txt b/resources/git/commit-template.txt index 568dc35..28e556d 100644 --- a/resources/git/commit-template.txt +++ b/resources/git/commit-template.txt @@ -1,11 +1,12 @@ <> + Summary: Test Plan: Reviewers: CC: ############################################################################### diff --git a/src/console/PhutilConsoleFormatter.php b/src/console/PhutilConsoleFormatter.php index 363f761..8221b62 100644 --- a/src/console/PhutilConsoleFormatter.php +++ b/src/console/PhutilConsoleFormatter.php @@ -1,86 +1,86 @@ 0, 'red' => 1, 'green' => 2, 'yellow' => 3, 'blue' => 4, 'magenta' => 5, 'cyan' => 6, 'white' => 7, 'default' => 9, ); - + private static $disableANSI = false; - + public static function disableANSI($disable) { self::$disableANSI = $disable; } public static function formatString($format /* ... */) { $colors = implode('|', array_keys(self::$colorCodes)); if (self::$disableANSI) { $format = preg_replace('/\*\*(.*)\*\*/sU', '\1', $format); $format = preg_replace('/__(.*)__/sU', '\1', $format); $format = preg_replace('/##(.*)##/sU', '\1', $format); $format = preg_replace( '@<(fg|bg):('.$colors.')>(.*)@sU', '\3', $format); } else { $esc = chr(27); $bold = $esc.'[1m'.'\\1'.$esc.'[m'; $underline = $esc.'[4m'.'\\1'.$esc.'[m'; $invert = $esc.'[7m'.'\\1'.$esc.'[m'; $format = preg_replace('/\*\*(.*)\*\*/sU', $bold, $format); $format = preg_replace('/__(.*)__/sU', $underline, $format); $format = preg_replace('/##(.*)##/sU', $invert, $format); $format = preg_replace_callback( '@<(fg|bg):('.$colors.')>(.*)@sU', array('PhutilConsoleFormatter', 'replaceColorCode'), $format); } $args = func_get_args(); $args[0] = $format; return call_user_func_array('sprintf', $args); } public static function replaceColorCode($matches) { $codes = self::$colorCodes; $offset = 30 + $codes[$matches[2]]; $default = 39; if ($matches[1] == 'bg') { $offset += 10; $default += 10; } return chr(27).'['.$offset.'m'.$matches[3].chr(27).'['.$default.'m'; } } diff --git a/src/future/https/HTTPSFuture.php b/src/future/https/HTTPSFuture.php index 69a9813..55003fd 100644 --- a/src/future/https/HTTPSFuture.php +++ b/src/future/https/HTTPSFuture.php @@ -1,40 +1,44 @@ result = array(200, $result); } public function isReady() { return true; } } diff --git a/src/future/proxy/FutureProxy.php b/src/future/proxy/FutureProxy.php index 97d48cd..b2f0634 100644 --- a/src/future/proxy/FutureProxy.php +++ b/src/future/proxy/FutureProxy.php @@ -1,69 +1,75 @@ proxied = $proxied; } public function isReady() { return $this->proxied->isReady(); } public function resolve($timeout = null) { $this->proxied->resolve($timeout); return $this->getResult(); } public function setException(Exception $ex) { $this->proxied->setException($ex); return $this; } public function getException() { return $this->proxied->getException(); } public function getReadSockets() { return $this->proxied->getReadSockets(); } public function getWriteSockets() { return $this->proxied->getWriteSockets(); } protected function getResult() { if ($this->result === null) { $result = $this->proxied->resolve(); $result = $this->didReceiveResult($result); $this->result = $result; } return $this->result; } public function start() { $this->proxied->start(); return $this; } abstract protected function didReceiveResult($result); } diff --git a/src/markup/render.php b/src/markup/render.php index b469fe6..40c6f7f 100644 --- a/src/markup/render.php +++ b/src/markup/render.php @@ -1,49 +1,52 @@ $v) { if ($v === null) { continue; } $v = phutil_escape_html($v); $attributes[$k] = ' '.$k.'="'.$v.'"'; } $attributes = implode('', $attributes); if ($content === null) { return '<'.$tag.$attributes.' />'; } else { return '<'.$tag.$attributes.'>'.$content.''; } } /** * @group markup */ function phutil_escape_html($string) { return htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); } +/** + * @group markup + */ function phutil_escape_uri($string) { return rawurlencode($string); } diff --git a/src/parser/docblock/PhutilDocblockParser.php b/src/parser/docblock/PhutilDocblockParser.php index af588bb..73ad7d4 100644 --- a/src/parser/docblock/PhutilDocblockParser.php +++ b/src/parser/docblock/PhutilDocblockParser.php @@ -1,97 +1,99 @@ $line) { if (preg_match('/^\s*@\w/i', $line)) { $last = $k; continue; } else if (preg_match('/^\s*$/', $line)) { $last = false; } else if ($last) { $lines[$last] = rtrim($lines[$last]).' '.trim($line); unset($lines[$k]); } } $docblock = implode("\n", $lines); $special = array(); // Parse @specials. $matches = null; $have_specials = preg_match_all( '/^\s*@(\w+)\s*([^\n]*)/m', $docblock, $matches, PREG_SET_ORDER); if ($have_specials) { $docblock = preg_replace('/^\s*@(\w+)\s*([^\n]*)/m', '', $docblock); foreach ($matches as $match) { list($_, $type, $data) = $match; $data = trim($data); if (isset($special[$type])) { $special[$type] = $special[$type]."\n".$data; } else { $special[$type] = $data; } } } $docblock = str_replace("\t", ' ', $docblock); // Smush the whole docblock to the left edge. $min_indent = 80; $indent = 0; foreach (array_filter(explode("\n", $docblock)) as $line) { for ($ii = 0; $ii < strlen($line); $ii++) { if ($line[$ii] != ' ') { break; } $indent++; } $min_indent = min($indent, $min_indent); } $docblock = preg_replace( '/^'.str_repeat(' ', $min_indent).'/m', '', $docblock); $docblock = rtrim($docblock); // Trim any empty lines off the front, but leave the indent level if there // is one. $docblock = preg_replace('/^\s*\n/', '', $docblock); return array($docblock, $special); } } diff --git a/src/parser/docblock/__tests__/PhutilDocblockParserTestCase.php b/src/parser/docblock/__tests__/PhutilDocblockParserTestCase.php index be6a8a9..b9ee733 100644 --- a/src/parser/docblock/__tests__/PhutilDocblockParserTestCase.php +++ b/src/parser/docblock/__tests__/PhutilDocblockParserTestCase.php @@ -1,90 +1,95 @@ parseDocblock($root.$file); } } private function parseDocblock($doc_file) { $contents = Filesystem::readFile($doc_file); $file = basename($doc_file); $parser = new PhutilDocblockParser(); list($docblock, $specials) = $parser->parse($contents); switch ($file) { case 'embedded-specials.docblock': $this->assertEqual(array(), $specials); $this->assertEqual( "So long as a @special does not appear at the beginning of a line,\n". "it is parsed as normal text.", $docblock); break; case 'indented-block.docblock': $this->assertEqual(array(), $specials); $this->assertEqual( "Cozy lummox gives smart squid who asks for job pen.", $docblock); break; case 'indented-text.docblock': $this->assertEqual(array(), $specials); $this->assertEqual( "Cozy lummox gives smart squid who asks for job pen.", $docblock); break; case 'multi-specials.docblock': $this->assertEqual( array( 'special' => "north\nsouth", ), $specials); $this->assertEqual( "", $docblock); break; case 'specials.docblock': $this->assertEqual( array( 'type' => 'type', 'task' => 'task', ), $specials); $this->assertEqual( "", $docblock); break; case 'linebreak-breaks-specials.docblock': $this->assertEqual( array( 'title' => 'title', ), $specials); $this->assertEqual( "This is normal text, not part of the @title.", $docblock); break; default: throw new Exception("No test case to handle file '{$file}'!"); } } } diff --git a/src/parser/uri/PhutilURI.php b/src/parser/uri/PhutilURI.php index e2cdc6c..bd64ddc 100644 --- a/src/parser/uri/PhutilURI.php +++ b/src/parser/uri/PhutilURI.php @@ -1,153 +1,158 @@ parseURI($uri); if ($parts) { $this->protocol = $parts[1]; $this->domain = $parts[2]; $this->port = $parts[3]; $this->path = $parts[4]; parse_str($parts[5], $this->query); $this->fragment = $parts[6]; } } private static function parseURI($uri) { // NOTE: We allow "+" in the protocol for "svn+ssh" and similar. $protocol = '([\w+]+):\/\/'; $domain = '([a-zA-Z0-9\.\-_]*)'; $port = '(?::(\d+))?'; $path = '((?:\/|^)[^#?]*)?'; $query = '(?:\?([^#]*))?'; $anchor = '(?:#(.*))?'; $regexp = '/^(?:'.$protocol.$domain.$port.')?'.$path.$query.$anchor.'$/S'; $matches = null; $ok = preg_match($regexp, $uri, $matches); if ($ok) { return array_pad($matches, 7, null); } return null; } public function __toString() { $prefix = null; if ($this->protocol || $this->domain || $this->port) { $protocol = nonempty($this->protocol, 'http'); $prefix = $protocol.'://'.$this->domain; if ($this->port) { $prefix .= ':'.$this->port; } } if ($this->query) { $query = '?'.http_build_query($this->query); } else { $query = null; } if (strlen($this->getFragment())) { $fragment = '#'.$this->getFragment(); } else { $fragment = null; } return $prefix.$this->getPath().$query.$fragment; } public function setQueryParam($key, $value) { if ($value === null) { unset($this->query[$key]); } else { $this->query[$key] = $value; } return $this; } public function setQueryParams(array $params) { $this->query = $params; return $this; } public function getQueryParams() { return $this->query; } public function setProtocol($protocol) { $this->protocol = $protocol; return $this; } public function getProtocol() { return $this->protocol; } public function setDomain($domain) { $this->domain = $domain; return $this; } public function getDomain() { return $this->domain; } public function setPort($port) { $this->port = $port; return $this; } public function getPort() { return $this->port; } public function setPath($path) { $this->path = $path; return $this; } public function getPath() { return $this->path; } public function setFragment($fragment) { $this->fragment = $fragment; return $this; } public function getFragment() { return $this->fragment; } public function alter($key, $value) { $altered = clone $this; $altered->setQueryParam($key, $value); return $altered; } }