Commit 2b2ba298 authored by Nikolai Kosjar's avatar Nikolai Kosjar

CppEditor: Generate doxygen comments for functions with macros

...at least for object-like macros. This handles the common case where a
macro before the function signature annotates the DLL import/export.

Task-number: QTCREATORBUG-15819
Change-Id: I79f22508188019402fb7345222408aaf90106f20
Reviewed-by: default avatarErik Verbruggen <erik.verbruggen@theqtcompany.com>
parent f99f5dcd
......@@ -757,8 +757,23 @@ void Snapshot::insert(Document::Ptr doc)
}
}
static QList<Macro> macrosDefinedUntilLine(const QList<Macro> &macros, int line)
{
QList<Macro> filtered;
foreach (const Macro &macro, macros) {
if (macro.line() <= unsigned(line))
filtered.append(macro);
else
break;
}
return filtered;
}
Document::Ptr Snapshot::preprocessedDocument(const QByteArray &source,
const Utils::FileName &fileName) const
const Utils::FileName &fileName,
int withDefinedMacrosFromDocumentUntilLine) const
{
Document::Ptr newDoc = Document::create(fileName.toString());
if (Document::Ptr thisDocument = document(fileName)) {
......@@ -768,10 +783,15 @@ Document::Ptr Snapshot::preprocessedDocument(const QByteArray &source,
newDoc->_resolvedIncludes = thisDocument->_resolvedIncludes;
newDoc->_unresolvedIncludes = thisDocument->_unresolvedIncludes;
newDoc->setLanguageFeatures(thisDocument->languageFeatures());
if (withDefinedMacrosFromDocumentUntilLine != -1) {
newDoc->_definedMacros = macrosDefinedUntilLine(thisDocument->_definedMacros,
withDefinedMacrosFromDocumentUntilLine);
}
}
FastPreprocessor pp(*this);
const QByteArray preprocessedCode = pp.run(newDoc, source);
const bool mergeDefinedMacrosOfDocument = !newDoc->_definedMacros.isEmpty();
const QByteArray preprocessedCode = pp.run(newDoc, source, mergeDefinedMacrosOfDocument);
newDoc->setUtf8Source(preprocessedCode);
return newDoc;
}
......
......@@ -425,10 +425,16 @@ public:
Snapshot simplified(Document::Ptr doc) const;
Document::Ptr preprocessedDocument(const QByteArray &source,
const Utils::FileName &fileName) const;
const Utils::FileName &fileName,
int withDefinedMacrosFromDocumentUntilLine = -1) const;
Document::Ptr preprocessedDocument(const QByteArray &source,
const QString &fileName) const
{ return preprocessedDocument(source, Utils::FileName::fromString(fileName)); }
const QString &fileName,
int withDefinedMacrosFromDocumentUntilLine = -1) const
{
return preprocessedDocument(source,
Utils::FileName::fromString(fileName),
withDefinedMacrosFromDocumentUntilLine);
}
Document::Ptr documentFromSource(const QByteArray &preprocessedDocument,
const QString &fileName) const;
......
......@@ -38,7 +38,9 @@ FastPreprocessor::FastPreprocessor(const Snapshot &snapshot)
, _addIncludesToCurrentDoc(false)
{ }
QByteArray FastPreprocessor::run(Document::Ptr newDoc, const QByteArray &source)
QByteArray FastPreprocessor::run(Document::Ptr newDoc,
const QByteArray &source,
bool mergeDefinedMacrosOfDocument)
{
std::swap(newDoc, _currentDoc);
_addIncludesToCurrentDoc = _currentDoc->resolvedIncludes().isEmpty()
......@@ -57,6 +59,9 @@ QByteArray FastPreprocessor::run(Document::Ptr newDoc, const QByteArray &source)
foreach (const Document::Include &i, doc->resolvedIncludes())
mergeEnvironment(i.resolvedFileName());
if (mergeDefinedMacrosOfDocument)
_env.addMacros(_currentDoc->definedMacros());
}
const QByteArray preprocessed = _preproc.run(fileName, source);
......
......@@ -51,7 +51,9 @@ class CPLUSPLUS_EXPORT FastPreprocessor: public Client
public:
FastPreprocessor(const Snapshot &snapshot);
QByteArray run(Document::Ptr newDoc, const QByteArray &source);
QByteArray run(Document::Ptr newDoc,
const QByteArray &source,
bool mergeDefinedMacrosOfDocument = false);
// CPlusPlus::Client
virtual void sourceNeeded(unsigned line, const QString &fileName, IncludeType mode,
......
......@@ -268,7 +268,8 @@ bool handleDoxygenContinuation(QTextCursor &cursor,
namespace CppEditor {
namespace Internal {
bool trySplitComment(TextEditor::TextEditorWidget *editorWidget)
bool trySplitComment(TextEditor::TextEditorWidget *editorWidget,
const CPlusPlus::Snapshot &snapshot)
{
const CommentsSettings &settings = CppToolsSettings::instance()->commentsSettings();
if (!settings.m_enableDoxygen && !settings.m_leadingAsterisks)
......@@ -310,7 +311,9 @@ bool trySplitComment(TextEditor::TextEditorWidget *editorWidget)
}
if (!cursor.atEnd()) {
const QString &comment = doxygen.generate(cursor);
const QString &comment = doxygen.generate(cursor,
snapshot,
editorWidget->textDocument()->filePath());
if (!comment.isEmpty()) {
cursor.beginEditBlock();
cursor.setPosition(pos);
......
......@@ -29,11 +29,13 @@
#include "cppeditor_global.h"
namespace TextEditor { class TextEditorWidget; }
namespace CPlusPlus { class Snapshot; }
namespace CppEditor {
namespace Internal {
bool trySplitComment(TextEditor::TextEditorWidget *editorWidget);
bool trySplitComment(TextEditor::TextEditorWidget *editorWidget,
const CPlusPlus::Snapshot &snapshot);
} // namespace Internal
} // namespace CppEditor
......
......@@ -281,6 +281,18 @@ void DoxygenTest::testBasic_data()
"*foo /*\n"
" \n"
);
QTest::newRow("withMacroFromDocumentBeforeFunction") << _(
"#define API\n"
"/**|\n"
"API void f();\n"
) << _(
"#define API\n"
"/**\n"
" * @brief f\n"
" */\n"
"API void f();\n"
);
}
void DoxygenTest::testBasic()
......@@ -290,6 +302,25 @@ void DoxygenTest::testBasic()
runTest(given, expected);
}
void DoxygenTest::testWithMacroFromHeaderBeforeFunction()
{
const QByteArray given =
"#include \"header.h\"\n"
"/**|\n"
"API void f();\n";
const QByteArray expected =
"#include \"header.h\"\n"
"/**\n"
" * @brief f\n"
" */\n"
"API void f();\n";
const TestDocument headerDocumentDefiningMacro("header.h", "#define API\n");
runTest(given, expected, /*settings=*/ 0, { headerDocumentDefiningMacro });
}
void DoxygenTest::testNoLeadingAsterisks_data()
{
QTest::addColumn<QByteArray>("given");
......@@ -323,8 +354,10 @@ void DoxygenTest::verifyCleanState() const
}
/// The '|' in the input denotes the cursor position.
void DoxygenTest::runTest(const QByteArray &original, const QByteArray &expected,
CppTools::CommentsSettings *settings)
void DoxygenTest::runTest(const QByteArray &original,
const QByteArray &expected,
CppTools::CommentsSettings *settings,
const TestDocuments &includedHeaderDocuments)
{
// Write files to disk
CppTools::Tests::TemporaryDir temporaryDir;
......@@ -334,6 +367,10 @@ void DoxygenTest::runTest(const QByteArray &original, const QByteArray &expected
testDocument.m_source.remove(testDocument.m_cursorPosition, 1);
testDocument.setBaseDirectory(temporaryDir.path());
QVERIFY(testDocument.writeToDisk());
foreach (TestDocument testDocument, includedHeaderDocuments) {
testDocument.setBaseDirectory(temporaryDir.path());
QVERIFY(testDocument.writeToDisk());
}
// Update Code Model
QVERIFY(TestCase::parseFiles(testDocument.filePath()));
......
......@@ -26,6 +26,8 @@
#ifndef CPPDOXYGEN_TEST_H
#define CPPDOXYGEN_TEST_H
#include "cppeditortestcase.h"
#include <cpptools/commentssettings.h>
#include <QObject>
......@@ -48,13 +50,17 @@ private slots:
void testBasic_data();
void testBasic();
void testWithMacroFromHeaderBeforeFunction();
void testNoLeadingAsterisks_data();
void testNoLeadingAsterisks();
private:
void verifyCleanState() const;
void runTest(const QByteArray &original, const QByteArray &expected,
CppTools::CommentsSettings *settings = 0);
void runTest(const QByteArray &original,
const QByteArray &expected,
CppTools::CommentsSettings *settings = 0,
const TestDocuments &includedHeaderDocuments = TestDocuments());
QScopedPointer<CppTools::CommentsSettings> oldSettings;
};
......
......@@ -69,6 +69,7 @@
#include <texteditor/refactoroverlay.h>
#include <cplusplus/ASTPath.h>
#include <cplusplus/FastPreprocessor.h>
#include <utils/qtcassert.h>
#include <QAction>
......@@ -603,7 +604,7 @@ void CppEditorWidget::keyPressEvent(QKeyEvent *e)
return;
if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
if (trySplitComment(this)) {
if (trySplitComment(this, semanticInfo().snapshot)) {
e->accept();
return;
}
......
......@@ -30,6 +30,8 @@
#include <cpptools/cpptoolstestcase.h>
#include <QVector>
namespace CppEditor {
namespace Internal {
......@@ -55,6 +57,8 @@ public:
CppEditorWidget *m_editorWidget;
};
using TestDocuments = QVector<TestDocument>;
class TestCase : public CppTools::Tests::TestCase
{
public:
......
......@@ -25,15 +25,20 @@
#include "doxygengenerator.h"
#include <texteditor/convenience.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/CppDocument.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QStringBuilder>
#include <QTextDocument>
#include <QDebug>
#include <limits>
using namespace CppTools;
using namespace CPlusPlus;
......@@ -64,8 +69,24 @@ void DoxygenGenerator::setAddLeadingAsterisks(bool add)
m_addLeadingAsterisks = add;
}
QString DoxygenGenerator::generate(QTextCursor cursor)
static int lineBeforeCursor(const QTextCursor &cursor)
{
int line, column;
const bool converted = TextEditor::Convenience::convertPosition(cursor.document(),
cursor.position(),
&line,
&column);
QTC_ASSERT(converted, return std::numeric_limits<int>::max());
return line - 1;
}
QString DoxygenGenerator::generate(QTextCursor cursor,
const CPlusPlus::Snapshot &snapshot,
const Utils::FileName &documentFilePath)
{
const QTextCursor initialCursor = cursor;
const QChar &c = cursor.document()->characterAt(cursor.position());
if (!c.isLetter() && c != QLatin1Char('_'))
return QString();
......@@ -100,8 +121,9 @@ QString DoxygenGenerator::generate(QTextCursor cursor)
if (declCandidate.endsWith(QLatin1Char('{')))
declCandidate.append(QLatin1Char('}'));
Document::Ptr doc = Document::create(QLatin1String("<doxygen>"));
doc->setUtf8Source(declCandidate.toUtf8());
Document::Ptr doc = snapshot.preprocessedDocument(declCandidate.toUtf8(),
documentFilePath,
lineBeforeCursor(initialCursor));
doc->parse(Document::ParseDeclaration);
doc->check(Document::FastCheck);
......
......@@ -33,6 +33,8 @@
QT_FORWARD_DECLARE_CLASS(QTextCursor)
namespace CPlusPlus { class DeclarationAST; }
namespace CPlusPlus { class Snapshot; }
namespace Utils { class FileName; }
namespace CppTools {
......@@ -53,7 +55,9 @@ public:
void setGenerateBrief(bool gen);
void setAddLeadingAsterisks(bool add);
QString generate(QTextCursor cursor);
QString generate(QTextCursor cursor,
const CPlusPlus::Snapshot &snapshot,
const Utils::FileName &documentFilePath);
QString generate(QTextCursor cursor, CPlusPlus::DeclarationAST *decl);
private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment