Luxeritas のシンタックスハイライターに他の言語を追加する

2021年9月25日WordPressGo言語,Luxeritas,カスタマイズ,テーマ,ワードプレスWordPress,Go言語,Luxeritas,カスタマイズ,テーマ,ワードプレス

記事ヘッダー_Luxeritasのシンタックスハイライターに言語を追加

今回は Luxceritas のシンタックスハイライター機能に、初期状態では選べなかった言語を追加してみます。
というのも、なんか…選べる言語に Go言語 がなかったんですよね…(涙目

Luxceritas のシンタックスハイライターに言語を追加する

早速作業に入ります。
たぶん難易度的には低めだと思うんだけど…、多少面倒くさく、少しややこしい…気がします。

Prism の言語別スクリプトを入手する

まずやることは、新たに追加したい言語用のシンタックスハイライタースクリプトを入手するところから。

Luxeritas はシンタックスハイライトの機能を「Prism」で実現しているようです。通常「Prism」のスクリプト構成だと、様々な言語用のスクリプトが1ファイルにまとまっていますが、Luxeritas では、Prism本体、HTML用、Java用、C用、PHP用…、といった感じでファイル分けされバラバラ。
なので追加したい言語用のスクリプトだけを入手すれば良さげです。

スクリプトの入手場所となる「Prism」の公式サイトはここ。

言語別のスクリプトをダウンロードする

公式サイトを開きダウンロードページに移動する。

Prism のダウンロードページに入る
Prism のダウンロードページに入る

追加したい言語のスクリプトをダウンロードする

一旦「Lunguages」のチェックをすべて外す。

言語選択で全てのチェックを外す
言語選択で全てのチェックを外す

必要な言語のチェックを入れる。
依存関係のある言語があれば、それらも自動的にチェックが入る。依存する言語は後の設定で関係してくるので覚えておくこと。

必要な言語のチェックを入れる
必要な言語のチェックを入れる

ダウンロードする。

スクリプトのダウンロード
スクリプトのダウンロード

スクリプトの不要な行を削除する

ダウンロードしたスクリプトをエディタ等で開き、追加したい言語用のスクリプトのみを残し他は削除する。

スクリプトの中身
スクリプトの中身
// 1~2行目はコメント
/* PrismJS 1.25.0
https://prismjs.com/download.html#themes=prism&languages=clike+go */
// Prism の本体(かな?)
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalSco
// clike 用のスクリプト(Go言語のハイライトをする前提になるスクリプト)
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,look
// ↓ Go言語用のスクリプト、この行だけを残して他は消す
Prism.languages.go=Prism.languages.extend("clike",{string:{pattern:/(["'`])(?:\

不要な行を削除した状態。追加したい言語用のスクリプト1行だけが残る。

不要な行を削除した状態
不要な行を削除した状態

ファイル名を次のルールで変更しておく。
ここでつける [言語名] は後の設定で絡んできます。

# ファイル名のルール
[言語名].js
# e.g.
go.js

最初から同梱されていたスクリプトはこいつら。
どのファイルが何の言語なのかはファイル名から察せると思う。あと、言語ごとのスクリプトに加えて prism.jsprism-options.js など、Prism の本体(?)も入っている。
Luxeritas は、こんな風にスクリプトをバラバラにしておいて、シンタックスハイライターが表示された時、利用している言語、それと依存関係のある言語のスクリプトのみをロードすることで、高速化を図ってるみたい。
もし追加したい言語に依存する言語用スクリプトがこの中になければ、その言語用スクリプトも同様に配置し、このあとに触るが thk_highlighter_load ファンクションの中で一緒ロードされるよう条件を加える必要が出てくる。

  • apacheconf.js
  • aspnet.js
  • autohotkey.js
  • bash.js
  • basic.js
  • batch.js
  • c.js
  • clike.js
  • cpp.js
  • csharp.js
  • css.js
  • diff.js
  • git.js
  • java.js
  • javascript.js
  • json.js
  • markup.js
  • nginx.js
  • nim.js
  • perl.js
  • php.js
  • plsql.js
  • powershell.js
  • prism-options.js
  • prism.js
  • python.js
  • r.js
  • ruby.js
  • rust.js
  • sass.js
  • sql.js
  • vbnet.js
  • vim.js

スクリプトを Luxeritas に配置する

先程入手したスクリプトを親テーマの Luxeritas に FTP かなんかで配置します。

スクリプトの配置場所を確認する

配置する場所は Luxeritas 親テーマの次の位置

/[WordPressRoot]/wp-content/themes/luxeritas/js/prism

ディレクトリ内にはシンタックスハイライターで使用可能となっている言語と紐づくスクリプトが存在するはずです。

スクリプトの配置場所
スクリプトの配置場所

スクリプトを配置する

前述のディレクトリにスクリプトを配置します。
今回はGo言語を追加したいので、先程ダウンロードした go.js (Go言語用のスクリプト)を配置。
配置したらパーミッションも変更しておく。

スクリプトを配置
スクリプトを配置

function.php にコードを追記する

続いては、子テーマの function.php に2つのファンクションを追記します。
このファンクションは、 Luxeritas の親テーマからパクってきたものに、Go言語用スクリプトも読み込ませるように少し変更しました。変更箇所はコメントで分かるようにしています。

// thk_highlighter_load() の割り込み再定義
function thk_highlighter_load( $loads, $list, $active ) {
  global $luxe, $post;

  foreach( $list as $key => $val ) {
    if( strpos( $post->post_content, '<code class="language-' . str_replace( 'highlight_', '', $key ) . '"' ) !== false ) {
      $active = true;
      break;
    }
  }
  if( $active === true ) {
    $jsdir  = TPATH . DSEP . 'js' . DSEP . 'prism' . DSEP;
    $cssdir = TPATH . DSEP . 'css' . DSEP . 'prism' . DSEP;

    if( !isset( $loads[1]['prism'] ) ) {
      $loads[0] .= thk_fgc( $jsdir . 'prism.js' );
      $loads[1]['prism'] = true;
    }

    // CSS
    if( !isset( $luxe['highlighter_css_loaded'] ) ) {
      if( isset( $luxe['highlighter_css'] ) && $luxe['highlighter_css'] !== 'none' ) {
        $highlighter_css = trim( thk_fgc( $cssdir . 'prism-' . $luxe['highlighter_css'] . '.min.css' ) );
        $highlighter_css .= 'pre[class*="language-"]{margin:20px 0 30px 0}';
        if( TPATH !== SPATH ) {
          wp_add_inline_style( 'luxech', $highlighter_css ); 
          $luxe['highlighter_css_loaded'] = true;
        }
        else {
          wp_add_inline_style( 'luxe', $highlighter_css );
          $luxe['highlighter_css_loaded'] = true;
        }
      }
    }

    // Javascript
    foreach( $list as $key => $val ) {
      if( strpos( $post->post_content, '<code class="language-' . str_replace( 'highlight_', '', $key ) . '"' ) !== false ) {
        $lang = str_replace( 'highlight_', '', $key );

        if( !isset( $loads[1][$lang] ) ) {
          // markup の場合
          if( $lang === 'markup' ) {
            // 言語ごとの読み込み
            $loads[0] .= thk_fgc( $jsdir . $lang . '.js' );
            $loads[1][$lang] = true;
          }
          /*
           * 他言語の依存チェック
           */
          // markup
          if(
            !isset( $loads[1]['markup'] ) &&
            ( $lang === 'php' || $lang === 'aspnet' )
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'markup.js' );
            $loads[1]['markup'] = true;
          }
          // css
          if(
            !isset( $loads[1]['css'] ) &&
            ( $lang === 'markup' || $lang === 'php' || $lang === 'aspnet' || $lang === 'sass' )
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'css.js' );
            $loads[1]['css'] = true;
          }
          // clike
          if(
            !isset( $loads[1]['clike'] ) &&
            // 変更箇所1:Go の依存言語として clike も読み込ませる
            //( $lang === 'markup' || $lang === 'javascript' || $lang === 'java' || $lang === 'php' || $lang === 'aspnet' || $lang === 'c' || $lang === 'cpp' || $lang === 'csharp' || $lang === 'ruby' || $lang === 'nginx' )
            ( $lang === 'go' || $lang === 'markup' || $lang === 'javascript' || $lang === 'java' || $lang === 'php' || $lang === 'aspnet' || $lang === 'c' || $lang === 'cpp' || $lang === 'csharp' || $lang === 'ruby' || $lang === 'nginx' )
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'clike.js' );
            $loads[1]['clike'] = true;
          }
          // javascript
          if(
            !isset( $loads[1]['javascript'] ) &&
            ( $lang === 'markup' || $lang === 'php' || $lang === 'aspnet' )
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'javascript.js' );
            $loads[1]['javascript'] = true;
          }
          // c
          if(
            !isset( $loads[1]['c'] ) &&
            $lang === 'cpp'
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'c.js' );
            $loads[1]['c'] = true;
          }
          // basic
          if(
            !isset( $loads[1]['basic'] ) &&
            $lang === 'vbnet'
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'basic.js' );
            $loads[1]['basic'] = true;
          }
          // sql
          if(
            !isset( $loads[1]['sql'] ) &&
            $lang === 'plsql'
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'sql.js' );
            $loads[1]['sql'] = true;
          }

          // markup 以外の場合
          if( $lang !== 'markup' ) {
            // 言語ごとの読み込み
            $loads[0] .= thk_fgc( $jsdir . $lang . '.js' );
            $loads[1][$lang] = true;
          }
        }
      }
    }

    if( !isset( $loads[1]['options'] ) ) {
      $loads[0] .= thk_fgc( $jsdir . 'prism-options.js' );
      $loads[1]['options'] = true;
    }
  }
  return $loads;
}

// thk_syntax_highlighter_list() の割り込み再定義
function thk_syntax_highlighter_list() {
  return array(
    'highlight_markup'  => 'HTML / XHTML', 
    'highlight_apacheconf'  => 'Apache Config',
    'highlight_aspnet'  => 'ASP.NET',
    'highlight_autohotkey'  => 'autoHotkey',
    'highlight_bash'  => 'Bash', 
    'highlight_basic' => 'Basic',
    'highlight_batch' => 'Batch',
    'highlight_clike' => 'Clike',
    'highlight_c'   => 'C', 
    'highlight_cpp'   => 'C++',
    'highlight_csharp'  => 'C#',
    'highlight_css'   => 'CSS',
    'highlight_diff'  => 'Diff', 
    'highlight_git'   => 'Git',
    'highlight_java'  => 'Java', 
    'highlight_javascript'  => 'Javascript',
    'highlight_json'  => 'JSON', 
    'highlight_nginx' => 'nginx',
    'highlight_nim'   => 'Nim',
    'highlight_perl'  => 'Perl',
    'highlight_php'   => 'PHP',
    'highlight_plsql' => 'PL/SQL',
    'highlight_powershell'  => 'PowerShell',
    'highlight_python'  => 'Python',
    'highlight_r'   => 'R',
    'highlight_ruby'  => 'Ruby',
    'highlight_rust'  => 'Rust',
    'highlight_sass'  => 'Sass',
    'highlight_sql'   => 'SQL',
    'highlight_vbnet' => 'VB.NET',
    'highlight_vim'   => 'Vim',
    'highlight_go'    => 'Go',  // 変更箇所2:Go言語を追加
  );
}

変更した部分について

変更箇所1について。
依存関係の解決をしている処理ですね。 Go のハイライト実現には Clike 用のスクリプトも必要なので、次の通り clike をロードする条件に go も加えました。ちなみにですが、追加した条件にある言語名 'go' は、この後出てくる変更箇所2の key 名の言語名部分と同じです。
まぁ、依存関係がなければこの辺の変更は不要みたいですがね。

          // clike
          if(
            !isset( $loads[1]['clike'] ) &&
            // 変更箇所1:Go の依存言語として clike も読み込ませる
            //( $lang === 'markup' || $lang === 'javascript' || $lang === 'java' || $lang === 'php' || $lang === 'aspnet' || $lang === 'c' || $lang === 'cpp' || $lang === 'csharp' || $lang === 'ruby' || $lang === 'nginx' )
            ( $lang === 'go' || $lang === 'markup' || $lang === 'javascript' || $lang === 'java' || $lang === 'php' || $lang === 'aspnet' || $lang === 'c' || $lang === 'cpp' || $lang === 'csharp' || $lang === 'ruby' || $lang === 'nginx' )
          ) {
            $loads[0] .= thk_fgc( $jsdir . 'clike.js' );
            $loads[1]['clike'] = true;
          }

変更箇所2について。
言語リストを返却するファンクションに Go言語 を加えます。

    'highlight_go'    => 'Go',  // 変更箇所2:Go言語を追加
    ^^^^^^^^^^^^^^
    "highlight_" + [言語名]

重要なのは key の名前で highlight_ に続く go (言語名)の部分。
この go の部分がプログラム内では $lang として扱われ、先程配置した言語用スクリプトのファイル名("$lang"+".js")だったり、 変更箇所1でも言語を識別するための値として使われてるようです。

言語名を一致させる
言語名を一致させる

ちなみに value に設定する値はラベルとして使われてる様子。(シンタックスハイライター右上だったり、編集画面のシンタックスハイライターブロックで選択できる言語一覧だったり…)

# thk_highlighter_load
-> /[WPRoot]/wp-content/themes/luxeritas/inc/load-inline.php

# thk_syntax_highlighter_list
-> /[WPRoot]/wp-content/themes/luxeritas/inc/wpfunc.php

動作確認をする

編集画面のシンタックスハイライターブロックで、言語一覧から Go が選べるようになったし、ちゃんと色分けされてますね!

Goが選べるようになった
Goが選べるようになった
package main
import "fmt"
main() {
  fmt.Println( "Hello World!" )
}

さいごに

といった感じで長くなりましたが、今回やりたかったことは達成できました。

んが、親テーマのディレクトリにスクリプトぶち込んだり、親テーマからファンクションパクって割り込み定義したりしてるので、親テーマのバージョンアップで動かねぐなるかも知んねぇです。。。

些細なことでリスクを背負い込むより、素直に作者さんに要望出したほうが面倒が少なくていいのかもね?_(:3」∠)_
というわけで、いじょ!٩( 'ω’ )و