No description
Find a file
2025-10-20 08:25:52 +02:00
assets initial commit 2025-09-07 15:21:51 +02:00
lib Integrate support for blocks of single line documentation comments (#1). 2025-10-20 08:17:30 +02:00
spec Integrate support for blocks of single line documentation comments (#1). 2025-10-20 08:17:30 +02:00
.gitignore Integrate support for blocks of single line documentation comments (#1). 2025-10-20 08:17:30 +02:00
ex-doc-include.gemspec Update gem version as per GemHub post 2025-10-20 08:25:52 +02:00
LICENSE initial commit 2025-09-07 15:21:51 +02:00
README.adoc Integrate support for blocks of single line documentation comments (#1). 2025-10-20 08:17:30 +02:00

ex-doc-include is an AsciiDoctor plug-in aiming to support C/C++ developers in keeping documentation as close as possible to the related code by embedding parts that naturally fit into source code comments.

The basic AsciiDoctor include directive assumes the referenced files are valid AsciiDoc and allows for cherry-picking smaller regions that match specific tags. Of course this approach is perfectly fine and works in every practical case, but still it does not leverage the structure that the included file inherently has.

In the case of external documentation for C and C++ libraries, it is sensible and fairly common to document elements placing a block comment right before it. Due to the widespread of Doxygen as documentation generator, it is very common to have those comment blocks marked with either an initial exclamation mark, or an asterisk.

/*! This is a fancy function!
Maybe it requires some more documentation details... */
std::string fancy( std::string arg);

/** This is a fancy function!
Maybe this requires some more documentation details...
... and even more...                                 */
std::string fancy( std::string arg);

The basic inclusion feature requires to mark both the start and the end of the block on a specific line. Of course those new line should be inside the function documentation block resulting in more noise in the code.

/*!
tag:doc[]
This is a fancy function!
Maybe it requires some more documentation details...
end:doc[] */
std::string fancy( std::string arg );

The idea behind ex-doc-include is to focus on the use case and leverage the language-defined block structure.

ex-doc-include implicitly use its own tags to mark the region of the file to be included. That is it will include the region between:

  • a line starting with /*! #ex-doc [1] (after leading spaces have been trimmed);

  • a line ending with */ (including any content found on the line on the left of the marker)

The allowed comment block is more concise and the tag use more clearly referred to the intent of the comment block:

/*! #ex-doc
This is a fancy function!
Maybe it requires some more documentation details... */
std::string fancy( std::string arg )

single-line comment format

In addition to block comments, ex-doc-include also supports sequences of single-line comments using the /// prefix.

/// #ex-doc [.api-ref]
/// int add( int x, int y)
///
/// Somma due interi
int add( int x, int y )
{
  return x + y;
}

The single-line comment block:

  • Starts with a line containing /// #ex-doc (after leading spaces have been trimmed)

  • Continues with consecutive lines starting with ///

  • Ends when a line without /// is encountered

Both block and single-line formats can be used within the same file.

installation

ex-doc-include is distributed as a Gem. As usual to install it you can run: gem install ex-doc-include.

use

When you want to use AsciiDoctor with the ex-doc-include plug-in enabled, add -r ex-doc-include to your command line; as in:

asciidoctor test/sample.adoc -r ex-doc-include --out sample.html

modified include directive

The ex-doc-include plug-in goes into action when one of the following file type gets included in the master document:

  • .h

  • .hpp

  • .cpp

By default, the included document is linked rather than expanded at the inclusion location. That is, another document is created for you and a link to it is added in place of the original include. The default behavior can be overridden setting the expand attribute.

== Master document

Some initial notes..


=== features

include:features/feature_a.hpp[link="feature a", articles-dir=features] (1) (2)
include:features/feature_a.hpp[expand=true]
1 The optional link attribute provides the text that will be used for the link.
2 The optional articles-dir attribute controls the target directory where the linked documents are rendered. It is relative to the folder of the main output document.
The linked document borrows all the attributes from the master document.

more features

first block role

Perhaps the comment block provides to the including document a block with specific semantic and needs to carry it in the rendered document in a way that is section-unrelated.

To fulfill this purpose, ex-doc-include allows the first included AsciiDoctor paragraph to be optionally given a role indicated directly in the block open sequence as in: /*! #ex-doc [<_user defined role_>] for block comments or /// #ex-doc [<_user defined role_>] for single-line comments.

/*! #ex-doc [.api-ref]
OutIt stlext::transform( const ContainerT& c, OutIt out, F func)

Applies the provided transformation function to each of the elements in the
container and puts the results in the provided output iterator.
...                                                                           */
template <typename ContainerT, typename OutIt, typename F>
OutIt transform( const ContainerT& c, OutIt out, F func )
{
  return std::transform( std::begin( c ), std::end( c ), out, func );
}

The same can be achieved using single-line comments:

/// #ex-doc [.api-ref]
/// OutIt stlext::transform( const ContainerT& c, OutIt out, F func)
///
/// Applies the provided transformation function to each of the elements in the
/// container and puts the results in the provided output iterator.
template <typename ContainerT, typename OutIt, typename F>
OutIt transform( const ContainerT& c, OutIt out, F func )
{
  return std::transform( std::begin( c ), std::end( c ), out, func );
}

indentation

Perhaps the subject of the included documentation is indented and is visually pleasing to have the documentation comment indented at the same level.

In the master document, indentation carries the specific semantics assigned by AsciiDoctor; therefore, the block must be realigned, while preserving any intentional indentation provided as part of the AsciiDoctor content.

To help users document their code naturally and keep it visually clear even within the source, ex-doc-include determines the indentation level by referencing the position of the block open sequence.

namespace details{

  // ...

  class Storage {

    // ...

    /*! #ex-doc [.method] (1)
    bool contains( PredT p ) const

    Checks whether the range contains an element satisfying the predicate
    received.                                                                 */
    template <typename F>
    bool contains( F f ) const {
      return std::find_if( m_begin, m_end, f ) != m_end;
    }

  }

} // namespace details
1 Block recognized at 4 space indentation; the content will be unindented accordingly.

The same indentation handling applies to single-line comments:

namespace details{

  // ...

  class Storage {

    // ...

    /// #ex-doc [.method]
    /// bool contains( PredT p ) const
    ///
    /// Checks whether the range contains an element satisfying the predicate
    /// received.
    template <typename F>
    bool contains( F f ) const {
      return std::find_if( m_begin, m_end, f ) != m_end;
    }

  }

} // namespace details

assumption when referencing documents

The references used when including documents processed by ex-doc-include are interpreted as relative to the master document.

Assuming that you have a project structure like:

.
├── include
│   └── your-lib
│       ├── simple.h
│       └── transitive.h
└── master.adoc

With the master document including content from simple.h and transitively from transitive.h. The reference from the master document will take the form of: include::include/your-lib/simple.h[expand=true], while content in simple may refer to transitive with: include::include/your-lib/transitive.h.

Notice then that the references are not relative to the file you are working on, which may be undesirable. Instead of attempting a generalized solution to this notoriously complex problem, ex-doc-include aims for consistency. It lets users handle path resolution, for example via an attribute conventionally set by the master document that includes documentation comments.

master.adoc
== Master document

Content of the master document

Unresolved directive in <stdin> - include::include/your-lib/simple.h[expand=true]
simple.h
/*! #ex-doc

=== Simple.h

Some more content extracted from the header file.

include::{your-lib-res}/transitive.h[expand=true]                             /*

destination folder for linked documents

When an included ex-doc-include document is linked rather than expanded in place, the referred document is created by default at the same location as the master document. This behavior can be altered providing the additional attribute articles-dir at the inclusion point.


1. block open sequence