Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 9985d13

Browse files
authored
feat: support overview page (#365)
* feat: grab repository metadata * feat: do not generate summary pages if metadata not found * feat: add support for overview pages and potential additional content * test: add markdown tests * test: add test files * feat: address review comments * feat: address review comments
1 parent 1318b24 commit 9985d13

7 files changed

Lines changed: 162 additions & 39 deletions

docfx_yaml/extension.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,7 @@ def convert_module_to_package_if_needed(obj):
20392039
{
20402040
"name": f"{app.env.library_shortname} APIs",
20412041
"items": [
2042+
{"name": "Overview", "href": "summary_overview.md"},
20422043
{"name": "Classes", "href": "summary_class.yml"},
20432044
{"name": "Methods", "href": "summary_method.yml"},
20442045
{"name": "Properties and Attributes", "href": "summary_property.yml"},

docfx_yaml/markdown_utils.py

Lines changed: 97 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,41 @@ def _prepend_markdown_header(filename: str, mdfile: Iterable[str]) -> None:
259259
mdfile.write(file_content)
260260

261261

262+
def _merge_markdown_content(
263+
*,
264+
base_file: str,
265+
additional_content_file: str,
266+
prepend_additional_content: bool = False,
267+
) -> None:
268+
"""Merges the Markdown file contents.
269+
270+
The additional_content_file's contents get appended to the base_file's
271+
content.
272+
273+
Args:
274+
base_file: The base content to append content to.
275+
additional_content_file: The additional content to be appended to
276+
the base file.
277+
prepend_additional_content: Optional. If specified, puts the additional
278+
content before the base file content.
279+
"""
280+
try:
281+
with (
282+
open(base_file, 'r+') as base,
283+
open(additional_content_file, 'r') as additional_content
284+
):
285+
file_content = (
286+
f'{additional_content.read()}\n{base.read()}'
287+
if prepend_additional_content
288+
else f'{base.read()}\n{additional_content.read()}'
289+
)
290+
base.seek(0)
291+
base.write(file_content)
292+
except OSError:
293+
print("Could not open the given files.")
294+
return
295+
296+
262297
def move_markdown_pages(
263298
app: sphinx.application,
264299
outdir: Path,
@@ -280,7 +315,13 @@ def move_markdown_pages(
280315

281316
"reference.md", # Reference docs overlap with Overview. Will try and incorporate this in later.
282317
# See https://github.com/googleapis/sphinx-docfx-yaml/issues/106.
283-
"summary_overview.md", # Already included in the TOC.
318+
"overview_content.md", # Content will be merged into summary_overview page.
319+
]
320+
321+
# Use this to move the page but do not add them in the TOC.
322+
ignore_in_toc = [
323+
"summary_overview.md", # Already included in the TOC with other
324+
# summary pages.
284325
]
285326

286327
files_to_rename = {
@@ -312,6 +353,17 @@ def move_markdown_pages(
312353
"readme.md" not in markdown_file_names):
313354
files_to_ignore.remove("index.md")
314355

356+
if (
357+
"summary_overview.md" in markdown_file_names and
358+
"overview_content.md" in markdown_file_names
359+
):
360+
# Keep the summary_overview file, prepend the additioanl content.
361+
_merge_markdown_content(
362+
base_file=markdown_dir/"summary_overview.md",
363+
additional_content_file=markdown_dir/"overview_content.md",
364+
prepend_additional_content=True,
365+
)
366+
315367
# For each file, if it is a markdown file move to the top level pages.
316368
for mdfile in markdown_dir.iterdir():
317369
if mdfile.is_dir():
@@ -320,62 +372,68 @@ def move_markdown_pages(
320372
# Restore the original cwd after finish working on the directory.
321373
cwd.pop()
322374

323-
if mdfile.is_file() and mdfile.name.lower() not in files_to_ignore:
324-
mdfile_name = ""
375+
if not mdfile.is_file() or mdfile.name.lower() in files_to_ignore:
376+
continue
325377

326-
_remove_license(mdfile)
378+
mdfile_name = ""
327379

328-
# Extract the header name for TOC.
329-
with open(mdfile) as mdfile_iterator:
330-
name = _extract_header_from_markdown(mdfile_iterator)
380+
_remove_license(mdfile)
331381

332-
if not name:
333-
with open(mdfile, 'r+') as mdfile_iterator:
334-
mdfile_name = mdfile_iterator.name.split("/")[-1].split(".")[0].capitalize()
382+
# Extract the header name for TOC.
383+
with open(mdfile) as mdfile_iterator:
384+
name = _extract_header_from_markdown(mdfile_iterator)
335385

336-
print(f"Could not find a title for {mdfile_iterator.name}. Using {mdfile_name} as the title instead.")
337-
name = mdfile_name
386+
if not name:
387+
with open(mdfile, 'r+') as mdfile_iterator:
388+
mdfile_name = mdfile_iterator.name.split("/")[-1].split(".")[0].capitalize()
338389

339-
_prepend_markdown_header(name, mdfile_iterator)
390+
print(f"Could not find a title for {mdfile_iterator.name}. Using {mdfile_name} as the title instead.")
391+
name = mdfile_name
340392

393+
_prepend_markdown_header(name, mdfile_iterator)
341394

342-
mdfile_name_to_use = mdfile.name.lower()
343-
if mdfile_name_to_use in files_to_rename:
344-
mdfile_name_to_use = files_to_rename[mdfile_name_to_use]
345395

346-
if cwd and mdfile_name_to_use == "index.md":
347-
mdfile_name_to_use = f"{'_'.join(cwd)}_{mdfile_name_to_use}"
396+
mdfile_name_to_use = mdfile.name.lower()
397+
if mdfile_name_to_use in files_to_rename:
398+
mdfile_name_to_use = files_to_rename[mdfile_name_to_use]
348399

349-
mdfile_outdir = f"{outdir}/{mdfile_name_to_use}"
400+
if cwd and mdfile_name_to_use == "index.md":
401+
mdfile_name_to_use = f"{'_'.join(cwd)}_{mdfile_name_to_use}"
350402

351-
shutil.copy(mdfile, mdfile_outdir)
352-
app.env.moved_markdown_pages.add(mdfile_name_to_use)
403+
mdfile_outdir = f"{outdir}/{mdfile_name_to_use}"
353404

354-
_highlight_md_codeblocks(mdfile_outdir)
355-
_clean_image_links(mdfile_outdir)
405+
shutil.copy(mdfile, mdfile_outdir)
356406

357-
if not cwd:
358-
# Use Overview as the name for top-level index file.
359-
if 'index.md' in mdfile_name_to_use:
360-
# Save the index page entry.
361-
index_page_entry = {
362-
'name': 'Overview',
363-
'href': 'index.md',
364-
}
365-
continue
407+
_highlight_md_codeblocks(mdfile_outdir)
408+
_clean_image_links(mdfile_outdir)
366409

367-
# Use '/' to reserve for top level pages.
368-
app.env.markdown_pages['/'].append({
369-
'name': name,
370-
'href': mdfile_name_to_use,
371-
})
410+
if mdfile.name.lower() in ignore_in_toc:
411+
continue
412+
413+
app.env.moved_markdown_pages.add(mdfile_name_to_use)
414+
415+
if not cwd:
416+
# Use Overview as the name for top-level index file.
417+
if 'index.md' in mdfile_name_to_use:
418+
# Save the index page entry.
419+
index_page_entry = {
420+
'name': 'Overview',
421+
'href': 'index.md',
422+
}
372423
continue
373424

374-
# Add the file to the TOC later.
375-
app.env.markdown_pages[cwd[-1]].append({
425+
# Use '/' to reserve for top level pages.
426+
app.env.markdown_pages['/'].append({
376427
'name': name,
377428
'href': mdfile_name_to_use,
378429
})
430+
continue
431+
432+
# Add the file to the TOC later.
433+
app.env.markdown_pages[cwd[-1]].append({
434+
'name': name,
435+
'href': mdfile_name_to_use,
436+
})
379437

380438
if app.env.markdown_pages.get('/'):
381439
# Sort the top level pages. Other pages will be sorted when they're
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## This is the additional content.
2+
3+
Additional content to be appended.

tests/markdown_example_base.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Base markdown file content.
2+
3+
This is the content for base Markdown file.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Base markdown file content.
2+
3+
This is the content for base Markdown file.
4+
5+
## This is the additional content.
6+
7+
Additional content to be appended.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## This is the additional content.
2+
3+
Additional content to be appended.
4+
5+
# Base markdown file content.
6+
7+
This is the content for base Markdown file.

tests/test_markdown.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,49 @@ def test_remove_license(self, base_filename, want_filename):
306306
self.assertEqual(test_file.read(), mdfile_want.read())
307307

308308

309+
test_markdown_filenames = [
310+
[
311+
"tests/markdown_example_base.md",
312+
"tests/markdown_example_additional_content.md",
313+
"tests/markdown_example_base_expected.md",
314+
],
315+
[
316+
# The content should be the same as base, and not throw any errors.
317+
"tests/markdown_example_base.md",
318+
"tests/markdown_example_does_not_exist.md",
319+
"tests/markdown_example_base.md",
320+
],
321+
[
322+
# The content should be the same as base, and not throw any errors.
323+
"tests/markdown_example_base.md",
324+
"tests/markdown_example_additional_content.md",
325+
"tests/markdown_example_prepended_base.md",
326+
True,
327+
],
328+
]
329+
@parameterized.expand(test_markdown_filenames)
330+
def test_merges_markdown_content(
331+
self,
332+
base_file,
333+
additional_file,
334+
expected_file,
335+
prepend_additional_content=False,
336+
):
337+
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as test_file:
338+
with open(base_file) as base:
339+
test_file.write(base.read())
340+
test_file.flush()
341+
test_file.seek(0)
342+
343+
markdown_utils._merge_markdown_content(
344+
base_file=test_file.name,
345+
additional_content_file=additional_file,
346+
prepend_additional_content=prepend_additional_content,
347+
)
348+
test_file.seek(0)
349+
350+
with open(expected_file) as mdfile_expected:
351+
self.assertEqual(test_file.read(), mdfile_expected.read())
352+
309353
if __name__ == '__main__':
310354
unittest.main()

0 commit comments

Comments
 (0)