smartchr.vim導入以来、
快適に入力するためにi(nore)?map周りの設定をいろいろ変更していて、
1キーでいかに複数の文字を入力するか、
というアプローチでいろいろ考えていたんだけど、
こちらのエントリをみて目から鱗が落ちた。
http://ujihisa.nowa.jp/entry/155f135406
押しやすいキーの組み合わせの2ストロークで、
単一の文字を入力することによって、指の負荷を減らす(=FPを節約)、
というのもありなのか!なるほど!!
i(nore)?mapでの複数ストロークによる入力なんて考えもしなかったす。
ということで、このアプローチを真似させていただくことにしますた。
元エントリだと、:をプレフィックスキー的につかっているけど、
自分としてはもうちょっと押しやすいキーがいいと思った。
で、なにがいいかを考えたところ、
Jキーは一等地にあるくせに、テキスト入力の頻度が少ないような気がした。
(キー配列はQWERTY)
ただまあ、なんとなくでやるのも何なので、
過去に自分が書いたソースコードで、各文字がどれくらい使われているのかを計測してみることにした。
こんな感じのスクリプトを書いて、自分のソースコードを食わせてみた。
# encoding: cp932 # 使い方: <スクリプトファイル> [カウント対象ファイルが置かれたディレクトリ]... # パラメータを指定しなかったら、カレントディレクトリを再帰的に集計 import os, sys # 検出対象ディレクトリ dirs = sys.argv[1:] if len(dirs) == 0: dirs.append(os.path.abspath(".")) # 処理対象ファイル拡張子 targetext = [ '.c', '.cpp', '.h' ] # 再帰的に処理するか? recurse = True # エンコード方法 fencode = 'shift_jis' # 除外パターン exclude_patterns = [ '' ] # 指定したキャラクタが集計対象文字かどうかを判定 def isTargetChr(c): return c.isalnum() or c in ';:+-*/=<>#,.?{}[]()&#"\'!%~^|_ \t' # キャラクタ毎の出現数を集計 chrMap = [0] * 256 for d in dirs: files = os.listdir(d) for f in files: fullPath = os.path.join(d, f) if os.path.isdir(fullPath): if recurse: dirs.append(fullPath) continue if os.path.splitext(f)[1] not in targetext: continue try: fIn = open(fullPath) except: continue print "counting: " + fullPath for line in fIn: for pat in exclude_patterns: n = line.find(pat) if n != -1: line = line.replace(pat, '') # 一旦Unicode変換することで、全角文字を人文字として処理できるように for c in unicode(line, fencode): c = c.encode(fencode) if isTargetChr(c): chrMap[ord(c)] += 1 # 結果の出力 for i, count in enumerate(chrMap): if isTargetChr(chr(i)): print "%s : %d" % (chr(i), count)
その結果、下記のような結果が得られた。ちなみに、元ソースはC++。
(他の言語だと状況がかわってくるかも、Javaとかはもうちょっと'J'の活躍の機会が多そうw
あと、書く人によっても違うかも知れないし。)
325488 | |
e | 275193 |
t | 250354 |
* | 244387 |
r | 172858 |
n | 171743 |
i | 156436 |
a | 146553 |
o | 119466 |
s | 111291 |
l | 95709 |
) | 94625 |
( | 94622 |
C | 85812 |
S | 81605 |
c | 80389 |
p | 80361 |
d | 76729 |
/ | 76562 |
m | 73286 |
; | 72619 |
T | 61704 |
u | 60118 |
_ | 59126 |
I | 56677 |
L | 55340 |
E | 51776 |
D | 50289 |
P | 48793 |
R | 48471 |
f | 47909 |
O | 40381 |
= | 39246 |
V | 38371 |
A | 37399 |
, | 37242 |
: | 36399 |
. | 33032 |
N | 32450 |
h | 31328 |
y | 30011 |
G | 29894 |
g | 29695 |
x | 25012 |
F | 24849 |
M | 23585 |
b | 23451 |
B | 22362 |
} | 20964 |
{ | 20963 |
U | 20763 |
w | 20688 |
20230 | |
v | 19666 |
0 | 18846 |
- | 18782 |
> | 17774 |
& | 14298 |
! | 12237 |
z | 11640 |
W | 11052 |
1 | 10712 |
" | 9967 |
2 | 8539 |
H | 8236 |
k | 7745 |
# | 7283 |
[ | 7103 |
] | 7103 |
+ | 6979 |
6553 | |
Y | 5863 |
X | 5632 |
3 | 4135 |
5 | 3897 |
4 | 3285 |
6 | 3110 |
K | 2975 |
j | 2474 |
2299 | |
9 | 2255 |
8 | 2243 |
7 | 1885 |
% | 1342 |
~ | 1063 |
Z | 1054 |
q | 829 |
' | 557 |
? | 495 |
Q | 448 |
J | 424 |
^ | 29 |
'*'とか
(コメント定型文で「******」みたいのがいっぱいある。)
しかし、やっばJキーはあまり活用されてねえー。
英字だけでみると、小文字ではqに次いでワースト2、大文字に至ってはワースト1じゃないか。
一等地にいるくせになんてやつだ。けしからん。
ということで、自分はJキーをプレフィックスキーとして
上のテーブルの上位にある、押しにくいキーを割り当てていくことにした。
その際、jに続く文字には母音を除外し、
jj..と連続するのも避けるようにして割り振ってみたら、
こんな感じのマッピングになった。
imap <buffer> jdj * imap <buffer> jdd / imap <buffer> jde % inoremap <buffer><expr> jg Closebracket('()[]{}', ['cString','cCppString','cCharacter', 'cCommentL', 'cComment']) imap <buffer> jff ( imap <buffer> jk = imap <buffer> jt _ imap <buffer> jv : imap <buffer> jfg { imap <buffer> jdm - imap <buffer> jcc > imap <buffer> jr & imap <buffer> jn ! imap <buffer> jw " imap <buffer> jq ' imap <buffer> jdp + imap <buffer> jcj < imap <buffer> jfd [ imap <buffer> jx <bar>
3ストローク入力とかはちょっと煩雑かも。
この辺は改良の余地ありまくりの予感。
3ストロークにするくらいなら、Kあたりのキーもプレフィックスキーとして使って、2ストロークの組み合わせ数を増やすほうがいいかもしれない。
しかし、元エントリにある「直感的操作感」なんてあったもんじゃない。これはひどいw
たまに、このマッピングのせいで困ることがあるかもしれないけど、
そんときはC-Vでマッピングを回避するということで。
jgにマッピングしているClosebracketという関数は、
直前のテキストを調べて、対応する閉じ括弧を選択する、というもの。
対応する')'と'}'と']'を調べて、もっとも内側の括弧を閉じることで、
ひとつのキーで括弧閉じを済まそうという魂胆。
こんな感じの関数
function! Closebracket(brackets, ignore_syntaxs) let ignore_syntax_names = {} for x in a:ignore_syntaxs let ignore_syntax_names[x] = 1 endfor " 現在位置を保持しておく let [ cur_l,cur_c ] = [ line('.'),col('.')] let org_lz = &lz let &lz=1 " 各括弧のカウント変数 let search_pattern = '\V' let chr_dict_index = {} let counts = [] let close_chr = [] for x in range(0, len(a:brackets)-1) let chr = a:brackets[x] let chr_dict_index[chr] = [x/2, x%2] if x%2 == 0 | let counts += [1] | endif if x%2 != 0 | let close_chr += [ chr ] | endif if x != 0 let search_pattern .= '\|' endif let search_pattern .= chr endfor while(1) " 1.検索する let pos = searchpos(search_pattern,'bW') if pos == [0,0] break endif " 2.見つかった場合、その位置のシンタックスをしらべる。 " もし、文字列、文字、コメント文字列なら、再度検索を実行する。 let syn_name = synIDattr(synID(line("."),col("."),1), "name") if get(ignore_syntax_names, syn_name, 0) != 0 continue endif " 3.現在位置の文字を取得 getline(".")[col(".")-1] let chr = getline(".")[col(".")-1] " 4.検索語の位置のキャラクタに応じてカウントを変更。 " 変更した結果、値が0になったら、その文字を返す。 let index = chr_dict_index[chr][0] if chr_dict_index[chr][1] == 0 let counts[index] = counts[index]-1 else let counts[index] = counts[index]+1 endif if counts[index]==0 " 終了するまえに、カーソルを元の位置に戻すことを忘れないこと call cursor(cur_l,cur_c) let &lz=org_lz return close_chr[index] endif endwhile call cursor(cur_l, cur_c) let &lz=org_lz return '' endfunction
ちょっと使ってみた感じ、慣れることさえできれば、指の負荷はだいぶ軽減されそうな予感。
しばらく使って、問題なくつかえるようであれば、C/C++以外のファイルタイプにも適用しよう。