普段ja patched rxvt-2.7.10-6 on Cygwinを使っているのですが、何故かShift-Tabが使えないことに気付きました。より正確には、Shift-Tabを押したときに送られる文字が\033 [ Zではなく\011 (水平タブ) になります。このままだとShift-Tabに何か機能を割り当てることができません。無くても生きていけますが、少々不便。
何でだろう。ソースを見たところ、Shift-Tabに対して\033 [ Zを送る箇所があるので、取り敢えず原因はそれ以前の段階のはず。どこだ。
Vimの:highlightの設定を変更する場合、既存の設定をそのまま使うには:highlight link {from-group} {to-group}を使います。大抵の場合はこれでも十分なのですが、linkしたグループの設定を変更することはできないので、「既存の設定をそのまま使うが一部の値は変更したい」という場合には使えません。
という訳で発想を変えて「:highlightの設定をコピーして一部の値を変更する」ことにしました。
function! s:ExtendHighlight(target_group, original_group, new_settings)
redir => resp
silent execute 'highlight' a:original_group
redir END
if resp =~# 'xxx cleared'
let original_settings = ''
elseif resp =~# 'xxx links to'
return s:ExtendHighlight(
\ a:target_group,
\ substitute(resp, '\_.*xxx links to\s\+\(\S\+\)', '\1', ''),
\ a:new_settings
\ )
else " xxx {key}={arg} ...
let t = substitute(resp,'\_.*xxx\(\(\_s\+[^= \t]\+=[^= \t]\+\)*\)','\1','')
let original_settings = substitute(t, '\_s\+', ' ', 'g')
endif
silent execute 'highlight' a:target_group 'NONE'
\ '|' 'highlight' a:target_group original_settings
\ '|' 'highlight' a:target_group a:new_settings
endfunction使用例は次のような感じ:
autocmd ColorScheme *
\ call <SID>ExtendHighlight('Pmenu', 'Normal', 'cterm=underline')
\ | call <SID>ExtendHighlight('PmenuSel', 'Search', 'cterm=underline')
\ | call <SID>ExtendHighlight('PmenuSbar', 'Normal', 'cterm=reverse')
\ | call <SID>ExtendHighlight('PmenuThumb', 'Search', '')しかし、Vim7のデフォルトのPmenuのハイライトはどうにかならないものかなぁ。それに添付のcolor scheme全て (ただしdelekを除く) で設定がないため、color schemeによっては見れたものではない場合があります。
以前から稀にbluewindでシステムリソースが足りません
というエラーが出ます。一度出るとbluewindを再起動しない限り直りません。エラーの発生するタイミングは補完中のため、使用不能に近い状態になります。
で、今回、何となくエラーが発生し始める条件が分かりました。Windowsを長時間起動していたときに良く起こります (私の場合、PCの電源を落とす代わりに休止状態にするのですが、起動しっぱなしと同様の状態と考えられます)。
「長時間起動 + リソース不足のメッセージ = メモリリーク」と思ったのでタスクマネージャを開いてみたところ……こりゃアウトだ。USERオブジェクトとGDIオブジェクトの数が異常。起動直後はどちらも3桁に届かないレベルなのに。
その後、色々と試しているうちに補完動作でリークが起こっていることが判明。どうしたものか。
Vim scriptの行継続が使えるのは:sourceされた場合のみです。
Vim scriptの行継続は「次の行の行頭が\なら継続」という文法なので、コマンドラインからインタラクティブに実行する場合に使えないのは当然なのですが、:executeで実行するスクリプト内でも使えません。
別に:execute中で使えてもいいんじゃないかなぁ……
ふと思いついて試してみたところ、Operaのサーチエンジンのキーワード (デフォルトだとg fooでGoogle検索できる。このgのこと) に2文字以上の文字列が使えるようになってました。昔 (記憶している限りではOpera7まで) は1文字しか登録できなかったのですが、何時の間に変わったのやら (Opera 9.10で確認)。
ずっと1文字限定だと思っていたので少々不便に思っていたのですが、これである程度改善されました。例えばweを英語版Wikipediaの検索に、wjを日本語版Wikipediaの検索に割り当てることができます。1文字限定だとこういう場合に困るのです。
ただ、キーワードの大文字小文字を区別しない点は昔から変わっていないようです。個人的には「小文字で通常の検索」、「大文字でその変化形」という風に使いたいのでけどね (例: gでGoogle検索、GでI'm feeling lucky)。
VimでEmacs likeなscratch bufferを提供するプラグインを書いてみました。あったらあったで便利なものですし。細かいところは使っていくうちに弄るかな。
欠点としては、複数行のスクリプトの実行は明示的に選択してから行うことと、スクリプトの実行結果をバッファに挿入できないことです。どちらもVim scriptなので仕方がありません。あれば便利なのですが、使う機会を考えるとなくても別に困らないと思います。
Vimで任意の名前のバッファを作成する方法:
hide enew -- 無名のバッファを作る。setlocal noswapfile -- スワップファイルを使わないように設定する。file `='set name as you like'` -- バッファの名前を設定する。ワイルドカード等の問題を回避するため、変数か文字列を`=...`経由で渡すとよい。作成するバッファがスワップファイル不要かつ分かり易さのために任意の名前を付けたい場合 (例えばプラグイン等で一時的な情報を提供するため) に使います。スワップファイルはバッファ名をベースとした名前で作成されるため、バッファ名にワイルドカード等が含まれていると警告 (E303) が表示されます。自動処理したい場面で一部の種類の警告が発生すると確認のプロンプトが表示されるため、困ったことになります。
今日はこれだけのためにかなり時間を食われました。無名バッファを開く方法はnewしか知らなかったのが主な原因。本来の目的はEmacs likeなscratchバッファを提供するプラグインを書くことだったのですが、今日はもう公開する気力が尽きたのでまた今度。
何故か急に右手の親指が痛み始めました。指の付け根から手首までの間辺りに時折痛みが感じられます。親指を曲げたり力んだりした際に痛むことが多い気がするし、痛みが何だか腱鞘炎に似てるので、多分腱鞘炎気味なのかな。
仮に腱鞘炎だとすれば何か原因があります。そして思いつくのはキーボードの叩きすぎしかありません。でも右親指はそんなに使う指ではないしなぁ……
……と書いてたら気付きました。日本語入力時のIMEのON/OFFの切り替えです。切り替えは右Altで行っているのですが、これを押すときの右手の位置や姿勢に少し無理があります。
使用しているキーボードはHHK Lite2 (US配列) なのですが、これで右Altを右手の親指で打つことを想像してください (ただしAltと◇の位置は交換しています)。どうしても右親指を内側に曲げなければなりません。右手首をずらせばある程度軽減できるものの、それでも少々無理があります。
しかし仮にこれが原因だとしても、ホットキーをどこに変えればいいんでしょう? スペースキーの右側を分割して独立させれば無理なく押せる位置になりますけど、それは物理的に無理です。これが普通のJIS配列のキーボードであれば、Ctrl (小指で付け根で押す) か変換やひらがな (親指で押す) を右Altに割り当てることで解決できるのですけど。こういう時はHHKのコンパクトさがデメリットになりますね。
財布に余裕があればキーボードを買い換えようかなぁ。最近はKinesis Contoured Keyboardに心を惹かれるものがあるのですが、さすがに「高い」・「でかい」・「海外」の三拍子が揃ってるので迷ってます。
痛みは大分引いてきました。でもIME ON/OFFのキーは変えようかと思います。やはり今のままだと遠い。
ホームポジションでLの上に置いた薬指を滑らせる感じ
の押し方は……うーん、キーボードに対して手首の位置が高ければそれも良いかな。というのも、普段から手首を机に置いてタイピングしているので、その方法だと.が薬指に引っかかります。小指で右◇を押すなら引っかかりませんけど、これだと指の曲げ具合が大きくなるので今一。
位置的に←が右手の側面で押せてナイスなのですが、さすがに←を潰すのは怖い。次に良いのは右Shiftなのですが、既に\に割り当ててるので無理。
うーん、Vimの+multi_byte_imeがWindows GUI版以外でも使えたら大分軽減されるんですけどね……
このサイトの更新情報配信用のRSSですが、各記事の全文も含めるようにしました。
内容については一通り確認しましたが何か問題が残ってるかも知れません。調べた範囲では、Bloglinesだと<rss:description>が<content:encoded>よりも優先されてしまうことを確認しました。でも他のサイトのRSSだと<content:encoded>の方が優先されるという謎。ぱっと見た感じでは両者に差異はないと思うのですけどね。
Vim: セクション/関数単位の移動の改善で書いた設定ですが、実は落とし穴がありました。設定自体は間違っていないものの、ファイルの種類によってはftpluginにより後からバッファローカルなkey mappingsが追加されるため、結果として設定がオーバーライドされてしまいます。
という訳で、問題のkey mappingsを強制的に削除すればOK。
autocmd FileType * call <SID>FileType_any() function! s:FileType_any() silent! vunmap <buffer> ]] silent! vunmap <buffer> ][ silent! vunmap <buffer> [] silent! vunmap <buffer> [[ silent! ounmap <buffer> ]] silent! ounmap <buffer> ][ silent! ounmap <buffer> [] silent! ounmap <buffer> [[ endfunction
問題はこの対策が必要になるファイルの種類がデフォルトでは三つしかないこと。その一つがvimなのでまあいいかな (実際、上記コードのコピーに役に立ったし)。
ところで、削除するkey mappingsが未定義だった場合のエラーの抑制は簡潔にできないんですね。他のコマンドと同様にunmap! {lhs}で済むのかと思ったら、これだと別の意味になってしまう。この辺は歴史的事情というものなのかな。
ああああ……エラーを無視するだけならsilent! {command}でできる。こちらを使うように修正。
Vimの落とし穴: Key mappingの展開結果でもモードの切り替えは起こる。当然のことなのだけど、特定のモードのmappingしか定義してないときに引っかかることがある。例えば次のような感じ:
nnoremap (some-action) ... nnoremap (another-action) ... nmap (wrong-action) (some-action)v(another-action)
この場合(wrong-action)の展開結果は「(some-action)の後、visual modeに切り替え、(another-action)を行う」という動作になり、(another-action)に対してvisual mode用のmappingが定義されていないので困ったことになります。
今日はこれで30分くらい詰まった。
Vimのabbreviations (:abbreviate) だけど、これはkey mappingと何が違うんだろうか? 少々の差異はあるものの、abbreviationsでできることはkey mappingで十分に賄えるはず。ならばこの二つを分けている理由は何なのだろうか?
両者の差異は:help usr_40.txtのMAPPINGS AND ABBREVIATIONSに述べられている:
| ポイント | Abbreviations | Key mapping |
|---|---|---|
| 展開するタイミング | Non-word characterを入力したとき | Key mappingの最後の文字を入力したとき |
| 入力中 | 入力した文字列はそのまま挿入され、abbreviationが展開される際に置き換えられる。 | Key mappingが動作するまで何も入力されない (入力状況はオプション'showcmd'が設定されていれば表示される)。 |
| 曖昧な展開元文字列 | 存在しない (動作するタイミングから曖昧になりえない)。 | 一定時間の間に入力があるかないかで判別。 |
……こうやって眺めてたら分けてるのは当然な気がしてきた。
Vimの嫌いなところ: 原因は各機能に名前がなく、デフォルトのキーバインドで指定しなければならない点。特にkey mappingやnormal modeのコマンドを指定する際に顕著になる。
些細なもの (例えばcnoremap <C-U> <C-E><C-U>程度) ならまだ読めるからいいものの、ちょっと凝ったことをすると即座に解読不能な代物になる。normal! ggj0vG$hyなんて何がしたいのか書いた本人でさえさっぱり分からない。
少しでも良いから読みやすくしようと、normal! ggj0 v G$h yのようにある程度の塊で分けて書きたくなるのだが、そうすると動作が変わってしまう。どうしろと。
……と、ここまで書いてて思いついたのだけれど、
noremap (forward-char) l noremap (backward-char) h noremap (next-line) j noremap (previous-line) k
という風に「名前」を付けてやれば
map <C-L> 4(forward-char)
のようにして名前で指定することもできなくはない。
さらに読みやすさのためのスペースもnoremap <Space> <Nop>とすれば不可能ではない。
まあnoremap等の{rhs}には使えないし、command-line modeに適用するには少々無理がある (大体は問題ないと思うが)。結局のところ解決にはならない。
Vimでdosini (*.ini) 用のセクション単位の移動を行う設定。JumpSectionN自体は汎用なので他のものを書くときにも使える (と思う)。
autocmd FileType dosini call <SID>FileType_dosini()
function! s:FileType_dosini()
nnoremap <buffer> <silent> ]] :<C-U>call JumpSectionN('/^\[')<Return>
nnoremap <buffer> <silent> ][ :<C-U>call JumpSectionN('/\n\(\[\)\@=')<Return>
nnoremap <buffer> <silent> [[ :<C-U>call JumpSectionN('?^\[')<Return>
nnoremap <buffer> <silent> [] :<C-U>call JumpSectionN('?\n\(\[\)\@=')<Return>
endfunction
function! JumpSectionN(pattern)
let pattern = strpart(a:pattern, '1')
if strpart(a:pattern, 0, 1) == '/'
let flags = 'W'
else
let flags = 'Wb'
endif
mark '
let i = 0
while i < v:count1
if search(pattern, flags) == 0
if stridx(flags, 'b') != -1
normal! gg
else
normal! G
endif
break
endif
let i = i + 1
endwhile
endfunctionVimのセクション/関数単位の移動コマンド]]等ですが、ファイルの種類によっては適切なものにmapされています。例えばvim scriptではnoremap <silent><buffer> ]] m':call search('^\s*fu\%[nction]\>', "W")</CR>となっています。
しかし、ほとんどの場合normal modeのmappingしか定義されていないため、visual modeやoperator-pending modeではデフォルトの動作になってしまい、少々不便です。
という訳で汎用的な設定を考えてみました。Visual modeとoperator-pending modeでもnormal modeと同様の]]等が使えます。
vnoremap <silent> ]] :<C-U>call JumpSectionV(']]')<Return>
vnoremap <silent> ][ :<C-U>call JumpSectionV('][')<Return>
vnoremap <silent> [[ :<C-U>call JumpSectionV('[[')<Return>
vnoremap <silent> [] :<C-U>call JumpSectionV('[]')<Return>
onoremap <silent> ]] :<C-U>call JumpSectionO(']]')<Return>
onoremap <silent> ][ :<C-U>call JumpSectionO('][')<Return>
onoremap <silent> [[ :<C-U>call JumpSectionO('[[')<Return>
onoremap <silent> [] :<C-U>call JumpSectionO('[]')<Return>
function! JumpSectionV(motion)
execute 'normal!' "gv\<Esc>"
execute 'normal' v:count1 . a:motion
let line = line('.')
let col = col('.')
normal! gv
call cursor(line, col)
endfunction
function! JumpSectionO(motion)
execute 'normal' v:count1 . a:motion
endfunction書いてみたのはいいのですが、Vim同梱のftpluginで]]等を定義しなおしているものの数はそんなにないんですよね (6種類だけ)。しかもやりかたがまちまち。どうにかならないものかな。
Vimでファイルの種類別に設定を行う方法は次の2通り:
autocmd FileType FOO {cmd}を書く。setlocal expandtab softtabstop=2 shitwidth=2) だけならこれで十分。~/.vim/after/ftplugin/FOO.vimに書く。~/.vim/ftplugin/FOO.vimに書いてもいいけど、そうするとデフォルトの設定を上書きできない。Cygwin上のVimでレジスタを使ってWindowsのクリップボードにアクセスしたいと思っていたのですが、ちょっと頑張ったらvim scriptのみでできました。基本的にVim6/7のどちらでも動作しますが、"+yは'operatorfunc'を使うのでVim7でしか動作しません。
基本的に通常のものと同様に動作しますが、次の欠点があります:
p/Pが常にcharacterwiseになるp/Pするテキストの最後の行の改行文字が取り除かれる:read !getclipで行っているのですが、最後の行の改行文字がない場合はVimが自動的に付加します。よって改行文字の有無をスクリプト側で判断することができません。/dev/clipboardを普通のファイルとして読み込んでくれるなら可能なのですが。完全を期すならVim本体に手を入れた方がいいのですが、そこまでやる気力はありませんでした。
" Yank/Put with the Windows' clipboard.
" BUGS: Putting is always characterwise.
" BUGS: The last <EOL> in text to be put is always stripped.
if has('win32unix') && !has('clipboard')
" Key mapping
nmap "* "+
vmap "* "+
nnoremap <silent> "+y :set operatorfunc=<SID>YankToClipboard<Return>g@
nmap "+yy V"+y
nmap "+Y "+yy
vnoremap <silent> "+y :<C-U>call <SID>YankToClipboard(visualmode())<Return>
vnoremap <silent> "+Y :<C-U>call <SID>YankToClipboard('V')<Return>
nnoremap <silent> "+p :call <SID>PutFromClipboard('', 'p')<Return>
nnoremap <silent> "+P :call <SID>PutFromClipboard('', 'P')<Return>
vnoremap <silent> "+p :<C-U>call <SID>PutFromClipboard(visualmode(), 'p')<Return>
vnoremap <silent> "+P :<C-U>call <SID>PutFromClipboard(visualmode(), 'P')<Return>
" Main functions
function! s:YankToClipboard(motion_type)
let old_reg = @@
call s:SelectLastMotion(a:motion_type)
normal! y
new
setlocal buftype=nofile bufhidden=wipe noswapfile nobuflisted
setlocal binary noendofline
silent normal! P
silent write! /dev/clipboard
bwipe
let @@ = old_reg
endfunction
function! s:PutFromClipboard(motion_type, put_type)
let old_reg = @@
call s:GetClipboardContent()
if a:motion_type == ''
execute 'normal' a:put_type
let @@ = old_reg
else
call s:SelectLastMotion(a:motion_type)
execute 'normal' a:put_type
endif
endfunction
" Misc. functions
function! s:SelectLastMotion(motion_type)
let old_selection = &selection
let &selection = 'inclusive'
if a:motion_type == 'char'
silent normal! `[v`]
elseif a:motion_type == 'line'
silent normal! '[V']
elseif a:motion_type == 'block'
silent execute "normal! `[\<C-V>`]"
else " invoked from visual mode
silent execute "normal! `<" . a:motion_type . "`>"
endif
let &selection = old_selection
endfunction
function! s:GetClipboardContent()
new
setlocal buftype=nofile bufhidden=wipe noswapfile nobuflisted
read !getclip
silent normal! ggj0vG$hy
bwipe
endfunction
endif
エコーの抑制方法を修正 (:help :map-<silent>)。
あ、良く考えたらcとdについての定義が足りない。まああんまり使わないからいいか。
cereja 0.0a2をリリースしました。細かいバグフィックスのみ。
現在の.vimrc、ほとんどがオプションの設定だけで凝ったことはしていない。ここ数年ぐらいは全然編集していない気がするし。
でも最近は色々と考え方が変わってきたので、もっと凝ったことをしようと思っている。Normal modeで<Space>は全然使わないので、まずはこれをベースに色々と弄ってみるかな。
という訳で、今年の抱負は「Vimを極める」ことにする。
set nocompatible
set encoding=japan
if has('iconv')
let s:enc_euc = 'euc-jp'
let s:enc_jis = 'iso-2022-jp'
if iconv("\x87\x64\x87\x6a", 'cp932', 'euc-jisx0213') ==# "\xad\xc5\xad\xcb"
let s:enc_euc = 'euc-jisx0213,euc-jp'
let s:enc_jis = 'iso-2022-jp-3'
endif
let &fileencodings = &fileencodings . ',' . s:enc_jis
set fileencodings+=utf-8,ucs-2le,ucs-2
if &encoding =~# '^euc-\%(jp\|jisx0213\)$'
set fileencodings+=cp932
let &encoding = s:enc_euc
else
let &fileencodings = &fileencodings . ',' . s:enc_euc
endif
unlet s:enc_euc
unlet s:enc_jis
endif
if 1 < &t_Co && has('syntax')
syntax enable
colorscheme default
set background=dark
endif
set autoindent
set backspace=indent,eol,start
set backup
set backupcopy&
set backupdir=.,~/tmp
set directory=.,~/tmp
set noequalalways
set history=100
set hlsearch
set incsearch
set mouse=
set ruler
set showcmd
set showmode
set smartindent
set updatetime=60000
set title
set titlestring=vi:\ %f\ %h%r%m
set viminfo=<50,'10,h,r/a,n~/.viminfo
filetype plugin indent on
function ToggleOption(opt_name)
execute "if &" . a:opt_name . "\n"
\ . " let &" . a:opt_name . " = 0\n"
\ . " echo '" . a:opt_name . " off'\n"
\ . "else\n"
\ . " let &" . a:opt_name . " = 1\n"
\ . " echo '" . a:opt_name . " on'\n"
\ . "endif"
endfunction
nnoremap \ :call ToggleOption("wrap")<CR>
nnoremap <C-J> :cn<CR>
nnoremap <C-K> :cN<CR>
nnoremap <F2> :e #<CR>
nnoremap <Esc>2 :e #<CR>
cnoremap <Esc>h <Left>
cnoremap <Esc>j <Down>
cnoremap <Esc>k <Up>
cnoremap <Esc>l <Right>
cnoremap <Esc>H <Home>
cnoremap <Esc>L <End>
cnoremap <Esc>w <S-Right>
cnoremap <Esc>b <S-Left>
cnoremap <Esc>x <Del>
cnoremap <C-U> <C-E><C-U>
let mapleader=','
inoremap <Leader>df <C-R>=strftime('%Y-%m-%dT%H:%M:%S+09:00')<CR>
inoremap <Leader>dd <C-R>=strftime('%Y-%m-%d')<CR>
inoremap <Leader>dt <C-R>=strftime('%H:%M:%S')<CR>
set secure