diff options
author | Leandro Melo <leandro.melo@nokia.com> | 2011-12-07 15:05:02 +0100 |
---|---|---|
committer | Leandro Melo <leandro.melo@nokia.com> | 2011-12-09 10:25:59 +0100 |
commit | beede7d7cff3e740ec0b0053ae9e382693e7f42c (patch) | |
tree | 74c0ffc3cad7569aa2f7946a2884dfd6d89c7361 /src | |
parent | 24b4c127372c6a9c496c8d0bd812696f6ad0f4d4 (diff) | |
download | qt-creator-beede7d7cff3e740ec0b0053ae9e382693e7f42c.tar.gz |
C++: Automatic Doxygen comment blocks generation
This improves our completion support for documentation
comments. It's now possible to have a Doxygen block
generated when hitting enter after a /** or /*! comment
start. A couple other related options are also available.
Task-number: QTCREATORBUG-2752
Task-number: QTCREATORBUG-3165
Change-Id: I1c81c0b4b370eb1d409ef72a9c7f22c357f202f4
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@nokia.com>
Reviewed-by: Christian Kamm <christian.d.kamm@nokia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/cppeditor/cppeditor.cpp | 106 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.h | 7 | ||||
-rw-r--r-- | src/plugins/cpptools/commentssettings.cpp | 46 | ||||
-rw-r--r-- | src/plugins/cpptools/commentssettings.h | 35 | ||||
-rw-r--r-- | src/plugins/cpptools/completionsettingspage.cpp | 43 | ||||
-rw-r--r-- | src/plugins/cpptools/completionsettingspage.h | 16 | ||||
-rw-r--r-- | src/plugins/cpptools/completionsettingspage.ui | 85 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptools.pro | 8 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptoolsplugin.cpp | 1 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptoolssettings.cpp | 25 | ||||
-rw-r--r-- | src/plugins/cpptools/cpptoolssettings.h | 6 | ||||
-rw-r--r-- | src/plugins/cpptools/doxygengenerator.cpp | 289 | ||||
-rw-r--r-- | src/plugins/cpptools/doxygengenerator.h | 71 |
13 files changed, 723 insertions, 15 deletions
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 0885be8eae..0b5f2eaea1 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -69,6 +69,8 @@ #include <cpptools/cppcodestylesettings.h> #include <cpptools/cpprefactoringchanges.h> #include <cpptools/cpptoolsreuse.h> +#include <cpptools/doxygengenerator.h> +#include <cpptools/cpptoolssettings.h> #include <coreplugin/icore.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -415,6 +417,7 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent) , m_inRenameChanged(false) , m_firstRenameChange(false) , m_objcEnabled(false) + , m_commentsSettings(CppTools::CppToolsSettings::instance()->commentsSettings()) { m_initialized = false; qRegisterMetaType<CppEditor::Internal::SemanticInfo>("CppEditor::Internal::SemanticInfo"); @@ -451,6 +454,11 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent) m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this); connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)), this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>))); + + connect(CppTools::CppToolsSettings::instance(), + SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)), + this, + SLOT(onCommentsSettingsChanged(CppTools::CommentsSettings))); } CPPEditorWidget::~CPPEditorWidget() @@ -1552,7 +1560,8 @@ void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e) void CPPEditorWidget::keyPressEvent(QKeyEvent *e) { if (m_currentRenameSelection == NoCurrentRenameSelection) { - TextEditor::BaseTextEditorWidget::keyPressEvent(e); + if (!handleDocumentationComment(e)) + TextEditor::BaseTextEditorWidget::keyPressEvent(e); return; } @@ -2239,4 +2248,99 @@ void CPPEditorWidget::abortDeclDefLink() m_declDefLink.clear(); } +bool CPPEditorWidget::handleDocumentationComment(QKeyEvent *e) +{ + if (!m_commentsSettings.m_enableDoxygen + && !m_commentsSettings.m_leadingAsterisks) { + return false; + } + + if (e->key() == Qt::Key_Return + || e->key() == Qt::Key_Enter) { + QTextCursor cursor = textCursor(); + if (!autoCompleter()->isInComment(cursor)) + return false; + + // We are interested on two particular cases: + // 1) The cursor is right after a /** or /*! and the user pressed enter. If Doxygen + // is enabled we need to generate an entire comment block. + // 2) The cursor is already in the middle of a multi-line comment and the user pressed + // enter. If leading asterisk(s) is set we need to write a comment continuation + // with those. + + if (m_commentsSettings.m_enableDoxygen + && cursor.positionInBlock() >= 3) { + const int pos = cursor.position(); + if (characterAt(pos - 3) == QChar('/') + && characterAt(pos - 2) == QChar('*') + && (characterAt(pos - 1) == QChar('*') + || characterAt(pos - 1) == QChar('!'))) { + CppTools::DoxygenGenerator doxygen; + doxygen.setAddLeadingAsterisks(m_commentsSettings.m_leadingAsterisks); + doxygen.setGenerateBrief(m_commentsSettings.m_generateBrief); + doxygen.setStartComment(false); + if (characterAt(pos - 1) == QLatin1Char('!')) + doxygen.setStyle(CppTools::DoxygenGenerator::QtStyle); + else + doxygen.setStyle(CppTools::DoxygenGenerator::JavaStyle); + + // Move until we reach any possibly meaningful content. + while (document()->characterAt(cursor.position()).isSpace()) + cursor.movePosition(QTextCursor::NextCharacter); + + const QString &comment = doxygen.generate(cursor); + if (!comment.isEmpty()) { + cursor.beginEditBlock(); + cursor.setPosition(pos); + cursor.insertText(comment); + cursor.setPosition(pos - 3, QTextCursor::KeepAnchor); + indent(document(), cursor, QChar::Null); + cursor.endEditBlock(); + e->accept(); + return true; + } + cursor.setPosition(pos); + } + } + + if (!m_commentsSettings.m_leadingAsterisks) + return false; + + const QString &text = cursor.block().text(); + const int length = text.length(); + int offset = 0; + for (; offset < length; ++offset) { + const QChar ¤t = text.at(offset); + if (!current.isSpace()) + break; + } + if (offset < length + && (text.at(offset) == QLatin1Char('*') + || (offset < length - 1 + && text.at(offset) == QLatin1Char('/') + && text.at(offset + 1) == QLatin1Char('*')))) { + QString newLine(QLatin1Char('\n')); + newLine.append(QString(offset, QLatin1Char(' '))); + if (text.at(offset) == QLatin1Char('/')) { + newLine.append(QLatin1String(" *")); + } else { + int start = offset; + while (offset < length && text.at(offset) == QLatin1Char('*')) + ++offset; + newLine.append(QString(offset - start, QLatin1Char('*'))); + } + cursor.insertText(newLine); + e->accept(); + return true; + } + } + + return false; +} + +void CPPEditorWidget::onCommentsSettingsChanged(const CppTools::CommentsSettings &settings) +{ + m_commentsSettings = settings; +} + #include "cppeditor.moc" diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 7d576c6599..f02c444f81 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -42,6 +42,7 @@ #include <cplusplus/LookupContext.h> #include <texteditor/basetexteditor.h> #include <texteditor/quickfix.h> +#include <cpptools/commentssettings.h> #include <QtCore/QThread> #include <QtCore/QMutex> @@ -250,6 +251,8 @@ private Q_SLOTS: void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker); + void onCommentsSettingsChanged(const CppTools::CommentsSettings &settings); + private: void markSymbols(const QTextCursor &tc, const SemanticInfo &info); bool sortedOutline() const; @@ -284,6 +287,8 @@ private: QModelIndex indexForPosition(int line, int column, const QModelIndex &rootIndex = QModelIndex()) const; + bool handleDocumentationComment(QKeyEvent *e); + CPlusPlus::CppModelManagerInterface *m_modelManager; QComboBox *m_outlineCombo; @@ -325,6 +330,8 @@ private: FunctionDeclDefLinkFinder *m_declDefLinkFinder; QSharedPointer<FunctionDeclDefLink> m_declDefLink; + + CppTools::CommentsSettings m_commentsSettings; }; diff --git a/src/plugins/cpptools/commentssettings.cpp b/src/plugins/cpptools/commentssettings.cpp new file mode 100644 index 0000000000..0cf2edfc96 --- /dev/null +++ b/src/plugins/cpptools/commentssettings.cpp @@ -0,0 +1,46 @@ +#include "commentssettings.h" + +#include <QtCore/QSettings> + +using namespace CppTools; + +namespace { + +const char kDocumentationCommentsGroup[] = "DocumentationComments"; +const char kEnableDoxygenBlocks[] = "EnableDoxygenBlocks"; +const char kGenerateBrief[] = "GenerateBrief"; +const char kAddLeadingAsterisks[] = "AddLeadingAsterisks"; + +} + +CommentsSettings::CommentsSettings() + : m_enableDoxygen(true) + , m_generateBrief(true) + , m_leadingAsterisks(true) +{} + +void CommentsSettings::toSettings(const QString &category, QSettings *s) const +{ + s->beginGroup(category + QLatin1String(kDocumentationCommentsGroup)); + s->setValue(QLatin1String(kEnableDoxygenBlocks), m_enableDoxygen); + s->setValue(QLatin1String(kGenerateBrief), m_generateBrief); + s->setValue(QLatin1String(kAddLeadingAsterisks), m_leadingAsterisks); + s->endGroup(); +} + +void CommentsSettings::fromSettings(const QString &category, QSettings *s) +{ + s->beginGroup(category + QLatin1String(kDocumentationCommentsGroup)); + m_enableDoxygen = s->value(QLatin1String(kEnableDoxygenBlocks), true).toBool(); + m_generateBrief = m_enableDoxygen + && s->value(QLatin1String(kGenerateBrief), true).toBool(); + m_leadingAsterisks = s->value(QLatin1String(kAddLeadingAsterisks), true).toBool(); + s->endGroup(); +} + +bool CommentsSettings::equals(const CommentsSettings &other) const +{ + return m_enableDoxygen == other.m_enableDoxygen + && m_generateBrief == other.m_generateBrief + && m_leadingAsterisks == other.m_leadingAsterisks; +} diff --git a/src/plugins/cpptools/commentssettings.h b/src/plugins/cpptools/commentssettings.h new file mode 100644 index 0000000000..2e6c140e1b --- /dev/null +++ b/src/plugins/cpptools/commentssettings.h @@ -0,0 +1,35 @@ +#ifndef COMMENTSSETTINGS_H +#define COMMENTSSETTINGS_H + +#include "cpptools_global.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace CppTools { + +class CPPTOOLS_EXPORT CommentsSettings +{ +public: + CommentsSettings(); + + void toSettings(const QString &category, QSettings *s) const; + void fromSettings(const QString &category, QSettings *s); + + bool equals(const CommentsSettings &other) const; + + bool m_enableDoxygen; + bool m_generateBrief; + bool m_leadingAsterisks; +}; + +inline bool operator==(const CommentsSettings &a, const CommentsSettings &b) +{ return a.equals(b); } + +inline bool operator!=(const CommentsSettings &a, const CommentsSettings &b) +{ return !(a == b); } + +} // namespace CppTools + +#endif // COMMENTSSETTINGS_H diff --git a/src/plugins/cpptools/completionsettingspage.cpp b/src/plugins/cpptools/completionsettingspage.cpp index 7af6d37709..dbb5cb33de 100644 --- a/src/plugins/cpptools/completionsettingspage.cpp +++ b/src/plugins/cpptools/completionsettingspage.cpp @@ -32,6 +32,7 @@ #include "completionsettingspage.h" #include "ui_completionsettingspage.h" +#include "cpptoolsconstants.h" #include <coreplugin/icore.h> #include <extensionsystem/pluginmanager.h> @@ -40,11 +41,16 @@ #include <QtCore/QTextStream> #include <QtCore/QCoreApplication> +using namespace CppTools; using namespace CppTools::Internal; +using namespace CppTools::Constants; -CompletionSettingsPage::CompletionSettingsPage() - : m_page(0) +CompletionSettingsPage::CompletionSettingsPage(QObject *parent) + : TextEditor::TextEditorOptionsPage(parent) + , m_page(0) { + if (QSettings *s = Core::ICore::instance()->settings()) + m_commentsSettings.fromSettings(QLatin1String(CPPTOOLS_SETTINGSGROUP), s); } CompletionSettingsPage::~CompletionSettingsPage() @@ -103,6 +109,9 @@ QWidget *CompletionSettingsPage::createPage(QWidget *parent) m_page->surroundSelectedText->setChecked(settings.m_surroundingAutoBrackets); m_page->partiallyComplete->setChecked(settings.m_partiallyComplete); m_page->spaceAfterFunctionName->setChecked(settings.m_spaceAfterFunctionName); + m_page->enableDoxygenCheckBox->setChecked(m_commentsSettings.m_enableDoxygen); + m_page->generateBriefCheckBox->setChecked(m_commentsSettings.m_generateBrief); + m_page->leadingAsterisksCheckBox->setChecked(m_commentsSettings.m_leadingAsterisks); if (m_searchKeywords.isEmpty()) { QTextStream(&m_searchKeywords) << m_page->caseSensitivityLabel->text() @@ -110,10 +119,15 @@ QWidget *CompletionSettingsPage::createPage(QWidget *parent) << ' ' << m_page->surroundSelectedText->text() << ' ' << m_page->completionTriggerLabel->text() << ' ' << m_page->partiallyComplete->text() - << ' ' << m_page->spaceAfterFunctionName->text(); + << ' ' << m_page->spaceAfterFunctionName->text() + << ' ' << m_page->enableDoxygenCheckBox->text() + << ' ' << m_page->generateBriefCheckBox->text() + << ' ' << m_page->leadingAsterisksCheckBox->text(); m_searchKeywords.remove(QLatin1Char('&')); } + m_page->generateBriefCheckBox->setEnabled(m_page->enableDoxygenCheckBox->isChecked()); + return w; } @@ -130,6 +144,17 @@ void CompletionSettingsPage::apply() settings.m_spaceAfterFunctionName = m_page->spaceAfterFunctionName->isChecked(); TextEditor::TextEditorSettings::instance()->setCompletionSettings(settings); + + if (!requireCommentsSettingsUpdate()) + return; + + m_commentsSettings.m_enableDoxygen = m_page->enableDoxygenCheckBox->isChecked(); + m_commentsSettings.m_generateBrief = m_page->generateBriefCheckBox->isChecked(); + m_commentsSettings.m_leadingAsterisks = m_page->leadingAsterisksCheckBox->isChecked(); + if (QSettings *s = Core::ICore::instance()->settings()) + m_commentsSettings.toSettings(QLatin1String(CPPTOOLS_SETTINGSGROUP), s); + + emit commentsSettingsChanged(m_commentsSettings); } bool CompletionSettingsPage::matches(const QString &s) const @@ -168,3 +193,15 @@ void CompletionSettingsPage::finish() delete m_page; m_page = 0; } + +const CommentsSettings &CompletionSettingsPage::commentsSettings() const +{ + return m_commentsSettings; +} + +bool CompletionSettingsPage::requireCommentsSettingsUpdate() const +{ + return m_commentsSettings.m_enableDoxygen != m_page->enableDoxygenCheckBox->isChecked() + || m_commentsSettings.m_generateBrief != m_page->generateBriefCheckBox->isChecked() + || m_commentsSettings.m_leadingAsterisks != m_page->leadingAsterisksCheckBox->isChecked(); +} diff --git a/src/plugins/cpptools/completionsettingspage.h b/src/plugins/cpptools/completionsettingspage.h index 627125a4c8..590aebd512 100644 --- a/src/plugins/cpptools/completionsettingspage.h +++ b/src/plugins/cpptools/completionsettingspage.h @@ -33,13 +33,17 @@ #ifndef COMPLETIONSETTINGSPAGE_H #define COMPLETIONSETTINGSPAGE_H +#include "commentssettings.h" + #include <texteditor/completionsettings.h> #include <texteditor/texteditoroptionspage.h> namespace CppTools { namespace Internal { -namespace Ui { class CompletionSettingsPage; } +namespace Ui { +class CompletionSettingsPage; +} // TODO: Move this class to the text editor plugin @@ -48,7 +52,7 @@ class CompletionSettingsPage : public TextEditor::TextEditorOptionsPage Q_OBJECT public: - CompletionSettingsPage(); + CompletionSettingsPage(QObject *parent); ~CompletionSettingsPage(); QString id() const; @@ -59,12 +63,20 @@ public: void finish(); virtual bool matches(const QString &) const; + const CommentsSettings &commentsSettings() const; + +signals: + void commentsSettingsChanged(const CppTools::CommentsSettings &settings); + private: TextEditor::CaseSensitivity caseSensitivity() const; TextEditor::CompletionTrigger completionTrigger() const; + bool requireCommentsSettingsUpdate() const; + Ui::CompletionSettingsPage *m_page; QString m_searchKeywords; + CommentsSettings m_commentsSettings; }; } // namespace Internal diff --git a/src/plugins/cpptools/completionsettingspage.ui b/src/plugins/cpptools/completionsettingspage.ui index 0be439363b..89e37c31d6 100644 --- a/src/plugins/cpptools/completionsettingspage.ui +++ b/src/plugins/cpptools/completionsettingspage.ui @@ -6,11 +6,11 @@ <rect> <x>0</x> <y>0</y> - <width>393</width> - <height>241</height> + <width>484</width> + <height>376</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> @@ -197,6 +197,65 @@ </widget> </item> <item> + <widget class="QGroupBox" name="docCommentsGroup"> + <property name="title"> + <string>Documentation Comments</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="enableDoxygenCheckBox"> + <property name="toolTip"> + <string>Automatically create a Doxygen comment upon pressing enter after a /** or /*!</string> + </property> + <property name="text"> + <string>Enable Doxygen blocks</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>30</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="generateBriefCheckBox"> + <property name="toolTip"> + <string>Generate a <i>brief</i> command with an initial description for the corresponding declaration</string> + </property> + <property name="text"> + <string>Generate brief description</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="leadingAsterisksCheckBox"> + <property name="toolTip"> + <string>Add leading asterisks when continuing comments on new lines</string> + </property> + <property name="text"> + <string>Add leading asterisks</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -224,8 +283,8 @@ <y>131</y> </hint> <hint type="destinationlabel"> - <x>265</x> - <y>182</y> + <x>333</x> + <y>206</y> </hint> </hints> </connection> @@ -245,5 +304,21 @@ </hint> </hints> </connection> + <connection> + <sender>enableDoxygenCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>generateBriefCheckBox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>153</x> + <y>272</y> + </hint> + <hint type="destinationlabel"> + <x>204</x> + <y>293</y> + </hint> + </hints> + </connection> </connections> </ui> diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 42fe7721dc..cbf4d3cd70 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -35,7 +35,9 @@ HEADERS += completionsettingspage.h \ cppcodestylesettings.h \ cppcodestylepreferencesfactory.h \ cppcodestylepreferences.h \ - cpptoolsreuse.h + cpptoolsreuse.h \ + doxygengenerator.h \ + commentssettings.h SOURCES += completionsettingspage.cpp \ cppclassesfilter.cpp \ @@ -62,7 +64,9 @@ SOURCES += completionsettingspage.cpp \ cppcodestylesettings.cpp \ cppcodestylepreferencesfactory.cpp \ cppcodestylepreferences.cpp \ - cpptoolsreuse.cpp + cpptoolsreuse.cpp \ + doxygengenerator.cpp \ + commentssettings.cpp FORMS += completionsettingspage.ui \ cppfilesettingspage.ui \ diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 14b453a0cb..4873e8edf3 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -122,7 +122,6 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) addAutoReleasedObject(new CppClassesFilter(m_modelManager)); addAutoReleasedObject(new CppFunctionsFilter(m_modelManager)); addAutoReleasedObject(new CppCurrentDocumentFilter(m_modelManager, core->editorManager())); - addAutoReleasedObject(new CompletionSettingsPage); addAutoReleasedObject(new CppFileSettingsPage(m_fileSettings)); addAutoReleasedObject(new SymbolsFindFilter(m_modelManager)); addAutoReleasedObject(new CppCodeStyleSettingsPage); diff --git a/src/plugins/cpptools/cpptoolssettings.cpp b/src/plugins/cpptools/cpptoolssettings.cpp index 47d144ede5..08b68e5c93 100644 --- a/src/plugins/cpptools/cpptoolssettings.cpp +++ b/src/plugins/cpptools/cpptoolssettings.cpp @@ -34,11 +34,14 @@ #include "cpptoolsconstants.h" #include "cppcodestylepreferences.h" #include "cppcodestylepreferencesfactory.h" +#include "commentssettings.h" +#include "completionsettingspage.h" #include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h> #include <texteditor/tabsettings.h> #include <texteditor/codestylepool.h> +#include <extensionsystem/pluginmanager.h> #include <utils/settingsutils.h> #include <utils/qtcassert.h> @@ -48,6 +51,7 @@ static const char *idKey = "CppGlobal"; using namespace CppTools; +using namespace CppTools::Internal; using TextEditor::TabSettings; namespace CppTools { @@ -56,7 +60,13 @@ namespace Internal { class CppToolsSettingsPrivate { public: + CppToolsSettingsPrivate() + : m_globalCodeStyle(0) + , m_completionSettingsPage(0) + {} + CppCodeStylePreferences *m_globalCodeStyle; + CompletionSettingsPage *m_completionSettingsPage; }; } // namespace Internal @@ -70,8 +80,17 @@ CppToolsSettings::CppToolsSettings(QObject *parent) { QTC_ASSERT(!m_instance, return); m_instance = this; + qRegisterMetaType<CppTools::CppCodeStyleSettings>("CppTools::CppCodeStyleSettings"); + d->m_completionSettingsPage = new CompletionSettingsPage(this); + ExtensionSystem::PluginManager::instance()->addObject(d->m_completionSettingsPage); + + connect(d->m_completionSettingsPage, + SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings)), + this, + SIGNAL(commentsSettingsChanged(CppTools::CommentsSettings))); + TextEditor::TextEditorSettings *textEditorSettings = TextEditor::TextEditorSettings::instance(); // code style factory @@ -224,6 +243,8 @@ CppToolsSettings::CppToolsSettings(QObject *parent) CppToolsSettings::~CppToolsSettings() { + ExtensionSystem::PluginManager::instance()->removeObject(d->m_completionSettingsPage); + delete d; m_instance = 0; @@ -239,3 +260,7 @@ CppCodeStylePreferences *CppToolsSettings::cppCodeStyle() const return d->m_globalCodeStyle; } +const CommentsSettings &CppToolsSettings::commentsSettings() const +{ + return d->m_completionSettingsPage->commentsSettings(); +} diff --git a/src/plugins/cpptools/cpptoolssettings.h b/src/plugins/cpptools/cpptoolssettings.h index 62cb35bc8c..3444231d9b 100644 --- a/src/plugins/cpptools/cpptoolssettings.h +++ b/src/plugins/cpptools/cpptoolssettings.h @@ -40,7 +40,7 @@ namespace CppTools { class CppCodeStylePreferences; - +class CommentsSettings; namespace Internal { @@ -61,6 +61,10 @@ public: static CppToolsSettings *instance(); CppCodeStylePreferences *cppCodeStyle() const; + const CommentsSettings &commentsSettings() const; + +signals: + void commentsSettingsChanged(const CppTools::CommentsSettings &settings); private: Internal::CppToolsSettingsPrivate *d; diff --git a/src/plugins/cpptools/doxygengenerator.cpp b/src/plugins/cpptools/doxygengenerator.cpp new file mode 100644 index 0000000000..8e3df8d6b8 --- /dev/null +++ b/src/plugins/cpptools/doxygengenerator.cpp @@ -0,0 +1,289 @@ +#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> + +#include <QtCore/QStringBuilder> +#include <QtGui/QTextDocument> +#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>")); + doc->setSource(declCandidate.toUtf8()); + 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 "); + + Q_ASSERT(command == BriefCommand); + 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(' ')); +} diff --git a/src/plugins/cpptools/doxygengenerator.h b/src/plugins/cpptools/doxygengenerator.h new file mode 100644 index 0000000000..78b6b6b914 --- /dev/null +++ b/src/plugins/cpptools/doxygengenerator.h @@ -0,0 +1,71 @@ +#ifndef DOXYGEGENERATOR_H +#define DOXYGEGENERATOR_H + +#include "cpptools_global.h" + +#include <cplusplus/Overview.h> + +#include <QtCore/QLatin1String> +#include <QtGui/QTextCursor> + +namespace CPlusPlus { +class DeclarationAST; +} + +namespace CppTools { + +class CPPTOOLS_EXPORT DoxygenGenerator +{ +public: + DoxygenGenerator(); + + enum DocumentationStyle { + JavaStyle, + QtStyle + }; + + void setStyle(DocumentationStyle style); + void setStartComment(bool start); + void setGenerateBrief(bool gen); + void setAddLeadingAsterisks(bool add); + + QString generate(QTextCursor cursor); + QString generate(QTextCursor cursor, CPlusPlus::DeclarationAST *decl); + +private: + QChar startMark() const; + QChar styleMark() const; + + enum Command { + BriefCommand, + ParamCommand, + ReturnCommand + }; + static QString commandSpelling(Command command); + + void writeStart(QString *comment) const; + void writeEnd(QString *comment) const; + void writeContinuation(QString *comment) const; + void writeNewLine(QString *comment) const; + void writeCommand(QString *comment, + Command command, + const QString &commandContent = QString()) const; + void writeBrief(QString *comment, + const QString &brief, + const QString &prefix = QString(), + const QString &suffix = QString()); + + void assignCommentOffset(QTextCursor cursor); + QString offsetString() const; + + bool m_addLeadingAsterisks; + bool m_generateBrief; + bool m_startComment; + DocumentationStyle m_style; + CPlusPlus::Overview m_printer; + int m_commentOffset; +}; + +} // CppTools + +#endif // DOXYGEGENERATOR_H |