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
35 changes: 35 additions & 0 deletions docs/directives.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@ This page documents the custom directives provided by ``awdur``.
Specify the template used to render the final file.
Only needs to be used on one code block within a file.

.. rst:option:: project

Specify the name of the project to associate the code block with.
If not given the default name ``default`` will be used.

.. rst:directive:: awdur:template

Define a custom template to use with a project.
See :doc:`/examples/inline-templates` for example usage.

.. rst:option:: project

Specify the name of the project to associate the template with.
If not given the default name ``default`` will be used.

.. rst:directive:: awdur:project-tree

.. note::

This directive only has an effect with html outputs.

Insert an interactive file explorer for code files produced by the given project.
If no name is given, the default name ``default`` will be used.

See :doc:`/examples/project-tree` and :doc:`/examples/multiple-projects` for example usage.


Sphinx Only
-----------
Expand All @@ -34,3 +60,12 @@ The following directives are only available when using the Sphinx extension (``a
sourcecode

See :rst:dir:`code`.


.. rst:directive:: awdur:render

.. note::

This directive only has an effect with html outputs.

Run the given ``<filename>`` through the ``awdur render`` cli command and embed the result into the page using an ``iframe``
14 changes: 14 additions & 0 deletions docs/examples/multiple-projects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Multiple Projects
=================

**Source Document**

.. literalinclude:: ../../examples/multiple-projects/multiple-projects.rst
:language: restructuredtext

**Rendered Document**

``$ awdur render multiple-projects.rst -o multiple-projects.html``

.. awdur:render:: ../examples/multiple-projects/multiple-projects.rst
:height: 400px
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ But if you are interested, below are some examples of what is currently possible
examples/multiple-files
examples/inline-templates
examples/project-tree
examples/multiple-projects

For a more "real world" example of this project in action you can check out my `blog <https://www.alcarney.me>`__

Expand Down
137 changes: 137 additions & 0 deletions examples/multiple-projects/multiple-projects.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
Multiple Projects
=================

Awdur allows for multiple code projects to be embedded within a single documentation artifact.
Where relevant awdur's directives accept a ``:project:`` option that allow you to specify which project it should be assoicated with.

The ``awdur:project-tree`` directive accepts a project name as an argument.

Hello World
-----------

Where no project name is given, the name ``default`` will be used as... well, the default.

.. awdur:project-tree::

The code below is a valid "Hello, World!" application in Python.

.. code:: python
:filename: hello.py

print("Hello, World!")


Shapes
------

This project deals with geometric shapes

.. awdur:project-tree:: shapes

Setup
^^^^^

The following template is used when defining an elisp module in this project.

.. awdur:template:: elisp-module
:project: shapes

{% extends "default" %}

{% block header %};;; {{ path.name }} --- Description

{% endblock %}

{% block footer %}

(provide '{{ path.stem }}){% endblock %}

Triangles
^^^^^^^^^

The code block below defines a function to compute the area of a triangle.

.. code:: emacs-lisp
:project: shapes
:filename: triangle.el
:template: elisp-module

(defun triangle-area (a b c)
(* 0.5 a b))

And this defines a function to compute the perimeter, note that now we've the template once we don't need to repeat it.

.. code:: emacs-lisp
:project: shapes
:filename: triangle.el

(defun triangle-perimeter (a b c)
(+ a b c))

Rectangles
^^^^^^^^^^

The following code deals with rectangles.

.. code:: emacs-lisp
:project: shapes
:filename: rectangle.el
:template: elisp-module

(defun rectangle-area (w h)
(* w h))

(defun rectangle-perimeter (w h)
(* 2 (+ w h))

Math
----

This project deals with number sequences

.. awdur:project-tree:: math

Fibbonacci
^^^^^^^^^^

Below is a function to calculate the n\ :sup:`th` Fibonacci number

.. code:: python
:project: math
:filename: fib.py

def fib(n):
if n == 0 or n == 1:
return n
return fib(n-1) + fib(n - 2)

Which we can then use to print the first 10 Fibonacci numbers

.. code:: python
:project: math
:filename: fib.py

nums = [str(fib(n)) for n in range(1, 11)]
print(f"The first 10 Fibonacci numbers are: {', '.join(nums)}")


Square Numbers
^^^^^^^^^^^^^^

Here is a function for calculating the square of a number

.. code:: python
:project: math
:filename: square.py

def square(n):
return n * n

Which we can then use to print the first 10 square numbers

.. code:: python
:project: math
:filename: square.py

nums = [str(square(n)) for n in range(1,11)]
print(f"The first 10 square numbers are: {', '.join(nums)}")
7 changes: 7 additions & 0 deletions lib/awdur/awdur/cli/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ def get_parser() -> argparse.ArgumentParser:
_ = extract_cmd.add_argument(
"source", type=pathlib.Path, help="the source file to extract code from"
)
_ = extract_cmd.add_argument(
"-p",
"--project",
dest="project_name",
default="default",
help="the code project to extract",
)
_ = extract_cmd.add_argument(
"-o", "--output", type=pathlib.Path, help="the location to write to"
)
Expand Down
27 changes: 22 additions & 5 deletions lib/awdur/awdur/cli/extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
from docutils.parsers import get_parser_class
from docutils.readers import get_reader_class

from awdur.project import Project
from awdur.project import ProjectManager
from awdur.writers import SourceCodeWriter


def extract(source: pathlib.Path, *, output: pathlib.Path | None = None):
def extract(
source: pathlib.Path,
*,
output: pathlib.Path | None = None,
project_name: str = "default",
):
"""Extract source code from documentation sources.

Parameters
Expand All @@ -21,6 +26,9 @@ def extract(source: pathlib.Path, *, output: pathlib.Path | None = None):

output
The location to write to

project_name
The project name to extract
"""
reader_cls = get_reader_class("standalone")
parser_cls = get_parser_class("restructuredtext")
Expand All @@ -34,10 +42,10 @@ def extract(source: pathlib.Path, *, output: pathlib.Path | None = None):
source_class=io.FileInput,
)

project = Project(default_name=source.stem)
manager = ProjectManager(default_name=source.stem)
publisher.process_programmatic_settings(
settings_spec=None,
settings_overrides={"awdur_project": project},
settings_overrides={"awdur_project_manager": manager},
config_section=None,
)

Expand All @@ -48,9 +56,18 @@ def extract(source: pathlib.Path, *, output: pathlib.Path | None = None):
if document.reporter.max_level >= 3:
return 1

if project_name not in manager:
raise ValueError(f"Project {project_name!r} is not defined")

if output is None:
output = source.with_suffix("")
# Use the project name if it's not the default one
if project_name != "default":
output = source.with_name(project_name)
else:
output = source.with_suffix("")

if output == source:
raise ValueError("Please provide a destination")

project = manager[project_name]
project.export(output)
11 changes: 7 additions & 4 deletions lib/awdur/awdur/cli/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@
from docutils.parsers import get_parser_class
from docutils.readers import get_reader_class

from awdur.project import Project
from awdur.project import ProjectManager
from awdur.writers import HTMLWriter


def render(source: pathlib.Path, output: pathlib.Path | None):
def render(source: pathlib.Path, *, output: pathlib.Path | None = None):
"""Render sources to produce a documentation artifact.

Parameters
----------
source
The source file to build.

output
The output to write to
"""
reader_cls = get_reader_class("standalone")
parser_cls = get_parser_class("restructuredtext")
Expand All @@ -35,10 +38,10 @@ def render(source: pathlib.Path, output: pathlib.Path | None):

# It looks like the easiest way to inject additional stylesheets, rather than replace the defaults
# is to first let docutils initialize the default settings, then append the extra file(s) to the list
project = Project(default_name=source.stem)
project = ProjectManager(default_name=source.stem)
publisher.process_programmatic_settings(
settings_spec=None,
settings_overrides={"awdur_project": project},
settings_overrides={"awdur_project_manager": project},
config_section=None,
)

Expand Down
14 changes: 11 additions & 3 deletions lib/awdur/awdur/directives.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import annotations

import typing

from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
Expand All @@ -21,6 +19,7 @@ def run(self):
return result

code.attributes["kind"] = "code"
code.attributes["project"] = self.options.get("project", "default")
code.attributes["template"] = self.options.get("template")

if (filename := self.options.get("filename")) is not None:
Expand All @@ -46,6 +45,7 @@ def run(self):
"option_spec": {
**base.option_spec,
"filename": directives.uri,
"project": directives.unchanged,
"template": directives.unchanged,
},
"run": run,
Expand All @@ -70,6 +70,7 @@ def run(self):

code.attributes["kind"] = "template"
code.attributes["name"] = template_name
code.attributes["project"] = self.options.get("project", "default")

# Add a header to the code block indicating where it is being saved to.
header = nodes.container(
Expand All @@ -91,6 +92,7 @@ def run(self):
"required_arguments": 1,
"option_spec": {
**base.option_spec,
"project": directives.unchanged,
},
"run": run,
},
Expand All @@ -101,9 +103,15 @@ class ProjectTreeDirective(Directive):
"""A directive that inserts a project file browser into the page."""

required_arguments = 0
optional_arguments = 1

def run(self):
return [project_tree("", name="default")]
if len(self.arguments) > 0:
name = self.arguments[0]
else:
name = "default"

return [project_tree(name=name)]


class project_tree(nodes.General, nodes.Element):
Expand Down
17 changes: 17 additions & 0 deletions lib/awdur/awdur/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ def get_source(
return (source, None, None)


class ProjectManager:
"""Manages multiple Project instances."""

def __init__(self, *, default_name: str = "out"):
self.default_name: str = default_name
self.projects: dict[str, Project] = {}

def __contains__(self, key: str):
return key in self.projects

def __getitem__(self, key: str):
if key not in self.projects:
self.projects[key] = Project(default_name=self.default_name)

return self.projects[key]


class Project:
"""An awdur project."""

Expand Down
Loading