Scouter

久しぶりに Scouter してみたら, 820 (2150 行), 中級者に昇級していた. このブログ始める(≒ ShougoWare に出会う)までは vim を日本語使える vi として使っていて, 20 行程度だったのにだいぶ増えていた.
上級者めざしてがんばんべよ,

と, vimrc 読書会をこそっと覗きながら思うのでした.

vim-quickrun でやりたいことメモ

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

pukiwiki で作業ログを書いているときに, 一つのページに複数のコマンドの作業ログを書くことがある.
再実行させるときに, 毎回自分で切り替えながらコマンド実行しているけど, 範囲を選択して \r で実行できるようにしたい.
ヘッダ部用意して, いつでもシェルを起動するようにすればいいのかな?

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

ほげほげ ほげほげ


 ここからは ruby コード 
yyy
yyy
 ここまでは ruby コード 

quickrun 用の window の開き方

自動的にかわっているようだけど自分にとってヘンな位置にいくことがある.
どう開くかは機能としてありそうだけど, 自分にとってうまい位置、をどうにか設定したい.

watchdogs.vimwindows サイズ

これはパラメタ用意してありそう. あとで探す.

writefile() 時の改行コード

試しに fileformat を指定してから結果を確認してみた.

set fileformat=dos
call writefile(a, "/tmp/dos")
set fileformat=mac
call writefile(a, "/tmp/mac")
set fileformat=unix
call writefile(a, "/tmp/unix")

set fileformat=dos
call writefile(a, "/tmp/dos2", "b")
set fileformat=mac
call writefile(a, "/tmp/mac2", "b")
set fileformat=unix
call writefile(a, "/tmp/unix2", "b")

これだとバイナリ版とそうでない版での違いはでる(最後の改行コード有無)が, 改行コードは同じもの.
windows でもいつも LF であるなら問題ないのだけど, windows vim 環境はないのであった.

writefile() 時に改行コードを設定するオプションはなさげ. いったいどうするんだろう.

 /* Always open the file in binary mode, library functions have a mind of
  * their own about CR-LF conversion. */

 ...
if (!binary || li->li_next != NULL)
    if (putc('\n', fd) == EOF)
    {
        ret = -1;
        break;
    }
https://code.google.com/p/vim/source/browse/src/eval.c

ふむむ..

2013/05/27 追記

:h writefile()
 ...
                Inserting CR characters needs to be done before passing {list}
                to writefile(). 

vital.vim の Data.List を使ってみた

typo があったので修正しました.

Haskell を触ったことがない人はたくさんいると思うので、
list のヘルプ「Haskell 風に動作する」は修正したほうが良いと思います.

fixed document by syngan · Pull Request #55 · vim-jp/vital.vim · GitHub

なんとなくヘルプを眺めていたら typo みつけて, そのながれで Data.List を見てみたけど,
vim 力が足りないからなのかなんなのかわからないけど,
そのうち pull req 送ろうと思うよメモ.

pop()
pop({list})				*Vital.Data.List.pop()*
	Removes the last element from a list and returns the element.
https://github.com/vim-jp/vital.vim/blob/5547268490ae58b3f2b7daf2d37c81b590f6f682/doc/vital-data-list.txt
:let VITAL = vital#of('vital')
:let List = VITAL.import('Data.List')
:let p = [1, 2, 3]
:echo List.pop(p)
3
:echo p
[1, 2]
  • 引数がリストであることの説明がない.
push()
push({list}, {val})			*Vital.Data.List.push()*
	Appends {val} to {list} and returns the list itself.
https://github.com/vim-jp/vital.vim/blob/5547268490ae58b3f2b7daf2d37c81b590f6f682/doc/vital-data-list.txt
:let VITAL = vital#of('vital')
:let List = VITAL.import('Data.List')
:let p = [1, 2, 3]
:echo List.push(p, [5])
[1, 2, 3, [5]]
:echo p
[1, 2, 3, [5]]
  • 引数がリストであることの説明がない.
  • 一番後ろに追加することの説明がない
shift()

shift({list}) *Vital.Data.List.shift()*
Removes the first element from a list and returns the element.

https://github.com/vim-jp/vital.vim/blob/5547268490ae58b3f2b7daf2d37c81b590f6f682/doc/vital-data-list.txt
:let VITAL = vital#of('vital')
:let List = VITAL.import('Data.List')
:let p = [1, 2, 3]
:echo List.shift(p)
1
:echo p
[2, 3]
  • 引数がリストであることの説明がない.
unshift()
unshift({list}, {val})			*Vital.Data.List.unshift()*
	Inserts {val} to the head of {list} and returns the list itself.
https://github.com/vim-jp/vital.vim/blob/5547268490ae58b3f2b7daf2d37c81b590f6f682/doc/vital-data-list.txt

echodoc を使ってみて その2

Vimエコーエリアに情報を表示させるプラグイン.

難点として、まだドキュメントがありません。

http://vim-users.jp/2010/12/hack189/
echodoc を使ってみた - vim 初心者の作業メモ
  • 関数の I/F については, ドキュメント書いて pull req 送って取り込んでもらった. 英語がう○こだけど.
  • 日本語マニュアルは取り込まれないと *思う* ので, ここで.
    • 他の Shougo Ware のように更新が進むことはないと思われるので当分使えると思います.
    • 最新版との差異があっても自己責任でお願いします.
    • ドキュメント内の "思います" は syngan の感想文.

概要

echodoc はエコーエリアに情報を表示するプラグインです.
一定時間キー操作を行わなかった場合(autocmd CursorHold, CursorHoldI)にカーソル上にある文字列に対応する情報を表示します.

echodoc は以下の特徴を持ちます. 少なくともこれらの機能は ftplugin では実現できません.

  1. 複数のプラグインから出力する (cmdheight が十分大きな値を持っている必要があります)
  2. ドキュメントを検索する優先度を設定できます.
  3. プラグインをいつでも追加・削除できます.
  4. ハイライトの設定ができます.
  5. neocomplecache がインストールされていれば content filetype に対応できます.
  6. 認識するファイルタイプは複数設定できます.

インストール

NeoBundle (https://github.com/Shougo/neobundle.vim) を利用する場合には
.vimrc に以下のように記述してください.

NeoBundle 'Shougo/echodoc', '', 'default'
call neobundle#config('echodoc', {
      \ 'lazy' : 1,
      \ 'autoload' : {
      \ 'insert' : 1,
      \ }})

注意: 本設定では挿入モードが開始したときに echodoc がロードされます.

insert (Number)
If set to non-zero, neobundle will |:NeoBundleSource| on |InsertEnter| autocmd.
https://github.com/Shougo/neobundle.vim/blob/436e4d4c5fff7023e95d93886724adfb690f3425/doc/neobundle.txt#L700

インターフェース:コマンド

:EchoDocEnable

echodoc を有効にします.

:EchoDocDisable

echodoc を無効にします.

インターフェース:変数

g:echodoc_enable_at_startup

本変数の値が 0 以外の場合には echodoc は起動時に自動的に有効になります.
デフォルト値は 0 (無効) です.

インターフェース:関数

echodoc#enable()

echodoc を有効にします. :EchoDocEnable の関数版です.

echodoc#disable()

echodoc を無効にします. :EchoDocDisable の関数版です.

echodoc#is_enabled()

echodoc が有効な場合に

echodoc#register({name}, {dict})

ドキュメント {dict} を {name} という名前で登録します.
もし同じ {name} のドキュメントが追加済みの場合には上書きされます.
{dict} は辞書型で以下を要素に持ちます:

  • "name" 文字列型 (必須)
    • このドキュメントの名前です. ユーザが任意に設定することができます.
    • 引数の {name} と同じ値であることが要求されます.
  • "rank" 数値型 (必須)
    • このドキュメントの検索優先度です. この値が小さいほど優先度が高くなります.
    • (注意: 1 を設定しておけば良いと思います.)
  • "filetypes" 辞書型 (必須)
    • このドキュメントを検索対象とする filetype を指定します. キー名が対象とするファイルタイプで

キー値は任意の値を設定します.

    • (注意: 1 を設定しておけば良いと思います. プラグイン内では使用されていません)
  • "search" 関数型 (必須)
    • 検索に使用する関数を指定します. この関数はカーソル位置より前にある文字列(?) が cur_text を引数として渡されます.
    • 復帰値は辞書型のリストです.
    • 辞書は以下の要素を持ちます.
      • "text" : 出力する文字列 (必須)
      • "highlight" : 出力する文字列に対するハイライト (任意)
echodoc#unregister({name})

名前が {name} であるドキュメントの登録を解除します.

  • (注意: echodoc#register の引数 {name} が対象ではないことに注意.)
echodoc#get({name})

名前が {name} である {dics} を返します. 登録されているか確認するのに利用できます.

対応している Shougo Ware

neocomplecache や neosnippet, vimshell を利用すると自動的に検索用のドキュメントが追加されます.

	[
		" neocomplcache
		{'rank': 5, 
		 'name': 'include_complete', 
		  'search': function('120'), 
		  'filetypes': {}}, 
		" neocomplcache
 		{'rank': 10, 
		 'name': 'vim_complete', 
		 'search': function('144'), 
		 'filetypes': {'vim': 1}}, 
		" neosnippet
		{'rank': 100,
		 'name': 'neosnippet',
		 'search': function('102'), 'filetypes': {}}, {...}
	]
register()
let s:sample_dict = {
\   'name' : 'sample',
\   'rank' : 1,
\   'filetypes' : {'text' : 1, 'vim' : 1},
\}

function! s:sample_dict.search(cur_text)
	let completion_length = 4
	let words = []
	let i = 0
	while i >= 0
		let word = matchstr(a:cur_text, "[0-9a-zA-Z]\\+", i)
		call add(words, word)
		let i = matchend(a:cur_text, "[0-9a-zA-Z]\\+", i)
	endwhile
	if len(words) == 0
		return []
	endif

	for word in reverse(words)
		if len(word) >= completion_length
			break
		endif
	endfor
	return [
		\ {'text' : word, 'highlight' : 'Identifier'},
		\ {'text' : ' is found'},
		\]
endfunction

call echodoc#register("sample", s:sample_dict)

注意事項

  1. showmode が有効になっていて, cmdheight=1 の時には, 挿入モードにおいて echodoc からの情報は表示されません. showmode を無効にするか, cmdheight に大きな値を設定してください.
  2. search 時に重たい処理を行うと vim が固まったように感じます. 検索に外部コマンドを用いるような場合には本プラグインには向いていないと思います.

echodoc を使ってみた

概要

Vimエコーエリアに情報を表示させるプラグイン.

eldoc.elはエコーエリアに関数の引数を表示させることができるプラグインです。 これと同様なものとして、私はechodocを開発しました。 現在neocomplcacheが対応しています。 eldocのように対応プラグインが自由に機能を追加できるのですが、難点として、まだドキュメントがありません。

http://vim-users.jp/2010/12/hack189/

1.複数のプラグインから出力できる(cmdheightを大きくしないと意味がない)
2.優先度を設定できる
3.動的にプラグインを追加・削除できる
4.ハイライトを設定できる
5.neocomplcacheがインストールされていれば、context filetypeに対応できる
6.認識するファイルタイプは複数設定できる
少なくとも、ftpluginではこういうことはできません
statuslineに表示できないのはデメリットかもしれませんが。
とはいえ、statuslineに長い情報を載せるわけにはいかないので、
るびきちさんも画面最上部に表示するようにしていましたし
echo areaに表示しても問題はないと思います

http://lingr.com/room/vim/archives/2010/08/10#message-610766
syngan の解釈
  1. echodoc#register() で登録された dict を探索して, 見つかった情報をエコーエリアに表示する
  2. 対象となる文字はカーソル位置より前にある文字列?
  3. 一定時間カーソルが動作しない (autocmd CursorHold, CursorHoldI) ときに発動する.
autocmd CursorHold,CursorHoldI * call s:on_cursor_moved()

インストール

NeoBundle で. 最初は lazy じゃないほうがいいかも.
register() されたタイミングで動作するようになるので vim 起動時に動作するわけではないことに注意.

NeoBundle 'Shougo/echodoc', '', 'default'
call neobundle#config('echodoc', {
      \ 'lazy' : 1,
      \ 'autoload' : {
      \ 'insert' : 1,
      \ }})
https://github.com/Shougo/shougo-s-github/blob/36def7452ab88258d0160b1ec45dd6e799809811/vim/.vimrc#L135
insert (Number)
If set to non-zero, neobundle will |:NeoBundleSource| on |InsertEnter| autocmd.
https://github.com/Shougo/neobundle.vim/blob/436e4d4c5fff7023e95d93886724adfb690f3425/doc/neobundle.txt#L700
5/18 22:05 追記

lazy 設定すると, g:echodoc_enable_at_startup が有効にならない感じがする. issue あげていたからそのうち直ると思う.

5/20 21:00 追記
  • 上記のバグは修正されました. neobundle の更新が必要
NOTE
  • "-- 挿入 --" とかが表示されていて, insert モードで動作したことがない. Vim の設定で返れるのかな
    • 5/18 23:30 追記: ":set showmode" の影響. ":set cmdheight=2" とすれば insert モードでも表示できる.
  • なぜ動作しないのか, がなかなかわからない.
    1. register() されているかを確認する方法はない
    2. 対象とするキーワードが何になるかが予想と違った. カーソル位置にある単語が対象ではない
      • hoge の o の位置にカーソルがある場合には ho が検索対象になる

echodoc#register(name, dict)

独自でエコーエリアに表示させたいものがある場合には, echodoc#register を使って登録すれば良い.

  • name は unregister で使用される. dict.name と同じ値を指定すれば良いと思う.
  • dict: 辞書形式, 以下のキーを持つ
    • name::string (必須)
      • 任意につける名前. unregister 時のみに使われる.
    • rank::integer (必須)
      • 登録する dict の優先度. rank が小さいものから優先的に探索される.
    • filetypes::dictionary (必須)
      • dict を検索対象とする filetype を登録する. 辞書のキー名のみを使用し, 値は使用していない. dict.filetypes が空の場合には, すべての filetype に対して探索対象となる (必須)
    • search::funcref(cur_text::string)
      • 探索する関数. カーソル位置より前にある文字列?が cur_text に設定される. (@see echodoc#s:get_cur_text())
      • 復帰値は dictionary の list. dictionary は以下のキーを持つ
      1. text: 出力する文字列 (必須)
      2. highlight echohl により設定される文字列のハイライト(任意)
注意点
  • 同じ名前で登録すると, 以前登録したものは削除される.

autoload/echodoc.vim

function! echodoc#register(name, dict) "{{{
  call echodoc#unregister(a:name)
  call add(s:echodoc_dicts, a:dict)
  call sort(s:echodoc_dicts, 's:compare')
endfunction"}}}

なぜか何もしなくても使える

Shougo Ware を install しているといくつかの plugin が register する

今のところ、neocomplcache や vimshell が対応している

Shougoの開発環境

実際, こんな感じで echodoc が追加されていた.

	[
		" neocomplcache
		{'rank': 5, 
		 'name': 'include_complete', 
		  'search': function('120'), 
		  'filetypes': {}}, 
		" neocomplcache
 		{'rank': 10, 
		 'name': 'vim_complete', 
		 'search': function('144'), 
		 'filetypes': {'vim': 1}}, 
		" neosnippet
		{'rank': 100,
		 'name': 'neosnippet',
		 'search': function('102'), 'filetypes': {}}, {...}
	]

登録部はこんな感じ

autoload/neocomplcache/sources/include_complete.vim

  if neocomplcache#exists_echodoc()
    call echodoc#register('include_complete', s:doc_dict)
  endif
NOTE
  • insert モードに入った時に echodoc が初期化されるが, そのときに echodoc#register() されるわけではないので, vim 起動時にすぐに動作するわけではない. neocomplcache 発動時などに登録される.

TODO

某言語のマニュアルを引くために, 外部コマンドを叩きにいくのだけど, これがとっても遅くて固まる.
非同期でやりたいけど vim 力不足により回避できないでございます.

vital.vim の Web.Http.request を使ってみた その2

  • ヘルプには記述が見当たらないが, 実行環境に curlwget がインストールされている必要がある.

get/post/request

get({url}, {param}, {header})           Vital.Web.Http.get()
post({url}, {param}, {header})          Vital.Web.Http.post()
request(...)                   Vital.Web.Http.request()

get/post に関しては request のラッパであり, request() が使えるようになればいい.

function! s:get(url, ...)
  let settings = {
  \    'url': a:url,
  \    'param': a:0 > 0 ? a:1 : {},
  \    'headers': a:0 > 1 ? a:2 : {},
  \ }
  return s:request(settings)
endfunction

function! s:post(url, ...)
  let settings = {
  \    'url': a:url,
  \    'data': a:0 > 0 ? a:1 : {},
  \    'headers': a:0 > 1 ? a:2 : {},
  \    'method': a:0 > 2 ? a:3 : 'POST',
  \ }
  return s:request(settings)
endfunction
client/command
        "client"        Default: "curl" or "wget" (executable one)
                HTTP client used for a request.

        "command"
                Command name for client.  You should use with "client".
  • 現状, "client" には "curl" か "wget" しか設定できない.
  • "command" は使い道がわからない. たとえば "curl" と全く同じ引数をとるコマンド "hoge" が存在するなら, 以下のように設定すればいいはずだが...
  • "command" は "client" へのフルパスを指定するのを想定しているらしい.
  {"client" : "curl", "command" : "hoge"} 
"maxRedirect"

manpage of curl
リダイレクト追跡回数の上限を指示します

    • wget は指定方法が間違っている気がする.
-   let command .= ' --max-redirect ' . a:settings.maxRedirect
+   let command .= ' --max-redirect=' . a:settings.maxRedirect
"param"

manpage of curl
指定したデータを GET リクエストとして HTTP サーバに送信します。

    • dictionary or string と書いてあるが, string は動作しない.
	"param"		Default: (None)
		Get parameters.  This is a string or a dictionary.
		If dictionary, it is converted to a string by
		|Vital.Web.Http.encodeURI()|.
    • "data" は対応しているのでバグだとおもう
-	let getdatastr = s:encodeURI(settings.param)
+	if s:Prelude.is_dict(settings.param)
+		let getdatastr = s:encodeURI(settings.param)
+	else
+		let getdatastr = settings.param
+	endif
"data"

manpage of curl
指定したデータを POST リクエストとして HTTP サーバに送信します。

    • curl で -d @filename のような @指定ができない?
    • PukiWiki.vim でのファイル添付はどうすんべよ
元のコマンドとの対応
Vital curl wget
maxRedirect --max-redirs --max-redirect
timeout --max-time --timeout
username/password --anyauth --user --http-user, --http-password
data --data-binary --post-file?
headers -H --header
復帰値

ヘルプに復帰値に関しての説明はほとんどない. 実行結果での比較

let g:VITAL = vital#of('vital')
let HTTP = g:VITAL.import('Web.Http')
echo HTTP.get("localhost")
{
   'status': 200, 
   'success': 1, 
   'header': [
       'Date: Tue, 07 May 2013 06:24:47 GMT', 
       'Server: Apache/2.2.22 (Ubuntu)', 
       'Last-Modified: Fri, 26 Feb 2010 10:31:14 GMT', 
       'ETag: "64d92-2d-4807e66192080"', 
       'Accept-Ranges: bytes', 
       'Content-Length: 45', 
       'Content-Type: text/html'
   ], 
   'statusText': 'OK', 
   'content': '<html><body><h1>It works!</h1></body></html>
'}
% telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK                             <== status, statusText
Date: Tue, 07 May 2013 06:30:25 GMT
Server: Apache/2.2.22 (Ubuntu)
Last-Modified: Fri, 26 Feb 2010 10:31:14 GMT
ETag: "64d92-2d-4807e66192080"
Accept-Ranges: bytes
Content-Length: 45
Connection: close
Content-Type: text/html

<html><body><h1>It works!</h1></body></html>
Connection closed by foreign host.
  • 一番問題なのは正常終了か異常終了かの確認方法が記述されていないこと.
    • HTTP から復帰がある場合には, "success", "status", "statusText" に値が設定される.
      • HTTP の status コードをみて, "success" に 0 (異常)または 1 (正常/200番台)が設定される.
    • たとえばネットワークエラーで web サーバに接続できなかったときには "success" は設定されない.
      • エラーメッセージも得られない. 何か異常があったことだけはわかる.
    • たとえば 302 が返ってきた場合にも 0 (異常)が設定される
      • あとから追加された "maxRedirect" の考慮不足かな. うちのせい?
  • "header" はヘッダ部の情報が一行ずつ文字列としてリストに格納される.
    • 解析には parseHeader を使うと良い

parseHeader

parseHeader({headers})                  Vital.Web.Http.parseHeader()
  • request/get/post 関数で得られるヘッダ部は「文字列のリスト」でそれを解析して辞書に変形する.
    • requst() 関数の処理を軽くするために別にしてあるのだと思う.
let g:VITAL = vital#of('vital')
let HTTP = g:VITAL.import('Web.Http')
let RET = HTTP.get("localhost")
echo HTTP.parseHeader(RET.header)
{
	'ETag': '"64d92-2d-4807e66192080"', 
	'Content-Type': 'text/html', 
	'Date': 'Tue, 07 May 2013 06:39:55 GMT', 
	'Accept-Ranges': 'bytes', 
	'Content-Length': '45', 
	'Last-Modified': 'Fri, 26 Feb 2010 10:31:14 GMT', 
	'Server': 'Apache/2.2.22 (Ubuntu)'
}

encodeURI/decodeURI

encodeURI({param})                      Vital.Web.Http.encodeURI()
decodeURI({str})                        Vital.Web.Http.decodeURI()

URL エンコード/デコードをする. まだ使っていない. ローカルの関数を呼び出し中. そのうち使う

encodeURIComponent

encodeURIComponent({str})               Vital.Web.Http.encodeURIComponent()

まだ使っていない. 使い道もしらない.

まとめ

差分

--- autoload/vital/__latest__/web/http.vim	2013-05-05 23:55:12.000000000 +0900
+++ autoload/vital/_3370e3e/web/http.vim	2013-05-07 22:12:02.000000000 +0900
@@ -116,7 +116,11 @@
     let settings.headers['Content-Type'] = settings.contentType
   endif
   if has_key(settings, 'param')
-    let getdatastr = s:encodeURI(settings.param)
+	if s:Prelude.is_dict(settings.param)
+		let getdatastr = s:encodeURI(settings.param)
+	else
+		let getdatastr = settings.param
+	endif
     if strlen(getdatastr)
       let settings.url .= '?' . getdatastr
     endif
@@ -194,7 +198,7 @@
   let command .= ' -O ' . a:quote . a:settings._file.content . a:quote
   let command .= ' --server-response -q -L '
   if has_key(a:settings, 'maxRedirect')
-    let command .= ' --max-redirect ' . a:settings.maxRedirect
+    let command .= ' --max-redirect=' . a:settings.maxRedirect
   endif
   let command .= s:_make_header_args(a:settings.headers, '--header=', a:quote)
   let timeout = get(a:settings, 'timeout', '')
@@ -210,7 +214,7 @@
   let command .= ' ' . a:quote . a:settings.url . a:quote
   if has_key(a:settings._file, 'post')
     let file = a:settings._file.post
-    let command .= ' --post-data @' . a:quote . file . a:quote
+    let command .= ' --post-file ' . a:quote . file . a:quote
   endif
 
   call s:Prelude.system(command)