PythonでSublimeTextのスニペットをAtom仕様に変換してみた

# やること
SublimeTextで使用していたPythonの自作スニペットをAtom仕様に変換する

# 動機
最近Atomを使い始めたのだが、SublimeTextのスニペットを１つずつコピペするのが面倒なので自動化を試みた

# SublimeTextとAtomのスニペットの違い

##SublimeText
- １ファイルに１スニペット
- ```![CDATA[]]```内の文字列は、raw文字列として扱われる
 
```sublime_sample1.sublime-snippet
<snippet>
	<content><![CDATA[
print('hello world')
]]></content>
	<description>hello world</description>
	<tabTrigger>hello</tabTrigger>
	<scope>source.python</scope>
</snippet>
```
```sublime_sample2.sublime-snippet
<snippet>
	<content><![CDATA[
a = 'foo'
b = a.replace(${1:'foo'}, $2)
c = '\n'
]]></content>
	<description>sample</description>
	<tabTrigger>test</tabTrigger>
	<scope>source.python</scope>
</snippet>
```


##Atom
- １ファイル（```snippets.cson```）に全スニペットをまとめる
- ```body```はraw文字列として扱われない（例えば```\n```は改行として扱われる）
- ```body```が複数行に渡る場合には、```'''```（あるいは```"""```）で閉じる必要がある
- 特殊文字（例えば```'\n'```）を、```'\\\\n'```のように表記しなければならない（[参考](https://qiita.com/KoiShin/items/5a34083b9c389ba411b0)）

```snippets.cson
'.source.python':
	'hello world':
		'prefix': 'hello' 
		'body': "print('hello world')"

	'sample':
		'prefix': 'test'
		'body': '''
			a = 'foo'
			b = a.replace(${1:'foo'}, $2)
			c = '\\\\n'
		'''
```

#コード
1. ```glob```でSublimeTextのスニペットファイルを取得
2. 正規表現を使用し、必要な情報を抽出
3. ```content```以外（```tabTrigger```と```description```）はそのまま使用
4. ```content```は特殊文字を置換した後、複数行に渡るか、```'```や```"```を含むかチェックしAtom仕様に加工
5. 各情報をAtomのスニペットフォーマットに代入し、```snippets.cson```に書き込み


```sblm2atom.py
import os
import glob
import re


def convert_body(body):

	# 特殊文字の置換(この他にも色々ありますが、、、)
	sc1 = [r'\n', r'\t', r'\'', r'\"']
	sc2 = [r'\\\\n', r'\\\\t', r"\\\\'", r'\\\\"']
	for x, y in zip(sc1, sc2):
		body = body.replace(x, y)

	# bodyが複数行の場合
	if len(body.split('\n')) > 1:

		# 各行にTabを追加
		body = '\n'.join(['\t\t\t' + x for x in body.split('\n')])
		body = "'''\n{}\n\t\t'''".format(body)

	# bodyが1行の場合
	else:
		if '\'' in body and '\"' in body:
			body = "'''\n{}\n\t\t'''".format('\t\t\t' + body)
		elif '\'' in body:
			body = '\"{}\"'.format(body)
		else:
			body = '\'{}\''.format(body)

	return body


def main():
	sblm_snippets = glob.glob('*.sublime-snippet')

	fname_out = 'python_snippets.cson'
	with open(fname_out, 'w', encoding='utf-8') as f:
		f.write("'.source.python':" + '\n')

	# 情報を抽出するために必要な正規表現
	body_pattern = r'<!\[CDATA\[(.*)\]\]></content>'
	prefix_pattern = r'<tabTrigger>(.*)</tabTrigger>'
	desc_pattern = r'<description>(.*)</description>'

	atom_snippet_fmt = '''
	'{}':
		'prefix': '{}'
		'body': {}
	'''

	for sblm_snippet in sblm_snippets:

		# snippetの読み込み
		with open(sblm_snippet, 'r', encoding='utf-8') as f:
			src = f.read()

		# 正規表現で必要な情報を抽出
		body = re.findall(body_pattern, src, flags=re.DOTALL)[0].strip()
		body = convert_body(body)
		prefix = re.findall(prefix_pattern, src, flags=re.DOTALL)[0]
		desc = re.findall(desc_pattern, src, flags=re.DOTALL)
		desc = desc[0] if desc else 'no description'

		print(desc)
		print(prefix)
		print(body)
		print()

		# Atom snippet formatに抽出した情報を挿入
		atom_snippet = atom_snippet_fmt.format(desc, prefix, body).strip('\n')

		# ファイルに書き込み
		with open(fname_out, 'a', encoding='utf-8') as f:
			f.write(atom_snippet + '\n')


if __name__ == '__main__':
	main()

```


#テスト
試しに上述の```sublime_sample1.sublime-snippet```と```sublime_sample2.sublime-snippet```
を変換してみた⇩


```snippets.cson
'.source.python':
	'hello world':
		'prefix': 'hello'
		'body': "print('hello world')"
		
	'sample':
		'prefix': 'test'
		'body': '''
			a = 'foo'
			b = a.replace(${1:'foo'}, $2)
			c = '\\\\n'
		'''
```
正しく変換されていることが確認できた