home
スポンサーリンク
2016-03-28

JavaScriptライブラリやプラグインを使わずにシンタックスハイライトする方法

ブログやWebサイトのコンテンツを作成する際、プログラムのソースコードを記載するような場面も、エンジニアならあるかと思います。

そんなとき、記載するソースコードの可読性が少しでも向上するように、プログラミング言語の変数名や予約語などを色分けする、いわゆる「シンタックスハイライト(Syntax highlighting)」を施したい場面も多いかと思います。

そこで、大多数の筆者の方々が採用しているのが、JavaScriptベースのフロントエンド側によるシンタックスハイライトライブラリの導入です。WordPressのプラグインなどで導入する際も、多くがその手法を用いたものです。フロントエンドではなくサーバーサイドでハイライトしてくれるWordPressプラグインにはまだ出会ったことがありません。少なくとも今現在メジャーではないはずです。

JavaScriptライブラリでは、下記のようなものが一般的です。

しかし、これらのフロントエンド側で処理するJavaScriptライブラリの欠点は、表示がもたつくという点です。ブラウザでソースコードを読み込んだ直後には、ハイライトされていないソースコードが表示されてしまい、その後しばらくしてガクッガクッとレイアウトが変わることもあります。できればそういった挙動は避けたいところです。

そういった問題を避けるため、今回は、あらかじめシンタックスハイライトされたHTMLを生成する方法をご紹介します。

注意点

世の中には、様々なコマンドラインツールやサービスがありますが、今回紹介する方法は、下記の道具さえあれば誰でもできる、ローテクなものです。過度な期待は勘弁ください。

必要なもの

  • Mac (Windowsでも同様のツールがあればできるとは思いますが、Windows持ってないのでやりたい方は本記事を参考に自分でなんとかしてください)
  • IDE (リッチテキスト形式でのコピーに対応しているもの。本記事ではIntelliJ IDEAを使用)

やり方

原理は至極簡単です。IDEにソースコードを記述し、IDEによるシンタックスハイライトごとクリップボードにコピーして(リッチテキスト形式)、それをHTMLとして出力するというものです。

では、手元にあるIntelliJ IDEAで実際にやってみます。

2016-03-28 2.54.21

いつも通り、IDE上にソースコードを記述します。画像のとおり、いつも見慣れた形でシンタックスハイライトされています。フォーマットを変更したい場合は、IDEの設定などで、予め文字色やインデント幅などを決めておきます。

ソースコードの表示設定と記述が終わったら、Macに標準でインストールされている「テキストエディット」アプリを開きます(多くの場合、Launchpad -> その他 -> テキストエディットの手順)。「新規書籍」ボタンをクリックし、空のウィンドウを開きます。

そして、IDE上のソースコードを全てクリップボードへコピーし(command + A -> command + C)、開いたテキストエディットへ貼り付けます。

2016-03-28 3.06.22

リッチテキスト形式で、文字色ごとコピーできているかと思います。後は、メニューからHTMLを出力するだけなのですが、出力前に設定を見ておきましょう。「テキストエディット -> 環境設定 -> 開く/保存」から、HTML保存オプションを変更できます。

2016-03-28 3.10.09

スタイルは「インラインCSS」にしておくが吉です。

こうして、メニューから「ファイル -> 保存」で、「フォーマット」欄を「Webページ(.html)」として保存してやれば、できあがりです。

出力されたHTMLの<body>タグ内の<p>要素を丸々コピーして、ブログの記事などに埋め込めば、装飾されたコードが表示できると思います。うまく表示できない場合は、<p>タグを<pre>に変更すると、多くのブログでそのまま綺麗に表示できるかもしれません。

以下のようになります。

define([
"./core",
"./core/access",
"./data/var/dataPriv",
"./data/var/dataUser"
], function (jQuery, access, dataPriv, dataUser) {

// Implementation Summary
//
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
// 2. Improve the module's maintainability by reducing the storage
// paths to a single mechanism.
// 3. Use the same single mechanism to support "private" and "user" data.
// 4. _Never_ expose "private" data to user code (
TODO: Drop _data, _removeData)
// 5. Avoid exposing implementation details on user objects (eg. expando properties)
// 6. Provide a clear path for implementation upgrade to WeakMap in 2014

var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
rmultiDash = /[A-Z]/g;

function dataAttr(elem, key, data) {
var name;

// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if (data === undefined && elem.nodeType === 1) {
name = "data-" + key.replace(rmultiDash, "-$&").toLowerCase();
data = elem.
getAttribute(name);

if (typeof data === "string") {
try {
data = data ===
"true" ? true :
data ===
"false" ? false :
data ===
"null" ? null :

// Only convert to a number if it doesn't change the string
+data + "" === data ? +data :
rbrace.test(data) ? JSON.parse(data) :
data;
}
catch (e) {
}

// Make sure we set the data so it isn't changed later
dataUser.set(elem, key, data);
}
else {
data =
undefined;
}
}
return data;
}

jQuery.
extend({
hasData: function (elem) {
return dataUser.hasData(elem) || dataPriv.hasData(elem);
},

data: function (elem, name, data) {
return dataUser.access(elem, name, data);
},

removeData: function (elem, name) {
dataUser.
remove(elem, name);
},

// TODO: Now that all calls to _data and _removeData have been replaced
// with direct calls to dataPriv methods, these can be deprecated.
_data: function (elem, name, data) {
return dataPriv.access(elem, name, data);
},

_removeData: function (elem, name) {
dataPriv.
remove(elem, name);
}
});

jQuery.fn.
extend({
data: function (key, value) {
var i, name, data,
elem = this[0],
attrs = elem && elem.attributes;

// Gets all values
if (key === undefined) {
if (this.length) {
data = dataUser.get(elem);

if (elem.nodeType === 1 && !dataPriv.get(elem, "hasDataAttrs")) {
i = attrs.length;
while (i--) {

// Support: IE11+
// The attrs elements can be null (#14894)
if (attrs[i]) {
name = attrs[i].name;
if (name.indexOf("data-") === 0) {
name = jQuery.camelCase(name.slice(5));
dataAttr(
elem, name, data[name]);
}
}
}
dataPriv.
set(elem, "hasDataAttrs", true);
}
}

return data;
}

// Sets multiple values
if (typeof key === "object") {
return this.each(function () {
dataUser.
set(this, key);
});
}

return access(this, function (value) {
var data;

// The calling jQuery object (element matches) is not empty
// (and therefore has an element appears at this[ 0 ]) and the
// `value` parameter was not undefined. An empty jQuery object
// will result in `undefined` for elem = this[ 0 ] which will
// throw an exception if an attempt to read a data cache is made.
if (elem && value === undefined) {

// Attempt to get data from the cache
// The key will always be camelCased in Data
data = dataUser.get(elem, key);
if (data !== undefined) {
return data;
}

// Attempt to "discover" the data in
// HTML5 custom data-* attrs
data = dataAttr(elem, key);
if (data !== undefined) {
return data;
}

// We tried really hard, but the data doesn't exist.
return;
}

// Set the data...
this.each(function () {

// We always store the camelCased key
dataUser.set(this, key, value);
});
},
null, value, arguments.length > 1, null, true);
},

removeData: function (key) {
return this.each(function () {
dataUser.
remove(this, key);
});
}
});

return jQuery;
});

ブログごとの細かいデザインの調整は必要になるかもしれませんが、大体1度CSSファイルを編集してしまえば済むのでそんなに手間ではないかと思います。

慣れれば、画像を記事に貼り付けるのと同じような感覚で、チャチャッとできると思いますので、ぜひやってみてください。

まとめ

これで、重た〜いシンタックスハイライト機能とおさらばできます。

それどころか、IDEのシンタックスハイライトはJavaScriptライブラリとは比べ物にならないくらい優秀なので、より見やすいソースコードにもなりますし、IDEの設定次第で好きな文字色やインデント幅に変更することも可能です。

ローテクな方法ですが、試してみる価値ありです。ぜひ、どうぞ。

コメントを残す

スポンサーリンク
スポンサーリンク
ツイートする
シェアする
オススメする
ブックマークする
購読する