@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix project hashtag regexp to stop matching terminal periods

Summary:
Fixes T6416. The comment is consistent with intent, but the actual regexp doesn't quite work right. In particular, we incorrectly match `#security.` as `security.` (with a period) instead of `security` (with no period).

Since this stuff is a pain to test and I evidently got it wrong in this case in D8703, make it unit testable.

Test Plan:
Added unit tests. Also:

{F227181}

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T6416

Differential Revision: https://secure.phabricator.com/D10753

+131 -11
+2
src/__phutil_library_map__.php
··· 2823 2823 'ProjectCreateProjectsCapability' => 'applications/project/capability/ProjectCreateProjectsCapability.php', 2824 2824 'ProjectQueryConduitAPIMethod' => 'applications/project/conduit/ProjectQueryConduitAPIMethod.php', 2825 2825 'ProjectRemarkupRule' => 'applications/project/remarkup/ProjectRemarkupRule.php', 2826 + 'ProjectRemarkupRuleTestCase' => 'applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php', 2826 2827 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 2827 2828 'ReleephAuthorFieldSpecification' => 'applications/releeph/field/specification/ReleephAuthorFieldSpecification.php', 2828 2829 'ReleephBranch' => 'applications/releeph/storage/ReleephBranch.php', ··· 6015 6016 'ProjectCreateProjectsCapability' => 'PhabricatorPolicyCapability', 6016 6017 'ProjectQueryConduitAPIMethod' => 'ProjectConduitAPIMethod', 6017 6018 'ProjectRemarkupRule' => 'PhabricatorObjectRemarkupRule', 6019 + 'ProjectRemarkupRuleTestCase' => 'PhabricatorTestCase', 6018 6020 'QueryFormattingTestCase' => 'PhabricatorTestCase', 6019 6021 'ReleephAuthorFieldSpecification' => 'ReleephFieldSpecification', 6020 6022 'ReleephBranch' => array(
+1 -1
src/applications/project/remarkup/ProjectRemarkupRule.php
··· 30 30 // In other contexts, the PhabricatorProjectProjectPHIDType pattern is 31 31 // controlling and these names should parse correctly. 32 32 33 - return '[^\s.!,:;{}#]*[^\s\d!,:;{}#]+(?:[^\s.!,:;{}#][^\s!,:;{}#]*)*'; 33 + return '[^\s.\d!,:;{}#]+(?:[^\s!,:;{}#][^\s.!,:;{}#]+)*'; 34 34 } 35 35 36 36 protected function loadObjects(array $ids) {
+57
src/applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php
··· 1 + <?php 2 + 3 + final class ProjectRemarkupRuleTestCase extends PhabricatorTestCase { 4 + 5 + public function testProjectObjectRemarkup() { 6 + $cases = array( 7 + 'I like #ducks.' => array( 8 + 'embed' => array(), 9 + 'ref' => array( 10 + array( 11 + 'offset' => 8, 12 + 'id' => 'ducks', 13 + ), 14 + ), 15 + ), 16 + 'We should make a post on #blog.example.com tomorrow.' => array( 17 + 'embed' => array(), 18 + 'ref' => array( 19 + array( 20 + 'offset' => 26, 21 + 'id' => 'blog.example.com', 22 + ), 23 + ), 24 + ), 25 + 'We should make a post on #blog.example.com.' => array( 26 + 'embed' => array(), 27 + 'ref' => array( 28 + array( 29 + 'offset' => 26, 30 + 'id' => 'blog.example.com', 31 + ), 32 + ), 33 + ), 34 + '#123' => array( 35 + 'embed' => array(), 36 + 'ref' => array(), 37 + ), 38 + '#security#123' => array( 39 + 'embed' => array(), 40 + 'ref' => array( 41 + array( 42 + 'offset' => 1, 43 + 'id' => 'security', 44 + 'tail' => '123', 45 + ), 46 + ), 47 + ), 48 + ); 49 + 50 + foreach ($cases as $input => $expect) { 51 + $rule = new ProjectRemarkupRule(); 52 + $matches = $rule->extractReferences($input); 53 + $this->assertEqual($expect, $matches, $input); 54 + } 55 + } 56 + 57 + }
+71 -10
src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php
··· 95 95 } 96 96 97 97 public function apply($text) { 98 + $text = preg_replace_callback( 99 + $this->getObjectEmbedPattern(), 100 + array($this, 'markupObjectEmbed'), 101 + $text); 102 + 103 + $text = preg_replace_callback( 104 + $this->getObjectReferencePattern(), 105 + array($this, 'markupObjectReference'), 106 + $text); 107 + 108 + return $text; 109 + } 110 + 111 + private function getObjectEmbedPattern() { 98 112 $prefix = $this->getObjectNamePrefix(); 99 - $prefix = preg_quote($prefix, '@'); 113 + $prefix = preg_quote($prefix); 100 114 $id = $this->getObjectIDPattern(); 101 115 102 - $text = preg_replace_callback( 103 - '@\B{'.$prefix.'('.$id.')((?:[^}\\\\]|\\\\.)*)}\B@u', 104 - array($this, 'markupObjectEmbed'), 105 - $text); 116 + return '(\B{'.$prefix.'('.$id.')((?:[^}\\\\]|\\\\.)*)}\B)u'; 117 + } 118 + 119 + private function getObjectReferencePattern() { 120 + $prefix = $this->getObjectNamePrefix(); 121 + $prefix = preg_quote($prefix); 122 + 123 + $id = $this->getObjectIDPattern(); 106 124 107 125 // If the prefix starts with a word character (like "D"), we want to 108 126 // require a word boundary so that we don't match "XD1" as "D1". If the ··· 121 139 // The "\b" allows us to link "(abcdef)" or similar without linking things 122 140 // in the middle of words. 123 141 124 - $text = preg_replace_callback( 125 - '((?<![#-])'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?(?!\w))u', 126 - array($this, 'markupObjectReference'), 127 - $text); 142 + return '((?<![#-])'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?(?!\w))u'; 143 + } 144 + 145 + 146 + /** 147 + * Extract matched object references from a block of text. 148 + * 149 + * This is intended to make it easy to write unit tests for object remarkup 150 + * rules. Production code is not normally expected to call this method. 151 + * 152 + * @param string Text to match rules against. 153 + * @return wild Matches, suitable for writing unit tests against. 154 + */ 155 + public function extractReferences($text) { 156 + $embed_matches = null; 157 + preg_match_all( 158 + $this->getObjectEmbedPattern(), 159 + $text, 160 + $embed_matches, 161 + PREG_OFFSET_CAPTURE | PREG_SET_ORDER); 128 162 129 - return $text; 163 + $ref_matches = null; 164 + preg_match_all( 165 + $this->getObjectReferencePattern(), 166 + $text, 167 + $ref_matches, 168 + PREG_OFFSET_CAPTURE | PREG_SET_ORDER); 169 + 170 + $results = array(); 171 + $sets = array( 172 + 'embed' => $embed_matches, 173 + 'ref' => $ref_matches, 174 + ); 175 + foreach ($sets as $type => $matches) { 176 + $formatted = array(); 177 + foreach ($matches as $match) { 178 + $format = array( 179 + 'offset' => $match[1][1], 180 + 'id' => $match[1][0], 181 + ); 182 + if (isset($match[2][0])) { 183 + $format['tail'] = $match[2][0]; 184 + } 185 + $formatted[] = $format; 186 + } 187 + $results[$type] = $formatted; 188 + } 189 + 190 + return $results; 130 191 } 131 192 132 193 public function markupObjectEmbed($matches) {