让 Typecho 下标式超链接支持标题

过了几年了我才发现原来 Typecho 不支持这种下标式超链接设置 title 属性:

[应该有 title 的链接][1] >-< [没 title 的链接][2]
  [1]: http://example.com "Example"
  [2]: http://example.com/2

这次偶然点了自己博客文章的链接才发现,这样会把可选的 title(即上面引号里的内容)当作链接一块解析。过程中会去除不安全的特殊符号,最终会变成指向 http://example.comexample 之类的超链接。搜了一下网上和 issue 没有人提,确定了 Markdown 是有这种语法,不想一个个改文章链接,遂自己从源码层面解决。

最开始按照我转载的《Typecho 中使 Markdown 文章的超链接在新窗口打开》,使劲改 var/CommonMark/HtmlRenderer.php,发现没用。一度以为是 Opcache 缓存原因,直到查看 GitHub 上的源码,后面我才意识到新版本 Typecho 渲染 Markdown 已经不在这了。用 IDE 搜了一下很快确定位置在 var/Utils/HyperDown.php

瞄了几眼知道几件事。图像是能解析标题的,甚至行内的超链接也能解析标题(正则表达式我让 LLM AI 读的),但下标式的就没实现。貌似 _definitions 是用来存储下标文本的数组。不过这个下标文本经过了 cleanUrl 处理。于是另外加一个变量存储原始值;后来又看到 _definitions 拢共没几处调用,索性在原函数上改,在调用的地方手动包裹 cleanUrl。仿照行内超链接的写法加一个处理标题的逻辑。如此这般向官方提交了一个 pull request

在合并之前如果你也想要这个逻辑,可以按照我的 commit 去改。

出于好奇搜了一下,我发现 GitHub 竟然能提供 .patch 和 .diff 文件,只需要在 commit url 后加对应的后缀名就能访问到,算是一个隐藏功能。于是也可以下载 commit 为 .patch 文件,然后进入 Typecho 目录用 patch 命令修补:

patch -p0 ./var/Utils/HyperDown.php < /your/d02bf06074e0fb7bbc89699b1bc2fb7a8c0317ed.patch

如果你网络环境不佳,我在这里直接贴出 patch 文件内容:

From d02bf06074e0fb7bbc89699b1bc2fb7a8c0317ed Mon Sep 17 00:00:00 2001
From: shansing <i@shansing.com>
Date: Wed, 14 Jan 2026 20:40:15 +0800
Subject: [PATCH] fix: optional title attribute in Markdown reference-style
 links

---
 var/Utils/HyperDown.php | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/var/Utils/HyperDown.php b/var/Utils/HyperDown.php
index b446bc617b..5d03e72e72 100644
--- a/var/Utils/HyperDown.php
+++ b/var/Utils/HyperDown.php
@@ -506,7 +506,7 @@ function ($matches) {
                 $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
 
                 $result = isset($this->_definitions[$matches[2]]) ?
-                    "<img src=\"{$this->_definitions[$matches[2]]}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
+                    "<img src=\"{$this->cleanUrl($this->_definitions[$matches[2]])}\" alt=\"{$escaped}\" title=\"{$escaped}\">"
                     : $escaped;
 
                 return $this->makeHolder($result);
@@ -536,10 +536,12 @@ function ($matches) {
                 $escaped = $this->parseInline(
                     $this->escapeBracket($matches[1]), '', false
                 );
-                $result = isset($this->_definitions[$matches[2]]) ?
-                    "<a href=\"{$this->_definitions[$matches[2]]}\">{$escaped}</a>"
-                    : $escaped;
-
+                $result = $escaped;
+                if (isset($this->_definitions[$matches[2]])) {
+                    [$url, $title] = $this->cleanUrl($this->_definitions[$matches[2]], true);
+                    $title = empty($title) ? '' : " title=\"{$title}\"";
+                    $result = "<a href=\"{$url}\"{$title}>{$escaped}</a>";
+                }
                 return $this->makeHolder($result);
             },
             $text
@@ -992,7 +994,7 @@ private function parseBlockFootnote(?array $block, int $key, string $line): bool
     private function parseBlockDefinition(?array $block, int $key, string $line): bool
     {
         if (preg_match("/^\s*\[((?:[^\]]|\\]|\\[)+?)\]:\s*(.+)$/", $line, $matches)) {
-            $this->_definitions[$matches[1]] = $this->cleanUrl($matches[2]);
+            $this->_definitions[$matches[1]] = $matches[2];
             $this->startBlock('definition', $key)
                 ->endBlock();

从稳定版到我提交 PR 的这段期间这个文件几乎没怎么变动,实测对于最新正式稳定版(v1.2.1)也能正常修补,不一定需要是开发版。用 patch 命令不会去校验 git 生成的文件摘要。

若无特别说明,本文系原创,遵循 署名-非商业性使用 3.0 (CC BY-NC 3.0) 协议,转载文章请注明来自【闪星空间】,或链接上原文地址:http://shansing.com/read/557/

发表评论»

NO SPAMS! 不要发垃圾评论哦!

表情