年に何回かSublime Text 3(以下ST3)用のプラグインを書く機会に遭遇するのですが、都度、0スタートになるので自分用にメモしておきます。
##前提など
- ネット上にはST2の情報が多いのですが、ST3で頑張ってみます。
- 開発はMacでします。プラグインはSTが動くならどこでも動きます。
- STではPythonでプラグイン書きます。
- ST2ではPython2.x系でしたが、ST3ではPython3.xです。
- ST3ではWindowの関連処理が少し変わりました。
##基本1:プラグインの基本構造?を理解する
###ひな形の生成から実行まで
####ひな形の生成
STメニューのtools->New Plugin...を選択します。
すると新規ウインドウが開、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という名前で保存します。
この階層にはSTのメニューのSublime Text->Preferences->Packages...でアクセスできます。
####実行
STメニューのView->Show Consoleを選択し、STのコンソール(STの一番下に1行表示される入力画面)を開きます。
そこに、
view.run_command('example')
と打ってエンターを押します。
すると、コードを書いていたファイルの頭(0文字目)に、"Hello World!"と表示されます。
ここまでをまとめてみます。
###まとめ
非常に簡単なサンプルですが、基本的な概念を包含しているので整理します。
- 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の方が引っかかる)と入力するとコマンドが見つかり、選択すると実行されます。
フォルダ名やファイル名に規則は無いのですが、フィルダ名を基軸にわかりやすい名前をつけましょう。
##基本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)
どこかテキストを選択してからコマンドを実行してみてください。
エンターを押すと、選択した文字が表示されます。
注意が必要なのは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')で実行します。
実行すると、コンソールの位置に、入力画面が表示されます。
何か文字列を入力してエンターを押します。
すると、入力した文字列が表示されます。
##応用1:CREATE TABLE SQL文を作ってみる
ここまでの知識で実務的なプラグインを作成してみます。
###動き
Excelで作成したテーブル定義図をペーストした文字列から、CREATE TABLE文を生成するサンプルです。
先に、動きを確認します。
####エクセルでテーブル仕様を書く
まあ、よくこういうの書きますよね。嫌いですけど。定義をコピーします。
####Sublime Textにペースト(そして、選択する)
ペーストしたエリアを選択範囲とします。ExcelのカラムはTab区切りでペーストされるようです。
####コマンドを呼び出す(create table)
コマンドパレットからcreate tableを呼び出します。
####テーブル名を指定する
入力画面でテーブル名を入力します。ここではmembersとしました。
####create table文が生成される
エンターを押すと、CREATE TABLE文が生成されます。後は、これをDBのコンソールに貼り付けて実行したり、SQL文ではなく、フレームワークのMigrate文とかを生成したりしてもいいでしょう。
まあ、大した機能ではないですが、カラム数が数十個になったような場合は便利かもしれません。
###実装
実装は下記の通りです。特に特筆すべきこともありません。
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ファイルにリストとして追記すればいいだけです。
とりあえず以上です。