LoginSignup
8

More than 5 years have passed since last update.

SublimeLinterにD言語(他の任意の言語)を追加してハイライト出来るようにする

Last updated at Posted at 2012-11-26

SublimeLinterはSublimeText2のPackageの一つで、コンパイルエラーやシンタックスエラーが起きた箇所をハイライトする機能です。
下の画像みたいな感じ。

SublimeLinter例

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は知らない)
BrowsePackage…

んで、パッケージ入ってるフォルダ開いたら、[〜〜/Packages/SublimeLinter/sublimelinter/modules/]フォルダを開く。中にpython.pyとかjava.pyとか入ってると思うので、d.pyを作成する。

d.pyのガワを作成

d.pyに、objective-j.pyからソースをコピペして、必要ない所削除して以下みたいにする。

d.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_errorsbuilt_in_checkから来たエラーを元にハイライトする場所を決める関数に当たる。

built_in_checkの中身

built_in_checkの引数はself,view,code,filename。selfは自信で(C#で言うthis)、viewはまぁタブのクラス、codeは編集中の保存してないファイルの中身で、filenameはそのまま表示中のファイルの名前。
んで、'built_in_check'にコードコンパイルする中身処理を書くんだけど、注意点とかは以下の通り。

  1. コンパイル対象はcodeにする。filenameのファイルをコンパイルすると編集前のファイルをコンパイルするのでおかしな事になる。codeをd.pyのある適当なフォルダに保存してそれをコンパイルする
  2. dmdのコンパイルオプションは-c -o- -w -unittest -debug
  3. 分からなくなったらbase_linter.pyを読もう

ということでその結果が以下。

d.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部分でコンパイルエラー起こる(と思う)。
設定は以下の感じで。

xxx.sublime-project
{
    "folders":
    [
        {
            //プロジェクトのフォルダ
            "path": "~~~~"
        }
    ],

    "settings":
    {
        "SublimeLinter":
        {
            "d":
            {
                //dmd動かしたいフォルダ。上のフォルダと同じになったりするかも。
                "working_directory": "~~~~~"
            }
        }
    }
}

parse_errorsの中身

built_in_checkのreturnで渡されたコンパイルエラーのエラーメッセージはparse_errors引数のlinesに渡される。
後はlinesの各行にあるエラーメッセージから、エラーメッセージとそのエラー行を切り出してself.add_message関数に渡せばいい。
ここでの注意点は以下の通り。

  1. dmdのv2.060では、一部書いたソース意外の場所でのコンパイルエラーが吐き出される。これは修正される予定。この段階ではそんなの吐き出されたらimportしてるだろって前提でimportしてる行にエラー全部入れ込んでる。
  2. errorMessages渡してるがここはあまり意味が無い。というか何が来るか調べてない。多分何も入ってない。
  3. 分からなくなったらbase_linter.pyを読もう(大事なことなので二回目)

ということで(ry

d.py

    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)

これでおそらく上手く行くはずです。上手く行かなかったら修正してください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8