qutebrowser/scripts/asciidoc2html.py

303 lines
10 KiB
Python
Raw Normal View History

2014-09-22 20:21:00 +02:00
#!/usr/bin/env python3
2014-09-16 20:08:08 +02:00
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2017-05-09 21:37:03 +02:00
# Copyright 2014-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
2014-09-16 20:08:08 +02:00
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Generate the html documentation based on the asciidoc files."""
2015-10-21 07:31:47 +02:00
import re
2014-09-16 20:08:08 +02:00
import os
2014-09-22 20:41:12 +02:00
import os.path
import sys
2014-09-16 20:08:08 +02:00
import subprocess
import glob
import shutil
import tempfile
2014-12-05 23:29:37 +01:00
import argparse
2015-10-29 16:30:25 +01:00
import io
2014-09-16 20:08:08 +02:00
2014-09-22 20:41:12 +02:00
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
from scripts import utils
2014-09-16 20:08:08 +02:00
2015-10-20 06:54:43 +02:00
class AsciiDoc:
2015-10-20 06:54:43 +02:00
"""Abstraction of an asciidoc subprocess."""
2017-09-17 21:23:36 +02:00
FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
2015-10-20 06:54:43 +02:00
def __init__(self, args):
self._cmd = None
self._args = args
self._homedir = None
self._themedir = None
self._tempdir = None
self._failed = False
2015-10-20 06:54:43 +02:00
def prepare(self):
"""Get the asciidoc command and create the homedir to use."""
self._cmd = self._get_asciidoc_cmd()
self._homedir = tempfile.mkdtemp()
self._themedir = os.path.join(
self._homedir, '.asciidoc', 'themes', 'qute')
self._tempdir = os.path.join(self._homedir, 'tmp')
os.makedirs(self._tempdir)
os.makedirs(self._themedir)
def cleanup(self):
"""Clean up the temporary home directory for asciidoc."""
if self._homedir is not None and not self._failed:
shutil.rmtree(self._homedir)
2015-10-20 06:54:43 +02:00
def build(self):
if self._args.website:
self._build_website()
else:
self._build_docs()
self._copy_images()
2014-09-16 20:08:08 +02:00
2015-10-20 06:54:43 +02:00
def _build_docs(self):
2015-11-17 20:10:53 +01:00
"""Render .asciidoc files to .html sites."""
2017-09-17 21:23:36 +02:00
files = [('doc/{}.asciidoc'.format(f),
2017-09-17 22:10:06 +02:00
'qutebrowser/html/doc/{}.html'.format(f))
2017-09-17 21:23:36 +02:00
for f in self.FILES]
2015-10-20 06:54:43 +02:00
for src in glob.glob('doc/help/*.asciidoc'):
name, _ext = os.path.splitext(os.path.basename(src))
dst = 'qutebrowser/html/doc/{}.html'.format(name)
files.append((src, dst))
# patch image links to use local copy
replacements = [
("http://qutebrowser.org/img/cheatsheet-big.png",
"qute://help/img/cheatsheet-big.png"),
("http://qutebrowser.org/img/cheatsheet-small.png",
"qute://help/img/cheatsheet-small.png")
]
2017-10-26 22:42:55 +02:00
asciidoc_args = ['-a', 'source-highlighter=pygments']
2015-10-20 06:54:43 +02:00
for src, dst in files:
src_basename = os.path.basename(src)
modified_src = os.path.join(self._tempdir, src_basename)
with open(modified_src, 'w', encoding='utf-8') as modified_f, \
open(src, 'r', encoding='utf-8') as f:
for line in f:
for orig, repl in replacements:
line = line.replace(orig, repl)
modified_f.write(line)
2017-10-26 22:42:55 +02:00
self.call(modified_src, dst, *asciidoc_args)
def _copy_images(self):
"""Copy image files to qutebrowser/html/doc."""
print("Copying files...")
dst_path = os.path.join('qutebrowser', 'html', 'doc', 'img')
try:
os.mkdir(dst_path)
except FileExistsError:
pass
for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
src = os.path.join('doc', 'img', filename)
dst = os.path.join(dst_path, filename)
shutil.copy(src, dst)
2015-10-20 06:54:43 +02:00
2015-11-17 20:10:53 +01:00
def _build_website_file(self, root, filename):
"""Build a single website file."""
src = os.path.join(root, filename)
src_basename = os.path.basename(src)
parts = [self._args.website[0]]
dirname = os.path.dirname(src)
if dirname:
parts.append(os.path.relpath(os.path.dirname(src)))
parts.append(
os.extsep.join((os.path.splitext(src_basename)[0],
'html')))
dst = os.path.join(*parts)
os.makedirs(os.path.dirname(dst), exist_ok=True)
modified_src = os.path.join(self._tempdir, src_basename)
shutil.copy('www/header.asciidoc', modified_src)
outfp = io.StringIO()
with open(modified_src, 'r', encoding='utf-8') as header_file:
header = header_file.read()
header += "\n\n"
with open(src, 'r', encoding='utf-8') as infp:
outfp.write("\n\n")
hidden = False
found_title = False
title = ""
last_line = ""
for line in infp:
if line.strip() == '// QUTE_WEB_HIDE':
assert not hidden
hidden = True
elif line.strip() == '// QUTE_WEB_HIDE_END':
assert hidden
hidden = False
elif line == "The Compiler <mail@qutebrowser.org>\n":
continue
elif re.match(r'^:\w+:.*', line):
# asciidoc field
continue
2015-11-17 20:10:53 +01:00
if not found_title:
if re.match(r'^=+$', line):
line = line.replace('=', '-')
found_title = True
2015-11-20 21:39:17 +01:00
title = last_line.rstrip('\n') + " | qutebrowser\n"
title += "=" * (len(title) - 1)
2015-11-17 20:10:53 +01:00
elif re.match(r'^= .+', line):
line = '==' + line[1:]
found_title = True
2015-11-20 21:39:17 +01:00
title = last_line.rstrip('\n') + " | qutebrowser\n"
title += "=" * (len(title) - 1)
2015-11-17 20:10:53 +01:00
if not hidden:
outfp.write(line.replace(".asciidoc[", ".html["))
last_line = line
current_lines = outfp.getvalue()
outfp.close()
with open(modified_src, 'w+', encoding='utf-8') as final_version:
final_version.write(title + "\n\n" + header + current_lines)
2017-10-26 22:42:55 +02:00
asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual',
'-a', 'source-highlighter=pygments']
2017-05-29 23:15:22 +02:00
self.call(modified_src, dst, *asciidoc_args)
2015-11-17 20:10:53 +01:00
2015-10-20 06:54:43 +02:00
def _build_website(self):
2015-10-30 14:52:09 +01:00
"""Prepare and build the website."""
theme_file = os.path.abspath(os.path.join('www', 'qute.css'))
shutil.copy(theme_file, self._themedir)
outdir = self._args.website[0]
2015-10-20 06:54:43 +02:00
for root, _dirs, files in os.walk(os.getcwd()):
for filename in files:
2015-11-18 07:43:10 +01:00
basename, ext = os.path.splitext(filename)
2015-11-18 06:54:59 +01:00
if (ext != '.asciidoc' or
basename in ['header', 'OpenSans-License']):
2015-10-20 06:54:43 +02:00
continue
2015-11-17 20:10:53 +01:00
self._build_website_file(root, filename)
2015-10-30 14:52:09 +01:00
copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}
2015-10-29 16:52:38 +01:00
for src, dest in copy.items():
full_dest = os.path.join(outdir, dest)
try:
shutil.rmtree(full_dest)
except FileNotFoundError:
pass
shutil.copytree(src, full_dest)
2015-10-20 06:54:43 +02:00
for dst, link_name in [
('README.html', 'index.html'),
(os.path.join('doc', 'quickstart.html'), 'quickstart.html'),
]:
try:
os.symlink(dst, os.path.join(outdir, link_name))
except FileExistsError:
pass
2015-10-21 07:31:47 +02:00
2015-10-20 06:54:43 +02:00
def _get_asciidoc_cmd(self):
"""Try to find out what commandline to use to invoke asciidoc."""
if self._args.asciidoc is not None:
return self._args.asciidoc
try:
subprocess.call(['asciidoc'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
pass
else:
return ['asciidoc']
try:
subprocess.call(['asciidoc.py'], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except OSError:
pass
else:
return ['asciidoc.py']
raise FileNotFoundError
def call(self, src, dst, *args):
2015-10-20 06:54:43 +02:00
"""Call asciidoc for the given files.
Args:
src: The source .asciidoc file.
dst: The destination .html file, or None to auto-guess.
*args: Additional arguments passed to asciidoc.
2015-10-20 06:54:43 +02:00
"""
print("Calling asciidoc for {}...".format(os.path.basename(src)))
cmdline = self._cmd[:]
2015-10-20 06:54:43 +02:00
if dst is not None:
cmdline += ['--out-file', dst]
cmdline += args
cmdline.append(src)
2015-10-20 06:54:43 +02:00
try:
env = os.environ.copy()
env['HOME'] = self._homedir
subprocess.check_call(cmdline, env=env)
2015-10-20 06:54:43 +02:00
except (subprocess.CalledProcessError, OSError) as e:
self._failed = True
2015-10-20 06:54:43 +02:00
utils.print_col(str(e), 'red')
print("Keeping modified sources in {}.".format(self._homedir))
2015-10-20 06:54:43 +02:00
sys.exit(1)
2014-09-16 20:08:08 +02:00
def main(colors=False):
2014-09-22 20:11:28 +02:00
"""Generate html files for the online documentation."""
utils.change_cwd()
utils.use_color = colors
2014-12-05 23:29:37 +01:00
parser = argparse.ArgumentParser()
2015-10-20 06:54:43 +02:00
parser.add_argument('--website', help="Build website into a given "
2014-12-05 23:29:37 +01:00
"directory.", nargs=1)
parser.add_argument('--asciidoc', help="Full path to python and "
"asciidoc.py. If not given, it's searched in PATH.",
nargs=2, required=False,
metavar=('PYTHON', 'ASCIIDOC'))
2014-12-05 23:29:37 +01:00
args = parser.parse_args()
2014-09-16 20:08:08 +02:00
try:
os.mkdir('qutebrowser/html/doc')
except FileExistsError:
pass
2015-10-20 06:54:43 +02:00
asciidoc = AsciiDoc(args)
try:
2015-10-20 06:54:43 +02:00
asciidoc.prepare()
except FileNotFoundError:
utils.print_col("Could not find asciidoc! Please install it, or use "
"the --asciidoc argument to point this script to the "
"correct python/asciidoc.py location!", 'red')
sys.exit(1)
try:
asciidoc.build()
finally:
asciidoc.cleanup()
2014-09-16 20:08:08 +02:00
if __name__ == '__main__':
main(colors=True)