From 7761a5afbc0a2d88c0094adba24a277dc6da0823 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 2 Sep 2021 08:51:22 +0200 Subject: [PATCH] CppEditor: Move trySplitComment() to its only point of use Change-Id: I50dc26835a54558879498bcfedf3f246f99f86d2 Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- src/plugins/cppeditor/CMakeLists.txt | 1 - .../cppdocumentationcommenthelper.cpp | 334 ------------------ .../cppeditor/cppdocumentationcommenthelper.h | 40 --- src/plugins/cppeditor/cppeditor.pro | 2 - src/plugins/cppeditor/cppeditor.qbs | 2 - src/plugins/cppeditor/cppeditorwidget.cpp | 294 ++++++++++++++- 6 files changed, 293 insertions(+), 380 deletions(-) delete mode 100644 src/plugins/cppeditor/cppdocumentationcommenthelper.cpp delete mode 100644 src/plugins/cppeditor/cppdocumentationcommenthelper.h diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index a0729bc002b..c53b5a57360 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -39,7 +39,6 @@ add_qtc_plugin(CppEditor cppcompletionassistprovider.cpp cppcompletionassistprovider.h cppcurrentdocumentfilter.cpp cppcurrentdocumentfilter.h cppcursorinfo.h - cppdocumentationcommenthelper.cpp cppdocumentationcommenthelper.h cppdoxygen.cpp cppdoxygen.h cppeditor.qrc cppeditor_global.h diff --git a/src/plugins/cppeditor/cppdocumentationcommenthelper.cpp b/src/plugins/cppeditor/cppdocumentationcommenthelper.cpp deleted file mode 100644 index e2e684be6de..00000000000 --- a/src/plugins/cppeditor/cppdocumentationcommenthelper.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "cppdocumentationcommenthelper.h" - -#include "cppautocompleter.h" -#include "cpptoolssettings.h" -#include "doxygengenerator.h" - -#include -#include -#include -#include -#include - -#include -#include - -namespace CppEditor::Internal { - -namespace { - -bool isStartOfDoxygenComment(const QTextCursor &cursor) -{ - const int pos = cursor.position(); - - QTextDocument *document = cursor.document(); - QString comment = QString(document->characterAt(pos - 3)) - + document->characterAt(pos - 2) - + document->characterAt(pos - 1); - - return comment == QLatin1String("/**") - || comment == QLatin1String("/*!") - || comment == QLatin1String("///") - || comment == QLatin1String("//!"); -} - -DoxygenGenerator::DocumentationStyle doxygenStyle(const QTextCursor &cursor, - const QTextDocument *doc) -{ - const int pos = cursor.position(); - - QString comment = QString(doc->characterAt(pos - 3)) - + doc->characterAt(pos - 2) - + doc->characterAt(pos - 1); - - if (comment == QLatin1String("/**")) - return DoxygenGenerator::JavaStyle; - else if (comment == QLatin1String("/*!")) - return DoxygenGenerator::QtStyle; - else if (comment == QLatin1String("///")) - return DoxygenGenerator::CppStyleA; - else - return DoxygenGenerator::CppStyleB; -} - -/// Check if previous line is a CppStyle Doxygen Comment -bool isPreviousLineCppStyleComment(const QTextCursor &cursor) -{ - const QTextBlock ¤tBlock = cursor.block(); - if (!currentBlock.isValid()) - return false; - - const QTextBlock &actual = currentBlock.previous(); - if (!actual.isValid()) - return false; - - const QString text = actual.text().trimmed(); - return text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")); -} - -/// Check if next line is a CppStyle Doxygen Comment -bool isNextLineCppStyleComment(const QTextCursor &cursor) -{ - const QTextBlock ¤tBlock = cursor.block(); - if (!currentBlock.isValid()) - return false; - - const QTextBlock &actual = currentBlock.next(); - if (!actual.isValid()) - return false; - - const QString text = actual.text().trimmed(); - return text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")); -} - -bool isCppStyleContinuation(const QTextCursor& cursor) -{ - return isPreviousLineCppStyleComment(cursor) || isNextLineCppStyleComment(cursor); -} - -bool lineStartsWithCppDoxygenCommentAndCursorIsAfter(const QTextCursor &cursor, - const QTextDocument *doc) -{ - QTextCursor cursorFirstNonBlank(cursor); - cursorFirstNonBlank.movePosition(QTextCursor::StartOfLine); - while (doc->characterAt(cursorFirstNonBlank.position()).isSpace() - && cursorFirstNonBlank.movePosition(QTextCursor::NextCharacter)) { - } - - const QTextBlock& block = cursorFirstNonBlank.block(); - const QString text = block.text().trimmed(); - if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!"))) - return (cursor.position() >= cursorFirstNonBlank.position() + 3); - - return false; -} - -bool isCursorAfterNonNestedCppStyleComment(const QTextCursor &cursor, - TextEditor::TextEditorWidget *editorWidget) -{ - QTextDocument *document = editorWidget->document(); - QTextCursor cursorBeforeCppComment(cursor); - while (document->characterAt(cursorBeforeCppComment.position()) != QLatin1Char('/') - && cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) { - } - - if (!cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) - return false; - - if (document->characterAt(cursorBeforeCppComment.position()) != QLatin1Char('/')) - return false; - - if (!cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) - return false; - - return !CPlusPlus::MatchingText::isInCommentHelper(cursorBeforeCppComment); -} - -bool handleDoxygenCppStyleContinuation(QTextCursor &cursor) -{ - const int blockPos = cursor.positionInBlock(); - const QString &text = cursor.block().text(); - int offset = 0; - for (; offset < blockPos; ++offset) { - if (!text.at(offset).isSpace()) - break; - } - - // If the line does not start with the comment we don't - // consider it as a continuation. Handles situations like: - // void d(); /// - if (offset + 3 > text.size()) - return false; - const QStringView commentMarker = QStringView(text).mid(offset, 3); - if (commentMarker != QLatin1String("///") && commentMarker != QLatin1String("//!")) - return false; - - QString newLine(QLatin1Char('\n')); - newLine.append(text.left(offset)); // indent correctly - newLine.append(commentMarker.toString()); - newLine.append(QLatin1Char(' ')); - - cursor.insertText(newLine); - return true; -} - -bool handleDoxygenContinuation(QTextCursor &cursor, - TextEditor::TextEditorWidget *editorWidget, - const bool enableDoxygen, - const bool leadingAsterisks) -{ - const QTextDocument *doc = editorWidget->document(); - - // It might be a continuation if: - // a) current line starts with /// or //! and cursor is positioned after the comment - // b) current line is in the middle of a multi-line Qt or Java style comment - - if (!cursor.atEnd()) { - if (enableDoxygen && lineStartsWithCppDoxygenCommentAndCursorIsAfter(cursor, doc)) - return handleDoxygenCppStyleContinuation(cursor); - - if (isCursorAfterNonNestedCppStyleComment(cursor, editorWidget)) - return false; - } - - // We continue the comment if the cursor is after a comment's line asterisk and if - // there's no asterisk immediately after the cursor (that would already be considered - // a leading asterisk). - int offset = 0; - const int blockPos = cursor.positionInBlock(); - const QString ¤tLine = cursor.block().text(); - for (; offset < blockPos; ++offset) { - if (!currentLine.at(offset).isSpace()) - break; - } - - // In case we don't need to insert leading asteriskses, this code will be run once (right after - // hitting enter on the line containing '/*'). It will insert a continuation without an - // asterisk, but with an extra space. After that, the normal indenting will take over and do the - // Right Thing . - if (offset < blockPos - && (currentLine.at(offset) == QLatin1Char('*') - || (offset < blockPos - 1 - && currentLine.at(offset) == QLatin1Char('/') - && currentLine.at(offset + 1) == QLatin1Char('*')))) { - // Ok, so the line started with an '*' or '/*' - int followinPos = blockPos; - // Now search for the first non-whitespace character to align to: - for (; followinPos < currentLine.length(); ++followinPos) { - if (!currentLine.at(followinPos).isSpace()) - break; - } - if (followinPos == currentLine.length() // a) - || currentLine.at(followinPos) != QLatin1Char('*')) { // b) - // So either a) the line ended after a '*' and we need to insert a continuation, or - // b) we found the start of some text and we want to align the continuation to that. - QString newLine(QLatin1Char('\n')); - QTextCursor c(cursor); - c.movePosition(QTextCursor::StartOfBlock); - c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset); - newLine.append(c.selectedText()); - if (currentLine.at(offset) == QLatin1Char('/')) { - if (leadingAsterisks) - newLine.append(QLatin1String(" * ")); - else - newLine.append(QLatin1String(" ")); - offset += 3; - } else { - // If '*' is not within a comment, skip. - QTextCursor cursorOnFirstNonWhiteSpace(cursor); - const int positionOnFirstNonWhiteSpace = cursor.position() - blockPos + offset; - cursorOnFirstNonWhiteSpace.setPosition(positionOnFirstNonWhiteSpace); - if (!CPlusPlus::MatchingText::isInCommentHelper(cursorOnFirstNonWhiteSpace)) - return false; - - // ...otherwise do the continuation - int start = offset; - while (offset < blockPos && currentLine.at(offset) == QLatin1Char('*')) - ++offset; - const QChar ch = leadingAsterisks ? QLatin1Char('*') : QLatin1Char(' '); - newLine.append(QString(offset - start, ch)); - } - for (; offset < blockPos && currentLine.at(offset) == ' '; ++offset) - newLine.append(QLatin1Char(' ')); - cursor.insertText(newLine); - return true; - } - } - - return false; -} - -} // anonymous namespace - -bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, - const CPlusPlus::Snapshot &snapshot) -{ - const TextEditor::CommentsSettings &settings = CppToolsSettings::instance()->commentsSettings(); - if (!settings.m_enableDoxygen && !settings.m_leadingAsterisks) - return false; - - QTextCursor cursor = editorWidget->textCursor(); - if (!CPlusPlus::MatchingText::isInCommentHelper(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 (settings.m_enableDoxygen && cursor.positionInBlock() >= 3) { - const int pos = cursor.position(); - if (isStartOfDoxygenComment(cursor)) { - QTextDocument *textDocument = editorWidget->document(); - DoxygenGenerator::DocumentationStyle style = doxygenStyle(cursor, textDocument); - - // Check if we're already in a CppStyle Doxygen comment => continuation - // Needs special handling since CppStyle does not have start and end markers - if ((style == DoxygenGenerator::CppStyleA || style == DoxygenGenerator::CppStyleB) - && isCppStyleContinuation(cursor)) { - return handleDoxygenCppStyleContinuation(cursor); - } - - DoxygenGenerator doxygen; - doxygen.setStyle(style); - doxygen.setAddLeadingAsterisks(settings.m_leadingAsterisks); - doxygen.setGenerateBrief(settings.m_generateBrief); - doxygen.setStartComment(false); - - // Move until we reach any possibly meaningful content. - while (textDocument->characterAt(cursor.position()).isSpace() - && cursor.movePosition(QTextCursor::NextCharacter)) { - } - - if (!cursor.atEnd()) { - const QString &comment = doxygen.generate(cursor, - snapshot, - editorWidget->textDocument()->filePath()); - if (!comment.isEmpty()) { - cursor.beginEditBlock(); - cursor.setPosition(pos); - cursor.insertText(comment); - cursor.setPosition(pos - 3, QTextCursor::KeepAnchor); - editorWidget->textDocument()->autoIndent(cursor); - cursor.endEditBlock(); - return true; - } - cursor.setPosition(pos); - } - } - } // right after first doxygen comment - - return handleDoxygenContinuation(cursor, - editorWidget, - settings.m_enableDoxygen, - settings.m_leadingAsterisks); -} - -} // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/cppdocumentationcommenthelper.h b/src/plugins/cppeditor/cppdocumentationcommenthelper.h deleted file mode 100644 index af864b1bfc9..00000000000 --- a/src/plugins/cppeditor/cppdocumentationcommenthelper.h +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "cppeditor_global.h" - -namespace TextEditor { class TextEditorWidget; } -namespace CPlusPlus { class Snapshot; } - -namespace CppEditor { -namespace Internal { - -bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, - const CPlusPlus::Snapshot &snapshot); - -} // namespace Internal -} // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 134e96c87bb..c4c5fa8b43b 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -36,7 +36,6 @@ HEADERS += \ cppcompletionassistprovider.h \ cppcurrentdocumentfilter.h \ cppcursorinfo.h \ - cppdocumentationcommenthelper.h \ cppdoxygen.h \ cppeditor_global.h \ cppeditor_utils.h \ @@ -158,7 +157,6 @@ SOURCES += \ cppcompletionassistprocessor.cpp \ cppcompletionassistprovider.cpp \ cppcurrentdocumentfilter.cpp \ - cppdocumentationcommenthelper.cpp \ cppdoxygen.cpp \ cppeditordocument.cpp \ cppeditoroutline.cpp \ diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index f2433fb17be..cdaeb40514e 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -96,8 +96,6 @@ QtcPlugin { "cppcurrentdocumentfilter.cpp", "cppcurrentdocumentfilter.h", "cppcursorinfo.h", - "cppdocumentationcommenthelper.cpp", - "cppdocumentationcommenthelper.h", "cppdoxygen.cpp", "cppdoxygen.h", "cppdoxygen.kwgen", diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index 539e25f88a7..1c68b42f34a 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -31,7 +31,7 @@ #include "cppcodeformatter.h" #include "cppcodemodelsettings.h" #include "cppcompletionassistprovider.h" -#include "cppdocumentationcommenthelper.h" +#include "doxygengenerator.h" #include "cppeditorconstants.h" #include "cppeditordocument.h" #include "cppeditoroutline.h" @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,297 @@ using namespace Utils; namespace CppEditor { namespace Internal { +namespace { + +bool isStartOfDoxygenComment(const QTextCursor &cursor) +{ + const int pos = cursor.position(); + + QTextDocument *document = cursor.document(); + QString comment = QString(document->characterAt(pos - 3)) + + document->characterAt(pos - 2) + + document->characterAt(pos - 1); + + return comment == QLatin1String("/**") + || comment == QLatin1String("/*!") + || comment == QLatin1String("///") + || comment == QLatin1String("//!"); +} + +DoxygenGenerator::DocumentationStyle doxygenStyle(const QTextCursor &cursor, + const QTextDocument *doc) +{ + const int pos = cursor.position(); + + QString comment = QString(doc->characterAt(pos - 3)) + + doc->characterAt(pos - 2) + + doc->characterAt(pos - 1); + + if (comment == QLatin1String("/**")) + return DoxygenGenerator::JavaStyle; + else if (comment == QLatin1String("/*!")) + return DoxygenGenerator::QtStyle; + else if (comment == QLatin1String("///")) + return DoxygenGenerator::CppStyleA; + else + return DoxygenGenerator::CppStyleB; +} + +/// Check if previous line is a CppStyle Doxygen Comment +bool isPreviousLineCppStyleComment(const QTextCursor &cursor) +{ + const QTextBlock ¤tBlock = cursor.block(); + if (!currentBlock.isValid()) + return false; + + const QTextBlock &actual = currentBlock.previous(); + if (!actual.isValid()) + return false; + + const QString text = actual.text().trimmed(); + return text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")); +} + +/// Check if next line is a CppStyle Doxygen Comment +bool isNextLineCppStyleComment(const QTextCursor &cursor) +{ + const QTextBlock ¤tBlock = cursor.block(); + if (!currentBlock.isValid()) + return false; + + const QTextBlock &actual = currentBlock.next(); + if (!actual.isValid()) + return false; + + const QString text = actual.text().trimmed(); + return text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!")); +} + +bool isCppStyleContinuation(const QTextCursor& cursor) +{ + return isPreviousLineCppStyleComment(cursor) || isNextLineCppStyleComment(cursor); +} + +bool lineStartsWithCppDoxygenCommentAndCursorIsAfter(const QTextCursor &cursor, + const QTextDocument *doc) +{ + QTextCursor cursorFirstNonBlank(cursor); + cursorFirstNonBlank.movePosition(QTextCursor::StartOfLine); + while (doc->characterAt(cursorFirstNonBlank.position()).isSpace() + && cursorFirstNonBlank.movePosition(QTextCursor::NextCharacter)) { + } + + const QTextBlock& block = cursorFirstNonBlank.block(); + const QString text = block.text().trimmed(); + if (text.startsWith(QLatin1String("///")) || text.startsWith(QLatin1String("//!"))) + return (cursor.position() >= cursorFirstNonBlank.position() + 3); + + return false; +} + +bool isCursorAfterNonNestedCppStyleComment(const QTextCursor &cursor, + TextEditor::TextEditorWidget *editorWidget) +{ + QTextDocument *document = editorWidget->document(); + QTextCursor cursorBeforeCppComment(cursor); + while (document->characterAt(cursorBeforeCppComment.position()) != QLatin1Char('/') + && cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) { + } + + if (!cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) + return false; + + if (document->characterAt(cursorBeforeCppComment.position()) != QLatin1Char('/')) + return false; + + if (!cursorBeforeCppComment.movePosition(QTextCursor::PreviousCharacter)) + return false; + + return !CPlusPlus::MatchingText::isInCommentHelper(cursorBeforeCppComment); +} + +bool handleDoxygenCppStyleContinuation(QTextCursor &cursor) +{ + const int blockPos = cursor.positionInBlock(); + const QString &text = cursor.block().text(); + int offset = 0; + for (; offset < blockPos; ++offset) { + if (!text.at(offset).isSpace()) + break; + } + + // If the line does not start with the comment we don't + // consider it as a continuation. Handles situations like: + // void d(); /// + if (offset + 3 > text.size()) + return false; + const QStringView commentMarker = QStringView(text).mid(offset, 3); + if (commentMarker != QLatin1String("///") && commentMarker != QLatin1String("//!")) + return false; + + QString newLine(QLatin1Char('\n')); + newLine.append(text.left(offset)); // indent correctly + newLine.append(commentMarker.toString()); + newLine.append(QLatin1Char(' ')); + + cursor.insertText(newLine); + return true; +} + +bool handleDoxygenContinuation(QTextCursor &cursor, + TextEditor::TextEditorWidget *editorWidget, + const bool enableDoxygen, + const bool leadingAsterisks) +{ + const QTextDocument *doc = editorWidget->document(); + + // It might be a continuation if: + // a) current line starts with /// or //! and cursor is positioned after the comment + // b) current line is in the middle of a multi-line Qt or Java style comment + + if (!cursor.atEnd()) { + if (enableDoxygen && lineStartsWithCppDoxygenCommentAndCursorIsAfter(cursor, doc)) + return handleDoxygenCppStyleContinuation(cursor); + + if (isCursorAfterNonNestedCppStyleComment(cursor, editorWidget)) + return false; + } + + // We continue the comment if the cursor is after a comment's line asterisk and if + // there's no asterisk immediately after the cursor (that would already be considered + // a leading asterisk). + int offset = 0; + const int blockPos = cursor.positionInBlock(); + const QString ¤tLine = cursor.block().text(); + for (; offset < blockPos; ++offset) { + if (!currentLine.at(offset).isSpace()) + break; + } + + // In case we don't need to insert leading asteriskses, this code will be run once (right after + // hitting enter on the line containing '/*'). It will insert a continuation without an + // asterisk, but with an extra space. After that, the normal indenting will take over and do the + // Right Thing . + if (offset < blockPos + && (currentLine.at(offset) == QLatin1Char('*') + || (offset < blockPos - 1 + && currentLine.at(offset) == QLatin1Char('/') + && currentLine.at(offset + 1) == QLatin1Char('*')))) { + // Ok, so the line started with an '*' or '/*' + int followinPos = blockPos; + // Now search for the first non-whitespace character to align to: + for (; followinPos < currentLine.length(); ++followinPos) { + if (!currentLine.at(followinPos).isSpace()) + break; + } + if (followinPos == currentLine.length() // a) + || currentLine.at(followinPos) != QLatin1Char('*')) { // b) + // So either a) the line ended after a '*' and we need to insert a continuation, or + // b) we found the start of some text and we want to align the continuation to that. + QString newLine(QLatin1Char('\n')); + QTextCursor c(cursor); + c.movePosition(QTextCursor::StartOfBlock); + c.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, offset); + newLine.append(c.selectedText()); + if (currentLine.at(offset) == QLatin1Char('/')) { + if (leadingAsterisks) + newLine.append(QLatin1String(" * ")); + else + newLine.append(QLatin1String(" ")); + offset += 3; + } else { + // If '*' is not within a comment, skip. + QTextCursor cursorOnFirstNonWhiteSpace(cursor); + const int positionOnFirstNonWhiteSpace = cursor.position() - blockPos + offset; + cursorOnFirstNonWhiteSpace.setPosition(positionOnFirstNonWhiteSpace); + if (!CPlusPlus::MatchingText::isInCommentHelper(cursorOnFirstNonWhiteSpace)) + return false; + + // ...otherwise do the continuation + int start = offset; + while (offset < blockPos && currentLine.at(offset) == QLatin1Char('*')) + ++offset; + const QChar ch = leadingAsterisks ? QLatin1Char('*') : QLatin1Char(' '); + newLine.append(QString(offset - start, ch)); + } + for (; offset < blockPos && currentLine.at(offset) == ' '; ++offset) + newLine.append(QLatin1Char(' ')); + cursor.insertText(newLine); + return true; + } + } + + return false; +} + +static bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, + const CPlusPlus::Snapshot &snapshot) +{ + const TextEditor::CommentsSettings &settings = CppToolsSettings::instance()->commentsSettings(); + if (!settings.m_enableDoxygen && !settings.m_leadingAsterisks) + return false; + + QTextCursor cursor = editorWidget->textCursor(); + if (!CPlusPlus::MatchingText::isInCommentHelper(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 (settings.m_enableDoxygen && cursor.positionInBlock() >= 3) { + const int pos = cursor.position(); + if (isStartOfDoxygenComment(cursor)) { + QTextDocument *textDocument = editorWidget->document(); + DoxygenGenerator::DocumentationStyle style = doxygenStyle(cursor, textDocument); + + // Check if we're already in a CppStyle Doxygen comment => continuation + // Needs special handling since CppStyle does not have start and end markers + if ((style == DoxygenGenerator::CppStyleA || style == DoxygenGenerator::CppStyleB) + && isCppStyleContinuation(cursor)) { + return handleDoxygenCppStyleContinuation(cursor); + } + + DoxygenGenerator doxygen; + doxygen.setStyle(style); + doxygen.setAddLeadingAsterisks(settings.m_leadingAsterisks); + doxygen.setGenerateBrief(settings.m_generateBrief); + doxygen.setStartComment(false); + + // Move until we reach any possibly meaningful content. + while (textDocument->characterAt(cursor.position()).isSpace() + && cursor.movePosition(QTextCursor::NextCharacter)) { + } + + if (!cursor.atEnd()) { + const QString &comment = doxygen.generate(cursor, + snapshot, + editorWidget->textDocument()->filePath()); + if (!comment.isEmpty()) { + cursor.beginEditBlock(); + cursor.setPosition(pos); + cursor.insertText(comment); + cursor.setPosition(pos - 3, QTextCursor::KeepAnchor); + editorWidget->textDocument()->autoIndent(cursor); + cursor.endEditBlock(); + return true; + } + cursor.setPosition(pos); + } + } + } // right after first doxygen comment + + return handleDoxygenContinuation(cursor, + editorWidget, + settings.m_enableDoxygen, + settings.m_leadingAsterisks); +} + +} // anonymous namespace + class CppEditorWidgetPrivate { public: