如何使用 highlight.js (React) 正确地逐行突出显示代码?

How can I correctly highlight a line by line code, using highlight.js (React)?

我需要突出显示一段代码,但同时,我需要将代码放在单独的编号行中,为每行代码留下注释,就像在 [=14 上所做的那样=].我设法做到了这一点,但因为我突出显示了每一行的代码,所以多行上的“SQL”代码将无法正确突出显示(仅突出显示第一行)并且我无法修复此问题。你有什么建议吗?

       if (data.success === 1) {
          setCodeLanguage(translateLanguage(data.code.language));
          let rows = [];
          data.code.source_code.split("\n").forEach((line, index) => {
            rows.push(
              <tr key={index} className="line">
                <td className="line-number">{index + 1}</td>
                <td id={"plus" + index} className="plus-square-line">
                  <PlusSquareTwoTone className="plus-square" />
                </td>
                <td id={"codeblock" + index}
                  className={'language-' + codeLanguage} style={rowStyle}>
                  {line}
                </td>
              </tr>
            );
          });
          setCodeRows(rows);
          for (let i = 0; i < codeRows.length; ++i) {
            hljs.highlightBlock(document.getElementById("codeblock" + i));
          } 

您不能简单地拆分 \n 上的输出,因为跨度可以跨越线边界:

var x = <span class="string>"This is a
really long string
that spans multiple lines
super annoying"</span>

您必须编写代码将其变成:

var x = <span class="string">"This is a</span>
<span class="string">really long string</span>
<span class="string">that spans multiple lines</span>
<span class="string">super annoying"</span>

IE,在任何时候你都必须跟踪所有打开的标签并在一行结束时关闭它们,然后在下一行开始之前打开它们。

可以这么说,这并不是 Highlight.js 的典型用例,因此您可以自己构建它。


没有简单的方法可以做到这一点,但是如果您访问原始解析树(而不是生成的 HTML),您可以编写一些遍历它的东西逐个节点并找出线条的位置。如何访问解析树对象(发射器):

highlight(code).__emitter

或者您可以简单地将发射器替换为您自己的自定义发射器。查看源文件以了解您需要实现的 API:

https://github.com/highlightjs/highlight.js/blob/master/src/lib/token_tree.js

然后你必须遍历树,跟踪哪些标签是打开的,当你找到一行结束时你需要关闭标签...... re-opening 下一个线。 IE,你几乎需要从解析树开始写你自己的 HTML 渲染引擎。

请注意:整个 __emitter API 不被视为 public API 的一部分,可能会在未来的更新中随时更改或中断 - 虽然通常只要您确保测试新版本,就应该“相当安全”地使用它。我没有计划在不久的将来对其进行重大更改。

[免责声明:我是当前的 Highlight.js 维护者。]

我们最近不得不实现行号,这是我们在 Typescript 中将其添加为插件的实现:

hljs.addPlugin({
  "after:highlight": (params: { value: string; }) => {
    const openTags: string[] = [];
    
    params.value = params.value.replace(/(<span [^>]+>)|(<\/span>)|(\n)/g, match => {
      if (match === "\n") {
        return "</span>".repeat(openTags.length) + "\n" + openTags.join("");
      }
      
      if (match === "</span>") {
        openTags.pop();
      } else {
        openTags.push(match);
      }
      
      return match;
    });
  },
});

这会将 highlightjs 输出的字符串更改为可以很容易地被 \n 拆分的字符串。

如果您在客户端执行此操作,则可能需要使用 "after:highlightBlock""after:highlightElement",具体取决于 highlightjs.

的版本

或者您可以只在输出上调用该函数,然后将其拆分为 \n