【ST2】Sublime Text 2 のプラグインを作ってみた

ちょっと前に、恋に落ちるテキストエディタ "Sublime Text 2" に恋をしました。

恋ってほど恋してもいないんですが、もともと Windows で EmEditor を愛用してたのですが、Mac に移行したので使えなくなってしまい、そのかわりとして Sublime Text 2 を使うようになったのでした。日本語にもう少し寛容になってほしいとか、細かいところにちょいちょい不満はありますが、これも好意の裏返しということで、概ね満足しています。

ということで、お世話になることになった Sublime Text 2 で、プラグインを作ってみたので、手順をご紹介。

結構カンタンです。

そもそも "Sublime Text 2" については、参考URLをいくつか載せておくのでこちらを参照してください。

プラグインディレクトリ

プラグイン(パッケージ)は、次のパスに格納されているはずです。システムによって若干異なるので注意。

Mac OSX
"/Users/{$ユーザー名}/Library/Application Support/Sublime Text 2/Packages"
Windows 7
"C:\Users\{$ユーザー名}\AppData\Roaming\Sublime Text 2\Packages"

このディレクトリを開くと、予めたくさんのディレクトリが格納されているはずです。これらは初期状態ですでにインストールされているパッケージで、構文定義やスニペットなどもこの中に定義されています。

このパスを、以降 <Packages> と略して記載しています。

ここにディレクトリを作る

なんでもいいんだけど、とりあえずディレクトリを1つ作ります。

ここでは、 "st2PluginTest" という名前にします。

  • <Packages>/st2PluginTest/

プラグイン自体は Python で書かれている

作成したディレクトリ <Packages>/st2PluginTest/ に、Python ファイルを作成します。
ファイル名はなんでもいいです。なんでもいいんですが、とりあえずプラグイン名ということで "st2PluginTest.py" としてみます。拡張子は *.py です。

<Packages>/st2PluginTest/st2PluginTest.py

Hello World してみる

とりあえず、Hello World を出力するプラグインを実装してみましょう。開いているテキストファイルの先頭に、文字列 "Hello World" を出力するだけの簡単なプラグインを作ります。

# -*- coding: utf-8 -*-
import sublime, sublime_plugin

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

どうやら 1機能 1クラス の構造になっているようです。

ここではクラス名 st2PluginTestCommand として定義しています。プラグイン名は st2PluginTest の部分で、最後に固定接尾辞 Command をつけるルールみたいです。

記法は必ずキャメルケースです。次の手順でクラス名からコマンド名を生成しますが、この命名規則が重要な意味を持つことになるので、かならずキャメルケースで命名しましょう。

ちなみに、1行目の # -*- coding: utf-8 -*- は、このスクリプトがUTF-8で書かれていることを示す Python のお約束、だそうです。

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

作った機能を実行する方法はいくつか用意されています。コンソールからコマンドを叩いて実行する方法や、メニューバーにメニューとして追加剃る方法、ショートカットキーの組み合わせで呼び出す方法、コマンドパレットから実行する方法などがあります。

ここでは、一番簡単だと思われる、コマンドパレットから実行する方法で実行できるよう、コマンドを登録する方法を紹介します。

コマンドパレットは、パッケージ(=拡張機能)の機能を呼び出す機能で、メニューの [Tools -> Command Palette] から起動することができます。
コマンドパレットを起動すると、テキスト入力が表示されるので、呼び出したい機能の名称を入力して絞り込み、選択・実行するインターフェイスです。

このインターフェイスから呼び出せるように、Sublime Text 2 にパッケージ名を登録します。

まず、次のファイルを作成し、Sublime Text 2 で開きます。

  • <Packages>/st2PluginTest/Default.sublime-commands

内容は、次のようなJSONにします。

[
    {
        "caption": "st2PluginTest",
        "command": "st2_plugin_test"
    }
]

"caption" が、機能名です。ここで設定した名称が、コマンドパレット上で検索される対象になります。
"command" は、呼び出すコマンド名です。定義したクラス名は st2PluginTestCommand ですが、このままで呼び出すことはできません。 st2PluginTestCommand を元に、末尾の "Command" をトルツメ、キャメルケースをアンダースコア記法に置き換えたもの、すなわち st2_plugin_test が、コマンド名となります。

{ } のくくりで1つの機能を登録しています。これをカンマ区切りで複数登録することもできます。

登録したプラグインを実行してみる

ここまでできたら、コマンドパレットを起動して、"st2PluginTest" を検索してみてください。候補の中に見つかるはずです。

見つかったら、これを実行してみてください。テキストの先頭に "Hello World" が出力されたら成功です。

もし、見つからない場合は、*.py ファイルにシンタックスエラーがったり、Default.sublime-commands の記述にエラーがあったり、あるいはファイル名が間違っているなどの可能性が考えられます。1つ1つ、丁寧に確認してみてください。

もうちょっとだけ、いろいろやってみる

一旦、Hello World アプリはできたけど、もうちょっと便利に使える機能を実装してみます。

カーソル位置にHTMLソースを出力してみる

まずは実装例。

# -*- coding: utf-8 -*-
import sublime, sublime_plugin

class st2PluginTestCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		# 選択範囲を取得
		selection = self.view.sel()[0]

		# 挿入する文字列を生成
		str = 'Hello World'

		# 選択範囲を生成した文字列で置き換える
		self.view.replace(edit, selection, str)

これは、str に格納した文字列を、カーソル位置に挿入するスクリプトです。

カーソル位置として、選択範囲を取得しています。

# 選択範囲を取得
		selection = self.view.sel()[0]

挿入する文字列は、str の内容を変更すると変えられます。

# 挿入する文字列を生成
		str = 'Hello World'

最後に、選択範囲の文字列を、生成した文字列で置き換えています。(selection が選択範囲を格納しています。)

# 選択範囲を生成した文字列で置き換える
		self.view.replace(edit, selection, str)

選択範囲をHTML特殊文字変換してみる

次は、選択範囲の文字列に置換処理を施すサンプルです。

# -*- coding: utf-8 -*-
import sublime, sublime_plugin
from xml.sax.saxutils import *
import re

class st2PluginTestCommand(sublime_plugin.TextCommand):
	def run(self, edit):
		# 選択範囲を取得
		selection = self.view.sel()[0]

		# 選択範囲の文字列を取得
		selected_string = self.view.substr(selection)

		# 選択した文字列を置換
		selected_string = escape(selected_string,{'"':'&quot;'})
		selected_string = re.sub(r'(\r\n|\r|\n)', '<br />'+"\n", selected_string)

		# 選択範囲を置換した文字列で置き換える
		self.view.replace(edit, selection, selected_string)

先ほどの、固定文字列を挿入するサンプルと大枠は同じですが、(1)選択範囲の文字列を取得する (2)取得した文字列を置換する の2点が異なります。

次の、self.view.substr(selection) が、選択範囲 selection の文字列を取得しています。

# 選択範囲の文字列を取得
		selected_string = self.view.substr(selection)

次の部分が、置換の実装です。 escape() で、HTML特殊文字を変換しているのに加え、re.sub() で正規表現による置換処理を施しています。(改行コードを、<br /> に変換)

# 選択した文字列を置換
		selected_string = escape(selected_string,{'"':'&quot;'})
		selected_string = re.sub(r'(\r\n|\r|\n)', '<br />'+"\n", selected_string)

escape()re.sub() を呼び出すために、スクリプトの先頭に次の行が追加されていることに注意してください。
Python では、たくさんある機能をモジュール単位で管理していて、必要に応じて import で呼び込む、という仕様になっています。従って、import されていない機能を使おうとすると、エラーになってしまいます。

from xml.sax.saxutils import *
import re

パッケージ管理ツールへの登録方法

Sublime Text 2 には、パッケージ管理ツールが提供されている。ここには、たくさんのパッケージが世界中から集まってきており、検索もインストールも簡単にできるようになっている。

自分で作ったオリジナルのパッケージも、このパッケージ管理ツールに登録することができるらしい。わたしはやってみてないので詳しいことは言えないが、資料 Sublime Text 2 プラグイン開発 - ゼロからPackageControlへ登録まで によると、次のような手順で登録を行う、とのこと。

これで受理されれば、パッケージ管理ツールから検索とインストールができるようになる、みたい。

興味ある方は試してみて頂きたい。

ちなみに、パッケージ管理機能をインストールするには、[View -> Show Console] でコンソールを開き、次のコードをコピペし、実行する。(騙されたと思って実行する)

import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation'

まとめ

今回は、ごくごく簡単なSublime Text 2用プラグインの実装サンプルを取り上げました。サクっと作ってサクっと使えて、ここで触れた機能だけでも、かなり効率よくコードを書けるようになるんじゃないでしょうか。

もちろんこれ以外にも、できることはたくさんあります。Python 言語自体のビルトイン機能や、Sublime Text 2 の Plugin API ドキュメント を参考にしてみてください。

それから何より、(Sublime Text 2 に限らず、ですが)他のパッケージの実装を覗いてみるのが一番参考になります。既に世にはすごい数のパッケージが出回っているので、インストールしてみて、動かしてみて、ソースを見てみるとよいでしょう。

最後にちょっぴり宣伝。PxFW 1.x系 で定義されたドキュメントモジュールを書き出すパッケージを作成してみました。このリポジトリ含まれているので、ぜひご活用ください。


プロフィール

コヤナギ トモヤ

まったりウェブ系コーダーしてます。PHP製静的CMS Pickles 2 を開発しています。

RSSフィード

  • このサイトは、 コヤナギ トモヤ の個人サイトです。
  • 個人的な主張や、活動の記録などを掲載しています。 所属する企業、団体、その他の意見や立場を代表するものではありません。
  • 掲載された内容は古くなっている可能性があります。 特に古い記事では、現在の筆者の考えと異なる主張をしていることがありますが、記録としてそのまま残しております。 予めご了承ください。
ページの先頭へ戻る