Add documentation generator example

This commit is contained in:
Jonathan Müller 2017-10-31 18:05:27 +01:00
commit 6a54fdd277
2 changed files with 161 additions and 0 deletions

View file

@ -8,3 +8,4 @@ function(_cppast_example name)
endfunction()
_cppast_example(ast_printer)
_cppast_example(documentation_generator)

View file

@ -0,0 +1,160 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
/// \file
/// A primitive documentation generator.
#include <cppast/code_generator.hpp> // code_generator, generate_code()
#include <cppast/visitor.hpp> // visit()
#include "example_parser.hpp"
// don't show in code generation
bool is_excluded_synopsis(const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access)
{
// exclude privates and those marked for exclusion
return access == cppast::cpp_private || cppast::has_attribute(e, "documentation::exclude");
}
// don't show in documentation
bool is_excluded_documentation(const cppast::cpp_entity& e,
const cppast::cpp_access_specifier_kind access)
{
// exclude uninteresting entities
return e.kind() == cppast::cpp_entity_kind::access_specifier_t
|| e.kind() == cppast::cpp_entity_kind::using_declaration_t
|| e.kind() == cppast::cpp_entity_kind::using_directive_t
|| e.kind() == cppast::cpp_entity_kind::static_assert_t
|| e.kind() == cppast::cpp_entity_kind::include_directive_t
// and all excluded in synopsis
|| is_excluded_synopsis(e, access);
}
// generates synopsis of an entity
std::string generate_synopsis(const cppast::cpp_entity& e)
{
// the generator for the synopsis
class synopsis_generator final : public cppast::code_generator
{
public:
// get the resulting string
std::string result()
{
return std::move(str_);
}
private:
// whether or not the entity is the main entity that is being documented
bool is_main_entity(const cppast::cpp_entity& e)
{
if (cppast::is_templated(e) || cppast::is_friended(e))
// need to ask the real entity
return is_main_entity(e.parent().value());
else
return &e == &this->main_entity();
}
// get some nicer formatting
cppast::formatting do_get_formatting() const override
{
return cppast::formatting_flags::brace_nl | cppast::formatting_flags::comma_ws
| cppast::formatting_flags::operator_ws;
}
// calculate generation options
generation_options do_get_options(const cppast::cpp_entity& e,
cppast::cpp_access_specifier_kind access) override
{
if (is_excluded_synopsis(e, access))
return cppast::code_generator::exclude;
else if (!is_main_entity(e))
// only generation declaration for the non-documented entity
return cppast::code_generator::declaration;
else
// default options
return {};
}
// update indendation level
void do_indent() override
{
++indent_;
}
void do_unindent() override
{
if (indent_)
--indent_;
}
// write specified tokens
// need to change indentation for each newline
void do_write_token_seq(cppast::string_view tokens) override
{
if (was_newline_)
{
str_ += std::string(indent_ * 2u, ' ');
was_newline_ = false;
}
str_ += tokens.c_str();
}
// write + remember newline
void do_write_newline() override
{
str_ += "\n";
was_newline_ = true;
}
std::string str_;
unsigned indent_ = 0;
bool was_newline_ = false;
} generator;
cppast::generate_code(generator, e);
return generator.result();
}
void generate_documentation(const cppast::cpp_file& file)
{
// visit each entity
cppast::visit(file,
[](const cppast::cpp_entity& e, cppast::cpp_access_specifier_kind access) {
if (is_excluded_documentation(e, access))
// exclude this and all children
return cppast::visit_filter::exclude_and_children;
else if (cppast::is_templated(e) || cppast::is_friended(e))
// continue on with children for a dummy entity
return cppast::visit_filter::exclude;
else
return cppast::visit_filter::include;
},
[](const cppast::cpp_entity& e, const cppast::visitor_info& info) {
if (info.is_old_entity())
// already done
return;
// print name
std::cout << "## " << cppast::to_string(e.kind()) << " '" << e.name()
<< "'\n";
std::cout << '\n';
// print synopsis
std::cout << "```\n";
std::cout << generate_synopsis(e);
std::cout << "```\n\n";
// print documentation comment
if (e.comment())
std::cout << e.comment().value() << '\n';
// print separator
std::cout << "\n---\n\n";
});
std::cout << "\n\n";
}
int main(int argc, char* argv[])
{
return example_main(argc, argv, {}, &generate_documentation);
}