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


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

##前提など

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

##基本１：プラグインの基本構造？を理解する

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

####ひな形の生成

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

![subl](http://www.bluecode.jp/qiita/subl001.png)

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

```py
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](http://www.bluecode.jp/qiita/subl002.png)

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

####実行

STメニューのView->Show Consoleを選択し、STのコンソール（STの一番下に1行表示される入力画面）を開きます。
![subl](http://www.bluecode.jp/qiita/subl003.png)
そこに、

```py
view.run_command('example')
```

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

![subl](http://www.bluecode.jp/qiita/subl004.png)

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

![subl](http://www.bluecode.jp/qiita/subl005.png)

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

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

* 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というファイルを作り、下記のように書きます。

```py
[
	{"caption":"example plugin.","command":"example"}
]
```
すると、shift + command + p で、開くウインドウで、example（captionの方が引っかかる）と入力するとコマンドが見つかり、選択すると実行されます。

![subl](http://www.bluecode.jp/qiita/subl006.png)

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

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

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

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

```py
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](http://www.bluecode.jp/qiita/subl010.png)

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

![subl](http://www.bluecode.jp/qiita/subl011.png)

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

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

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

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

```py
#入力画面を表示するスレッド
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](http://www.bluecode.jp/qiita/subl007.png)

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

![subl](http://www.bluecode.jp/qiita/subl008.png)

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

![subl](http://www.bluecode.jp/qiita/subl009.png)

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

##応用１：CREATE TABLE SQL文を作ってみる
ここまでの知識で実務的なプラグインを作成してみます。

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

####エクセルでテーブル仕様を書く
まあ、よくこういうの書きますよね。嫌いですけど。定義をコピーします。
![subl](http://www.bluecode.jp/qiita/subl012.png)

####Sublime Textにペースト(そして、選択する）
ペーストしたエリアを選択範囲とします。ExcelのカラムはTab区切りでペーストされるようです。
![subl](http://www.bluecode.jp/qiita/subl014.png)

####コマンドを呼び出す(create table)
コマンドパレットからcreate tableを呼び出します。
![subl](http://www.bluecode.jp/qiita/subl015.png)

####テーブル名を指定する
入力画面でテーブル名を入力します。ここではmembersとしました。
![subl](http://www.bluecode.jp/qiita/subl016.png)

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

![subl](http://www.bluecode.jp/qiita/subl017.png)

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

###実装

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

```py
#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("１ブロクだけ選択して下さい。")

			else:

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

				#１行毎に分割
				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
```
ちょっとエラー処理とかおかしいですが、とりあえずよしとしましょう。
コマンドパレットからも呼び出せるようにします。

```py
[
	{"caption":"example plugin.","command":"example"},
	{"caption":"create table","command":"sql_createtable"}
]
```
複数のコマンドを定義する場合は、*.sublime-commandsファイルにリストとして追記すればいいだけです。
とりあえず以上です。