SublimeLinterはSublimeText2のPackageの一つで、コンパイルエラーやシンタックスエラーが起きた箇所をハイライトする機能です。
下の画像みたいな感じ。
SublimeLinterについて公式は以下のページ。
https://github.com/SublimeLinter/SublimeLinter
SublimeLinterのインストールは以下のページ参照。
http://perl.no-tubo.net/2012/05/03/mac%E3%81%AE%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF-sublime-text2%E3%81%A7javascript%E3%81%AE%E3%82%B7%E3%83%B3%E3%82%BF%E3%83%83%E3%82%AF%E3%82%B9%E3%82%A8%E3%83%A9/
http://watson1978.github.com/blog/2012/08/23/make-a-sublime-text-environment/
で、これでどうやってD言語のハイライトするかを以下に。
###D言語用ファイル追加
SublimeLinterインストール後、[Sublime Text 2]->[Preference]->[Browse Packages…]でパッケージ入ってるフォルダ開く。(MacOSの場合。他のOSは知らない)
んで、パッケージ入ってるフォルダ開いたら、[〜〜/Packages/SublimeLinter/sublimelinter/modules/]フォルダを開く。中にpython.pyとかjava.pyとか入ってると思うので、d.pyを作成する。
###d.pyのガワを作成
d.pyに、objective-j.pyからソースをコピペして、必要ない所削除して以下みたいにする。
import re
import os
import subprocess
from base_linter import BaseLinter
CONFIG = {
'language': 'D'
}
class Linter(BaseLinter):
def built_in_check(self, view, code, filename):
return ''
def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
return ''
built_in_check
がコードをコンパイルしてコンパイルエラーを返す関数、parse_errors
がbuilt_in_check
から来たエラーを元にハイライトする場所を決める関数に当たる。
###built_in_check
の中身
built_in_check
の引数はself
,view
,code
,filename
。selfは自信で(C#で言うthis)、viewはまぁタブのクラス、codeは編集中の保存してないファイルの中身で、filenameはそのまま表示中のファイルの名前。
んで、'built_in_check'にコードコンパイルする中身処理を書くんだけど、注意点とかは以下の通り。
- コンパイル対象はcodeにする。filenameのファイルをコンパイルすると編集前のファイルをコンパイルするのでおかしな事になる。codeをd.pyのある適当なフォルダに保存してそれをコンパイルする
- dmdのコンパイルオプションは
-c -o- -w -unittest -debug
で - 分からなくなったら
base_linter.py
を読もう
ということでその結果が以下。
…
#書いてる途中のファイルを書き出すtempフォルダ
TMPPATH_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '.dtmpfiles'))
if not os.path.exists(TMPPATH_DIR):
os.mkdir(TMPPATH_DIR)
…
class Linter(BaseLinter):
def built_in_check(self, view, code, filename):
def built_in_check(self, view, code, filename):
#パス取得
work_path = os.path.dirname(filename)
file_name = os.path.basename(filename)
#コンパイル場所の設定取得
settings = view.settings().get('SublimeLinter', {}).get(self.language, {})
if(settings):
dwd = settings.get('working_directory', [])
if(dwd):
#プロジェクトに設定あったらそれを取得
work_path = dwd
#書いてる途中のファイルをtempファイルとしてd.py直下に書き出し
tempfilePath = os.path.join(TMPPATH_DIR, file_name)
with open(tempfilePath, 'w') as f:
f.write(code)
args = ["dmd", "-c", "-o-", "-w", "-unittest", "-debug", tempfilePath]
try:
#コンパイル
process = subprocess.Popen(args,
cwd=work_path,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=self.get_startupinfo())
result = process.communicate(None)[0]
finally:
if tempfilePath:
#保存したファイルの削除
os.remove(tempfilePath)
return result.strip()
…
SublimeText2にはプロジェクト管理の機能があるのだけど、そのプロジェクトファイルに設定を書くと、dmdする場所を設定出来る。自分で作ったmoduleとかをimportしてる時はそれをしないとimport部分でコンパイルエラー起こる(と思う)。
設定は以下の感じで。
{
"folders":
[
{
//プロジェクトのフォルダ
"path": "~~~~"
}
],
"settings":
{
"SublimeLinter":
{
"d":
{
//dmd動かしたいフォルダ。上のフォルダと同じになったりするかも。
"working_directory": "~~~~~"
}
}
}
}
###parse_errors
の中身
built_in_check
のreturnで渡されたコンパイルエラーのエラーメッセージはparse_errors
引数のlines
に渡される。
後はlines
の各行にあるエラーメッセージから、エラーメッセージとそのエラー行を切り出してself.add_message
関数に渡せばいい。
ここでの注意点は以下の通り。
- dmdのv2.060では、一部書いたソース意外の場所でのコンパイルエラーが吐き出される。これは修正される予定。この段階ではそんなの吐き出されたらimportしてるだろって前提でimportしてる行にエラー全部入れ込んでる。
-
errorMessages
渡してるがここはあまり意味が無い。というか何が来るか調べてない。多分何も入ってない。 - 分からなくなったら
base_linter.py
を読もう(大事なことなので二回目)
ということで(ry
…
def parse_errors(self, view, errors, lines, errorUnderlines, violationUnderlines, warningUnderlines, errorMessages, violationMessages, warningMessages):
for line in errors.splitlines():
match = re.match(r'^(?P<filename>.+\.d)\((?P<line>\d+)\): Error: (?P<error>.+)', line)
if match:
tab_filename = os.path.basename(view.file_name())
error_filename = os.path.basename(match.group('filename'))
error, line = match.group('error'), match.group('line')
if(tab_filename == error_filename):
self.add_message(int(line), lines, error, errorMessages)
else:
line = 1
module_name = error_filename.replace('.d', '')
regions = view.find_all(r'import.*\.' + module_name)
if(regions):
line = view.rowcol(regions[0].a)[0] + 1
self.add_message(line, lines, error, errorMessages)
これでおそらく上手く行くはずです。上手く行かなかったら修正してください。