We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Running asciidoc2html.py from outside folder
**Version info**:
qutebrowser v1.13.0                            
Git commit: 6e9f24bc8-dirty on master (2020-07-01 15:34:57 +0200)
Backend: QtWebEngine (Chromium 80.0.3987.163)
Qt: 5.15.0  

**Does the bug happen if you start with `--temp-basedir`?**:

**Description**
If one runs `scripts/asciidoc2html.py` (or `script/mkvenv.py`) outside of the folder, this fails since the path are not relative to the root of the repo.

**How to reproduce**
Run `scripts/mkvenv.py` from outside the repo root.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/asciidoc2html.py`
Content:
```
1 #!/usr/bin/env python3
2 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
3 
4 # Copyright 2014-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
5 
6 # This file is part of qutebrowser.
7 #
8 # qutebrowser is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # qutebrowser is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
20 
21 """Generate the html documentation based on the asciidoc files."""
22 
23 from typing import List, Optional
24 import re
25 import os
26 import sys
27 import subprocess
28 import shutil
29 import tempfile
30 import argparse
31 import io
32 import pathlib
33 
34 sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
35 
36 from scripts import utils
37 
38 DOC_DIR = pathlib.Path("qutebrowser/html/doc")
39 
40 
41 class AsciiDoc:
42 
43     """Abstraction of an asciidoc subprocess."""
44 
45     FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
46 
47     def __init__(self,
48                  asciidoc: Optional[List[str]],
49                  website: Optional[str]) -> None:
50         self._cmd = None        # type: Optional[List[str]]
51         self._asciidoc = asciidoc
52         self._website = website
53         self._homedir = None    # type: Optional[pathlib.Path]
54         self._themedir = None   # type: Optional[pathlib.Path]
55         self._tempdir = None    # type: Optional[pathlib.Path]
56         self._failed = False
57 
58     def prepare(self) -> None:
59         """Get the asciidoc command and create the homedir to use."""
60         self._cmd = self._get_asciidoc_cmd()
61         self._homedir = pathlib.Path(tempfile.mkdtemp())
62         self._themedir = self._homedir / '.asciidoc' / 'themes' / 'qute'
63         self._tempdir = self._homedir / 'tmp'
64         self._tempdir.mkdir(parents=True)
65         self._themedir.mkdir(parents=True)
66 
67     def cleanup(self) -> None:
68         """Clean up the temporary home directory for asciidoc."""
69         if self._homedir is not None and not self._failed:
70             shutil.rmtree(str(self._homedir))
71 
72     def build(self) -> None:
73         """Build either the website or the docs."""
74         if self._website:
75             self._build_website()
76         else:
77             self._build_docs()
78             self._copy_images()
79 
80     def _build_docs(self) -> None:
81         """Render .asciidoc files to .html sites."""
82         files = [(pathlib.Path('doc/{}.asciidoc'.format(f)),
83                   DOC_DIR / (f + ".html")) for f in self.FILES]
84         for src in pathlib.Path('doc/help/').glob('*.asciidoc'):
85             dst = DOC_DIR / (src.stem + ".html")
86             files.append((src, dst))
87 
88         # patch image links to use local copy
89         replacements = [
90             ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png",
91              "qute://help/img/cheatsheet-big.png"),
92             ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png",
93              "qute://help/img/cheatsheet-small.png")
94         ]
95         asciidoc_args = ['-a', 'source-highlighter=pygments']
96 
97         for src, dst in files:
98             assert self._tempdir is not None    # for mypy
99             modified_src = self._tempdir / src.name
100             with modified_src.open('w', encoding='utf-8') as moded_f, \
101                     src.open('r', encoding='utf-8') as f:
102                 for line in f:
103                     for orig, repl in replacements:
104                         line = line.replace(orig, repl)
105                     moded_f.write(line)
106             self.call(modified_src, dst, *asciidoc_args)
107 
108     def _copy_images(self) -> None:
109         """Copy image files to qutebrowser/html/doc."""
110         print("Copying files...")
111         dst_path = DOC_DIR / 'img'
112         dst_path.mkdir(exist_ok=True)
113         for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
114             src = pathlib.Path('doc') / 'img' / filename
115             dst = dst_path / filename
116             shutil.copy(str(src), str(dst))
117 
118     def _build_website_file(self, root: pathlib.Path, filename: str) -> None:
119         """Build a single website file."""
120         src = root / filename
121         assert self._website is not None    # for mypy
122         dst = pathlib.Path(self._website)
123         dst = dst / src.parent.relative_to('.') / (src.stem + ".html")
124         dst.parent.mkdir(exist_ok=True)
125 
126         assert self._tempdir is not None    # for mypy
127         modified_src = self._tempdir / src.name
128         shutil.copy('www/header.asciidoc', modified_src)
129 
130         outfp = io.StringIO()
131 
132         header = modified_src.read_text(encoding='utf-8')
133         header += "\n\n"
134 
135         with src.open('r', encoding='utf-8') as infp:
136             outfp.write("\n\n")
137             hidden = False
138             found_title = False
139             title = ""
140             last_line = ""
141 
142             for line in infp:
143                 line = line.rstrip()
144                 if line == '// QUTE_WEB_HIDE':
145                     assert not hidden
146                     hidden = True
147                 elif line == '// QUTE_WEB_HIDE_END':
148                     assert hidden
149                     hidden = False
150                 elif line == "The Compiler <mail@qutebrowser.org>":
151                     continue
152                 elif re.fullmatch(r':\w+:.*', line):
153                     # asciidoc field
154                     continue
155 
156                 if not found_title:
157                     if re.fullmatch(r'=+', line):
158                         line = line.replace('=', '-')
159                         found_title = True
160                         title = last_line + " | qutebrowser\n"
161                         title += "=" * (len(title) - 1)
162                     elif re.fullmatch(r'= .+', line):
163                         line = '==' + line[1:]
164                         found_title = True
165                         title = last_line + " | qutebrowser\n"
166                         title += "=" * (len(title) - 1)
167 
168                 if not hidden:
169                     outfp.write(line.replace(".asciidoc[", ".html[") + '\n')
170                     last_line = line
171 
172         current_lines = outfp.getvalue()
173         outfp.close()
174 
175         modified_str = title + "\n\n" + header + current_lines
176         modified_src.write_text(modified_str, encoding='utf-8')
177 
178         asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual',
179                          '-a', 'source-highlighter=pygments']
180         self.call(modified_src, dst, *asciidoc_args)
181 
182     def _build_website(self) -> None:
183         """Prepare and build the website."""
184         theme_file = (pathlib.Path('www') / 'qute.css').resolve()
185         assert self._themedir is not None   # for mypy
186         shutil.copy(theme_file, self._themedir)
187 
188         assert self._website is not None    # for mypy
189         outdir = pathlib.Path(self._website)
190 
191         for item_path in pathlib.Path().rglob('*.asciidoc'):
192             if item_path.stem in ['header', 'OpenSans-License']:
193                 continue
194             self._build_website_file(item_path.parent, item_path.name)
195 
196         copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}
197 
198         for src, dest in copy.items():
199             full_dest = outdir / dest
200             try:
201                 shutil.rmtree(full_dest)
202             except FileNotFoundError:
203                 pass
204             shutil.copytree(src, full_dest)
205 
206         for dst, link_name in [
207                 ('README.html', 'index.html'),
208                 ((pathlib.Path('doc') / 'quickstart.html'),
209                  'quickstart.html')]:
210             assert isinstance(dst, (str, pathlib.Path))    # for mypy
211             try:
212                 (outdir / link_name).symlink_to(dst)
213             except FileExistsError:
214                 pass
215 
216     def _get_asciidoc_cmd(self) -> List[str]:
217         """Try to find out what commandline to use to invoke asciidoc."""
218         if self._asciidoc is not None:
219             return self._asciidoc
220 
221         try:
222             subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,
223                            stderr=subprocess.DEVNULL, check=True)
224         except OSError:
225             pass
226         else:
227             return ['asciidoc']
228 
229         try:
230             subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL,
231                            stderr=subprocess.DEVNULL, check=True)
232         except OSError:
233             pass
234         else:
235             return ['asciidoc.py']
236 
237         raise FileNotFoundError
238 
239     def call(self, src: pathlib.Path, dst: pathlib.Path, *args):
240         """Call asciidoc for the given files.
241 
242         Args:
243             src: The source .asciidoc file.
244             dst: The destination .html file, or None to auto-guess.
245             *args: Additional arguments passed to asciidoc.
246         """
247         print("Calling asciidoc for {}...".format(src.name))
248         assert self._cmd is not None    # for mypy
249         cmdline = self._cmd[:]
250         if dst is not None:
251             cmdline += ['--out-file', str(dst)]
252         cmdline += args
253         cmdline.append(str(src))
254 
255         # So the virtualenv's Pygments is found
256         bin_path = pathlib.Path(sys.executable).parent
257 
258         try:
259             env = os.environ.copy()
260             env['HOME'] = str(self._homedir)
261             env['PATH'] = str(bin_path) + os.pathsep + env['PATH']
262             subprocess.run(cmdline, check=True, env=env)
263         except (subprocess.CalledProcessError, OSError) as e:
264             self._failed = True
265             utils.print_error(str(e))
266             print("Keeping modified sources in {}.".format(self._homedir),
267                   file=sys.stderr)
268             sys.exit(1)
269 
270 
271 def parse_args() -> argparse.Namespace:
272     """Parse command-line arguments."""
273     parser = argparse.ArgumentParser()
274     parser.add_argument('--website', help="Build website into a given "
275                         "directory.")
276     parser.add_argument('--asciidoc', help="Full path to python and "
277                         "asciidoc.py. If not given, it's searched in PATH.",
278                         nargs=2, required=False,
279                         metavar=('PYTHON', 'ASCIIDOC'))
280     return parser.parse_args()
281 
282 
283 def run(**kwargs) -> None:
284     """Regenerate documentation."""
285     DOC_DIR.mkdir(exist_ok=True)
286 
287     asciidoc = AsciiDoc(**kwargs)
288     try:
289         asciidoc.prepare()
290     except FileNotFoundError:
291         utils.print_error("Could not find asciidoc! Please install it, or use "
292                           "the --asciidoc argument to point this script to "
293                           "the correct python/asciidoc.py location!")
294         sys.exit(1)
295 
296     try:
297         asciidoc.build()
298     finally:
299         asciidoc.cleanup()
300 
301 
302 def main(colors: bool = False) -> None:
303     """Generate html files for the online documentation."""
304     utils.change_cwd()
305     utils.use_color = colors
306     args = parse_args()
307     run(asciidoc=args.asciidoc, website=args.website)
308 
309 
310 if __name__ == '__main__':
311     main(colors=True)
312
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py
--- a/scripts/asciidoc2html.py
+++ b/scripts/asciidoc2html.py
@@ -35,7 +35,8 @@
 
 from scripts import utils
 
-DOC_DIR = pathlib.Path("qutebrowser/html/doc")
+REPO_ROOT = pathlib.Path(__file__).parent.parent
+DOC_DIR = REPO_ROOT / 'qutebrowser' / 'html' / 'doc'
 
 
 class AsciiDoc:
@@ -79,9 +80,9 @@
 
     def _build_docs(self) -> None:
         """Render .asciidoc files to .html sites."""
-        files = [(pathlib.Path('doc/{}.asciidoc'.format(f)),
+        files = [(REPO_ROOT / 'doc' / '{}.asciidoc'.format(f)),
                   DOC_DIR / (f + ".html")) for f in self.FILES]
-        for src in pathlib.Path('doc/help/').glob('*.asciidoc'):
+        for src in (REPO_ROOT / 'doc' / 'help').glob('*.asciidoc'):
             dst = DOC_DIR / (src.stem + ".html")
             files.append((src, dst))
 
@@ -111,7 +112,7 @@
         dst_path = DOC_DIR / 'img'
         dst_path.mkdir(exist_ok=True)
         for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
-            src = pathlib.Path('doc') / 'img' / filename
+            src = REPO_ROOT / 'doc' / 'img' / filename
             dst = dst_path / filename
             shutil.copy(str(src), str(dst))
 
@@ -125,7 +126,7 @@
 
         assert self._tempdir is not None    # for mypy
         modified_src = self._tempdir / src.name
-        shutil.copy('www/header.asciidoc', modified_src)
+        shutil.copy(str(REPO_ROOT / 'www' / 'header.asciidoc'), modified_src)
 
         outfp = io.StringIO()
 
@@ -181,7 +182,7 @@
 
     def _build_website(self) -> None:
         """Prepare and build the website."""
-        theme_file = (pathlib.Path('www') / 'qute.css').resolve()
+        theme_file = REPO_ROOT / 'www' / 'qute.css'
         assert self._themedir is not None   # for mypy
         shutil.copy(theme_file, self._themedir)
 
@@ -205,7 +206,7 @@
 
         for dst, link_name in [
                 ('README.html', 'index.html'),
-                ((pathlib.Path('doc') / 'quickstart.html'),
+                ((REPO_ROOT / 'doc' / 'quickstart.html'),
                  'quickstart.html')]:
             assert isinstance(dst, (str, pathlib.Path))    # for mypy
             try:

{"golden_diff": "diff --git a/scripts/asciidoc2html.py b/scripts/asciidoc2html.py\n--- a/scripts/asciidoc2html.py\n+++ b/scripts/asciidoc2html.py\n@@ -35,7 +35,8 @@\n \n from scripts import utils\n \n-DOC_DIR = pathlib.Path(\"qutebrowser/html/doc\")\n+REPO_ROOT = pathlib.Path(__file__).parent.parent\n+DOC_DIR = REPO_ROOT / 'qutebrowser' / 'html' / 'doc'\n \n \n class AsciiDoc:\n@@ -79,9 +80,9 @@\n \n     def _build_docs(self) -> None:\n         \"\"\"Render .asciidoc files to .html sites.\"\"\"\n-        files = [(pathlib.Path('doc/{}.asciidoc'.format(f)),\n+        files = [(REPO_ROOT / 'doc' / '{}.asciidoc'.format(f)),\n                   DOC_DIR / (f + \".html\")) for f in self.FILES]\n-        for src in pathlib.Path('doc/help/').glob('*.asciidoc'):\n+        for src in (REPO_ROOT / 'doc' / 'help').glob('*.asciidoc'):\n             dst = DOC_DIR / (src.stem + \".html\")\n             files.append((src, dst))\n \n@@ -111,7 +112,7 @@\n         dst_path = DOC_DIR / 'img'\n         dst_path.mkdir(exist_ok=True)\n         for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:\n-            src = pathlib.Path('doc') / 'img' / filename\n+            src = REPO_ROOT / 'doc' / 'img' / filename\n             dst = dst_path / filename\n             shutil.copy(str(src), str(dst))\n \n@@ -125,7 +126,7 @@\n \n         assert self._tempdir is not None    # for mypy\n         modified_src = self._tempdir / src.name\n-        shutil.copy('www/header.asciidoc', modified_src)\n+        shutil.copy(str(REPO_ROOT / 'www' / 'header.asciidoc'), modified_src)\n \n         outfp = io.StringIO()\n \n@@ -181,7 +182,7 @@\n \n     def _build_website(self) -> None:\n         \"\"\"Prepare and build the website.\"\"\"\n-        theme_file = (pathlib.Path('www') / 'qute.css').resolve()\n+        theme_file = REPO_ROOT / 'www' / 'qute.css'\n         assert self._themedir is not None   # for mypy\n         shutil.copy(theme_file, self._themedir)\n \n@@ -205,7 +206,7 @@\n \n         for dst, link_name in [\n                 ('README.html', 'index.html'),\n-                ((pathlib.Path('doc') / 'quickstart.html'),\n+                ((REPO_ROOT / 'doc' / 'quickstart.html'),\n                  'quickstart.html')]:\n             assert isinstance(dst, (str, pathlib.Path))    # for mypy\n             try:\n", "issue": "Running asciidoc2html.py from outside folder\n**Version info**:\r\nqutebrowser v1.13.0                            \r\nGit commit: 6e9f24bc8-dirty on master (2020-07-01 15:34:57 +0200)\r\nBackend: QtWebEngine (Chromium 80.0.3987.163)\r\nQt: 5.15.0  \r\n\r\n**Does the bug happen if you start with `--temp-basedir`?**:\r\n\r\n**Description**\r\nIf one runs `scripts/asciidoc2html.py` (or `script/mkvenv.py`) outside of the folder, this fails since the path are not relative to the root of the repo.\r\n\r\n**How to reproduce**\r\nRun `scripts/mkvenv.py` from outside the repo root.\r\n\n", "before_files": [{"content": "#!/usr/bin/env python3\n# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Generate the html documentation based on the asciidoc files.\"\"\"\n\nfrom typing import List, Optional\nimport re\nimport os\nimport sys\nimport subprocess\nimport shutil\nimport tempfile\nimport argparse\nimport io\nimport pathlib\n\nsys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))\n\nfrom scripts import utils\n\nDOC_DIR = pathlib.Path(\"qutebrowser/html/doc\")\n\n\nclass AsciiDoc:\n\n    \"\"\"Abstraction of an asciidoc subprocess.\"\"\"\n\n    FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']\n\n    def __init__(self,\n                 asciidoc: Optional[List[str]],\n                 website: Optional[str]) -> None:\n        self._cmd = None        # type: Optional[List[str]]\n        self._asciidoc = asciidoc\n        self._website = website\n        self._homedir = None    # type: Optional[pathlib.Path]\n        self._themedir = None   # type: Optional[pathlib.Path]\n        self._tempdir = None    # type: Optional[pathlib.Path]\n        self._failed = False\n\n    def prepare(self) -> None:\n        \"\"\"Get the asciidoc command and create the homedir to use.\"\"\"\n        self._cmd = self._get_asciidoc_cmd()\n        self._homedir = pathlib.Path(tempfile.mkdtemp())\n        self._themedir = self._homedir / '.asciidoc' / 'themes' / 'qute'\n        self._tempdir = self._homedir / 'tmp'\n        self._tempdir.mkdir(parents=True)\n        self._themedir.mkdir(parents=True)\n\n    def cleanup(self) -> None:\n        \"\"\"Clean up the temporary home directory for asciidoc.\"\"\"\n        if self._homedir is not None and not self._failed:\n            shutil.rmtree(str(self._homedir))\n\n    def build(self) -> None:\n        \"\"\"Build either the website or the docs.\"\"\"\n        if self._website:\n            self._build_website()\n        else:\n            self._build_docs()\n            self._copy_images()\n\n    def _build_docs(self) -> None:\n        \"\"\"Render .asciidoc files to .html sites.\"\"\"\n        files = [(pathlib.Path('doc/{}.asciidoc'.format(f)),\n                  DOC_DIR / (f + \".html\")) for f in self.FILES]\n        for src in pathlib.Path('doc/help/').glob('*.asciidoc'):\n            dst = DOC_DIR / (src.stem + \".html\")\n            files.append((src, dst))\n\n        # patch image links to use local copy\n        replacements = [\n            (\"https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png\",\n             \"qute://help/img/cheatsheet-big.png\"),\n            (\"https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png\",\n             \"qute://help/img/cheatsheet-small.png\")\n        ]\n        asciidoc_args = ['-a', 'source-highlighter=pygments']\n\n        for src, dst in files:\n            assert self._tempdir is not None    # for mypy\n            modified_src = self._tempdir / src.name\n            with modified_src.open('w', encoding='utf-8') as moded_f, \\\n                    src.open('r', encoding='utf-8') as f:\n                for line in f:\n                    for orig, repl in replacements:\n                        line = line.replace(orig, repl)\n                    moded_f.write(line)\n            self.call(modified_src, dst, *asciidoc_args)\n\n    def _copy_images(self) -> None:\n        \"\"\"Copy image files to qutebrowser/html/doc.\"\"\"\n        print(\"Copying files...\")\n        dst_path = DOC_DIR / 'img'\n        dst_path.mkdir(exist_ok=True)\n        for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:\n            src = pathlib.Path('doc') / 'img' / filename\n            dst = dst_path / filename\n            shutil.copy(str(src), str(dst))\n\n    def _build_website_file(self, root: pathlib.Path, filename: str) -> None:\n        \"\"\"Build a single website file.\"\"\"\n        src = root / filename\n        assert self._website is not None    # for mypy\n        dst = pathlib.Path(self._website)\n        dst = dst / src.parent.relative_to('.') / (src.stem + \".html\")\n        dst.parent.mkdir(exist_ok=True)\n\n        assert self._tempdir is not None    # for mypy\n        modified_src = self._tempdir / src.name\n        shutil.copy('www/header.asciidoc', modified_src)\n\n        outfp = io.StringIO()\n\n        header = modified_src.read_text(encoding='utf-8')\n        header += \"\\n\\n\"\n\n        with src.open('r', encoding='utf-8') as infp:\n            outfp.write(\"\\n\\n\")\n            hidden = False\n            found_title = False\n            title = \"\"\n            last_line = \"\"\n\n            for line in infp:\n                line = line.rstrip()\n                if line == '// QUTE_WEB_HIDE':\n                    assert not hidden\n                    hidden = True\n                elif line == '// QUTE_WEB_HIDE_END':\n                    assert hidden\n                    hidden = False\n                elif line == \"The Compiler <mail@qutebrowser.org>\":\n                    continue\n                elif re.fullmatch(r':\\w+:.*', line):\n                    # asciidoc field\n                    continue\n\n                if not found_title:\n                    if re.fullmatch(r'=+', line):\n                        line = line.replace('=', '-')\n                        found_title = True\n                        title = last_line + \" | qutebrowser\\n\"\n                        title += \"=\" * (len(title) - 1)\n                    elif re.fullmatch(r'= .+', line):\n                        line = '==' + line[1:]\n                        found_title = True\n                        title = last_line + \" | qutebrowser\\n\"\n                        title += \"=\" * (len(title) - 1)\n\n                if not hidden:\n                    outfp.write(line.replace(\".asciidoc[\", \".html[\") + '\\n')\n                    last_line = line\n\n        current_lines = outfp.getvalue()\n        outfp.close()\n\n        modified_str = title + \"\\n\\n\" + header + current_lines\n        modified_src.write_text(modified_str, encoding='utf-8')\n\n        asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual',\n                         '-a', 'source-highlighter=pygments']\n        self.call(modified_src, dst, *asciidoc_args)\n\n    def _build_website(self) -> None:\n        \"\"\"Prepare and build the website.\"\"\"\n        theme_file = (pathlib.Path('www') / 'qute.css').resolve()\n        assert self._themedir is not None   # for mypy\n        shutil.copy(theme_file, self._themedir)\n\n        assert self._website is not None    # for mypy\n        outdir = pathlib.Path(self._website)\n\n        for item_path in pathlib.Path().rglob('*.asciidoc'):\n            if item_path.stem in ['header', 'OpenSans-License']:\n                continue\n            self._build_website_file(item_path.parent, item_path.name)\n\n        copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}\n\n        for src, dest in copy.items():\n            full_dest = outdir / dest\n            try:\n                shutil.rmtree(full_dest)\n            except FileNotFoundError:\n                pass\n            shutil.copytree(src, full_dest)\n\n        for dst, link_name in [\n                ('README.html', 'index.html'),\n                ((pathlib.Path('doc') / 'quickstart.html'),\n                 'quickstart.html')]:\n            assert isinstance(dst, (str, pathlib.Path))    # for mypy\n            try:\n                (outdir / link_name).symlink_to(dst)\n            except FileExistsError:\n                pass\n\n    def _get_asciidoc_cmd(self) -> List[str]:\n        \"\"\"Try to find out what commandline to use to invoke asciidoc.\"\"\"\n        if self._asciidoc is not None:\n            return self._asciidoc\n\n        try:\n            subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,\n                           stderr=subprocess.DEVNULL, check=True)\n        except OSError:\n            pass\n        else:\n            return ['asciidoc']\n\n        try:\n            subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL,\n                           stderr=subprocess.DEVNULL, check=True)\n        except OSError:\n            pass\n        else:\n            return ['asciidoc.py']\n\n        raise FileNotFoundError\n\n    def call(self, src: pathlib.Path, dst: pathlib.Path, *args):\n        \"\"\"Call asciidoc for the given files.\n\n        Args:\n            src: The source .asciidoc file.\n            dst: The destination .html file, or None to auto-guess.\n            *args: Additional arguments passed to asciidoc.\n        \"\"\"\n        print(\"Calling asciidoc for {}...\".format(src.name))\n        assert self._cmd is not None    # for mypy\n        cmdline = self._cmd[:]\n        if dst is not None:\n            cmdline += ['--out-file', str(dst)]\n        cmdline += args\n        cmdline.append(str(src))\n\n        # So the virtualenv's Pygments is found\n        bin_path = pathlib.Path(sys.executable).parent\n\n        try:\n            env = os.environ.copy()\n            env['HOME'] = str(self._homedir)\n            env['PATH'] = str(bin_path) + os.pathsep + env['PATH']\n            subprocess.run(cmdline, check=True, env=env)\n        except (subprocess.CalledProcessError, OSError) as e:\n            self._failed = True\n            utils.print_error(str(e))\n            print(\"Keeping modified sources in {}.\".format(self._homedir),\n                  file=sys.stderr)\n            sys.exit(1)\n\n\ndef parse_args() -> argparse.Namespace:\n    \"\"\"Parse command-line arguments.\"\"\"\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--website', help=\"Build website into a given \"\n                        \"directory.\")\n    parser.add_argument('--asciidoc', help=\"Full path to python and \"\n                        \"asciidoc.py. If not given, it's searched in PATH.\",\n                        nargs=2, required=False,\n                        metavar=('PYTHON', 'ASCIIDOC'))\n    return parser.parse_args()\n\n\ndef run(**kwargs) -> None:\n    \"\"\"Regenerate documentation.\"\"\"\n    DOC_DIR.mkdir(exist_ok=True)\n\n    asciidoc = AsciiDoc(**kwargs)\n    try:\n        asciidoc.prepare()\n    except FileNotFoundError:\n        utils.print_error(\"Could not find asciidoc! Please install it, or use \"\n                          \"the --asciidoc argument to point this script to \"\n                          \"the correct python/asciidoc.py location!\")\n        sys.exit(1)\n\n    try:\n        asciidoc.build()\n    finally:\n        asciidoc.cleanup()\n\n\ndef main(colors: bool = False) -> None:\n    \"\"\"Generate html files for the online documentation.\"\"\"\n    utils.change_cwd()\n    utils.use_color = colors\n    args = parse_args()\n    run(asciidoc=args.asciidoc, website=args.website)\n\n\nif __name__ == '__main__':\n    main(colors=True)\n", "path": "scripts/asciidoc2html.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2014-2020 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"Generate the html documentation based on the asciidoc files.\"\"\"\n\nfrom typing import List, Optional\nimport re\nimport os\nimport sys\nimport subprocess\nimport shutil\nimport tempfile\nimport argparse\nimport io\nimport pathlib\n\nsys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))\n\nfrom scripts import utils\n\nREPO_ROOT = pathlib.Path(__file__).parent.parent\nDOC_DIR = REPO_ROOT / 'qutebrowser' / 'html' / 'doc'\n\n\nclass AsciiDoc:\n\n    \"\"\"Abstraction of an asciidoc subprocess.\"\"\"\n\n    FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']\n\n    def __init__(self,\n                 asciidoc: Optional[List[str]],\n                 website: Optional[str]) -> None:\n        self._cmd = None        # type: Optional[List[str]]\n        self._asciidoc = asciidoc\n        self._website = website\n        self._homedir = None    # type: Optional[pathlib.Path]\n        self._themedir = None   # type: Optional[pathlib.Path]\n        self._tempdir = None    # type: Optional[pathlib.Path]\n        self._failed = False\n\n    def prepare(self) -> None:\n        \"\"\"Get the asciidoc command and create the homedir to use.\"\"\"\n        self._cmd = self._get_asciidoc_cmd()\n        self._homedir = pathlib.Path(tempfile.mkdtemp())\n        self._themedir = self._homedir / '.asciidoc' / 'themes' / 'qute'\n        self._tempdir = self._homedir / 'tmp'\n        self._tempdir.mkdir(parents=True)\n        self._themedir.mkdir(parents=True)\n\n    def cleanup(self) -> None:\n        \"\"\"Clean up the temporary home directory for asciidoc.\"\"\"\n        if self._homedir is not None and not self._failed:\n            shutil.rmtree(str(self._homedir))\n\n    def build(self) -> None:\n        \"\"\"Build either the website or the docs.\"\"\"\n        if self._website:\n            self._build_website()\n        else:\n            self._build_docs()\n            self._copy_images()\n\n    def _build_docs(self) -> None:\n        \"\"\"Render .asciidoc files to .html sites.\"\"\"\n        files = [(REPO_ROOT / 'doc' / '{}.asciidoc'.format(f)),\n                  DOC_DIR / (f + \".html\")) for f in self.FILES]\n        for src in (REPO_ROOT / 'doc' / 'help').glob('*.asciidoc'):\n            dst = DOC_DIR / (src.stem + \".html\")\n            files.append((src, dst))\n\n        # patch image links to use local copy\n        replacements = [\n            (\"https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png\",\n             \"qute://help/img/cheatsheet-big.png\"),\n            (\"https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png\",\n             \"qute://help/img/cheatsheet-small.png\")\n        ]\n        asciidoc_args = ['-a', 'source-highlighter=pygments']\n\n        for src, dst in files:\n            assert self._tempdir is not None    # for mypy\n            modified_src = self._tempdir / src.name\n            with modified_src.open('w', encoding='utf-8') as moded_f, \\\n                    src.open('r', encoding='utf-8') as f:\n                for line in f:\n                    for orig, repl in replacements:\n                        line = line.replace(orig, repl)\n                    moded_f.write(line)\n            self.call(modified_src, dst, *asciidoc_args)\n\n    def _copy_images(self) -> None:\n        \"\"\"Copy image files to qutebrowser/html/doc.\"\"\"\n        print(\"Copying files...\")\n        dst_path = DOC_DIR / 'img'\n        dst_path.mkdir(exist_ok=True)\n        for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:\n            src = REPO_ROOT / 'doc' / 'img' / filename\n            dst = dst_path / filename\n            shutil.copy(str(src), str(dst))\n\n    def _build_website_file(self, root: pathlib.Path, filename: str) -> None:\n        \"\"\"Build a single website file.\"\"\"\n        src = root / filename\n        assert self._website is not None    # for mypy\n        dst = pathlib.Path(self._website)\n        dst = dst / src.parent.relative_to('.') / (src.stem + \".html\")\n        dst.parent.mkdir(exist_ok=True)\n\n        assert self._tempdir is not None    # for mypy\n        modified_src = self._tempdir / src.name\n        shutil.copy(str(REPO_ROOT / 'www' / 'header.asciidoc'), modified_src)\n\n        outfp = io.StringIO()\n\n        header = modified_src.read_text(encoding='utf-8')\n        header += \"\\n\\n\"\n\n        with src.open('r', encoding='utf-8') as infp:\n            outfp.write(\"\\n\\n\")\n            hidden = False\n            found_title = False\n            title = \"\"\n            last_line = \"\"\n\n            for line in infp:\n                line = line.rstrip()\n                if line == '// QUTE_WEB_HIDE':\n                    assert not hidden\n                    hidden = True\n                elif line == '// QUTE_WEB_HIDE_END':\n                    assert hidden\n                    hidden = False\n                elif line == \"The Compiler <mail@qutebrowser.org>\":\n                    continue\n                elif re.fullmatch(r':\\w+:.*', line):\n                    # asciidoc field\n                    continue\n\n                if not found_title:\n                    if re.fullmatch(r'=+', line):\n                        line = line.replace('=', '-')\n                        found_title = True\n                        title = last_line + \" | qutebrowser\\n\"\n                        title += \"=\" * (len(title) - 1)\n                    elif re.fullmatch(r'= .+', line):\n                        line = '==' + line[1:]\n                        found_title = True\n                        title = last_line + \" | qutebrowser\\n\"\n                        title += \"=\" * (len(title) - 1)\n\n                if not hidden:\n                    outfp.write(line.replace(\".asciidoc[\", \".html[\") + '\\n')\n                    last_line = line\n\n        current_lines = outfp.getvalue()\n        outfp.close()\n\n        modified_str = title + \"\\n\\n\" + header + current_lines\n        modified_src.write_text(modified_str, encoding='utf-8')\n\n        asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual',\n                         '-a', 'source-highlighter=pygments']\n        self.call(modified_src, dst, *asciidoc_args)\n\n    def _build_website(self) -> None:\n        \"\"\"Prepare and build the website.\"\"\"\n        theme_file = REPO_ROOT / 'www' / 'qute.css'\n        assert self._themedir is not None   # for mypy\n        shutil.copy(theme_file, self._themedir)\n\n        assert self._website is not None    # for mypy\n        outdir = pathlib.Path(self._website)\n\n        for item_path in pathlib.Path().rglob('*.asciidoc'):\n            if item_path.stem in ['header', 'OpenSans-License']:\n                continue\n            self._build_website_file(item_path.parent, item_path.name)\n\n        copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}\n\n        for src, dest in copy.items():\n            full_dest = outdir / dest\n            try:\n                shutil.rmtree(full_dest)\n            except FileNotFoundError:\n                pass\n            shutil.copytree(src, full_dest)\n\n        for dst, link_name in [\n                ('README.html', 'index.html'),\n                ((REPO_ROOT / 'doc' / 'quickstart.html'),\n                 'quickstart.html')]:\n            assert isinstance(dst, (str, pathlib.Path))    # for mypy\n            try:\n                (outdir / link_name).symlink_to(dst)\n            except FileExistsError:\n                pass\n\n    def _get_asciidoc_cmd(self) -> List[str]:\n        \"\"\"Try to find out what commandline to use to invoke asciidoc.\"\"\"\n        if self._asciidoc is not None:\n            return self._asciidoc\n\n        try:\n            subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,\n                           stderr=subprocess.DEVNULL, check=True)\n        except OSError:\n            pass\n        else:\n            return ['asciidoc']\n\n        try:\n            subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL,\n                           stderr=subprocess.DEVNULL, check=True)\n        except OSError:\n            pass\n        else:\n            return ['asciidoc.py']\n\n        raise FileNotFoundError\n\n    def call(self, src: pathlib.Path, dst: pathlib.Path, *args):\n        \"\"\"Call asciidoc for the given files.\n\n        Args:\n            src: The source .asciidoc file.\n            dst: The destination .html file, or None to auto-guess.\n            *args: Additional arguments passed to asciidoc.\n        \"\"\"\n        print(\"Calling asciidoc for {}...\".format(src.name))\n        assert self._cmd is not None    # for mypy\n        cmdline = self._cmd[:]\n        if dst is not None:\n            cmdline += ['--out-file', str(dst)]\n        cmdline += args\n        cmdline.append(str(src))\n\n        # So the virtualenv's Pygments is found\n        bin_path = pathlib.Path(sys.executable).parent\n\n        try:\n            env = os.environ.copy()\n            env['HOME'] = str(self._homedir)\n            env['PATH'] = str(bin_path) + os.pathsep + env['PATH']\n            subprocess.run(cmdline, check=True, env=env)\n        except (subprocess.CalledProcessError, OSError) as e:\n            self._failed = True\n            utils.print_error(str(e))\n            print(\"Keeping modified sources in {}.\".format(self._homedir),\n                  file=sys.stderr)\n            sys.exit(1)\n\n\ndef parse_args() -> argparse.Namespace:\n    \"\"\"Parse command-line arguments.\"\"\"\n    parser = argparse.ArgumentParser()\n    parser.add_argument('--website', help=\"Build website into a given \"\n                        \"directory.\")\n    parser.add_argument('--asciidoc', help=\"Full path to python and \"\n                        \"asciidoc.py. If not given, it's searched in PATH.\",\n                        nargs=2, required=False,\n                        metavar=('PYTHON', 'ASCIIDOC'))\n    return parser.parse_args()\n\n\ndef run(**kwargs) -> None:\n    \"\"\"Regenerate documentation.\"\"\"\n    DOC_DIR.mkdir(exist_ok=True)\n\n    asciidoc = AsciiDoc(**kwargs)\n    try:\n        asciidoc.prepare()\n    except FileNotFoundError:\n        utils.print_error(\"Could not find asciidoc! Please install it, or use \"\n                          \"the --asciidoc argument to point this script to \"\n                          \"the correct python/asciidoc.py location!\")\n        sys.exit(1)\n\n    try:\n        asciidoc.build()\n    finally:\n        asciidoc.cleanup()\n\n\ndef main(colors: bool = False) -> None:\n    \"\"\"Generate html files for the online documentation.\"\"\"\n    utils.change_cwd()\n    utils.use_color = colors\n    args = parse_args()\n    run(asciidoc=args.asciidoc, website=args.website)\n\n\nif __name__ == '__main__':\n    main(colors=True)\n", "path": "scripts/asciidoc2html.py"}]}