Metadata-Version: 2.4
Name: sphinx-source-tree
Version: 0.2.3
Summary: Ship entire project source code and directory tree with your Sphinx documentation
Author-email: Artur Barseghyan <artur.barseghyan@gmail.com>
Maintainer-email: Artur Barseghyan <artur.barseghyan@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/barseghyanartur/sphinx-source-tree
Project-URL: Issues, https://github.com/barseghyanartur/sphinx-source-tree/issues
Project-URL: Documentation, https://sphinx-source-tree.readthedocs.io/
Project-URL: Repository, https://github.com/barseghyanartur/sphinx-source-tree/
Project-URL: Changelog, https://sphinx-source-tree.readthedocs.io/en/latest/changelog.html
Keywords: sphinx,documentation,source-tree,literalinclude
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Sphinx
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python
Classifier: Topic :: Documentation
Classifier: Topic :: Documentation :: Sphinx
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: all
Requires-Dist: sphinx-source-tree[build,dev,docs,test]; extra == "all"
Provides-Extra: dev
Requires-Dist: detect-secrets; extra == "dev"
Requires-Dist: doc8; extra == "dev"
Requires-Dist: ipython; extra == "dev"
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pydoclint; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: uv; extra == "dev"
Provides-Extra: build
Requires-Dist: build; extra == "build"
Requires-Dist: pkginfo; extra == "build"
Requires-Dist: twine; extra == "build"
Requires-Dist: wheel; extra == "build"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pytest-codeblock; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx; extra == "docs"
Requires-Dist: sphinx-autobuild; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.3.0; extra == "docs"
Requires-Dist: sphinx_llms_txt_link; extra == "docs"
Requires-Dist: sphinx-no-pragma; extra == "docs"
Requires-Dist: PyYAML; extra == "docs"
Dynamic: license-file

===================
sphinx-source-tree
===================
Ship entire project source code and directory tree with your Sphinx
documentation.

.. External references

.. _Sphinx: https://www.sphinx-doc.org/
.. _reStructuredText: https://docutils.sourceforge.io/rst.html
.. _Sphinx literalinclude range options: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#directive-literalinclude

.. Internal references

.. _Read the Docs: http://sphinx-source-tree.readthedocs.io/
.. _GitHub: https://github.com/barseghyanartur/sphinx-source-tree

.. image:: https://img.shields.io/pypi/v/sphinx-source-tree.svg
   :target: https://pypi.python.org/pypi/sphinx-source-tree
   :alt: PyPI Version

.. image:: https://img.shields.io/pypi/pyversions/sphinx-source-tree.svg
    :target: https://pypi.python.org/pypi/sphinx-source-tree/
    :alt: Supported Python versions

.. image:: https://github.com/barseghyanartur/sphinx-source-tree/actions/workflows/test.yml/badge.svg?branch=main
   :target: https://github.com/barseghyanartur/sphinx-source-tree/actions
   :alt: Build Status

.. image:: https://readthedocs.org/projects/sphinx-source-tree/badge/?version=latest
    :target: http://sphinx-source-tree.readthedocs.io
    :alt: Documentation Status

.. image:: https://img.shields.io/badge/docs-llms.txt-blue
    :target: https://sphinx-source-tree.readthedocs.io/en/latest/llms.txt
    :alt: llms.txt - documentation for LLMs

.. image:: https://img.shields.io/badge/license-MIT-blue.svg
   :target: https://github.com/barseghyanartur/sphinx-source-tree/#License
   :alt: MIT

.. image:: https://coveralls.io/repos/github/barseghyanartur/sphinx-source-tree/badge.svg?branch=main&service=github
    :target: https://coveralls.io/github/barseghyanartur/sphinx-source-tree?branch=main
    :alt: Coverage

Generate a reStructuredText (``.rst``) file that contains:

1. An ASCII directory tree of your project.
2. A ``literalinclude`` directive for every source file you select.

The result is a single ``.rst`` document ready to be included in a `Sphinx`_
documentation build, specifically for the ``llms.txt``, providing full
project context for LLMs.

Prerequisites
=============
Python 3.10+

Installation
============

.. code-block:: sh

   uv pip install sphinx-source-tree

Usage
=====
Quick start
-----------

Run in your project root:

.. code-block:: sh

   sphinx-source-tree

This writes ``docs/source_tree.rst`` with the full tree and
``literalinclude`` blocks for ``.js``, ``.json``, ``.md``, ``.py``, ``.rst``,
``.toml``, ``.yaml`` and ``.yml`` files.

Print to stdout instead:

.. code-block:: sh

   sphinx-source-tree --stdout

CLI reference
-------------

.. code-block:: sh

   sphinx-source-tree [OPTIONS]

``-p, --project-root PATH``
    Project directory.  Default: current directory.

``-d, --depth N``
    Maximum tree depth.  Default: ``10``.

``-o, --output PATH``
    Output ``.rst`` file.  Default: ``docs/source_tree.rst``.

``-e, --extensions EXT [EXT ...]``
    File suffixes to include via ``literalinclude``.
    Default: ``.js .json .md .py .rst .toml .yaml .yml``.

``-i, --ignore PAT [PAT ...]``
    Glob patterns to ignore (matched against both the relative path
    and the bare file name).

``-w, --whitelist DIR [DIR ...]``
    Restrict output to these directories.  Ignored when
    ``--include-all`` is active.

``--include-all / --no-include-all``
    Include everything regardless of whitelist.  Default: on.

``-t, --title TEXT``
    RST section title.  Default: ``Project source-tree``.

``--linenos / --no-linenos``
    Attach ``:linenos:`` to ``literalinclude`` directives.
    Default: off.

``--order PATH [PATH ...]``
    Explicit ordering for the ``literalinclude`` listing.  The files
    listed here appear **first**, in the given sequence; all remaining
    collected files follow in their default sorted order.  Has no
    effect on the ASCII directory tree.

``--stdout``
    Write to stdout instead of the output file.

``-V, --version``
    Show version and exit.

Configuration via pyproject.toml
---------------------------------

All CLI options (except ``--stdout`` and ``--version``) can be set under
``[tool.sphinx-source-tree]`` in your project's ``pyproject.toml``.
CLI arguments always take precedence.

Single-file example:

.. code-block:: toml

   [tool.sphinx-source-tree]
   depth = 4
   output = "docs/source_tree.rst"
   extensions = [".py", ".rst", ".toml"]
   ignore = ["__pycache__", "*.pyc", ".git", "*.egg-info"]
   whitelist = ["src", "docs"]
   include-all = false
   title = "Source listing"
   linenos = true
   extra-languages = {".vue" = "vue", ".svelte" = "svelte"}
   order = ["README.rst", "pyproject.toml", "src/app.py"]

Key names use hyphens (``include-all``) to follow TOML/PEP 621
convention; they are normalised internally.

Multiple output files
---------------------

You can generate several ``.rst`` files in one run by adding
``[[tool.sphinx-source-tree.files]]`` entries.  Top-level settings act as
shared defaults; each entry can override any of them.

.. code-block:: toml

   [tool.sphinx-source-tree]
   # Shared defaults — applied to every file unless overridden
   depth = 10
   ignore = ["__pycache__", "*.pyc", ".git", "*.egg-info"]
   linenos = false

   [[tool.sphinx-source-tree.files]]
   output = "docs/source_tree.rst"
   title = "Full project source"
   # inherits depth, ignore, linenos from the section above

   [[tool.sphinx-source-tree.files]]
   output = "docs/api_tree.rst"
   title = "API source"
   extensions = [".py"]
   whitelist = ["src"]
   include-all = false
   depth = 5  # overrides the shared default

   [[tool.sphinx-source-tree.files]]
   output = "docs/docs_tree.rst"
   title = "Documentation files"
   extensions = [".rst", ".md"]
   whitelist = ["docs"]
   include-all = false

The merge priority is: **built-in defaults** < **top-level
``[tool.sphinx-source-tree]``** < **per-file ``[[…files]]`` entry** <
**CLI arguments**.

When no ``[[files]]`` entries are present the tool behaves exactly as
before, so existing configurations are fully backward compatible.

Controlling listing order
-------------------------

By default, ``literalinclude`` blocks are emitted in alphabetical order.
The ``order`` option lets you pin specific files to the top of the
listing while leaving all other files in their default sorted order.

.. note::

   ``order`` affects only the ``literalinclude`` source-code listing.
   The ASCII directory tree is always rendered in its natural sorted
   order and is unaffected by this setting.

**Via** ``pyproject.toml`` **(top-level)**

The top-level ``order`` list is shared by all output files (or inherited
by ``[[files]]`` entries that do not set their own):

.. code-block:: toml

   [tool.sphinx-source-tree]
   order = [
       "README.rst",
       "pyproject.toml",
       "src/app.py",
   ]

**Via** ``[[tool.sphinx-source-tree.files]]``

Each ``[[files]]`` entry can define its own ``order``, which overrides
the top-level value for that output file only:

.. code-block:: toml

   [tool.sphinx-source-tree]
   ignore = ["__pycache__", "*.pyc"]

   [[tool.sphinx-source-tree.files]]
   output = "docs/source_tree.rst"
   title = "Full project source"
   # no order — uses default alphabetical listing

   [[tool.sphinx-source-tree.files]]
   output = "docs/llm_tree.rst"
   title = "Source for LLMs"
   order = ["src/core.py", "src/models.py", "src/utils.py"]

**Via the CLI**

.. code-block:: sh

   sphinx-source-tree --order src/core.py src/models.py src/utils.py

**Via the Python API**

Pass the ``order`` keyword argument to ``generate()``:

.. pytestfixture: safe_test_path
.. code-block:: python
    :name: test_python_api_order

    from pathlib import Path
    from sphinx_source_tree import generate

    rst = generate(
        project_root=Path("."),
        output=Path("docs/source_tree_ordered.rst"),
        extensions=[".py", ".rst"],
        order=[
            "README.rst",
            "src/app.py",
        ],
    )
    Path("docs/source_tree_ordered.rst").write_text(rst)

Files listed in ``order`` that do not match any collected file (because
they are excluded by extension or ignore rules, or simply do not exist)
emit a warning to stderr and are silently skipped.

Per-file inclusion options
--------------------------

You can restrict how much of each file is shown by attaching
`Sphinx literalinclude range options`_ to individual files.  The
following options are supported:

* ``:lines:`` — explicit line numbers or ranges (e.g. ``1-20, 30``)
* ``:start-at:`` — include from the first line that contains the marker
* ``:start-after:`` — include from the line *after* the marker
* ``:end-before:`` — include up to, but not including, the marker line
* ``:end-at:`` — include up to and including the marker line

**Via** ``pyproject.toml`` **(flat)**

Add a ``[tool.sphinx-source-tree.file-options]`` table whose keys are
file paths relative to the project root.  This mapping is used by all
output files that do not select a named profile:

.. code-block:: toml

   [tool.sphinx-source-tree.file-options]
   "src/app.py" = {"end-before" = "# *** Tests ***"}
   "src/utils.py" = {"start-after" = "# -- public API --"}
   "src/models.py" = {"lines" = "1-60"}

This produces ``literalinclude`` blocks such as:

.. code-block:: rst

   .. literalinclude:: ../src/app.py
      :language: python
      :caption: src/app.py
      :end-before: # *** Tests ***

Option keys may be written with either hyphens (``end-before``) or
underscores (``end_before``); both are accepted and normalised to the
hyphenated form that Sphinx expects.  Unknown option keys emit a warning
to stderr and are ignored.

**Via** ``pyproject.toml`` **(named profiles)**

When you need different inclusion rules for different output files —
for example a full source tree and a compact one for LLMs — define
named profiles under ``[tool.sphinx-source-tree.file-options-profiles]``
and select one per ``[[files]]`` entry with ``file-options-profile``:

.. code-block:: toml

   [tool.sphinx-source-tree]
   ignore = ["__pycache__", "*.pyc", ".git"]

   # "full" profile — no restrictions (empty table = include everything)
   [tool.sphinx-source-tree.file-options-profiles.full]

   # "compact" profile — trim each file at its test boundary
   [tool.sphinx-source-tree.file-options-profiles.compact]
   "src/app.py" = {"end-before" = "# ********** Tests **********"}
   "src/models.py" = {"end-before" = "# ********** Tests **********"}
   "src/utils.py" = {"lines" = "1-60"}

   [[tool.sphinx-source-tree.files]]
   output = "docs/source_tree_full.rst"
   title = "Full project source"
   file-options-profile = "full"

   [[tool.sphinx-source-tree.files]]
   output = "docs/source_tree.rst"
   title = "Compact source for LLMs"
   file-options-profile = "compact"

Resolution order:

1. If ``file-options-profile`` names a key in ``file-options-profiles``,
   that profile's mapping is used.
2. If the name is not found, a warning is printed to stderr and the tool
   falls back to the top-level ``file-options`` table.
3. If no profile is specified, the top-level ``file-options`` table is
   used directly (fully backward compatible).

**Via the Python API**

Pass the ``file_options`` keyword argument to ``generate()`` with the
already-resolved mapping for that output file. Profile selection happens
in ``_generate_from_cfg``; when calling ``generate()`` directly simply
pass whichever dict applies:

.. pytestfixture: safe_test_path
.. code-block:: python
    :name: test_python_api_file_options

    from pathlib import Path
    from sphinx_source_tree import generate

    compact_options = {
        "src/app.py": {"end-before": "# *** Tests ***"},
        "src/utils.py": {"start-after": "# -- public API --"},
    }

    rst = generate(
        project_root=Path("."),
        output=Path("docs/source_tree2.rst"),
        file_options=compact_options,
    )

Absolute paths are also accepted as keys and are resolved relative to
``project_root`` automatically.

Python API
----------

You can also call the generator from Python:

.. pytestfixture: safe_test_path
.. code-block:: python
    :name: test_python_api

    from pathlib import Path
    from sphinx_source_tree import generate

    rst = generate(
        project_root=Path("."),
        output=Path("docs/source_tree1.rst"),
        depth=5,
        extensions=[".py", ".rst"],
        ignore=["__pycache__", "*.pyc"],
        title="My project source",
    )
    Path("docs/source_tree.rst").write_text(rst)

``generate()`` returns the RST content as a string and never writes to
disk, so you can post-process or redirect as needed.

Lower-level helpers are also importable:

- ``build_tree()`` -- ASCII tree string.
- ``collect_files()`` -- list of ``Path`` objects to include.
- ``detect_language()`` -- suffix-to-Sphinx-language mapping.
- ``load_config()`` -- read ``[tool.sphinx-source-tree]`` from
  ``pyproject.toml``.

Documentation
=============
- Documentation is available on `Read the Docs`_.

Tests
=====

Run the tests:

.. code-block:: sh

   pytest -vvv

Writing documentation
=====================

Keep the following hierarchy.

.. code-block:: text

   =====
   title
   =====

   header
   ======

   sub-header
   ----------

   sub-sub-header
   ~~~~~~~~~~~~~~

   sub-sub-sub-header
   ^^^^^^^^^^^^^^^^^^

   sub-sub-sub-sub-header
   ++++++++++++++++++++++

   sub-sub-sub-sub-sub-header
   **************************

License
=======

MIT

Support
=======
For security issues contact me at the e-mail given in the `Author`_ section.

For overall issues, go to `GitHub`_.

Author
======

Artur Barseghyan <artur.barseghyan@gmail.com>
