少しばかり機能追加した

$ ./bin/vimlint.sh file1 [...]
if cond
  let hoge = 10
endif
if exists('hoge')
  echo hoge
endif
    • 以下のような場合には対応していない.
      • case1: exists の引数が文字列でない場合
let str = "hoge"
if exists(str)
  echo hoge
      • case2: あるルートで複数の変数に値を代入して, 一つの変数のみでチェックする場合
if cond
  let hoge1 = 10
  let hoge2 = 20
endif
if exists('hoge1')
  echo hoge1
  echo hoge2
endif
  • エラーメッセージの抑止をできるようにした
    • https://github.com/syngan/vim-vimlint/issues/8
    • ただ, これだといつでも抑止される可能性大なので, 部分的に抑止する方法を提供したい.
    • また, 現状では watchdogs 経由の場合には効果がない.

では皆様良い vim script ライフを.

vim-vimlint 作った

この記事は Vim Advent Calendar 2012 357日目の記事になります.356日目は id:leafcage さんによる 'nobuflisted' なバッファの作り方 - cafegale(LeafCage備忘録) でした.

vim-vimlint を作りはじめたのはだいぶ前で更新も止まっているのですが, 記事を書いていなかったので紹介します. いまだにドキュメントまともに書いていません. すいません.

概要

vim-vimlintvim script の文法チェックを行うプログラムです.
以下のような実行時ではないと検出できないエラーを検出します.

  • 初期化されていない変数の参照
  • 初期化されているが参照されない変数
  • build-in 関数の使用法誤り (引数の数チェック)
  • 使用されない関数の引数

これにより, 関数の引数に a: を付け忘れて参照していたり, エラールートなど実装したけど通したことがないルートのバグを事前に検出することができます.

例えば以下のようなバグを検出しました.

インストール

NeoBundle を使う場合, 以下のように vimrc に記述してください.

NeoBundle 'syngan/vim-vimlint', {
    \ 'depends' : 'ynkdir/vim-vimlparser',
    \ 'autoload' : {
    \ 'functions' : 'vimlint#vimlint'}}

簡単な使い方

" 任意のファイル
:call vimlint#vimlint(filename)

" 指定されたディレクトリ配下の .vim ファイル
:call vimlint#vimlint(directory)

上記のように実行すると, echo でチェック結果を出力します. 出力先はオプションで変更します.

" ファイル outputfilename に出力する場合
:call vimlint#vimlint(filename, {'output': 'outputfilename'})

" リストで結果を取得する場合
:echo vimlint#vimlint(filename, {'output': []})

出力

echo 出力は以下の形式です.

'%f:%l:%c:%m'

%m は以下の形式です.

$ERRNO: $(ERRMSG) 

$ERRNO は

  • EVPxxx の形式は vim-vimlparser によるエラーメッセージ. このメッセージが出力された場合には vim-vimlparser の解析が停止しているのでそれ以上の処理はできなくなります.
  • EVLxxx または Exx の形式は vim-vimlint によるエラーメッセージです.

詳細は vim-vimlint/vimlint.txt at master · syngan/vim-vimlint · GitHub を参照してください.

実装について

プラグインvim-vimlparser により解析されたデータを基にしています. したがって, vim-vimlparse が構文解析に失敗すると, 本プラグインは動作しません. コマンドまわりで停止することがあり, 特に test/spec 系のソースではエラーになってしまうことが多いです.

また, どちらも pure vim script で書かれていて, とても遅いです.

vim-vimlint でチェックしている内容は vim-vimlint/test at master · syngan/vim-vimlint · GitHub を見てもらうと一応確認できます. lint と名前をつけたので基本的にあやしいものはエラーに倒す方針で実装しています.
例えば以下のようなコードは判定できず未使用変数のエラーになります.

function! g:open(...)
  ....
  let path = printf('gitlab://%s', join(base + args, '/'))
  let opener = 'edit'
  execute opener . '`=path`'
endfunction
dbakker/vim-lint

vim script の文法チェックを行うプログラムには他に GitHub - dbakker/vim-lint: Check your .vimrc for errors があります. ざっくり簡単な比較を行うと, 以下のような感じです.
vimlparser のように構文解析しているわけではないので未使用変数などのエラーは検出できていないように感じます.

  syngan/vim-vimlint dbakker/vim-lint
言語 vim script python
実行速度 ×
検出精度

連携プラグイン

おすすめの連携プラグインは, vim-watchdogs

vimproc がインストールされていれば, 保存時に非同期で文法チェックしてくれます. watchdogs 便利.
ここで注意ですが, watchdogs で vimlint を使う場合 NeoBundleLazy にしていると動作しないので, Lazy にしないことをお勧めします.

  • vimlparser も Lazy だと動作しません (2013/11/25 追記)

終わりに

以下のことをやりたいと思っていますが, 手が回っていません.

  • ドキュメント
  • エラーを抑止する設定
    • unused argument などは無視して良いことが多い
    • オプションを追加するか, ソース上にコメントを追加する形にするか.
  • PR お待ちしています.

lint を保存毎に実施する必要はないと思いますが, メジャーバージョンアップ時などに思い出して使ってもらえると幸いでございます.

さーて明日の VAC さんは?

明日以降の VAC は決まっていないようですね. まだ #vimconf2013 が終わってない人がたくさんいますよね.

vim-gitlab 作った

今日は vimconf2013 が開催されるようですが, 土日は身動き取れない身分なので参加できません. 残念です..

さて, この記事は Vim Advent Calendar 2012 351日目の記事になります. VAC 初参加であります.
350日目は syui さんによる http://mba-hack.blogspot.jp/2013/11/vim-vim-script.html でした。

近頃は社内で開発している製品のソースも git で管理をしているのですが, 社内で開発している製品を github で公開するわけにはいかないので, GitLab を使っています.

で, GitLab の参照・変更のたびにブラウザと Vim を行き来するのは面倒なので, GitLab を操作する vim プラグイン vim-gitlab を作ってみました.

このプラグインは, id:thinca さん作の絶賛プロトタイプバージョンな vim-github を元に作成しました. (アルファにも届いていないそうですが, 素人がスクラッチ開発するよりは良い作りになっているはず.)

ということで, まだドキュメントを書いてはいませんが,

  • vim-gitlab は vim-github と同様の動作をします.
  • 現在 issues のみに対応

さて, 以下, Vim から Github Issues が使いたくなったので github.vim を作り始めた - 永遠に未完成 の転載に近い状態ですが,

インストール

NeoBundle を使う場合, 以下のように vimrc に記述してください.

NeoBundleLazy 'syngan/vim-gitlab', {
    \ 'autoload' : { 'commands' : 'Gitlab'}}
  • GitLab 6-2-stable 以上推奨
    • 6-1-stable 以下でも動作しますが, API が対応していないため, issue 番号と vim-gitlab が表示する番号が一致しません.
  • 必須: +python or curl or wget
    • vital.vim の Web.HTTP に依存

今のところできること

  • Issues のリストの取得/表示
  • 個々の Issue の表示(コメント付き)
  • 新しい Issue の登録
  • 既存の Issue の close/reopen
  • 既存の Issue の編集
  • コメントの追加

簡単な使い方

準備

グローバル変数 g:gitlab_config に環境を設定します.
Github と異なり, 複数の URL にアクセスできるようにするためです.
g:gitlab_config は辞書でキーは任意に設定します.
キー値は辞書で以下のように設定します.

g:gitlab_config['name'] = {
\   'url' : 'http://localhost/',
\   'user' : '',
\   'email' : 'admin@local.host',
\   'password' : 'optional',
\}

ここで,

  • url: アクセスする GitLab の URL (必須)
  • user/email: ログイン ID (必須)
  • password: パスワード (任意)
起動
" 任意のリポジトリ
:Github name issues {user}/{repos}

" Issue 番号指定
:Github name issues {user}/{repos} {numberk}

" Issue 新規作成
:Github name issues {user}/{repos} new

ここで, 第 1 引数 name は g:gitlab_config で設定したキーです.

バッファが開いたら、
  • R か でリストを更新
  • で個々の Issue を開く
  • で Issue リストに戻る
  • button 上で を押すとそれっぽいことが起きる
  • で次の Issue, で前の Issue を開く
その他
  • UI 部分などは vim-github を引き継いでいるので同じ問題が残っています.

連携プラグイン

おすすめの連携プラグインは, vim-precious

以下のように設定しておけば, issues に登録されたソースをそのまま実行できます.

let g:context_filetype#filetypes = {
\	"gitlab-issues" : [{
\		'filetype': '\1', 
\		'start': '^\s*```\s*\(\h\w*\)', 
\		'end': '^\s*```$',
\	}],
\}

終わりに

さて, 明日以降の VAC は決まっていないようですが, #VimConf2013 の報告に期待しませう.

352日目の VAC は deris さんの vimconf2013 参加報告でした.

実践vim メモ

ビジュアルモード

TIP21 p.69
  • o: 強調表示されているテキストのもう一方の端点に移動

コマンドラインモード

TIP27 p.84
  • {register} でレジスタの内容を挿入
    • 直前にヤンクした内容は無名レジスタに保存されているので " で挿入できる
TIP33 p.99
  • でカーソル位置の内容を挿入

ファイルのオープン

TIP41 p.135
  • :e %:h でアクティブなファイルのディレクトリが開ける

検索

TIP84 p.267
  • /pattern/e で検索のマッチの末尾にカーソルをオフセット
    • 検索語の最後に文字を追加など, ドットの公式適用時に便利

... 続く

エラー発生箇所の探索

function <SNR>96_on_cursor_moved..139 の処理中にエラーが検出されました:
行   22:
E715: 辞書型が必要です

上記のエラーが発生した. エラー発生箇所特定のためのメモ*1. このメッセージは, 関数 96_on_cursor_moved() が 関数 139 を呼び出し, 関数 139 の 22 行目で E715 が発生していることを示す.

最初に 96 が何かを調べる.

:scriptnames
.... () ....
 94: ~/.vim/bundle/context_filetype.vim/autoload/context_filetype.vim
 95: ~/.vim/bundle/echodoc/plugin/echodoc.vim
 96: ~/.vim/bundle/echodoc/autoload/echodoc.vim
 97: ~/.vim/bundle/neosnippet/autoload/neosnippet.vim
 98: ~/.vim/bundle/neosnippet/autoload/neosnippet/util.vim
.... () ....

一つ目の関数は echodoc/autoload/echodoc.vim に定義されている関数 s:on_cursor_moved() だとわかる.

次に, 139 が何かを調べる. これは numbered function と呼ばれるもの.

                *numbered-function* *anonymous-function*
関数に名前をつける必要をなくすために、関数を定義して直接辞書に代入することがで
きる: >
    :let mydict = {'data': [0, 1, 2, 3]}
    :function mydict.len() dict
    : return len(self.data)
    :endfunction
    :echo mydict.len()

こうすると関数に番号がふられ、dict.lenがこの関数を参照する|Funcref|となる。こ
の関数は|Funcref|を通してのみ呼び出せる。参照している|Funcref|がなくなると、こ
の関数は自動的に削除される。

番号付き関数には "dict" 属性を付ける必要はない。

番号付き関数でエラーが発生したときは、あるトリックを使うことで発生源を確認でき
る。例えば 42 という関数なら次のようにする: >
    :function {42}

関数の特定には以下のようにすれば良い.

:verbose function {139}
   function 139(cur_text) dict
        Last set from ~/.vim/bundle/neocomplete/autoload/neocomplete/sources/include.vim
.... () ....
18
19   for word in reverse(words)
20     let key = tolower(word[: completion_length-1])
21
22     for include in filter(copy(s:include_info[bufnr('%')].include_files), 'ha
s_key(s:include_cache, v:val) && has_key(s:include_cache[v:val], key)')
24       for matched in filter(values(s:include_cache[include][key]), 'v:val.wor
d ==# word && has_key(v:val, "kind") && v:val.kind != ""')
26         let ret = []
27
.... () ....

{139} の 22 行目でエラーが発生しているので, 上記の部分が問題の箇所であると分かった*2.

*1:lingrid:thinca さんに教えていただいた. 感謝

*2:これは修正されました

precious.vim がチョー便利

複数の種類のコマンドの切り分けを自動的にやらせる

pukiwiki で作業ログを書いているときに, 一つのページに複数のコマンドの作業ログを書くことがある.
再実行させるときに, 毎回自分で切り替えながらコマンド実行しているけど, 範囲を選択して \r で実行できるようにしたい.
...

 ここからは python コード 
xxx
xxx
 ここまでは python コード 

ほげほげ ほげほげ

 ここからは ruby コード 
yyy
yyy
 ここまでは ruby コード 
http://d.hatena.ne.jp/syngan/20130623/1371923016

以前の記事に作者の id:osyo-manga さんからコメントで教えていただいた. precious.vim + quickrun.vim で, pukiwiki.vim での作業が捗ることこの上ない.

設定

必須プラグインである context_filetype.vim に記述している部分が他言語であることを教えてあげる必要がある.
pukiwiki 上に, 整形済みテキストとしてソースコードを記述しているのでそこに自由に仕様を決めれば良い.

FormattingRules
行頭が半角空白で始まる行は整形済みテキストとなります。行の自動折り返しは行なわれません。

他言語毎に設定を書くのは面倒なので markdown の設定を参考にした.
pukiwiki 上では少し目立つようにしたかったので ``` を ### として書き換えた.

\ 'pukiwiki': [
\ {
\ 'start' : '^\s*###\s*\(\h\w*\)',
\ 'end' : '^\s*###$',
\ 'filetype' : '\1',
\ },
\ ],

あとは, quickrun を使う設定をすれば \r すれば自動的に言語を切り替えて実行してくれる.

  • 最初は私の使っている vim のバージョンが古いために動かなかったが, 対応してもらった. thx!
  • ヘルプで example を書くときなど便利な場面はたくさんありそう.

vim-pukiwiki で画像表示に対応した

添付ファイルを display コマンド(imagemagic)で表示するだけなんだけど、画像表示に対応した. とっても便利だ.

だけども, 画像を開くたびに違うウィンドウが立ち上がるので、マウスを操作する必要がでてきてうっとーしくて仕方がない。
何か同じウィンドウで画像だけを切り替える方法はないだろうか

  • display. たぶん無理
  • eog. ローカルだけかと思ったらそうじゃなかった. 便利.
    • eog -w. session は何かできそうなにおい.
       Session management options
         --sm-client-id=ID          Specify session management ID
         --sm-config-prefix=PREFIX  Specify prefix of saved
                                    configuration
         --sm-disable               Disable connection to
                                    session manager
アプリケーションのオプション:
  -f, --fullscreen          フルスクリーンモードで開く
  -g, --disable-gallery     画像ギャラリーを無効にする
  -s, --slide-show          スライドショーモードで開く
  -n, --new-instance        既存のウィンドウを再利用せずに新しいインスタンスを起動する
  -w, --single-window       単一のウィンドウで開く (複数のウィンドウを開いている場合は最初のウィンドウを使用する)