React Syntax Highlighterで差分行をハイライト(diff highlight)する
やりたいこと
Reactでコードをシンタックスハイライトする際に、ライブラリの候補として挙がってくるreact-syntax-highlighter 。
行番号表示など十分な機能が揃ってはいるが、唯一差分のハイライト(diff highlight)機能は備わっていない。
function foo() {  console.log("");  console.log("foo");}しかしブログを書く上で上記のように差分を強調したい場面は数多くあるので、素のPrism.jsの差分ハイライト機能に似たものを下記の要件で実装してみる。
- コードブロックの言語名をdiff-xxと指定することで有効になる
- +-を文頭に付けることで差分行と判定
実装方法
import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism";
export default function CodeBlock({  lang,  code,}: {  lang: string;  code: string;}) {  return <SyntaxHighlighter language={lang}>{code}</SyntaxHighlighter>;}まずSyntaxHighlighterをラップしただけのコンポーネントを作成。
import styles from "../_style/module/code-block.module.scss";import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism";
export default function CodeBlock({  lang,  code,}: {  lang: string;  code: string;}) {  const addRowNums: number[] = [];  const deleteRowNums: number[] = [];
  const diffMatch = lang.match(/^diff-(.+)$/);  if (diffMatch) {    const codeRows = String(children).split(/\n/);    const excludedPrefixCodeRows = codeRows.map((codeRow, i) => {      if (/^\+.*$/.test(codeRow)) {        addRowNums.push(i);        return codeRow.slice(1);      } else if (/^-.*$/.test(codeRow)) {        deleteRowNums.push(i);        return codeRow.slice(1);      }      return codeRow;    });
    code = excludedPrefixCodeRows.join("\n");  } else {    code = String(children).replace(/\n$/, "");  }
  return (    <SyntaxHighlighter      language={diffMatch ? diffMatch[1] : lang}      wrapLines={true}      showLineNumbers={true}      lineProps={(lineNumber) => {        if (addRowNums.includes(lineNumber - 1)) {          return { className: styles.addRow };        } else if (deleteRowNums.includes(lineNumber - 1)) {          return { className: styles.deleteRow };        }        return { className: undefined };      }}    >      {code}    </SyntaxHighlighter>  );}- 受け取ったコードを行ごとに分割し、文頭が+-であればその行数を記録- 自分の場合+-はCSSの擬似要素として表示したかったので、文字列からは除外している
 
- 自分の場合
- linePropspropsにて、差分行に専用のCSSクラスを付与- lineNumberを数値として受け取るには- showLineNumbersを- trueにしないといけないらしい(指定しないと真偽値が返ってしまう)
 
import CodeBlock from "./codeBlock";
export default function sampleComponent() {  const code = `  function foo() {-  console.log("");+  console.log("foo");}  `;  return <CodeBlock lang="diff-ts" code={code} />;}あとは上記のように各propsを渡しつつコンポーネントを呼び出すだけ。
function foo() {  console.log("");  console.log("foo");}これで上記のように、差分行がハイライトされて表示されるようになった。