Help us understand the problem. What is going on with this article?

Sublime Text 3でプラグインを作るメモ

More than 3 years have passed since last update.

年に何回かSublime Text 3(以下ST3)用のプラグインを書く機会に遭遇するのですが、都度、0スタートになるので自分用にメモしておきます。

前提など

  • ネット上にはST2の情報が多いのですが、ST3で頑張ってみます。
  • 開発はMacでします。プラグインはSTが動くならどこでも動きます。
  • STではPythonでプラグイン書きます。
  • ST2ではPython2.x系でしたが、ST3ではPython3.xです。
  • ST3ではWindowの関連処理が少し変わりました。

基本1:プラグインの基本構造?を理解する

ひな形の生成から実行まで

ひな形の生成

STメニューのtools->New Plugin...を選択します。

subl

すると新規ウインドウが開、Pluginのひな形が挿入された新規ファイルが生成されます。

import sublime, sublime_plugin

class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.insert(edit, 0, "Hello, World!")

保存をすると、標準でPackages->Userフォルダが開かれますが、同じ階層にtestとりうフォルダを作り、その中に、test.pyという名前で保存します。
subl

この階層にはSTのメニューのSublime Text->Preferences->Packages...でアクセスできます。

実行

STメニューのView->Show Consoleを選択し、STのコンソール(STの一番下に1行表示される入力画面)を開きます。
subl
そこに、

view.run_command('example')

と打ってエンターを押します。

subl

すると、コードを書いていたファイルの頭(0文字目)に、"Hello World!"と表示されます。

subl

ここまでをまとめてみます。

まとめ

非常に簡単なサンプルですが、基本的な概念を包含しているので整理します。

  • import sublime, sublime_pluginはplugin開発するときのおまじない。
  • ファイルはPackage以下ならどこにどの様に保存しても関係ない(Package以下ならロードされる)。
  • 実行コマンド名は、class ExampleCommand()のExample部分が小文字になったものになる。
  • ひとまず、コンソールでview.run_command('cmmand_name')とすると実行できる(コマンドパレットに統合します)。
    • その名の通り、runメソッドを呼び出している。
  • selfというのはSTアプリ自体を示す。まあ、他の言語でいうところのthis。
  • viewというのは、STの編集画面だと思うことにする。
  • editは、編集バッファだと思うことにする。
  • self.view.insert(edit, 0, "Hello, World!")というのは、
    • STの.編集画面に.挿入しろ!(編集バッファの,0文字目に,"Hello World!を")という意味。

コマンドをコマンドパレットから呼び出せるようにする

さすがに毎回、view.run_command()とするのはしんどいしカッコ悪い。STらしく、コマンドパレットから実行したいものです。やり方は簡単です。testフォルダの中に、test.sublime-commandsというファイルを作り、下記のように書きます。

[
    {"caption":"example plugin.","command":"example"}
]

すると、shift + command + p で、開くウインドウで、example(captionの方が引っかかる)と入力するとコマンドが見つかり、選択すると実行されます。

subl

フォルダ名やファイル名に規則は無いのですが、フィルダ名を基軸にわかりやすい名前をつけましょう。

基本2:選択範囲を操作する

STでよくやる操作は選択範囲に対して、何かをやる操作です。
そのためにはまず、選択範囲の理解、情報の取得等を理解しましょう。

選択範囲を取得する

選択範囲は、self.viwe.sel()で取得することができます。選択範囲の文字を取得して、ダイアログに出力してみます。

なお、関数名をキャメル方式で記述した場合、コマンドはスネークケースになります。

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

        #選択範囲(の開始および終了位置)を取得
        sel_area = self.view.sel()

        if sel_area[0].empty():
            #どこも選択されていない場合
            sublime.message_dialog("どこか選択して下さい。")
        else:
            #選択範囲を文字列として取得
            sel_string = self.view.substr(sel_area[0])
            #取得した文字列を出力
            sublime.message_dialog(sel_string)

どこかテキストを選択してからコマンドを実行してみてください。

subl

エンターを押すと、選択した文字が表示されます。

subl

注意が必要なのはself.view.sel()で取得できるのは、文字列そのものではなく、region(選択場所の開始位置と終了位置)の配列ということです。STは、複数選択が可能なので、複数箇所選択した場合は、複数のregionが返ります。

文字列自体を得るためには、self.view.substr(region)メソッドを実行する必要があります。

入力を受ける

プラグインを作っているとユーザーの入力を受けたい場合があります。ST2までは、入力と処理を同じクラスの中で処理できたのですがST3はクラスをわける必要があるよう?なので、そうします。

今までは、編集画面を操作するsublime_plugin.TextCommandを継承していましたが、Window関連を処理するためにsublime_plugin.WindowCommandを継承します(sublime_plugin.TextCommandでもいいようですが)。

#入力画面を表示するスレッド
class UserInputCommand(sublime_plugin.WindowCommand):
    def run(self):
        #入力パネルを表示
        self.window.show_input_panel("Input your name:","",self.on_done,None,None)

    #入力完了イベント
    def on_done(self,word):

        #未入力
        if word == "":
            sublime.message_dialog("文字を入力して下さい。")
        #入力あり
        else:
            #別コマンドに渡す
            self.window.run_command("output",{"word":word})

#入力値を処理するスレッド
class OutputCommand(sublime_plugin.TextCommand):
    #受け取る
    def run(self,edit,word):
        #表示
        sublime.message_dialog("受け取った値は" + word + "です。")

なお、WindowCommandを継承したコマンドは、window.run_command('command')で実行します。

subl

実行すると、コンソールの位置に、入力画面が表示されます。

subl

何か文字列を入力してエンターを押します。

subl

すると、入力した文字列が表示されます。

応用1:CREATE TABLE SQL文を作ってみる

ここまでの知識で実務的なプラグインを作成してみます。

動き

Excelで作成したテーブル定義図をペーストした文字列から、CREATE TABLE文を生成するサンプルです。
先に、動きを確認します。

エクセルでテーブル仕様を書く

まあ、よくこういうの書きますよね。嫌いですけど。定義をコピーします。
subl

Sublime Textにペースト(そして、選択する)

ペーストしたエリアを選択範囲とします。ExcelのカラムはTab区切りでペーストされるようです。
subl

コマンドを呼び出す(create table)

コマンドパレットからcreate tableを呼び出します。
subl

テーブル名を指定する

入力画面でテーブル名を入力します。ここではmembersとしました。
subl

create table文が生成される

エンターを押すと、CREATE TABLE文が生成されます。後は、これをDBのコンソールに貼り付けて実行したり、SQL文ではなく、フレームワークのMigrate文とかを生成したりしてもいいでしょう。

subl

まあ、大した機能ではないですが、カラム数が数十個になったような場合は便利かもしれません。

実装

実装は下記の通りです。特に特筆すべきこともありません。
STの知識といういより、後はPythonの文字列操作の能力を向上させると何でもできそうです。

#main class
#テーブル名を受ける
class SqlCreatetableCommand(sublime_plugin.WindowCommand):

    def run(self):

        #テーブル名を受ける
        self.window.show_input_panel("Input TABLE NAME: ","",self.on_done,None,None)

    #入力完了時メソッド
    def on_done(self,word):

        #空だったら
        if word == "":
            self.window.run_command("テーブル名を入力して下さい。")
        else:
            #SqlCreatetableSubに値を渡して実行
            self.window.run_command("sql_createtable_sub",{"word":word})


#sub class
#CREATE TABLE文を生成
class SqlCreatetableSubCommand(sublime_plugin.TextCommand):

    def run(self,edit,word):

        #選択範囲を取得
        select_area = self.view.sel()

        #選択範囲の数だけループ
        for region in select_area:

            #選択範囲がない場合
            if region.empty():

                sublime.message_dialog("変換エリアを選んで下さい。")

            #複数選択していた場合
            elif len(select_area) >= 2:

                sublime.message_dialog("1ブロクだけ選択して下さい。")

            else:

                #選択範囲を文字列で取得
                area = self.view.substr(region)

                #1行毎に分割
                rows = area.split('\n')

                #変換用の文字列生成
                _str = ""
                _str = "DROP TABLE IF EXISTS " + word + ";\n"
                _str = "CREATE TABLE " + word + "(\n"

                #業の数だけループ
                for row in rows:

                    _str += "\t" + row.replace("\t"," ").rstrip() + ",\n"

                else:

                    #おしりの,と改行を削除
                    _str = _str.rstrip(",\n")
                    #\n);を追加
                    _str += "\n);"

                #選択範囲と入替え
                self.view.replace(edit, region, _str)

        else:
            pass

ちょっとエラー処理とかおかしいですが、とりあえずよしとしましょう。
コマンドパレットからも呼び出せるようにします。

[
    {"caption":"example plugin.","command":"example"},
    {"caption":"create table","command":"sql_createtable"}
]

複数のコマンドを定義する場合は、*.sublime-commandsファイルにリストとして追記すればいいだけです。
とりあえず以上です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away