Skip to content

Commit 21b5bde

Browse files
authored
Set up readthedocs in shader-slang.github.io (#71)
This change prepares this repo to be used in a readthedocs project for hosting the Slang Documentation in a single place. The setup and theming are based on the existing SlangPy readthedocs site (https://slangpy.shader-slang.org/en/latest/), but with the primary difference being that this change also adds 2 Python scripts for post-processing the html build outputs. This post processing is necessary because Sphinx/readthedocs does not assume that relative paths without file extensions should resolve to .html link, and although there is an option to set it up so that it does make that assumption, it will not work with links with anchors. The debug prints in the script would only be visible in the readthedocs build logs. Currently the table of contents links to the documents in this repo, as well as the docs in the Slang Standard Module Reference, with other docs like the Slang User Guide to be added in the future. For now, those links go out to the existing docs websites on GitHub Pages.
1 parent d193c67 commit 21b5bde

File tree

12 files changed

+338
-0
lines changed

12 files changed

+338
-0
lines changed

.gitmodules

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[submodule "docs/external/slang"]
2+
path = docs/external/slang
3+
url = https://github.com/shader-slang/slang.git
4+
[submodule "docs/external/slangpy"]
5+
path = docs/external/slangpy
6+
url = https://github.com/shader-slang/slangpy.git
7+
[submodule "docs/external/stdlib-reference"]
8+
path = docs/external/stdlib-reference
9+
url = https://github.com/shader-slang/stdlib-reference.git

.readthedocs.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# .readthedocs.yaml
2+
# Read the Docs configuration file
3+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4+
5+
version: 2
6+
7+
build:
8+
os: ubuntu-24.04
9+
tools:
10+
python: "3.10"
11+
12+
sphinx:
13+
configuration: docs/conf.py
14+
fail_on_warning: false
15+
16+
python:
17+
install:
18+
- requirements: requirements.txt
19+
20+
submodules:
21+
include: all

docs/__static/theme_overrides.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.container {
2+
width: 100%;
3+
}
4+
5+
.toggle .header {
6+
display: block;
7+
clear: both;
8+
padding-bottom: 1em;
9+
}
10+
11+
.toggle .header:after {
12+
content: " ▶";
13+
}
14+
15+
.toggle .header.open:after {
16+
content: " ▼";
17+
}
18+
19+
th.head {
20+
text-align: left;
21+
}
22+
23+
/*
24+
Fix for horizontal stacking weirdness in the RTD theme with Python properties:
25+
https://github.com/readthedocs/sphinx_rtd_theme/issues/1301
26+
*/
27+
.py.property {
28+
display: block !important;
29+
}

docs/_ext/fix_links.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
Custom Sphinx extension to fix relative links with fragment identifiers.
3+
"""
4+
from sphinx.util import logging
5+
import re
6+
import os
7+
from sphinx.application import Sphinx
8+
9+
logger = logging.getLogger(__name__)
10+
11+
def fix_md_links_post_process(app, exception):
12+
"""
13+
Post-processing to fix links in the HTML output files.
14+
This is our main function that runs after the build is complete.
15+
"""
16+
if exception:
17+
return
18+
19+
# Only run in HTML builder
20+
if app.builder.name != 'html':
21+
return
22+
23+
output_dir = app.builder.outdir
24+
logger.info(f"[DEBUG] Post-processing HTML files in {output_dir}")
25+
26+
# Specifically focus on stdlib-reference directory
27+
stdlib_dir = os.path.join(output_dir, 'external', 'stdlib-reference')
28+
if not os.path.exists(stdlib_dir):
29+
logger.info(f"[DEBUG] stdlib-reference directory not found: {stdlib_dir}")
30+
return
31+
32+
count = 0
33+
fixed = 0
34+
35+
# Walk through HTML files
36+
for root, _, files in os.walk(stdlib_dir):
37+
for filename in files:
38+
if filename.endswith('.html'):
39+
filepath = os.path.join(root, filename)
40+
count += 1
41+
42+
try:
43+
with open(filepath, 'r', encoding='utf-8') as f:
44+
content = f.read()
45+
46+
# Original content for comparison
47+
original_content = content
48+
49+
# Look for href="#../path/to/file#fragment" pattern
50+
# This is the problematic pattern where a relative path is treated as a fragment
51+
pattern = r'href=(["\'])#(\.\.\/[^"\']+?)#([^"\']+?)\1'
52+
matches = re.findall(pattern, content)
53+
54+
if matches:
55+
logger.info(f"[DEBUG] Found {len(matches)} problematic links in {filepath}")
56+
57+
# Fix each match
58+
for quote, path, fragment in matches:
59+
# Create the correct path with .html extension
60+
if not path.endswith('/') and '.' not in path.split('/')[-1]:
61+
path_with_html = path + '.html'
62+
else:
63+
path_with_html = path
64+
65+
# Replace in content
66+
old = f'href={quote}#{path}#{fragment}{quote}'
67+
new = f'href={quote}{path_with_html}#{fragment}{quote}'
68+
content = content.replace(old, new)
69+
logger.info(f"[DEBUG] Fixed: {old} -> {new}")
70+
71+
# Also fix simpler case: href="#../path/to/file"
72+
pattern = r'href=(["\'])#(\.\.\/[^"\'#]+?)\1'
73+
matches = re.findall(pattern, content)
74+
75+
if matches:
76+
logger.info(f"[DEBUG] Found {len(matches)} simple problematic links in {filepath}")
77+
78+
# Fix each match
79+
for quote, path in matches:
80+
# Create the correct path with .html extension
81+
if not path.endswith('/') and '.' not in path.split('/')[-1]:
82+
path_with_html = path + '.html'
83+
else:
84+
path_with_html = path
85+
86+
# Replace in content
87+
old = f'href={quote}#{path}{quote}'
88+
new = f'href={quote}{path_with_html}{quote}'
89+
content = content.replace(old, new)
90+
logger.info(f"[DEBUG] Fixed: {old} -> {new}")
91+
92+
# Save the file if changes were made
93+
if content != original_content:
94+
with open(filepath, 'w', encoding='utf-8') as f:
95+
f.write(content)
96+
fixed += 1
97+
logger.info(f"[DEBUG] Fixed file: {filepath}")
98+
99+
except Exception as e:
100+
logger.info(f"[DEBUG] Error processing {filepath}: {e}")
101+
102+
logger.info(f"[DEBUG] Post-processed {count} HTML files, fixed {fixed} files")
103+
104+
105+
def setup(app: Sphinx):
106+
"""Set up the extension."""
107+
logger.info("[DEBUG] Registering link fixer extension (post-processing only)")
108+
109+
# Register post-processing function to run after the build is complete
110+
app.connect('build-finished', fix_md_links_post_process)
111+
112+
return {
113+
'version': '0.1',
114+
'parallel_read_safe': True,
115+
'parallel_write_safe': True,
116+
}

docs/_ext/fix_toc.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from docutils import nodes
2+
from sphinx.transforms import SphinxTransform
3+
from sphinx.addnodes import toctree
4+
5+
class TocFilter(SphinxTransform):
6+
"""Ensure toctree nodes have titlesonly set to true."""
7+
8+
default_priority = 700
9+
10+
def apply(self, **kwargs):
11+
# Find all toctree nodes
12+
for toctree_node in self.document.traverse(toctree):
13+
# Make sure the 'titlesonly' option is set
14+
toctree_node['titlesonly'] = True
15+
16+
def setup(app):
17+
app.add_transform(TocFilter)
18+
return {
19+
'version': '0.1',
20+
'parallel_read_safe': True,
21+
'parallel_write_safe': True,
22+
}

docs/_templates/sidebar/page.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!-- Based on https://stackoverflow.com/a/25543713/1130282 -->
2+
{% extends "!page.html" %}
3+
4+
{% block footer %}
5+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
6+
<script type="text/javascript">
7+
$(document).ready(function() {
8+
$(".toggle > *").hide();
9+
$(".toggle .header").show();
10+
$(".toggle .header").click(function() {
11+
$(this).parent().children().not(".header").toggle(400);
12+
$(this).parent().children(".header").toggleClass("open");
13+
})
14+
});
15+
</script>
16+
{% endblock %}

docs/conf.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Configuration file for the Sphinx documentation builder.
3+
#
4+
# For the full list of built-in configuration values, see the documentation:
5+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
6+
7+
# -- Project information -----------------------------------------------------
8+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
9+
import os
10+
import sys
11+
sys.path.insert(0, os.path.abspath('.')) # For finding _ext
12+
sys.path.insert(0, os.path.abspath('..'))
13+
14+
project = 'Slang Documentation'
15+
author = 'Chris Cummings, Benedikt Bitterli, Sai Bangaru, Yong Hei, Aidan Foster'
16+
release = '0.1.0'
17+
18+
# -- General configuration ---------------------------------------------------
19+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
20+
21+
source_suffix = ['.rst', '.md']
22+
source_parsers = {
23+
'.md': 'myst_parser.sphinx_',
24+
}
25+
26+
# Load extensions - make sure fix_links is early in the list
27+
extensions = [
28+
'_ext.fix_links', # Custom extension to fix relative links with fragments
29+
'sphinx.ext.duration',
30+
'sphinx.ext.doctest',
31+
'sphinx.ext.autodoc',
32+
'sphinx.ext.autosummary',
33+
'sphinx.ext.intersphinx',
34+
'myst_parser',
35+
'_ext.fix_toc', # Simple extension to set titlesonly=True
36+
]
37+
38+
# Debugging flag for verbose output
39+
verbose = True
40+
41+
intersphinx_mapping = {
42+
'python': ('https://docs.python.org/3/', None),
43+
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
44+
}
45+
intersphinx_disabled_domains = ['std']
46+
47+
templates_path = ['_templates']
48+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'index.md']
49+
50+
# Configure myst-parser for markdown files
51+
myst_enable_extensions = [
52+
"colon_fence",
53+
"linkify",
54+
"smartquotes",
55+
"replacements",
56+
"html_image",
57+
]
58+
59+
myst_url_schemes = ["http", "https", "mailto", "ftp"]
60+
myst_heading_anchors = 3
61+
myst_title_to_header = True
62+
63+
# -- Options for HTML output -------------------------------------------------
64+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
65+
66+
html_theme = "furo"
67+
html_title = "Slang Documentation"
68+
html_css_files = ["theme_overrides.css"]
69+
html_theme_options = {
70+
"light_css_variables": {
71+
"color-api-background": "#f7f7f7",
72+
},
73+
"dark_css_variables": {
74+
"color-api-background": "#1e1e1e",
75+
},
76+
}
77+
78+
# Use default Furo sidebar configuration - remove custom sidebar
79+
# html_sidebars = {} # Let Furo use its defaults

docs/external/slang

Submodule slang added at 54acb11

docs/external/slangpy

Submodule slangpy added at 42cc1f2

docs/external/stdlib-reference

Submodule stdlib-reference added at 2f726a9

0 commit comments

Comments
 (0)