mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Add an "{anchor #xyz}" rule to Remarkup
Summary: Ref T13410. Fixes T4280. Allows you to put a named anchor into a document explicitly. Test Plan: Used `{anchor ...}` in Remarkup, used location bar to jump to anchors. Maniphest Tasks: T13410, T4280 Differential Revision: https://secure.phabricator.com/D20825
This commit is contained in:
parent
bff72ce3b5
commit
b1d4d5c00c
5 changed files with 85 additions and 6 deletions
|
@ -5598,6 +5598,7 @@ phutil_register_library_map(array(
|
||||||
'PhutilQsprintfInterface' => 'infrastructure/storage/xsprintf/PhutilQsprintfInterface.php',
|
'PhutilQsprintfInterface' => 'infrastructure/storage/xsprintf/PhutilQsprintfInterface.php',
|
||||||
'PhutilQueryString' => 'infrastructure/storage/xsprintf/PhutilQueryString.php',
|
'PhutilQueryString' => 'infrastructure/storage/xsprintf/PhutilQueryString.php',
|
||||||
'PhutilRealNameContextFreeGrammar' => 'infrastructure/lipsum/PhutilRealNameContextFreeGrammar.php',
|
'PhutilRealNameContextFreeGrammar' => 'infrastructure/lipsum/PhutilRealNameContextFreeGrammar.php',
|
||||||
|
'PhutilRemarkupAnchorRule' => 'infrastructure/markup/markuprule/PhutilRemarkupAnchorRule.php',
|
||||||
'PhutilRemarkupBlockInterpreter' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockInterpreter.php',
|
'PhutilRemarkupBlockInterpreter' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockInterpreter.php',
|
||||||
'PhutilRemarkupBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php',
|
'PhutilRemarkupBlockRule' => 'infrastructure/markup/blockrule/PhutilRemarkupBlockRule.php',
|
||||||
'PhutilRemarkupBlockStorage' => 'infrastructure/markup/PhutilRemarkupBlockStorage.php',
|
'PhutilRemarkupBlockStorage' => 'infrastructure/markup/PhutilRemarkupBlockStorage.php',
|
||||||
|
@ -12391,6 +12392,7 @@ phutil_register_library_map(array(
|
||||||
'PhutilPhabricatorAuthAdapter' => 'PhutilOAuthAuthAdapter',
|
'PhutilPhabricatorAuthAdapter' => 'PhutilOAuthAuthAdapter',
|
||||||
'PhutilQueryString' => 'Phobject',
|
'PhutilQueryString' => 'Phobject',
|
||||||
'PhutilRealNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
'PhutilRealNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
||||||
|
'PhutilRemarkupAnchorRule' => 'PhutilRemarkupRule',
|
||||||
'PhutilRemarkupBlockInterpreter' => 'Phobject',
|
'PhutilRemarkupBlockInterpreter' => 'Phobject',
|
||||||
'PhutilRemarkupBlockRule' => 'Phobject',
|
'PhutilRemarkupBlockRule' => 'Phobject',
|
||||||
'PhutilRemarkupBlockStorage' => 'Phobject',
|
'PhutilRemarkupBlockStorage' => 'Phobject',
|
||||||
|
|
|
@ -715,6 +715,18 @@ Press {key down down-right right LP} to activate the hadoken technique.
|
||||||
> Press {key down down-right right LP} to activate the hadoken technique.
|
> 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 =
|
= Fullscreen Mode =
|
||||||
|
|
||||||
Remarkup editors provide a fullscreen composition mode. This can make it easier
|
Remarkup editors provide a fullscreen composition mode. This can make it easier
|
||||||
|
|
|
@ -539,6 +539,7 @@ final class PhabricatorMarkupEngine extends Phobject {
|
||||||
$rules[] = new PhutilRemarkupDelRule();
|
$rules[] = new PhutilRemarkupDelRule();
|
||||||
$rules[] = new PhutilRemarkupUnderlineRule();
|
$rules[] = new PhutilRemarkupUnderlineRule();
|
||||||
$rules[] = new PhutilRemarkupHighlightRule();
|
$rules[] = new PhutilRemarkupHighlightRule();
|
||||||
|
$rules[] = new PhutilRemarkupAnchorRule();
|
||||||
|
|
||||||
foreach (self::loadCustomInlineRules() as $rule) {
|
foreach (self::loadCustomInlineRules() as $rule) {
|
||||||
$rules[] = clone $rule;
|
$rules[] = clone $rule;
|
||||||
|
|
|
@ -162,12 +162,7 @@ final class PhutilRemarkupHeaderBlockRule extends PhutilRemarkupBlockRule {
|
||||||
|
|
||||||
public static function getAnchorNameFromHeaderText($text) {
|
public static function getAnchorNameFromHeaderText($text) {
|
||||||
$anchor = phutil_utf8_strtolower($text);
|
$anchor = phutil_utf8_strtolower($text);
|
||||||
|
$anchor = PhutilRemarkupAnchorRule::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, '-');
|
|
||||||
|
|
||||||
// Truncate the fragment to something reasonable.
|
// Truncate the fragment to something reasonable.
|
||||||
$anchor = id(new PhutilUTF8StringTruncator())
|
$anchor = id(new PhutilUTF8StringTruncator())
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue