Static html generation with prismjs - how to enable line-numbers?

萝らか妹 提交于 2020-12-09 17:42:56

问题


I'm using node.js to generate static html files from code, formatting them with prismjs. Within my app, I do not have access to an HTML renderer that supports Javascript (I'm using 'htmllite'). So I need to be able to generate HTML that does not require Javascript.

const Prism = require('prismjs');
const loadLanguages = require('prismjs/components/');
loadLanguages(['csharp']);
const code = '<a bunch of C# code>';
const html = Prism.highlight(code, Prism.languages.csharp, 'csharp');

This works great. But I want to use the line-numbers plugin and don't see how to make it work. My <pre> has the line-numbers class, and I get a bigger left margin, but no line numbers.


回答1:


PrismJS needs DOM for most plugins to work. After looking at the code inside plugins/line-numbers/prism-line-numbers.js#L109, we can see that the line numbers is just a span element with class="line-numbers-rows" that it contains an empty span for each line. We can emulate this behavior without DOM by just using the same regular expression that prism-line-numbers uses to get the lines number and then compose a string that has the html code of the span.line-numbers-rows and add an empty string <span></span> for each line.

Prism.highlight runs only 2 hooks, before-tokenize and after-tokenize. We'll use after-tokenize to compose a lineNumbersWrapper string that contains the span.line-numbers-rows element and the empty span line elements:

const Prism = require('prismjs');
const loadLanguages = require('prismjs/components/');
loadLanguages(['csharp']);

const code = `Console.WriteLine();
Console.WriteLine("Demo: Prism line-numbers plugin with nodejs");`;

// https://github.com/PrismJS/prism/blob/master/plugins/line-numbers/prism-line-numbers.js#L109
var NEW_LINE_EXP = /\n(?!$)/g;
var lineNumbersWrapper;

Prism.hooks.add('after-tokenize', function (env) {
  var match = env.code.match(NEW_LINE_EXP);
  var linesNum = match ? match.length + 1 : 1;
  var lines = new Array(linesNum + 1).join('<span></span>');

  lineNumbersWrapper = `<span aria-hidden="true" class="line-numbers-rows">${lines}</span>`;
});

const formated = Prism.highlight(code, Prism.languages.csharp, 'csharp');
const html = formated + lineNumbersWrapper;

console.log(html);

This will output:

Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"Demo: Generate invalid numbers"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span>

which has span.line-numbers-rows at the end:

<span aria-hidden="true" class="line-numbers-rows">
  <span></span>
  <span></span>
</span>

Now if we use that output in a pre.language-csharp.line-numbers code.language-csharp element, we'll get the proper line numbers result. Check this Codepen that has only themes/prism.css and plugins/line-numbers/prism-line-numbers.css and properly displays line numbers with the above outputted code.

Note that each line (except the first one) has to be markup intended for the code to appear properly and that's because we're inside a pre.code block, but I guess you already know that.

UPDATE

In case you don't rely on CSS and you want just a line number before each line, then you can add one by splitting all the lines and add each index + 1 with a space padding at the start using padStart:

const Prism = require('prismjs');
const loadLanguages = require('prismjs/components/');
loadLanguages(['csharp']);

const code = `Console.WriteLine();
Console.WriteLine("Demo: Prism line-numbers plugin with nodejs");`;

const formated = Prism.highlight(code, Prism.languages.csharp, 'csharp');

const html = formated
  .split('\n')
  .map((line, num) => `${(num + 1).toString().padStart(4, ' ')}. ${line}`)
  .join('\n');

console.log(html);

Will output:

   1. Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   2. Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span><span class="token string">"Demo: Prism line-numbers plugin with nodejs"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>



回答2:


Maybe this help you:

Codepen with error:

link

(the codepen lines exceed the limit of stack-overflow !)

Codepen working OK:

link

(the codepen lines exceed the limit of stack-overflow !)

Changes between both:

pre.line-numbers {
	position: relative;
	padding-left: 3.8em;
	counter-reset: linenumber;
}

pre.line-numbers>code {
	position: relative;
}

.line-numbers .line-numbers-rows {
	position: absolute;
	pointer-events: none;
	top: 0;
	font-size: 100%;
	left: -3.8em;
	width: 3em;
	/* works for line-numbers below 1000 lines */
	letter-spacing: -1px;
	border-right: 1px solid #999;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

.line-numbers-rows>span {
	pointer-events: none;
	display: block;
	counter-increment: linenumber;
}

.line-numbers-rows>span:before {
	content: counter(linenumber);
	color: #999;
	display: block;
	padding-right: 0.8em;
	text-align: right;
}



回答3:


I had a React website with code snippets, and I used prismjs node module like that:

SourceCode.js

import * as Prism from "prismjs";
export default function SourceCode(props) {
    return (
        <div>
            <div style={{ maxWidth: 900 }}>
                <pre className="language-javascript" style={{ backgroundColor: "#272822", fontSize: "0.8em" }}>
                    <code
                        style={{ fontFamily: "Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace" }}
                        dangerouslySetInnerHTML={{
                            __html: Prism.highlight(props.code, Prism.languages.javascript, "javascript"),
                        }}
                    />
                </pre>
            </div>
        </div>
    );
};

Then I decided to add line-numbers plugin and I had hard time figuring out how to make it work with Node.js and React. The problem was that line-numbers used DOM, and one does not simply use DOM in Node.js.

What I finally did. I uninstalled prismjs module and did it in an old fashion way :).

index.html

<html lang="en-us">
    <head>
        <meta charset="utf-8" />
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
        <title>SciChart Web Demo</title>
        <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/themes/prism-okaidia.min.css" />
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/plugins/line-numbers/prism-line-numbers.min.css" />
        <script async type="text/javascript" src="bundle.js"></script>
    </head>
    <body>
        <div id="react-root"></div>
        <script>
            window.Prism = window.Prism || {};
            Prism.manual = true;
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/prism.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
    </body>
</html>

SourceCode.js

import * as React from "react";

export default function SourceCode(props) {
    React.useEffect(() => {
        window.Prism.highlightAll();
    }, []);
    return (
        <div>
            <div style={{ maxWidth: 900 }}>
                <pre
                    className="language-javascript line-numbers"
                    style={{ backgroundColor: "#272822", fontSize: "0.8em" }}
                >
                    <code style={{ fontFamily: "Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace" }}>
                        {props.code}
                    </code>
                </pre>
            </div>
        </div>
    );
};


来源:https://stackoverflow.com/questions/59508413/static-html-generation-with-prismjs-how-to-enable-line-numbers

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!