Qiita は、全体は小さいけど、多数のファイルで構成されたソースコードを記事に入れようと思うと
- 記事にするときも手間
- 利用するときも手間
なので、そういうものは記事にしにくい側面があります。そこで、(Qiita で完結する前提で)手間を軽減したいわけですが、ファイルから Markdown にするものと、その逆があれば少しは良くなるでしょう。既に誰かやっててもよさそうな気がしますが、探せなかったので作ることにしました。
変換処理は
-
ファイルから Markdown へ
- テキスト ファイルの前後に「```」の行を追加
- ファイルの種類に応じたコードの挿入
- 種類の判定が必要
- ファイル内行頭「```」
- 元のコードをうまく表示する法方を見つけられなかったので「\」を先頭に追加
- ファイルの種類に応じたコードの挿入
- テキスト ファイルの前後に「```」の行を追加
-
Markdown からファイルへ
- Markdown の「```」から「```」までをテキスト ファイルにする
- ファイル内行頭「```」を復元
- Markdown の「```」から「```」までをテキスト ファイルにする
の簡素なものにしています。
Python スクリプト: ファイルから Markdown へ
f2m.py
#!/usr/bin/env python
import os
import sys
import argparse
mode_map = {
'applescript': ('.scpt',),
'ada': ('.ada',),
#'armasm': ('.s',),
'awk': ('.awk',),
'bash': ('.bash',),
'bat': ('.bat',),
'bpf': ('.bpf',),
'cl': ('.cl',),
'css': ('.css',),
'csharp': ('.cs',),
'cuda': ('.cu',),
'c++': ('.c', '.cc', '.cpp', 'c++',
'.h', '.hpp', 'h++'), # '.hh'
'd': ('.d',),
'diff': ('.diff',),
'ecl': ('.ecl',),
'elisp': ('.el',),
'elm': ('.elm',),
'email': ('.eml',),
'eps': ('.eps',),
'erl': ('.erl',),
'erb': ('.erb',),
'fea': ('.fea',),
'ff': ('.ff',),
'fortran': ('.f', '.f90', '.f95', '.f03', '.f15'),
'fsharp': ('.fs',),
'gd': ('.gd',),
'glsl': ('.glsl', '.glslv', '.frag', '.vert'),
'go': ('.go',),
'groovy': ('.groovy', '.gvy', '.gy', '.gsh'),
'haml': ('.haml',),
'hack': ('.hack',), # '.hh'
'hcl': ('.hcl',),
'hlsl': ('.hlsl',),
'hql': ('.hql',),
'hs': ('.hs',),
'html': ('.html',),
'hx': ('.hx',),
'hy': ('.hy',),
'idlang': ('.idl',),
'java': ('.java',),
'js': ('.js',),
'json': ('.json',),
'ksh': ('.ksh',),
'latex': ('.latex',),
'lhs': ('.lhs',),
'llvm': ('.ll',),
'lua': ('.lua',),
#'m68k': ('.s',),
'make': ('.mak', '.mk'),
#'mathematica': ('.m',),
'matlab': ('.mat',),
'md': ('.md',),
'mf': ('.mf',),
'minizinc': ('.mzn',),
'mkd': ('.mkd',),
#'ml': ('.ml',),
'moon': ('.moon',),
#'nasm': ('.s',),
'nim': ('.nim',),
#'objc': ('.m',), # '.h'
'objcpp': ('.mm',), # '.hh'
'ocaml': ('.mli',), # '.ml'
'pascal': ('.p', 'pas', '.pp'),
'patch': ('.patch',),
'php': ('.php',),
'pl': ('.pl',),
'plist': ('.plist',),
'ps': ('.ps',),
'py': ('.py',),
'pyx': ('.pyx',),
'rb': ('.rb',),
'rs': ('.rs',),
'saas': ('.saas',),
'sas': ('.sas',),
'scala': ('.scala', '.sc'),
'scss': ('.scss',),
'sed': ('.sed',),
'sh': ('.sh',),
'sql': ('.sql',),
'st': ('.st',),
'swift': ('.swift',),
'tcl': ('.tcl',),
'tex': ('.tex',),
'toml': ('.toml',),
'ts': ('.ts',),
'tsx': ('.tsx',),
'vb': ('.vb',),
'xml': ('.xml',),
'yaml': ('.yaml', '.yml',),
'yang': ('.yang',),
}
suffix_map = {}
for k in mode_map:
for x in mode_map[k]:
if x in suffix_map:
raise Exception(x, suffix_map[x], k)
suffix_map[x] = k
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output', default=None)
parser.add_argument('files', metavar='FILE', type=str, nargs='+')
args = parser.parse_args()
md = ''
for fn in args.files:
fn = fn.replace('&', '&')
fn = fn.replace(' ', ' ')
ct = 'text'
sfx = os.path.splitext(fn)[1]
fnl = fn.lower()
if fnl == 'makefile':
ct = 'makefile'
elif fnl == 'gnumakefile':
ct = 'gnumake'
elif sfx in suffix_map:
ct = suffix_map[sfx.lower()]
md = md + ('\n```%s:%s\n' % (ct, fn))
for line in open(fn).readlines():
if len(line) > 3 and line[:3] == '```':
line = '\\' + line
md = md + line
md = md + '```\n'
o = args.output
if o is None or o == '-':
o = sys.stdout
else:
o = open(o, 'w')
o.write(md)
Python スクリプト: Markdown からファイルへ
m2f.py
#!/usr/bin/env python
import os
import sys
import argparse
import urllib.request
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--console', action='store_true', default=False)
parser.add_argument('-m', '--math', action='store_true', default=False)
parser.add_argument('-o', '--outputdir', metavar='DIR', default=None)
parser.add_argument('markdown', metavar='URL/FILE', type=str, default=None)
args = parser.parse_args()
lines = []
if args.markdown is None:
lines = sys.stdin.readlines()
elif args.markdown == '-':
lines = sys.stdin.readlnes()
elif args.markdown.split(':')[0] in ('http', 'https'):
with urllib.request.urlopen(args.markdown) as res:
lines = []
for line in res.read().decode().split('\n'):
lines.append(line + '\n')
else:
lines = open(args.markdown).readlines()
odir = args.outputdir
if odir is None:
odir = '.'
if odir[-1] != '/':
odir += '/'
exclude_modes = []
if not args.console:
exclude_modes.append('console')
if not args.math:
exclude_modes.append('math')
index = 0
output = None
for line in lines:
if len(line) > 3 and line[:3] == '```':
if output is None:
code = line[3:].split(':', 1)
mode = code[0].strip().lower()
if len(mode) == 0:
continue
if mode in exclude_modes:
continue
fname = ''
if len(code) > 1:
fname = code[1].strip()
if len(fname) == 0:
fname = 'code:%d.%s' % (index, mode)
index += 1
fname = fname.replace(' ', ' ')
fname = fname.replace('&', '&')
path = odir + fname
os.makedirs(os.path.dirname(path), exist_ok=True)
print(path)
output = open(path, 'w')
continue
output.close()
output = None
continue
if output is None:
continue
if len(line) > 4 and line[:4] == '\\```':
line = line[1:]
output.write(line)
こちらの記事の取得例
$ python3 m2f.py -o sample https://qiita.com/ikiuo/items/2dc82e76337010f935a1.md
sample/main.swift
sample/zlibwrapper.m
sample/project-Bridging-Header.h