Skip to content

Commit 3ebe3a3

Browse files
committed
Doc transation from Sphix to Zensical
1 parent b7977a7 commit 3ebe3a3

45 files changed

Lines changed: 2229 additions & 3202 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,9 @@ dmypy.json
133133
*.wpr
134134
*.wpu
135135

136-
# Sphinx build
137-
docs/_build
136+
# Doc build
137+
docs/site
138138
docs/firebird-base.docset/
139139

140140
# Other local files and directories
141141
local/
142-
zendocs/

‎.readthedocs.yml‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@
55
# Required
66
version: 2
77

8-
# Build documentation in the docs/ directory with Sphinx
9-
sphinx:
10-
configuration: docs/conf.py
11-
128
build:
13-
os: "ubuntu-22.04"
9+
os: "ubuntu-24.04"
1410
tools:
1511
python: "3.11"
16-
17-
# Optionally set the version of Python and requirements required to build your docs
18-
python:
12+
jobs:
1913
install:
20-
- requirements: docs/requirements.txt
14+
- pip install . zensical mkdocstrings-python
15+
build:
16+
html:
17+
- cd docs && zensical build
18+
post_build:
19+
- mkdir -p "$READTHEDOCS_OUTPUT/html"
20+
- cp -a docs/site/. "$READTHEDOCS_OUTPUT/html/"

‎convert.py‎

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import os
2+
import re
3+
4+
def convert_rst_to_md(filename, content):
5+
# Rule 6: Determine module name
6+
base_name = os.path.splitext(os.path.basename(filename))[0]
7+
module_name = f"firebird.base.{base_name}"
8+
9+
# Overrides for some files
10+
if base_name in ['index', 'introduction', 'changelog', 'license', 'modules']:
11+
module_name = "firebird.base"
12+
13+
lines = content.splitlines()
14+
new_lines = []
15+
i = 0
16+
17+
def process_roles(text):
18+
def replace_role(match):
19+
role = match.group(1)
20+
content = match.group(2)
21+
display_content = content
22+
if display_content.startswith('~'):
23+
display_content = display_content[1:].split('.')[-1]
24+
25+
if role == 'doc':
26+
return f"[{display_content.capitalize()}]({display_content}.md)"
27+
return f"`{display_content}`"
28+
29+
return re.sub(r':(doc|ref|class|func|obj|mod|attr|exc):`([^`]+)`', replace_role, text)
30+
31+
while i < len(lines):
32+
line = lines[i]
33+
34+
# Skip module/synopsis
35+
if line.strip().startswith('.. module::') or line.strip().startswith(':synopsis:'):
36+
i += 1
37+
continue
38+
39+
# Rule 1: Headers
40+
# Title with overline and underline
41+
if i + 2 < len(lines) and re.match(r'^[#*=+^"~-]+$', line.strip()) and \
42+
re.match(r'^[#*=+^"~-]+$', lines[i+2].strip()) and \
43+
len(line.strip()) >= len(lines[i+1].strip()) and len(lines[i+2].strip()) >= len(lines[i+1].strip()):
44+
new_lines.append(f"# {lines[i+1].strip()}")
45+
i += 3
46+
continue
47+
48+
# Header with underline
49+
if i + 1 < len(lines) and re.match(r'^[#*=+^"~-]+$', lines[i+1].strip()) and \
50+
len(lines[i+1].strip()) >= len(line.strip()) and len(line.strip()) > 0:
51+
char = lines[i+1].strip()[0]
52+
level = 2
53+
if char == '#': level = 1
54+
elif char == '=': level = 2
55+
elif char == '-': level = 3
56+
elif char == '^': level = 4
57+
elif char == '"': level = 5
58+
elif char == '~': level = 6
59+
new_lines.append(f"{'#' * level} {line.strip()}")
60+
i += 2
61+
continue
62+
63+
# Rule 2: code-block
64+
if line.strip().startswith('.. code-block::'):
65+
lang = line.split('::')[-1].strip()
66+
new_lines.append(f"```{lang}")
67+
i += 1
68+
# Skip empty lines until indented block
69+
while i < len(lines) and not lines[i].strip():
70+
i += 1
71+
# Find indentation
72+
if i < len(lines):
73+
indent = len(lines[i]) - len(lines[i].lstrip())
74+
while i < len(lines) and (not lines[i].strip() or len(lines[i]) - len(lines[i].lstrip()) >= indent):
75+
new_lines.append(lines[i][indent:])
76+
i += 1
77+
new_lines.append("```")
78+
continue
79+
80+
# Rule 3: Admonitions
81+
admonitions = ['note', 'tip', 'warning', 'important', 'caution', 'seealso', 'admonition', 'error', 'danger']
82+
match_adm = re.match(r'^\.\.\s+(' + '|'.join(admonitions) + r')::(.*)', line.strip())
83+
if match_adm:
84+
adm_type = match_adm.group(1)
85+
adm_text = match_adm.group(2).strip()
86+
87+
if adm_type == 'admonition':
88+
new_lines.append(f"!!! {adm_text}")
89+
i += 1
90+
else:
91+
new_lines.append(f"!!! {adm_type}")
92+
if adm_text:
93+
new_lines.append(f" {process_roles(adm_text)}")
94+
i += 1
95+
96+
# Skip empty lines
97+
while i < len(lines) and not lines[i].strip():
98+
i += 1
99+
if i < len(lines):
100+
curr_indent = len(lines[i]) - len(lines[i].lstrip())
101+
if curr_indent > 0:
102+
indent = curr_indent
103+
while i < len(lines) and (not lines[i].strip() or len(lines[i]) - len(lines[i].lstrip()) >= indent):
104+
content_line = lines[i][indent:]
105+
content_line = process_roles(content_line)
106+
content_line = re.sub(r'^\.\.\s+(' + '|'.join(admonitions) + r')::', r'!!! \1', content_line)
107+
new_lines.append(f" {content_line}")
108+
i += 1
109+
continue
110+
111+
# Rule 5: mkdocstrings
112+
match_auto = re.match(r'^\.\.\s+auto(class|function|exception|data)::\s+([\w\.]+)', line.strip())
113+
if match_auto:
114+
obj_type = match_auto.group(1)
115+
obj_name = match_auto.group(2)
116+
full_name = obj_name if '.' in obj_name else f"{module_name}.{obj_name}"
117+
new_lines.append(f"::: {full_name}")
118+
i += 1
119+
# Skip options
120+
while i < len(lines) and (not lines[i].strip() or (len(lines[i]) - len(lines[i].lstrip()) > 0 and lines[i].strip().startswith(':'))):
121+
i += 1
122+
continue
123+
124+
# Rule 4 & 7: Sphinx roles and toctree
125+
line = process_roles(line)
126+
127+
if line.strip().startswith('.. toctree::'):
128+
i += 1
129+
while i < len(lines) and (not lines[i].strip() or lines[i].strip().startswith(':')):
130+
i += 1
131+
while i < len(lines) and lines[i].strip() and not lines[i].strip().startswith('.'):
132+
fname = lines[i].strip()
133+
new_lines.append(f"- [{fname.capitalize()}]({fname}.md)")
134+
i += 1
135+
continue
136+
137+
# Rule 8: license include
138+
if line.strip().startswith('.. include::'):
139+
inc_path = line.split('::')[-1].strip()
140+
if 'LICENSE' in inc_path:
141+
try:
142+
with open('LICENSE', 'r') as f:
143+
new_lines.extend(f.read().splitlines())
144+
except:
145+
new_lines.append(f"(Include {inc_path})")
146+
i += 1
147+
continue
148+
149+
# Horizontal rules
150+
if re.match(r'^[#*=+^"~-]{3,}$', line.strip()):
151+
new_lines.append("---")
152+
i += 1
153+
continue
154+
155+
# Handle simple links like `Firebird Project`_
156+
line = re.sub(r'`([^`]+)`_', r'\1', line)
157+
# Handle link definitions
158+
match_link_def = re.match(r'^\.\.\s+_([^:]+):\s+(.*)', line.strip())
159+
if match_link_def:
160+
new_lines.append(f"[{match_link_def.group(1)}]: {match_link_def.group(2)}")
161+
i += 1
162+
continue
163+
164+
if line.strip().startswith('.. |'):
165+
i += 1
166+
continue
167+
168+
if line.strip() == '|':
169+
new_lines.append("")
170+
i += 1
171+
continue
172+
173+
new_lines.append(line)
174+
i += 1
175+
176+
return "\n".join(new_lines)
177+
178+
files_to_convert = [
179+
'buffer.txt', 'changelog.txt', 'collections.txt', 'config.txt',
180+
'hooks.txt', 'index.txt', 'introduction.txt', 'license.txt',
181+
'logging.txt', 'modules.txt', 'protobuf.txt', 'signal.txt',
182+
'strconv.txt', 'trace.txt', 'types.txt'
183+
]
184+
185+
for filename in files_to_convert:
186+
src_path = os.path.join('docs', filename)
187+
dest_path = os.path.join('zendocs/docs', filename.replace('.txt', '.md'))
188+
189+
if os.path.exists(src_path):
190+
with open(src_path, 'r') as f:
191+
content = f.read()
192+
193+
converted = convert_rst_to_md(filename, content)
194+
195+
with open(dest_path, 'w') as f:
196+
f.write(converted)
197+
print(f"Converted {src_path} to {dest_path}")
198+
else:
199+
print(f"Skipping {src_path}, not found.")

‎docs/Makefile‎

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)