By | 2009年4月14日

id:dann さんの記事 「snipMateのsnippetが書きやすい件」snipMate.vim が紹介されてて試してみた。
snipettsEmu.vim は snippets の追加がめんどくさかったってのもあるし
入力がタイポしてたりすると編集後に次の場所に移動できなかったりとか

<h1>(1)</h1>
(2)

(1)入力後 => (2)に移動といったことをしようとすると html_snippets.vim を書き直す必要があった。
snipMate は sunppets の追加が簡単そうだったので乗り換えてみた。

インストール

snipMate.vim をダウンロードしてきて
.vim ディレクトリ以下で解凍すれば終了。
使い方は snipettsEmu と同じです。

snippetsの構造

$ tree html
snippets
|-- html.snippet <- filetype
 
$ tree html
snippets
|-- html <- filetype
    |-- body.snippet <- trigger
    `-- h1.snippet

html.snippets

snippet body <- trigger
    <body>
        ${1}
    </body>
snippet h1 <- trigger
    <h1>${1}</h1>
    ${2}

body.snippet

<body>
${1}
</body>

h1.snippet

<h1>${1}</h1>
${2}

この2つの構造は全く同じ意味です。
つまりは filetype ベースで trigger を作成しておけば良いということですね。
少しだけ .snippets の書き方の説明をしておきます。

${1} : 最初に入力する単語。デフォルトのままだと <tab> を入力すると ${2} に移動します。
${1:hoge} : デフォルトで hoge という単語が入力されている状態になります。
$1 : ${1} で入力された値と同期されます。

このように最初のインデントが必須。

trigger.snippets の場合は

<h1>${1}</h1>
${2}

このように実際に補完される文字をファイルに書いておけばいいから分かりよい。
ただ、ファイルが増えまくるのもどうかとも思うが。
自分の好きな方で filetype.sunippet もしくはディレクトリ構造に分ければいいと思う。

snippetが複数マッチする場合

この機能がかなり便利。

[dealforest@imac-local] tree html
snippets
|– html
|– doctype
|– HTML.snippet
`– XHTML.snippet

といった構造だとする。
この場合に hoge.html を開き doctype を入力し を実行すると

1. HTML
2. XHTML
Type number and <enter> or click with mouse (empty cancels):

とう画面が表示されるので 2 と入力すると XHTML の snippet が補完される。
ちなみにこれは html.snippets でいうと

snippets doctype HTML
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""
    "http://www.w3.org/TR/html4/loose.dtd">
snippets doctype XHTML
     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

と同じこと。

補完を tab から c-b に変更

.vim/after/plugin/snipMate.vim

-ino <silent> <tab> <c-r>=TriggerSnippet()<cr>
-snor <silent> <tab> <esc>i<right><c-r>=TriggerSnippet()<cr>
+ino <silent> <c-b> <c-r>=TriggerSnippet()<cr>
+snor <silent> <c-b> <esc>i<right><c-r>=TriggerSnippet()<cr>

.vim/plugin/snipMate.vim

s/<tab>/<c-b>/

document には.vim/after/plugin/snipMate.vim だけ変更するといけるって書いてるけどそれだけじゃ駄目。
.vim/plugin/snipMate.vim の方も変更してやらないとたまに動かなくなるので注意。
<tab> ってハードコードされてた。。。。

tips

コマンド実行

snippet date
    `system("date +%Y-%m-%d")`

ファイル名に変換

snippet constructor
    public `Filename()`() {
    }

こんなことも可能。おほー

TT, perl の snippet を移行した

snippetsEmu で使わせてもらってた snippet を、せっかくなんでそのまま snipMate に移行しました。
ちなみに使用していた snippet は CatalystとTT用snippetsEmu.vim設定 です。
id:spiritloose さんのスニペットには本当にいつも助けられていました。感謝感謝。

tt2.snippet

snippet class
	[% USE ${1:class} = Class('${2:ClassName}') %]
	${3}
snippet url
	[% USE ${1:name} = url('${2:path}') %]
	${3}
snippet linkto
	<a href="[% c.uri_for('${1:path}', ${2:param}) %]¥">${3:label}</a>
	${4}
snippet if
	[% IF ${1:condition} -%]"
	${2}
	[% END -%]
	${3}
snippet ife
	[% IF ${1:condition} -%]
	${2}
	[% ELSE -%]
	${3}
	[% END -%]
	${4}
snippet ifee
	[% IF ${1:condition1} -%]
	${2}
	[% ELSIF ${3:condition2} -%]
	${4}
	[% ELSE -%]
	${5}
	[% END -%]
	${6}
snippet unless
	[% UNLESS ${1:condition} -%]
	${2}
	[% END -%]
	${3}
snippet for
	[% FOR ${1:var} = ${2:list} %]
	${3}
	[% END -%]
	${4}
snippet foreach
	[% FOREACH ${1:var} = ${2:list} %]
	${3}
	[% END -%]
	${4}
snippet while
	[% WHILE (${1:var} = ${2:rs}.next) -%]
	${3}
	[% END -%]
	${4}
snippet switch
	[% SWITCH ${1:var} -%]
	${2}
	[% END -%]
	${3}
snippet case
	[% CASE ${1:var} -%]
	${2}
snippet include
	[% INCLUDE '${1:file}' %]
	${2}
snippet process
	[% PROCESS '${1:file}' %]
	${2}
snippet macro
	[% MACRO ${1:name}(${2:arg}) BLOCK %]
	${3}
	[% END %]
	${4}
snippet var
	[% ${1:var} %]
	${2}
snippet hvar
	[% ${1:var} | html %]
	${2}
snippet hlvar
	[% ${1:var} | html | html_line_break %]
	${2}
snippet null
	[%- FILTER null -%]
	${1}
	[%- END -%]
	${2}

perl.snippet

#perl
snippet dump
	use Data::Dumper; warn Dumper ${1:$var};
	${2}
snippet say
	print ${1:$var}, "¥n";
	${2}
snippet self
	my $self = shift;
	${2}
snippet data
	my $data = do {
		local $/;
		<data>
	};
	${1}
snippet argf
	while (<>) {
		chomp;
		${1}
	}
	${2}
snippet isa
	if (blessed ${1:$var} and ${2:$var}->isa('${3:Class}')) {
		${4}
	}
	${5}
snippet readcsv
	use IO::File;
	use Text::CSV_XS;
 
	my $fh = IO::File->new('${1:filename}') or die 'cannot open file.';
	my $csv = Text::CSV_XS->new({ sep_char => "¥t", binary => 1 });
	until ($fh->eof) {
		my $cols = $csv->getline($fh);
		unless ($cols) {
			warn $csv->error_diag;
			next;
		}
		my (${2}) = @$cols;
	}
	$fh->close;
	${3}
 
#class-C3
snippet next
	$self->next::method(@_);
	${1}
snippet maybe
	$self->maybe::next::method(@_);
	${1}
 
#Catalyst
snippet debug
	$c->log->debug('${1:[ debug ]}: '. ${2:$var});
	${3}
snippet warn
	$c->log->warn('${1:[ warn ]}: '. ${2:$var});
	${3}
snippet dumper
	$c->log->dumper('${1:[ dumper ]}: '. ${2:$var});
	${3}
snippet model
	$c->model('${1:model}')
	${2}
snippet view
	$c->view('${1:view}')
	${2}
snippet template
	$c->view('View::TT')->template('${1:name}');
	${2}
snippet config
	$c->config->{${1:name}}
	${2}
snippet controller
	sub ${1:func} : ${2:Attribute} {
		my ($self, $c) = @_;
		${3}
	}
	${4}
snippet begin
	sub begin : Private {
		my ($self, $c) = @_;
		${1}
		1;
	}
	${2}
snippet auto
	sub auto : Private {
		my ($self, $c) = @_;
		${1}
		1;
	}
	${2}
snippet detach
	$c->detach('${1:name}');
	${2}
snippet forward
	$c->forward('${1:name}');
	${2}
snippet stash
	$c->stash->{${1:var}}${2}
snippet flash
	$c->flash->{${1:var}}${2}
snippet session
	$c->session->{${1:var}}${2}
snippet sstash
	$c->stash->{${1:var}} = ${2};
	${3}
snippet sflash
	$c->flash->{${1:var}} = ${2};
	${3}
snippet ssession
	$c->session->{${1:var}} = ${2};
	${3}
snippet rs
	$c->model('DBIC::${1:Source}')
	${2}
snippet redirect
	$c->res->redirect($c->uri_for('${1:uri}'));
	${2}
snippet param
	$c->req->param('${1:param}')
	${2}

snippetsEmu の不便だったとこがなくなったー
相当快適になった。
ただどうしても分からなかったのが外部で定義した snippet を include するやり方がわからなかった。
そのため .tt の場合には filetype を tt2.html とすることで回避した。