Page MenuHomePhabricator

D20825.diff
No OneTemporary

D20825.diff

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
@@ -5598,6 +5598,7 @@
'PhutilQsprintfInterface' => 'infrastructure/storage/xsprintf/PhutilQsprintfInterface.php',
'PhutilQueryString' => 'infrastructure/storage/xsprintf/PhutilQueryString.php',
'PhutilRealNameContextFreeGrammar' => 'infrastructure/lipsum/PhutilRealNameContextFreeGrammar.php',
+ 'PhutilRemarkupAnchorRule' => 'infrastructure/markup/markuprule/PhutilRemarkupAnchorRule.php',
'PhutilRemarkupBlockInterpreter' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockInterpreter.php',
'PhutilRemarkupBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php',
'PhutilRemarkupBlockStorage' => 'infrastructure/markup/PhutilRemarkupBlockStorage.php',
@@ -12391,6 +12392,7 @@
'PhutilPhabricatorAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilQueryString' => 'Phobject',
'PhutilRealNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
+ 'PhutilRemarkupAnchorRule' => 'PhutilRemarkupRule',
'PhutilRemarkupBlockInterpreter' => 'Phobject',
'PhutilRemarkupBlockRule' => 'Phobject',
'PhutilRemarkupBlockStorage' => 'Phobject',
diff --git a/src/docs/user/userguide/remarkup.diviner b/src/docs/user/userguide/remarkup.diviner
--- a/src/docs/user/userguide/remarkup.diviner
+++ b/src/docs/user/userguide/remarkup.diviner
@@ -715,6 +715,18 @@
> Press {key down down-right right LP} to activate the hadoken technique.
+Anchors
+========
+
+You can use `{anchor #xyz}` to create a document anchor and later link to
+it directly with `#xyz` in the URI.
+
+Headers also automatically create named anchors.
+
+If you navigate to `#xyz` in your browser location bar, the page will scroll
+to the first anchor with "xyz" as a prefix of the anchor name.
+
+
= Fullscreen Mode =
Remarkup editors provide a fullscreen composition mode. This can make it easier
diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php
--- a/src/infrastructure/markup/PhabricatorMarkupEngine.php
+++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php
@@ -539,6 +539,7 @@
$rules[] = new PhutilRemarkupDelRule();
$rules[] = new PhutilRemarkupUnderlineRule();
$rules[] = new PhutilRemarkupHighlightRule();
+ $rules[] = new PhutilRemarkupAnchorRule();
foreach (self::loadCustomInlineRules() as $rule) {
$rules[] = clone $rule;
diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupHeaderBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupHeaderBlockRule.php
--- a/src/infrastructure/markup/blockrule/PhutilRemarkupHeaderBlockRule.php
+++ b/src/infrastructure/markup/blockrule/PhutilRemarkupHeaderBlockRule.php
@@ -162,12 +162,7 @@
public static function getAnchorNameFromHeaderText($text) {
$anchor = phutil_utf8_strtolower($text);
-
- // Replace all latin characters which are not "a-z" or "0-9" with "-".
- // Preserve other characters, since non-latin letters and emoji work
- // fine in anchors.
- $anchor = preg_replace('/[\x00-\x2F\x3A-\x60\x7B-\x7F]+/', '-', $anchor);
- $anchor = trim($anchor, '-');
+ $anchor = PhutilRemarkupAnchorRule::normalizeAnchor($anchor);
// Truncate the fragment to something reasonable.
$anchor = id(new PhutilUTF8StringTruncator())
diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupAnchorRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupAnchorRule.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/markup/markuprule/PhutilRemarkupAnchorRule.php
@@ -0,0 +1,69 @@
+<?php
+
+final class PhutilRemarkupAnchorRule extends PhutilRemarkupRule {
+
+ public function getPriority() {
+ return 200.0;
+ }
+
+ public function apply($text) {
+ return preg_replace_callback(
+ '/{anchor\s+#([^\s}]+)}/s',
+ array($this, 'markupAnchor'),
+ $text);
+ }
+
+ protected function markupAnchor(array $matches) {
+ $engine = $this->getEngine();
+
+ if ($engine->isTextMode()) {
+ return null;
+ }
+
+ if ($engine->isHTMLMailMode()) {
+ return null;
+ }
+
+ if ($engine->isAnchorMode()) {
+ return null;
+ }
+
+ if (!$this->isFlatText($matches[0])) {
+ return $matches[0];
+ }
+
+ if (!self::isValidAnchorName($matches[1])) {
+ return $matches[0];
+ }
+
+ $tag_view = phutil_tag(
+ 'a',
+ array(
+ 'name' => $matches[1],
+ ),
+ '');
+
+ return $this->getEngine()->storeText($tag_view);
+ }
+
+ public static function isValidAnchorName($anchor_name) {
+ $normal_anchor = self::normalizeAnchor($anchor_name);
+
+ if ($normal_anchor === $anchor_name) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function normalizeAnchor($anchor) {
+ // Replace all latin characters which are not "a-z" or "0-9" with "-".
+ // Preserve other characters, since non-latin letters and emoji work
+ // fine in anchors.
+ $anchor = preg_replace('/[\x00-\x2F\x3A-\x60\x7B-\x7F]+/', '-', $anchor);
+ $anchor = trim($anchor, '-');
+
+ return $anchor;
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Nov 25, 4:04 AM (13 h, 17 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6784881
Default Alt Text
D20825.diff (5 KB)

Event Timeline