Skip to content

Commit 98ce996

Browse files
committed
Update the specification of "documentation_of" string
- Making the document clear and encourage to use `./` or `..` for relative paths and `//` for absolute paths. - In UNIX, traditionally, a path begins with two successive slashes is interpreted in an implementation-defined manner. - This doesn't break existing documents in almost cases. It has a conservative fallback mechanism.
1 parent 016c137 commit 98ce996

File tree

4 files changed

+109
-7
lines changed

4 files changed

+109
-7
lines changed

.verify-helper/docs/static/document.ja.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,22 @@ list_dependencies = "sed 's/^@include \"\\(.*\\)\"$/\\1/ ; t ; d' {path}"
101101
[Front Matter](http://jekyllrb-ja.github.io/docs/front-matter/) 形式で `documentation_of` という項目にファイルを指定しておくと、指定したファイルについての生成されたドキュメント中に、Markdown ファイルの中身が挿入されます。
102102

103103
たとえば、`path/to/segment_tree.hpp` というファイルに説明を Markdown で追加したいときは `for/bar.md` などに次のように書きます。
104-
[Front Matter](https://jekyllrb.com/docs/front-matter/)
105104

106105
```
107106
---
108107
title: Segment Tree
109-
documentation_of: path/to/segment_tree.hpp
108+
documentation_of: ./path/to/segment_tree.hpp
110109
---
111110
112111
## 説明
113112
114113
このファイルでは、……
115114
```
116115

116+
`documentation_of` 文字列は、`./` あるいは `..` から始まる場合は Markdown ファイルのパスからの相対パスであると認識されます。また、`//` から初まる場合は `.verify-helper` ディレクトリがある場所をルートとする絶対パスであると認識されます。
117+
また、ディレクトリ区切り文字には `/` を使い、大文字小文字を正しく入力してください。
118+
119+
117120
### トップページへの Markdown の埋め込み
118121

119122
`.verify-helper/docs/index.md` というファイルを作って、そこに Markdown で書いてください。

.verify-helper/docs/static/document.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,19 @@ For example, to add description to a document of a file `path/to/segment_tree.hp
104104
```
105105
---
106106
title: Segment Tree
107-
documentation_of: path/to/segment_tree.hpp
107+
documentation_of: ./path/to/segment_tree.hpp
108108
---
109109
110110
## Description
111111
112112
In this file, ...
113113
```
114114

115+
The `documentation_of` string is recognized as a relative path from the Markdown file when the string starts with `./` or `..`.
116+
The `documentation_of` string is recognized as a absolute path from the directory which has `.verify-helper` directory as the root when the string starts with `//`.
117+
The path should use `/` as directory separator be case-sensitive.
118+
119+
115120

116121
### Embedding Markdown to the top page
117122

onlinejudge_verify/documentation/configure.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,32 @@ def apply_exclude_list_to_stats(*, excluded_paths: List[pathlib.Path], source_co
212212
return result
213213

214214

215+
def resolve_documentation_of(documentation_of: str, *, markdown_path: pathlib.Path, basedir: pathlib.Path) -> Optional[pathlib.Path]:
216+
if documentation_of.startswith('.'):
217+
# a relative path
218+
path = markdown_path.parent / pathlib.PosixPath(documentation_of)
219+
if path.exists() and basedir in path.resolve().parents:
220+
return path
221+
elif documentation_of.startswith('//'):
222+
# from the document root
223+
path = basedir / pathlib.PosixPath(documentation_of[2:])
224+
if path.exists() and basedir in path.resolve().parents:
225+
return path
226+
227+
# guessing
228+
logger.warning('No file at the expected path from the `documentation_of` path. The `documentation_of` path should use `/` for path separator, and start with `.` for a relative path from the path of the Markdown file, or start with `//` for a absolute path from the root of the repository.: %s', documentation_of)
229+
candidate_paths = [
230+
basedir / pathlib.PosixPath(documentation_of),
231+
basedir / pathlib.Path(documentation_of),
232+
markdown_path.parent / pathlib.PosixPath(documentation_of),
233+
markdown_path.parent / pathlib.Path(documentation_of),
234+
]
235+
for path in candidate_paths:
236+
if path.exists() and basedir in path.resolve().parents:
237+
return path
238+
return None
239+
240+
215241
def convert_to_page_render_jobs(*, source_code_stats: List[SourceCodeStat], markdown_paths: List[pathlib.Path], site_render_config: SiteRenderConfig) -> List[PageRenderJob]:
216242
basedir = site_render_config.basedir
217243

@@ -230,11 +256,14 @@ def convert_to_page_render_jobs(*, source_code_stats: List[SourceCodeStat], mark
230256
path = markdown_relative_path
231257
documentation_of = front_matter.get(FrontMatterItem.documentation_of.value)
232258
if documentation_of is not None:
233-
if not (basedir / pathlib.Path(documentation_of)).exists():
259+
documentation_of_path = resolve_documentation_of(documentation_of, markdown_path=path, basedir=basedir)
260+
if documentation_of_path is None:
234261
logger.warning('the `documentation_of` path of %s is not found: %s', str(path), documentation_of)
262+
del front_matter[FrontMatterItem.documentation_of.value]
235263
continue
236-
documentation_of_path = (basedir / pathlib.Path(documentation_of)).resolve().relative_to(basedir)
237-
path = documentation_of_path.parent / (documentation_of_path.name + '.md')
264+
documentation_of_relative_path = documentation_of_path.resolve().relative_to(basedir)
265+
front_matter[FrontMatterItem.documentation_of.value] = str(documentation_of_relative_path)
266+
path = documentation_of_relative_path.parent / (documentation_of_path.name + '.md')
238267

239268
job = PageRenderJob(
240269
path=path,

tests/test_docs.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,78 @@
11
"""This module has tests for the docs subcommand.
22
"""
33

4+
import pathlib
5+
import random
6+
import textwrap
47
import unittest
8+
from typing import *
59

610
import onlinejudge_verify.main
11+
import tests.utils as utils
712

813

914
class TestDocsSubcommandSmoke(unittest.TestCase):
10-
"""TestDocsSubcommandSmoke is a smoke tests of `docs` subcommand.
15+
"""TestDocsSubcommandSmoke is a class for smoke tests of `docs` subcommand.
1116
"""
1217
def test_run(self) -> None:
1318
onlinejudge_verify.main.subcommand_docs()
19+
20+
21+
class TestDocsSubcommand(unittest.TestCase):
22+
"""TestDocsSubcommand is a class for end-to-end tests of `docs` subcommand.
23+
"""
24+
def test_documentation_of(self) -> None:
25+
get_random_string = lambda: ''.join([random.choice('0123456789abcdef') for _ in range(64)])
26+
random_relative_hpp = get_random_string()
27+
random_absolute_hpp = get_random_string()
28+
random_no_document_hpp = get_random_string()
29+
random_relative_md = get_random_string()
30+
random_absolute_md = get_random_string()
31+
random_standalone_page_md = get_random_string()
32+
33+
files = {
34+
pathlib.Path('src', 'a', 'b', 'relative.hpp'): random_relative_hpp.encode(),
35+
pathlib.Path('src', 'a', 'b', 'absolute.hpp'): random_absolute_hpp.encode(),
36+
pathlib.Path('src', 'a', 'b', 'no-document.hpp'): random_no_document_hpp.encode(),
37+
pathlib.Path('docs', 'x', 'y', 'relative.md'): textwrap.dedent(f"""\
38+
---
39+
title: relative.md
40+
documentation_of: ../../../src/a/b/relative.hpp
41+
---
42+
43+
{random_relative_md}
44+
""").encode(),
45+
pathlib.Path('docs', 'x', 'y', 'absolute.md'): textwrap.dedent(f"""\
46+
---
47+
title: absolute.md
48+
documentation_of: //src/a/b/absolute.hpp
49+
---
50+
51+
{random_absolute_md}
52+
""").encode(),
53+
pathlib.Path('docs', 'x', 'y', 'standalone-page.md'): textwrap.dedent(f"""\
54+
---
55+
title: standalone page
56+
---
57+
58+
{random_standalone_page_md}
59+
""").encode(),
60+
}
61+
62+
destination_dir = pathlib.Path('.verify-helper', 'markdown')
63+
expected: Dict[pathlib.Path, List[str]] = {
64+
destination_dir / 'src' / 'a' / 'b' / 'relative.hpp.md': [random_relative_hpp, random_relative_md],
65+
destination_dir / 'src' / 'a' / 'b' / 'absolute.hpp.md': [random_absolute_hpp, random_absolute_md],
66+
destination_dir / 'src' / 'a' / 'b' / 'no-document.hpp.md': [random_no_document_hpp],
67+
destination_dir / 'docs' / 'x' / 'y' / 'standalone-page.md': [random_standalone_page_md],
68+
}
69+
70+
with utils.load_files_pathlib(files) as tempdir:
71+
with utils.chdir(tempdir):
72+
onlinejudge_verify.main.subcommand_docs()
73+
for path, keywords in expected.items():
74+
self.assertTrue((tempdir / path).exists())
75+
with open(tempdir / path) as fh:
76+
content = fh.read()
77+
for keyword in keywords:
78+
self.assertIn(keyword, content)

0 commit comments

Comments
 (0)