さわだのノート

書籍のお仕事に役立つかもしれない思いつきを記録しています。

RSS: 記事の更新情報 Rss Feed

Vimで選択範囲の文字数をカウントする

ライター/編集に必須の文字数チェック

原稿やブログの記事を書くときは、いつもVimGVim)を使っています。
原稿をちょこちょこっと直したり、正規表現で文字列を修正したりといったことがスムーズにできるのがいいですね。

しかし、ライティングになくてはならない文字数のカウント機能がVimにはありません。また、論理行数も目視で数えないといけません。行数が多くなるとこれも面倒です。

ウェブの記事では、正確に文字数や行数を求められることはあまりありません。一方、1ページあたりのスペースが有限な書籍では、結構シビアに文字数を調整する必要があります。特に、フォーマットががちがちに決まったカタログものでは必須です。

Vimスクリプトだけど、Perlで文字数と行数を計算

以上のような理由で、選択範囲の文字数と論理行数を計算するVimスクリプトを書いてみることにしました。ただ、Vimスクリプト上で2バイト文字と1バイト文字を見分けて計算するという方法がわからなかったので、実際の処理はPerlを使用しています。

Perlインターフェイスといって、Vimから直接Perlスクリプトを実行できる仕組みがあるようです。下記の記事はPythonのものですが、使い方はPerlPythonも大して変わらないので、十分参考になるかと思います。

Vim-users.jp - Hack #132: Pythonインタフェースを使う(1)
Vim-users.jp - Hack #136: Pythonインタフェースを使う(2)

あとは、

h perl

でヘルプを覗いてみてください。

文字数は、半角英数記号は2文字で1文字分として計算しています。論理行は、カレントウィンドウのウィンドウ幅で文字数を除算することで割り出しています。

ここら辺の仕組みは、あまり好ましくない方法をとっているかもしれません。

function! Count_Char() range
    let str = getline(a:firstline, a:lastline)

    if line('$') > 1000
        let stable = 5
    else
        let stable = 4
    endif
    let word_of_line = winwidth(0) - stable

perl << EOF
use strict;
use warnings;
use utf8;
use feature qw/say switch/;

use open IO => qw/:utf8 :std/;
use Encode qw/encode decode/;

# 日本語文字列を渡すときはdecodeしないと化ける
my $str = decode('utf-8', VIM::Eval('str'));

my $line = 0;
my $word_of_line = VIM::Eval('word_of_line') / 2;
my $gross_words = 0;

foreach (split /\n/, $str) {
    # 1行あたりの文字数のカウント
    my @char = m|\p{InBasicLatin}|g;
    my @wide = m|\P{InBasicLatin}|g;
    my $num_char = int (($#char + 1) / 2 + 0.5);
    my $num_wide = $#wide + 1;

    # 行数の計算。
    my $test = ($num_char + $num_wide) / $word_of_line;
    # 正規表現で行数を判定。整数以外は+1して丸める。
    if ($test =~ /^\d+$/) {
        $line += $test;
    } else {
        $line += int ($test + 1);
    }

    # 合計文字数の計算
    $gross_words += $num_char + $num_wide;
}
VIM::Msg("現在の一行の文字数は $word_of_line です");
VIM::Msg("選択範囲の論理行数は $line ライン分です");
VIM::Msg("選択範囲の総文字数は $gross_words です");
EOF
endfunction

ショートカットを設定する

あとは、適当なキーに割り当てます。
ビジュアルモード中に利用できるように設定しておくとよいかと思います。

vnoremap <leader>ii :call Count_Char()<cr>

実際に使ってみました。
f:id:takepierrot:20111225082220p:image
何とか動いてくれてます。