Added asc sorted headings

This commit is contained in:
Kevin Veen-Birkenbach 2025-03-15 14:32:15 +01:00
parent cd21645148
commit b446fb5476
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E

View File

@ -7,6 +7,13 @@ logger = logging.getLogger(__name__)
# Set the maximum heading level to include (e.g., include headings up to H3) # Set the maximum heading level to include (e.g., include headings up to H3)
MAX_HEADING_LEVEL = 3 MAX_HEADING_LEVEL = 3
def natural_sort_key(text):
"""
Generate a key for natural (human-friendly) sorting,
where numbers in the text are taken into account by their numeric value.
"""
return [int(c) if c.isdigit() else c.lower() for c in re.split('(\d+)', text)]
def extract_headings_from_file(filepath, max_level=MAX_HEADING_LEVEL): def extract_headings_from_file(filepath, max_level=MAX_HEADING_LEVEL):
""" """
Extract Markdown headings (up to max_level) from the file at filepath. Extract Markdown headings (up to max_level) from the file at filepath.
@ -17,7 +24,7 @@ def extract_headings_from_file(filepath, max_level=MAX_HEADING_LEVEL):
with open(filepath, 'r', encoding='utf-8') as f: with open(filepath, 'r', encoding='utf-8') as f:
in_code_block = False in_code_block = False
for line in f: for line in f:
# Toggle fenced code block state if a line starts with ``` # Toggle code block state if a line starts with ```
if line.strip().startswith("```"): if line.strip().startswith("```"):
in_code_block = not in_code_block in_code_block = not in_code_block
continue continue
@ -29,10 +36,10 @@ def extract_headings_from_file(filepath, max_level=MAX_HEADING_LEVEL):
level = len(match.group(1)) level = len(match.group(1))
if level <= max_level: if level <= max_level:
heading_text = match.group(2).strip() heading_text = match.group(2).strip()
# Create a simple slug for the anchor by: # Create a simple slug for the anchor:
# - converting to lowercase # - convert to lowercase
# - replacing spaces with hyphens # - replace spaces with hyphens
# - removing non-alphanumeric characters (except hyphens) # - remove non-alphanumeric characters (except hyphens)
anchor = re.sub(r'\s+', '-', heading_text.lower()) anchor = re.sub(r'\s+', '-', heading_text.lower())
anchor = re.sub(r'[^a-z0-9\-]', '', anchor) anchor = re.sub(r'[^a-z0-9\-]', '', anchor)
headings.append({'level': level, 'text': heading_text, 'anchor': anchor}) headings.append({'level': level, 'text': heading_text, 'anchor': anchor})
@ -43,10 +50,11 @@ def extract_headings_from_file(filepath, max_level=MAX_HEADING_LEVEL):
def add_local_md_headings(app, pagename, templatename, context, doctree): def add_local_md_headings(app, pagename, templatename, context, doctree):
""" """
For every Markdown file in the same directory as the current page, For every Markdown file in the same directory as the current page,
extract its headings and add them to the context. extract its headings, sort them in natural ascending order, and add them
to the context.
""" """
srcdir = app.srcdir srcdir = app.srcdir
# Determine the directory of the current page (e.g., "directory/file" "directory") # Determine the directory of the current page (e.g., "directory/file" -> "directory")
directory = os.path.dirname(pagename) directory = os.path.dirname(pagename)
abs_dir = os.path.join(srcdir, directory) abs_dir = os.path.join(srcdir, directory)
if not os.path.isdir(abs_dir): if not os.path.isdir(abs_dir):
@ -57,13 +65,9 @@ def add_local_md_headings(app, pagename, templatename, context, doctree):
local_md_headings = [] local_md_headings = []
for file in os.listdir(abs_dir): for file in os.listdir(abs_dir):
if file.endswith('.md'): if file.endswith('.md'):
# Optionally, you may choose to skip the current file:
# if file == os.path.basename(pagename):
# continue
filepath = os.path.join(abs_dir, file) filepath = os.path.join(abs_dir, file)
headings = extract_headings_from_file(filepath) headings = extract_headings_from_file(filepath)
for heading in headings: for heading in headings:
# Build a full link: if there is a directory, include it in the path.
file_link = os.path.join(directory, file) if directory else file file_link = os.path.join(directory, file) if directory else file
full_link = file_link + '#' + heading['anchor'] full_link = file_link + '#' + heading['anchor']
local_md_headings.append({ local_md_headings.append({
@ -71,9 +75,10 @@ def add_local_md_headings(app, pagename, templatename, context, doctree):
'text': heading['text'], 'text': heading['text'],
'link': full_link 'link': full_link
}) })
# Sort headings in natural ascending order using natural_sort_key.
local_md_headings.sort(key=lambda x: natural_sort_key(x['text']))
context['local_md_headings'] = local_md_headings context['local_md_headings'] = local_md_headings
def setup(app): def setup(app):
# Connect our handler to the "html-page-context" event.
app.connect('html-page-context', add_local_md_headings) app.connect('html-page-context', add_local_md_headings)
return {'version': '0.1', 'parallel_read_safe': True} return {'version': '0.1', 'parallel_read_safe': True}