SublimeTextのプラグイン作成方法SublimeTextのプラグイン作成方法

最近目の調子が悪いのでJINSでブルーライトカットレンズのメガネを買った森です.メガネを変えてから心なしか目の負担が減ったように感じます.今でもたまに右外眼筋が痙攣しますが…

去年からSublime Textというテキストエディタを使っているのですが,1つの大きな特徴としてPythonでプラグインを作成し簡単に機能を拡張することができるということが挙げられます.すでに多くの有用なプラグインが存在し簡単に導入できるのですが,今回は自分でプラグインを作成する方法について簡単にまとめたいと思います.
example-resultexample-result

前提

最初に前提として,Sublime Text ver. 2.xはプラグインの実行にPython2.6系のインタプリタを使います.従ってPython2.6系で実行できるように実装を行わなければなりません.また,Sublime Text本体とプラグインは同一プロセスで動くので,アレなコードを書いてしまってプラグインが落ちると最悪本体ごともっていかれます.

現在後継として開発が行われているSublime Text ver. 3.xでは使用するインタプリタがPython3.3系になり本体とプラグインも別プロセスになりますが,現在Beta版しか公開されていないので今回はSublime Text 2向けに話をします.とはいえ多少APIの仕様が変わった程度なので,処理自体がPython3.3でも動くコードであれば移行は簡単そうです.

参考

ぶっちゃけ上の2つを見れば作れます (本記事は忘備録的なものという事でお許し下さい…

Hello, World!

とりあえずメニューバーから Tools > New Plugin...を選択します.

するとプラグインのひな形ができます.まずはこれを実行してみましょう.ファイルを保存してCtrl + `View > Show Consoleでコンソールを開き,view.run_command('example')というコマンドを実行してみましょう.

# ちなみにファイルの保存場所がデフォルトではUserディレクトリになっていますが,1つ上の階層のPackagesに任意のディレクトリを作ってその下にファイルを置くといいと思います.

上の例のように編集しているファイルの先頭にHello, World!と挿入されたらきちんとSampleは動作しています.

それではコードについて少し解説.当然ですが,1行目のimport sublime, sublime_pluginはSublimeのAPI関数を使うためにimportします.コマンド名はクラス名のExampleCommandからCommandを取り除き,スネークケースにしたexampleになります.この例ではExampleCommandクラスはsublime_plugin.TextCommandクラスを継承していますが,Sublime Textはプラグイン用に下記の4つのベースクラスが用意されており,目的によって継承するクラスを変更します.

  • WindowCommand: self.windowでカレントウィンドウにアクセスできる
  • TextCommand: self.viewで編集中のファイルにアクセスできる
  • EventListener: ファイルの保存などイベントをフックして実行できる
  • ApplicationCommand: ウィンドウやファイルにアクセス出来ない

ちなみにApplicationCommandとEventListenerはフィールドに何も持ちません.ApplicationCommandはあまり使われているのを見かけませんし正直使いどころはイマイチ分かりません…

これらのクラスを継承しているとSublime Text側から実行可能なコマンドとして認識され,呼び出されるとrun()の処理が自動的に実行されます.各コマンドの実行方法は以下の通り.

  • WindowCommand: window.run_command(‘example’)
  • TextCommand: view.run_command(‘example’)
  • ApplicationCommand: sublime.run_command(‘example’)

それではWindowCommandを継承した例も作ってみましょう.

example-result

アウトプットパネルに”Hello, World!”と出力するサンプルです.WindowCommandクラスを継承してつくり,カレントウィンドウのアウトプットパネルをget_output_panel()で取得してinsert()で文字を出力します.

このようにファイルからテキストを取得したり,カレントウィンドウに結果を出力したりする事がベースクラスの関数を使うことで簡単に行えます.これらのAPIを使えばSulime Text本体とのデータのやり取りはできるので,あとはPythonで任意の処理をするコードを書けばプラグインが出来上がります.

コマンドパレットから実行できるようにする

Default.sublime-commandsというファイルを作り,以下のように記述します.

すると,コマンドパレット上からコマンドを実行することができるようになります.

example-command

ショートカットの設定

ショートカットを設定する場合は,Default (Linux).sublime-keymap,Default (OSX).sublime-keymap,Default (Windows).sublime-keymapというようなファイルを作成し,以下のように記述します.

メニューバーへの追加

Main.sublime-menuというファイルに以下のように記述します.

すると,Preferences->Package Settingsから設定を変えたりKeyMapを変更したりすることができるようになります.

example-main-menu

Tips

では次に,実際にプラグインを作ってみて自分がつまった事とその解決方法を忘備録的に書いておきます.

THREADING

上で述べたように,Sublime Text ver. 2.x ではエディタ本体とプラグインは同一プロセス上で実行されます.従って処理に時間がかかるコード(例えばHTTP Requestなど)を含むコマンドを呼び出すと,エディタ自体が止まってしまいます.幸いにもスレッドが普通に使えるので,スレッドで実行することで対処します.

targetに__call__()をもつクラスのインスタンスを指定してやれば,__call__()の処理がスレッドで実行されます.

ちなみにスレッドから普通にSublime TextのAPIを呼ぶと怒られるので,以下の例ようにsublime.set_timeout()を使ってメインスレッドで実行します.

TEXTCOMMANDとWINDOWCOMMANDの両方のAPIを使う

カレントファイルから文字列を取得して,何らかの処理を行った後結果をアウトプットパネルで表示したいなんてことありますよね.そんな時は以下のようにします.

このサンプルコードでは選択範囲の文字列を取得して,それをそのままアウトプットパネルに出力します.キモはself.view.window()ですね.self.view.window()を使うことでTextCommandでもwindowオブジェクトが取得出来ます.

日本語の取り扱い

日本語を含む文字列をSublime TextのAPIに渡すと以下のようなエラーがでます.

最初は渡す文字列をdecode(‘utf-8’)と明示的にデコードすれば回避できると思いましたが,どうやら受け取る関数側がAsciiとして受け取ってしまうようでダメでした.仕方ないので以下のようにしてデフォルトエンコーディングを指定してやることで無理やり回避…

もう少しスマートな方法があると良いのですが…

# Python 3.x系ではデフォルトの文字列型がUnicodeに変わったので,Sublime Text 3ではこういう問題は起こらないと思います.YATTA! 🙂

まとめ

今回はSublime Textのプラグインの作成方法について簡単に紹介しました.Pythonで簡単に書けるので,皆さんもエディタに不満があればサクッとプラグインを作ってみてはいかがでしょうか.ちなみに僕はGingerというサービスのAPIをSublime Textから叩いて,英文のGrammarCheckを行うプラグインを作ってみました.Githubに置いてあるのでよろしければどうぞ.

Sublime2Ginger – Github
Gingerを使って英文校正を行うSublime Textプラグインをつくった – 404 Engineer Logs

By Kenta MORI