Vimのpegjs用シンタックスファイルを改造してみた


研究でPEG(解析表現文法)を扱っており、パーザージェネレータにPEG.jsを使っています。
pegjs形式のファイルはJavaScriptとPEGの文法規則を並べたものから構成されているので、上手いカラーシンタックスが出来ません。一番近いのはJavaScriptシンタックスですが、PEGの文法規則の部分は上手く表示してくれません。

そこで、普段使っているEmacs用のpegjs用シンタックスファイルをWebで探してみるも、見つからず。
Vim用とSublime Text2用のシンタックスファイルはあったので、これを機にMacVimを導入してみました。

MacVimはEmacsに比べて全体的にUIが綺麗な印象です。起動もEmacsよりかなり早い。あとEmacsと違ってCtrl-f4を吸収しないのもいいですね。
ただ、やはりモード切り替えが慣れない。ずっと使ってれば慣れるのかな。

こちらのpegjs用シンタックスを使ってみましたが、JavaScriptの部分のコードの色づけをしてくれません。僕が普段書くコードはJavaScriptの部分が大半を占めるので、これではあまり使い勝手がよくありません。


そこで、マニュアルを見ながらpegjs.vimファイルを改造してJavaScriptの部分にも色が付くようにしました。
具体的には、MacVimに標準で入っているJavaScriptシンタックスファイルのjavascript.vimを丸ごとpegjs.vimにコピーし、pegjs.vimJavaScriptと判断される部分に適用されるようにしました。

"This file is pegjs.vim + javascript.vim(vim default).


	" pegjs.vim
	" Language: PEG JS Grammars
	" Maintainer: Andrew Lunny <alunny@gmail.com>
	" Latest Revision: 15 June 2012
	" License: MIT



" PEG's rule Syntax(import from pegjs.vim)

if exists("b:current_syntax")
    finish
endif

syn match pegOperator "[/.+*?&!]"
syn region charSet start="\[" end="\]"
syn match rule "[a-zA-Z$_][a-zA-Z$_0-9]*"
syn match ruleDef "[_a-zA-Z$][a-zA-Z$_0-9]*" contained
syn match equals "=" contained
syn match initialize "[a-zA-Z$_][a-zA-Z$_0-9]*[\n\t ]*\".*\"[\n\t ]*=" contains=ruleDef,equals,innerLiteral
syn match initialize "[a-zA-Z$_][a-zA-Z$_0-9]*[\n\t ]*'.*'[\n\t ]*=" contains=ruleDef,equals,innerLiteral
syn match initialize "[a-zA-Z$_][a-zA-Z$_0-9]*[\n\t ]*=" contains=ruleDef,equals
syn match exprLabel "[a-zA-Z$_][a-zA-Z$_0-9]*:"he=e-1
syn region literal start="'" end="'" 
syn region literal start="\"" end="\"" 
syn region innerLiteral start="'" end="'" contained
syn region innerLiteral start="\"" end="\"" contained
syn region comment start="/[*]" end="[*]/"
syn region comment start="//" end="\n"

syn region jsBlock start="{" end="}" contains=ALLBUT,pegOperator,charSet,rule,ruleDef,equals,initialize,exprLabel,literal,innerLiteral,comment,jsBlock

hi def link ruleDef         PreProc
hi def link rule            Type
hi def link exprLabel       Identifier
hi def link literal         String
hi def link pegOperator     Operator
hi def link charSet         Operator
hi def link whitespace      PreProc
hi def link innerLiteral    Comment
"hi def link jsBlock		    Comment
"hi jsBlock guibg=#cccccc
"hi ruleDef gui=underline,bold



" JavaScript Block Syntax(import from javascript.vim)

if !exists("main_syntax")
  if version < 600
    syntax clear
  elseif exists("b:current_syntax")
    finish
  endif
  let main_syntax = 'javascript'
endif

" Drop fold if it set but vim doesn't support it.
if version < 600 && exists("javaScript_fold")
  unlet javaScript_fold
endif


syn keyword javaScriptCommentTodo      TODO FIXME XXX TBD contained
syn match   javaScriptLineComment      "\/\/.*" contains=@Spell,javaScriptCommentTodo contained
syn match   javaScriptCommentSkip      "^[ \t]*\*\($\|[ \t]\+\)" contained

syn region  javaScriptComment	       start="/\*"  end="\*/" contains=@Spell,javaScriptCommentTodo contained
syn match   javaScriptSpecial	       "\\\d\d\d\|\\." contained
syn region  javaScriptStringD	       start=+"+  skip=+\\\\\|\\"+  end=+"\|$+	contains=javaScriptSpecial,@htmlPreproc contained
syn region  javaScriptStringS	       start=+'+  skip=+\\\\\|\\'+  end=+'\|$+	contains=javaScriptSpecial,@htmlPreproc contained

syn match   javaScriptSpecialCharacter "'\\.'" contained
syn match   javaScriptNumber	       "-\=\<\d\+L\=\>\|0[xX][0-9a-fA-F]\+\>"
syn region  javaScriptRegexpString     start=+/[^/*]+me=e-1 skip=+\\\\\|\\/+ end=+/[gi]\{0,2\}\s*$+ end=+/[gi]\{0,2\}\s*[;.,)\]}]+me=e-1 contains=@htmlPreproc oneline contained

syn keyword javaScriptConditional	if else switch
syn keyword javaScriptRepeat		while for do in
syn keyword javaScriptBranch		break continue
syn keyword javaScriptOperator		new delete instanceof typeof
syn keyword javaScriptType		Array Boolean Date Function Number Object String RegExp
syn keyword javaScriptStatement		return with
syn keyword javaScriptBoolean		true false
syn keyword javaScriptNull		null undefined
syn keyword javaScriptIdentifier	arguments this var let
syn keyword javaScriptLabel		case default
syn keyword javaScriptException		try catch finally throw
syn keyword javaScriptMessage		alert confirm prompt status
syn keyword javaScriptGlobal		self window top parent
syn keyword javaScriptMember		document event location 
syn keyword javaScriptDeprecated	escape unescape
syn keyword javaScriptReserved		abstract boolean byte char class const debugger double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile 

if exists("javaScript_fold")
    syn match	javaScriptFunction	"\<function\>"
    syn region	javaScriptFunctionFold	start="\<function\>.*[^};]$" end="^\z1}.*$" transparent fold keepend

    syn sync match javaScriptSync	grouphere javaScriptFunctionFold "\<function\>"
    syn sync match javaScriptSync	grouphere NONE "^}"

    setlocal foldmethod=syntax
    setlocal foldtext=getline(v:foldstart)
else
    syn keyword javaScriptFunction	function
    "syn match	javaScriptBraces	   "[{}\[\]]" contained
    syn match	javaScriptBraces	   "[\[\]]" contained
	syn region jsBlocks matchgroup=Function start="{" end="}" contains=ALLBUT,pegOperator,charSet,rule,ruleDef,equals,initialize,exprLabel,literal,innerLiteral,comment,jsBlock containedin=jsBlock,jsBlocks
    syn match	javaScriptParens	   "[()]"
endif

syn sync fromstart
syn sync maxlines=100

if main_syntax == "javascript"
  syn sync ccomment javaScriptComment
endif

" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_javascript_syn_inits")
  if version < 508
    let did_javascript_syn_inits = 1
    command -nargs=+ HiLink hi link <args>
  else
    command -nargs=+ HiLink hi def link <args>
  endif
  HiLink javaScriptComment		Comment
  HiLink javaScriptLineComment		Comment
  HiLink javaScriptCommentTodo		Todo
  HiLink javaScriptSpecial		Special
  HiLink javaScriptStringS		String
  HiLink javaScriptStringD		String
  HiLink javaScriptCharacter		Character
  HiLink javaScriptSpecialCharacter	javaScriptSpecial
  HiLink javaScriptNumber		javaScriptValue
  HiLink javaScriptConditional		Conditional
  HiLink javaScriptRepeat		Repeat
  HiLink javaScriptBranch		Conditional
  HiLink javaScriptOperator		Operator
  HiLink javaScriptType			Type
  HiLink javaScriptStatement		Statement
  HiLink javaScriptFunction		Function
  HiLink javaScriptBraces		Function
  HiLink javaScriptError		Error
  HiLink javaScrParenError		javaScriptError
  HiLink javaScriptNull			Keyword
  HiLink javaScriptBoolean		Boolean
  HiLink javaScriptRegexpString		String
  HiLink javaScriptIdentifier		Identifier
  HiLink javaScriptLabel		Label
  HiLink javaScriptException		Exception
  HiLink javaScriptMessage		Keyword
  HiLink javaScriptGlobal		Keyword
  HiLink javaScriptMember		Keyword
  HiLink javaScriptDeprecated		Exception 
  HiLink javaScriptReserved		Keyword
  HiLink javaScriptDebug		Debug
  HiLink javaScriptConstant		Label

  delcommand HiLink
endif

let b:current_syntax = "javascript"
if main_syntax == 'javascript'
  unlet main_syntax
endif


上記を.vim/syntax/pegjs.vimに書いて、.vimrcに

autocmd BufNewFile,BufReadPost *.pegjs set filetype=pegjs

と書くと拡張子がpegjsのファイルにこのシンタックスが適用されます。今のところ問題なく動くみたい。しばらくvimを使ってみます。


偶然にも今日はpegjs.vimの作者様のLatest Rivisionのぴったり1年後でした。