2011-12-07 15:05:02 +01:00
|
|
|
#include "doxygengenerator.h"
|
|
|
|
|
|
|
|
|
|
#include <cplusplus/SimpleLexer.h>
|
|
|
|
|
#include <cplusplus/BackwardsScanner.h>
|
|
|
|
|
#include <cplusplus/Token.h>
|
|
|
|
|
#include <cplusplus/TranslationUnit.h>
|
|
|
|
|
#include <cplusplus/AST.h>
|
|
|
|
|
#include <cplusplus/Symbols.h>
|
|
|
|
|
#include <cplusplus/CppDocument.h>
|
|
|
|
|
#include <cplusplus/Scope.h>
|
|
|
|
|
#include <cplusplus/LookupContext.h>
|
|
|
|
|
|
2012-01-23 17:44:49 +01:00
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QStringBuilder>
|
|
|
|
|
#include <QTextDocument>
|
2011-12-07 15:05:02 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
|
|
using namespace CppTools;
|
|
|
|
|
using namespace CPlusPlus;
|
|
|
|
|
|
|
|
|
|
DoxygenGenerator::DoxygenGenerator()
|
|
|
|
|
: m_addLeadingAsterisks(true)
|
|
|
|
|
, m_generateBrief(true)
|
|
|
|
|
, m_startComment(true)
|
|
|
|
|
, m_style(QtStyle)
|
|
|
|
|
, m_commentOffset(0)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::setStyle(DocumentationStyle style)
|
|
|
|
|
{
|
|
|
|
|
m_style = style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::setStartComment(bool start)
|
|
|
|
|
{
|
|
|
|
|
m_startComment = start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::setGenerateBrief(bool get)
|
|
|
|
|
{
|
|
|
|
|
m_generateBrief = get;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::setAddLeadingAsterisks(bool add)
|
|
|
|
|
{
|
|
|
|
|
m_addLeadingAsterisks = add;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DoxygenGenerator::generate(QTextCursor cursor)
|
|
|
|
|
{
|
|
|
|
|
const QChar &c = cursor.document()->characterAt(cursor.position());
|
|
|
|
|
if (!c.isLetter() && c != QLatin1Char('_'))
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
|
|
// Try to find what would be the declaration we are interested in.
|
|
|
|
|
SimpleLexer lexer;
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
const QString &text = block.text();
|
|
|
|
|
const QList<Token> &tks = lexer(text);
|
|
|
|
|
foreach (const Token &tk, tks) {
|
|
|
|
|
if (tk.is(T_SEMICOLON) || tk.is(T_LBRACE)) {
|
|
|
|
|
// No need to continue beyond this, we might already have something meaningful.
|
|
|
|
|
cursor.setPosition(block.position() + tk.end(), QTextCursor::KeepAnchor);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cursor.hasSelection())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cursor.hasSelection())
|
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
|
|
QString declCandidate = cursor.selectedText();
|
|
|
|
|
declCandidate.replace(QChar::ParagraphSeparator, QLatin1Char('\n'));
|
|
|
|
|
|
|
|
|
|
// Let's append a closing brace in the case we got content like 'class MyType {'
|
|
|
|
|
if (declCandidate.endsWith(QLatin1Char('{')))
|
|
|
|
|
declCandidate.append(QLatin1Char('}'));
|
|
|
|
|
|
|
|
|
|
Document::Ptr doc = Document::create(QLatin1String("<doxygen>"));
|
2012-01-12 17:53:56 +01:00
|
|
|
doc->setUtf8Source(declCandidate.toUtf8());
|
2011-12-07 15:05:02 +01:00
|
|
|
doc->parse(Document::ParseDeclaration);
|
|
|
|
|
doc->check(Document::FastCheck);
|
|
|
|
|
|
|
|
|
|
if (!doc->translationUnit()
|
|
|
|
|
|| !doc->translationUnit()->ast()
|
|
|
|
|
|| !doc->translationUnit()->ast()->asDeclaration()) {
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return generate(cursor, doc->translationUnit()->ast()->asDeclaration());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DoxygenGenerator::generate(QTextCursor cursor, CPlusPlus::DeclarationAST *decl)
|
|
|
|
|
{
|
|
|
|
|
SpecifierAST *spec = 0;
|
|
|
|
|
DeclaratorAST *decltr = 0;
|
|
|
|
|
if (SimpleDeclarationAST *simpleDecl = decl->asSimpleDeclaration()) {
|
|
|
|
|
if (simpleDecl->declarator_list
|
|
|
|
|
&& simpleDecl->declarator_list->value) {
|
|
|
|
|
decltr = simpleDecl->declarator_list->value;
|
|
|
|
|
} else if (simpleDecl->decl_specifier_list
|
|
|
|
|
&& simpleDecl->decl_specifier_list->value) {
|
|
|
|
|
spec = simpleDecl->decl_specifier_list->value;
|
|
|
|
|
}
|
|
|
|
|
} else if (FunctionDefinitionAST * defDecl = decl->asFunctionDefinition()) {
|
|
|
|
|
decltr = defDecl->declarator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assignCommentOffset(cursor);
|
|
|
|
|
|
|
|
|
|
QString comment;
|
|
|
|
|
if (m_startComment)
|
|
|
|
|
writeStart(&comment);
|
|
|
|
|
writeNewLine(&comment);
|
|
|
|
|
writeContinuation(&comment);
|
|
|
|
|
|
|
|
|
|
if (decltr
|
|
|
|
|
&& decltr->core_declarator
|
|
|
|
|
&& decltr->core_declarator->asDeclaratorId()
|
|
|
|
|
&& decltr->core_declarator->asDeclaratorId()->name) {
|
|
|
|
|
CoreDeclaratorAST *coreDecl = decltr->core_declarator;
|
|
|
|
|
if (m_generateBrief)
|
|
|
|
|
writeBrief(&comment, m_printer.prettyName(coreDecl->asDeclaratorId()->name->name));
|
|
|
|
|
else
|
|
|
|
|
writeNewLine(&comment);
|
|
|
|
|
|
|
|
|
|
if (decltr->postfix_declarator_list
|
|
|
|
|
&& decltr->postfix_declarator_list->value
|
|
|
|
|
&& decltr->postfix_declarator_list->value->asFunctionDeclarator()) {
|
|
|
|
|
FunctionDeclaratorAST *funcDecltr =
|
|
|
|
|
decltr->postfix_declarator_list->value->asFunctionDeclarator();
|
|
|
|
|
if (funcDecltr->parameter_declaration_clause
|
|
|
|
|
&& funcDecltr->parameter_declaration_clause->parameter_declaration_list) {
|
|
|
|
|
for (ParameterDeclarationListAST *it =
|
|
|
|
|
funcDecltr->parameter_declaration_clause->parameter_declaration_list;
|
|
|
|
|
it;
|
|
|
|
|
it = it->next) {
|
|
|
|
|
ParameterDeclarationAST *paramDecl = it->value;
|
|
|
|
|
if (paramDecl->declarator
|
|
|
|
|
&& paramDecl->declarator->core_declarator
|
|
|
|
|
&& paramDecl->declarator->core_declarator->asDeclaratorId()
|
|
|
|
|
&& paramDecl->declarator->core_declarator->asDeclaratorId()->name) {
|
|
|
|
|
DeclaratorIdAST *paramId =
|
|
|
|
|
paramDecl->declarator->core_declarator->asDeclaratorId();
|
|
|
|
|
writeContinuation(&comment);
|
|
|
|
|
writeCommand(&comment,
|
|
|
|
|
ParamCommand,
|
|
|
|
|
m_printer.prettyName(paramId->name->name));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (funcDecltr->symbol
|
|
|
|
|
&& funcDecltr->symbol->returnType().type()
|
|
|
|
|
&& !funcDecltr->symbol->returnType()->isVoidType()
|
|
|
|
|
&& !funcDecltr->symbol->returnType()->isUndefinedType()) {
|
|
|
|
|
writeContinuation(&comment);
|
|
|
|
|
writeCommand(&comment, ReturnCommand);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (spec && m_generateBrief) {
|
|
|
|
|
bool briefWritten = false;
|
|
|
|
|
if (ClassSpecifierAST *classSpec = spec->asClassSpecifier()) {
|
|
|
|
|
if (classSpec->name) {
|
|
|
|
|
QString aggregate;
|
|
|
|
|
if (classSpec->symbol->isClass())
|
|
|
|
|
aggregate = QLatin1String("class");
|
|
|
|
|
else if (classSpec->symbol->isStruct())
|
|
|
|
|
aggregate = QLatin1String("struct");
|
|
|
|
|
else
|
|
|
|
|
aggregate = QLatin1String("union");
|
|
|
|
|
writeBrief(&comment,
|
|
|
|
|
m_printer.prettyName(classSpec->name->name),
|
|
|
|
|
QLatin1String("The"),
|
|
|
|
|
aggregate);
|
|
|
|
|
briefWritten = true;
|
|
|
|
|
}
|
|
|
|
|
} else if (EnumSpecifierAST *enumSpec = spec->asEnumSpecifier()) {
|
|
|
|
|
if (enumSpec->name) {
|
|
|
|
|
writeBrief(&comment,
|
|
|
|
|
m_printer.prettyName(enumSpec->name->name),
|
|
|
|
|
QLatin1String("The"),
|
|
|
|
|
QLatin1String("enum"));
|
|
|
|
|
briefWritten = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!briefWritten)
|
|
|
|
|
writeNewLine(&comment);
|
|
|
|
|
} else {
|
|
|
|
|
writeNewLine(&comment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeEnd(&comment);
|
|
|
|
|
|
|
|
|
|
return comment;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QChar DoxygenGenerator::startMark() const
|
|
|
|
|
{
|
|
|
|
|
if (m_style == QtStyle)
|
|
|
|
|
return QLatin1Char('!');
|
|
|
|
|
return QLatin1Char('*');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QChar DoxygenGenerator::styleMark() const
|
|
|
|
|
{
|
|
|
|
|
if (m_style == QtStyle)
|
|
|
|
|
return QLatin1Char('\\');
|
|
|
|
|
return QLatin1Char('@');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DoxygenGenerator::commandSpelling(Command command)
|
|
|
|
|
{
|
|
|
|
|
if (command == ParamCommand)
|
|
|
|
|
return QLatin1String("param ");
|
|
|
|
|
if (command == ReturnCommand)
|
|
|
|
|
return QLatin1String("return ");
|
|
|
|
|
|
2012-01-23 17:44:49 +01:00
|
|
|
QTC_ASSERT(command == BriefCommand, return QString());
|
2011-12-07 15:05:02 +01:00
|
|
|
return QLatin1String("brief ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeStart(QString *comment) const
|
|
|
|
|
{
|
|
|
|
|
comment->append(offsetString() % QLatin1String("/*") % startMark());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeEnd(QString *comment) const
|
|
|
|
|
{
|
|
|
|
|
comment->append(offsetString() % QLatin1String(" */"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeContinuation(QString *comment) const
|
|
|
|
|
{
|
|
|
|
|
if (m_addLeadingAsterisks)
|
|
|
|
|
comment->append(offsetString() % QLatin1String(" *"));
|
|
|
|
|
else
|
|
|
|
|
comment->append(offsetString() % QLatin1String(" "));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeNewLine(QString *comment) const
|
|
|
|
|
{
|
|
|
|
|
comment->append(QLatin1Char('\n'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeCommand(QString *comment,
|
|
|
|
|
Command command,
|
|
|
|
|
const QString &commandContent) const
|
|
|
|
|
{
|
|
|
|
|
comment->append(QLatin1Char(' ')
|
|
|
|
|
% styleMark()
|
|
|
|
|
% commandSpelling(command)
|
|
|
|
|
% commandContent
|
|
|
|
|
% QLatin1Char('\n'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::writeBrief(QString *comment,
|
|
|
|
|
const QString &brief,
|
|
|
|
|
const QString &prefix,
|
|
|
|
|
const QString &suffix)
|
|
|
|
|
{
|
|
|
|
|
QString content = prefix % QLatin1Char(' ') % brief % QLatin1Char(' ') % suffix;
|
|
|
|
|
writeCommand(comment, BriefCommand, content.trimmed());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DoxygenGenerator::assignCommentOffset(QTextCursor cursor)
|
|
|
|
|
{
|
|
|
|
|
if (cursor.hasSelection()) {
|
|
|
|
|
if (cursor.anchor() < cursor.position())
|
|
|
|
|
cursor.setPosition(cursor.anchor());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_commentOffset = cursor.positionInBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString DoxygenGenerator::offsetString() const
|
|
|
|
|
{
|
|
|
|
|
// Note: Currently we don't indent comments, but simply preserve them in the original
|
|
|
|
|
// relative positions. What we do here is just to make sure that such positions are correct,
|
|
|
|
|
// although they might still be wrong from an indentation point of view (for instance,
|
|
|
|
|
// using spaces instead of tabs). Therefore, the content generated should still have
|
|
|
|
|
// the indentation strings fixed.
|
|
|
|
|
|
|
|
|
|
return QString(m_commentOffset, QLatin1Char(' '));
|
|
|
|
|
}
|