MacのマークダウンエディタKobitoでコードに行番号を表示させてみたよ

Qiitaと連動したMacのマークダウンエディタKobitoでコードに行番号を表示できるようにしました。やることはKobitoのアプリケーションフォルダ内のContents/Resourcesに入っているheader.html、markdown.css、highlight.pack.jsの編集だけです。

ファイルの編集

編集するファイルは

  • header.html
  • markdown.css
  • highlight.pack.js

の3つです。ターミナルで/Applications/Kobito.app/Contents/Resources/に移動するか、Finderでアプリケーションフォルダに移動した後、Kobitoを選んでダブルタップでコンテキストメニューを表示させて「パッケージの内容を表示」を選び、Contents→Resourcesと進んでください。Resources中に上記の3つのファイルが見つかると思います。

header.html

head要素内でhighlight.pack.jsを読み込んでいる部分があるのでその上に

<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

を追加してjQueryを読み込んでください。

highlight.pack.js

Kobitoではコードのシンタックスハイライトにhighlight.jsを使っています。ですがhighlight.jsは仕様で行数が表示されないようになっています。ということでファイルの最後に以下を追加してください;

$(function(){
    // ruler用のcanvas要素をbody要素内に追加
    $('body').append('&lt;canvas id="ruler"&gt;&lt;/span&gt;');
    // 各コードブロックで関数を実行
    $('pre code').each(function(){
        // コードブロック内のコードに対して改行コードを検知して各行を配列に格納
        var codeStrings = $(this).text().split(/\r\n|\r|\n/);
        // コードブロック内の行数を取得
        var lines = codeStrings.length - 1;
        // 行数を表示させるためのul要素を作成
        var $numbering = $('&lt;ul/&gt;').addClass('pre-numbering');
        // コードブロックの描画幅を取得
        var elWidth = $(this).width();
        // コード内のフォントを取得
        var textFont = $('pre code').css('font');
        // 行数表示用の要素を親要素の下に追加
        $(this)
            .parent()
            .append($numbering);
        var i,j,wraps;
        // コードの各行に関するループ
        for(i=0;i&lt;lines;i++){
            // 文字列の描画幅を取得して画面を何度折り返すかを調べる
            wraps = Math.floor(strWidth(codeStrings[i])/elWidth);
            // 行数をli要素の中に入れる
            $numbering.append($('&lt;li/&gt;').text(i+1));
            // 折り返しがある場合は折り返しの数だけ空のli要素を追加
            if(wraps&gt;0){
                for (j=0;j&lt;wraps;j++){
                    $numbering.append('&lt;li&gt; &lt;/li&gt;');
                }
            }
        }
        // canvasを使った文字列の描画幅を取得する関数
        function strWidth(str) {
            var canvas = document.getElementById('ruler');
            if (canvas.getContext) {
                var context = canvas.getContext('2d');
                context.font = textFont;
                var metrics = context.measureText(str);
                return metrics.width;
            }
            return -1;
        }
    });
});

基本はSyntax Highlighting with Highlight.js – idodev.co.uk – Dev blog of Toby Foordで書かれている通りです。jQueryで$('pre code')要素を取得して、splitで中のテキストを分割し、行数を数えたらその分だけli要素を追加していきます。このli要素内に行番号を入れます。

これだけだと一行が長い場合に折り返しに対応できません。そこでjavascriptで文字列の描画幅を取得する方法 – Qiitaで説明されているようにcanvasを使って文字列の描画幅を取得し、コードブロックの描画幅に対して何度折り返しがあるかをチェックします。そして折り返しがある場合はその分だけ空のli要素を追加しています。

markdown.css

行数表示を追加するためにCSSを編集します。markdown.cssに以下を追加してください;

/* for line numbering */
.code-frame .highlight pre {
    position: relative;
    padding: 0;
    overflow: hidden;
}
 
.code-frame .highlight pre code {
    display: block;
    overflow-y: auto;
    padding: 0.5em 0 0.5em 3.5em;
}
 
.pre-numbering {
    position: absolute;
    top: 0;
    left: 0;
    width: 2.3em;
    margin: 0;
    padding: 0.5em 0.2em 0.5em 0;
    border-right: 1px solid #C3CCD0;
    border-radius: 3px 0 0 3px;
    background-color: #EEE;
}
 
.pre-numbering li {
    list-style-type: none;
    text-align: right;
    line-height: 1.44em;
    font-size: 0.9em;
    font-family: Menlo, monospace;
    color: #AAA;
}
 
#ruler {
  visibility: hidden;
  position: absolute;
  white-space: nowrap;
}

ファイルの修正は以上です。これで新しい記事を作成するとこんな感じ

で自動的にコードに行数が表示されます。

おまけ:Kobitoを使ってMarkdownファイルをHTMLファイルに変換

ちょっと手間がかかりますが、Kobitoで作ったmarkdownファイルからHTMLファイルを抜き出せます。まずプレビュー画面でコンテキストメニューを表示させます。すると下の画像

のように「Inspect Element(要素を検証)」というのが表示されるので、それをクリックしてください。クリックするとプレビュー画面の下にWeb Inspectorが表示されます;

ここで上の画像の赤い矢印で示した緑色の「DOM Tree」をクリックすると

のように「Sounce Code」が選べるのでそれを選択します。するとインスペクタの画面内にソースコードが表示されます。これを⌘+Cでコピーします。

あとは適当なフォルダをつくってそこに空のhtmlファイルをつくり、先ほどコピーした内容をペーストします。同じフォルダに先ほど編集したhighlight.pack.js、markdown.cssに加えてgithub.min.cssをKobitoのContents/Resouces内からコピーしておくと、プレビュー画面と同じ内容を表示させることができます。pdfとして吐き出す場合はSafari等のブラウザでhtmlファイルを開き、pdfで印刷してください。