diff --git a/src/applications/project/remarkup/ProjectRemarkupRule.php b/src/applications/project/remarkup/ProjectRemarkupRule.php index 5bdebfa13e..fa0552593f 100644 --- a/src/applications/project/remarkup/ProjectRemarkupRule.php +++ b/src/applications/project/remarkup/ProjectRemarkupRule.php @@ -30,7 +30,21 @@ final class ProjectRemarkupRule extends PhabricatorObjectRemarkupRule { // In other contexts, the PhabricatorProjectProjectPHIDType pattern is // controlling and these names should parse correctly. - return '[^\s.\d!,:;{}#\(\)]+(?:[^\s!,:;{}#\(\)]*[^\s.!,:;{}#\(\)]+)*'; + // These characters may never appear anywhere in a hashtag. + $never = '\s?!,:;{}#\\(\\)"\''; + + // These characters may not appear at the beginning. + $never_first = '.\d'; + + // These characters may not appear at the end. + $never_last = '.'; + + return + '[^'.$never_first.$never.']+'. + '(?:'. + '[^'.$never.']*'. + '[^'.$never_last.$never.']+'. + ')*'; } protected function loadObjects(array $ids) { diff --git a/src/applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php b/src/applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php index 4df2aad3d9..437b025491 100644 --- a/src/applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php +++ b/src/applications/project/remarkup/__tests__/ProjectRemarkupRuleTestCase.php @@ -81,6 +81,36 @@ final class ProjectRemarkupRuleTestCase extends PhabricatorTestCase { ), ), + 'Is this #urgent?' => array( + 'embed' => array(), + 'ref' => array( + array( + 'offset' => 9, + 'id' => 'urgent', + ), + ), + ), + + 'This is "#urgent".' => array( + 'embed' => array(), + 'ref' => array( + array( + 'offset' => 10, + 'id' => 'urgent', + ), + ), + ), + + 'This is \'#urgent\'.' => array( + 'embed' => array(), + 'ref' => array( + array( + 'offset' => 10, + 'id' => 'urgent', + ), + ), + ), + ); foreach ($cases as $input => $expect) {