/mrst

The Ms.Pacman of Rst

Primary LanguagePython

M Rst

https://travis-ci.org/TimSimpson/mrst.svg?branch=master

M Rst makes it possible to avoid duplicating the docs you already have in C++ source files (such as header files making up your project's interface or .cpp files used for example code) or in Markdown files (which are preferable to .rst when repositories are being viewed in GitHub).

It does this by processing special .mrst files containing Ms. Rst directives and generating a new directory full of plain ReStructured Text files to be used by Sphinx.

How to run it

First, make sure the following programs are on your path:

Now, take a typical Sphinx project, which will consist of a source directory and a Make file.

Open up the conf.py file used by Sphinx (it's in your source directory) and change the variable source_suffix by appending .mrst to the list.

Now, instead of using the make file to build sphinx docs, run this:

mrst --source source --output output

mrst will invoke Sphinx for you. To avoid this and just generate the intermediate project, add the flag --skip-sphix.

By default, all docs in source gets copied into output/gen which is will then be used by Sphinx (Sphinx will put it's output in output/build).

Using Mrst Files

Files ending with mrst stand for M ReStructured Text. There's a custom parser for them which writes them to the generated doc directory.

These files are just like mrst files but with the ~dumpfile directive which takes a relative file and includes it inside of the original document.

If the given file being dumped is markdown, it's first translated using pandoc. If the file is C++, it's translated into ReStructured Text using rules described below. In either case the resulting ReStructured Text is dumped into the given file at the point where ~dumpfile appears.

The ~dumpfile directive

The ~dumpfile directive looks like this:

~dumpfile "file" <start> <end> <indent> <section>

~dumpfile takes several directives by position or via keywords. The following two examples are equivalent:

~dumpfile "file" 0 10 4 ~

~dumpfile "file" end=10 start=0 section=~ indent=4

Note that the keyword argument syntax requires no spaces between the equal sign.

start and indent default to 0 if not set. end defaults to the end of the file, which can also be specified explicitly using ~.

This next snippet means "include all text from line 12 until the end of the file and indent everything by 4 characters:

~dumpfile "file" 12 ~ 4

This simply dumps the entire file:

~dumpfile "file"

There's also a section keyword argument, explained below.

Markdown Conversion

Markdown translation is provided courtesy of Pandoc. A subset of the desired Markdown file is generated in a temporary directory (so that start and end will work) and Pandoc is called to produce a file which is read and included where ~dumpfile is seen.

One gotcha is that currently the section headers from Markdown docs are brought in as is, which may not work in the context of a larger rst project.

For example, you may want to dump the contents of the README.md file at the root of your git repo into your Sphinx generated documentation. However, if this file begins with a top header (such as # My Library, which it almost certainly does) that will translate to a top level section header in your generated RsT project, which will probably mess up how your document is nested.

This can be avoided by simply skipping the first line (which contains the section header) by setting the start argument to 2 or more.

C++ to ReStructured Text Conversion

The parser reads C++ code and ignores everything until it sees special comment syntax it likes, which looks like this:

// ---------------------------------------------

The important bit is that there are two slashes, a space, and then at least two hyphens (in other words, // --).

Everything after that is included in the rst file until it sees another similar line.

Here's an example:

// --------------------------------------------
// Section Header
// ===========================================
// This describes something important.
// -------------------------------------------/

This gets translated to the following rst:

Section Header
==============
This describes something important.

Note the last C++ comment is a line full of dashes ending with /: that's important. It tells the translator to stop until it sees the next comment that looks like rst.

Alternatively, it's possible to make the translator scoop up actual C++ code. There's two ways to do this.

The first is to use the special directive // ~begin-code. That will tell mrst to put all the code below as a C++ snippet in the rst file until it gets to // ~end-code. For example:

// ~begin-code

int main() {
    // this documents how you can have a signature for main like this
    // on some platforms
}

// ~end-code

becomes:

.. code-block:: c++

    int main() {
        // this documents how you can have a signature for main like this
        // on some platforms
    }

Instead of // ~end-doc you can also just give it a comment like described above, like this:

// ------------------------------------------------------------------
// get_customer_id
// ------------------------------------------------------------------
//      Grabs a customer.
// ------------------------------------------------------------------
template<typename Customer>
inline int get_customer_id(Customer & c) {
    return get_id(c);
}

// ------------------------------------------------------------------
// charge_customer
// ------------------------------------------------------------------
//      Used to charge a customer.
// ------------------------------------------------------------------
void charge_customer(int c_id, double money);

becomes:

get_customer_id
---------------
Grabs a customer.

.. code-block:: c++

    template<typename Customer>
    inline int get_customer_id(Customer & c) {
        return get_id(c);
    }

charge_customer
---------------
Used to charge a customer.

.. code-block:: c++

    void charge_customer(int c_id, double money);

This behavior of treating the end of the special comment block like an // ~end-doc is to make the pattern seen above easier.

If you don't want to consume the code below a special comment, end it with // ---/ as seen above.

Here's an example of a class being included in rst:

// --------------------------------------------
// class RenderPlatform
// --------------------------------------------
//      A platform for renderers.
//      Note how this text will get de-dented.
// --------------------------------------------

class RenderPlatform {
public:
    virtual ~RenderPlatform();
    virtual const char * get_name() const;
    virtual const int priority() const;
};
// ~end-doc

the above turns into:

class RenderPlatform
--------------------
A platform for renderers.
Note how this text will get de-dented.

.. code-block:: c++

       class RenderPlatform {
        public:
            virtual ~RenderPlatform();
            virtual const char * get_name() const;
            virtual const int priority() const;
        };

Section headers

When parsing C++ files it's sometimes necessary to tell the C++ to rst generator what section header the incoming dumped rst should be nested under. The expected order of the section headers can be found in the HEADERS var defined in cpp_rst.py (note: Sphinx lets you use an arbitrary order, but you have to use the same order mrst uses in order to chnage the section headers found in C++ files).

Let's say you want the documentation in a header file to appear under an existing section header in your rst file. You'd do this:

namespace blah
~~~~~~~~~~~~~~

~dumpfile "blah/util.hpp" section=~

This would tell the C++ rst translator to start the next section after ~, meaning the first section header would be generated as ^.