Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@ html {
--pst-font-family-base: SUSE, var(--pst-font-family-base-system);
--pst-font-family-heading: SUSE, sans-serif, var(--pst-font-family-base-system);
--pst-font-family-monospace: Courier, var(--pst-font-family-monospace-system);
}
}

/* Add a horizontal line below the glossary letter titles */
.glossary-letter-title {
border-bottom: 1px solid var(--pst-color-border); /* Use theme's border color */
padding-bottom: 0.5em; /* Space between title text and the line */
margin-bottom: 1em; /* Space between the line and the content below */
}
139 changes: 139 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
from datetime import datetime
from pathlib import Path
import sys
from typing import Self

from docutils import nodes
from sphinx.application import Sphinx
from sphinx.errors import ExtensionError
from sphinx.transforms import SphinxTransform
from sphinx.util import logging

from docbuild.__about__ import __version__
Expand Down Expand Up @@ -287,7 +290,143 @@ def run_toml_generator(app: Sphinx) -> None:
generate_toml_reference(toml_input, rst_output, **config_data)


class GlossaryRearranger(SphinxTransform):
"""Group glossary terms by first letter into section nodes.

The glossary directive emits a flat definition list. This transform rewrites
it into per-letter sections so each letter gets its own heading and anchor.
"""

default_priority = 400

@staticmethod
def _is_glossary_definition_list(node: nodes.definition_list) -> bool:
"""Return whether a definition list belongs to a glossary block."""
parent = node.parent
if parent is None:
return False

return "glossary" in parent.get("classes", []) or "glossary" in node.get(
"classes", []
)

@staticmethod
def _group_terms_by_letter(
node: nodes.definition_list,
) -> dict[str, list[nodes.definition_list_item]]:
"""Group glossary items by the first letter of each term."""
groups: dict[str, list[nodes.definition_list_item]] = {}
for item in list(node.children):
if not isinstance(item, nodes.definition_list_item):
continue

term_node = item.next_node(nodes.term)
if not term_node:
continue

term_text = term_node.astext().strip()
if not term_text:
continue

letter = term_text[0].upper()
groups.setdefault(letter, []).append(item)

return groups

@staticmethod
def _build_letter_sections(
groups: dict[str, list[nodes.definition_list_item]],
) -> list[nodes.section]:
"""Build one section per glossary letter group."""
new_sections: list[nodes.section] = []
for letter in sorted(groups.keys()):
sec = nodes.section(
ids=[letter],
classes=["glossary-section", f"glossary-letter-{letter.lower()}"],
)
sec += nodes.title(letter, letter, classes=["glossary-letter-title"])

letter_list = nodes.definition_list()
letter_list.extend(groups[letter])
sec += letter_list
new_sections.append(sec)

return new_sections

@staticmethod
def _replace_with_sections(
parent: nodes.Element,
node: nodes.definition_list,
new_sections: list[nodes.section],
) -> None:
"""Replace glossary list with generated sections in the proper parent."""
if getattr(parent, "tagname", "") == "glossary" and parent.parent is not None:
# Sphinx's ToC collector ignores <section> nodes nested inside a
# <glossary> node. Hoist sections one level up so they are indexed.
grandparent = parent.parent
idx = grandparent.index(parent)
for sec in reversed(new_sections):
grandparent.insert(idx, sec)
parent.remove(node)
if not parent.children:
grandparent.remove(parent)
return

node.replace_self(new_sections)

def apply(self: Self) -> None:
"""Rewrite glossary definition lists into grouped letter sections."""
for node in list(self.document.findall(nodes.definition_list)):
if node.get("_glossary_rearranged", False):
continue

if not self._is_glossary_definition_list(node):
continue

groups = self._group_terms_by_letter(node)
if not groups:
continue

new_sections = self._build_letter_sections(groups)
parent = node.parent
if not isinstance(parent, nodes.Element):
continue

self._replace_with_sections(parent, node, new_sections)

node["_glossary_rearranged"] = True
logger.info("GlossaryRearranger: Grouped %d letters.", len(groups))


class GlossaryToCBuilder(SphinxTransform):
"""Add glossary term permalinks used by the rendered HTML output.

This transform keeps generated glossary terms linkable via header links.
"""

default_priority = 950

def apply(self) -> None:
"""Attach permalink references to glossary term nodes."""
# Inject Term Permalinks (existing logic)
for term_node in self.document.findall(nodes.term):
t_ids = term_node.get("ids", [])
if t_ids and not any(
isinstance(c, nodes.reference) for c in term_node.children
):
t_ref = nodes.reference(
"",
"#",
refuri=f"#{t_ids[0]}",
classes=["headerlink"],
reftitle="Permalink",
)
term_node += t_ref


def setup(app: Sphinx) -> None:
"""Sphinx setup function to connect the TOML generator to the build process."""
# This hook ensures the file exists before Sphinx tries to 'include' it
app.connect("builder-inited", run_toml_generator)
app.add_transform(GlossaryRearranger)
app.add_transform(GlossaryToCBuilder)
93 changes: 81 additions & 12 deletions docs/source/glossary.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
Glossary
========

:term:`A <ADoc>` | :term:`C <Changelog>` | :term:`D <DAPS>` | :term:`G <GIL>` | :term:`I <IPython>` | :term:`L <Lifecycle>` | :term:`M <Module>` | :term:`O <Option>` | :term:`P <Package>` | :term:`R <RNC>` | :term:`S <SemVer>` | :term:`U <UV>` | :term:`V <VENV>` | :term:`X <XML>`

For Python specific terms, look into:

* https://docs.python.org/3/glossary.html
Expand Down Expand Up @@ -52,16 +50,24 @@ For Python specific terms, look into:
See also :term:`Asynchronous Programming`, :term:`asyncio`, :term:`Parallelism`

DAPS
The *Documentation and Publishing System* (DAPS) is a tool to build documentation from DocBook or ADoc files.
The *DocBook and Publishing System* (DAPS) is a tool to build documentation from DocBook or ADoc files.
It is used to generate various output formats such as HTML, PDF, and EPUB.

See https://github.com/openSUSE/daps
See :term:`DocBook`, :term:`ADoc`, :term:`DC File`

See also https://github.com/openSUSE/daps

DC File
The *DAPS Configuration File* (DC file) is a configuration file used by DAPS to define parameters for building documentation. For example, it contains information about the entry file, what stylesheets to use, and other build options.
The *DAPS Configuration* File ("DC file") is a configuration file used by DAPS to define parameters for building documentation. For example, it contains information about the entry file, what stylesheets to use, and other build options.

Deliverable
The smallest unit of documentation that can be built. It's mapped to a DC File. A deliverable is usually being built in different formats.
The smallest publishable unit of documentation. It is either dynamically
generated by DAPS from source into various output formats or supplied
as a pre-built deliverable.

See also :term:`DC File` and :term:`Pre-built Deliverable`

See class :class:`docbuild.models.deliverable.Deliverable`.

DocBook
A semantic markup language based on :term:`XML` used for writing
Expand All @@ -70,7 +76,7 @@ For Python specific terms, look into:
See also :term:`ADoc`, https://www.docbook.org

Docset
Usually a release or version of a project. For example, ``15-SP6``.
See :term:`Release`

Doctype
A formal syntax to identify one or many set of documents.
Expand All @@ -79,6 +85,11 @@ For Python specific terms, look into:

See section :ref:`doctype-syntax`.

Docserv Config
A deprecated XML configuration that defines products, releases, deliverables, and translations.

See :term:`Portal Config`.

GIL
Global Interpreter Lock
A mutex (a lock) in the standard CPython interpreter that ensures only one thread can execute Python bytecode at any given time within a single process. This lock effectively prevents multi-threaded, CPU-bound Python programs from achieving true parallelism on multi-core processors, as only one thread can run on one core at a time.
Expand All @@ -96,10 +107,16 @@ For Python specific terms, look into:

See section :ref:`use-ipython`.

Language
The linguistic and regional setting for a deliverable. In this context, a language is defined by a combination of a language code and a country code. For example, ``en-us`` for English (United States).

See class :class:`docbuild.models.language.LanguageCode`.


Lifecycle
Describes the distinct stages a product goes through, from its initial introduction to the market until its eventual decline and retirement.

See class :class:`~docbuild.models.lifecycle.LifecycleFlag`.
See class :class:`docbuild.models.lifecycle.LifecycleFlag`.

Module
In Python context, a single Python file containing code.
Expand Down Expand Up @@ -136,10 +153,30 @@ For Python specific terms, look into:

See https://peps.python.org/

Portal Config
XML configuration that describes all products, their releases,
deliverables and translations if available.

See also :term:`XML`

Pre-built Deliverable
A documentation artifact provided in its final form, generated by
external toolchains rather than by DAPS. These deliverables are
integrated as static files and do not require further processing or
transformation.

See also :term:`DC File`

Product
A abbreviated name for a SUSE product. For example, ``sles``.
A distinct software offering or suite of components.
It represents the top-level entity in the documentation hierarchy,
under which specific releases and deliverables are organized.
Every product is identified by a full formal name and a unique
abbreviation used for internal references and build configurations.

See also :term:`Doctype`, :term:`Release`, :term:`Deliverable`

See class :class:`~docbuild.models.product.Product`.
See class :class:`docbuild.models.product.Product`.

Pydantic
A Python library for data validation and settings management using Python type annotations. It provides a way to define data models with strict type checking and validation rules, making it easier to ensure that the data your application works with is correct and consistent.
Expand All @@ -162,13 +199,27 @@ For Python specific terms, look into:
RELAX NG
RNC
RNG
A schema language for XML used to define and validate the structure
and content of XML documents.
*REgular LAnguage for XML Next Generation* is a schema language for XML
used to define and validate the structure and content of XML documents.
RNC is the compact syntax of RELAX NG, while RNG is the XML syntax.
Both are equivalent in terms of expressiveness.

See https://relaxng.org/

Release
A collection of deliverables associated with a specific version of a
product.
A release represents a published milestone in the product lifecycle,
encompassing both beta and final (GA) states.

See also :term:`Docset`, :term:`Product`

Repository
A centralized digital storage space for the documentation source code.
Typically hosted on platforms such as GitHub or GitLab.

See class :class:`docbuild.models.repo.Repo`

Ruff
A fast extensible linter and code formatter to improve code qualitiy
and enforce style guidelines.
Expand Down Expand Up @@ -221,12 +272,30 @@ For Python specific terms, look into:

See section :ref:`prepare-devel-env`.

XInclude
A standard XML mechanism used to assemble complex structures from
smaller, independent fragments. In this project, it is used to
modularize both documentation source files and the Portal Config.

See also :term:`DocBook`, :term:`Portal Config`, :term:`XML`

See https://www.w3.org/TR/xinclude-11/

XML
The *eXtensible Markup Language* is a text-based markup
language used to structure, store, and transport data in
a format that is both human- and machine-readable.

XPath
A query language used to navigate and address specific parts of an
XML document. It enables tools and build scripts to precisely locate,
retrieve, or manipulate data.

See https://www.w3.org/TR/xpath-10/

XSLT
The *eXtensible Stylesheet Language for Transformations*
is a language that transforms XML documents into other
formats like HTML, plain text, or new XML structures.

See https://www.w3.org/TR/xslt-10/
6 changes: 3 additions & 3 deletions docs/source/user/config/index.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Viewing and Validating Configuration
------------------------------------
====================================

You can Use the :command:`config` subcommand to list or validate your current settings.

Listing Configuration
~~~~~~~~~~~~~~~~~~~~~
---------------------

To see the current merged configuration:

Expand All @@ -15,7 +15,7 @@ To see the current merged configuration:
Use the ``--flat`` flag to see the dotted-path format, or filter by ``--app`` or ``--env``.

Validating Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
------------------------

To ensure your TOML files match the required schema:

Expand Down
1 change: 1 addition & 0 deletions docs/source/user/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ User Guide

install
config
portal-config/index
run-docbuild
config/index
build
Expand Down
Loading
Loading