javascript indent in vim

javascriptの自動インデントがしたいんです。
いまいちなのしかなかったので、自作。ソースは下のほうに。

http://d.hatena.ne.jp/nagaton/20070623/1182580881
このプラグイン使ってたんだけどさ、けっこう精度が低い。

foo(hoge,function(){})

の})あたりで誤マッチしたり、いろいろ。
オートインデントの精度が低いと、悲惨なことになるので、なんとかしたい。
このプラグインをベースに改造。ちょっとはマシになったんだけど、ラベルのインデントをどうやればいいのか分からない。

var x= {
  name: value,
  func:
    function() {
      hogehoge
    } //ここで二段ぶん戻さないといけない
}

みたいなやつ。
マッチする括弧がある行を簡単に取得する方法ってないのだろうか。
これは

var x= {
name: value,
func:
  function() {
    hogehoge
  }
}

という整形にすることで一応解決。

var y={
name: 'hogehoge',
value: 10
}

となって、かなりださいけど。

多段にネストした、ブロックがないif/for/else(一般的になんて呼ぶんだろ)のインデントも同じ問題で困った。

if(cond)
  for(var x in array)
    if(cond2)
      hage()
foo()

みたいなやつ。これは二段までハードコーディングして、「この形式で三段以上ネストするのは馬鹿か変態」と強弁することにした。

あと、一行ifの判別。

if(cond) body

このif(cond)部にマッチさせるにはどうすればいいのか。
condが複数行になるケースを除外するとしても、正規表現は原理的にネストした括弧のマッチができないはず。
結局、一行ifの行末には*必ず*セミコロンがあるはずだという制限を設けた。

このへん、かなり適当なことしてるからむりしないでね。素直に中括弧書いたほうがいいよ。



というか、正規表現による行単位マッチで自動インデントしようってのが無謀なんだよね。誤認識が許されない(一箇所間違えると連鎖的に後方のインデントが崩れる)んだから。理想的には構文解析ベースじゃないと困る。
BNFベースの文字列検索とかできるといいんだけど。

以下ソース。indent/javascript.vimにいれるとうごくよ

" Vim indent file
" Language:	JavaScript
" Author:	id:gnarl(http://d.hatena.ne.jp/gnarl)
" URL:		-
" Last Change:  2007 aug 4
"
" oritinal author: Ryan (ryanthe) Fabella <ryanthe at gmail dot com>
" original file: http://www.vim.org/scripts/script.php?script_id=1936

if 1 "release only
	if exists('b:did_indent')
		finish
	endif
	let b:did_indent = 1
	" Only define the functions once per Vim session.
	if exists("*GetJsIndent")
		finish 
	endif
endif


setlocal indentexpr=GetJsIndent()
setlocal indentkeys=0{,0},0),0:,!^F,o,O,e,*<Return>,=*/
" Clean CR when the file is in Unix format
if &fileformat == "unix" 
    silent! %s/\r$//g
endif
function! GetJsIndent()
    let pnum = prevnonblank(v:lnum - 1)
    if pnum == 0
       return 0
    endif
	let ppnum=prevnonblank(pnum-1)
	let pppnum=prevnonblank(ppnum-1)

    let line = substitute(getline(v:lnum),'//.*','','')
    let pline = substitute(getline(pnum),'//.*','','')

	let ppline=''
	if ppnum != 0
		let ppline=substitute(getline(ppnum),'//.*','','')
	endif
	let pppline=''
	if pppnum != 0
		let pppline=substitute(getline(pppnum),'//.*','','')
	endif

    let ind = indent(pnum)

	let line_end='\s*$'
	let js_name='[a-zA-Z_][a-zA-Z0-9_]*'
	let js_lit='\S*'
	let reg_or='\|'

	"normal block
	if pline =~ '('.line_end .reg_or.'{'.line_end .reg_or.'['.line_end
		let ind=ind+&sw
	endif
	if line =~ '^\s*}'.reg_or.'^\s*)'.reg_or.'^\s*})'.reg_or.'^\s*]'
		let ind=ind-&sw
	endif

	" if/else/for
	let js_open_oneline='^\s*if(.*)' .line_end .reg_or. '^\s*for(.*)' .line_end .reg_or. '^\s*}\=\s*else'.line_end	
	if pline =~ js_open_oneline
		let ind=ind+&sw
	endif
	if ppline =~ js_open_oneline && pline !~ js_open_oneline
		let ind=ind-&sw
	endif
	if pppline =~ js_open_oneline && ppline =~ js_open_oneline && pline !~ js_open_oneline
		let ind=ind-&sw
	endif

	" object name/case label
	let obj_name='^\s*'.js_name.'\s*:'
	let case_label='^\s*case\s*'.js_lit.'\s*:'
	let label=obj_name.reg_or.case_label
	if line=~label && pline !~ label
		let ind=ind-&sw
	endif
	if pline =~label && line !~ label
		let ind=ind+&sw
	endif
    return ind
endfunction