diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index f3e98c0491d..83723f329ab 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -205,6 +205,9 @@ private slots: void test_useSelections_data(); void test_useSelections(); + void test_selectionFiltering_data(); + void test_selectionFiltering(); + // tests for "Include Hierarchy" void test_includehierarchy_data(); void test_includehierarchy(); diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index fb46661218c..354638dca76 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -326,7 +326,8 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, if (revision != documentRevision()) return; - setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); + setExtraSelections(TextEditorWidget::CodeWarningsSelection, + unselectLeadingWhitespace(selections)); setRefactorMarkers(refactorMarkers + RefactorMarker::filterOutType( this->refactorMarkers(), CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID)); } @@ -1163,6 +1164,63 @@ void CppEditorWidget::invokeTextEditorWidgetAssist(TextEditor::AssistKind assist invokeAssist(assistKind, provider); } +const QList CppEditorWidget::unselectLeadingWhitespace( + const QList &selections) +{ + QList filtered; + for (const QTextEdit::ExtraSelection &sel : selections) { + QList splitSelections; + int firstNonWhitespacePos = -1; + int lastNonWhitespacePos = -1; + bool split = false; + const QTextBlock firstBlock = sel.cursor.document()->findBlock(sel.cursor.selectionStart()); + bool inIndentation = firstBlock.position() == sel.cursor.selectionStart(); + const auto createSplitSelection = [&] { + QTextEdit::ExtraSelection newSelection; + newSelection.cursor = QTextCursor(sel.cursor.document()); + newSelection.cursor.setPosition(firstNonWhitespacePos); + newSelection.cursor.setPosition(lastNonWhitespacePos + 1, QTextCursor::KeepAnchor); + newSelection.format = sel.format; + splitSelections << newSelection; + }; + for (int i = sel.cursor.selectionStart(); i < sel.cursor.selectionEnd(); ++i) { + const QChar curChar = sel.cursor.document()->characterAt(i); + if (!curChar.isSpace()) { + if (firstNonWhitespacePos == -1) + firstNonWhitespacePos = i; + lastNonWhitespacePos = i; + } + if (!inIndentation) { + if (curChar == QChar::ParagraphSeparator) + inIndentation = true; + continue; + } + if (curChar == QChar::ParagraphSeparator) + continue; + if (curChar.isSpace()) { + if (firstNonWhitespacePos != -1) { + createSplitSelection(); + firstNonWhitespacePos = -1; + lastNonWhitespacePos = -1; + } + split = true; + continue; + } + inIndentation = false; + } + + if (!split) { + filtered << sel; + continue; + } + + if (firstNonWhitespacePos != -1) + createSplitSelection(); + filtered << splitSelections; + } + return filtered; +} + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index 7bd9f4b4d3b..64172862fce 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -94,6 +94,9 @@ public: void invokeTextEditorWidgetAssist(TextEditor::AssistKind assistKind, TextEditor::IAssistProvider *provider) override; + static const QList + unselectLeadingWhitespace(const QList &selections); + protected: bool event(QEvent *e) override; void contextMenuEvent(QContextMenuEvent *) override; diff --git a/src/plugins/cppeditor/cppuseselections_test.cpp b/src/plugins/cppeditor/cppuseselections_test.cpp index 8eb1700a063..10d69274340 100644 --- a/src/plugins/cppeditor/cppuseselections_test.cpp +++ b/src/plugins/cppeditor/cppuseselections_test.cpp @@ -249,5 +249,70 @@ void CppEditorPlugin::test_useSelections() Tests::UseSelectionsTestCase(testDocument, expectedSelections); } +void CppEditorPlugin::test_selectionFiltering_data() +{ + QTest::addColumn("source"); + QTest::addColumn("original"); + QTest::addColumn("filtered"); + + QTest::addRow("QTCREATORBUG-18659") + << QString("int main()\n" + "{\n" + " [](const Foo &foo) -> Foo {\n" + " return foo;\n" + " };\n" + "}\n") + << SelectionList{{3, 4, 53}} + << SelectionList{{3, 4, 27}, {4, 8, 11}, {5, 4, 1}}; + QTest::addRow("indentation-selected-in-first-line") + << QString("int main()\n" + "{\n" + " [](const Foo &foo) -> Foo {\n" + " return foo;\n" + " };\n" + "}\n") + << SelectionList{{3, 0, 57}} + << SelectionList{{3, 4, 27}, {4, 8, 11}, {5, 4, 1}}; +} + +void CppEditorPlugin::test_selectionFiltering() +{ + QFETCH(QString, source); + QFETCH(SelectionList, original); + QFETCH(SelectionList, filtered); + + QTextDocument doc; + doc.setPlainText(source); + + const auto convertList = [&doc](const SelectionList &in) { + QList out; + for (const Selection &selIn : in) { + QTextEdit::ExtraSelection selOut; + selOut.format.setFontItalic(true); + const QTextBlock startBlock = doc.findBlockByLineNumber(selIn.line - 1); + const int startPos = startBlock.position() + selIn.column; + selOut.cursor = QTextCursor(&doc); + selOut.cursor.setPosition(startPos); + selOut.cursor.setPosition(startPos + selIn.length, QTextCursor::KeepAnchor); + out << selOut; + } + return out; + }; + + const QList expected = convertList(filtered); + const QList actual + = CppEditorWidget::unselectLeadingWhitespace(convertList(original)); + + QCOMPARE(actual.length(), expected.length()); + for (int i = 0; i < expected.length(); ++i) { + const QTextEdit::ExtraSelection &expectedSelection = expected.at(i); + const QTextEdit::ExtraSelection &actualSelection = actual.at(i); + QCOMPARE(actualSelection.format, expectedSelection.format); + QCOMPARE(actualSelection.cursor.document(), expectedSelection.cursor.document()); + QCOMPARE(actualSelection.cursor.position(), expectedSelection.cursor.position()); + QCOMPARE(actualSelection.cursor.anchor(), expectedSelection.cursor.anchor()); + } +} + } // namespace Internal } // namespace CppEditor