diff options
Diffstat (limited to 'src')
115 files changed, 1981 insertions, 1344 deletions
diff --git a/src/libs/languageserverprotocol/basemessage.cpp b/src/libs/languageserverprotocol/basemessage.cpp index 70204a139c..65fc12e449 100644 --- a/src/libs/languageserverprotocol/basemessage.cpp +++ b/src/libs/languageserverprotocol/basemessage.cpp @@ -77,7 +77,7 @@ static QPair<QByteArray, QByteArray> splitHeaderFieldLine( return {headerFieldLine.mid(0, assignmentIndex), headerFieldLine.mid(assignmentIndex + fieldSeparatorLength)}; } - parseError = BaseMessage::tr("Unexpected header line \"%1\"") + parseError = BaseMessage::tr("Unexpected header line \"%1\".") .arg(QLatin1String(headerFieldLine)); return {}; } @@ -97,8 +97,8 @@ static void parseContentType(BaseMessage &message, QByteArray contentType, QStri if (equalindex > 0) codec = QTextCodec::codecForName(charset); if (!codec) { - parseError = BaseMessage::tr("Cannot decode content with \"%1\" " - "falling back to \"%2\"") + parseError = BaseMessage::tr("Cannot decode content with \"%1\". " + "Falling back to \"%2\".") .arg(QLatin1String(charset), QLatin1String(defaultCharset)); } @@ -113,7 +113,7 @@ static void parseContentLength(BaseMessage &message, QByteArray contentLength, Q bool ok = true; message.contentLength = QString::fromLatin1(contentLength).toInt(&ok); if (!ok) { - parseError = BaseMessage::tr("Expected an integer in \"%1\", but got \"%2\"") + parseError = BaseMessage::tr("Expected an integer in \"%1\", but got \"%2\".") .arg(contentLengthFieldName, QLatin1String(contentLength)); } } @@ -146,7 +146,7 @@ void BaseMessage::parse(QBuffer *data, QString &parseError, BaseMessage &message } else if (headerFieldName == contentTypeFieldName) { parseContentType(message, headerFieldValue, parseError); } else { - parseError = tr("Unexpected header field \"%1\" in \"%2\"") + parseError = tr("Unexpected header field \"%1\" in \"%2\".") .arg(QLatin1String(headerFieldName), QLatin1String(headerFieldLine)); } diff --git a/src/libs/languageserverprotocol/jsonobject.cpp b/src/libs/languageserverprotocol/jsonobject.cpp index ea61092ec3..1f471db47b 100644 --- a/src/libs/languageserverprotocol/jsonobject.cpp +++ b/src/libs/languageserverprotocol/jsonobject.cpp @@ -104,7 +104,7 @@ QString JsonObject::valueTypeString(QJsonValue::Type type) QString JsonObject::errorString(QJsonValue::Type expected, QJsonValue::Type actual) { - return tr("Expected Type %1 but value contained %2") + return tr("Expected type %1 but value contained %2") .arg(valueTypeString(expected), valueTypeString(actual)); } diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.cpp b/src/libs/languageserverprotocol/jsonrpcmessages.cpp index 85d1813585..15f19b4c9a 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.cpp +++ b/src/libs/languageserverprotocol/jsonrpcmessages.cpp @@ -141,9 +141,9 @@ QJsonObject JsonRpcMessageHandler::toJsonObject(const QByteArray &_content, if (doc.isObject()) return doc.object(); if (doc.isNull()) - parseError = tr("Could not parse Json message '%1'").arg(error.errorString()); + parseError = tr("Could not parse JSON message \"%1\".").arg(error.errorString()); else - parseError = tr("Expected Json object, but got a json '%1'").arg(docTypeName(doc)); + parseError = tr("Expected a JSON object, but got a JSON \"%1\".").arg(docTypeName(doc)); return QJsonObject(); } diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h index 7a3fea1158..3225fd4866 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.h +++ b/src/libs/languageserverprotocol/jsonrpcmessages.h @@ -125,7 +125,7 @@ public: } if (errorMessage) *errorMessage = QCoreApplication::translate("LanguageServerProtocol::Notification", - "No parameters in '%1'").arg(method()); + "No parameters in \"%1\".").arg(method()); return false; } }; @@ -274,7 +274,7 @@ public: return true; if (errorMessage) { *errorMessage = QCoreApplication::translate("LanguageServerProtocol::Request", - "No id set in '%1'").arg(this->method()); + "No ID set in \"%1\".").arg(this->method()); } return false; } diff --git a/src/libs/languageserverprotocol/languagefeatures.cpp b/src/libs/languageserverprotocol/languagefeatures.cpp index 6581588201..8009ed1ebd 100644 --- a/src/libs/languageserverprotocol/languagefeatures.cpp +++ b/src/libs/languageserverprotocol/languagefeatures.cpp @@ -331,7 +331,7 @@ bool MarkedString::isValid(QStringList *errorHierarchy) const *errorHierarchy << QCoreApplication::translate( "LanguageServerProtocol::MarkedString", "MarkedString should be either MarkedLanguageString, " - "MarkupContent or QList<MarkedLanguageString>"); + "MarkupContent, or QList<MarkedLanguageString>."); } return false; } @@ -356,7 +356,7 @@ bool DocumentFormattingProperty::isValid(QStringList *error) const if (error) { *error << QCoreApplication::translate( "LanguageServerProtocol::MarkedString", - "DocumentFormattingProperty should be either bool, double or QString"); + "DocumentFormattingProperty should be either bool, double, or QString."); } return false; } diff --git a/src/libs/languageserverprotocol/lsptypes.cpp b/src/libs/languageserverprotocol/lsptypes.cpp index c76d4ff6dd..286732d16b 100644 --- a/src/libs/languageserverprotocol/lsptypes.cpp +++ b/src/libs/languageserverprotocol/lsptypes.cpp @@ -136,7 +136,7 @@ bool MarkupOrString::isValid(QStringList *error) const return true; if (error) { *error << QCoreApplication::translate("LanguageServerProtocoll::MarkupOrString", - "Expected a string or MarkupContent in MarkupOrString"); + "Expected a string or MarkupContent in MarkupOrString."); } return false; } diff --git a/src/libs/utils/textutils.cpp b/src/libs/utils/textutils.cpp index 192c68bcbd..c05e6cce84 100644 --- a/src/libs/utils/textutils.cpp +++ b/src/libs/utils/textutils.cpp @@ -140,5 +140,20 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor) return cursor; } +int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffer, int line) +{ + if (textDocument->characterCount() == buffer.size() + 1) + return textDocument->findBlockByNumber(line - 1).position(); + + int pos = 0; + for (int count = 0; count < line - 1; ++count) { + pos = buffer.indexOf('\n', pos); + if (pos == -1) + return -1; + ++pos; + } + return pos; +} + } // Text } // Utils diff --git a/src/libs/utils/textutils.h b/src/libs/utils/textutils.h index d5414e3194..1d4e8f7ffb 100644 --- a/src/libs/utils/textutils.h +++ b/src/libs/utils/textutils.h @@ -29,7 +29,6 @@ #include "utils_global.h" #include <QString> -#include <QTextCursor> QT_FORWARD_DECLARE_CLASS(QTextDocument) QT_FORWARD_DECLARE_CLASS(QTextCursor) @@ -37,7 +36,7 @@ QT_FORWARD_DECLARE_CLASS(QTextCursor) namespace Utils { namespace Text { -// line is 1-based, column is 0-based +// line is 1-based, column is 1-based QTCREATOR_UTILS_EXPORT bool convertPosition(const QTextDocument *document, int pos, int *line, int *column); @@ -55,42 +54,9 @@ QTCREATOR_UTILS_EXPORT QTextCursor flippedCursor(const QTextCursor &cursor); QTCREATOR_UTILS_EXPORT QTextCursor wordStartCursor(const QTextCursor &cursor); -template <class CharacterProvider> -void moveToPrevChar(CharacterProvider &provider, QTextCursor &cursor) -{ - cursor.movePosition(QTextCursor::PreviousCharacter); - while (provider.characterAt(cursor.position()).isSpace()) - cursor.movePosition(QTextCursor::PreviousCharacter); -} - -template <class CharacterProvider> -bool matchPreviousWord(CharacterProvider &provider, QTextCursor cursor, QString pattern) -{ - cursor.movePosition(QTextCursor::PreviousWord); - while (provider.characterAt(cursor.position()) == ':') - cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2); - - int previousWordStart = cursor.position(); - cursor.movePosition(QTextCursor::NextWord); - moveToPrevChar(provider, cursor); - QString toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1); - - pattern = pattern.simplified(); - while (!pattern.isEmpty() && pattern.endsWith(toMatch)) { - pattern.chop(toMatch.length()); - if (pattern.endsWith(' ')) - pattern.chop(1); - if (!pattern.isEmpty()) { - cursor.movePosition(QTextCursor::StartOfWord); - cursor.movePosition(QTextCursor::PreviousWord); - previousWordStart = cursor.position(); - cursor.movePosition(QTextCursor::NextWord); - moveToPrevChar(provider, cursor); - toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1); - } - } - return pattern.isEmpty(); -} +QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument, + const QByteArray &buffer, + int line); } // Text } // Utils diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index 7b32e5be69..4ab1f4f0cc 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -451,6 +451,8 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath, QList<AndroidToolChain *> toolChainBundle; for (Core::Id lang : {ProjectExplorer::Constants::CXX_LANGUAGE_ID, ProjectExplorer::Constants::C_LANGUAGE_ID}) { FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, lang, version); + if (!compilerPath.exists()) + continue; AndroidToolChain *tc = findToolChain(compilerPath, lang, alreadyKnown); if (!tc || tc->originalTargetTriple().isEmpty()) { @@ -464,7 +466,8 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath, toolChainBundle.append(tc); } - QTC_ASSERT(!toolChainBundle.isEmpty(), continue); + if (toolChainBundle.isEmpty()) + continue; auto it = newestToolChainForArch.constFind(abi); if (it == newestToolChainForArch.constEnd()) diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index 7fdedda74e..9a513bd2d4 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -57,6 +57,7 @@ #include <projectexplorer/session.h> #include <projectexplorer/target.h> #include <texteditor/texteditor.h> +#include <texteditor/textdocument.h> #include <utils/textutils.h> #include <utils/utilsicons.h> @@ -261,11 +262,22 @@ void AutotestPlugin::onRunFileTriggered() runner->prepareToRunTests(TestRunMode::Run); } +static QList<TestConfiguration *> testItemsToTestConfigurations(const QList<TestTreeItem *> &items, + TestRunMode mode) +{ + QList<TestConfiguration *> configs; + for (const TestTreeItem * item : items) { + if (TestConfiguration *currentConfig = item->asConfiguration(mode)) + configs << currentConfig; + } + return configs; +} + void AutotestPlugin::onRunUnderCursorTriggered(TestRunMode mode) { - QTextCursor cursor = Utils::Text::wordStartCursor( - TextEditor::BaseTextEditor::currentTextEditor()->editorWidget()->textCursor()); - cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + TextEditor::BaseTextEditor *currentEditor = TextEditor::BaseTextEditor::currentTextEditor(); + QTextCursor cursor = currentEditor->editorWidget()->textCursor(); + cursor.select(QTextCursor::WordUnderCursor); const QString text = cursor.selectedText(); if (text.isEmpty()) return; // Do not trigger when no name under cursor @@ -274,11 +286,15 @@ void AutotestPlugin::onRunUnderCursorTriggered(TestRunMode mode) if (testsItems.isEmpty()) return; // Wrong location triggered - QList<TestConfiguration *> testsToRun; - for (const TestTreeItem * item : testsItems){ - if (TestConfiguration *cfg = item->asConfiguration(mode)) - testsToRun << cfg; - } + // check whether we have been triggered on a test function definition + const uint line = uint(currentEditor->currentLine()); + const QString &filePath = currentEditor->textDocument()->filePath().toString(); + const QList<TestTreeItem *> filteredItems = Utils::filtered(testsItems, [&](TestTreeItem *it){ + return it->line() == line && it->filePath() == filePath; + }); + + const QList<TestConfiguration *> testsToRun = testItemsToTestConfigurations( + filteredItems.size() == 1 ? filteredItems : testsItems, mode); if (testsToRun.isEmpty()) { MessageManager::write(tr("Selected test was not found (%1).").arg(text), MessageManager::Flash); diff --git a/src/plugins/clangcodemodel/clangassistproposalitem.cpp b/src/plugins/clangcodemodel/clangassistproposalitem.cpp index ed15a006f6..e88970bd6e 100644 --- a/src/plugins/clangcodemodel/clangassistproposalitem.cpp +++ b/src/plugins/clangcodemodel/clangassistproposalitem.cpp @@ -198,9 +198,9 @@ void ClangAssistProposalItem::apply(TextDocumentManipulatorInterface &manipulato QTextCursor cursor = manipulator.textCursorAt(basePosition); bool abandonParen = false; - if (::Utils::Text::matchPreviousWord(manipulator, cursor, "&")) { - ::Utils::Text::moveToPrevChar(manipulator, cursor); - ::Utils::Text::moveToPrevChar(manipulator, cursor); + if (Utils::Text::matchPreviousWord(manipulator, cursor, "&")) { + Utils::Text::moveToPreviousWord(manipulator, cursor); + Utils::Text::moveToPreviousChar(manipulator, cursor); const QChar prevChar = manipulator.characterAt(cursor.position()); cursor.setPosition(basePosition); abandonParen = QString("(;,{}").contains(prevChar); @@ -211,7 +211,7 @@ void ClangAssistProposalItem::apply(TextDocumentManipulatorInterface &manipulato if (!abandonParen && ccr.completionKind == CodeCompletion::FunctionDefinitionCompletionKind) { const CodeCompletionChunk resultType = ccr.chunks.first(); if (resultType.kind == CodeCompletionChunk::ResultType) { - if (::Utils::Text::matchPreviousWord(manipulator, cursor, resultType.text.toString())) { + if (Utils::Text::matchPreviousWord(manipulator, cursor, resultType.text.toString())) { extraCharacters += methodDefinitionParameters(ccr.chunks); // To skip the next block. abandonParen = true; diff --git a/src/plugins/clangcodemodel/clangbackendcommunicator.cpp b/src/plugins/clangcodemodel/clangbackendcommunicator.cpp index 6354b50548..4e0c08b672 100644 --- a/src/plugins/clangcodemodel/clangbackendcommunicator.cpp +++ b/src/plugins/clangcodemodel/clangbackendcommunicator.cpp @@ -207,9 +207,26 @@ bool BackendCommunicator::isNotWaitingForCompletion() const return !m_receiver.isExpectingCompletionsMessage(); } +void BackendCommunicator::setBackendJobsPostponed(bool postponed) +{ + if (postponed) { + if (!m_postponeBackendJobs) + documentVisibilityChanged(Utf8String(), {}); + ++m_postponeBackendJobs; + } else { + if (QTC_GUARD(m_postponeBackendJobs > 0)) + --m_postponeBackendJobs; + if (!m_postponeBackendJobs) + documentVisibilityChanged(); + } +} + void BackendCommunicator::documentVisibilityChanged(const Utf8String ¤tEditorFilePath, const Utf8StringVector &visibleEditorsFilePaths) { + if (m_postponeBackendJobs) + return; + const DocumentVisibilityChangedMessage message(currentEditorFilePath, visibleEditorsFilePaths); m_sender->documentVisibilityChanged(message); } @@ -459,9 +476,14 @@ void BackendCommunicator::initializeBackendWithCurrentData() void BackendCommunicator::documentsOpened(const FileContainers &fileContainers) { - const DocumentsOpenedMessage message(fileContainers, - currentCppEditorDocumentFilePath(), - visibleCppEditorDocumentsFilePaths()); + Utf8String currentDocument; + Utf8StringVector visibleDocuments; + if (!m_postponeBackendJobs) { + currentDocument = currentCppEditorDocumentFilePath(); + visibleDocuments = visibleCppEditorDocumentsFilePaths(); + } + + const DocumentsOpenedMessage message(fileContainers, currentDocument, visibleDocuments); m_sender->documentsOpened(message); } diff --git a/src/plugins/clangcodemodel/clangbackendcommunicator.h b/src/plugins/clangcodemodel/clangbackendcommunicator.h index 7d676cf5a5..9709b60707 100644 --- a/src/plugins/clangcodemodel/clangbackendcommunicator.h +++ b/src/plugins/clangcodemodel/clangbackendcommunicator.h @@ -108,6 +108,8 @@ public: void updateChangeContentStartPosition(const QString &filePath, int position); bool isNotWaitingForCompletion() const; + void setBackendJobsPostponed(bool postponed); + private: void initializeBackend(); void initializeBackendWithCurrentData(); @@ -134,6 +136,7 @@ private: QTimer m_backendStartTimeOut; QScopedPointer<ClangBackEnd::ClangCodeModelServerInterface> m_sender; int m_connectedCount = 0; + int m_postponeBackendJobs = 1; // Initial application state is inactive, so no jobs should be run. }; } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index e8d7086662..db68f680bc 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -98,9 +98,9 @@ static void addFunctionOverloadAssistProposalItem(QList<AssistProposalItemInterf cursor.movePosition(QTextCursor::StartOfWord); const ClangBackEnd::CodeCompletionChunk resultType = codeCompletion.chunks.first(); - if (::Utils::Text::matchPreviousWord(*interface->textEditorWidget(), - cursor, - resultType.text.toString())) { + if (Utils::Text::matchPreviousWord(*interface->textEditorWidget(), + cursor, + resultType.text.toString())) { // Function definition completion - do not merge completions together. addAssistProposalItem(items, codeCompletion, name); } else { @@ -445,6 +445,12 @@ bool ClangCompletionAssistProcessor::completeInclude(const QTextCursor &cursor) completeIncludePath(realPath, suffixes); } + auto includesCompare = [](AssistProposalItemInterface *first, + AssistProposalItemInterface *second) { + return first->text() < second->text(); + }; + std::sort(m_completions.begin(), m_completions.end(), includesCompare); + return !m_completions.isEmpty(); } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index de02be4686..66abc7913b 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -53,7 +53,7 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> -#include <QCoreApplication> +#include <QApplication> #include <QMenu> #include <QTextBlock> @@ -75,6 +75,8 @@ ModelManagerSupportClang::ModelManagerSupportClang() QTC_CHECK(!m_instance); m_instance = this; + QApplication::instance()->installEventFilter(this); + CppTools::CppModelManager::instance()->setCurrentDocumentFilter( std::make_unique<ClangCurrentDocumentFilter>()); @@ -138,6 +140,11 @@ std::unique_ptr<CppTools::AbstractOverviewModel> ModelManagerSupportClang::creat return std::make_unique<OverviewModel>(); } +void ModelManagerSupportClang::setBackendJobsPostponed(bool postponed) +{ + m_communicator.setBackendJobsPostponed(postponed); +} + CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) { @@ -211,6 +218,20 @@ void ModelManagerSupportClang::connectToWidgetsMarkContextMenuRequested(QWidget } } +bool ModelManagerSupportClang::eventFilter(QObject *obj, QEvent *e) +{ + if (obj == QApplication::instance() && e->type() == QEvent::ApplicationStateChange) { + switch (QApplication::applicationState()) { + case Qt::ApplicationInactive: setBackendJobsPostponed(true); break; + case Qt::ApplicationActive: setBackendJobsPostponed(false); break; + default: + QTC_CHECK(false && "Unexpected Qt::ApplicationState"); + } + } + + return false; +} + void ModelManagerSupportClang::onEditorOpened(Core::IEditor *editor) { QTC_ASSERT(editor, return); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index e11191006f..9578804ebb 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -72,6 +72,7 @@ public: CppTools::FollowSymbolInterface &followSymbolInterface() override; CppTools::RefactoringEngineInterface &refactoringEngineInterface() override; std::unique_ptr<CppTools::AbstractOverviewModel> createOverviewModel() override; + void setBackendJobsPostponed(bool postponed) override; BackendCommunicator &communicator(); QString dummyUiHeaderOnDiskDirPath() const; @@ -82,6 +83,8 @@ public: static ModelManagerSupportClang *instance(); private: + bool eventFilter(QObject *obj, QEvent *e) override; + void onEditorOpened(Core::IEditor *editor); void onEditorClosed(const QList<Core::IEditor *> &editors); void onCurrentEditorChanged(Core::IEditor *newCurrent); diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h index 956f1dee4a..43866e6a9c 100644 --- a/src/plugins/clangcodemodel/clangutils.h +++ b/src/plugins/clangcodemodel/clangutils.h @@ -29,6 +29,8 @@ #include <cpptools/projectpart.h> +#include <QTextCursor> + QT_BEGIN_NAMESPACE class QTextBlock; QT_END_NAMESPACE @@ -66,5 +68,53 @@ QString diagnosticCategoryPrefixRemoved(const QString &text); void generateCompilationDB(::Utils::FileName projectDir, CppTools::ProjectInfo projectInfo); +namespace Text { + +template <class CharacterProvider> +void moveToPreviousChar(CharacterProvider &provider, QTextCursor &cursor) +{ + cursor.movePosition(QTextCursor::PreviousCharacter); + while (provider.characterAt(cursor.position()).isSpace()) + cursor.movePosition(QTextCursor::PreviousCharacter); +} + +template <class CharacterProvider> +void moveToPreviousWord(CharacterProvider &provider, QTextCursor &cursor) +{ + cursor.movePosition(QTextCursor::PreviousWord); + while (provider.characterAt(cursor.position()) == ':') + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2); +} + +template <class CharacterProvider> +bool matchPreviousWord(CharacterProvider &provider, QTextCursor cursor, QString pattern) +{ + cursor.movePosition(QTextCursor::PreviousWord); + while (provider.characterAt(cursor.position()) == ':') + cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2); + + int previousWordStart = cursor.position(); + cursor.movePosition(QTextCursor::NextWord); + moveToPreviousChar(provider, cursor); + QString toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1); + + pattern = pattern.simplified(); + while (!pattern.isEmpty() && pattern.endsWith(toMatch)) { + pattern.chop(toMatch.length()); + if (pattern.endsWith(' ')) + pattern.chop(1); + if (!pattern.isEmpty()) { + cursor.movePosition(QTextCursor::StartOfWord); + cursor.movePosition(QTextCursor::PreviousWord); + previousWordStart = cursor.position(); + cursor.movePosition(QTextCursor::NextWord); + moveToPreviousChar(provider, cursor); + toMatch = provider.textAt(previousWordStart, cursor.position() - previousWordStart + 1); + } + } + return pattern.isEmpty(); +} + +} // namespace Text } // namespace Utils } // namespace Clang diff --git a/src/plugins/clangformat/clangformat.pro b/src/plugins/clangformat/clangformat.pro index c2e2edd49f..74331b9e5f 100644 --- a/src/plugins/clangformat/clangformat.pro +++ b/src/plugins/clangformat/clangformat.pro @@ -24,7 +24,8 @@ HEADERS = \ clangformatconfigwidget.h \ clangformatindenter.h \ clangformatplugin.h \ - clangformatconstants.h + clangformatconstants.h \ + clangformatutils.h FORMS += \ clangformatconfigwidget.ui diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs index f470951e9d..37bb4c1f64 100644 --- a/src/plugins/clangformat/clangformat.qbs +++ b/src/plugins/clangformat/clangformat.qbs @@ -27,10 +27,11 @@ QtcPlugin { "clangformatconfigwidget.cpp", "clangformatconfigwidget.h", "clangformatconfigwidget.ui", + "clangformatconstants.h", "clangformatindenter.cpp", "clangformatindenter.h", "clangformatplugin.cpp", "clangformatplugin.h", - "clangformatconstants.h", + "clangformatutils.h", ] } diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index 60d58909e3..d2daa3adf7 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -25,6 +25,8 @@ #include "clangformatconfigwidget.h" + +#include "clangformatutils.h" #include "ui_clangformatconfigwidget.h" #include <clang/Format/Format.h> @@ -40,23 +42,6 @@ using namespace ProjectExplorer; namespace ClangFormat { -namespace Internal { - -static void createGlobalClangFormatFileIfNeeded(const QString &settingsDir) -{ - const QString fileName = settingsDir + "/.clang-format"; - if (QFile::exists(fileName)) - return; - - QFile file(fileName); - if (!file.open(QFile::WriteOnly)) - return; - - const clang::format::FormatStyle defaultStyle = clang::format::getLLVMStyle(); - const std::string configuration = clang::format::configurationAsText(defaultStyle); - file.write(configuration.c_str()); - file.close(); -} static void readTable(QTableWidget *table, std::istringstream &stream) { @@ -137,50 +122,75 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje { m_ui->setupUi(this); - std::string testFilePath; + initialize(); +} + +void ClangFormatConfigWidget::initialize() +{ + m_ui->projectHasClangFormat->show(); + m_ui->clangFormatOptionsTable->show(); + m_ui->applyButton->show(); + if (m_project && !m_project->projectDirectory().appendPath(".clang-format").exists()) { - m_ui->projectHasClangFormat->setText("No .clang-format file for the project"); + m_ui->projectHasClangFormat->setText(tr("No .clang-format file for the project.")); m_ui->clangFormatOptionsTable->hide(); m_ui->applyButton->hide(); + + connect(m_ui->createFileButton, &QPushButton::clicked, + this, [this]() { + createStyleFileIfNeeded(m_project->projectDirectory()); + initialize(); + }); return; } + m_ui->createFileButton->hide(); + + std::string testFilePath; if (m_project) { + m_ui->projectHasClangFormat->hide(); testFilePath = m_project->projectDirectory().appendPath("t.cpp").toString().toStdString(); connect(m_ui->applyButton, &QPushButton::clicked, this, &ClangFormatConfigWidget::apply); } else { + const Project *currentProject = SessionManager::startupProject(); + if (!currentProject + || !currentProject->projectDirectory().appendPath(".clang-format").exists()) { + m_ui->projectHasClangFormat->hide(); + } else { + m_ui->projectHasClangFormat->setText( + tr(" Current project has its own .clang-format file " + "and can be configured in Projects > Clang Format.")); + } const QString settingsDir = Core::ICore::userResourcePath(); - createGlobalClangFormatFileIfNeeded(settingsDir); + createStyleFileIfNeeded(Utils::FileName::fromString(settingsDir)); testFilePath = settingsDir.toStdString() + "/t.cpp"; m_ui->applyButton->hide(); } + fillTable(testFilePath); +} + +void ClangFormatConfigWidget::fillTable(const std::string &testFilePath) +{ llvm::Expected<clang::format::FormatStyle> formatStyle = - clang::format::getStyle("file", testFilePath, "LLVM", ""); - if (!formatStyle) - return; + clang::format::getStyle("file", testFilePath, "LLVM"); + std::string configText; + bool brokenConfig = false; + if (!formatStyle) { + handleAllErrors(formatStyle.takeError(), [](const llvm::ErrorInfoBase &) { + // do nothing + }); + configText = clang::format::configurationAsText(clang::format::getLLVMStyle()); + brokenConfig = true; + } else { + configText = clang::format::configurationAsText(*formatStyle); + } - const std::string configText = clang::format::configurationAsText(*formatStyle); std::istringstream stream(configText); - readTable(m_ui->clangFormatOptionsTable, stream); + if (brokenConfig) + apply(); - if (m_project) { - m_ui->projectHasClangFormat->hide(); - return; - } - - const Project *currentProject = SessionManager::startupProject(); - if (!currentProject || !currentProject->projectDirectory().appendPath(".clang-format").exists()) - m_ui->projectHasClangFormat->hide(); - - connect(SessionManager::instance(), &SessionManager::startupProjectChanged, - this, [this](ProjectExplorer::Project *project) { - if (project && project->projectDirectory().appendPath(".clang-format").exists()) - m_ui->projectHasClangFormat->show(); - else - m_ui->projectHasClangFormat->hide(); - }); } ClangFormatConfigWidget::~ClangFormatConfigWidget() = default; @@ -201,5 +211,4 @@ void ClangFormatConfigWidget::apply() file.close(); } -} // namespace Internal } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatconfigwidget.h b/src/plugins/clangformat/clangformatconfigwidget.h index 004e97128c..5694bd820b 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.h +++ b/src/plugins/clangformat/clangformatconfigwidget.h @@ -32,7 +32,6 @@ namespace ProjectExplorer { class Project; } namespace ClangFormat { -namespace Internal { namespace Ui { class ClangFormatConfigWidget; @@ -49,9 +48,11 @@ public: void apply(); private: + void initialize(); + void fillTable(const std::string &testFilePath); + ProjectExplorer::Project *m_project; std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui; }; -} // namespace Internal } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatconfigwidget.ui b/src/plugins/clangformat/clangformatconfigwidget.ui index e3efbea109..41774e133d 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.ui +++ b/src/plugins/clangformat/clangformatconfigwidget.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ClangFormat::Internal::ClangFormatConfigWidget</class> - <widget class="QWidget" name="ClangFormat::Internal::ClangFormatConfigWidget"> + <class>ClangFormat::ClangFormatConfigWidget</class> + <widget class="QWidget" name="ClangFormat::ClangFormatConfigWidget"> <property name="geometry"> <rect> <x>0</x> @@ -29,7 +29,7 @@ <item> <widget class="QLabel" name="projectHasClangFormat"> <property name="text"> - <string> Current project has its own .clang-format file and can be configured in Projects -> ClangFormat.</string> + <string/> </property> </widget> </item> @@ -39,6 +39,13 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> + <widget class="QPushButton" name="createFileButton"> + <property name="text"> + <string>Create Clang Format Configuration File</string> + </property> + </widget> + </item> + <item> <widget class="QPushButton" name="applyButton"> <property name="text"> <string>Apply</string> diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index 3164f4ad00..f6cc910be8 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -25,16 +25,21 @@ #include "clangformatindenter.h" +#include "clangformatutils.h" + #include <clang/Format/Format.h> #include <clang/Tooling/Core/Replacement.h> #include <coreplugin/icore.h> +#include <cpptools/cppmodelmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/session.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> #include <utils/hostosinfo.h> +#include <utils/textutils.h> +#include <utils/qtcassert.h> #include <llvm/Config/llvm-config.h> @@ -42,6 +47,8 @@ #include <QFileInfo> #include <QTextBlock> +#include <fstream> + using namespace clang; using namespace format; using namespace llvm; @@ -50,67 +57,35 @@ using namespace ProjectExplorer; using namespace TextEditor; namespace ClangFormat { -namespace Internal { namespace { -void adjustFormatStyleForLineBreak(format::FormatStyle &style, - int length, - int prevBlockSize, - bool prevBlockEndsWithPunctuation) +void adjustFormatStyleForLineBreak(format::FormatStyle &style) { - if (length > 0) - style.ColumnLimit = prevBlockSize; - style.AlwaysBreakBeforeMultilineStrings = true; -#if LLVM_VERSION_MAJOR >= 7 - style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; -#else - style.AlwaysBreakTemplateDeclarations = true; + style.DisableFormat = false; + style.ColumnLimit = 0; +#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED + style.KeepLineBreaksForNonEmptyLines = true; #endif - - style.AllowAllParametersOfDeclarationOnNextLine = true; - style.AllowShortBlocksOnASingleLine = true; - style.AllowShortCaseLabelsOnASingleLine = true; - style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty; - style.AllowShortIfStatementsOnASingleLine = true; - style.AllowShortLoopsOnASingleLine = true; - if (prevBlockEndsWithPunctuation) { - style.BreakBeforeBinaryOperators = FormatStyle::BOS_None; - style.BreakBeforeTernaryOperators = false; - style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon; - } else { - style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; - style.BreakBeforeTernaryOperators = true; - style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma; - } + style.MaxEmptyLinesToKeep = 2; } Replacements filteredReplacements(const Replacements &replacements, - unsigned int offset, - unsigned int lengthForFilter, - int extraOffsetToAdd, - int prevBlockLength) + int offset, + int lengthForFilter, + int extraOffsetToAdd) { Replacements filtered; for (const Replacement &replacement : replacements) { - unsigned int replacementOffset = replacement.getOffset(); + int replacementOffset = static_cast<int>(replacement.getOffset()); if (replacementOffset > offset + lengthForFilter) break; - if (offset > static_cast<unsigned int>(prevBlockLength) - && replacementOffset < offset - static_cast<unsigned int>(prevBlockLength)) - continue; - - if (lengthForFilter == 0 && replacement.getReplacementText().find('\n') == std::string::npos - && filtered.empty()) { - continue; - } - if (replacementOffset + 1 >= offset) - replacementOffset += static_cast<unsigned int>(extraOffsetToAdd); + replacementOffset += extraOffsetToAdd; Error error = filtered.add(Replacement(replacement.getFilePath(), - replacementOffset, + static_cast<unsigned int>(replacementOffset), replacement.getLength(), replacement.getReplacementText())); // Throws if error is not checked. @@ -120,92 +95,83 @@ Replacements filteredReplacements(const Replacements &replacements, return filtered; } -std::string assumedFilePath() +Utils::FileName styleConfigPath() { const Project *project = SessionManager::startupProject(); if (project && project->projectDirectory().appendPath(".clang-format").exists()) - return project->projectDirectory().appendPath("test.cpp").toString().toStdString(); + return project->projectDirectory(); - return QString(Core::ICore::userResourcePath() + "/test.cpp").toStdString(); + return Utils::FileName::fromString(Core::ICore::userResourcePath()); } -FormatStyle formatStyle() +FormatStyle formatStyle(Utils::FileName styleConfigPath) { - Expected<FormatStyle> style = format::getStyle("file", assumedFilePath(), "none", ""); + createStyleFileIfNeeded(styleConfigPath); + + Expected<FormatStyle> style = format::getStyle( + "file", styleConfigPath.appendPath("test.cpp").toString().toStdString(), "LLVM"); if (style) return *style; - return FormatStyle(); -} -Replacements replacements(const std::string &buffer, - unsigned int offset, - unsigned int length, - bool blockFormatting = false, - const QChar &typedChar = QChar::Null, - int extraOffsetToAdd = 0, - int prevBlockLength = 1, - bool prevBlockEndsWithPunctuation = false) -{ - FormatStyle style = formatStyle(); + handleAllErrors(style.takeError(), [](const ErrorInfoBase &) { + // do nothing + }); - if (blockFormatting && typedChar == QChar::Null) - adjustFormatStyleForLineBreak(style, length, prevBlockLength, prevBlockEndsWithPunctuation); + return format::getLLVMStyle(); +} - std::vector<Range> ranges{{offset, length}}; - FormattingAttemptStatus status; +void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock) +{ + QTextBlock prevBlock = currentBlock.previous(); + while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) + prevBlock = prevBlock.previous(); - Replacements replacements = reformat(style, buffer, ranges, assumedFilePath(), &status); + if (prevBlock.text().trimmed().isEmpty()) + return; - if (!status.FormatComplete) - Replacements(); + const QString initialText = prevBlock.text(); + if (!initialText.at(initialText.size() - 1).isSpace()) + return; - unsigned int lengthForFilter = 0; - if (!blockFormatting) - lengthForFilter = length; + int extraSpaceCount = 1; + for (int i = initialText.size() - 2; i >= 0; --i) { + if (!initialText.at(i).isSpace()) + break; + ++extraSpaceCount; + } - return filteredReplacements(replacements, - offset, - lengthForFilter, - extraOffsetToAdd, - prevBlockLength); + QTextCursor cursor(prevBlock); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, + initialText.size() - extraSpaceCount); + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount); + cursor.removeSelectedText(); + cursor.endEditBlock(); } -void applyReplacements(QTextDocument *doc, - const std::string &stdStrBuffer, - const tooling::Replacements &replacements, - int totalShift) +// Returns the total langth of previous lines with pure whitespace. +int previousEmptyLinesLength(const QTextBlock ¤tBlock) { - if (replacements.empty()) - return; - - QTextCursor editCursor(doc); - int fullOffsetDiff = 0; - for (const Replacement &replacement : replacements) { - const int utf16Offset - = QString::fromStdString(stdStrBuffer.substr(0, replacement.getOffset())).length() - + totalShift + fullOffsetDiff; - const int utf16Length = QString::fromStdString(stdStrBuffer.substr(replacement.getOffset(), - replacement.getLength())) - .length(); - const QString replacementText = QString::fromStdString(replacement.getReplacementText()); - - editCursor.beginEditBlock(); - editCursor.setPosition(utf16Offset); - editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, utf16Length); - editCursor.removeSelectedText(); - editCursor.insertText(replacementText); - editCursor.endEditBlock(); - fullOffsetDiff += replacementText.length() - utf16Length; + int length{0}; + QTextBlock prevBlock = currentBlock.previous(); + while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) { + length += prevBlock.text().length() + 1; + prevBlock = prevBlock.previous(); } + + return length; } -// Returns offset shift. -int modifyToIndentEmptyLines(QString &buffer, int &offset, int &length, const QTextBlock &block) +void modifyToIndentEmptyLines(QByteArray &buffer, int &offset, int &length, const QTextBlock &block) { + const QString blockText = block.text().trimmed(); + const bool closingParenBlock = blockText.startsWith(')'); + if (length != 0 && !closingParenBlock) + return; + //This extra text works for the most cases. - QString extraText("a;"); + QByteArray extraText("a;"); - const QString blockText = block.text().trimmed(); // Search for previous character QTextBlock prevBlock = block.previous(); while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) @@ -213,7 +179,6 @@ int modifyToIndentEmptyLines(QString &buffer, int &offset, int &length, const QT if (prevBlock.text().endsWith(',')) extraText = "int a,"; - const bool closingParenBlock = blockText.startsWith(')'); if (closingParenBlock) { if (prevBlock.text().endsWith(',')) extraText = "int a"; @@ -221,97 +186,151 @@ int modifyToIndentEmptyLines(QString &buffer, int &offset, int &length, const QT extraText = "&& a"; } - if (length == 0 || closingParenBlock) { - length += extraText.length(); - buffer.insert(offset, extraText); - } - - if (blockText.startsWith('}')) { - buffer.insert(offset - 1, extraText); - offset += extraText.size(); - return extraText.size(); - } - - return 0; + length += extraText.length(); + buffer.insert(offset, extraText); } -// Returns first non-empty block (searches from current block backwards). -QTextBlock clearFirstNonEmptyBlockFromExtraSpaces(const QTextBlock ¤tBlock) +static const int kMaxLinesFromCurrentBlock = 200; + +Replacements replacements(QByteArray buffer, + int utf8Offset, + int utf8Length, + const QTextBlock *block = nullptr, + const QChar &typedChar = QChar::Null) { - QTextBlock prevBlock = currentBlock.previous(); - while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) - prevBlock = prevBlock.previous(); + Utils::FileName stylePath = styleConfigPath(); + FormatStyle style = formatStyle(stylePath); + + int extraOffset = 0; + if (block) { + if (block->blockNumber() > kMaxLinesFromCurrentBlock) { + extraOffset = Utils::Text::utf8NthLineOffset( + block->document(), buffer, block->blockNumber() - kMaxLinesFromCurrentBlock); + } + buffer = buffer.mid(extraOffset, + std::min(buffer.size(), utf8Offset + kMaxLinesFromCurrentBlock) + - extraOffset); + utf8Offset -= extraOffset; - if (prevBlock.text().trimmed().isEmpty()) - return prevBlock; + const int emptySpaceLength = previousEmptyLinesLength(*block); + utf8Offset -= emptySpaceLength; + buffer.remove(utf8Offset, emptySpaceLength); - const QString initialText = prevBlock.text(); - if (!initialText.at(initialText.length() - 1).isSpace()) - return prevBlock; + extraOffset += emptySpaceLength; - QTextCursor cursor(prevBlock); - cursor.beginEditBlock(); - cursor.movePosition(QTextCursor::EndOfBlock); - cursor.movePosition(QTextCursor::Left); + adjustFormatStyleForLineBreak(style); + if (typedChar == QChar::Null) + modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, *block); + } - while (cursor.positionInBlock() >= 0 && initialText.at(cursor.positionInBlock()).isSpace()) - cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); - if (cursor.hasSelection()) - cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - cursor.endEditBlock(); - return prevBlock; + std::vector<Range> ranges{{static_cast<unsigned int>(utf8Offset), + static_cast<unsigned int>(utf8Length)}}; + FormattingAttemptStatus status; + + const std::string assumedFilePath + = stylePath.appendPath("test.cpp").toString().toStdString(); + Replacements replacements = reformat(style, buffer.data(), ranges, assumedFilePath, &status); + + if (!status.FormatComplete) + Replacements(); + + int lengthForFilter = 0; + if (block == nullptr) + lengthForFilter = utf8Length; + + return filteredReplacements(replacements, + utf8Offset, + lengthForFilter, + extraOffset); } -// Returns the total langth of previous lines with pure whitespace. -int previousEmptyLinesLength(const QTextBlock ¤tBlock) +Utils::LineColumn utf16LineColumn(const QTextBlock &block, + int blockOffsetUtf8, + const QByteArray &utf8Buffer, + int utf8Offset) { - int length{0}; - QTextBlock prevBlock = currentBlock.previous(); - while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) { - length += prevBlock.text().length() + 1; - prevBlock = prevBlock.previous(); + if (utf8Offset < blockOffsetUtf8 - 1) + return Utils::LineColumn(); + + if (utf8Offset == blockOffsetUtf8 - 1) { + const int lineStart = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1; + const QByteArray lineText = utf8Buffer.mid(lineStart, utf8Offset - lineStart); + return Utils::LineColumn(block.blockNumber(), QString::fromUtf8(lineText).size() + 1); } - return length; -} + int pos = blockOffsetUtf8; + int prevPos = pos; + int line = block.blockNumber(); // Start with previous line. + while (pos != -1 && pos <= utf8Offset) { + // Find the first pos which comes after offset and take the previous line. + ++line; + prevPos = pos; + pos = utf8Buffer.indexOf('\n', pos); + if (pos != -1) + ++pos; + } -static constexpr const int MinCharactersBeforeCurrentInBuffer = 200; -static constexpr const int MaxCharactersBeforeCurrentInBuffer = 500; + const QByteArray lineText = utf8Buffer.mid(prevPos, utf8Offset - prevPos); + return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1); +} -int startOfIndentationBuffer(const QString &buffer, int start) +tooling::Replacements utf16Replacements(const QTextBlock &block, + int blockOffsetUtf8, + const QByteArray &utf8Buffer, + const tooling::Replacements &replacements) { - if (start < MaxCharactersBeforeCurrentInBuffer) - return 0; - - auto it = buffer.cbegin() + (start - MinCharactersBeforeCurrentInBuffer); - for (; it != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer); --it) { - if (*it == '{') { - // Find the start of it's line. - for (auto inner = it; - inner != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer); - --inner) { - if (*inner == '\n') - return inner + 1 - buffer.cbegin(); - } + tooling::Replacements convertedReplacements; + for (const Replacement &replacement : replacements) { + const Utils::LineColumn lineColUtf16 = utf16LineColumn( + block, blockOffsetUtf8, utf8Buffer, static_cast<int>(replacement.getOffset())); + if (!lineColUtf16.isValid()) + continue; + const int utf16Offset = Utils::Text::positionInText(block.document(), + lineColUtf16.line, + lineColUtf16.column); + const int utf16Length = QString::fromUtf8( + utf8Buffer.mid(static_cast<int>(replacement.getOffset()), + static_cast<int>(replacement.getLength()))).size(); + Error error = convertedReplacements.add( + Replacement(replacement.getFilePath(), + static_cast<unsigned int>(utf16Offset), + static_cast<unsigned int>(utf16Length), + replacement.getReplacementText())); + // Throws if error is not checked. + if (error) break; - } } - return it - buffer.cbegin(); + return convertedReplacements; } -int nextEndingScopePosition(const QString &buffer, int start) +void applyReplacements(const QTextBlock &block, + int blockOffsetUtf8, + const QByteArray &utf8Buffer, + const tooling::Replacements &replacements) { - if (start >= buffer.size() - 1) - return buffer.size() - 1; + if (replacements.empty()) + return; - for (auto it = buffer.cbegin() + (start + 1); it != buffer.cend(); ++it) { - if (*it == '}') - return it - buffer.cbegin(); - } + tooling::Replacements convertedReplacements = utf16Replacements(block, + blockOffsetUtf8, + utf8Buffer, + replacements); - return buffer.size() - 1; + int fullOffsetShift = 0; + QTextCursor editCursor(block); + for (const Replacement &replacement : convertedReplacements) { + const QString replacementString = QString::fromStdString(replacement.getReplacementText()); + const int replacementLength = static_cast<int>(replacement.getLength()); + editCursor.beginEditBlock(); + editCursor.setPosition(static_cast<int>(replacement.getOffset()) + fullOffsetShift); + editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + replacementLength); + editCursor.removeSelectedText(); + editCursor.insertText(replacementString); + editCursor.endEditBlock(); + fullOffsetShift += replacementString.length() - replacementLength; + } } } // anonymous namespace @@ -342,30 +361,34 @@ void ClangFormatIndenter::indent(QTextDocument *doc, bool autoTriggered) { if (typedChar == QChar::Null && (cursor.hasSelection() || !autoTriggered)) { - TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget(); - int offset; - int length; + int utf8Offset; + int utf8Length; + const QByteArray buffer = doc->toPlainText().toUtf8(); if (cursor.hasSelection()) { const QTextBlock start = doc->findBlock(cursor.selectionStart()); const QTextBlock end = doc->findBlock(cursor.selectionEnd()); - offset = start.position(); - length = std::max(0, end.position() + end.length() - start.position() - 1); + utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, start.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return;); + utf8Length = + Utils::Text::textAt( + QTextCursor(doc), + start.position(), + std::max(0, end.position() + end.length() - start.position() - 1)) + .toUtf8().size(); + applyReplacements(start, + utf8Offset, + buffer, + replacements(buffer, utf8Offset, utf8Length)); } else { const QTextBlock block = cursor.block(); - offset = block.position(); - length = std::max(0, block.length() - 1); + utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return;); + utf8Length = block.text().toUtf8().size(); + applyReplacements(block, + utf8Offset, + buffer, + replacements(buffer, utf8Offset, utf8Length)); } - QString buffer = editor->toPlainText(); - const int totalShift = startOfIndentationBuffer(buffer, offset); - const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1; - buffer = buffer.mid(totalShift, cutAtPos - totalShift); - offset -= totalShift; - const std::string stdStrBefore = buffer.left(offset).toStdString(); - const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString(); - applyReplacements(doc, - stdStrBuffer, - replacements(stdStrBuffer, stdStrBefore.length(), length), - totalShift); } else { indentBlock(doc, cursor.block(), typedChar, tabSettings); } @@ -389,46 +412,16 @@ void ClangFormatIndenter::indentBlock(QTextDocument *doc, if (!editor) return; - const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block); + trimFirstNonEmptyBlock(block); + const QByteArray buffer = doc->toPlainText().toUtf8(); + const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return;); + const int utf8Length = block.text().toUtf8().size(); - int offset = block.position(); - int length = std::max(0, block.length() - 1); - QString buffer = editor->toPlainText(); - - int emptySpaceLength = previousEmptyLinesLength(block); - offset -= emptySpaceLength; - buffer.remove(offset, emptySpaceLength); - - int extraPrevBlockLength{0}; - int prevBlockTextLength{1}; - bool prevBlockEndsWithPunctuation = false; - if (typedChar == QChar::Null) { - extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block); - - const QString prevBlockText = prevBlock.text(); - prevBlockEndsWithPunctuation = prevBlockText.size() > 0 - ? prevBlockText.at(prevBlockText.size() - 1).isPunct() - : false; - prevBlockTextLength = prevBlockText.size() + 1; - } - - const int totalShift = startOfIndentationBuffer(buffer, offset); - const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1; - buffer = buffer.mid(totalShift, cutAtPos - totalShift); - offset -= totalShift; - const std::string stdStrBefore = buffer.left(offset).toStdString(); - const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString(); - applyReplacements(doc, - stdStrBuffer, - replacements(stdStrBuffer, - stdStrBefore.length(), - length, - true, - typedChar, - emptySpaceLength - extraPrevBlockLength, - prevBlockTextLength + extraPrevBlockLength, - prevBlockEndsWithPunctuation), - totalShift); + applyReplacements(block, + utf8Offset, + buffer, + replacements(buffer, utf8Offset, utf8Length, &block, typedChar)); } int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &) @@ -437,36 +430,14 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta if (!editor) return -1; - const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block); - - int offset = block.position(); - int length = std::max(0, block.length() - 1); - QString buffer = editor->toPlainText(); - - int emptySpaceLength = previousEmptyLinesLength(block); - offset -= emptySpaceLength; - buffer.replace(offset, emptySpaceLength, ""); - int extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block); - - const QString prevBlockText = prevBlock.text(); - bool prevBlockEndsWithPunctuation = prevBlockText.size() > 0 - ? prevBlockText.at(prevBlockText.size() - 1).isPunct() - : false; - - const int totalShift = startOfIndentationBuffer(buffer, offset); - const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1; - buffer = buffer.mid(totalShift, cutAtPos - totalShift); - offset -= totalShift; - const std::string stdStrBefore = buffer.left(offset).toStdString(); - const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString(); - Replacements toReplace = replacements(stdStrBuffer, - stdStrBefore.length(), - length, - true, - QChar::Null, - emptySpaceLength - extraPrevBlockLength, - prevBlockText.size() + extraPrevBlockLength + 1, - prevBlockEndsWithPunctuation); + trimFirstNonEmptyBlock(block); + const QTextDocument *doc = block.document(); + const QByteArray buffer = doc->toPlainText().toUtf8(); + const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return 0;); + const int utf8Length = block.text().toUtf8().size(); + + Replacements toReplace = replacements(buffer, utf8Offset, utf8Length, &block); if (toReplace.empty()) return -1; @@ -481,7 +452,7 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta TabSettings ClangFormatIndenter::tabSettings() const { - FormatStyle style = formatStyle(); + FormatStyle style = formatStyle(styleConfigPath()); TabSettings tabSettings; switch (style.UseTab) { @@ -495,8 +466,8 @@ TabSettings ClangFormatIndenter::tabSettings() const tabSettings.m_tabPolicy = TabSettings::MixedTabPolicy; } - tabSettings.m_tabSize = style.TabWidth; - tabSettings.m_indentSize = style.IndentWidth; + tabSettings.m_tabSize = static_cast<int>(style.TabWidth); + tabSettings.m_indentSize = static_cast<int>(style.IndentWidth); if (style.AlignAfterOpenBracket) tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithSpaces; @@ -506,5 +477,4 @@ TabSettings ClangFormatIndenter::tabSettings() const return tabSettings; } -} // namespace Internal } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatindenter.h b/src/plugins/clangformat/clangformatindenter.h index 00dde55c16..dfe3b5b95f 100644 --- a/src/plugins/clangformat/clangformatindenter.h +++ b/src/plugins/clangformat/clangformatindenter.h @@ -28,7 +28,6 @@ #include <texteditor/indenter.h> namespace ClangFormat { -namespace Internal { class ClangFormatIndenter final : public TextEditor::Indenter { @@ -42,9 +41,9 @@ public: const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings) override; void indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings) override; + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) override; int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; bool isElectricCharacter(const QChar &ch) const override; @@ -53,5 +52,4 @@ public: TextEditor::TabSettings tabSettings() const override; }; -} // namespace Internal } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatplugin.cpp b/src/plugins/clangformat/clangformatplugin.cpp index 3da6cbfa08..f421b686ac 100644 --- a/src/plugins/clangformat/clangformatplugin.cpp +++ b/src/plugins/clangformat/clangformatplugin.cpp @@ -45,6 +45,8 @@ #include <projectexplorer/projectpanelfactory.h> #include <projectexplorer/target.h> +#include <clang/Format/Format.h> + #include <QAction> #include <QDebug> #include <QMainWindow> @@ -56,7 +58,6 @@ using namespace ProjectExplorer; namespace ClangFormat { -namespace Internal { class ClangFormatOptionsPage : public Core::IOptionsPage { @@ -98,7 +99,7 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS { Q_UNUSED(arguments); Q_UNUSED(errorString); - +#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED m_optionsPage = std::make_unique<ClangFormatOptionsPage>(); auto panelFactory = new ProjectPanelFactory(); @@ -112,9 +113,8 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS CppTools::CppModelManager::instance()->setCppIndenterCreator([]() { return new ClangFormatIndenter(); }); - +#endif return true; } -} // namespace Internal } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatplugin.h b/src/plugins/clangformat/clangformatplugin.h index e86be64589..00b59cd950 100644 --- a/src/plugins/clangformat/clangformatplugin.h +++ b/src/plugins/clangformat/clangformatplugin.h @@ -30,7 +30,6 @@ #include <memory> namespace ClangFormat { -namespace Internal { class ClangFormatOptionsPage; @@ -50,5 +49,4 @@ private: std::unique_ptr<ClangFormatOptionsPage> m_optionsPage; }; -} // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangformat/clangformatutils.h b/src/plugins/clangformat/clangformatutils.h new file mode 100644 index 0000000000..ad4e21f889 --- /dev/null +++ b/src/plugins/clangformat/clangformatutils.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 <utils/fileutils.h> +#include <clang/Format/Format.h> + +#include <QFile> + +#include <fstream> + +namespace ClangFormat { + +inline void createStyleFileIfNeeded(Utils::FileName styleConfigPath) +{ + const QString configFile = styleConfigPath.appendPath(".clang-format").toString(); + if (QFile::exists(configFile)) + return; + + clang::format::FormatStyle newStyle = clang::format::getLLVMStyle(); + std::fstream newStyleFile(configFile.toStdString(), std::fstream::out); + if (newStyleFile.is_open()) { + newStyleFile << clang::format::configurationAsText(newStyle); + newStyleFile.close(); + } +} + +} diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp index 3c16ccfd56..de81af7535 100644 --- a/src/plugins/cppcheck/cppcheckoptions.cpp +++ b/src/plugins/cppcheck/cppcheckoptions.cpp @@ -71,7 +71,7 @@ public: variableChooser->addSupportedWidget (m_customArguments); m_unusedFunction->setToolTip(tr("Disables multithreaded check.")); - m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths." + m_ignorePatterns->setToolTip(tr("Comma-separated wildcards of full file paths. " "Files still can be checked if others include them.")); m_addIncludePaths->setToolTip(tr("Can find missing includes but makes " "checking slower. Use only when needed.")); diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 5db9dd601d..603ff3174b 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -1326,6 +1326,11 @@ void CppModelManager::renameIncludes(const QString &oldFileName, const QString & } } +void CppModelManager::setBackendJobsPostponed(bool postponed) +{ + d->m_activeModelManagerSupport->setBackendJobsPostponed(postponed); +} + void CppModelManager::onCoreAboutToClose() { Core::ProgressManager::cancelTasks(CppTools::Constants::TASK_INDEX); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 66cc133146..bafc3b3271 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -238,6 +238,8 @@ public: void renameIncludes(const QString &oldFileName, const QString &newFileName); + void setBackendJobsPostponed(bool postponed); + signals: /// Project data might be locked while this is emitted. void aboutToRemoveFiles(const QStringList &files); diff --git a/src/plugins/cpptools/cppmodelmanagersupport.h b/src/plugins/cpptools/cppmodelmanagersupport.h index c5457cd51f..f7ff56ca3b 100644 --- a/src/plugins/cpptools/cppmodelmanagersupport.h +++ b/src/plugins/cpptools/cppmodelmanagersupport.h @@ -60,6 +60,7 @@ public: virtual FollowSymbolInterface &followSymbolInterface() = 0; virtual RefactoringEngineInterface &refactoringEngineInterface() = 0; virtual std::unique_ptr<AbstractOverviewModel> createOverviewModel() = 0; + virtual void setBackendJobsPostponed(bool yesno) = 0; }; class CPPTOOLS_EXPORT ModelManagerSupportProvider diff --git a/src/plugins/cpptools/cppmodelmanagersupportinternal.h b/src/plugins/cpptools/cppmodelmanagersupportinternal.h index cfa326e778..b01f38f4b8 100644 --- a/src/plugins/cpptools/cppmodelmanagersupportinternal.h +++ b/src/plugins/cpptools/cppmodelmanagersupportinternal.h @@ -47,6 +47,7 @@ public: FollowSymbolInterface &followSymbolInterface() final; RefactoringEngineInterface &refactoringEngineInterface() final; std::unique_ptr<AbstractOverviewModel> createOverviewModel() final; + void setBackendJobsPostponed(bool) final {} private: QScopedPointer<CppCompletionAssistProvider> m_completionAssistProvider; diff --git a/src/plugins/cpptools/cpptools_clangtidychecks.h b/src/plugins/cpptools/cpptools_clangtidychecks.h index 9fc1fc516d..20dcbdf120 100644 --- a/src/plugins/cpptools/cpptools_clangtidychecks.h +++ b/src/plugins/cpptools/cpptools_clangtidychecks.h @@ -50,6 +50,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT { "android-", { + "tring-find-startswith", { "cloexec-", { @@ -76,7 +77,8 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "open", "socket" } - } + }, + "comparison-in-temp-failure-retry" } }, { @@ -93,16 +95,69 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "bool-pointer-implicit-conversion", "copy-constructor-init", "dangling-handle", + "exception-escape", "fold-init-type", "forward-declaration-namespace", + "forwarding-reference-overload", "inaccurate-erase", + "incorrect-roundings", "integer-division", - "misplaced-operator-in-strlen-in-alloc", + "lambda-function-name", + { + "macro-", + { + "parentheses", + "repeated-side-effects" + } + }, + { + "misplaced-", + { + "operator-in-strlen-in-alloc", + "widening-cast" + } + }, "move-forwarding-reference", "multiple-statement-macro", - "string-constructor", - "suspicious-memset-usage", + "narrowing-conversions", + "parent-virtual-call", + { + "sizeof-", + { + "container", + "expression" + } + }, + { + "string-", + { + "constructor", + "integer-assignment", + "literal-with-embedded-nul" + } + }, + { + "suspicious-", + { + "enum-usage", + "memset-usage", + "missing-comma", + "semicolon", + "string-compare" + } + }, + "swapped-arguments", + "terminating-continue", + "throw-keyword-missing", "undefined-memory-manipulation", + "undelegated-constructor", + { + "unused-", + { + "raii", + "return-value" + } + }, "use-after-move", "virtual-near-miss" } @@ -126,14 +181,22 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "fio38-c", "flp30-c", "msc30-c", + "msc32-c", "msc50-cpp", + "msc51-cpp", "oop11-cpp" } }, { "clang-analyzer-", { - "apiModeling.google.GTest", + { + "apiModeling.", + { + "TrustNonnull", + "google.GTest" + } + }, { "core.", { @@ -168,6 +231,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT { "cplusplus.", { + "InnerPointer", "NewDelete", "NewDeleteLeaks", "SelfAssignment" @@ -207,7 +271,13 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT } } }, - "performance.Padding", + { + "performance.", + { + "GCDAntipattern", + "Padding" + } + }, "portability.UnixAPI" } }, @@ -222,6 +292,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "cocoa.", { "AtSync", + "AutoreleaseWrite", "ClassRelease", "Dealloc", "IncompatibleMethodTypes", @@ -233,6 +304,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "NonNilReturnValue", "ObjCGenerics", "RetainCount", + "RunLoopAutoreleaseLeak", "SelfInit", "SuperDealloc", "UnusedIvars", @@ -264,6 +336,9 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "insecureAPI.", { "UncheckedReturn", + "bcmp", + "bcopy", + "bzero", "getpw", "gets", "mkstemp", @@ -306,8 +381,10 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT { "cppcoreguidelines-", { + "avoid-goto", "c-copy-assignment-signature", "interfaces-global-init", + "narrowing-conversions", "no-malloc", "owning-memory", { @@ -343,7 +420,12 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "fuchsia-", { "default-arguments", + "header-anon-namespaces", + "multiple-inheritance", "overloaded-operator", + "restrict-system-includes", + "statically-constructed-objects", + "trailing-return", "virtual-inheritance" } }, @@ -375,7 +457,6 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "casting", "function-size", "namespace-comments", - "redundant-smartptr-get", "todo" } }, @@ -383,7 +464,6 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "runtime-", { "int", - "member-string-references", "operator", "references" } @@ -393,6 +473,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT { "hicpp-", { + "avoid-goto", "braces-around-statements", "deprecated-headers", "exception-baseclass", @@ -401,6 +482,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "invalid-access-moved", "member-init", "move-const-arg", + "multiway-paths-covered", "named-parameter", "new-delete-operators", { @@ -449,62 +531,19 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "misc-", { "definitions-in-headers", - "forwarding-reference-overload", - "incorrect-roundings", - "lambda-function-name", - { - "macro-", - { - "parentheses", - "repeated-side-effects" - } - }, - { - "misplaced-", - { - "const", - "widening-cast" - } - }, + "misplaced-const", "new-delete-overloads", "non-copyable-objects", "redundant-expression", - { - "sizeof-", - { - "container", - "expression" - } - }, "static-assert", - { - "string-", - { - "compare", - "integer-assignment", - "literal-with-embedded-nul" - } - }, - { - "suspicious-", - { - "enum-usage", - "missing-comma", - "semicolon", - "string-compare" - } - }, - "swapped-arguments", "throw-by-value-catch-by-reference", "unconventional-assign-operator", - "undelegated-constructor", "uniqueptr-reset-release", { "unused-", { "alias-decls", "parameters", - "raii", "using-decls" } } @@ -554,6 +593,7 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "nullptr", "override", "transparent-functors", + "uncaught-exceptions", "using" } } @@ -609,7 +649,8 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "copy-initialization", "value-param" } - } + }, + "simd-intrinsics" } }, { @@ -646,7 +687,13 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT } } }, - "simplify-boolean-expr", + { + "simplify-", + { + "boolean-expr", + "subscript-expr" + } + }, { "static-", { @@ -654,7 +701,9 @@ static const TidyNode CLANG_TIDY_CHECKS_ROOT "definition-in-anonymous-namespace" } }, - "uniqueptr-delete-release" + "string-compare", + "uniqueptr-delete-release", + "rary-objects" } } } diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 9a8ba32fa1..db5b6332ed 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -1709,7 +1709,6 @@ void BreakHandler::removeBreakpoint(const Breakpoint &bp) break; case BreakpointInserted: case BreakpointInsertionProceeding: - bp->setState(BreakpointRemoveRequested); requestBreakpointRemoval(bp); break; case BreakpointNew: @@ -2565,17 +2564,17 @@ bool BreakpointManager::setData(const QModelIndex &idx, const QVariant &value, i // setCurrentIndex(index(row, 0)); FIXME return true; } -// if (kev->key() == Qt::Key_Space) { -// const QModelIndexList selectedIds = ev.selectedRows(); -// if (!selectedIds.isEmpty()) { -// const GlobalBreakpoints gbps = findBreakpointsByIndex(selectedIds); -// const bool isEnabled = gbps.isEmpty() || gbps.at(0)->isEnabled(); -// for (GlobalBreakpoint gbp : gbps) -// gbp->m_parameters.enabled = isEnabled; + if (kev->key() == Qt::Key_Space) { + const QModelIndexList selectedIds = ev.selectedRows(); + if (!selectedIds.isEmpty()) { + const GlobalBreakpoints gbps = findBreakpointsByIndex(selectedIds); + const bool isEnabled = gbps.isEmpty() || gbps.at(0)->isEnabled(); + for (GlobalBreakpoint gbp : gbps) + gbp->setEnabled(!isEnabled); // scheduleSynchronization(); -// return true; -// } -// } + return true; + } + } } if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) { diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h index 6df2080429..dee8b3e69b 100644 --- a/src/plugins/debugger/breakhandler.h +++ b/src/plugins/debugger/breakhandler.h @@ -98,7 +98,7 @@ private: void updateMarker(); void updateMarkerIcon(); void destroyMarker(); - void scheduleSynchronization(); +// void scheduleSynchronization(); QPointer<DebuggerEngine> usingEngine() const; bool isEngineRunning() const; diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 144dafc800..70a6b90914 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -224,7 +224,7 @@ void CdbEngine::init() m_stopMode = NoStopRequested; m_nextCommandToken = 0; m_currentBuiltinResponseToken = -1; - m_operateByInstruction = true; // Default CDB setting. + m_lastOperateByInstruction = true; // Default CDB setting. m_hasDebuggee = false; m_sourceStepInto = false; m_watchPointX = m_watchPointY = 0; @@ -266,14 +266,13 @@ void CdbEngine::init() CdbEngine::~CdbEngine() = default; -void CdbEngine::operateByInstructionTriggered(bool operateByInstruction) +void CdbEngine::adjustOperateByInstruction(bool operateByInstruction) { - DebuggerEngine::operateByInstructionTriggered(operateByInstruction); - if (m_operateByInstruction == operateByInstruction) + if (m_lastOperateByInstruction == operateByInstruction) return; - m_operateByInstruction = operateByInstruction; - runCommand({QLatin1String(m_operateByInstruction ? "l-t" : "l+t"), NoFlags}); - runCommand({QLatin1String(m_operateByInstruction ? "l-s" : "l+s"), NoFlags}); + m_lastOperateByInstruction = operateByInstruction; + runCommand({QLatin1String(m_lastOperateByInstruction ? "l-t" : "l+t"), NoFlags}); + runCommand({QLatin1String(m_lastOperateByInstruction ? "l-s" : "l+s"), NoFlags}); } bool CdbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const @@ -521,7 +520,7 @@ void CdbEngine::handleInitialSessionIdle() const DebuggerRunParameters &rp = runParameters(); if (!rp.commandsAfterConnect.isEmpty()) runCommand({rp.commandsAfterConnect, NoFlags}); - operateByInstructionTriggered(operatesByInstruction()); + //operateByInstructionTriggered(operatesByInstruction()); // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit // (attemptBreakpointSynchronization() will be directly called then) if (rp.breakOnMain) { @@ -758,10 +757,11 @@ bool CdbEngine::hasCapability(unsigned cap) const |AdditionalQmlStackCapability); } -void CdbEngine::executeStep() +void CdbEngine::executeStepIn(bool byInstruction) { - if (!m_operateByInstruction) + if (!m_lastOperateByInstruction) m_sourceStepInto = true; // See explanation at handleStackTrace(). + adjustOperateByInstruction(byInstruction); runCommand({"t", NoFlags}); // Step into-> t (trace) STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); @@ -774,23 +774,14 @@ void CdbEngine::executeStepOut() notifyInferiorRunRequested(); } -void CdbEngine::executeNext() +void CdbEngine::executeStepOver(bool byInstruction) { + adjustOperateByInstruction(byInstruction); runCommand({"p", NoFlags}); // Step over -> p STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") notifyInferiorRunRequested(); } -void CdbEngine::executeStepI() -{ - executeStep(); -} - -void CdbEngine::executeNextI() -{ - executeNext(); -} - void CdbEngine::continueInferior() { STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested") @@ -1756,8 +1747,9 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason, *message = bp->msgWatchpointByExpressionTriggered(bp->expression(), tid); else *message = bp->msgBreakpointTriggered(tid); - rc |= StopReportStatusMessage|StopNotifyStop; + rc |= StopReportStatusMessage; } + rc |= StopNotifyStop; return rc; } if (reason == "exception") { @@ -1848,7 +1840,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT if (stack.isValid()) { switch (parseStackTrace(stack, sourceStepInto)) { case ParseStackStepInto: // Hit on a frame while step into, see parseStackTrace(). - executeStep(); + executeStepIn(operatesByInstruction()); return; case ParseStackStepOut: // Hit on a frame with no source while step into. executeStepOut(); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index c367925fd3..16b71b954b 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -41,7 +41,7 @@ class CdbCommand; struct MemoryViewCookie; class StringInputStream; -class CdbEngine : public DebuggerEngine +class CdbEngine : public CppDebuggerEngine { Q_OBJECT @@ -64,11 +64,9 @@ public: void watchPoint(const QPoint &) override; void setRegisterValue(const QString &name, const QString &value) override; - void executeStep() override; + void executeStepOver(bool byInstruction) override; + void executeStepIn(bool byInstruction) override; void executeStepOut() override; - void executeNext() override; - void executeStepI() override; - void executeNextI() override; void continueInferior() override; void interruptInferior() override; @@ -113,7 +111,7 @@ private: void processError(); void processFinished(); void runCommand(const DebuggerCommand &cmd) override; - void operateByInstructionTriggered(bool) override; + void adjustOperateByInstruction(bool); void createFullBacktrace(); @@ -217,7 +215,7 @@ private: int m_currentBuiltinResponseToken = -1; QMap<QString, NormalizedSourceFileName> m_normalizedFileCache; const QString m_extensionCommandPrefix; //!< Library name used as prefix - bool m_operateByInstruction = true; // Default CDB setting. + bool m_lastOperateByInstruction = true; // Default CDB setting. bool m_hasDebuggee = false; enum Wow64State { wow64Uninitialized, diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index bc9bf9da18..294e806e1a 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -37,6 +37,8 @@ const char MODE_DEBUG[] = "Mode.Debug"; const char C_DEBUGMODE[] = "Debugger.DebugMode"; const char C_CPPDEBUGGER[] = "Gdb Debugger"; const char C_QMLDEBUGGER[] = "Qml/JavaScript Debugger"; +const char C_DEBUGGER_RUNNING[] = "Debugger.Running"; +const char C_DEBUGGER_NOTRUNNING[] = "Debugger.NotRunning"; const char PRESET_PERSPECTIVE_ID[] = "Debugger.Perspective.Preset"; @@ -54,6 +56,7 @@ const char ABORT[] = "Debugger.Abort"; const char STEP[] = "Debugger.StepLine"; const char STEPOUT[] = "Debugger.StepOut"; const char NEXT[] = "Debugger.NextLine"; +const char START_AND_BREAK_ON_MAIN[]= "Debugger.StartAndBreakOnMain"; const char REVERSE[] = "Debugger.ReverseDirection"; const char RESET[] = "Debugger.Reset"; const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction"; diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 992fc8cf3a..301a2801be 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -488,7 +488,7 @@ public: QAction m_breakAction{tr("Toggle Breakpoint")}; QAction m_resetAction{tr("Restart Debugging")}; OptionalAction m_operateByInstructionAction{tr("Operate by Instruction")}; - QAction m_recordForReverseOperationAction{tr("Record information to allpow reversal of Direction")}; + QAction m_recordForReverseOperationAction{tr("Record Information to Allow Reversal of Direction")}; OptionalAction m_operateInReverseDirectionAction{tr("Reverse Direction")}; OptionalAction m_snapshotAction{tr("Take Snapshot of Process State")}; @@ -526,7 +526,7 @@ void DebuggerEnginePrivate::setupViews() m_operateByInstructionAction.setIcon(Debugger::Icons::SINGLE_INSTRUCTION_MODE.icon()); m_operateByInstructionAction.setCheckable(true); m_operateByInstructionAction.setChecked(false); - m_operateByInstructionAction.setToolTip("<p>" + tr("This switches the debugger to instruction-wise " + m_operateByInstructionAction.setToolTip("<p>" + tr("Switches the debugger to instruction-wise " "operation mode. In this mode, stepping operates on single " "instructions and the source location view also shows the " "disassembled instructions.")); @@ -684,18 +684,18 @@ void DebuggerEnginePrivate::setupViews() connect(&m_abortAction, &QAction::triggered, m_engine, &DebuggerEngine::abortDebugger); - m_resetAction.setToolTip(tr("Restart the debugging session.")); + m_resetAction.setToolTip(tr("Restarts the debugging session.")); m_resetAction.setIcon(Icons::RESTART_TOOLBAR.icon()); connect(&m_resetAction, &QAction::triggered, m_engine, &DebuggerEngine::handleReset); m_stepOverAction.setIcon(Icons::STEP_OVER_TOOLBAR.icon()); connect(&m_stepOverAction, &QAction::triggered, - m_engine, &DebuggerEngine::handleExecNext); + m_engine, &DebuggerEngine::handleExecStepOver); m_stepIntoAction.setIcon(Icons::STEP_INTO_TOOLBAR.icon()); connect(&m_stepIntoAction, &QAction::triggered, - m_engine, &DebuggerEngine::handleExecStep); + m_engine, &DebuggerEngine::handleExecStepIn); m_stepOutAction.setIcon(Icons::STEP_OUT_TOOLBAR.icon()); connect(&m_stepOutAction, &QAction::triggered, @@ -1111,7 +1111,7 @@ void DebuggerEngine::notifyEngineSetupFailed() QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state()); setState(EngineSetupFailed); if (d->m_isPrimaryEngine) { - showMessage(tr("Debugging has failed"), NormalMessageFormat); + showMessage(tr("Debugging has failed."), NormalMessageFormat); d->m_progress.setProgressValue(900); d->m_progress.reportCanceled(); d->m_progress.reportFinished(); @@ -1472,7 +1472,7 @@ void DebuggerEnginePrivate::updateReverseActions() m_operateInReverseDirectionAction.setVisible(canReverse); m_operateInReverseDirectionAction.setEnabled(canReverse && stopped && doesRecord); m_operateInReverseDirectionAction.setIcon(Icons::DIRECTION_BACKWARD.icon()); - m_operateInReverseDirectionAction.setText(DebuggerEngine::tr("Operate in reverse direction")); + m_operateInReverseDirectionAction.setText(DebuggerEngine::tr("Operate in Reverse Direction")); } void DebuggerEnginePrivate::cleanupViews() @@ -1787,14 +1787,14 @@ DebuggerToolTipManager *DebuggerEngine::toolTipManager() return &d->m_toolTipManager; } -bool DebuggerEngine::debuggerActionsEnabled() const +bool DebuggerEngine::operatesByInstruction() const { - return debuggerActionsEnabledHelper(d->m_state); + return d->m_operateByInstructionAction.isChecked(); } -bool DebuggerEngine::operatesByInstruction() const +bool DebuggerEngine::debuggerActionsEnabled() const { - return d->m_operateByInstructionAction.isChecked(); + return debuggerActionsEnabledHelper(d->m_state); } void DebuggerEngine::operateByInstructionTriggered(bool on) @@ -2212,7 +2212,7 @@ void DebuggerEngine::updateLocalsView(const GdbMi &all) static int count = 0; showMessage(QString("<Rebuild Watchmodel %1 @ %2 >") .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput); - showMessage(tr("Finished retrieving data"), 400, StatusBar); + showMessage(tr("Finished retrieving data."), 400, StatusBar); d->m_toolTipManager.updateToolTips(); @@ -2291,32 +2291,16 @@ void DebuggerEngine::handleReset() resetInferior(); } -void DebuggerEngine::handleExecStep() +void DebuggerEngine::handleExecStepIn() { - if (state() == DebuggerNotReady) { - DebuggerRunTool::setBreakOnMainNextTime(); - ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE); - } else { - resetLocation(); - if (operatesByInstruction()) - executeStepI(); - else - executeStep(); - } + resetLocation(); + executeStepIn(operatesByInstruction()); } -void DebuggerEngine::handleExecNext() +void DebuggerEngine::handleExecStepOver() { - if (state() == DebuggerNotReady) { - DebuggerRunTool::setBreakOnMainNextTime(); - ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE); - } else { - resetLocation(); - if (operatesByInstruction()) - executeNextI(); - else - executeNext(); - } + resetLocation(); + executeStepOver(operatesByInstruction()); } void DebuggerEngine::handleExecStepOut() diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 05c30078d4..987ca38073 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -434,8 +434,8 @@ public: void handleUserStop(); void handleAbort(); void handleReset(); - void handleExecStep(); - void handleExecNext(); + void handleExecStepIn(); + void handleExecStepOver(); void handleExecStepOut(); void handleExecReturn(); void handleExecJumpToLine(); @@ -480,11 +480,9 @@ protected: virtual void resetInferior() {} virtual void detachDebugger() {} - virtual void executeStep() {} + virtual void executeStepOver(bool /*byInstruction*/ = false) {} + virtual void executeStepIn(bool /*byInstruction*/ = false) {} virtual void executeStepOut() {} - virtual void executeNext() {} - virtual void executeStepI() {} - virtual void executeNextI() {} virtual void executeReturn() {} virtual void continueInferior() {} diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 3489ed59b8..1435147346 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -56,7 +56,6 @@ const char DEBUGGER_INFORMATION_DISPLAYNAME[] = "DisplayName"; const char DEBUGGER_INFORMATION_ID[] = "Id"; const char DEBUGGER_INFORMATION_ENGINETYPE[] = "EngineType"; const char DEBUGGER_INFORMATION_AUTODETECTED[] = "AutoDetected"; -const char DEBUGGER_INFORMATION_AUTODETECTION_SOURCE[] = "AutoDetectionSource"; const char DEBUGGER_INFORMATION_VERSION[] = "Version"; const char DEBUGGER_INFORMATION_ABIS[] = "Abis"; const char DEBUGGER_INFORMATION_LASTMODIFIED[] = "LastModified"; @@ -82,7 +81,6 @@ DebuggerItem::DebuggerItem(const QVariantMap &data) m_workingDirectory = FileName::fromUserInput(data.value(DEBUGGER_INFORMATION_WORKINGDIRECTORY).toString()); m_unexpandedDisplayName = data.value(DEBUGGER_INFORMATION_DISPLAYNAME).toString(); m_isAutoDetected = data.value(DEBUGGER_INFORMATION_AUTODETECTED, false).toBool(); - m_autoDetectionSource = data.value(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE).toString(); m_version = data.value(DEBUGGER_INFORMATION_VERSION).toString(); m_engineType = DebuggerEngineType(data.value(DEBUGGER_INFORMATION_ENGINETYPE, static_cast<int>(NoEngineType)).toInt()); @@ -252,7 +250,6 @@ QVariantMap DebuggerItem::toMap() const data.insert(DEBUGGER_INFORMATION_WORKINGDIRECTORY, m_workingDirectory.toString()); data.insert(DEBUGGER_INFORMATION_ENGINETYPE, int(m_engineType)); data.insert(DEBUGGER_INFORMATION_AUTODETECTED, m_isAutoDetected); - data.insert(DEBUGGER_INFORMATION_AUTODETECTION_SOURCE, m_autoDetectionSource); data.insert(DEBUGGER_INFORMATION_VERSION, m_version); data.insert(DEBUGGER_INFORMATION_ABIS, abiNames()); data.insert(DEBUGGER_INFORMATION_LASTMODIFIED, m_lastModified); @@ -306,11 +303,6 @@ void DebuggerItem::setVersion(const QString &version) m_version = version; } -void DebuggerItem::setAutoDetectionSource(const QString &autoDetectionSource) -{ - m_autoDetectionSource = autoDetectionSource; -} - void DebuggerItem::setAbis(const QList<Abi> &abis) { m_abis = abis; diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h index e93d30e8b7..20f141600b 100644 --- a/src/plugins/debugger/debuggeritem.h +++ b/src/plugins/debugger/debuggeritem.h @@ -81,9 +81,6 @@ public: QString version() const; void setVersion(const QString &version); - QString autoDetectionSource() const { return m_autoDetectionSource; } - void setAutoDetectionSource(const QString &autoDetectionSource); - const QList<ProjectExplorer::Abi> &abis() const { return m_abis; } void setAbis(const QList<ProjectExplorer::Abi> &abis); void setAbi(const ProjectExplorer::Abi &abi); @@ -115,7 +112,6 @@ private: Utils::FileName m_command; Utils::FileName m_workingDirectory; bool m_isAutoDetected = false; - QString m_autoDetectionSource; QString m_version; QList<ProjectExplorer::Abi> m_abis; QDateTime m_lastModified; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index c4d1f9df7d..449842e0b9 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -768,7 +768,7 @@ public: Action m_interruptAction{tr("Interrupt"), interruptIcon(false), &DebuggerEngine::handleExecInterrupt}; Action m_abortAction{tr("Abort Debugging"), {}, &DebuggerEngine::abortDebugger, tr("Aborts debugging and resets the debugger to the initial state.")}; - QAction m_stepAction{tr("Step Into")}; + QAction m_stepInAction{tr("Step Into")}; Action m_stepOutAction{tr("Step Out"), Icons::STEP_OUT.icon(), &DebuggerEngine::handleExecStepOut}; Action m_runToLineAction{tr("Run to Line"), {}, &DebuggerEngine::handleExecRunToLine}; @@ -776,7 +776,8 @@ public: Action m_jumpToLineAction{tr("Jump to Line"), {}, &DebuggerEngine::handleExecJumpToLine}; // In the Debug menu. Action m_returnFromFunctionAction{tr("Immediately Return From Inner Function"), {}, &DebuggerEngine::executeReturn}; - QAction m_nextAction{tr("Step Over")}; + QAction m_stepOverAction{tr("Step Over")}; + QAction m_startAndBreakOnMain{tr("Start and Break on Main")}; Action m_watchAction{tr("Add Expression Evaluator"), {}, &DebuggerEngine::handleAddToWatchWindow}; Command *m_watchCommand = nullptr; QAction m_breakAction{tr("Toggle Breakpoint")}; @@ -1012,6 +1013,11 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, QString *errorMessage) { Q_UNUSED(errorMessage); + + const Context debuggerRunning(C_DEBUGGER_RUNNING); + const Context debuggerNotRunning(C_DEBUGGER_NOTRUNNING); + ICore::addAdditionalContext(debuggerNotRunning); + m_arguments = arguments; if (!m_arguments.isEmpty()) connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::finishedInitialization, @@ -1225,34 +1231,37 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, debugMenu->addSeparator(); - cmd = ActionManager::registerAction(&m_nextAction, Constants::NEXT); + cmd = ActionManager::registerAction(&m_startAndBreakOnMain, + Constants::START_AND_BREAK_ON_MAIN, + debuggerNotRunning); cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10"))); cmd->setAttribute(Command::CA_Hide); - cmd->setAttribute(Command::CA_UpdateText); debugMenu->addAction(cmd); - m_nextAction.setIcon(Icons::STEP_OVER.icon()); - connect(&m_nextAction, &QAction::triggered, this, [] { - if (DebuggerEngine *engine = EngineManager::currentEngine()) { - engine->handleExecNext(); - } else { - DebuggerRunTool::setBreakOnMainNextTime(); - ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false); - } + connect(&m_startAndBreakOnMain, &QAction::triggered, this, [] { + DebuggerRunTool::setBreakOnMainNextTime(); + ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false); + }); + + cmd = ActionManager::registerAction(&m_stepOverAction, Constants::NEXT, debuggerRunning); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+O") : tr("F10"))); + cmd->setAttribute(Command::CA_Hide); + debugMenu->addAction(cmd); + m_stepOverAction.setIcon(Icons::STEP_OVER.icon()); + connect(&m_stepOverAction, &QAction::triggered, this, [] { + DebuggerEngine *engine = EngineManager::currentEngine(); + QTC_ASSERT(engine, return); + engine->handleExecStepOver(); }); - cmd = ActionManager::registerAction(&m_stepAction, Constants::STEP); + cmd = ActionManager::registerAction(&m_stepInAction, Constants::STEP, debuggerRunning); cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? tr("Ctrl+Shift+I") : tr("F11"))); cmd->setAttribute(Command::CA_Hide); - cmd->setAttribute(Command::CA_UpdateText); debugMenu->addAction(cmd); - m_stepAction.setIcon(Icons::STEP_OVER.icon()); - connect(&m_stepAction, &QAction::triggered, this, [] { - if (DebuggerEngine *engine = EngineManager::currentEngine()) { - engine->handleExecStep(); - } else { - DebuggerRunTool::setBreakOnMainNextTime(); - ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, false); - } + m_stepInAction.setIcon(Icons::STEP_INTO.icon()); + connect(&m_stepInAction, &QAction::triggered, this, [] { + DebuggerEngine *engine = EngineManager::currentEngine(); + QTC_ASSERT(engine, return); + engine->handleExecStepIn(); }); @@ -1469,10 +1478,10 @@ void DebuggerPluginPrivate::updatePresetState() // correspond to the current start up project. // Step into/next: Start and break at 'main' unless a debugger is running. QString stepToolTip = canRun ? tr("Start \"%1\" and break at function \"main\"").arg(startupRunConfigName) : whyNot; - m_stepAction.setToolTip(stepToolTip); - m_nextAction.setToolTip(stepToolTip); - m_stepAction.setEnabled(canRun); - m_nextAction.setEnabled(canRun); + m_stepInAction.setEnabled(canRun); + m_stepInAction.setToolTip(stepToolTip); + m_stepOverAction.setEnabled(canRun); + m_stepOverAction.setToolTip(stepToolTip); m_startAction.setEnabled(canRun); m_startAction.setIcon(startIcon(false)); m_startAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -1492,8 +1501,8 @@ void DebuggerPluginPrivate::updatePresetState() QTC_ASSERT(currentEngine, return); // We have a current engine, and it belongs to the startup runconfig. - m_stepAction.setToolTip(QString()); - m_nextAction.setToolTip(QString()); + m_stepInAction.setToolTip(QString()); + m_stepOverAction.setToolTip(QString()); // The 'state' bits only affect the fat debug button, not the preset start button. m_startAction.setIcon(startIcon(false)); @@ -1522,8 +1531,8 @@ void DebuggerPluginPrivate::updatePresetState() m_debugWithoutDeployAction.setEnabled(false); m_visibleStartAction.setAction(&m_continueAction); m_hiddenStopAction.setAction(&m_exitAction); - m_stepAction.setEnabled(!companionPreventsAction); - m_nextAction.setEnabled(!companionPreventsAction); + m_stepInAction.setEnabled(!companionPreventsAction); + m_stepOverAction.setEnabled(!companionPreventsAction); m_jumpToLineAction.setEnabled(currentEngine->hasCapability(JumpToLineCapability)); m_returnFromFunctionAction.setEnabled(currentEngine->hasCapability(ReturnFromFunctionCapability)); m_detachAction.setEnabled(!isCore); @@ -1541,8 +1550,8 @@ void DebuggerPluginPrivate::updatePresetState() m_debugWithoutDeployAction.setEnabled(false); m_visibleStartAction.setAction(&m_interruptAction); m_hiddenStopAction.setAction(&m_interruptAction); - m_stepAction.setEnabled(false); - m_nextAction.setEnabled(false); + m_stepInAction.setEnabled(false); + m_stepOverAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); m_returnFromFunctionAction.setEnabled(false); m_detachAction.setEnabled(false); @@ -1560,8 +1569,8 @@ void DebuggerPluginPrivate::updatePresetState() m_debugWithoutDeployAction.setEnabled(canRun); m_visibleStartAction.setAction(&m_startAction); m_hiddenStopAction.setAction(&m_undisturbableAction); - m_stepAction.setEnabled(false); - m_nextAction.setEnabled(false); + m_stepInAction.setEnabled(false); + m_stepOverAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); m_returnFromFunctionAction.setEnabled(false); m_detachAction.setEnabled(false); @@ -1579,8 +1588,8 @@ void DebuggerPluginPrivate::updatePresetState() m_debugWithoutDeployAction.setEnabled(false); m_visibleStartAction.setAction(&m_exitAction); m_hiddenStopAction.setAction(&m_exitAction); - m_stepAction.setEnabled(false); - m_nextAction.setEnabled(false); + m_stepInAction.setEnabled(false); + m_stepOverAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); m_returnFromFunctionAction.setEnabled(false); m_detachAction.setEnabled(false); @@ -1601,8 +1610,8 @@ void DebuggerPluginPrivate::updatePresetState() m_debugWithoutDeployAction.setEnabled(false); m_visibleStartAction.setAction(&m_undisturbableAction); m_hiddenStopAction.setAction(&m_undisturbableAction); - m_stepAction.setEnabled(false); - m_nextAction.setEnabled(false); + m_stepInAction.setEnabled(false); + m_stepOverAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); m_returnFromFunctionAction.setEnabled(false); m_detachAction.setEnabled(false); @@ -2064,13 +2073,13 @@ void DebuggerPluginPrivate::setInitialState() m_interruptAction.setEnabled(false); m_continueAction.setEnabled(false); - m_stepAction.setEnabled(true); + m_stepInAction.setEnabled(true); m_stepOutAction.setEnabled(false); m_runToLineAction.setEnabled(false); m_runToSelectedFunctionAction.setEnabled(true); m_returnFromFunctionAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); - m_nextAction.setEnabled(true); + m_stepOverAction.setEnabled(true); action(AutoDerefPointers)->setEnabled(true); action(ExpandStack)->setEnabled(false); diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index c2345e2081..7c2bc7ec19 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -774,11 +774,6 @@ void DebuggerRunTool::setSolibSearchPath(const QStringList &list) m_runParameters.solibSearchPath = list; } -void DebuggerRunTool::quitDebugger() -{ - initiateStop(); -} - bool DebuggerRunTool::fixupParameters() { DebuggerRunParameters &rp = m_runParameters; diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index 23d2a5e030..cc4a1f191b 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -59,8 +59,6 @@ public: void start() override; void stop() override; - void quitDebugger(); - bool isCppDebugging() const; bool isQmlDebugging() const; int portsUsedByDebugger() const; diff --git a/src/plugins/debugger/disassembleragent.cpp b/src/plugins/debugger/disassembleragent.cpp index 3244919ebe..1fad2a4b63 100644 --- a/src/plugins/debugger/disassembleragent.cpp +++ b/src/plugins/debugger/disassembleragent.cpp @@ -400,6 +400,7 @@ void DisassemblerAgent::updateBreakpointMarker(const Breakpoint &bp) auto marker = new DisassemblerBreakpointMarker(bp, lineNumber); d->breakpointMarks.append(marker); + QTC_ASSERT(d->document, return); d->document->addMark(marker); } diff --git a/src/plugins/debugger/enginemanager.cpp b/src/plugins/debugger/enginemanager.cpp index 8e0f102914..4ea21bf99a 100644 --- a/src/plugins/debugger/enginemanager.cpp +++ b/src/plugins/debugger/enginemanager.cpp @@ -344,8 +344,13 @@ void EngineManagerPrivate::selectUiForCurrentEngine() int row = 0; if (m_currentItem && m_currentItem->m_engine) { + ICore::updateAdditionalContexts(Context(Debugger::Constants::C_DEBUGGER_NOTRUNNING), + Context(Debugger::Constants::C_DEBUGGER_RUNNING)); perspective = m_currentItem->m_engine->perspective(); m_currentItem->m_engine->updateState(false); + } else { + ICore::updateAdditionalContexts(Context(Debugger::Constants::C_DEBUGGER_RUNNING), + Context(Debugger::Constants::C_DEBUGGER_NOTRUNNING)); } if (m_currentItem) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 167ec5b0e0..ad4b484d68 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -901,7 +901,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) // there is no debug information. Divert to "-exec-next-step" showMessage("APPLYING WORKAROUND #3"); notifyInferiorStopOk(); - executeNextI(); + executeStepOver(true); } else if (msg.startsWith("Couldn't get registers: No such process.")) { // Happens on archer-tromey-python 6.8.50.20090910-cvs // There might to be a race between a process shutting down @@ -1324,7 +1324,7 @@ void GdbEngine::handleStop1(const GdbMi &data) if (isSkippableFunction(funcName, fileName)) { //showMessage(_("SKIPPING ") + funcName); ++stepCounter; - executeStep(); + executeStepIn(false); return; } //if (stepCounter) @@ -1838,25 +1838,32 @@ void GdbEngine::continueInferior() continueInferiorInternal(); } -void GdbEngine::executeStep() +void GdbEngine::executeStepIn(bool byInstruction) { CHECK_STATE(InferiorStopOk); setTokenBarrier(); notifyInferiorRunRequested(); showStatusMessage(tr("Step requested..."), 5000); + + DebuggerCommand cmd; if (isNativeMixedActiveFrame()) { - DebuggerCommand cmd("executeStep", RunRequest); + cmd.flags = RunRequest; + cmd.function = "executeStep"; cmd.callback = CB(handleExecuteStep); - runCommand(cmd); + } else if (byInstruction) { + cmd.flags = RunRequest|NeedsFlush; + cmd.function = "-exec-step-instruction"; + if (isReverseDebugging()) + cmd.function += "--reverse"; + cmd.callback = CB(handleExecuteContinue); } else { - DebuggerCommand cmd; cmd.flags = RunRequest|NeedsFlush; cmd.function = "-exec-step"; if (isReverseDebugging()) cmd.function += " --reverse"; cmd.callback = CB(handleExecuteStep); - runCommand(cmd); } + runCommand(cmd); } void GdbEngine::handleExecuteStep(const DebuggerResponse &response) @@ -1882,7 +1889,7 @@ void GdbEngine::handleExecuteStep(const DebuggerResponse &response) notifyInferiorRunFailed(); if (isDying()) return; - executeStepI(); // Fall back to instruction-wise stepping. + executeStepIn(true); // Fall back to instruction-wise stepping. } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) { showExecutionError(msg); notifyInferiorRunFailed(); @@ -1895,21 +1902,6 @@ void GdbEngine::handleExecuteStep(const DebuggerResponse &response) } } -void GdbEngine::executeStepI() -{ - CHECK_STATE(InferiorStopOk); - setTokenBarrier(); - notifyInferiorRunRequested(); - showStatusMessage(tr("Step by instruction requested..."), 5000); - DebuggerCommand cmd; - cmd.flags = RunRequest|NeedsFlush; - cmd.function = "-exec-step-instruction"; - if (isReverseDebugging()) - cmd.function += "--reverse"; - cmd.callback = CB(handleExecuteContinue); - runCommand(cmd); -} - void GdbEngine::executeStepOut() { CHECK_STATE(InferiorStopOk); @@ -1928,23 +1920,29 @@ void GdbEngine::executeStepOut() } } -void GdbEngine::executeNext() +void GdbEngine::executeStepOver(bool byInstruction) { CHECK_STATE(InferiorStopOk); setTokenBarrier(); notifyInferiorRunRequested(); showStatusMessage(tr("Step next requested..."), 5000); + + DebuggerCommand cmd; + cmd.flags = RunRequest; if (isNativeMixedActiveFrame()) { - runCommand({"executeNext", RunRequest}); + cmd.function = "executeNext"; + } else if (byInstruction) { + cmd.function = "-exec-next-instruction"; + if (isReverseDebugging()) + cmd.function += " --reverse"; + cmd.callback = CB(handleExecuteContinue); } else { - DebuggerCommand cmd; - cmd.flags = RunRequest; cmd.function = "-exec-next"; if (isReverseDebugging()) cmd.function += " --reverse"; cmd.callback = CB(handleExecuteNext); - runCommand(cmd); } + runCommand(cmd); } void GdbEngine::handleExecuteNext(const DebuggerResponse &response) @@ -1967,7 +1965,7 @@ void GdbEngine::handleExecuteNext(const DebuggerResponse &response) || msg.contains("Error accessing memory address ")) { notifyInferiorRunFailed(); if (!isDying()) - executeNextI(); // Fall back to instruction-wise stepping. + executeStepOver(true); // Fall back to instruction-wise stepping. } else if (msg.startsWith("Cannot execute this command while the selected thread is running.")) { showExecutionError(msg); notifyInferiorRunFailed(); @@ -1981,21 +1979,6 @@ void GdbEngine::handleExecuteNext(const DebuggerResponse &response) } } -void GdbEngine::executeNextI() -{ - CHECK_STATE(InferiorStopOk); - setTokenBarrier(); - notifyInferiorRunRequested(); - showStatusMessage(tr("Step next instruction requested..."), 5000); - DebuggerCommand cmd; - cmd.flags = RunRequest; - cmd.function = "-exec-next-instruction"; - if (isReverseDebugging()) - cmd.function += " --reverse"; - cmd.callback = CB(handleExecuteContinue); - runCommand(cmd); -} - static QString addressSpec(quint64 address) { return "*0x" + QString::number(address, 16); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index c93178ee71..097b5ea81c 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -192,11 +192,9 @@ private: ////////// General Interface ////////// void updateBreakpoint(const Breakpoint &bp) final; void enableSubBreakpoint(const SubBreakpoint &sbp, bool on) final; - void executeStep() final; + void executeStepIn(bool byInstruction) final; void executeStepOut() final; - void executeNext() final; - void executeStepI() final; - void executeNextI() final; + void executeStepOver(bool byInstruction) final; void continueInferiorInternal(); void continueInferior() final; diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 6400908da0..365654034f 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -336,16 +336,10 @@ void LldbEngine::interruptInferior() runCommand({"interruptInferior"}); } -void LldbEngine::executeStep() +void LldbEngine::executeStepIn(bool byInstruction) { notifyInferiorRunRequested(); - runCommand({"executeStep"}); -} - -void LldbEngine::executeStepI() -{ - notifyInferiorRunRequested(); - runCommand({"executeStepI"}); + runCommand({QLatin1String(byInstruction ? "executeStepI" : "executeStep")}); } void LldbEngine::executeStepOut() @@ -354,16 +348,10 @@ void LldbEngine::executeStepOut() runCommand({"executeStepOut"}); } -void LldbEngine::executeNext() -{ - notifyInferiorRunRequested(); - runCommand({"executeNext"}); -} - -void LldbEngine::executeNextI() +void LldbEngine::executeStepOver(bool byInstruction) { notifyInferiorRunRequested(); - runCommand({"executeNextI"}); + runCommand({QLatin1String(byInstruction ? "executeNextI" : "executeNext")}); } void LldbEngine::continueInferior() diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 907a5c2bdf..7d0b01aeba 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -60,11 +60,9 @@ signals: void outputReady(const QString &data); private: - void executeStep() override; + void executeStepIn(bool byInstruction) override; void executeStepOut() override; - void executeNext() override; - void executeStepI() override; - void executeNextI() override; + void executeStepOver(bool byInstruction) override; void setupEngine() override; void runEngine() override; diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 4afd067606..9e6733ae94 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -167,14 +167,7 @@ void PdbEngine::interruptInferior() interruptProcess(m_proc.processId(), GdbEngineType, &error); } -void PdbEngine::executeStep() -{ - notifyInferiorRunRequested(); - notifyInferiorRunOk(); - postDirectCommand("step"); -} - -void PdbEngine::executeStepI() +void PdbEngine::executeStepIn(bool) { notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -188,14 +181,7 @@ void PdbEngine::executeStepOut() postDirectCommand("return"); } -void PdbEngine::executeNext() -{ - notifyInferiorRunRequested(); - notifyInferiorRunOk(); - postDirectCommand("next"); -} - -void PdbEngine::executeNextI() +void PdbEngine::executeStepOver(bool) { notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -269,8 +255,24 @@ void PdbEngine::insertBreakpoint(const Breakpoint &bp) void PdbEngine::updateBreakpoint(const Breakpoint &bp) { - Q_UNUSED(bp); - QTC_CHECK(false); + QTC_ASSERT(bp, return); + const BreakpointState state = bp->state(); + if (QTC_GUARD(state == BreakpointUpdateRequested)) + notifyBreakpointChangeProceeding(bp); + if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?) + return; + + // FIXME figure out what needs to be changed (there might be more than enabled state) + const BreakpointParameters &requested = bp->requestedParameters(); + if (requested.enabled != bp->isEnabled()) { + if (bp->isEnabled()) + postDirectCommand("disable " + bp->responseId()); + else + postDirectCommand("enable " + bp->responseId()); + bp->setEnabled(!bp->isEnabled()); + } + // Pretend it succeeds without waiting for response. + notifyBreakpointChangeOk(bp); } void PdbEngine::removeBreakpoint(const Breakpoint &bp) @@ -278,6 +280,10 @@ void PdbEngine::removeBreakpoint(const Breakpoint &bp) QTC_ASSERT(bp, return); QTC_CHECK(bp->state() == BreakpointRemoveRequested); notifyBreakpointRemoveProceeding(bp); + if (bp->responseId().isEmpty()) { + notifyBreakpointRemoveFailed(bp); + return; + } showMessage(QString("DELETING BP %1 IN %2") .arg(bp->responseId()).arg(bp->fileName())); postDirectCommand("clear " + bp->responseId()); @@ -504,8 +510,12 @@ void PdbEngine::handleOutput2(const QString &data) bp->setLineNumber(lineNumber); bp->adjustMarker(); bp->setPending(false); - QTC_CHECK(!bp->needsChange()); notifyBreakpointInsertOk(bp); + if (bp->needsChange()) { + bp->gotoState(BreakpointUpdateRequested, BreakpointInserted); + updateBreakpoint(bp); +// QTC_CHECK(!bp->needsChange()); + } } } } diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index 420e0639c7..57a040d5dc 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -47,12 +47,9 @@ public: PdbEngine(); private: - // DebuggerEngine implementation - void executeStep() override; + void executeStepIn(bool) override; void executeStepOut() override; - void executeNext() override; - void executeStepI() override; - void executeNextI() override; + void executeStepOver(bool) override; void setupEngine() override; void runEngine() override; diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 26647100c9..567a3852e8 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -610,15 +610,7 @@ void QmlEngine::interruptInferior() showStatusMessage(tr("Waiting for JavaScript engine to interrupt on next statement.")); } -void QmlEngine::executeStep() -{ - clearExceptionSelection(); - d->continueDebugging(StepIn); - notifyInferiorRunRequested(); - notifyInferiorRunOk(); -} - -void QmlEngine::executeStepI() +void QmlEngine::executeStepIn(bool) { clearExceptionSelection(); d->continueDebugging(StepIn); @@ -634,7 +626,7 @@ void QmlEngine::executeStepOut() notifyInferiorRunOk(); } -void QmlEngine::executeNext() +void QmlEngine::executeStepOver(bool) { clearExceptionSelection(); d->continueDebugging(Next); @@ -642,11 +634,6 @@ void QmlEngine::executeNext() notifyInferiorRunOk(); } -void QmlEngine::executeNextI() -{ - executeNext(); -} - void QmlEngine::executeRunToLine(const ContextData &data) { QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 8d407b11ae..df31c3c6c5 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -72,11 +72,9 @@ private: void resetLocation() override; - void executeStep() override; + void executeStepOver(bool) override; + void executeStepIn(bool) override; void executeStepOut() override; - void executeNext() override; - void executeStepI() override; - void executeNextI() override; void setupEngine() override; void runEngine() override; diff --git a/src/plugins/git/TODO.txt b/src/plugins/git/TODO.txt deleted file mode 100644 index 6f17e577ad..0000000000 --- a/src/plugins/git/TODO.txt +++ /dev/null @@ -1,17 +0,0 @@ -Commands: - - P3: - - allow to use external viewer instead of greenhouse one - as these have more functionality usually - -GUI: - - Better diff view - - Commit action View - - Able to add further files to commit (list of modified/untracked files) - - use List for Log (and allow 10+ entries) -Backend: - - Don't use forked processes, instead find a library connection like libgit-thin - - http://repo.or.cz/w/git/libgit-gsoc.git - - apply to SCM Manager in Greenhouse, currently it's mostly independent - -Suggestions: - - Bjorn: Use a "Summary" Lineedit in the commit dialog to make commits look nicer on gitweb or such. diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp index 35340e0731..6f7f83609c 100644 --- a/src/plugins/git/branchmodel.cpp +++ b/src/plugins/git/branchmodel.cpp @@ -123,8 +123,6 @@ public: QStringList fullName(bool includePrefix = false) const { - QTC_ASSERT(isLeaf(), return QStringList()); - QStringList fn; QList<const BranchNode *> nodes; const BranchNode *current = this; diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp index 095cbcedcf..c37bc87abb 100644 --- a/src/plugins/git/branchview.cpp +++ b/src/plugins/git/branchview.cpp @@ -37,6 +37,7 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/inavigationwidgetfactory.h> #include <utils/elidinglabel.h> +#include <utils/fancylineedit.h> #include <utils/navigationtreeview.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -48,6 +49,7 @@ #include <QMenu> #include <QMessageBox> #include <QPoint> +#include <QSortFilterProxyModel> #include <QTreeView> #include <QToolButton> #include <QVBoxLayout> @@ -57,6 +59,21 @@ using namespace Core; namespace Git { namespace Internal { +class BranchFilterModel : public QSortFilterProxyModel +{ +public: + BranchFilterModel(QObject *parent) : QSortFilterProxyModel(parent) {} +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override + { + QAbstractItemModel *m = sourceModel(); + // Filter leaves only. The root node and all intermediate nodes should always be visible + if (!sourceParent.isValid() || m->rowCount(m->index(sourceRow, 0, sourceParent)) > 0) + return true; + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } +}; + BranchView::BranchView() : m_includeOldEntriesAction(new QAction(tr("Include Old Entries"), this)), m_includeTagsAction(new QAction(tr("Include Tags"), this)), @@ -64,7 +81,8 @@ BranchView::BranchView() : m_refreshButton(new QToolButton(this)), m_repositoryLabel(new Utils::ElidingLabel(this)), m_branchView(new Utils::NavigationTreeView(this)), - m_model(new BranchModel(GitPlugin::client(), this)) + m_model(new BranchModel(GitPlugin::client(), this)), + m_filterModel(new BranchFilterModel(this)) { m_addButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); m_addButton->setProperty("noArrow", true); @@ -75,12 +93,21 @@ BranchView::BranchView() : m_refreshButton->setProperty("noArrow", true); connect(m_refreshButton, &QToolButton::clicked, this, &BranchView::refreshCurrentRepository); - m_branchView->setModel(m_model); m_branchView->setHeaderHidden(true); setFocus(); m_repositoryLabel->setElideMode(Qt::ElideLeft); + + m_filterModel->setSourceModel(m_model); + m_filterModel->setFilterRole(Qt::EditRole); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_branchView->setModel(m_filterModel); + auto filterEdit = new Utils::FancyLineEdit(this); + filterEdit->setFiltering(true); + connect(filterEdit, &Utils::FancyLineEdit::textChanged, + m_filterModel, QOverload<const QString &>::of(&BranchFilterModel::setFilterRegExp)); auto layout = new QVBoxLayout(this); + layout->addWidget(filterEdit); layout->addWidget(m_repositoryLabel); layout->addWidget(m_branchView); layout->setContentsMargins(0, 2, 0, 0); @@ -100,7 +127,7 @@ BranchView::BranchView() : m_branchView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_branchView, &QAbstractItemView::doubleClicked, - this, &BranchView::log); + this, [this](const QModelIndex &idx) { log(m_filterModel->mapToSource(idx)); }); connect(m_branchView, &QWidget::customContextMenuRequested, this, &BranchView::slotCustomContextMenu); connect(m_model, &QAbstractItemModel::modelReset, @@ -161,10 +188,11 @@ void BranchView::resizeColumns() void BranchView::slotCustomContextMenu(const QPoint &point) { - const QModelIndex index = m_branchView->indexAt(point); - if (!index.isValid()) + const QModelIndex filteredIndex = m_branchView->indexAt(point); + if (!filteredIndex.isValid()) return; + const QModelIndex index = m_filterModel->mapToSource(filteredIndex); const QModelIndex currentBranch = m_model->currentBranch(); const bool currentSelected = index.row() == currentBranch.row(); const bool isLocal = m_model->isLocal(index); @@ -185,9 +213,9 @@ void BranchView::slotCustomContextMenu(const QPoint &point) } if (hasActions) { if (!currentSelected && (isLocal || isTag)) - contextMenu.addAction(tr("Remove"), this, &BranchView::remove); + contextMenu.addAction(tr("Remove..."), this, &BranchView::remove); if (isLocal || isTag) - contextMenu.addAction(tr("Rename"), this, &BranchView::rename); + contextMenu.addAction(tr("Rename..."), this, &BranchView::rename); if (!currentSelected) contextMenu.addAction(tr("Checkout"), this, &BranchView::checkout); contextMenu.addSeparator(); @@ -246,7 +274,7 @@ QModelIndex BranchView::selectedIndex() QModelIndexList selected = m_branchView->selectionModel()->selectedIndexes(); if (selected.isEmpty()) return QModelIndex(); - return selected.at(0); + return m_filterModel->mapToSource(selected.at(0)); } bool BranchView::add() diff --git a/src/plugins/git/branchview.h b/src/plugins/git/branchview.h index 096510f550..8994cbe1dc 100644 --- a/src/plugins/git/branchview.h +++ b/src/plugins/git/branchview.h @@ -46,6 +46,7 @@ namespace Git { namespace Internal { class BranchModel; +class BranchFilterModel; class BranchView : public QWidget { @@ -87,6 +88,7 @@ private: Utils::ElidingLabel *m_repositoryLabel = nullptr; Utils::NavigationTreeView *m_branchView = nullptr; BranchModel *m_model = nullptr; + BranchFilterModel *m_filterModel = nullptr; QString m_repository; }; diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index ea1b229236..cd04e696fc 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -274,7 +274,7 @@ HelpPluginPrivate::HelpPluginPrivate() textEditorContextMenu->addAction(cmd, Core::Constants::G_HELP); } - action = new QAction(HelpPlugin::tr("Technical Support"), this); + action = new QAction(HelpPlugin::tr("Technical Support..."), this); cmd = ActionManager::registerAction(action, "Help.TechSupport"); ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT); connect(action, &QAction::triggered, this, [this] { diff --git a/src/plugins/languageclient/baseclient.cpp b/src/plugins/languageclient/baseclient.cpp index 260275e236..7239a7f5fa 100644 --- a/src/plugins/languageclient/baseclient.cpp +++ b/src/plugins/languageclient/baseclient.cpp @@ -24,7 +24,6 @@ ****************************************************************************/ #include "baseclient.h" -#include "languageclientcodeassist.h" #include "languageclientmanager.h" #include <coreplugin/icore.h> @@ -63,6 +62,7 @@ static Q_LOGGING_CATEGORY(LOGLSPCLIENTV, "qtc.languageclient.messages", QtWarnin BaseClient::BaseClient() : m_id(Core::Id::fromString(QUuid::createUuid().toString())) + , m_completionProvider(this) { m_buffer.open(QIODevice::ReadWrite | QIODevice::Append); m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(), @@ -72,6 +72,10 @@ BaseClient::BaseClient() BaseClient::~BaseClient() { m_buffer.close(); + // FIXME: instead of replacing the completion provider in the text document store the + // completion provider as a prioritised list in the text document + for (TextEditor::TextDocument *document : m_resetCompletionProvider) + document->setCompletionAssistProvider(nullptr); } void BaseClient::initialize() @@ -117,7 +121,7 @@ BaseClient::State BaseClient::state() const void BaseClient::openDocument(Core::IDocument *document) { using namespace TextEditor; - if (!isSupportedMimeType(document->mimeType())) + if (!isSupportedDocument(document)) return; const FileName &filePath = document->filePath(); const QString method(DidOpenTextDocumentNotification::methodName); @@ -149,7 +153,11 @@ void BaseClient::openDocument(Core::IDocument *document) documentContentsChanged(document); }); if (textDocument) { - textDocument->setCompletionAssistProvider(new LanguageClientCompletionAssistProvider(this)); + m_resetCompletionProvider << textDocument; + textDocument->setCompletionAssistProvider(&m_completionProvider); + connect(textDocument, &QObject::destroyed, this, [this, textDocument]{ + m_resetCompletionProvider.remove(textDocument); + }); if (BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(textDocument)) { if (QPointer<TextEditorWidget> widget = editor->editorWidget()) { connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget](){ @@ -489,19 +497,29 @@ void BaseClient::projectClosed(ProjectExplorer::Project *project) sendContent(change); } -void BaseClient::setSupportedMimeType(const QStringList &supportedMimeTypes) +void BaseClient::setSupportedLanguage(const LanguageFilter &filter) { - m_supportedMimeTypes = supportedMimeTypes; + m_languagFilter = filter; } -bool BaseClient::isSupportedMimeType(const QString &mimeType) const +bool BaseClient::isSupportedDocument(const Core::IDocument *document) const { - return m_supportedMimeTypes.isEmpty() || m_supportedMimeTypes.contains(mimeType); + QTC_ASSERT(document, return false); + if (m_languagFilter.mimeTypes.isEmpty() || m_languagFilter.mimeTypes.contains(document->mimeType())) + return true; + auto regexps = Utils::transform(m_languagFilter.filePattern, [](const QString &pattern){ + return QRegExp(pattern, Utils::HostOsInfo::fileNameCaseSensitivity(), QRegExp::Wildcard); + }); + return Utils::anyOf(regexps, [filePath = document->filePath()](const QRegExp ®){ + return reg.exactMatch(filePath.toString()) || reg.exactMatch(filePath.fileName()); + }); } -bool BaseClient::needsRestart(const BaseSettings *) const +bool BaseClient::needsRestart(const BaseSettings *settings) const { - return false; + QTC_ASSERT(settings, return false); + return m_languagFilter.mimeTypes != settings->m_languageFilter.mimeTypes + || m_languagFilter.filePattern != settings->m_languageFilter.filePattern; } bool BaseClient::reset() @@ -640,7 +658,7 @@ void BaseClient::intializeCallback(const InitializeResponse &initResponse) if (optional<ResponseError<InitializeError>> error = initResponse.error()) { if (error.value().data().has_value() && error.value().data().value().retry().value_or(false)) { - const QString title(tr("Language Server \"%1\" initialize error")); + const QString title(tr("Language Server \"%1\" Initialize Error")); auto result = QMessageBox::warning(Core::ICore::dialogParent(), title, error.value().message(), diff --git a/src/plugins/languageclient/baseclient.h b/src/plugins/languageclient/baseclient.h index 3ecefcd584..b9e2ee3d4c 100644 --- a/src/plugins/languageclient/baseclient.h +++ b/src/plugins/languageclient/baseclient.h @@ -26,6 +26,7 @@ #pragma once #include "dynamiccapabilities.h" +#include "languageclientcodeassist.h" #include "languageclientsettings.h" #include <coreplugin/id.h> @@ -104,8 +105,8 @@ public: const LanguageServerProtocol::IContent &content); void cancelRequest(const LanguageServerProtocol::MessageId &id); - void setSupportedMimeType(const QStringList &supportedMimeTypes); - bool isSupportedMimeType(const QString &mimeType) const; + void setSupportedLanguage(const LanguageFilter &filter); + bool isSupportedDocument(const Core::IDocument *document) const; void setName(const QString &name) { m_displayName = name; } QString name() const { return m_displayName; } @@ -153,11 +154,13 @@ private: QHash<QByteArray, ContentHandler> m_contentHandler; QBuffer m_buffer; QString m_displayName; - QStringList m_supportedMimeTypes; + LanguageFilter m_languagFilter; QList<Utils::FileName> m_openedDocument; Core::Id m_id; LanguageServerProtocol::ServerCapabilities m_serverCapabilities; DynamicCapabilities m_dynamicCapabilities; + LanguageClientCompletionAssistProvider m_completionProvider; + QSet<TextEditor::TextDocument *> m_resetCompletionProvider; LanguageServerProtocol::BaseMessage m_currentMessage; QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests; int m_restartsLeft = 5; diff --git a/src/plugins/languageclient/languageclientcodeassist.cpp b/src/plugins/languageclient/languageclientcodeassist.cpp index 63691b04ca..4a813889f9 100644 --- a/src/plugins/languageclient/languageclientcodeassist.cpp +++ b/src/plugins/languageclient/languageclientcodeassist.cpp @@ -69,6 +69,8 @@ public: bool operator <(const LanguageClientCompletionItem &other) const; + bool isPerfectMatch(int pos, QTextDocument *doc) const; + private: CompletionItem m_item; mutable QString m_sortText; @@ -194,6 +196,26 @@ bool LanguageClientCompletionItem::operator <(const LanguageClientCompletionItem return sortText() < other.sortText(); } +bool LanguageClientCompletionItem::isPerfectMatch(int pos, QTextDocument *doc) const +{ + QTC_ASSERT(doc, return false); + using namespace Utils::Text; + if (auto additionalEdits = m_item.additionalTextEdits()) { + if (!additionalEdits.value().isEmpty()) + return false; + } + if (auto edit = m_item.textEdit()) { + auto range = edit->range(); + const int start = positionInText(doc, range.start().line() + 1, range.start().character() + 1); + const int end = positionInText(doc, range.end().line() + 1, range.end().character() + 1); + auto text = textAt(QTextCursor(doc), start, end - start); + return text == edit->newText(); + } + const QString textToInsert(m_item.insertText().value_or(text())); + const int length = textToInsert.length(); + return textToInsert == textAt(QTextCursor(doc), pos - length, length); +} + class LanguageClientCompletionModel : public TextEditor::GenericProposalModel { public: @@ -201,6 +223,9 @@ public: bool isSortable(const QString &/*prefix*/) const override { return true; } void sort(const QString &/*prefix*/) override; bool supportsPrefixExpansion() const override { return false; } + + QList<LanguageClientCompletionItem *> items() const + { return Utils::static_container_cast<LanguageClientCompletionItem *>(m_currentItems); } }; void LanguageClientCompletionModel::sort(const QString &/*prefix*/) @@ -213,16 +238,44 @@ void LanguageClientCompletionModel::sort(const QString &/*prefix*/) }); } +class LanguageClientCompletionProposal : public TextEditor::GenericProposal +{ +public: + LanguageClientCompletionProposal(int cursorPos, LanguageClientCompletionModel *model) + : TextEditor::GenericProposal(cursorPos, TextEditor::GenericProposalModelPtr(model)) + , m_model(model) + { } + + // IAssistProposal interface + bool hasItemsToPropose(const QString &/*text*/, TextEditor::AssistReason reason) const override + { + if (m_model->size() <= 0 || m_document.isNull()) + return false; + + return m_model->keepPerfectMatch(reason) + || !Utils::anyOf(m_model->items(), [this](LanguageClientCompletionItem *item){ + return item->isPerfectMatch(m_pos, m_document); + }); + } + + LanguageClientCompletionModel *m_model; + QPointer<QTextDocument> m_document; + int m_pos = -1; +}; + + class LanguageClientCompletionAssistProcessor : public TextEditor::IAssistProcessor { public: LanguageClientCompletionAssistProcessor(BaseClient *client); TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override; bool running() override; + bool needsRestart() const override { return true; } private: void handleCompletionResponse(const Response<CompletionResult, LanguageClientNull> &response); + QPointer<QTextDocument> m_document; QPointer<BaseClient> m_client; bool m_running = false; int m_pos = -1; @@ -247,11 +300,15 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform( { QTC_ASSERT(m_client, return nullptr); m_pos = interface->position(); -// const QRegExp regexp("[_a-zA-Z][_a-zA-Z0-9]*"); // FIXME -// int delta = 0; -// while (m_pos - delta > 0 && regexp.exactMatch(interface->textAt(m_pos - delta - 1, delta + 1))) -// ++delta; -// m_pos -= delta; + if (interface->reason() == TextEditor::IdleEditor) { + // Trigger an automatic completion request only when we are on a word with more than 2 "identifier" character + const QRegExp regexp("[_a-zA-Z0-9]*"); + int delta = 0; + while (m_pos - delta > 0 && regexp.exactMatch(interface->textAt(m_pos - delta - 1, delta + 1))) + ++delta; + if (delta < 3) + return nullptr; + } CompletionRequest completionRequest; CompletionParams::CompletionContext context; context.setTriggerKind(interface->reason() == TextEditor::ActivationCharacter @@ -263,6 +320,7 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform( if (!Utils::Text::convertPosition(interface->textDocument(), m_pos, &line, &column)) return nullptr; --line; // line is 0 based in the protocol + --column; // column is 0 based in the protocol params.setPosition({line, column}); params.setTextDocument( DocumentUri::fromFileName(Utils::FileName::fromString(interface->fileName()))); @@ -272,6 +330,7 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform( completionRequest.setParams(params); m_client->sendContent(completionRequest); m_running = true; + m_document = interface->textDocument(); qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : request completions at " << m_pos << " by " << assistReasonString(interface->reason()); @@ -309,7 +368,9 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( model->loadContent(Utils::transform(items, [](const CompletionItem &item){ return static_cast<AssistProposalItemInterface *>(new LanguageClientCompletionItem(item)); })); - auto proposal = new GenericProposal(m_pos, GenericProposalModelPtr(model)); + auto proposal = new LanguageClientCompletionProposal(m_pos, model); + proposal->m_document = m_document; + proposal->m_pos = m_pos; proposal->setFragile(true); setAsyncProposalAvailable(proposal); qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : " diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index f139456aef..31697e1050 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -32,24 +32,33 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> #include <utils/delegates.h> +#include <utils/fancylineedit.h> #include <utils/qtcprocess.h> #include <utils/mimetypes/mimedatabase.h> #include <languageserverprotocol/lsptypes.h> #include <QBoxLayout> +#include <QCheckBox> #include <QComboBox> #include <QCompleter> #include <QCoreApplication> +#include <QDialog> +#include <QDialogButtonBox> +#include <QDir> #include <QFileInfo> #include <QHeaderView> +#include <QLabel> +#include <QListView> #include <QPushButton> #include <QSettings> -#include <QStyledItemDelegate> +#include <QSortFilterProxyModel> +#include <QStringListModel> #include <QTreeView> constexpr char nameKey[] = "name"; constexpr char enabledKey[] = "enabled"; constexpr char mimeTypeKey[] = "mimeType"; +constexpr char filePatternKey[] = "filePattern"; constexpr char executableKey[] = "executable"; constexpr char argumentsKey[] = "arguments"; constexpr char settingsGroupKey[] = "LanguageClient"; @@ -57,34 +66,25 @@ constexpr char clientsKey[] = "clients"; namespace LanguageClient { -class LanguageClientSettingsModel : public QAbstractTableModel +class LanguageClientSettingsModel : public QAbstractListModel { public: LanguageClientSettingsModel() = default; ~LanguageClientSettingsModel(); // QAbstractItemModel interface - int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const override { return m_settings.count(); } - int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const override { return ColumnCount; } - QVariant data(const QModelIndex &index, int role) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override; - bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - Qt::ItemFlags flags(const QModelIndex &index) const override; + int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const final { return m_settings.count(); } + QVariant data(const QModelIndex &index, int role) const final; + bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final; + bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final; + bool setData(const QModelIndex &index, const QVariant &value, int role) final; + Qt::ItemFlags flags(const QModelIndex &index) const final; void reset(const QList<StdIOSettings *> &settings); QList<StdIOSettings *> settings() const { return m_settings; } QList<StdIOSettings *> removed() const { return m_removed; } - - enum Columns { - DisplayNameColumn = 0, - EnabledColumn, - MimeTypeColumn, - ExecutableColumn, - ArgumentsColumn, - ColumnCount - }; + StdIOSettings *settingForIndex(const QModelIndex &index) const; + QModelIndex indexForSetting(StdIOSettings *setting) const; private: QList<StdIOSettings *> m_settings; // owned @@ -95,10 +95,18 @@ class LanguageClientSettingsPageWidget : public QWidget { public: LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings); + void currentChanged(const QModelIndex &index); + int currentRow() const; + void resetCurrentSettings(int row); + void applyCurrentSettings(); private: LanguageClientSettingsModel &m_settings; - QTreeView *m_view; + QTreeView *m_view = nullptr; + struct CurrentSettings { + StdIOSettings *setting = nullptr; + QWidget *widget = nullptr; + } m_currentSettings; void addItem(); void deleteItem(); @@ -123,61 +131,29 @@ private: QPointer<LanguageClientSettingsPageWidget> m_widget; }; -class LanguageChooseDelegate : public QStyledItemDelegate -{ -public: - QWidget *createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - void setEditorData(QWidget *editor, const QModelIndex &index) const override; -}; - -QWidget *LanguageChooseDelegate::createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - Q_UNUSED(option); - Q_UNUSED(index); - auto editor = new QComboBox(parent); - editor->addItem(noLanguageFilter); - editor->addItems(LanguageServerProtocol::languageIds().values()); - return editor; -} - -void LanguageChooseDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - if (auto comboBox = qobject_cast<QComboBox*>(editor)) - comboBox->setCurrentText(index.data().toString()); -} - LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings) : m_settings(settings) , m_view(new QTreeView()) { + auto mainLayout = new QVBoxLayout(); auto layout = new QHBoxLayout(); m_view->setModel(&m_settings); - m_view->header()->setStretchLastSection(true); - m_view->setRootIsDecorated(false); - m_view->setItemsExpandable(false); + m_view->setHeaderHidden(true); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setSelectionBehavior(QAbstractItemView::SelectItems); + connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged, + this, &LanguageClientSettingsPageWidget::currentChanged); auto mimeTypes = Utils::transform(Utils::allMimeTypes(), [](const Utils::MimeType &mimeType){ return mimeType.name(); }); - auto mimeTypeCompleter = new QCompleter(mimeTypes); - mimeTypeCompleter->setCaseSensitivity(Qt::CaseInsensitive); - mimeTypeCompleter->setFilterMode(Qt::MatchContains); - m_view->setItemDelegateForColumn(LanguageClientSettingsModel::MimeTypeColumn, - new Utils::CompleterDelegate(mimeTypeCompleter)); - auto executableDelegate = new Utils::PathChooserDelegate(); - executableDelegate->setExpectedKind(Utils::PathChooser::File); - executableDelegate->setHistoryCompleter("LanguageClient.ServerPathHistory"); - m_view->setItemDelegateForColumn(LanguageClientSettingsModel::ExecutableColumn, executableDelegate); auto buttonLayout = new QVBoxLayout(); auto addButton = new QPushButton(tr("&Add")); connect(addButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::addItem); auto deleteButton = new QPushButton(tr("&Delete")); connect(deleteButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::deleteItem); - setLayout(layout); + mainLayout->addLayout(layout); + setLayout(mainLayout); layout->addWidget(m_view); layout->addLayout(buttonLayout); buttonLayout->addWidget(addButton); @@ -185,6 +161,51 @@ LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClien buttonLayout->addStretch(10); } +void LanguageClientSettingsPageWidget::currentChanged(const QModelIndex &index) +{ + if (m_currentSettings.widget) { + applyCurrentSettings(); + layout()->removeWidget(m_currentSettings.widget); + delete m_currentSettings.widget; + } + + if (index.isValid()) { + m_currentSettings.setting = m_settings.settingForIndex(index); + m_currentSettings.widget = m_currentSettings.setting->createSettingsWidget(this); + layout()->addWidget(m_currentSettings.widget); + } else { + m_currentSettings.setting = nullptr; + m_currentSettings.widget = nullptr; + } +} + +int LanguageClientSettingsPageWidget::currentRow() const +{ + return m_settings.indexForSetting(m_currentSettings.setting).row(); +} + +void LanguageClientSettingsPageWidget::resetCurrentSettings(int row) +{ + if (m_currentSettings.widget) { + layout()->removeWidget(m_currentSettings.widget); + delete m_currentSettings.widget; + } + + m_currentSettings.setting = nullptr; + m_currentSettings.widget = nullptr; + m_view->setCurrentIndex(m_settings.index(row)); +} + +void LanguageClientSettingsPageWidget::applyCurrentSettings() +{ + if (!m_currentSettings.setting) + return; + + m_currentSettings.setting->applyFromSettingsWidget(m_currentSettings.widget); + auto index = m_settings.indexForSetting(m_currentSettings.setting); + m_settings.dataChanged(index, index); +} + void LanguageClientSettingsPageWidget::addItem() { const int row = m_settings.rowCount(); @@ -233,6 +254,8 @@ QWidget *LanguageClientSettingsPage::widget() void LanguageClientSettingsPage::apply() { qDeleteAll(m_settings); + if (m_widget) + m_widget->applyCurrentSettings(); m_settings = Utils::transform(m_model.settings(), [](const StdIOSettings *other){ return dynamic_cast<StdIOSettings *>(other->copy()); }); @@ -255,11 +278,19 @@ void LanguageClientSettingsPage::apply() } } } - m_model.reset(m_settings); + + if (m_widget) { + int row = m_widget->currentRow(); + m_model.reset(m_settings); + m_widget->resetCurrentSettings(row); + } else { + m_model.reset(m_settings); + } } void LanguageClientSettingsPage::finish() { + m_model.reset(m_settings); } LanguageClientSettingsModel::~LanguageClientSettingsModel() @@ -269,35 +300,13 @@ LanguageClientSettingsModel::~LanguageClientSettingsModel() QVariant LanguageClientSettingsModel::data(const QModelIndex &index, int role) const { - if (!index.isValid()) + StdIOSettings *setting = settingForIndex(index); + if (!setting) return QVariant(); - StdIOSettings *setting = m_settings[index.row()]; - QTC_ASSERT(setting, return false); - if (role == Qt::DisplayRole || role == Qt::EditRole) { - switch (index.column()) { - case DisplayNameColumn: return setting->m_name; - case MimeTypeColumn: return setting->m_mimeType; - case ExecutableColumn: return setting->m_executable; - case ArgumentsColumn: return setting->m_arguments; - } - } else if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { + if (role == Qt::DisplayRole) + return setting->m_name; + else if (role == Qt::CheckStateRole) return setting->m_enabled ? Qt::Checked : Qt::Unchecked; - } - return QVariant(); -} - -QVariant LanguageClientSettingsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Horizontal || role != Qt::DisplayRole) - return QVariant(); - - switch (section) { - case DisplayNameColumn: return tr("Name"); - case EnabledColumn: return tr("Enabled"); - case MimeTypeColumn: return tr("Mime Type"); - case ExecutableColumn: return tr("Executable"); - case ArgumentsColumn: return tr("Arguments"); - } return QVariant(); } @@ -326,44 +335,20 @@ bool LanguageClientSettingsModel::insertRows(int row, int count, const QModelInd bool LanguageClientSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid()) + StdIOSettings *setting = settingForIndex(index); + if (!setting || role != Qt::CheckStateRole) return false; - StdIOSettings *setting = m_settings[index.row()]; - QTC_ASSERT(setting, return false); - if (role == Qt::DisplayRole || role == Qt::EditRole) { - const QString strVal(value.toString()); - QString *settingsValue = nullptr; - switch (index.column()) { - case DisplayNameColumn: settingsValue = &setting->m_name; break; - case MimeTypeColumn: settingsValue = &setting->m_mimeType; break; - case ExecutableColumn: settingsValue = &setting->m_executable; break; - case ArgumentsColumn: settingsValue = &setting->m_arguments; break; - } - if (settingsValue) { - if (strVal != *settingsValue) { - *settingsValue = value.toString(); - emit dataChanged(index, index, { Qt::EditRole, Qt::DisplayRole }); - } - return true; - } - } - if (role == Qt::CheckStateRole && index.column() == EnabledColumn) { - if (setting->m_enabled != value.toBool()) { - setting->m_enabled = !setting->m_enabled; - emit dataChanged(index, index, { Qt::CheckStateRole }); - } - return true; + if (setting->m_enabled != value.toBool()) { + setting->m_enabled = !setting->m_enabled; + emit dataChanged(index, index, { Qt::CheckStateRole }); } - return false; + return true; } -Qt::ItemFlags LanguageClientSettingsModel::flags(const QModelIndex &index) const +Qt::ItemFlags LanguageClientSettingsModel::flags(const QModelIndex &/*index*/) const { - const auto defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; - if (index.column() == EnabledColumn) - return defaultFlags | Qt::ItemIsUserCheckable; - return defaultFlags; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; } void LanguageClientSettingsModel::reset(const QList<StdIOSettings *> &settings) @@ -378,6 +363,32 @@ void LanguageClientSettingsModel::reset(const QList<StdIOSettings *> &settings) endResetModel(); } +StdIOSettings *LanguageClientSettingsModel::settingForIndex(const QModelIndex &index) const +{ + if (!index.isValid() || index.row() >= m_settings.size()) + return nullptr; + return m_settings[index.row()]; +} + +QModelIndex LanguageClientSettingsModel::indexForSetting(StdIOSettings *setting) const +{ + const int index = m_settings.indexOf(setting); + return index < 0 ? QModelIndex() : createIndex(index, 0, setting); +} + +void BaseSettings::applyFromSettingsWidget(QWidget *widget) +{ + if (auto settingsWidget = qobject_cast<BaseSettingsWidget *>(widget)) { + m_name = settingsWidget->name(); + m_languageFilter = settingsWidget->filter(); + } +} + +QWidget *BaseSettings::createSettingsWidget(QWidget *parent) const +{ + return new BaseSettingsWidget(this, parent); +} + bool BaseSettings::needsRestart() const { return m_client ? !m_enabled || m_client->needsRestart(this) : m_enabled; @@ -398,7 +409,8 @@ QVariantMap BaseSettings::toMap() const QVariantMap map; map.insert(nameKey, m_name); map.insert(enabledKey, m_enabled); - map.insert(mimeTypeKey, m_mimeType); + map.insert(mimeTypeKey, m_languageFilter.mimeTypes); + map.insert(filePatternKey, m_languageFilter.filePattern); return map; } @@ -406,7 +418,8 @@ void BaseSettings::fromMap(const QVariantMap &map) { m_name = map[nameKey].toString(); m_enabled = map[enabledKey].toBool(); - m_mimeType = map[mimeTypeKey].toString(); + m_languageFilter.mimeTypes = map[mimeTypeKey].toStringList(); + m_languageFilter.filePattern = map[filePatternKey].toStringList(); } void LanguageClientSettings::init() @@ -438,6 +451,20 @@ void LanguageClientSettings::toSettings(QSettings *settings, const QList<StdIOSe settings->endGroup(); } +void StdIOSettings::applyFromSettingsWidget(QWidget *widget) +{ + if (auto settingsWidget = qobject_cast<StdIOSettingsWidget *>(widget)) { + BaseSettings::applyFromSettingsWidget(settingsWidget); + m_executable = settingsWidget->executable(); + m_arguments = settingsWidget->arguments(); + } +} + +QWidget *StdIOSettings::createSettingsWidget(QWidget *parent) const +{ + return new StdIOSettingsWidget(this, parent); +} + bool StdIOSettings::needsRestart() const { if (BaseSettings::needsRestart()) @@ -456,8 +483,7 @@ BaseClient *StdIOSettings::createClient() const { auto client = new StdIOClient(m_executable, m_arguments); client->setName(m_name); - if (m_mimeType != noLanguageFilter) - client->setSupportedMimeType({m_mimeType}); + client->setSupportedLanguage(m_languageFilter); return client; } @@ -476,4 +502,160 @@ void StdIOSettings::fromMap(const QVariantMap &map) m_arguments = map[argumentsKey].toString(); } +BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent) + : QWidget(parent) + , m_name(new QLineEdit(settings->m_name, this)) + , m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this)) + , m_filePattern(new QLineEdit(settings->m_languageFilter.filePattern.join(filterSeparator), this)) +{ + int row = 0; + auto *mainLayout = new QGridLayout; + mainLayout->addWidget(new QLabel(tr("Name:")), row, 0); + mainLayout->addWidget(m_name, row, 1); + mainLayout->addWidget(new QLabel(tr("Language:")), ++row, 0); + auto mimeLayout = new QHBoxLayout; + mimeLayout->addWidget(m_mimeTypes); + mimeLayout->addStretch(); + auto addMimeTypeButton = new QPushButton(tr("Set MIME Types..."), this); + mimeLayout->addWidget(addMimeTypeButton); + mainLayout->addLayout(mimeLayout, row, 1); + m_filePattern->setPlaceholderText(tr("File pattern")); + mainLayout->addWidget(m_filePattern, ++row, 1); + + connect(addMimeTypeButton, &QPushButton::pressed, + this, &BaseSettingsWidget::showAddMimeTypeDialog); + + setLayout(mainLayout); +} + +QString BaseSettingsWidget::name() const +{ + return m_name->text(); +} + +LanguageFilter BaseSettingsWidget::filter() const +{ + return {m_mimeTypes->text().split(filterSeparator), + m_filePattern->text().split(filterSeparator)}; +} + +class MimeTypeModel : public QStringListModel +{ +public: + using QStringListModel::QStringListModel; + QVariant data(const QModelIndex &index, int role) const final + { + if (index.isValid() && role == Qt::CheckStateRole) + return m_selectedMimeTypes.contains(index.data().toString()) ? Qt::Checked : Qt::Unchecked; + return QStringListModel::data(index, role); + } + bool setData(const QModelIndex &index, const QVariant &value, int role) final + { + if (index.isValid() && role == Qt::CheckStateRole) { + QString mimeType = index.data().toString(); + if (value.toInt() == Qt::Checked) { + if (!m_selectedMimeTypes.contains(mimeType)) + m_selectedMimeTypes.append(index.data().toString()); + } else { + m_selectedMimeTypes.removeAll(index.data().toString()); + } + return true; + } + return QStringListModel::setData(index, value, role); + } + + Qt::ItemFlags flags(const QModelIndex &index) const final + { + if (!index.isValid()) + return Qt::NoItemFlags; + return (QStringListModel::flags(index) + & ~(Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled)) + | Qt::ItemIsUserCheckable; + } + QStringList m_selectedMimeTypes; +}; + +class MimeTypeDialog : public QDialog +{ +public: + explicit MimeTypeDialog(const QStringList &selectedMimeTypes, QWidget *parent = nullptr) + : QDialog(parent) + { + setWindowTitle(tr("Select MIME Types")); + auto mainLayout = new QVBoxLayout; + auto filter = new Utils::FancyLineEdit(this); + filter->setFiltering(true); + mainLayout->addWidget(filter); + auto listView = new QListView(this); + mainLayout->addWidget(listView); + auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + mainLayout->addWidget(buttons); + setLayout(mainLayout); + + filter->setPlaceholderText(tr("Filter")); + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); + auto proxy = new QSortFilterProxyModel(this); + m_mimeTypeModel = new MimeTypeModel(Utils::transform(Utils::allMimeTypes(), + &Utils::MimeType::name), this); + m_mimeTypeModel->m_selectedMimeTypes = selectedMimeTypes; + proxy->setSourceModel(m_mimeTypeModel); + proxy->sort(0); + connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterWildcard); + listView->setModel(proxy); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + } + + MimeTypeDialog(const MimeTypeDialog &other) = delete; + MimeTypeDialog(MimeTypeDialog &&other) = delete; + + MimeTypeDialog operator=(const MimeTypeDialog &other) = delete; + MimeTypeDialog operator=(MimeTypeDialog &&other) = delete; + + + QStringList mimeTypes() const + { + return m_mimeTypeModel->m_selectedMimeTypes; + } +private: + MimeTypeModel *m_mimeTypeModel = nullptr; +}; + +void BaseSettingsWidget::showAddMimeTypeDialog() +{ + MimeTypeDialog dialog(m_mimeTypes->text().split(filterSeparator, QString::SkipEmptyParts), + Core::ICore::dialogParent()); + if (dialog.exec() == QDialog::Rejected) + return; + m_mimeTypes->setText(dialog.mimeTypes().join(filterSeparator)); +} + +StdIOSettingsWidget::StdIOSettingsWidget(const StdIOSettings *settings, QWidget *parent) + : BaseSettingsWidget(settings, parent) + , m_executable(new Utils::PathChooser(this)) + , m_arguments(new QLineEdit(settings->m_arguments, this)) +{ + auto mainLayout = qobject_cast<QGridLayout *>(layout()); + QTC_ASSERT(mainLayout, return); + const int baseRows = mainLayout->rowCount(); + mainLayout->addWidget(new QLabel(tr("Executable:")), baseRows, 0); + mainLayout->addWidget(m_executable, baseRows, 1); + mainLayout->addWidget(new QLabel(tr("Arguments:")), baseRows + 1, 0); + m_executable->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_executable->setPath(QDir::toNativeSeparators(settings->m_executable)); + mainLayout->addWidget(m_arguments, baseRows + 1, 1); +} + +QString StdIOSettingsWidget::executable() const +{ + return m_executable->path(); +} + +QString StdIOSettingsWidget::arguments() const +{ + return m_arguments->text(); +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h index 9f03d7d6ab..8068856ae2 100644 --- a/src/plugins/languageclient/languageclientsettings.h +++ b/src/plugins/languageclient/languageclientsettings.h @@ -28,32 +28,48 @@ #include <coreplugin/dialogs/ioptionspage.h> #include <QAbstractItemModel> +#include <QLabel> #include <QPointer> #include <QWidget> +QT_BEGIN_NAMESPACE +class QCheckBox; +class QLineEdit; +QT_END_NAMESPACE + +namespace Utils { class PathChooser; } + namespace LanguageClient { constexpr char noLanguageFilter[] = "No Filter"; class BaseClient; +struct LanguageFilter +{ + QStringList mimeTypes; + QStringList filePattern; +}; + class BaseSettings { public: BaseSettings() = default; - BaseSettings(const QString &name, bool enabled, const QString &mimeTypeName) + BaseSettings(const QString &name, bool enabled, const LanguageFilter &filter) : m_name(name) , m_enabled(enabled) - , m_mimeType(mimeTypeName) + , m_languageFilter(filter) {} virtual ~BaseSettings() = default; QString m_name = QString("New Language Server"); bool m_enabled = true; - QString m_mimeType = QLatin1String(noLanguageFilter); + LanguageFilter m_languageFilter; QPointer<BaseClient> m_client; // not owned + virtual void applyFromSettingsWidget(QWidget *widget); + virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const; virtual BaseSettings *copy() const { return new BaseSettings(*this); } virtual bool needsRestart() const; virtual bool isValid() const ; @@ -72,9 +88,9 @@ class StdIOSettings : public BaseSettings { public: StdIOSettings() = default; - StdIOSettings(const QString &name, bool enabled, const QString &mimeTypeName, + StdIOSettings(const QString &name, bool enabled, const LanguageFilter &filter, const QString &executable, const QString &arguments) - : BaseSettings(name, enabled, mimeTypeName) + : BaseSettings(name, enabled, filter) , m_executable(executable) , m_arguments(arguments) {} @@ -84,6 +100,8 @@ public: QString m_executable; QString m_arguments; + void applyFromSettingsWidget(QWidget *widget) override; + QWidget *createSettingsWidget(QWidget *parent = nullptr) const override; BaseSettings *copy() const override { return new StdIOSettings(*this); } bool needsRestart() const override; bool isValid() const override; @@ -106,4 +124,39 @@ public: static void toSettings(QSettings *settings, const QList<StdIOSettings *> &languageClientSettings); }; +class BaseSettingsWidget : public QWidget +{ + Q_OBJECT +public: + explicit BaseSettingsWidget(const BaseSettings* settings, QWidget *parent = nullptr); + ~BaseSettingsWidget() = default; + + QString name() const; + LanguageFilter filter() const; + +private: + void showAddMimeTypeDialog(); + + QLineEdit *m_name = nullptr; + QLabel *m_mimeTypes = nullptr; + QLineEdit *m_filePattern = nullptr; + + static constexpr char filterSeparator = ';'; +}; + +class StdIOSettingsWidget : public BaseSettingsWidget +{ + Q_OBJECT +public: + explicit StdIOSettingsWidget(const StdIOSettings* settings, QWidget *parent = nullptr); + ~StdIOSettingsWidget() = default; + + QString executable() const; + QString arguments() const; + +private: + Utils::PathChooser *m_executable = nullptr; + QLineEdit *m_arguments = nullptr; +}; + } // namespace LanguageClient diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 1c2b315095..cecf44be2c 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -278,11 +278,6 @@ QString CustomExecutableRunConfiguration::defaultDisplayName() const return tr("Run %1").arg(QDir::toNativeSeparators(rawExecutable())); } -Abi CustomExecutableRunConfiguration::abi() const -{ - return Abi(); // return an invalid ABI: We do not know what we will end up running! -} - // Factory CustomExecutableRunConfigurationFactory::CustomExecutableRunConfigurationFactory() : diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.h b/src/plugins/projectexplorer/customexecutablerunconfiguration.h index c8b9d8144b..6abd0f3185 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.h +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.h @@ -45,7 +45,6 @@ public: /** Returns whether this runconfiguration ever was configured with an executable */ bool isConfigured() const override; - Abi abi() const override; ConfigurationState ensureConfigured(QString *errorMessage) override; QString defaultDisplayName() const; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 1af06a98ed..394d4a26ae 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -489,7 +489,7 @@ LanguageExtensions GccToolChain::languageExtensions(const QStringList &cxxflags) else extensions &= ~LanguageExtensions(LanguageExtension::Gnu); } else if (flag == "-fopenmp") { - extensions |= LanguageExtension::Gnu; + extensions |= LanguageExtension::OpenMP; } else if (flag == "-fms-extensions") { extensions |= LanguageExtension::Microsoft; } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index c708dd5af4..894fadcfa4 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp @@ -31,6 +31,7 @@ #include "../project.h" #include "../projectexplorer.h" #include "../projectexplorerconstants.h" +#include "../projecttree.h" #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/messagemanager.h> @@ -290,7 +291,7 @@ void JsonWizard::accept() openFiles(m_files); auto node = static_cast<ProjectExplorer::Node*>(value(ProjectExplorer::Constants::PREFERRED_PROJECT_NODE).value<void*>()); - if (node) // PREFERRED_PROJECT_NODE is not set for newly created projects + if (node && ProjectTree::hasNode(node)) // PREFERRED_PROJECT_NODE is not set for newly created projects openProjectForNode(node); } diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 50c3a9d369..45fa8a5ece 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1296,6 +1296,11 @@ bool MsvcToolChain::operator ==(const ToolChain &other) const return m_varsBatArg == msvcTc->m_varsBatArg; } +void MsvcToolChain::cancelMsvcToolChainDetection() +{ + envModThreadPool()->clear(); +} + bool MsvcToolChainFactory::canRestore(const QVariantMap &data) { const Core::Id id = typeIdFromMap(data); diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 88ba1523cd..d26b8c52bf 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -82,6 +82,8 @@ public: bool operator == (const ToolChain &) const override; + static void cancelMsvcToolChainDetection(); + protected: explicit MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi, const QString &varsBat, const QString &varsBatArg, diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index d8a131dc5f..5048c2d0ca 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1739,6 +1739,7 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown() disconnect(ModeManager::instance(), &ModeManager::currentModeChanged, dd, &ProjectExplorerPluginPrivate::currentModeChanged); ProjectTree::aboutToShutDown(); + ToolChainManager::aboutToShutdown(); SessionManager::closeAllProjects(); dd->m_shuttingDown = true; diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 9cf3167a24..08842aabea 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -127,12 +127,18 @@ ProjectWelcomePage::ProjectWelcomePage() auto act = new QAction(tr("Open Session #%1").arg(i), this); Command *cmd = ActionManager::registerAction(act, sessionBase.withSuffix(i), welcomeContext); cmd->setDefaultKeySequence(QKeySequence((useMacShortcuts ? tr("Ctrl+Meta+%1") : tr("Ctrl+Alt+%1")).arg(i))); - connect(act, &QAction::triggered, this, [this, i] { openSessionAt(i - 1); }); + connect(act, &QAction::triggered, this, [this, i] { + if (i <= m_sessionModel->rowCount()) + openSessionAt(i - 1); + }); act = new QAction(tr("Open Recent Project #%1").arg(i), this); cmd = ActionManager::registerAction(act, projectBase.withSuffix(i), welcomeContext); cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+%1").arg(i))); - connect(act, &QAction::triggered, this, [this, i] { openProjectAt(i - 1); }); + connect(act, &QAction::triggered, this, [this, i] { + if (i <= m_projectModel->rowCount(QModelIndex())) + openProjectAt(i - 1); + }); } } diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index ee86abe895..500a13a4fc 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -456,10 +456,11 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const QStringList & } root->prependChild(createNoneNode(&selector)); - // Set combobox to context node: + // Set combobox to context node if that appears in the tree: auto predicate = [context](TreeItem *ti) { return static_cast<AddNewTree*>(ti)->node() == context; }; TreeItem *contextItem = root->findAnyChild(predicate); - m_ui->projectComboBox->setCurrentIndex(m_model.indexForItem(contextItem)); + if (contextItem) + m_ui->projectComboBox->setCurrentIndex(m_model.indexForItem(contextItem)); setAdditionalInfo(selector.deployingProjects()); setBestNode(selector.bestChoice()); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index e6f0302bdf..d5ec08471f 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -310,17 +310,6 @@ QVariantMap RunConfiguration::toMap() const return map; } -Abi RunConfiguration::abi() const -{ - BuildConfiguration *bc = target()->activeBuildConfiguration(); - if (!bc) - return Abi::hostAbi(); - ToolChain *tc = ToolChainKitInformation::toolChain(target()->kit(), Constants::CXX_LANGUAGE_ID); - if (!tc) - return Abi::hostAbi(); - return tc->targetAbi(); -} - BuildTargetInfo RunConfiguration::buildTargetInfo() const { return target()->applicationTargets().buildTargetInfo(m_buildKey); @@ -1334,13 +1323,6 @@ Utils::Icon RunControl::icon() const return d->icon; } -Abi RunControl::abi() const -{ - if (const RunConfiguration *rc = d->runConfiguration.data()) - return rc->abi(); - return Abi(); -} - IDevice::ConstPtr RunControl::device() const { return d->device; diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index 9a973b42eb..c31aeb9de0 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -46,7 +46,6 @@ namespace Utils { class OutputFormatter; } namespace ProjectExplorer { -class Abi; class BuildConfiguration; class GlobalOrProjectAspect; class Node; @@ -172,7 +171,6 @@ public: QVariantMap toMap() const override; virtual Runnable runnable() const; - virtual Abi abi() const; // Return a handle to the build system target that created this run configuration. // May return an empty string if no target built the executable! @@ -439,7 +437,6 @@ public: Utils::ProcessHandle applicationProcessHandle() const; void setApplicationProcessHandle(const Utils::ProcessHandle &handle); - Abi abi() const; IDevice::ConstPtr device() const; RunConfiguration *runConfiguration() const; diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index 2a5c899570..caa15e6d2f 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -27,6 +27,7 @@ #include "abi.h" #include "kitinformation.h" +#include "msvctoolchain.h" #include "toolchain.h" #include "toolchainsettingsaccessor.h" @@ -249,4 +250,11 @@ bool ToolChainManager::isLanguageSupported(const Core::Id &id) return Utils::contains(d->m_languages, Utils::equal(&LanguageDisplayPair::id, id)); } +void ToolChainManager::aboutToShutdown() +{ +#ifdef Q_OS_WIN + MsvcToolChain::cancelMsvcToolChainDetection(); +#endif +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainmanager.h b/src/plugins/projectexplorer/toolchainmanager.h index 3fdcd98126..2e26431b84 100644 --- a/src/plugins/projectexplorer/toolchainmanager.h +++ b/src/plugins/projectexplorer/toolchainmanager.h @@ -74,6 +74,8 @@ public: static QString displayNameOfLanguageId(const Core::Id &id); static bool isLanguageSupported(const Core::Id &id); + static void aboutToShutdown(); + void saveToolChains(); signals: diff --git a/src/plugins/projectexplorer/windebuginterface.cpp b/src/plugins/projectexplorer/windebuginterface.cpp index ce3950acb9..2f7bbcc4d8 100644 --- a/src/plugins/projectexplorer/windebuginterface.cpp +++ b/src/plugins/projectexplorer/windebuginterface.cpp @@ -177,6 +177,8 @@ void WinDebugInterface::dispatchDebugOutput() m_outputMutex.lock(); for (auto &entry : m_debugOutput) { std::vector<QString> &src = entry.second; + if (src.empty()) + continue; QString dst; size_t n = std::min(maxMessagesToSend, src.size()); for (size_t i = 0; i < n; ++i) diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 7981547ab3..a1df7d2e2c 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -72,6 +72,7 @@ #include <QElapsedTimer> #include <QFileInfo> #include <QMessageBox> +#include <QSet> #include <QVariantMap> #include <algorithm> @@ -936,13 +937,14 @@ void QbsProject::updateCppCodeModel() QString objcPch; QString objcxxPch; const auto &pchFinder = [&cPch, &cxxPch, &objcPch, &objcxxPch](const qbs::ArtifactData &a) { - if (a.fileTags().contains("c_pch_src")) + const QStringList fileTags = a.fileTags(); + if (fileTags.contains("c_pch_src")) cPch = a.filePath(); - else if (a.fileTags().contains("cpp_pch_src")) + if (fileTags.contains("cpp_pch_src")) cxxPch = a.filePath(); - else if (a.fileTags().contains("objc_pch_src")) + if (fileTags.contains("objc_pch_src")) objcPch = a.filePath(); - else if (a.fileTags().contains("objcpp_pch_src")) + if (fileTags.contains("objcpp_pch_src")) objcxxPch = a.filePath(); }; const QList<qbs::ArtifactData> &generatedArtifacts = prd.generatedArtifacts(); @@ -1038,7 +1040,7 @@ void QbsProject::updateCppCodeModel() } } - QStringList pchFiles; + QSet<QString> pchFiles; if (hasCFiles && props.getModuleProperty("cpp", "useCPrecompiledHeader").toBool() && !cPch.isEmpty()) { pchFiles << cPch; @@ -1061,7 +1063,7 @@ void QbsProject::updateCppCodeModel() << grp.name() << "in product" << prd.name(); qCWarning(qbsPmLog) << "Expect problems with code model"; } - rpp.setPreCompiledHeaders(pchFiles); + rpp.setPreCompiledHeaders(pchFiles.toList()); rpp.setFiles(grp.allFilePaths(), [filePathToSourceArtifact](const QString &filePath) { // Keep this lambda thread-safe! return cppFileType(filePathToSourceArtifact.value(filePath)); diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index 0dce2b2a10..43cc0f5f24 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -371,7 +371,11 @@ QString PuppetCreator::qmlPuppetDirectory(PuppetType puppetType) const return qmlPuppetToplevelBuildDirectory() + '/' + QCoreApplication::applicationVersion() + '/' + QString::fromLatin1(qtHash()); - return qmlPuppetFallbackDirectory(); +#ifndef QMLDESIGNER_TEST + return qmlPuppetFallbackDirectory(m_designerSettings); +#else + return QString(); +#endif } QString PuppetCreator::defaultPuppetFallbackDirectory() @@ -382,15 +386,16 @@ QString PuppetCreator::defaultPuppetFallbackDirectory() return Core::ICore::libexecPath(); } -QString PuppetCreator::qmlPuppetFallbackDirectory() const +QString PuppetCreator::qmlPuppetFallbackDirectory(const DesignerSettings &settings) { #ifndef QMLDESIGNER_TEST - QString puppetFallbackDirectory = m_designerSettings.value( + QString puppetFallbackDirectory = settings.value( DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY).toString(); - if (puppetFallbackDirectory.isEmpty()) + if (puppetFallbackDirectory.isEmpty() || !QFileInfo::exists(puppetFallbackDirectory)) return defaultPuppetFallbackDirectory(); return puppetFallbackDirectory; #else + Q_UNUSED(settings); return QString(); #endif } diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h index 95c7d7e68a..8cde9692ad 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h @@ -66,12 +66,12 @@ public: static QString defaultPuppetToplevelBuildDirectory(); static QString defaultPuppetFallbackDirectory(); + static QString qmlPuppetFallbackDirectory(const DesignerSettings &settings); protected: bool build(const QString &qmlPuppetProjectFilePath) const; QString qmlPuppetToplevelBuildDirectory() const; - QString qmlPuppetFallbackDirectory() const; QString qmlPuppetDirectory(PuppetType puppetPathType) const; QString qml2PuppetPath(PuppetType puppetType) const; diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 6fb88e5f21..041efd809b 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -69,7 +69,6 @@ SettingsPageWidget::SettingsPageWidget(QWidget *parent) : PuppetCreator::defaultPuppetFallbackDirectory()); } ); - m_ui.fallbackPuppetPathLineEdit->setPath(PuppetCreator::defaultPuppetFallbackDirectory()); m_ui.fallbackPuppetPathLineEdit->lineEdit()->setPlaceholderText(PuppetCreator::defaultPuppetFallbackDirectory()); connect(m_ui.resetQmlPuppetBuildPathButton, &QPushButton::clicked, [=]() { @@ -138,11 +137,15 @@ DesignerSettings SettingsPageWidget::settings() const m_ui.fallbackPuppetPathLineEdit->lineEdit()->placeholderText()); if (newFallbackPuppetPath.isEmpty()) newFallbackPuppetPath = m_ui.fallbackPuppetPathLineEdit->lineEdit()->placeholderText(); - QString oldFallbackPuppetPath = settings.value(DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY, - PuppetCreator::defaultPuppetFallbackDirectory()).toString(); - if (oldFallbackPuppetPath != newFallbackPuppetPath) { - settings.insert(DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY, - newFallbackPuppetPath); + QString oldFallbackPuppetPath = PuppetCreator::qmlPuppetFallbackDirectory(settings); + + if (oldFallbackPuppetPath != newFallbackPuppetPath && QFileInfo::exists(newFallbackPuppetPath)) { + if (newFallbackPuppetPath == PuppetCreator::defaultPuppetFallbackDirectory()) + settings.insert(DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY, QString()); + else + settings.insert(DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY, newFallbackPuppetPath); + } else if (!QFileInfo::exists(oldFallbackPuppetPath) || !QFileInfo::exists(newFallbackPuppetPath)){ + settings.insert(DesignerSettingsKey::PUPPET_FALLBACK_DIRECTORY, QString()); } if (!m_ui.puppetBuildPathLineEdit->path().isEmpty() && diff --git a/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp index a519f6a775..8f7ecb778e 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp @@ -54,6 +54,13 @@ void QmlProfilerToolTest::testAttachToWaitingApplication() settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/kitId"), newKitPtr->id().toSetting()); QmlProfilerTool profilerTool; + + QmlProfilerClientManager *clientManager = profilerTool.clientManager(); + clientManager->setRetryInterval(10); + clientManager->setMaximumRetries(10); + connect(clientManager, &QmlProfilerClientManager::connectionFailed, + clientManager, &QmlProfilerClientManager::retryConnect); + QTcpServer server; QUrl serverUrl = Utils::urlFromLocalHostAndFreePort(); QVERIFY(serverUrl.port() >= 0); @@ -64,7 +71,6 @@ void QmlProfilerToolTest::testAttachToWaitingApplication() connect(&server, &QTcpServer::newConnection, this, [&]() { connection.reset(server.nextPendingConnection()); fakeDebugServer(connection.data()); - server.close(); }); QTimer timer; @@ -94,7 +100,7 @@ void QmlProfilerToolTest::testAttachToWaitingApplication() QTRY_VERIFY(runControl->isRunning()); QTRY_VERIFY(modalSeen); QTRY_VERIFY(!timer.isActive()); - QTRY_VERIFY(profilerTool.clientManager()->isConnected()); + QTRY_VERIFY(clientManager->isConnected()); connection.reset(); QTRY_VERIFY(runControl->isStopped()); diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 55e32ec508..9ba68b556a 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -374,13 +374,6 @@ QString QmlProjectRunConfiguration::commandLineArguments() const return args; } -Abi QmlProjectRunConfiguration::abi() const -{ - Abi hostAbi = Abi::hostAbi(); - return Abi(hostAbi.architecture(), hostAbi.os(), hostAbi.osFlavor(), - Abi::RuntimeQmlFormat, hostAbi.wordWidth()); -} - void QmlProjectRunConfiguration::updateEnabledState() { bool qmlFileFound = m_mainQmlFileAspect->isQmlFilePresent(); diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h index b67e4238f0..adfd3103a7 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.h @@ -44,7 +44,6 @@ public: private: ProjectExplorer::Runnable runnable() const final; QString disabledReason() const final; - ProjectExplorer::Abi abi() const final; void updateEnabledState() final; QString mainScript() const; diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index 2a01ada15c..15c7f56cc8 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -245,10 +245,17 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, case IAssistProvider::Asynchronous: { processor->setAsyncCompletionAvailableHandler( [this, reason](IAssistProposal *newProposal){ - invalidateCurrentRequestData(); - displayProposal(newProposal, reason); - - emit q->finished(); + if (m_asyncProcessor && m_asyncProcessor->needsRestart() && m_receivedContentWhileWaiting) { + delete newProposal; + m_receivedContentWhileWaiting = false; + invalidateCurrentRequestData(); + requestProposal(reason, m_assistKind, m_requestProvider); + } else { + invalidateCurrentRequestData(); + displayProposal(newProposal, reason); + + emit q->finished(); + } }); // If there is a proposal, nothing asynchronous happened... diff --git a/src/plugins/texteditor/codeassist/iassistprocessor.h b/src/plugins/texteditor/codeassist/iassistprocessor.h index 23c06b6835..6ed29b66f1 100644 --- a/src/plugins/texteditor/codeassist/iassistprocessor.h +++ b/src/plugins/texteditor/codeassist/iassistprocessor.h @@ -50,6 +50,7 @@ public: void setAsyncCompletionAvailableHandler(const AsyncCompletionsAvailableHandler &finalizer); virtual bool running() { return false; } + virtual bool needsRestart() const { return false; } private: AsyncCompletionsAvailableHandler m_asyncCompletionsAvailableHandler; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 89823a7c12..3141a12620 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -2474,6 +2474,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e) e->accept(); if (extraBlocks > 0) { + cursor.joinPreviousEditBlock(); const int cursorPosition = cursor.position(); QTextCursor ensureVisible = cursor; while (extraBlocks > 0) { @@ -2493,6 +2494,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e) } setTextCursor(ensureVisible); cursor.setPosition(cursorPosition); + cursor.endEditBlock(); } setTextCursor(cursor); diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp index af1eac8f32..5433455981 100644 --- a/src/plugins/vcsbase/vcscommand.cpp +++ b/src/plugins/vcsbase/vcscommand.cpp @@ -29,6 +29,7 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/vcsmanager.h> +#include <cpptools/cppmodelmanager.h> #include <utils/synchronousprocess.h> #include <QProcessEnvironment> @@ -42,7 +43,7 @@ VcsCommand::VcsCommand(const QString &workingDirectory, Core::ShellCommand(workingDirectory, environment), m_preventRepositoryChanged(false) { - setOutputProxyFactory([this]() -> OutputProxy * { + setOutputProxyFactory([this] { auto proxy = new OutputProxy; VcsOutputWindow *outputWindow = VcsOutputWindow::instance(); @@ -60,12 +61,16 @@ VcsCommand::VcsCommand(const QString &workingDirectory, return proxy; }); connect(this, &VcsCommand::started, this, [this] { - if (flags() & ExpectRepoChanges) + if (flags() & ExpectRepoChanges) { Core::DocumentManager::setAutoReloadPostponed(true); + CppTools::CppModelManager::instance()->setBackendJobsPostponed(true); + } }); connect(this, &VcsCommand::finished, this, [this] { - if (flags() & ExpectRepoChanges) + if (flags() & ExpectRepoChanges) { Core::DocumentManager::setAutoReloadPostponed(false); + CppTools::CppModelManager::instance()->setBackendJobsPostponed(false); + } }); } diff --git a/src/shared/qbs b/src/shared/qbs -Subproject fa5e42f915211637da0d6461c9764962ee47f92 +Subproject b9e5dd581a06a97d45260fa9b393b135a9f1ad8 diff --git a/src/tools/clangbackend/source/clangfollowsymbol.cpp b/src/tools/clangbackend/source/clangfollowsymbol.cpp index f8d763ae5e..27e8fb61e9 100644 --- a/src/tools/clangbackend/source/clangfollowsymbol.cpp +++ b/src/tools/clangbackend/source/clangfollowsymbol.cpp @@ -112,7 +112,7 @@ FollowSymbolResult FollowSymbol::followSymbol(CXTranslationUnit tu, if (cursor.kind() == CXCursor_InclusionDirective) { CXFile file = clang_getIncludedFile(cursors[tokenIndex].cx()); const ClangString filename(clang_getFileName(file)); - const SourceLocation loc(tu, filename, 1, 1); + const SourceLocation loc(tu, clang_getLocation(tu, file, 1, 1)); FollowSymbolResult result; result.range = SourceRangeContainer(SourceRange(loc, loc)); // CLANG-UPGRADE-CHECK: Remove if we don't use empty generated ui_* headers anymore. diff --git a/src/tools/clangbackend/source/clangtranslationunit.cpp b/src/tools/clangbackend/source/clangtranslationunit.cpp index 6eebc8e203..cc848f89ab 100644 --- a/src/tools/clangbackend/source/clangtranslationunit.cpp +++ b/src/tools/clangbackend/source/clangtranslationunit.cpp @@ -154,14 +154,22 @@ DiagnosticSet TranslationUnit::diagnostics() const SourceLocation TranslationUnit::sourceLocationAt(uint line,uint column) const { - return SourceLocation(m_cxTranslationUnit, m_filePath, line, column); + return SourceLocation(m_cxTranslationUnit, + clang_getLocation(m_cxTranslationUnit, + clang_getFile(m_cxTranslationUnit, + m_filePath.constData()), + line, column)); } SourceLocation TranslationUnit::sourceLocationAt(const Utf8String &filePath, uint line, uint column) const { - return SourceLocation(m_cxTranslationUnit, filePath, line, column); + return SourceLocation(m_cxTranslationUnit, + clang_getLocation(m_cxTranslationUnit, + clang_getFile(m_cxTranslationUnit, + filePath.constData()), + line, column)); } SourceRange TranslationUnit::sourceRange(uint fromLine, diff --git a/src/tools/clangbackend/source/codecompleter.cpp b/src/tools/clangbackend/source/codecompleter.cpp index 5520b5b3a9..0613adf146 100644 --- a/src/tools/clangbackend/source/codecompleter.cpp +++ b/src/tools/clangbackend/source/codecompleter.cpp @@ -48,14 +48,14 @@ namespace ClangBackEnd { namespace { -CodeCompletions toCodeCompletions(const TranslationUnit &translationUnit, +CodeCompletions toCodeCompletions(const UnsavedFile &unsavedFile, const ClangCodeCompleteResults &results, bool onlyFunctionOverloads) { if (results.isNull()) return CodeCompletions(); - CodeCompletionsExtractor extractor(translationUnit.cxTranslationUnit(), results.data()); + CodeCompletionsExtractor extractor(unsavedFile, results.data()); CodeCompletions codeCompletions = extractor.extractAll(onlyFunctionOverloads); return codeCompletions; @@ -118,7 +118,9 @@ CodeCompletions CodeCompleter::complete(uint line, uint column, filterUnknownContextResults(clangCompletions, unsavedFile(), line, column); - return toCodeCompletions(translationUnit, clangCompletions, funcNameStartLine >= 0); + return toCodeCompletions(unsavedFiles.unsavedFile(translationUnit.filePath()), + clangCompletions, + funcNameStartLine >= 0); } // For given "make_unique<T>" / "make_shared<T>" / "QSharedPointer<T>::create" return "new T(" diff --git a/src/tools/clangbackend/source/codecompletionsextractor.cpp b/src/tools/clangbackend/source/codecompletionsextractor.cpp index 25be6bddcf..2e2817c72b 100644 --- a/src/tools/clangbackend/source/codecompletionsextractor.cpp +++ b/src/tools/clangbackend/source/codecompletionsextractor.cpp @@ -31,19 +31,20 @@ #include "codecompletionchunkconverter.h" #include "sourcelocation.h" #include "sourcerange.h" +#include "unsavedfile.h" #include <utils/algorithm.h> +#include <utils/qtcassert.h> #include <QDebug> namespace ClangBackEnd { -CodeCompletionsExtractor::CodeCompletionsExtractor(CXTranslationUnit cxTranslationUnit, +CodeCompletionsExtractor::CodeCompletionsExtractor(const UnsavedFile &unsavedFile, CXCodeCompleteResults *cxCodeCompleteResults) - : cxTranslationUnit(cxTranslationUnit) + : unsavedFile(unsavedFile) , cxCodeCompleteResults(cxCodeCompleteResults) { - } bool CodeCompletionsExtractor::next() @@ -342,6 +343,27 @@ void CodeCompletionsExtractor::extractCompletionChunks() currentCodeCompletion_.chunks = CodeCompletionChunkConverter::extract(currentCxCodeCompleteResult.CompletionString); } +SourceRangeContainer toRangeContainer(const UnsavedFile &file, CXSourceRange cxSourceRange) +{ + const CXSourceLocation start = clang_getRangeStart(cxSourceRange); + const CXSourceLocation end = clang_getRangeEnd(cxSourceRange); + + uint startLine = 0; + uint startColumn = 0; + uint endLine = 0; + uint endColumn = 0; + clang_getFileLocation(start, nullptr, &startLine, &startColumn, nullptr); + clang_getFileLocation(end, nullptr, &endLine, &endColumn, nullptr); + QTC_ASSERT(startLine == endLine, return SourceRangeContainer();); + + const Utf8String lineText = file.lineRange(startLine, endLine); + startColumn = QString(lineText.mid(0, startColumn - 1)).size() + 1; + endColumn = QString(lineText.mid(0, endColumn - 1)).size() + 1; + + return SourceRangeContainer(SourceLocationContainer(file.filePath(), startLine, startColumn), + SourceLocationContainer(file.filePath(), endLine, endColumn)); +} + void CodeCompletionsExtractor::extractRequiredFixIts() { #ifdef IS_COMPLETION_FIXITS_BACKPORTED @@ -358,7 +380,7 @@ void CodeCompletionsExtractor::extractRequiredFixIts() i, &range); currentCodeCompletion_.requiredFixIts.push_back( - FixItContainer(Utf8String(fixIt), SourceRange(cxTranslationUnit, range))); + FixItContainer(Utf8String(fixIt), toRangeContainer(unsavedFile, range))); } #endif } diff --git a/src/tools/clangbackend/source/codecompletionsextractor.h b/src/tools/clangbackend/source/codecompletionsextractor.h index 928dabd25d..bba9aa6eb5 100644 --- a/src/tools/clangbackend/source/codecompletionsextractor.h +++ b/src/tools/clangbackend/source/codecompletionsextractor.h @@ -35,10 +35,12 @@ namespace ClangBackEnd { +class UnsavedFile; + class CodeCompletionsExtractor { public: - CodeCompletionsExtractor(CXTranslationUnit cxTranslationUnit, + CodeCompletionsExtractor(const UnsavedFile &unsavedFile, CXCodeCompleteResults *cxCodeCompleteResults); CodeCompletionsExtractor(CodeCompletionsExtractor&) = delete; @@ -79,7 +81,7 @@ private: private: CodeCompletion currentCodeCompletion_; - CXTranslationUnit cxTranslationUnit; + const UnsavedFile &unsavedFile; CXCompletionResult currentCxCodeCompleteResult; CXCodeCompleteResults *cxCodeCompleteResults; uint cxCodeCompleteResultIndex = 0; diff --git a/src/tools/clangbackend/source/skippedsourceranges.cpp b/src/tools/clangbackend/source/skippedsourceranges.cpp index f15bd44668..2ce2f15acb 100644 --- a/src/tools/clangbackend/source/skippedsourceranges.cpp +++ b/src/tools/clangbackend/source/skippedsourceranges.cpp @@ -66,7 +66,11 @@ static SourceRange adaptedSourceRange(CXTranslationUnit cxTranslationUnit, const return SourceRange { range.start(), - SourceLocation(cxTranslationUnit, end.filePath(), end.line(), 1) + SourceLocation(cxTranslationUnit, + clang_getLocation(cxTranslationUnit, + clang_getFile(cxTranslationUnit, + end.filePath().constData()), + end.line(), 1)) }; } diff --git a/src/tools/clangbackend/source/sourcelocation.cpp b/src/tools/clangbackend/source/sourcelocation.cpp index 7fbe8c03eb..70bb0e16aa 100644 --- a/src/tools/clangbackend/source/sourcelocation.cpp +++ b/src/tools/clangbackend/source/sourcelocation.cpp @@ -40,93 +40,92 @@ namespace ClangBackEnd { SourceLocation::SourceLocation() - : cxSourceLocation(clang_getNullLocation()) + : m_cxSourceLocation(clang_getNullLocation()) { } const Utf8String &SourceLocation::filePath() const { - if (isFilePathNormalized_) - return filePath_; + if (!m_isEvaluated) + evaluate(); - isFilePathNormalized_ = true; - filePath_ = FilePath::fromNativeSeparators(filePath_); + if (m_isFilePathNormalized) + return m_filePath; - return filePath_; + m_isFilePathNormalized = true; + m_filePath = FilePath::fromNativeSeparators(m_filePath); + + return m_filePath; } uint SourceLocation::line() const { - return line_; + if (!m_isEvaluated) + evaluate(); + return m_line; } uint SourceLocation::column() const { - return column_; + if (!m_isEvaluated) + evaluate(); + return m_column; } uint SourceLocation::offset() const { - return offset_; + if (!m_isEvaluated) + evaluate(); + return m_offset; } SourceLocationContainer SourceLocation::toSourceLocationContainer() const { - return SourceLocationContainer(filePath(), line_, column_); + if (!m_isEvaluated) + evaluate(); + return SourceLocationContainer(filePath(), m_line, m_column); } -SourceLocation::SourceLocation(CXTranslationUnit cxTranslationUnit, - CXSourceLocation cxSourceLocation) - : cxSourceLocation(cxSourceLocation) - , cxTranslationUnit(cxTranslationUnit) +void SourceLocation::evaluate() const { + m_isEvaluated = true; + CXFile cxFile; - clang_getFileLocation(cxSourceLocation, + clang_getFileLocation(m_cxSourceLocation, &cxFile, - &line_, - &column_, - &offset_); + &m_line, + &m_column, + &m_offset); - isFilePathNormalized_ = false; + m_isFilePathNormalized = false; if (!cxFile) return; - filePath_ = ClangString(clang_getFileName(cxFile)); - if (column_ > 1) { - const uint lineStart = offset_ + 1 - column_; - const char *contents = clang_getFileContents(cxTranslationUnit, cxFile, nullptr); + m_filePath = ClangString(clang_getFileName(cxFile)); + if (m_column > 1) { + const uint lineStart = m_offset + 1 - m_column; + const char *contents = clang_getFileContents(m_cxTranslationUnit, cxFile, nullptr); if (!contents) return; // (1) column in SourceLocation is the actual column shown by CppEditor. // (2) column in Clang is the utf8 byte offset from the beginning of the line. // Here we convert column from (2) to (1). - column_ = static_cast<uint>(QString::fromUtf8(&contents[lineStart], - static_cast<int>(column_)).size()); + m_column = static_cast<uint>(QString::fromUtf8(&contents[lineStart], + static_cast<int>(m_column) - 1).size()) + 1; } } SourceLocation::SourceLocation(CXTranslationUnit cxTranslationUnit, - const Utf8String &filePath, - uint line, - uint column) - : cxSourceLocation(clang_getLocation(cxTranslationUnit, - clang_getFile(cxTranslationUnit, - filePath.constData()), - line, - column)), - cxTranslationUnit(cxTranslationUnit), - filePath_(filePath), - line_(line), - column_(column), - isFilePathNormalized_(true) + CXSourceLocation cxSourceLocation) + : m_cxSourceLocation(cxSourceLocation) + , m_cxTranslationUnit(cxTranslationUnit) { - clang_getFileLocation(cxSourceLocation, 0, 0, 0, &offset_); } SourceLocation::operator CXSourceLocation() const { - return cxSourceLocation; + return m_cxSourceLocation; } std::ostream &operator<<(std::ostream &os, const SourceLocation &sourceLocation) diff --git a/src/tools/clangbackend/source/sourcelocation.h b/src/tools/clangbackend/source/sourcelocation.h index da0e68f973..63a600a2a6 100644 --- a/src/tools/clangbackend/source/sourcelocation.h +++ b/src/tools/clangbackend/source/sourcelocation.h @@ -41,7 +41,7 @@ class SourceLocation friend bool operator==(const SourceLocation &first, const SourceLocation &second) { - return clang_equalLocations(first.cxSourceLocation, second.cxSourceLocation); + return clang_equalLocations(first.m_cxSourceLocation, second.m_cxSourceLocation); } friend bool operator!=(const SourceLocation &first, const SourceLocation &second) { @@ -51,10 +51,6 @@ class SourceLocation public: SourceLocation(); SourceLocation(CXTranslationUnit cxTranslationUnit, - const Utf8String &filePath, - uint line, - uint column); - SourceLocation(CXTranslationUnit cxTranslationUnit, CXSourceLocation cxSourceLocation); const Utf8String &filePath() const; @@ -64,20 +60,22 @@ public: SourceLocationContainer toSourceLocationContainer() const; - CXTranslationUnit tu() const { return cxTranslationUnit; } - CXSourceLocation cx() const { return cxSourceLocation; } + CXTranslationUnit tu() const { return m_cxTranslationUnit; } + CXSourceLocation cx() const { return m_cxSourceLocation; } private: operator CXSourceLocation() const; + void evaluate() const; private: - CXSourceLocation cxSourceLocation; - CXTranslationUnit cxTranslationUnit; - mutable Utf8String filePath_; - uint line_ = 0; - uint column_ = 0; - uint offset_ = 0; - mutable bool isFilePathNormalized_ = true; + CXSourceLocation m_cxSourceLocation; + CXTranslationUnit m_cxTranslationUnit; + mutable Utf8String m_filePath; + mutable uint m_line = 0; + mutable uint m_column = 0; + mutable uint m_offset = 0; + mutable bool m_isFilePathNormalized = true; + mutable bool m_isEvaluated = false; }; std::ostream &operator<<(std::ostream &os, const SourceLocation &sourceLocation); diff --git a/src/tools/clangbackend/source/sourcerange.cpp b/src/tools/clangbackend/source/sourcerange.cpp index e3a7da6445..413ddd77fa 100644 --- a/src/tools/clangbackend/source/sourcerange.cpp +++ b/src/tools/clangbackend/source/sourcerange.cpp @@ -40,7 +40,7 @@ SourceRange::SourceRange() SourceRange::SourceRange(const SourceLocation &start, const SourceLocation &end) : cxSourceRange(clang_getRange(start, end)), - cxTranslationUnit(start.cxTranslationUnit) + cxTranslationUnit(start.m_cxTranslationUnit) { } diff --git a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri index 6e965792bc..1e1dde72ec 100644 --- a/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri +++ b/src/tools/clangpchmanagerbackend/source/clangpchmanagerbackend-source.pri @@ -26,7 +26,9 @@ HEADERS += \ $$PWD/collectusedmacroactionfactory.h \ $$PWD/collectusedmacrosaction.h \ $$PWD/collectusedmacrosandsourcespreprocessorcallbacks.h \ - $$PWD/usedmacrosandsourcescollector.h + $$PWD/usedmacrosandsourcescollector.h \ + $$PWD/usedmacroandsourcestorageinterface.h \ + $$PWD/usedmacroandsourcestorage.h !isEmpty(LIBTOOLING_LIBS) { SOURCES += \ diff --git a/src/tools/clangpchmanagerbackend/source/collectincludespreprocessorcallbacks.h b/src/tools/clangpchmanagerbackend/source/collectincludespreprocessorcallbacks.h index 2a6d0a54d8..2754847711 100644 --- a/src/tools/clangpchmanagerbackend/source/collectincludespreprocessorcallbacks.h +++ b/src/tools/clangpchmanagerbackend/source/collectincludespreprocessorcallbacks.h @@ -70,7 +70,11 @@ public: const clang::FileEntry *file, llvm::StringRef /*searchPath*/, llvm::StringRef /*relativePath*/, - const clang::Module * /*imported*/) override + const clang::Module * /*imported*/ +#if LLVM_VERSION_MAJOR >= 7 + , clang::SrcMgr::CharacteristicKind /*fileType*/ +#endif + ) override { if (!m_skipInclude && file) { auto fileUID = file->getUID(); diff --git a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h index 1e3c7b7b64..48bd0ddec9 100644 --- a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h +++ b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h @@ -196,7 +196,11 @@ public: const clang::FileEntry *file, llvm::StringRef /*searchPath*/, llvm::StringRef /*relativePath*/, - const clang::Module * /*imported*/) override + const clang::Module * /*imported*/ +#if LLVM_VERSION_MAJOR >= 7 + , clang::SrcMgr::CharacteristicKind /*fileType*/ +#endif + ) override { if (!m_skipInclude && file) addSourceDependency(file, hashLocation); diff --git a/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorage.h b/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorage.h new file mode 100644 index 0000000000..ed45465c68 --- /dev/null +++ b/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorage.h @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "usedmacroandsourcestorageinterface.h" + +#include <compilermacro.h> +#include <sqliteexception.h> +#include <sqlitetable.h> +#include <sqlitetransaction.h> + +#include <utils/smallstringview.h> + +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> + +namespace ClangBackEnd { + +template<typename Database=Sqlite::Database> +class UsedMacroAndSourceStorage final : public UsedMacroAndSourceStorageInterface +{ + using ReadStatement = typename Database::ReadStatement; + using WriteStatement = typename Database::WriteStatement; +public: + UsedMacroAndSourceStorage(Database &database) + : m_transaction(database), + m_database(database) + { + m_transaction.commit(); + } + + void insertFileStatuses(const FileStatuses &fileStatuses) override + { + WriteStatement &statement = m_insertFileStatuses; + + for (const FileStatus &fileStatus : fileStatuses) + statement.write(fileStatus.filePathId.filePathId, + fileStatus.size, + fileStatus.lastModified, + fileStatus.isInPrecompiledHeader); + } + + long long fetchLowestLastModifiedTime(FilePathId sourceId) const override + { + ReadStatement &statement = m_getLowestLastModifiedTimeOfDependencies; + + return statement.template value<long long>(sourceId.filePathId).value_or(0); + } + + void insertOrUpdateUsedMacros(const UsedMacros &usedMacros) override + { + WriteStatement &insertStatement = m_insertIntoNewUsedMacrosStatement; + for (const UsedMacro &usedMacro : usedMacros) + insertStatement.write(usedMacro.filePathId.filePathId, usedMacro.macroName); + + m_syncNewUsedMacrosStatement.execute(); + m_deleteOutdatedUsedMacrosStatement.execute(); + m_deleteNewUsedMacrosTableStatement.execute(); + } + + void insertOrUpdateSourceDependencies(const SourceDependencies &sourceDependencies) override + { + WriteStatement &insertStatement = m_insertIntoNewSourceDependenciesStatement; + for (SourceDependency sourceDependency : sourceDependencies) + insertStatement.write(sourceDependency.filePathId.filePathId, + sourceDependency.dependencyFilePathId.filePathId); + + m_syncNewSourceDependenciesStatement.execute(); + m_deleteOutdatedSourceDependenciesStatement.execute(); + m_deleteNewSourceDependenciesStatement.execute(); + } + + static Utils::SmallString toJson(const Utils::SmallStringVector &strings) + { + QJsonDocument document; + QJsonArray array; + + std::transform(strings.begin(), strings.end(), std::back_inserter(array), [] (const auto &string) { + return QJsonValue(string.data()); + }); + + document.setArray(array); + + return document.toJson(QJsonDocument::Compact); + } + + static Utils::SmallString toJson(const CompilerMacros &compilerMacros) + { + QJsonDocument document; + QJsonObject object; + + for (const CompilerMacro ¯o : compilerMacros) + object.insert(QString(macro.key), QString(macro.value)); + + document.setObject(object); + + return document.toJson(QJsonDocument::Compact); + } + + Sqlite::Table createNewUsedMacrosTable() const + { + Sqlite::Table table; + table.setName("newUsedMacros"); + table.setUseTemporaryTable(true); + const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); + const Sqlite::Column ¯oNameColumn = table.addColumn("macroName", Sqlite::ColumnType::Text); + table.addIndex({sourceIdColumn, macroNameColumn}); + + table.initialize(m_database); + + return table; + } + + Sqlite::Table createNewSourceDependenciesTable() const + { + Sqlite::Table table; + table.setName("newSourceDependencies"); + table.setUseTemporaryTable(true); + const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); + const Sqlite::Column &dependencySourceIdColumn = table.addColumn("dependencySourceId", Sqlite::ColumnType::Text); + table.addIndex({sourceIdColumn, dependencySourceIdColumn}); + + table.initialize(m_database); + + return table; + } + +public: + Sqlite::ImmediateNonThrowingDestructorTransaction m_transaction; + Database &m_database; + Sqlite::Table newUsedMacroTable{createNewUsedMacrosTable()}; + Sqlite::Table newNewSourceDependenciesTable{createNewSourceDependenciesTable()}; + WriteStatement m_insertIntoNewUsedMacrosStatement{ + "INSERT INTO newUsedMacros(sourceId, macroName) VALUES (?,?)", + m_database + }; + WriteStatement m_syncNewUsedMacrosStatement{ + "INSERT INTO usedMacros(sourceId, macroName) SELECT sourceId, macroName FROM newUsedMacros WHERE NOT EXISTS (SELECT sourceId FROM usedMacros WHERE usedMacros.sourceId == newUsedMacros.sourceId AND usedMacros.macroName == newUsedMacros.macroName)", + m_database + }; + WriteStatement m_deleteOutdatedUsedMacrosStatement{ + "DELETE FROM usedMacros WHERE sourceId IN (SELECT sourceId FROM newUsedMacros) AND NOT EXISTS (SELECT sourceId FROM newUsedMacros WHERE newUsedMacros.sourceId == usedMacros.sourceId AND newUsedMacros.macroName == usedMacros.macroName)", + m_database + }; + WriteStatement m_deleteNewUsedMacrosTableStatement{ + "DELETE FROM newUsedMacros", + m_database + }; + mutable ReadStatement m_getLowestLastModifiedTimeOfDependencies{ + "WITH RECURSIVE sourceIds(sourceId) AS (VALUES(?) UNION SELECT dependencySourceId FROM sourceDependencies, sourceIds WHERE sourceDependencies.sourceId = sourceIds.sourceId) SELECT min(lastModified) FROM fileStatuses, sourceIds WHERE fileStatuses.sourceId = sourceIds.sourceId", + m_database + }; + WriteStatement m_insertIntoNewSourceDependenciesStatement{ + "INSERT INTO newSourceDependencies(sourceId, dependencySourceId) VALUES (?,?)", + m_database + }; + WriteStatement m_insertFileStatuses{ + "INSERT OR REPLACE INTO fileStatuses(sourceId, size, lastModified, isInPrecompiledHeader) VALUES (?,?,?,?)", + m_database + }; + WriteStatement m_syncNewSourceDependenciesStatement{ + "INSERT INTO sourceDependencies(sourceId, dependencySourceId) SELECT sourceId, dependencySourceId FROM newSourceDependencies WHERE NOT EXISTS (SELECT sourceId FROM sourceDependencies WHERE sourceDependencies.sourceId == newSourceDependencies.sourceId AND sourceDependencies.dependencySourceId == newSourceDependencies.dependencySourceId)", + m_database + }; + WriteStatement m_deleteOutdatedSourceDependenciesStatement{ + "DELETE FROM sourceDependencies WHERE sourceId IN (SELECT sourceId FROM newSourceDependencies) AND NOT EXISTS (SELECT sourceId FROM newSourceDependencies WHERE newSourceDependencies.sourceId == sourceDependencies.sourceId AND newSourceDependencies.dependencySourceId == sourceDependencies.dependencySourceId)", + m_database + }; + WriteStatement m_deleteNewSourceDependenciesStatement{ + "DELETE FROM newSourceDependencies", + m_database + }; +}; +} diff --git a/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorageinterface.h b/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorageinterface.h new file mode 100644 index 0000000000..2cbb9f84b5 --- /dev/null +++ b/src/tools/clangpchmanagerbackend/source/usedmacroandsourcestorageinterface.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "filestatus.h" +#include "sourcedependency.h" +#include "usedmacro.h" + +namespace ClangBackEnd { + +class UsedMacroAndSourceStorageInterface +{ +public: + UsedMacroAndSourceStorageInterface() = default; + UsedMacroAndSourceStorageInterface(const UsedMacroAndSourceStorageInterface &) = delete; + UsedMacroAndSourceStorageInterface &operator=(const UsedMacroAndSourceStorageInterface &) = delete; + + virtual void insertOrUpdateUsedMacros(const UsedMacros &usedMacros) = 0; + virtual void insertFileStatuses(const FileStatuses &fileStatuses) = 0; + virtual void insertOrUpdateSourceDependencies(const SourceDependencies &sourceDependencies) = 0; + virtual long long fetchLowestLastModifiedTime(FilePathId sourceId) const = 0; + + +protected: + ~UsedMacroAndSourceStorageInterface() = default; +}; + +} // namespace ClangBackEnd + diff --git a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri index 26fa44aa68..fb1671aaf7 100644 --- a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri +++ b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri @@ -9,7 +9,6 @@ HEADERS += \ $$PWD/symbolscollectorinterface.h \ $$PWD/symbolstorageinterface.h \ $$PWD/symbolstorage.h \ - $$PWD/storagesqlitestatementfactory.h \ $$PWD/symbolindexing.h \ $$PWD/symbolindexinginterface.h \ $$PWD/collectmacrospreprocessorcallbacks.h \ diff --git a/src/tools/clangrefactoringbackend/source/collectmacrospreprocessorcallbacks.h b/src/tools/clangrefactoringbackend/source/collectmacrospreprocessorcallbacks.h index 9581e9790a..689e291098 100644 --- a/src/tools/clangrefactoringbackend/source/collectmacrospreprocessorcallbacks.h +++ b/src/tools/clangrefactoringbackend/source/collectmacrospreprocessorcallbacks.h @@ -95,7 +95,11 @@ public: const clang::FileEntry *file, llvm::StringRef /*searchPath*/, llvm::StringRef /*relativePath*/, - const clang::Module * /*imported*/) override + const clang::Module * /*imported*/ +#if LLVM_VERSION_MAJOR >= 7 + , clang::SrcMgr::CharacteristicKind /*fileType*/ +#endif + ) override { if (!m_skipInclude && file) addSourceDependency(file, hashLocation); diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp index 01f5190c08..2c190c6a10 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp @@ -127,8 +127,12 @@ bool IndexDataConsumer::skipSymbol(clang::FileID fileId, clang::index::SymbolRol bool IndexDataConsumer::handleDeclOccurence(const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef<clang::index::SymbolRelation> symbolRelations, +#if LLVM_VERSION_MAJOR >= 7 + clang::SourceLocation sourceLocation, +#else clang::FileID fileId, unsigned offset, +#endif IndexDataConsumer::ASTNodeInfo astNodeInfo) { const auto *namedDeclaration = clang::dyn_cast<clang::NamedDecl>(declaration); @@ -136,11 +140,17 @@ bool IndexDataConsumer::handleDeclOccurence(const clang::Decl *declaration, if (!namedDeclaration->getIdentifier()) return true; +#if LLVM_VERSION_MAJOR >= 7 + if (skipSymbol(m_sourceManager->getFileID(sourceLocation), symbolRoles)) +#else if (skipSymbol(fileId, symbolRoles)) +#endif return true; SymbolIndex globalId = toSymbolIndex(declaration->getCanonicalDecl()); +#if LLVM_VERSION_MAJOR < 7 clang::SourceLocation sourceLocation = m_sourceManager->getLocForStartOfFile(fileId).getLocWithOffset(offset); +#endif auto found = m_symbolEntries.find(globalId); if (found == m_symbolEntries.end()) { diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h index 1024d0f500..8524455574 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h @@ -55,8 +55,12 @@ public: bool handleDeclOccurence(const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef<clang::index::SymbolRelation> symbolRelations, +#if LLVM_VERSION_MAJOR >= 7 + clang::SourceLocation sourceLocation, +#else clang::FileID fileId, unsigned offset, +#endif ASTNodeInfo astNodeInfo) override; private: diff --git a/src/tools/clangrefactoringbackend/source/storagesqlitestatementfactory.h b/src/tools/clangrefactoringbackend/source/storagesqlitestatementfactory.h deleted file mode 100644 index 7652795acb..0000000000 --- a/src/tools/clangrefactoringbackend/source/storagesqlitestatementfactory.h +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 <sqlitetransaction.h> -#include <sqlitetable.h> - -namespace ClangBackEnd { - -template<typename DatabaseType> -class StorageSqliteStatementFactory -{ -public: - using Database = DatabaseType; - using ReadStatement = typename Database::ReadStatement; - using WriteStatement = typename Database::WriteStatement; - - StorageSqliteStatementFactory(Database &database) - : transaction(database), - database(database) - { - transaction.commit(); - } - - Sqlite::Table createNewSymbolsTable() const - { - Sqlite::Table table; - table.setName("newSymbols"); - table.setUseTemporaryTable(true); - table.addColumn("temporarySymbolId", Sqlite::ColumnType::Integer, Sqlite::Contraint::PrimaryKey); - const Sqlite::Column &symbolIdColumn = table.addColumn("symbolId", Sqlite::ColumnType::Integer); - const Sqlite::Column &usrColumn = table.addColumn("usr", Sqlite::ColumnType::Text); - const Sqlite::Column &symbolNameColumn = table.addColumn("symbolName", Sqlite::ColumnType::Text); - table.addColumn("symbolKind", Sqlite::ColumnType::Integer); - table.addIndex({usrColumn, symbolNameColumn}); - table.addIndex({symbolIdColumn}); - - table.initialize(database); - - return table; - } - - Sqlite::Table createNewLocationsTable() const - { - Sqlite::Table table; - table.setName("newLocations"); - table.setUseTemporaryTable(true); - table.addColumn("temporarySymbolId", Sqlite::ColumnType::Integer); - table.addColumn("symbolId", Sqlite::ColumnType::Integer); - const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); - const Sqlite::Column &lineColumn = table.addColumn("line", Sqlite::ColumnType::Integer); - const Sqlite::Column &columnColumn = table.addColumn("column", Sqlite::ColumnType::Integer); - table.addColumn("locationKind", Sqlite::ColumnType::Integer); - table.addUniqueIndex({sourceIdColumn, lineColumn, columnColumn}); - - table.initialize(database); - - return table; - } - - Sqlite::Table createNewUsedMacrosTable() const - { - Sqlite::Table table; - table.setName("newUsedMacros"); - table.setUseTemporaryTable(true); - const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); - const Sqlite::Column ¯oNameColumn = table.addColumn("macroName", Sqlite::ColumnType::Text); - table.addIndex({sourceIdColumn, macroNameColumn}); - - table.initialize(database); - - return table; - } - - Sqlite::Table createNewSourceDependenciesTable() const - { - Sqlite::Table table; - table.setName("newSourceDependencies"); - table.setUseTemporaryTable(true); - const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); - const Sqlite::Column &dependencySourceIdColumn = table.addColumn("dependencySourceId", Sqlite::ColumnType::Text); - table.addIndex({sourceIdColumn, dependencySourceIdColumn}); - - table.initialize(database); - - return table; - } - -public: - Sqlite::ImmediateNonThrowingDestructorTransaction transaction; - Database &database; - Sqlite::Table newSymbolsTablet{createNewSymbolsTable()}; - Sqlite::Table newLocationsTable{createNewLocationsTable()}; - Sqlite::Table newUsedMacroTable{createNewUsedMacrosTable()}; - Sqlite::Table newNewSourceDependenciesTable{createNewSourceDependenciesTable()}; - WriteStatement insertSymbolsToNewSymbolsStatement{ - "INSERT INTO newSymbols(temporarySymbolId, usr, symbolName, symbolKind) VALUES(?,?,?,?)", - database}; - WriteStatement insertLocationsToNewLocationsStatement{ - "INSERT OR IGNORE INTO newLocations(temporarySymbolId, line, column, sourceId, locationKind) VALUES(?,?,?,?,?)", - database - }; - ReadStatement selectNewSourceIdsStatement{ - "SELECT DISTINCT sourceId FROM newLocations WHERE NOT EXISTS (SELECT sourceId FROM sources WHERE newLocations.sourceId == sources.sourceId)", - database - }; - WriteStatement addNewSymbolsToSymbolsStatement{ - "INSERT INTO symbols(usr, symbolName, symbolKind) " - "SELECT usr, symbolName, symbolKind FROM newSymbols WHERE NOT EXISTS " - "(SELECT usr FROM symbols WHERE symbols.usr == newSymbols.usr)", - database - }; - WriteStatement syncNewSymbolsFromSymbolsStatement{ - "UPDATE newSymbols SET symbolId = (SELECT symbolId FROM symbols WHERE newSymbols.usr = symbols.usr)", - database - }; - WriteStatement syncSymbolsIntoNewLocationsStatement{ - "UPDATE newLocations SET symbolId = (SELECT symbolId FROM newSymbols WHERE newSymbols.temporarySymbolId = newLocations.temporarySymbolId)", - database - }; - WriteStatement deleteAllLocationsFromUpdatedFilesStatement{ - "DELETE FROM locations WHERE sourceId IN (SELECT DISTINCT sourceId FROM newLocations)", - database - }; - WriteStatement insertNewLocationsInLocationsStatement{ - "INSERT INTO locations(symbolId, line, column, sourceId, locationKind) SELECT symbolId, line, column, sourceId, locationKind FROM newLocations", - database - }; - WriteStatement deleteNewSymbolsTableStatement{ - "DELETE FROM newSymbols", - database - }; - WriteStatement deleteNewLocationsTableStatement{ - "DELETE FROM newLocations", - database - }; - WriteStatement insertProjectPartStatement{ - "INSERT OR IGNORE INTO projectParts(projectPartName, compilerArguments, compilerMacros, includeSearchPaths) VALUES (?,?,?,?)", - database - }; - WriteStatement updateProjectPartStatement{ - "UPDATE projectParts SET compilerArguments = ?, compilerMacros = ?, includeSearchPaths = ? WHERE projectPartName = ?", - database - }; - ReadStatement getProjectPartIdStatement{ - "SELECT projectPartId FROM projectParts WHERE projectPartName = ?", - database - }; - WriteStatement deleteAllProjectPartsSourcesWithProjectPartIdStatement{ - "DELETE FROM projectPartsSources WHERE projectPartId = ?", - database - }; - WriteStatement insertProjectPartSourcesStatement{ - "INSERT INTO projectPartsSources(projectPartId, sourceId) VALUES (?,?)", - database - }; - ReadStatement getCompileArgumentsForFileIdStatement{ - "SELECT compilerArguments FROM projectParts WHERE projectPartId = (SELECT projectPartId FROM projectPartsSources WHERE sourceId = ?)", - database - }; - WriteStatement insertIntoNewUsedMacrosStatement{ - "INSERT INTO newUsedMacros(sourceId, macroName) VALUES (?,?)", - database - }; - WriteStatement syncNewUsedMacrosStatement{ - "INSERT INTO usedMacros(sourceId, macroName) SELECT sourceId, macroName FROM newUsedMacros WHERE NOT EXISTS (SELECT sourceId FROM usedMacros WHERE usedMacros.sourceId == newUsedMacros.sourceId AND usedMacros.macroName == newUsedMacros.macroName)", - database - }; - WriteStatement deleteOutdatedUsedMacrosStatement{ - "DELETE FROM usedMacros WHERE sourceId IN (SELECT sourceId FROM newUsedMacros) AND NOT EXISTS (SELECT sourceId FROM newUsedMacros WHERE newUsedMacros.sourceId == usedMacros.sourceId AND newUsedMacros.macroName == usedMacros.macroName)", - database - }; - WriteStatement deleteNewUsedMacrosTableStatement{ - "DELETE FROM newUsedMacros", - database - }; - WriteStatement insertFileStatuses{ - "INSERT OR REPLACE INTO fileStatuses(sourceId, size, lastModified, isInPrecompiledHeader) VALUES (?,?,?,?)", - database - }; - WriteStatement insertIntoNewSourceDependenciesStatement{ - "INSERT INTO newSourceDependencies(sourceId, dependencySourceId) VALUES (?,?)", - database - }; - WriteStatement syncNewSourceDependenciesStatement{ - "INSERT INTO sourceDependencies(sourceId, dependencySourceId) SELECT sourceId, dependencySourceId FROM newSourceDependencies WHERE NOT EXISTS (SELECT sourceId FROM sourceDependencies WHERE sourceDependencies.sourceId == newSourceDependencies.sourceId AND sourceDependencies.dependencySourceId == newSourceDependencies.dependencySourceId)", - database - }; - WriteStatement deleteOutdatedSourceDependenciesStatement{ - "DELETE FROM sourceDependencies WHERE sourceId IN (SELECT sourceId FROM newSourceDependencies) AND NOT EXISTS (SELECT sourceId FROM newSourceDependencies WHERE newSourceDependencies.sourceId == sourceDependencies.sourceId AND newSourceDependencies.dependencySourceId == sourceDependencies.dependencySourceId)", - database - }; - WriteStatement deleteNewSourceDependenciesStatement{ - "DELETE FROM newSourceDependencies", - database - }; - ReadStatement getProjectPartArtefactsBySourceId{ - "SELECT compilerArguments, compilerMacros, includeSearchPaths, projectPartId FROM projectParts WHERE projectPartId = (SELECT projectPartId FROM projectPartsSources WHERE sourceId = ?)", - database - }; - ReadStatement getProjectPartArtefactsByProjectPartName{ - "SELECT compilerArguments, compilerMacros, includeSearchPaths, projectPartId FROM projectParts WHERE projectPartName = ?", - database - }; - ReadStatement getLowestLastModifiedTimeOfDependencies{ - "WITH RECURSIVE sourceIds(sourceId) AS (VALUES(?) UNION SELECT dependencySourceId FROM sourceDependencies, sourceIds WHERE sourceDependencies.sourceId = sourceIds.sourceId) SELECT min(lastModified) FROM fileStatuses, sourceIds WHERE fileStatuses.sourceId = sourceIds.sourceId", - database - }; - ReadStatement getPrecompiledHeader{ - "SELECT pchPath, pchBuildTime FROM precompiledHeaders WHERE projectPartId = ?", - database - }; -}; -} // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp index 272e89fa89..02a2fd14a0 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp @@ -58,12 +58,14 @@ private: SymbolIndexer::SymbolIndexer(SymbolIndexerTaskQueueInterface &symbolIndexerTaskQueue, SymbolStorageInterface &symbolStorage, + UsedMacroAndSourceStorageInterface &usedMacroAndSourceStorage, ClangPathWatcherInterface &pathWatcher, FilePathCachingInterface &filePathCache, FileStatusCache &fileStatusCache, Sqlite::TransactionInterface &transactionInterface) : m_symbolIndexerTaskQueue(symbolIndexerTaskQueue), m_symbolStorage(symbolStorage), + m_usedMacroAndSourceStorage(usedMacroAndSourceStorage), m_pathWatcher(pathWatcher), m_filePathCache(filePathCache), m_fileStatusCache(fileStatusCache), @@ -115,11 +117,11 @@ void SymbolIndexer::updateProjectPart(V2::ProjectPartContainer &&projectPart) m_symbolStorage.updateProjectPartSources(projectPartId, symbolsCollector.sourceFiles()); - m_symbolStorage.insertOrUpdateUsedMacros(symbolsCollector.usedMacros()); + m_usedMacroAndSourceStorage.insertOrUpdateUsedMacros(symbolsCollector.usedMacros()); - m_symbolStorage.insertFileStatuses(symbolsCollector.fileStatuses()); + m_usedMacroAndSourceStorage.insertFileStatuses(symbolsCollector.fileStatuses()); - m_symbolStorage.insertOrUpdateSourceDependencies(symbolsCollector.sourceDependencies()); + m_usedMacroAndSourceStorage.insertOrUpdateSourceDependencies(symbolsCollector.sourceDependencies()); transaction.commit(); }; @@ -180,11 +182,11 @@ void SymbolIndexer::updateChangedPath(FilePathId filePathId, m_symbolStorage.updateProjectPartSources(projectPartId, symbolsCollector.sourceFiles()); - m_symbolStorage.insertOrUpdateUsedMacros(symbolsCollector.usedMacros()); + m_usedMacroAndSourceStorage.insertOrUpdateUsedMacros(symbolsCollector.usedMacros()); - m_symbolStorage.insertFileStatuses(symbolsCollector.fileStatuses()); + m_usedMacroAndSourceStorage.insertFileStatuses(symbolsCollector.fileStatuses()); - m_symbolStorage.insertOrUpdateSourceDependencies(symbolsCollector.sourceDependencies()); + m_usedMacroAndSourceStorage.insertOrUpdateSourceDependencies(symbolsCollector.sourceDependencies()); transaction.commit(); }; @@ -212,7 +214,7 @@ FilePathIds SymbolIndexer::filterChangedFiles(const V2::ProjectPartContainer &pr ids.reserve(projectPart.sourcePathIds.size()); for (const FilePathId &sourceId : projectPart.sourcePathIds) { - long long oldLastModified = m_symbolStorage.fetchLowestLastModifiedTime(sourceId); + long long oldLastModified = m_usedMacroAndSourceStorage.fetchLowestLastModifiedTime(sourceId); long long currentLastModified = m_fileStatusCache.lastModifiedTime(sourceId); if (oldLastModified < currentLastModified) ids.push_back(sourceId); diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.h b/src/tools/clangrefactoringbackend/source/symbolindexer.h index 6d1a1de50b..6e96e2fee5 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.h @@ -28,6 +28,7 @@ #include "filestatuscache.h" #include "symbolindexertaskqueueinterface.h" #include "symbolstorageinterface.h" +#include "usedmacroandsourcestorageinterface.h" #include "clangpathwatcher.h" #include <projectpartcontainerv2.h> @@ -42,6 +43,7 @@ class SymbolIndexer final : public ClangPathWatcherNotifier public: SymbolIndexer(SymbolIndexerTaskQueueInterface &symbolIndexerTaskQueue, SymbolStorageInterface &symbolStorage, + UsedMacroAndSourceStorageInterface &usedMacroAndSourceStorage, ClangPathWatcherInterface &pathWatcher, FilePathCachingInterface &filePathCache, FileStatusCache &fileStatusCache, @@ -71,6 +73,7 @@ public: private: SymbolIndexerTaskQueueInterface &m_symbolIndexerTaskQueue; SymbolStorageInterface &m_symbolStorage; + UsedMacroAndSourceStorageInterface &m_usedMacroAndSourceStorage; ClangPathWatcherInterface &m_pathWatcher; FilePathCachingInterface &m_filePathCache; FileStatusCache &m_fileStatusCache; diff --git a/src/tools/clangrefactoringbackend/source/symbolindexing.h b/src/tools/clangrefactoringbackend/source/symbolindexing.h index 6189fe23ae..488e73be9e 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexing.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexing.h @@ -27,7 +27,6 @@ #include "symbolindexinginterface.h" -#include "storagesqlitestatementfactory.h" #include "symbolindexer.h" #include "symbolscollector.h" #include "processormanager.h" @@ -35,6 +34,8 @@ #include "taskscheduler.h" #include "symbolstorage.h" +#include <usedmacroandsourcestorage.h> + #include <refactoringdatabaseinitializer.h> #include <filepathcachingfwd.h> @@ -74,14 +75,15 @@ private: class SymbolIndexing final : public SymbolIndexingInterface { public: - using StatementFactory = ClangBackEnd::StorageSqliteStatementFactory<Sqlite::Database>; - using Storage = ClangBackEnd::SymbolStorage<StatementFactory>; + using UsedMacroAndSourceStorage = ClangBackEnd::UsedMacroAndSourceStorage<Sqlite::Database>; + using SymbolStorage = ClangBackEnd::SymbolStorage<Sqlite::Database>; SymbolIndexing(Sqlite::Database &database, FilePathCachingInterface &filePathCache, const GeneratedFiles &generatedFiles, ProgressCounter::SetProgressCallback &&setProgressCallback) : m_filePathCache(filePathCache), - m_statementFactory(database), + m_usedMacroAndSourceStorage(database), + m_symbolStorage(database), m_collectorManger(generatedFiles, database), m_progressCounter(std::move(setProgressCallback)), m_indexerScheduler(m_collectorManger, m_indexerQueue, m_progressCounter, std::thread::hardware_concurrency()) @@ -112,8 +114,8 @@ public: private: using SymbolIndexerTaskScheduler = TaskScheduler<SymbolsCollectorManager, SymbolIndexerTask::Callable>; FilePathCachingInterface &m_filePathCache; - StatementFactory m_statementFactory; - Storage m_symbolStorage{m_statementFactory}; + UsedMacroAndSourceStorage m_usedMacroAndSourceStorage; + SymbolStorage m_symbolStorage; ClangPathWatcher<QFileSystemWatcher, QTimer> m_sourceWatcher{m_filePathCache}; FileStatusCache m_fileStatusCache{m_filePathCache}; SymbolsCollectorManager m_collectorManger; @@ -122,10 +124,11 @@ private: SymbolIndexerTaskQueue m_indexerQueue{m_indexerScheduler, m_progressCounter}; SymbolIndexer m_indexer{m_indexerQueue, m_symbolStorage, + m_usedMacroAndSourceStorage, m_sourceWatcher, m_filePathCache, m_fileStatusCache, - m_statementFactory.database}; + m_symbolStorage.m_database}; }; } // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolstorage.h b/src/tools/clangrefactoringbackend/source/symbolstorage.h index 4de47dab1d..4aae17c599 100644 --- a/src/tools/clangrefactoringbackend/source/symbolstorage.h +++ b/src/tools/clangrefactoringbackend/source/symbolstorage.h @@ -27,9 +27,10 @@ #include "symbolstorageinterface.h" +#include <filepathcachingfwd.h> #include <sqliteexception.h> #include <sqlitetransaction.h> -#include <filepathcachingfwd.h> +#include <sqlitetable.h> #include <QJsonArray> #include <QJsonDocument> @@ -37,17 +38,19 @@ namespace ClangBackEnd { -template <typename StatementFactory> +template <typename DatabaseType> class SymbolStorage final : public SymbolStorageInterface { - using ReadStatement = typename StatementFactory::ReadStatement; - using WriteStatement = typename StatementFactory::WriteStatement; - using Database = typename StatementFactory::Database; + using Database = DatabaseType; + using ReadStatement = typename Database::ReadStatement; + using WriteStatement = typename Database::WriteStatement; public: - SymbolStorage(StatementFactory &statementFactory) - : m_statementFactory(statementFactory) + SymbolStorage(Database &database) + : m_transaction(database), + m_database(database) { + m_transaction.commit(); } void addSymbolsAndSourceLocations(const SymbolEntries &symbolEntries, @@ -69,95 +72,54 @@ public: const CompilerMacros &compilerMacros, const Utils::SmallStringVector &includeSearchPaths) override { - m_statementFactory.database.setLastInsertedRowId(-1); + m_database.setLastInsertedRowId(-1); Utils::SmallString compilerArguementsAsJson = toJson(commandLineArguments); Utils::SmallString compilerMacrosAsJson = toJson(compilerMacros); Utils::SmallString includeSearchPathsAsJason = toJson(includeSearchPaths); - WriteStatement &insertStatement = m_statementFactory.insertProjectPartStatement; + WriteStatement &insertStatement = m_insertProjectPartStatement; insertStatement.write(projectPartName, compilerArguementsAsJson, compilerMacrosAsJson, includeSearchPathsAsJason); - if (m_statementFactory.database.lastInsertedRowId() == -1) { - WriteStatement &updateStatement = m_statementFactory.updateProjectPartStatement; + if (m_database.lastInsertedRowId() == -1) { + WriteStatement &updateStatement = m_updateProjectPartStatement; updateStatement.write(compilerArguementsAsJson, compilerMacrosAsJson, includeSearchPathsAsJason, projectPartName); } - return int(m_statementFactory.database.lastInsertedRowId()); + return int(m_database.lastInsertedRowId()); } Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact(FilePathId sourceId) const override { - ReadStatement &statement = m_statementFactory.getProjectPartArtefactsBySourceId; + ReadStatement &statement = m_getProjectPartArtefactsBySourceId; return statement.template value<ProjectPartArtefact, 4>(sourceId.filePathId); } Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact(Utils::SmallStringView projectPartName) const override { - ReadStatement &statement = m_statementFactory.getProjectPartArtefactsByProjectPartName; + ReadStatement &statement = m_getProjectPartArtefactsByProjectPartName; return statement.template value<ProjectPartArtefact, 4>(projectPartName); } - long long fetchLowestLastModifiedTime(FilePathId sourceId) const override - { - ReadStatement &statement = m_statementFactory.getLowestLastModifiedTimeOfDependencies; - - return statement.template value<long long>(sourceId.filePathId).value_or(0); - } - - void insertOrUpdateUsedMacros(const UsedMacros &usedMacros) override - { - WriteStatement &insertStatement = m_statementFactory.insertIntoNewUsedMacrosStatement; - for (const UsedMacro &usedMacro : usedMacros) - insertStatement.write(usedMacro.filePathId.filePathId, usedMacro.macroName); - - m_statementFactory.syncNewUsedMacrosStatement.execute(); - m_statementFactory.deleteOutdatedUsedMacrosStatement.execute(); - m_statementFactory.deleteNewUsedMacrosTableStatement.execute(); - } - - void insertOrUpdateSourceDependencies(const SourceDependencies &sourceDependencies) override - { - WriteStatement &insertStatement = m_statementFactory.insertIntoNewSourceDependenciesStatement; - for (SourceDependency sourceDependency : sourceDependencies) - insertStatement.write(sourceDependency.filePathId.filePathId, - sourceDependency.dependencyFilePathId.filePathId); - - m_statementFactory.syncNewSourceDependenciesStatement.execute(); - m_statementFactory.deleteOutdatedSourceDependenciesStatement.execute(); - m_statementFactory.deleteNewSourceDependenciesStatement.execute(); - } - void updateProjectPartSources(int projectPartId, const FilePathIds &sourceFilePathIds) override { - WriteStatement &deleteStatement = m_statementFactory.deleteAllProjectPartsSourcesWithProjectPartIdStatement; + WriteStatement &deleteStatement = m_deleteAllProjectPartsSourcesWithProjectPartIdStatement; deleteStatement.write(projectPartId); - WriteStatement &insertStatement = m_statementFactory.insertProjectPartSourcesStatement; + WriteStatement &insertStatement = m_insertProjectPartSourcesStatement; for (const FilePathId &sourceFilePathId : sourceFilePathIds) insertStatement.write(projectPartId, sourceFilePathId.filePathId); } - void insertFileStatuses(const FileStatuses &fileStatuses) - { - WriteStatement &statement = m_statementFactory.insertFileStatuses; - - for (const FileStatus &fileStatus : fileStatuses) - statement.write(fileStatus.filePathId.filePathId, - fileStatus.size, - fileStatus.lastModified, - fileStatus.isInPrecompiledHeader); - } - static Utils::SmallString toJson(const Utils::SmallStringVector &strings) { QJsonDocument document; @@ -187,7 +149,7 @@ public: void fillTemporarySymbolsTable(const SymbolEntries &symbolEntries) { - WriteStatement &statement = m_statementFactory.insertSymbolsToNewSymbolsStatement; + WriteStatement &statement = m_insertSymbolsToNewSymbolsStatement; for (const auto &symbolEntry : symbolEntries) { statement.write(symbolEntry.first, @@ -199,7 +161,7 @@ public: void fillTemporaryLocationsTable(const SourceLocationEntries &sourceLocations) { - WriteStatement &statement = m_statementFactory.insertLocationsToNewLocationsStatement; + WriteStatement &statement = m_insertLocationsToNewLocationsStatement; for (const auto &locationEntry : sourceLocations) { statement.write(locationEntry.symbolId, @@ -212,42 +174,42 @@ public: void addNewSymbolsToSymbols() { - m_statementFactory.addNewSymbolsToSymbolsStatement.execute(); + m_addNewSymbolsToSymbolsStatement.execute(); } void syncNewSymbolsFromSymbols() { - m_statementFactory.syncNewSymbolsFromSymbolsStatement.execute(); + m_syncNewSymbolsFromSymbolsStatement.execute(); } void syncSymbolsIntoNewLocations() { - m_statementFactory.syncSymbolsIntoNewLocationsStatement.execute(); + m_syncSymbolsIntoNewLocationsStatement.execute(); } void deleteAllLocationsFromUpdatedFiles() { - m_statementFactory.deleteAllLocationsFromUpdatedFilesStatement.execute(); + m_deleteAllLocationsFromUpdatedFilesStatement.execute(); } void insertNewLocationsInLocations() { - m_statementFactory.insertNewLocationsInLocationsStatement.execute(); + m_insertNewLocationsInLocationsStatement.execute(); } void deleteNewSymbolsTable() { - m_statementFactory.deleteNewSymbolsTableStatement.execute(); + m_deleteNewSymbolsTableStatement.execute(); } void deleteNewLocationsTable() { - m_statementFactory.deleteNewLocationsTableStatement.execute(); + m_deleteNewLocationsTableStatement.execute(); } Utils::optional<ProjectPartPch> fetchPrecompiledHeader(int projectPartId) const { - return m_statementFactory.getPrecompiledHeader.template value<ProjectPartPch, 2>(projectPartId); + return m_getPrecompiledHeader.template value<ProjectPartPch, 2>(projectPartId); } SourceLocationEntries sourceLocations() const @@ -255,8 +217,124 @@ public: return SourceLocationEntries(); } -private: - StatementFactory &m_statementFactory; + Sqlite::Table createNewSymbolsTable() const + { + Sqlite::Table table; + table.setName("newSymbols"); + table.setUseTemporaryTable(true); + table.addColumn("temporarySymbolId", Sqlite::ColumnType::Integer, Sqlite::Contraint::PrimaryKey); + const Sqlite::Column &symbolIdColumn = table.addColumn("symbolId", Sqlite::ColumnType::Integer); + const Sqlite::Column &usrColumn = table.addColumn("usr", Sqlite::ColumnType::Text); + const Sqlite::Column &symbolNameColumn = table.addColumn("symbolName", Sqlite::ColumnType::Text); + table.addColumn("symbolKind", Sqlite::ColumnType::Integer); + table.addIndex({usrColumn, symbolNameColumn}); + table.addIndex({symbolIdColumn}); + + table.initialize(m_database); + + return table; + } + + Sqlite::Table createNewLocationsTable() const + { + Sqlite::Table table; + table.setName("newLocations"); + table.setUseTemporaryTable(true); + table.addColumn("temporarySymbolId", Sqlite::ColumnType::Integer); + table.addColumn("symbolId", Sqlite::ColumnType::Integer); + const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); + const Sqlite::Column &lineColumn = table.addColumn("line", Sqlite::ColumnType::Integer); + const Sqlite::Column &columnColumn = table.addColumn("column", Sqlite::ColumnType::Integer); + table.addColumn("locationKind", Sqlite::ColumnType::Integer); + table.addUniqueIndex({sourceIdColumn, lineColumn, columnColumn}); + + table.initialize(m_database); + + return table; + } + +public: + Sqlite::ImmediateNonThrowingDestructorTransaction m_transaction; + Database &m_database; + Sqlite::Table newSymbolsTablet{createNewSymbolsTable()}; + Sqlite::Table newLocationsTable{createNewLocationsTable()}; + WriteStatement m_insertSymbolsToNewSymbolsStatement{ + "INSERT INTO newSymbols(temporarySymbolId, usr, symbolName, symbolKind) VALUES(?,?,?,?)", + m_database}; + WriteStatement m_insertLocationsToNewLocationsStatement{ + "INSERT OR IGNORE INTO newLocations(temporarySymbolId, line, column, sourceId, locationKind) VALUES(?,?,?,?,?)", + m_database + }; + ReadStatement m_selectNewSourceIdsStatement{ + "SELECT DISTINCT sourceId FROM newLocations WHERE NOT EXISTS (SELECT sourceId FROM sources WHERE newLocations.sourceId == sources.sourceId)", + m_database + }; + WriteStatement m_addNewSymbolsToSymbolsStatement{ + "INSERT INTO symbols(usr, symbolName, symbolKind) " + "SELECT usr, symbolName, symbolKind FROM newSymbols WHERE NOT EXISTS " + "(SELECT usr FROM symbols WHERE symbols.usr == newSymbols.usr)", + m_database + }; + WriteStatement m_syncNewSymbolsFromSymbolsStatement{ + "UPDATE newSymbols SET symbolId = (SELECT symbolId FROM symbols WHERE newSymbols.usr = symbols.usr)", + m_database + }; + WriteStatement m_syncSymbolsIntoNewLocationsStatement{ + "UPDATE newLocations SET symbolId = (SELECT symbolId FROM newSymbols WHERE newSymbols.temporarySymbolId = newLocations.temporarySymbolId)", + m_database + }; + WriteStatement m_deleteAllLocationsFromUpdatedFilesStatement{ + "DELETE FROM locations WHERE sourceId IN (SELECT DISTINCT sourceId FROM newLocations)", + m_database + }; + WriteStatement m_insertNewLocationsInLocationsStatement{ + "INSERT INTO locations(symbolId, line, column, sourceId, locationKind) SELECT symbolId, line, column, sourceId, locationKind FROM newLocations", + m_database + }; + WriteStatement m_deleteNewSymbolsTableStatement{ + "DELETE FROM newSymbols", + m_database + }; + WriteStatement m_deleteNewLocationsTableStatement{ + "DELETE FROM newLocations", + m_database + }; + WriteStatement m_insertProjectPartStatement{ + "INSERT OR IGNORE INTO projectParts(projectPartName, compilerArguments, compilerMacros, includeSearchPaths) VALUES (?,?,?,?)", + m_database + }; + WriteStatement m_updateProjectPartStatement{ + "UPDATE projectParts SET compilerArguments = ?, compilerMacros = ?, includeSearchPaths = ? WHERE projectPartName = ?", + m_database + }; + mutable ReadStatement m_getProjectPartIdStatement{ + "SELECT projectPartId FROM projectParts WHERE projectPartName = ?", + m_database + }; + WriteStatement m_deleteAllProjectPartsSourcesWithProjectPartIdStatement{ + "DELETE FROM projectPartsSources WHERE projectPartId = ?", + m_database + }; + WriteStatement m_insertProjectPartSourcesStatement{ + "INSERT INTO projectPartsSources(projectPartId, sourceId) VALUES (?,?)", + m_database + }; + mutable ReadStatement m_getCompileArgumentsForFileIdStatement{ + "SELECT compilerArguments FROM projectParts WHERE projectPartId = (SELECT projectPartId FROM projectPartsSources WHERE sourceId = ?)", + m_database + }; + mutable ReadStatement m_getProjectPartArtefactsBySourceId{ + "SELECT compilerArguments, compilerMacros, includeSearchPaths, projectPartId FROM projectParts WHERE projectPartId = (SELECT projectPartId FROM projectPartsSources WHERE sourceId = ?)", + m_database + }; + mutable ReadStatement m_getProjectPartArtefactsByProjectPartName{ + "SELECT compilerArguments, compilerMacros, includeSearchPaths, projectPartId FROM projectParts WHERE projectPartName = ?", + m_database + }; + mutable ReadStatement m_getPrecompiledHeader{ + "SELECT pchPath, pchBuildTime FROM precompiledHeaders WHERE projectPartId = ?", + m_database + }; }; } // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h b/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h index 0ade62cc44..0cb1dafd4f 100644 --- a/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h +++ b/src/tools/clangrefactoringbackend/source/symbolstorageinterface.h @@ -55,12 +55,8 @@ public: const Utils::SmallStringVector &includeSearchPaths) = 0; virtual void updateProjectPartSources(int projectPartId, const FilePathIds &sourceFilePathIds) = 0; - virtual void insertOrUpdateUsedMacros(const UsedMacros &usedMacros) = 0; - virtual void insertFileStatuses(const FileStatuses &fileStatuses) = 0; - virtual void insertOrUpdateSourceDependencies(const SourceDependencies &sourceDependencies) = 0; virtual Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact(FilePathId sourceId) const = 0; virtual Utils::optional<ProjectPartArtefact> fetchProjectPartArtefact(Utils::SmallStringView projectPartName) const = 0; - virtual long long fetchLowestLastModifiedTime(FilePathId sourceId) const = 0; virtual Utils::optional<ProjectPartPch> fetchPrecompiledHeader(int projectPartId) const = 0; protected: |