恥は/dev/nullへ by 初心者

プログラミング素人がのろのろと学んだことをつづっています♪

毎回使うタグを挿入するVimコマンド

はてなブログを書いていて、必ず使うタグが2種類あります。

<br> 改行
<span style="color: #b388dd"> </span> 見出しのテキスト色を指定


今まで brタグは必要な箇所に手入力し、spanタグは辞書登録して使っていました。しかし、文章を全て書き終えてから一発でタグを挿入した方が手間が少ないので、Vimコマンドにすることにしました。

目次


留意事項(特殊文字の入力)

この記事には ^M という文字が登場します。この文字を.vimrcに記述する場合は、

CTRL + v  CTRL + m

または

CTRL + v  RETURN

とする必要があります。

H1レベルの見出しにspanタグを挿入する

たとえば、次のような文章があったとします(Markdown)。

# 卵の中身を取り出す
器の縁に卵をぶつけて、ヒビを入れます。ヒビが入ったら両手で
卵を支えながら、慎重に器に中身を落とします。

# 卵をとく
精神を統一し、器を片手で支え、もう一方の手に持った道具(箸
など)で卵をかき回します。

この文章に登場する見出しは # で始まる「卵の中身を取り出す」と「卵をとく」です。これらのテキスト色を変更するためにspanタグを挿入して、以下のようにしようと思います。

# <span style="color: #b388dd">卵の中身を取り出す</span>
器の縁に卵をぶつけて、ヒビを入れます。ヒビが入ったら両手で
卵を支えながら、慎重に器に中身を落とします。

# <span style="color: #b388dd">卵をとく</span>
精神を統一し、器を片手で支え、もう一方の手に持った道具(箸
など)で卵をかき回します。


substituteコマンドで実行するなら、次のような感じでしょうか。

:%s/^# \([^<]..*\)/# \<span style="color: #b388dd"\>\1\<\/span\>/

ちなみに、[^<] は、既にタグを挿入してある見出しにこのコマンドが適用されないようにするためのものです。

これをコマンドとして利用できるよう、.vimrcに次の1行を追記しました。

" SetH1 コマンド
command SetH1 %s/^# \([^<]..*\)/# \<span style="color: #b388dd"\>\1\<\/span\>/


段落の後に空行が2つあったらbrタグを挿入

次はbrタグの挿入です。次のようなテキストがあるとします。

(1) 卵を割って容器に中身を落とします。

(2) 箸などで卵をかき回します。  


(3)卵をとき終えたら、卵に醤油をたらします。

このテキストに登場する3つのパートのうち、(2)と(3)の間には空行が2つあります。しかし、はてなブログにこのテキストを貼り付けると、この空行2つは1つの空行にまとめられてしまいます。

そこで、(2)の行末に半角スペースを2つ挿入し、その次の行にbrタグを挿入します。イメージとしては次のような感じです。

(1) 卵を割って容器に中身を落とします。

(2) 箸などで卵をかき回します。[space][space]
<br>

(3)卵をとき終えたら、卵に醤油をたらします。


substituteコマンドで実行するなら、次のような感じでしょうか。

:%s/\n^\n^\n/  ^M<br>^M^M/


これをコマンドとして利用できるよう、.vimrcに次の1行を追記しました。

" SetBRtag コマンド
command SetBRtag %s/\n^\n^\n/  ^M<br>^M^M/


関数にまとめたもの

上記のように個々のコマンドとして利用してもよいのですが、どちらのコマンドも毎回使用するので、まとめて関数にしてみました(Vimの関数について余り分かっていないため手探りですが・・・)。

" functionを使ったもの
function SetBlogTags()
    " [^<]はタグ挿入済みの行を除外するためのもの
    silent %s/^# \([^<]..*\)/# \<span style="color: #b388dd"\>\1\<\/span\>/
    silent %s/\n^\n^\n/  ^M<br>^M^M/
endfunction

command SetBlogTags call SetBlogTags()


" defを使ったもの
def SetBlogTags()
    silent :%s/^# \([^<]..*\)/# \<span style="color: #b388dd"\>\1\<\/span\>/
    silent :%s/\n^\n^\n/  ^M<br>^M^M/
enddef

command SetBlogTags call SetBlogTags()


これらのいずれかを.vimrcに記述して

:SetBlogTags

を実行します。

defとfunctionの違い

試してみたところ、functionを使った場合とdefを使った場合とで次の違いがありました。

・defを使ったものでは、%sの前に : が必要(functionを使ったものでは不要)
・defを使ったものでは、関数定義の途中にコメントを入れるとエラーになる


少し手を加えたもの(実験)

上述の内容を実際に使ってみたら、少々物足りないことに気づいたのでもう少し手を加えてみました。なお、以下の自作関数では、H1レベルではなくH4レベルの見出し(####)に対してテキスト色を指定しています。

function SetBlog()  
    silent %s/^#### \([^<]..*\)/#### \<span style="color: #b388dd"\>\1\<\/span\>/  
    silent %s/\n/  ^M/                   " 行末に半角スペース2つを挿入  
    silent %s/^\(```[a-z].*\)  \n/\1^M/  " ```hitmlや```vim等の行末にある半角スペースを除去  
    silent %s/^  \n^  \n/<br>^M  ^M/     " 空行が2つ連続している部分は1行目をbrタグにする  
    silent %s/  \n^  \n/  ^M<br>^M/      " 半角space2つの直後にある空行をbrタグにする  
    call cursor(1,1)                     " カーソルをファイルの先頭に移動  
endfunction  

はてなブログでは「```html」や「```vim」等の行末に半角スペースがあるとシンタックスハイライトが有効にならなかったので、4行目で半角スペースを除去しています。


(追記)
試してみたら問題が生じました。具体的には、```で囲んだ範囲(コード等を記述してある範囲)の中にまで、半角スペースやbrタグが挿入されてしまいました。

そこで、ろくに書いたことのないVimscriptを書いてみました。

function SetBlog()
    silent! %s/^  *```/```/             " ```の前にspaceがあったら除去
    let inside_quote = 0
    for i in range(1, line("$"))
        call cursor(i, 1)
        let line_content = getline(line("."))
    
        if line_content[:2] =~ "```"
            if inside_quote == 0
                let inside_quote = 1
            else
                let inside_quote = 0
            endif 
        endif
        if inside_quote == 0
            silent s/\n/  ^M/           " 行末に半角spaceを2つ挿入
        endif
    endfor
    unlet inside_quote
    unlet line_content

    silent! %s/^  \n^  \n/<br>^M  ^M/  " 半角space2つの行が2つ連続している部分は1行目を<br>にする
    silent! %s/  \n^  \n/  ^M<br>^M/    "「半角space2つ(行末)」「(行頭)半角space2つ(行末)」の後半を<br>にする
    silent! %s/^#### \([^<]..*\)  /#### \<span style="color: #b388dd"\>\1\<\/span\>/  " H4見出しの色を設定
    call cursor(1,1)                   " カーソルをファイルの先頭に移動
endfunction

このコードを書くまで知らなかったのですが、silent!は便利だと感じました。

たとえば、2行目では「バッククォート3つ」の前に半角spaceがあったら半角spaceを除去していますが、そのような箇所が存在しない場合、エラーメッセージが表示されます。これを抑制するためにsilent!を使用しています。

恥をさらしますと、ファイルの行数を取得する方法が分からなくて、line("$")の代わりに、当初は以下のようなコードを書いていました(汗)。

let line_count = len(readfile(expand('%:t')))


ひとまず問題は解決したものの、美しくないですね。正規表現の知識があったらもっと簡潔に処理できるんじゃないかしらと思いまして・・・。