はじめに
highlight.jsで行番号を付ける。p5.jsを使う。
highlight.jsはコードのハイライトをしてくれるライブラリである。主な使い方はこっちにいろいろ書いてある。
自分の書いた記事:
今回は行番号を付けたいので、色々調べたところ、次の記事が参考になった。
ただそっくりそのままではなぜかうまくいかなかったので、ちょっと加工してる。
こんな感じで番号が付く。横にスクロールしても隠れない:
必須ではないが、あると便利な場合もあるので紹介したい。
コード全文
/*
highlight.js:https://highlightjs.org/
コードハイライト:https://qiita.com/tadnakam/items/99088d78512a20e75ff3
行番号:https://cookbook88.com/js-cookbook/thirdparty/highlight-linenumber.php
*/
function setup(){
createCanvas(400, 400);
noStroke();
fill(0, 128, 255);
}
function draw(){
background(0,9);
for(let i = 0; i < 4; i++){
let prg = (frameCount % 80) / 80;
prg = prg * prg * (3 - 2 * prg);
circle(100 + 200 * prg, 100, 8);
translate(400, 0);
rotate(PI/2);
}
}
html,
body {
margin: 0;
padding: 0;
}
main{
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
width:400px;
margin:0 auto;
}
.codeblock {
width:100%;
height:300px;
margin:10px;
border:4px solid teal;
overflow:scroll;
overflow-x:hidden; /* なんか重複してたので */
}
pre {
margin:0;
padding:0;
}
code {
font-size:18px;
line-height: 150%;
font-family: Consolas;
}
.filename {
margin:4px;
font-size:1em;
font-family:sans-serif;
font-style:italic;
align-self:flex-start;
}
/* 行番号ここから */
pre{
counter-reset: rowNumber;
position:relative;
}
pre span.row-number{
counter-increment: rowNumber;
}
pre span.row-number::before {
content:counter(rowNumber);
/* 以下少しだけ装飾 */
width: 2rem;
display: inline-block;
color: rgba(255,255,255,0.4);
position:absolute;
left:0rem;
margin:0 2rem 0 0;
text-align:right;
padding:0 0.5rem 0 0;
background-color:rgb(0, 32, 32);
}
/* 行番号ここまで */
.hljs-comment{
color:aquamarine;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/p5@2.0.3/lib/p5.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/base16/oceanicnext.min.css"> <!-- パレット -->
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script> <!-- 生成機 -->
</head>
<body>
<main>
<p class="filename">mySketch.js</p>
<div class="codeblock">
<pre><code class="javascript">/* ここにコメント🐟
highlight.js:https://highlightjs.org/
コードハイライト:https://qiita.com/tadnakam/items/99088d78512a20e75ff3
行番号:https://cookbook88.com/js-cookbook/thirdparty/highlight-linenumber.php
*/
function setup(){
createCanvas(400, 400);
noStroke();
fill(0, 128, 255);
}
/* ここに長い長い長い長~~~~~~~~~~~~~~~~~~~~いコメント(確認用) */
function draw(){
background(0,9);
for(let i = 0; i < 4; i++){
let prg = (frameCount % 80) / 80;
prg = prg * prg * (3 - 2 * prg);
circle(100 + 200 * prg, 100, 8);
translate(400, 0);
rotate(PI/2);
}
}
</code></pre>
</div>
<p class="filename">style.css</p>
<div class="codeblock">
<pre><code class="css">html,
body {
margin: 0;
padding: 0;
}
.codeblock {
width:400px;
height:300px;
margin:10px;
border:4px solid teal;
overflow:scroll;
overflow-x:hidden; /* なんか重複してたので */
}
pre {
margin:0;
padding:0;
}
code {
font-size:18px;
line-height: 150%;
font-family: Consolas;
}
.filename {
margin:4px;
font-size:1em;
font-family:sans-serif;
font-style:italic;
}</code></pre>
</div>
</main>
<script>
new Promise((resolve, reject) => {
const codes = document.querySelectorAll("code");
for(const code of codes){
// paddingを使って行番号エリアを用意する
code.style.setProperty("padding-top", "0.3rem");
code.style.setProperty("padding-right", "0.3rem");
code.style.setProperty("padding-bottom", "0.3rem");
code.style.setProperty("padding-left", "3.2rem");
code.style.setProperty("background-color", "rgb(0,32,32)");
}
hljs.addPlugin({
'after:highlightElement': ({ el, result}) => {
// 「行頭」という概念的な位置にspanタグを配置するアルゴリズムらしいです
el.innerHTML = result.value.replace(/^/gm,'<span class="row-number"></span>');
}
});
resolve(true);
}).then((res) => {
hljs.highlightAll();
});
</script>
<script src="main.js"></script>
</body>
</html>
解説
highlight.js
最初にjsdelivrからファイルを読み込む。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/base16/oceanicnext.min.css"> <!-- パレット -->
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script> <!-- 生成機 -->
コメントアウトだけいじっている:
.hljs-comment{
color:aquamarine;
}
次に、<pre><code>~</code></pre>の範囲にコードを書く。classで言語の指定をする。最後にhljsでhighlightAll()を実行すればコードにハイライトが付く。ただし、
for(let i = 0; i < 4; i++){
見ればわかるようにエスケープしている。タグに使う記号は使えないので、適宜「<」や「>」などで置き換える必要がある。さっきのpreとかcodeとか述べた部分もソースは次のように書いている:
まず<pre><code>~</code></pre>の範囲にコードを書く。
行番号の要素を作る
先に述べた記事にあるように、プラグインを作成できる。hljsのaddPluginを使うと、highlightElementを実行した後の処理を勝手に用意できる。その辺りの処理はこちら:
new Promise((resolve, reject) => {
const codes = document.querySelectorAll("code");
for(const code of codes){
// paddingを使って行番号エリアを用意する
code.style.setProperty("padding-top", "0.3rem");
code.style.setProperty("padding-right", "0.3rem");
code.style.setProperty("padding-bottom", "0.3rem");
code.style.setProperty("padding-left", "3.2rem");
code.style.setProperty("background-color", "rgb(0,32,32)");
}
hljs.addPlugin({
'after:highlightElement': ({ el, result}) => {
// 「行頭」という概念的な位置にspanタグを配置するアルゴリズムらしいです
el.innerHTML = result.value.replace(/^/gm,'<span class="row-number"></span>');
}
});
resolve(true);
}).then((res) => {
hljs.highlightAll();
});
一応順序が大事かなと思ったので、必要ないかもだけどプロミス化。それで、codeタグを全部とらえてあとからpaddingと背景色を決めている。pluginでは、正規表現を使っている。正規表現で行頭をとらえるには/^/gmという記法を使うらしい。この辺は詳しくない...
助けてAIさん:
「/^/gm」は正規表現のパターンで、文字列の先頭にマッチします。
^ は文字列の先頭を意味し、g はグローバルマッチ、m はマルチラインマッチをそれぞれ指定します。
このパターンを「gm」と略して表現することがあります。
具体的に説明すると:
^:
この記号は、文字列の先頭にマッチします。
例えば、^abc というパターンは、abc で始まる文字列にマッチします。
g (グローバルマッチ):
このフラグは、パターンに一致するすべての箇所にマッチさせます。
例えば、a というパターンは、文字列 abcabc の中で最初の a にしかマッチしませんが、
a/g とすると、すべての a にマッチします。
m (マルチラインマッチ):
このフラグは、文字列を複数行として扱い、各行の先頭に^がマッチするようにします。
例えば、^abc というパターンは、abc で始まる文字列にマッチしますが、
m フラグを付けると、複数行の文字列の各行の先頭にabcがあればマッチします。
したがって、/^/gm は、文字列の先頭にマッチし、さらに複数行の文字列の各行の先頭にもマッチすることを
意味します。
正規表現は詳しくないのでいつかちゃんと調べたい...検索機能とかで使うようです。
そういうわけで、「/^/gm」とするとすべての行頭、また行が長くて複数行にわたる場合もそれぞれの行頭にマッチするというわけです。多分。
で、それをタグ記号で置き換えることで、行頭にタグを挿入している。
el.innerHTML = result.value.replace(/^/gm,'<span class="row-number"></span>');
このrow-numberというspanタグを使う。
cssの準備
cssの方でrow-numberの中身をcssカウンターにより用意する。
/* 行番号ここから */
pre{
counter-reset: rowNumber;
position:relative;
}
pre span.row-number{
counter-increment: rowNumber;
}
pre span.row-number::before {
content:counter(rowNumber);
/* 以下少しだけ装飾 */
width: 2rem;
display: inline-block;
color: rgba(255,255,255,0.4);
position:absolute;
left:0rem;
margin:0 2rem 0 0;
text-align:right;
padding:0 0.5rem 0 0;
background-color:rgb(0, 32, 32);
}
/* 行番号ここまで */
冒頭に上げたソースそのままではないです。preでカウンターをリセットして、その中のspanタグでrow-numberクラスを持つものに対してincrementしているので1,2,3,...と増えていきます。そのbeforeにcontentの形で数を入れればとりあえず行番号はできます。
以降、装飾。
まずdisplay:inline-blockで、marginで数の右に空白を設けています。text-align:rightで右寄せ。background-colorはcodeと合わせています。positionはabsoluteで、preがrelativeでleft:0としており、これにより横スクロールしても動かなくなります。元のコードは不親切で、どこにrelativeを入れるのか分かんなかったので...
以上です。サンプルサイト:
おわりに
拝読感謝。p5のスケッチは特に意味は無いんですが、OpenProcessingでやってる以上、何かしら必要と思ったので入れました。