今日のスクリプト: (Vim) Windowサイズと位置の調整

# はじめに

　今日はvimscriptとmapを使って，gvimウィンドウを調整する話です．

　Vimを使っていて，一時的に行数をディスプレイいっぱいにしたいことがあります．

　Windowsなら，ウィンドウの上端をダブルクリックで上下いっぱいになります．
また，ショートカット**"Win + ←／→／↑／↓ "**で画面が左半分/右半分/最大化/最小化．
これはこれで便利です．でも**「幅はそのままで行だけ縦に一杯」**にはできません．

　が，そもそもVimんちゅは
**「文字入力編集中にキーボードから指を離してマウス動かすのはどうなんよ」**
という発想の持ち主ですから．

単純なウィンドウの調整くらいvimscriptで，ということでやってみました[^hajime]．

**とにかくホームボジションから指が離れることがイヤだという貴兄に．**

以下，いわゆるGUIのみの設定です．動作確認はWindowsのみですあしからず．
← あ，行数や行幅はCUIのコンソールやターミナルでも一応可能です．が，純粋なコンソールは基本全画面なのであまり意味がありませんし，ターミナルエミュレータではウィンドウの大きさが対応して変わってくれるかどうかはわかりません[^xterm]．

# 1.ウィンドウサイズ
Gvimはvimの行数(lines)と桁数(columns)に合わせてウィンドウサイズを変えてくれます．ありがたや．
なので，直前の行数／桁数をメモっておいて，最大行・最大桁に変更する関数を別々に作りました．

## a.まず行数です．

```vimscript:SetLines()
""" (Toggle)Set win lines Full <-> previous
function! SetLines()
if !exists("g:oline")
    let g:oline = &lines
	let &lines=999
else
	let &lines = g:oline
	unlet g:oline
endif
endfunction
```

これはトグルになっていて，最大行← → 直前の行数が繰り返されます．シンプルでしょ？
好きなキーバインドにmapします．スクリプトの細かいことは後述しますね．

```Vimscript
map <Leader>j :call SetLines()<cr>
map <C-j> :call SetLines()<cr>
map ,wj :call SetLines()<cr>
```

## b.次に行幅です．

これも同じ要領でトグルにしました．

```Vimscript:SetWidth()
"""""" (Toggle)Set win width Full <-> previous
function! SetWidth()
if !exists("g:owide")
    let g:owide = &columns
	let &columns=999
else
	let &columns = g:owide
	unlet g:owide
endif
endfunction
;;
;;;; mapping
;;
map <Leader>l :call SetWidth()<cr>
map <C-@> :call SetWidth()<cr>
map ,wl :call SetWidth()<cr>
```

　※**おいらのmapのお話**："Ctrl-j"で縦長，"Ctrl-@"で横長にしています．横長はviキーバインドだと"Ctrl-l(エル)"が妥当だと思うんですが．いかんせん"Ctrl-l"は結構使う「再描画」なんで，あんまり変更したくない．
しょうがないんで"l"の隣の";"を，と思ったら，"\<C-;\>"はGUIでは有効にはならないみたいなんです[^nowork_c-;](;_;)で，しょうがなくx2の，"Ctrl+@"にしました．

# 2.ウィンドウのポジション
そしてポジション．これはGUI限定ですね．`winops`というguit限定のコマンドを利用しています．

* 引数をx,yの2つにしているので，自分のディスプレイ環境に合わせてプリセット的に設定できます．
  - プリセットの数値を決めるには，行き先までウィンドウを移動させといて，":winpos"です．
* これもトグル動作なので，変なポジションを指定しても再実行で戻せます．えっへん．

```vimscript:SetWinpos(posx,posy)
"""""" (Toggle)Set win Position to x,y <-> previous
function! SetWinpos(posx, posy)
if !exists("g:oposx")
	let g:oposx = getwinposx()
	let g:oposy = getwinposy()
	echo "winpos "a:posx . " " . a:posy
	execute "winpos " a:posx . " " . a:posy
else
	echo "winpos "g:oposx . "\  " . g:oposy
	execute "winpos " g:oposx . " " . g:oposy
	unlet g:oposx g:oposy
endif
endfunction
""
"" mapping: set winpos to Left and Top
map <Leader>1 :call SetWinpos(20,20)<cr>
map ,w1 :call SetWinpos(20,20)<cr>
""          set winpos to 2nd Display
map <Leader>w :call SetWinpos(-1700, -2400)<cr>
map ,ww :call SetWinpos(-1700, -2400)<cr>
```

どのポジションがいいかは本当にお好みで．mapでいくつでも設定できます．
※おいらの2ndディスプレイが縦長でメインディスプレイの左側なので，数値が大きな負になってます．

# 3.vimscript: 分かったこと/分かりにくかったこと

vimscriptの日本語ヘルプは「[Vim本家のhelp](https://vim-jp.org/vimdoc-ja/index.html)」にまとめてあります．が，これだけではちゃんと作れなのがプログラムです．
いろいろと困ったことにぶつかり，その度にネットの神様のお告げを参考にしました．
意外とまとまっていない，**細かいけど結構困ったこと**をまとめてみました．
Vimんちゅなら当たり前，なレベルのこともあるでしょうが，何の参考になれば．

* 関数は"function! 関数名()"で始め，"endfunction"で終わる．C言語みたいに"{}"で挟まない．Pascalっぽい．
* ifも同様で，"if 条件式"の次から，"endif"までは改行または";"で区切れば何行でもそのブロックになる．
* exists関数の引数は""で囲む：""がないとエラーに．(ex) `if !exists("g:oposx")`
* 変数への代入は"let"で．
* スクリプトのなかでvimのオプション(上の例だとlinesとかcolumns)を設定するのは `let &XXXX = hoge`
  * これを`set XXXX=hoge`とするのは可能だが，変数がうまく展開できずエラーに
* 関数定義について書いた記事にはよく"command ! -nargs=なんちゃら～"というのがあるが今回は不要
  * これはコロンコマンドとして実行したい場合．キーバインドで呼び出すなら不要．← "command!"の役割がわかってなかった……
* 一旦保存しておくのは，(関数内の)ローカル変数ではだめで，グローバル変数でないと．これは変数の頭に"g:"をつける(ローカル変数は"s:")．ただし，vimのコロンコマンドで参照する際には"g:"は不要．

  * スクリプト： `let g:oline = &lines`　 → g:olineに格納されたオプションlinesの値をvimで確かめるには
  * vim： `:echo oline`
* 変数の接頭語その2: 関数に指定された引数は，関数内では"a:"をつけて参照される．
  * (ex) `function! SetWinpos(posx, posy)`の引数posx,posyを使うには，"a:"をつけて，"a:posx""a:posy"
  
困ったのは「winposというコマンドに関数引数を渡して実行させる」こと．簡単なようですが……

* winposというのはオプションではなくコロンコマンド．しかも空白で別れた引数が2つ．スクリプトの中では"execute"を使って実行する．← コロンコマンドを直実行も可能だが，変数を展開して引数として渡せない("let &XXX"と同じ)
* 2つの変数を「空白でつなぐ」：これは"."と" "(ダブルクォートで挟んだ空白)でつなぎ合わせる[^cat-char]というルール．

  * (ex) winposに a:posx，a:posyを空白付きで渡して実行: `execute "winpos " . a:posx . " " . a:posy`
  
こういう**「単純なルール記述の組み合わせは意外と面倒」**だという，いい例ですな．


[^hajime]: はじめはmap一行，つまりコロンコマンドの範囲だったんですが，vimscriptなら場合分けできるじゃん，となって，簡単なスクリプトを書いてしまうことに．調べながらなんで結構手間暇かかりましたが……

[^xterm]: xterm互換であればいけるかも？

[^nowork_c-;]: 参照 → [Is it possible to map <C\-;> to : in vim? \- Stack Overflow](https://stackoverflow.com/questions/7652590/is-it-possible-to-map-c-to-in-vim)

[^cat-char]: 参照 → [vimスクリプトで文字列を扱う \- neovim/vim入門](https://kaworu.jpn.org/vim/vim%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%A7%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E6%89%B1%E3%81%86)*