ブログを書いていて、HTMLタグを書く場合等で、ちょこちょことHTMLエスケープが必要になる場合があります。 今までは、ほとんど使う機会が無かったですし、 必要になったら文字置換したり、HTMLエスケープを提供しているサイトを探して変換していました。
ブログの更新頻度が増えたこともあり、HTMLエスケープが必要な回数が増えてきました。 そこで、少しでも楽になるようにVimのコマンドとして、HTMLエスケープ処理を作成してみました。
&
、"
、'
、<
、>
を16進数表記に変換します。
let s:replaceEntity = [
\ ['\(\(&\(#*\w\+;\)\@!\)\|&\|&\)', '\&'],
\ ['\(\"\|"\|"\)', '\"'],
\ ["\\('\\|'\\)", '\''],
\ ['\(<\|<\|<\)', '\<'],
\ ['\(>\|>\|>\)', '\>'],
\]
function! s:htmlesc() range
for l:line in range(a:firstline, a:lastline)
let l:_line = getline(l:line)
for l:replace in s:replaceEntity
let l:_line = substitute(l:_line, l:replace[0], l:replace[1], 'g')
endfor
call setline(l:line, l:_line)
endfor
endfunction
command! -range HtmlEsc <line1>, <line2> call s:htmlesc()
使い方は、ソースをそのまま.vimrc
に貼り付けるか、
ソースをファイルに保存して、.vimrc
にsource htmlesc.vim
等というようにファイルを読み込みます。source
で読み込むのではなく、runtimepath
で指定されたディレクトリ内に入れる方法もあります。
置換したい行を選択して、:HtmlEsc
コマンドを打つと対象文字が置換されます。置換は1行ごとに処理しているので、"特定範囲だけ"という処理には対応していません。
対象文字の正規表現
このリストで置換対象文字と、置換後の文字を設定しています。&(アンパサンド)
と'(シングルクォーテーション)
は少し特殊な指定をしています。
let s:replaceEntity = [
\ ['\(\(&\(#*\w\+;\)\@!\)\|&\|&\)', '\&'],
\ ['\(\"\|"\|"\)', '\"'],
\ ["\\('\\|'\\)", '\''],
\ ['\(<\|<\|<\)', '\<'],
\ ['\(>\|>\|>\)', '\>'],
\]
&
は、置換後の文字にも含まれていますので、何度も繰り返し実行すると、文字がどんどん増えてしまいます。
そこで、&\(#*\w\+;\)\@!
を指定し、&
が1文字だけ独立している場合のみ、変換対象としています。
上記の(~)\@!
と指定している箇所がポイントで、"否定先読み"と呼ばれる指定手法です。
この指定によって"&
の後に、#*\w\+;
が無い場合のみマッチする"ということにります。
具体的には、
や、"
、"
といった文字が"マッチしない"ことになります。
'(シングルクォーテーション)
に関しては、正規表現を'(シングルクォーテーション)
で囲っていません。
'(シングルクォーテーション)
で囲ってしまうと、''
のようにシングルクォーテーション自体をシングルクォーテーションでエスケープしなければならず、正常に置換することができません。
'(シングルクォーテーション)
の場合は正規表現を"(ダブルクォーテーション)
で囲み、それに応じて各記号を\(バックスラッシュ)
でエスケープしてやります。
置換処理
先ほどの正規表現を使って、文字を置換していきます。
function! s:htmlesc() range
for l:line in range(a:firstline, a:lastline)
let l:_line = getline(l:line)
for l:replace in s:replaceEntity
let l:_line = substitute(l:_line, l:replace[0], l:replace[1], 'g')
endfor
call setline(l:line, l:_line)
endfor
endfunction
function
の右にある!
は"関数の再定義"の指定です。
この指定が無いと、スクリプトを再読込した場合にエラーが発生してしまいます。
関数にrange
を指定すると、行範囲を取得することができます。
そして、関数内で行範囲を示すa:firstline
が使えるようになります。と
a:lastline
s:replaceEntity
に格納された正規表現と置換後の文字をsubstitute()
に指定して1つずつ文字を置換します。
ちなみに、Vimならば:TOhtml
というコマンドもあります。:TOhtml
はVimに表示されている内容を、別ファイルとしてHTML化することができます。
改行や色も含めて出来る限りVimの見た目に近い形で出力してくれます。もちろん必要に応じてHTMLエスケープも対応しています。
範囲選択もできますので、ある程度の範囲をまとめてエスケープしつつ、綺麗に表示したい場合には便利かもしれないですね。
:TOhtml
使った表示
1 " 変換対象文字指定 2 let s:replaceEntity = [ 3 \ ['\(\(&\(#*\w\+;\)\@!\)\|&\|&\)', '\&'], 4 \ ['\(\"\|"\|"\)', '\"'], 5 \ ["\\('\\|'\\)", '\''], 6 \ ['\(<\|<\|<\)', '\<'], 7 \ ['\(>\|>\|>\)', '\>'], 8 \] 9 10 function! s:htmlesc() range 11 for l:line in range(a:firstline, a:lastline) 12 let l:_line = getline(l:line) 13 for l:replace in s:replaceEntity 14 let l:_line = substitute(l:_line, l:replace[0], l:replace[1], 'g') 15 endfor 16 call setline(l:line, l:_line) 17 endfor 18 endfunction 19 20 command! -range HtmlEsc <line1>, <line2> call s:htmlesc()
今回説明で利用したソースはこちらにあります。
参考サイト
Qiita - vimに自作コマンドを実装するhttp://qiita.com/shimbaroid/items/f2ad60c203ccdff7da16
vim-users.jp - Hack #158: ユーザコマンドを定義する
http://vim-jp.org/vim-users-jp/2010/06/29/Hack-158.html
IBM developerWorks® - Vim エディターのスクリプトの作成: 第 2 回 ユーザー定義関数
https://www.ibm.com/developerworks/jp/linux/library/l-vim-script-2/