没有 pre 兜着时会套 <p>,而 <p> 的 white-space: normal 会把连续空格折叠成一个,缩进全丢
wptexturize
把 ' 变成 ’、-- 变成 —、" 变成弯引号 —— 代码直接跑不了
make_clickable
把代码里的 URL 包成 <a href rel="nofollow">,复制出来全是 HTML
后两条是最容易忽略的:wptexturize 和 make_clickable只在没有 pre 包裹时才破坏内容,
一旦 pre 进了白名单,二者都会被 pre 挡住。已用真实 WP 核心跑通完整链路(11 条判据全通过): pre 保住、class 保住、无 <p>、无 <br>、缩进保留、引号不变弯、无 <a>、-- 不变 en dash。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
背景
站内代码高亮目前依赖别人写的插件(Enlighter / wp-editormd 残留 + 主题里一段 Prism 兜底),
存在三个问题:深色配色印在浅色底上导致大量 token 看不见、复制会带行号、评论里根本贴不了代码。
本次自研替换:用 highlight.js 自托管,主题侧统一接管,并打通评论区的代码输入与展示。
需求拆解
1. 配色 20 套 = 浅色 10 + 深色 10
深色组必须存在,理由:技术文章里把代码块做成深色能压住浅色正文,突出代码;
同一篇文章同时给 Python 和 Java 两种实现时也靠配色区分阅读节奏。
若 10+10 不好取舍,可退为 14 浅 + 6 深;但优先 10+10。
2. 行号
不需要额外开关,也不需要任何后处理。
3. 复制按钮
navigator.clipboard只在安全上下文可用,HTTP 下必须退回execCommand。4. 自动识别语言
但识别本身会出错,所以必须让用户能关掉、回退成纯文本。
不能把猜测伪装成事实。
5. 代码语言标签(新增)
代码块左上角直接显示语言名(
Java/Python/YAML…)。场景:同一篇文章同时给出多语言实现,读者容易复制错;标签在复制按钮旁边,
一眼能确认"我复制的是哪一种"。
6. 评论也要能贴代码(新增)
pre与code[class]、序列化器保留代码块结构、评论样式表补代码块外观。
验收标准
pre不被剥掉、class="language-xxx"不被剥掉、不被
wpautop套<p>折叠缩进、引号不变弯、URL 不变链接。hover: none)下复制按钮常驻可见。实现范围
主题侧:
inc/theme-highlight.php:语言注册、别名映射(text→plaintext为必需,站内 47 个代码块用
text)、渲染包装(语言标签 + 复制按钮 + 作用域类)。footer.php移除现有 Prism 兜底段。inc/theme-options.php增加三个选项。word-break: break-all必须改掉(会在任意字符断行,代码像乱码)。评论侧:
comments.php工具条加代码按钮 + 语言选择面板。assets/js/comments.js扩展serializeEditor()保留代码块。inc/theme-comments.php扩展 kses 白名单。发布方式
只走
/theme-update/更新通道(zip + readme + update.json),不覆盖服务器主题目录。补充 1:开工前的全站实测结论
按要求,开工前把站内全部文章、页面、评论遍历了一遍。以下都是实测数字,不是估算。
站点规模
代码块的 HTML 形态(最关键的一组数据)
<pre><code class="language-xxx">标准形态<pre>(没有<code>子元素)裸
pre这 80 个是实现硬要求:只处理pre code会整块漏掉近三成。用户已关闭 Enlighter(实测已不在 active_plugins 里),现在只剩 wp-editormd 启用中,
它产出的历史内容正是这种裸
pre形态。现有语言标记分布
bash131、text47、yaml11、python8、json4、javascript1、shell1、sql1。⚠️
text这 47 个必须做别名映射到plaintext。hljs 没有text这个语言名,不映射的话这 47 块会全部落到"未知语言"分支、静默不上色,而且不报错。
覆盖率
可处理 267 / 284 = 94.0%。剩下 17 个(6.0%)逐个查明:
gitlab-ctl reconfigure、su -、dpkg-reconfigure locales等)没有一个是"应该高亮却被漏掉的真实代码"。
行数分布
1 行 186(65.5%)、2–3 行 33、4–10 行 39、11–30 行 14、31–100 行 3、100 行以上 1。
六成半的代码块是一行命令 —— 这类块开不开自动识别差别很小,所以自动识别默认开启的代价很低。
自动识别的真实准确率(这是要不要做成选项的关键依据)
取 18 个 ≥4 行、且作者没标语言的代码块,用真 hljs 跑
highlightAuto():deb …8 行18 个只对 4 个(22%)。
另有两条实测结论值得记录:
所以不能"低于某个置信度就不上色"。
等于给这批老文章贴上 78% 的错误标签。
语言标签的实证价值
bash→python、python→json)这直接支撑了"代码块左上角显示语言标签"这个需求:确实存在读者会复制错的实际场景。
补充 2:评论区的现状(已用真实 WP 7.1.3 验证)
当前访客在评论里贴不了代码,三层都会坏:
pre不在白名单(整条被剥掉);code在,但任何属性都会被剥,包括classwpautoppre兜着时会套<p>,而<p>的white-space: normal会把连续空格折叠成一个,缩进全丢wptexturize'变成’、--变成—、"变成弯引号 —— 代码直接跑不了make_clickable<a href rel="nofollow">,复制出来全是 HTML后两条是最容易忽略的:
wptexturize和make_clickable只在没有pre包裹时才破坏内容,一旦
pre进了白名单,二者都会被pre挡住。已用真实 WP 核心跑通完整链路(11 条判据全通过):pre保住、class保住、无<p>、无<br>、缩进保留、引号不变弯、无<a>、--不变 en dash。补充 3:实现时必须改掉的两处现有样式
assets/css/kratos.css986–1035 行:word-break: break-all必须改成white-space: pre+overflow-x: auto,长行横向滚动而不是折断。
补充 4:原型已可交互验证
原型页已做出来并跑通验收(含反向验证),覆盖:
<pre>之外)hover: none下复制按钮常驻验收过程中发现并修掉两个真实缺陷(都不是判据问题,是实现真的坏了):
span.line::before计数器,但 hljs 输出的是 token span,没有行结构,
span.line根本不存在。而当时的判据只查"CSS 里有 counter-increment 规则"和"body 有 opt-ln 类"两件书面证据,
双双通过、画面空白。已改成
<pre>之外的独立行号槽。@media (hover: none) { opacity: 1 },但桌面端从未设过
opacity: 0,1就是默认值,这条规则删掉也不会有任何变化。已改为默认隐藏、hover 显示、触屏覆盖回可见。
另外量出一起几何错位:hljs 每套官方主题都自带
pre code.hljs{padding:1em},把代码整体压低 15.5px,行号槽没跟着压,屏幕上第一行代码旁边写着 2。
已归零该 padding,现在逐行坐标偏差 0.00px。
实现完成 + 验收结果
一、按需求逐项对照
1. 20 套主题 = 浅色 10 + 深色 10
按用户原话"10 套浅色、10 套深色是比较好的配置"直接做成 10+10,没有退到 14+6。
深色是给"技术文章里要把代码突出显示"的场景用的,卡片会带
data-kind="dark",顶栏(语言标签、复制按钮)跟着换深色底,不会出现"深色代码块配浅色按钮"的割裂。2. 复制不带行号 —— 未做成任何设置项,也未做任何处理。行号槽
<div class="cb-ln">在<pre>外面,复制取的是code.textContent,行号天然不在里面。3. 逐行对比 —— 不作为考虑项。
4. 行号默认关 —— 默认值是
0,服务端输出时就不带opt-ln类(不做成 JS 事后切换,避免首屏闪一下行号)。5. 复制按钮默认开 —— 默认值
1。6. 自动识别:有,且做成选项 —— 采纳用户自己的修正,做成
a_highlight_autodetect选项,默认开。只对没标语言的块生效,作者标了语言的块永远按标注来(因为实测准确率只有 22%,不能让它覆盖作者)。7. 代码语言标签 —— 每张卡片顶栏左侧固定显示语言名(
Python/Java/纯文本…),这是防"复制错语言"的那道标签。有实证价值:站内 19 篇多语言文章、相邻两块语言不同的有 38 处。标签用两种外观区分来源,猜的绝不冒充作者标的:
language-xxx—,仍然是虚线斜体8. 评论区的代码按钮 —— 评论工具条(加粗/斜体那一排)后新增"代码"按钮,点开是 29 种语言的选择面板,选中后插入
<pre><code class="language-xxx">并全选就位,直接敲或粘贴即可。评论区渲染走的是与正文同一个yang_hl_render_block(),所以卡片、行号、复制、语言标签全部一致。二、开工前要求的全站遍历(用户会关闭原插件)
已遍历线上 967 篇文章 + 11 个页面,用真实文章内容跑产品代码:
language-标记<pre>(无<code>子元素)语言标记分布:
bash131、text47、yaml11、python8、json4、javascript1、shell1、sql1。其中
text有一个隐坑:highlight.js 没有text这个语言,不映射就静默不上色(不报错、画面纯黑字)。已显式映射到plaintext,实测 47/47 全部正常。自动识别效果实测(用户专门问的那一点):在 ≥4 行的长块上跑
highlightAuto(),18 个只有 4 个认对(22%)。所以:—这正好印证了用户"应该给用户一个选项"的判断:自动识别的价值在"聊胜于无",不能当成可靠的标注来源。
评论区的现状(关闭原插件之前):2990 条评论样本里,含
<pre>的 0 条、含<code>的 0 条 —— 此前根本贴不了代码。原因是 WordPress 评论白名单里pre不在(整条被剥掉),code在但任何属性都被剥。本轮已放行。放行
pre之后有三件事是自动变好的,但很容易被后来的"顺手优化"破坏:wpautop见到pre会自动跳过,缩进得救wptexturize被pre挡住,"不会变成弯引号make_clickable被pre挡住,URL 不会变成链接代码里已写明警告,不要动这个白名单。
三、验收矩阵(全部实际跑过)
四、过程中抓出并修掉的 4 个真实缺陷
这几个都是"判据全绿、画面是错的",值得记一笔:
① 行号一行都没画出来(最严重)
原方案用 CSS 计数器画在
span.line::before上。但 highlight.js 输出的是hljs-keyword这类 token span,根本没有span.line。CSS 规则一条不少、body.opt-ln也挂上了、判据全绿 —— 画面上空白。修法:行号改成<pre>外部的独立槽,由服务端直接输出条数。② 触屏兜底是空操作
@media (hover: none) { .cb-copy { opacity: 1 } }—— 桌面端从来没设过opacity: 0,1本来就是默认值,这条规则删掉也不会变。而验收判据"触屏下 opacity=1"测的正是默认值,永绿。修法:默认opacity: 0、:hover时1、触屏覆盖回1。③ 行号与代码逐行错位 15.5px
每套 highlight.js 官方主题都自带
pre code.hljs { padding: 1em },把代码整体压低 1em,而行号槽没有这个 padding ⇒ 屏幕上"第 1 行代码旁边写着 2"。已把 code 的 padding 归零,偏差 15.5px → 0.00px。④ 认不出语言时标签冒充作者标注(本轮最后修的)
highlightAuto()认不出语言时,代码走了removeAttribute('data-guess'),于是 CSS 的虚线+斜体失效,标签退回实线正体的——— 和作者亲手写的「纯文本」长得一模一样。等于用"已确认"的样子表示"没认出来",恰好相反。修法:认不出也保留data-guess="1"。已用反向验证证明这条判据真的会红。五、发布方式
按约定走
/theme-update/通道(zip + readme + update.json),不覆盖服务器上的主题目录,在 WordPress 后台自行升级。版本号 3.2.41 → 3.2.42。已发布 3.2.42,等待确认
发布信息
/theme-update/(只放了 zip + readme + update.json)update.json已指向 3.2.42:https://yangyq.net/theme-update/update.jsonda0d9ae(已推送到 main)升级后建议先看这几处
关于自动识别的实测效果(用户专门问的)
站内 18 个 ≥4 行的未标注块,
highlightAuto()只认对 4 个(22%),而且置信度不能当阈值(LaTeX 得分 127 是错的、php 得分 13 是对的)。所以:—,都不会冒充"作者确认过"如果实际用下来觉得自动识别反而添乱,直接在设置里关掉即可,不影响其它功能。
顺带说明一处行为变化
以前的
footer.php里有一段 Prism 兜底,本版删掉了。原因是两套高亮器跑在同一页会互相叠加:Prism 会把 hljs 生成的<span class="hljs-*">当成纯文本再包一层,产生嵌套 span、行高算错,画面会错得很莫名。现在只留 hljs 一套。这一版修掉的 4 个真实缺陷(都是"判据全绿、画面是错的")
span.line::before,但 hljs 输出的是 token span,根本没有span.lineopacity:1本来就是默认值,那条@media删掉也不变pre code.hljs{padding:1em}removeAttribute('data-guess')让虚线斜体失效每条都用反向验证(把实现改坏、确认判据真的报红)证明过,不是"跑一遍没红就算过"。
可以关单的标志:升级后上面第 2、3 条都正常。如果有任何一处不对,请在这里回一句具体现象,我先修再关。
已交付并发布(3.2.42)
需求已全部实现并上线,用户确认可以关闭。
交付内容
data-lang,36 种语言白名单 +text → plaintext/shell → bash映射).cb-ln在<pre>外面,结构上保证复制textContent拿不到行号验证:静态 44 项 + 单元 24 项全通过;20 套配色每套都有对应样式文件;
每套深色逐一点名校验;包内 232 个文件、单一顶层目录。
发布:
kratos_yang.zip,commit 带#11,走/theme-update/通道,服务器主题目录未被覆盖。遗留(另单处理):评论区贴代码工具栏的
powershell / dos / dockerfile / nginx四个选项不在 hljs 打包的 36 种里,选了插入是白板。已并入后续版本讨论,本单不再跟踪。