Sublime Textプラグイン作成に関する話題

Sublimeプラグイン作成とスニペット,コード補完,マクロ,キーバインド,メニュー定義など付随する話題をあれこれと...

【 目次 】

プラグイン作成の参考URL

英語だがバイブル的なものから

日本語でわかりやすいのは以下の記事

  • Sublime Text2でプラグインを作る | Developers.IO
  • Sublime Text 2 のプラグインを作る | ありえるえりあ

    特に、Default のプラグインは、サンプル例となることを意識して作られているようなので、参考になります。
    プラグインから組み込みのコマンドを呼ぶことができるのですが、どのようなコマンドがあるかを調べるには、キーバインディングの定義が参考になります。
    メニューの Preferences > Key Bindings – Default で keymap のファイルを開き、バインドしているキーからコマンド名を逆引きするのが効率のよいやり方です。

メニューからTools->New Plugin...と選択し、プラグインのテンプレートを作成。
プラグインのクラス名はXxxCommand
通常はsublime_plugin.TextCommandを継承
クラス名からCommandを除き、スネークケースにしたものがコマンド名になる。
作成したプラグインソースはパッケージディレクトリ(メニューからPreferences > Browse Packagesを選択して表示されるディレクトリ)直下のUserディレクトリもしくはパッケージディレクトリ直下に新たにデレクトリを作成して保存。
プラグインの実行は、コンソールWindowから

view.run_command('コマンド名')

より詳しい話題やサンプルコードなど

作成したコマンドはkeymapやmenuに登録して実行する事になる。

メニューへ追加

Main.sublime-menu メインメニューに追加
Side Bar.sublime-menu サイドバーでファイルやフォルダを右クリックしたときに出るメニューに追加
Context.sublime-menu エディタで右クリックしたときに出るメニューに追加

差分を記入する。

メインメニューにMyCommandsという自作のコマンドを実行するメニューを追加する例を以下に示す。

Main.sublime-menu

[
    {
        "id": "myplugin",
        "caption": "MyCommands",
        "mnemonic": "M",
        "children":
        [
            {
                "id": "my_g_search",
                "caption": "googleで検索",
                "mnemonic": "g",
                "command": "my_google_search"
            },
            {
                "id": "my_surround_line",
                "caption": "<>で囲む",
                "mnemonic": "<",
                "command": "my_surround_line",
                "args": {"begin_str":"<", "end_str":">"}
            }
        ]
    },
]

captionにメニューに表示される文字列,argsでコマンドの引数(後述),mnemonicはショートカットキーを示しているようだ。

コンソールWindowよりPythonのコマンドを実行してみる。

Sublime Text2のコンソールWindowはPythonの対話型コンソールって感じ。

コンソールWindowは、ctrl + @またはctrl + shift + `またはメニューのView->Show Consoleにて表示
コンソールWindow下部のテキストボックスにてPythonのコマンドを実行できる。

コンソールの実行環境を確認

Sublime Text Tips 2. コンソールで使用できる変数 - 日記

Consoleでは pythonコードを実行可能です。
プラグインのエラーなどもこのコンソールで確認することができます。
Consoleでは手軽にpythonコードを試すことができます。
使える物の一覧は、「globals().keys()」で表示できます。

printコマンドによりコンソールに出力

x='あいうえお'
print(x[0])

カレントデレクトリを表示

import os
print(os.getcwd())

メモ帳を起動

os.system("notepad.exe")

プラグインのAPIを利用して、編集中のファイル名を表示

view.file_name()

Sublime Text 2Sublime Text 3のプラグインの互換性に関する話題

Sublime Text 2のプラグインソースを参考にSublime Text 3のプラグインを作る場合に、問題となることがある。
begin_edit() と end_edit()がSublime Text 3では使えない等。

プラグイン作成の参考URLで紹介した「Sublime Text 2 のプラグインを作る | ありえるえりあ」の記事にあるSublime Text 2ソースSublime Text 3で実行したところ19行目でエラーになってしまった。 on_done等のイベントの中でeditオブジェクトは使えないらしい。

それを自分なりに修正を試みたのが以下のコード

SublimeSurroundCommandをSublime3に対応

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Sublime3SurroundCommand(sublime_plugin.TextCommand):

    def on_done(self, word):
        self.view.run_command('sublime3_surround_sub',{"word": word})

    def run(self, edit):
        # Show input panel for surround word, then handling input
        self.view.window().show_input_panel('Surrond Word: ', '',
            self.on_done, None, None)

class Sublime3SurroundSubCommand(sublime_plugin.TextCommand):

    def run(self, edit, word):
        for region in self.view.sel():
            if not region.empty():
                s = self.view.substr(region)
                surroundStr = word + s + word

                # Replace the selection with transformed text
                self.view.replace(edit, region, surroundStr)

editを取得するにはbegin_edit()を使うのではなく、self.view.run_commandを使って別のコマンドを作成して呼び出すって事かな。

アウトプットパネルを表示してみる。

他のSublime Textの記事も参考に。

最終的なコードは

OutputPanel.py

1
2
3
4
5
6
7
8
9
class OutputPanelCommand(sublime_plugin.TextCommand):
    def run(self,edit):
        window = self.view.window()
        output_view = window.create_output_panel("WindowName")
        window.run_command("show_panel", {"panel":"output.WindowName"})

        output_view.set_read_only(False)
        output_view.insert(edit, output_view.size(), "Hello, Output")
        output_view.set_read_only(True)

キーバインドとコマンド

コマンドに引数を渡す。

通常、プラグインクラスのrunメソッドは以下のようなコードになるが、

プラグインクラスのrunメソッド

class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):

runメソッドには以下のように自由に引数を定義できる。

プラグインクラスのrunメソッド - 自由に引数を定義

class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit, arg1, arg2, arg3):
        print "arg1={0}, arg2={1}, arg3={2}".format(arg1, arg2, arg3)

この追加の引数付きのrunメソッドをコンソールより実行。

view.run_command('example', {"arg1": "arg_value1", "arg2": "arg_value2", "arg3": "arg_value3"})

コマンドをキーバインド(作成したコマンドをショトッカトキーに設定)

作成したコマンドをショートカットキーに登録するには

SublimeText2のキーバインドを、ちょっと突っ込んで解説 - Qiita

引数を追加したコマンドをkeymapに登録してみる。

{
    "keys": ["キー"],
    "command": "example",
    "args": {
        "arg1": "arg1_value",
        "arg2": "arg2_value",
        "arg3": "arg3_value"
    }
}

argsに引数リストを列挙すれば良い。

プラグインより他のsublime textのコマンドを実行

既に示したとおり、プラグインより他のプラグインのコマンド(sublime textのコマンド)を実行する事ができる。

プラグインより他のプラグインのコマンド)を実行

class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        ...
        self.view.run_command('sublime3_surround_sub',{"word": "*"})
        ...

sublime textのコマンドにはどのようなものがあるかは、メニューよりPreferences > Key Bindings – Default で Default (xxx).sublime-keymapを参照してみれば良い。
例えば、次の行は

    { "keys": ["ctrl+s"], "command": "save" },

以下のコマンドをコンソールにて実行する事で編集中のファイルが保存できる事を示している。

view.run_command('save')

コマンドを簡単に見つけるには

と、ここまで書いたところで、コマンドを見つけるもっと良い方法がある。(SublimeText2 - Sublime Text 2でコマンドパレットのコマンドにショートカットキーを割り当てる - Qiita)
コンソールにて以下ののコマンドを実行すると、どのようなコマンドが実行されたのかコンソールに出力してくれる。

sublime.log_commands(True)

コマンドをコマンドパレットに登録(拡張子.sublime-commandsというファイルに記述。 )

パッケージディレクトリに新しいデレクトリを作成し、 デレクトリ名.sublime-commandsというファイルにコマンド定義を記述してもいいみたい。

引数付きコマンドのコマンドパレット登録の例

[
    {
        "caption": "myPlugin: 自由に引数を定義できる。",
        "command": "example",
        "args": {
            "arg1": "arg1_value",
            "arg2": "arg2_value",
            "arg3": "arg3_value"
        }
    }
]

スニペットとコード補完

キーワードを入力してタブキーやctrl+spaceでコード補完やスニペットが可能。
例えばhtmlファイル編集中に以下のキーワードを入力してtabキーやctrl+spaceを押すと⇒のように展開される。、

srcまたはscript⇒<script type="text/javascript"></script>
link⇒<link rel="stylesheet" type="text/css" href="">
style⇒<style type="text/css"></style>
php⇒<?php ?>
html⇒<html>..

スニペット

スニペット定義ファイルの作成は、プラグインと同様、メニューからTools->New Snippet...と選択し、スニペットのテンプレートを作成。
作成したプラグインソースはパッケージディレクトリ直下のUserディレクトリもしくはパッケージディレクトリ配下に保存。
スニペット定義ファイルの拡張子は「.sublime-snippet」。

コマンドパレットにてsnippet:を入力,スニペットを検索して呼び出すこともできる。
メニューからTools > Snippets...を選択しても同じ。
1つのファイルに複数のスニペットを登録する事はできない。(1ファイル1スニペット)

insert_snippetコマンド

insert_snippetコマンドを使ってスニペットを挿入できる。 例えば、文字列を選択してコンソールより以下を実行すると、選択した文字列が{}で囲まれることになる。

view.run_command('insert_snippet',{"contents": "{${0:$SELECTION}}"})

同様に、keymapに以下を登録する事で、文字列を選択した状態で{をキー入力すれば、選択した文字列が{}で囲まれることになる。

// 文字列を選択状態で `{` を入力すると '}' が自動入力されて、選択文字列を `{}` で囲む
{ "keys": ["{"], "command": "insert_snippet", "args": {"contents": "{${0:$SELECTION}}"} }

コード補完

スニペットとの違いがわかりづらいのだが、似たような機能としてコード補完があるもよう。 Packagesデレクトリの配下に拡張子.sublime-completionsのファイルを作成して、コード補完の定義を記述する。

スニペットは1ファイル1スニペットの定義であるが、コード補完の定義ファイルは1つのファイルに複数のコード補完を定義できる。

シンタックス(ファイル)とScope Name,Syntax Definitions

スニペット定義ファイルのscopeの項目にsource.pythonと指定すると、pythonのソースを編集中でないとこのスニペットは有効にならない。
また、htmlファイル編集中の時だけスニペットを有効にするにはscopeにtext.htmlと指定する。

scopeにはどのような文字列を指定すれば良いのだろうか?

上記の記事によれば、コンソールWindowより
view.scope_name(view.sel()[0].begin()).strip().split(' ')[0]
を実行する事により、編集中のファイルのscope Nameを知る事ができるそうである。

またctrl+alt+shift+pキーを押下する事でウインドウ下のステータスバーに現在のscope文字列が表示される。

メニューから[Preferences] - [Browse Packages]と選択すればパッケージのディレクトリが出てきます。 Pythonディレクトリを探して中を開いてみると、そこにPython.tmLanguageがあるはずです。

ところがSublime Text 3の場合にはパッケージディレクトリ配下にPythonディレクトリがみあたらない。
Sublime Text 3のインストールデレクトリの直下のPackagesデレクトリ(C:\Program Files\Sublime Text 3\Packages)にPython.sublime-packageというファイルが存在する。
このファイルはzipファイルであるらしく、これをコピーして拡張子zipを付加して解凍するとPython.tmLanguageファイルを得る事ができる。
このファイルを開いてscope_nameで検索するとscope_nameを発見できる。

ところで、この拡張子.tmLanguageというファイルは何のためのファイルなんだろう。

Sublimeの言語パッケージの構文定義(Syntax Definitions)ファイルというものらしい。
Python.tmLanguageであればpythonの言語定義が記述されているパッケージのディレクトリという事になるようだ。

編集中のテキストのシンタックスファイルはコンソールよりview.settings().get('syntax')を実行する事で取得できる。
以下はpythonやhtml,markdownに編集中のファイルを切替えて実行した結果である。

>>> view.settings().get('syntax')
'Packages/Python/Python.tmLanguage'
>>> view.settings().get('syntax')
'Packages/HTML/HTML.tmLanguage'
>>> view.settings().get('syntax')
'Packages/Markdown Extended/Syntaxes/Markdown Extended.tmLanguage'

拡張子.pyにSublimeのsyntaxモードが関連付けられており、それに更にScope Nameが関連付けられているとい事なのかな...

scope_nameを取得して、状況に応じて動作を変更するプラグインの例を見つけた。

マクロ

プラグインをわざわざ作成しなくとも、簡単な動作ならマクロで実現できる。

Sublimeのコマンドを単純にシーケンシャルに実行するだけならマクロで事足りる。

パッケージディレクトリに、拡張子.sublime-macroのファイルを作成して、実行させたいコマンドのリストを記述。 キーボードショートカットやメニューにrun_macro_fileコマンドを登録するだけ。

"command": "run_macro_file",
"args": {"file": "Packages/マクロファイルへのパス"}

プラグインサンプル

Webブラウザを開く

上記の記事を参考に、選択した文字列をgoogleで検索するプログラムの例を示す。

# 選択した文字列をgoogleで検索
class GSearchCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        for region in self.view.sel():
            if not region.empty():
                # Get the selected text
                select_text = self.view.substr(region)

                url = 'http://www.google.co.jp/search?q=%s' % (select_text)
                import webbrowser
                webbrowser.open(url)

プラグインより指定のファイルを実行

# explorerを起動
class explorerCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        import os
        os.system('explorer')

編集中のテキスト行をスキャンする。

class LineScanCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        # 最終行を取得
        line_max, _ = self.view.rowcol(self.view.size())

        # 行スキャンのループ
        for line_no in range(0, line_max):
            # 行頭のpointを取得
            line_point = self.view.text_point(line_no, 0)
            # 行のreginオブジェクトを取得
            line_region = self.view.line(line_point)
            # 行の文字列を取得
            line_text = self.view.substr(line_region)
            print("{0:>10}: {1}".format(line_no + 1, line_text))

特定のシンタックスモードの時に、「保存時に設定ファイルのコマンドを実行するプラグイン」

蛇足

Sublime TextのJSONのコメント

Sublime Textの設定ファイルはkeymapファイルのようにJSONで記述される事が多い。
そこでSublime TextのJSONのコメントについて一言。

通常のJSONにはコメント構文は無い。
しかし、Sublime TextのJSONには「JavaScriptのコメント構文が許されている」そうである。 (JSONにはコメント構文がない - Qiita)

その他の参考記事

ページのトップへ戻る