diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp index 4fc91537da7..fd315a8e117 100644 --- a/src/libs/utils/fancymainwindow.cpp +++ b/src/libs/utils/fancymainwindow.cpp @@ -153,13 +153,16 @@ public: m_floatButton = new DockWidgetTitleButton(this); m_floatButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarNormalButton, &opt, q)); - m_floatButton->setAccessibleName(QDockWidget::tr("Float")); - m_floatButton->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget")); m_closeButton = new DockWidgetTitleButton(this); m_closeButton->setIcon(q->style()->standardIcon(QStyle::SP_TitleBarCloseButton, &opt, q)); + +#ifndef QT_NO_ACCESSIBILITY + m_floatButton->setAccessibleName(QDockWidget::tr("Float")); + m_floatButton->setAccessibleDescription(QDockWidget::tr("Undocks and re-attaches the dock widget")); m_closeButton->setAccessibleName(QDockWidget::tr("Close")); m_closeButton->setAccessibleDescription(QDockWidget::tr("Closes the dock widget")); +#endif setActive(false); diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index d19c12035df..5955d351f0e 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -613,7 +613,7 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions( QTC_CHECK(m_completions.isEmpty()); m_completions = toAssistProposalItems(completions); - if (m_addSnippets) + if (m_addSnippets && !m_completions.isEmpty()) addSnippets(); setAsyncProposalAvailable(createProposal(neededCorrection)); diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp index 54f35b9dca4..2714c254b91 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp @@ -932,18 +932,28 @@ void ClangCodeCompletionTest::testCompleteConstructorAndFallbackToGlobalCompleti QVERIFY(!hasSnippet(t.proposal, "class")); } +// Explicitly Inserting The Dot +// ---------------------------- +// Inserting the dot for is important since it will send the editor +// content to the backend and thus generate an unsaved file on the backend +// side. The unsaved file enables us to do the dot to arrow correction. + void ClangCodeCompletionTest::testCompleteWithDotToArrowCorrection() { - // Inserting the dot for this test is important since it will send the editor - // content to the backend and thus generate an unsaved file on the backend - // side. The unsaved file enables us to do the dot to arrow correction. - ProjectLessCompletionTest t("dotToArrowCorrection.cpp", - QStringLiteral(".")); + QStringLiteral(".")); // See above "Explicitly Inserting The Dot" QVERIFY(hasItem(t.proposal, "member")); } +void ClangCodeCompletionTest::testDontCompleteWithDotToArrowCorrectionForFloats() +{ + ProjectLessCompletionTest t("noDotToArrowCorrectionForFloats.cpp", + QStringLiteral(".")); // See above "Explicitly Inserting The Dot" + + QCOMPARE(t.proposal->size(), 0); +} + void ClangCodeCompletionTest::testCompleteProjectDependingCode() { const TestDocument testDocument("completionWithProject.cpp"); diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h index bd9151bcac4..3f163c62663 100644 --- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.h +++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.h @@ -48,6 +48,7 @@ private slots: void testCompleteConstructorAndFallbackToGlobalCompletion(); void testCompleteWithDotToArrowCorrection(); + void testDontCompleteWithDotToArrowCorrectionForFloats(); void testCompleteProjectDependingCode(); void testCompleteProjectDependingCodeAfterChangingProject(); diff --git a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc index c013ccd7f40..ad5ccb47681 100644 --- a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc +++ b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc @@ -22,5 +22,6 @@ objc_messages_3.mm preprocessorKeywordsCompletion.cpp dotToArrowCorrection.cpp + noDotToArrowCorrectionForFloats.cpp diff --git a/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp b/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp new file mode 100644 index 00000000000..7b29c3e171d --- /dev/null +++ b/src/plugins/clangcodemodel/test/data/noDotToArrowCorrectionForFloats.cpp @@ -0,0 +1,4 @@ +void f() +{ + 0 /* COMPLETE HERE */ +} diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index 4f258e6d239..53337230d8f 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -173,6 +173,8 @@ class PROJECTEXPLORER_EXPORT Runnable bool canReUseOutputPane(const std::unique_ptr &other) const override { + if (!other.get()) + return false; if (other->typeId() != typeId()) return false; auto that = static_cast *>(other.get()); diff --git a/src/tools/clangbackend/ipcsource/codecompleter.cpp b/src/tools/clangbackend/ipcsource/codecompleter.cpp index 436c6680524..4344a22aa93 100644 --- a/src/tools/clangbackend/ipcsource/codecompleter.cpp +++ b/src/tools/clangbackend/ipcsource/codecompleter.cpp @@ -68,8 +68,7 @@ CodeCompletions CodeCompleter::complete(uint line, uint column) translationUnit.cxUnsavedFiles(), translationUnit.unsavedFilesCount()); - if (results.hasNoResultsForDotCompletion() && hasDotAt(line, column - 1)) - results = completeWithArrowInsteadOfDot(line, column); + tryDotArrowCorrectionIfNoResults(results, line, column); return toCodeCompletions(results); } @@ -93,13 +92,6 @@ ClangCodeCompleteResults CodeCompleter::complete(uint line, defaultOptions()); } -bool CodeCompleter::hasDotAt(uint line, uint column) const -{ - const UnsavedFile &unsavedFile = translationUnit.unsavedFile(); - - return unsavedFile.hasCharacterAt(line, column, '.'); -} - uint CodeCompleter::defaultOptions() const { uint options = CXCodeComplete_IncludeMacros @@ -111,12 +103,25 @@ uint CodeCompleter::defaultOptions() const return options; } -ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, uint column) +void CodeCompleter::tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results, + uint line, + uint column) +{ + if (results.hasNoResultsForDotCompletion()) { + const UnsavedFile &unsavedFile = translationUnit.unsavedFile(); + bool positionIsOk = false; + const uint dotPosition = unsavedFile.toUtf8Position(line, column - 1, &positionIsOk); + if (positionIsOk && unsavedFile.hasCharacterAt(dotPosition, '.')) + results = completeWithArrowInsteadOfDot(line, column, dotPosition); + } +} + +ClangCodeCompleteResults CodeCompleter::completeWithArrowInsteadOfDot(uint line, + uint column, + uint dotPosition) { ClangCodeCompleteResults results; - - const SourceLocation location = translationUnit.sourceLocationAtWithoutReparsing(line, column - 1); - const bool replaced = translationUnit.unsavedFile().replaceAt(location.offset(), + const bool replaced = translationUnit.unsavedFile().replaceAt(dotPosition, 1, Utf8StringLiteral("->")); diff --git a/src/tools/clangbackend/ipcsource/codecompleter.h b/src/tools/clangbackend/ipcsource/codecompleter.h index 313323a9d93..c859b2d02e2 100644 --- a/src/tools/clangbackend/ipcsource/codecompleter.h +++ b/src/tools/clangbackend/ipcsource/codecompleter.h @@ -45,18 +45,20 @@ public: CompletionCorrection neededCorrection() const; -public: // for tests - bool hasDotAt(uint line, uint column) const; - private: uint defaultOptions() const; + void tryDotArrowCorrectionIfNoResults(ClangCodeCompleteResults &results, + uint line, + uint column); + ClangCodeCompleteResults complete(uint line, uint column, CXUnsavedFile *unsavedFiles, unsigned unsavedFileCount); - - ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line, uint column); + ClangCodeCompleteResults completeWithArrowInsteadOfDot(uint line, + uint column, + uint dotPosition); Utf8String filePath() const; static void checkCodeCompleteResult(CXCodeCompleteResults *completeResults); diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.cpp b/src/tools/clangbackend/ipcsource/unsavedfile.cpp index cc6e0296025..16d6638e8a5 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfile.cpp +++ b/src/tools/clangbackend/ipcsource/unsavedfile.cpp @@ -71,15 +71,24 @@ const char *UnsavedFile::filePath() const return cxUnsavedFile.Filename; } -bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const +uint UnsavedFile::toUtf8Position(uint line, uint column, bool *ok) const { Utf8PositionFromLineColumn converter(cxUnsavedFile.Contents); if (converter.find(line, column)) { - const uint utf8Position = converter.position(); - return hasCharacterAt(utf8Position, character); + *ok = true; + return converter.position(); } - return false; + *ok = false; + return 0; +} + +bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const +{ + bool positionIsOk = false; + const uint utf8Position = toUtf8Position(line, column, &positionIsOk); + + return positionIsOk && hasCharacterAt(utf8Position, character); } bool UnsavedFile::hasCharacterAt(uint position, char character) const diff --git a/src/tools/clangbackend/ipcsource/unsavedfile.h b/src/tools/clangbackend/ipcsource/unsavedfile.h index 3433093ae15..dcd4fd026b1 100644 --- a/src/tools/clangbackend/ipcsource/unsavedfile.h +++ b/src/tools/clangbackend/ipcsource/unsavedfile.h @@ -55,6 +55,7 @@ public: const char *filePath() const; // 1-based line and column + uint toUtf8Position(uint line, uint column, bool *ok) const; bool hasCharacterAt(uint line, uint column, char character) const; bool hasCharacterAt(uint position, char character) const; bool replaceAt(uint position, uint length, const Utf8String &replacement); diff --git a/tests/unit/unittest/codecompletiontest.cpp b/tests/unit/unittest/codecompletiontest.cpp index a370f935867..c25b03a492a 100644 --- a/tests/unit/unittest/codecompletiontest.cpp +++ b/tests/unit/unittest/codecompletiontest.cpp @@ -112,6 +112,19 @@ protected: readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer.cpp")), true }; + ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerBeforeTyping{ + Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"), + projectPart.projectPartId(), + readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp")), + true + }; + ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerAfterTyping{ + Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"), + projectPart.projectPartId(), + readFileContent(QStringLiteral("/complete_withDotArrowCorrectionForPointer_afterTyping.cpp")), + true + }; + ClangBackEnd::FileContainer dotArrowCorrectionForPointerFileContainerInitial{ Utf8StringLiteral(TESTDATA_DIR"/complete_withDotArrowCorrectionForPointer.cpp"), projectPart.projectPartId(), @@ -313,30 +326,6 @@ TEST_F(CodeCompleter, ArrowCompletion) ClangBackEnd::CompletionCorrection::NoCorrection); } -TEST_F(CodeCompleter, HasDotAt) -{ - auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer); - - ASSERT_TRUE(myCompleter.hasDotAt(5, 8)); -} - -TEST_F(CodeCompleter, HasDotAtWithUpdatedUnsavedFile) -{ - auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainerInitial); - unsavedFiles.createOrUpdate({dotArrowCorrectionForPointerFileContainerUpdated}); - - ASSERT_TRUE(myCompleter.hasDotAt(5, 8)); -} - -TEST_F(CodeCompleter, HasNoDotAtDueToMissingUnsavedFile) -{ - const ClangBackEnd::FileContainer fileContainer = dotArrowCorrectionForPointerFileContainer; - translationUnits.create({fileContainer}); - ClangBackEnd::CodeCompleter myCompleter(translationUnits.translationUnit(fileContainer)); - - ASSERT_FALSE(myCompleter.hasDotAt(5, 8)); -} - TEST_F(CodeCompleter, DotToArrowCompletionForPointer) { auto myCompleter = setupCompleter(dotArrowCorrectionForPointerFileContainer); @@ -350,6 +339,24 @@ TEST_F(CodeCompleter, DotToArrowCompletionForPointer) ClangBackEnd::CompletionCorrection::DotToArrowCorrection); } +TEST_F(CodeCompleter, DotToArrowCompletionForPointerInOutdatedTranslationUnit) +{ + auto fileContainerBeforeTyping = dotArrowCorrectionForPointerFileContainerBeforeTyping; + auto myCompleter = setupCompleter(fileContainerBeforeTyping); + auto translationUnit = translationUnits.translationUnit(fileContainerBeforeTyping.filePath(), + fileContainerBeforeTyping.projectPartId()); + translationUnit.cxTranslationUnit(); // Parse + unsavedFiles.createOrUpdate({dotArrowCorrectionForPointerFileContainerAfterTyping}); + + const ClangBackEnd::CodeCompletions completions = myCompleter.complete(5, 9); + + ASSERT_THAT(completions, + Contains(IsCodeCompletion(Utf8StringLiteral("member"), + CodeCompletion::VariableCompletionKind))); + ASSERT_THAT(myCompleter.neededCorrection(), + ClangBackEnd::CompletionCorrection::DotToArrowCorrection); +} + TEST_F(CodeCompleter, NoDotToArrowCompletionForObject) { auto myCompleter = setupCompleter(noDotArrowCorrectionForObjectFileContainer); diff --git a/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp new file mode 100644 index 00000000000..9f8d3645b2e --- /dev/null +++ b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_afterTyping.cpp @@ -0,0 +1,6 @@ +struct Foo { int member; }; + +void g(Foo *foo) +{ + foo. +} diff --git a/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp new file mode 100644 index 00000000000..0472cfacaa4 --- /dev/null +++ b/tests/unit/unittest/data/complete_withDotArrowCorrectionForPointer_beforeTyping.cpp @@ -0,0 +1,6 @@ +struct Foo { int member; }; + +void g(Foo *foo) +{ + +} diff --git a/tests/unit/unittest/unsavedfiletest.cpp b/tests/unit/unittest/unsavedfiletest.cpp index 3f0ae6d90b1..463c29be8b3 100644 --- a/tests/unit/unittest/unsavedfiletest.cpp +++ b/tests/unit/unittest/unsavedfiletest.cpp @@ -34,6 +34,7 @@ using ClangBackEnd::UnsavedFile; using ClangBackEnd::UnsavedFiles; +using ::testing::Eq; using ::testing::PrintToString; namespace { @@ -150,6 +151,44 @@ TEST_F(UnsavedFile, Replace) ASSERT_THAT(unsavedFile, IsUnsavedFile(filePath, expectedContent, expectedContent.byteSize())); } +TEST_F(UnsavedFile, ToUtf8PositionForValidLineColumn) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + bool ok = false; + + const uint position = unsavedFile.toUtf8Position(1, 1, &ok); + + ASSERT_TRUE(ok); + ASSERT_THAT(position, Eq(0)); +} + +TEST_F(UnsavedFile, ToUtf8PositionForInValidLineColumn) +{ + ::UnsavedFile unsavedFile(filePath, fileContent); + bool ok = false; + + unsavedFile.toUtf8Position(2, 1, &ok); + + ASSERT_FALSE(ok); +} + +TEST_F(UnsavedFile, ToUtf8PositionForDefaultConstructedUnsavedFile) +{ + ::UnsavedFile unsavedFile; + bool ok = false; + + unsavedFile.toUtf8Position(1, 1, &ok); + + ASSERT_FALSE(ok); +} + +TEST_F(UnsavedFile, HasNoCharacterForDefaultConstructedUnsavedFile) +{ + ::UnsavedFile unsavedFile; + + ASSERT_FALSE(unsavedFile.hasCharacterAt(0, 'x')); +} + TEST_F(UnsavedFile, HasNoCharacterForTooBigOffset) { ::UnsavedFile unsavedFile(filePath, fileContent);