diff options
author | Orgad Shaneh <orgad.shaneh@audiocodes.com> | 2019-02-15 13:42:44 +0200 |
---|---|---|
committer | Orgad Shaneh <orgad.shaneh@audiocodes.com> | 2019-02-15 13:42:44 +0200 |
commit | 6fc532f812f62b7206ec5e958d251f7389cd6ecf (patch) | |
tree | 285bab3ac533c84a77dc173b195e8af4d8435869 /src/plugins | |
parent | 72d962e949564fc963f91e1b48350a091aea1bb2 (diff) | |
parent | 603191ba77852b64e5438c339bc39fcdc608cfb8 (diff) | |
download | qt-creator-6fc532f812f62b7206ec5e958d251f7389cd6ecf.tar.gz |
Merge remote-tracking branch 'origin/4.9'
Change-Id: If4e8f52fc94c4e5fd9ec69c9000436d4ded913ff
Diffstat (limited to 'src/plugins')
51 files changed, 737 insertions, 377 deletions
diff --git a/src/plugins/clangcodemodel/clanghoverhandler.cpp b/src/plugins/clangcodemodel/clanghoverhandler.cpp index 58f414d9f1..c8e87c2577 100644 --- a/src/plugins/clangcodemodel/clanghoverhandler.cpp +++ b/src/plugins/clangcodemodel/clanghoverhandler.cpp @@ -93,79 +93,6 @@ ClangHoverHandler::~ClangHoverHandler() abort(); } -static int skipChars(QTextCursor *tc, - QTextCursor::MoveOperation op, - int offset, - std::function<bool(const QChar &)> skip) -{ - const QTextDocument *doc = tc->document(); - QChar ch = doc->characterAt(tc->position() + offset); - if (ch.isNull()) - return 0; - int count = 0; - while (skip(ch)) { - if (tc->movePosition(op)) - ++count; - else - break; - ch = doc->characterAt(tc->position() + offset); - } - return count; -} - -static int skipCharsForward(QTextCursor *tc, std::function<bool(const QChar &)> skip) -{ - return skipChars(tc, QTextCursor::NextCharacter, 0, skip); -} - -static int skipCharsBackward(QTextCursor *tc, std::function<bool(const QChar &)> skip) -{ - return skipChars(tc, QTextCursor::PreviousCharacter, -1, skip); -} - -static QStringList fallbackWords(QTextDocument *document, int pos) -{ - const auto isSpace = [](const QChar &c) { return c.isSpace(); }; - const auto isColon = [](const QChar &c) { return c == ':'; }; - const auto isValidIdentifierChar = [document](const QTextCursor &tc) { - return CppTools::isValidIdentifierChar(document->characterAt(tc.position())); - }; - // move to the end - QTextCursor endCursor(document); - endCursor.setPosition(pos); - do { - CppTools::moveCursorToEndOfIdentifier(&endCursor); - // possibly skip :: - QTextCursor temp(endCursor); - skipCharsForward(&temp, isSpace); - const int colons = skipCharsForward(&temp, isColon); - skipCharsForward(&temp, isSpace); - if (colons == 2 && isValidIdentifierChar(temp)) - endCursor = temp; - } while (isValidIdentifierChar(endCursor)); - - QStringList results; - QTextCursor startCursor(endCursor); - do { - CppTools::moveCursorToStartOfIdentifier(&startCursor); - if (startCursor.position() == endCursor.position()) - break; - QTextCursor temp(endCursor); - temp.setPosition(startCursor.position(), QTextCursor::KeepAnchor); - results.append(temp.selectedText().remove(QRegularExpression("\\s"))); - // possibly skip :: - temp = startCursor; - skipCharsBackward(&temp, isSpace); - const int colons = skipCharsBackward(&temp, isColon); - skipCharsBackward(&temp, isSpace); - if (colons == 2 - && CppTools::isValidIdentifierChar(document->characterAt(temp.position() - 1))) { - startCursor = temp; - } - } while (!isValidIdentifierChar(startCursor)); - return results; -} - void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, BaseHoverHandler::ReportPriority report) @@ -187,7 +114,9 @@ void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget, qCDebug(hoverLog) << "Requesting tooltip info at" << pos; m_reportPriority = report; m_futureWatcher.reset(new QFutureWatcher<CppTools::ToolTipInfo>()); - const QStringList fallback = fallbackWords(editorWidget->document(), pos); + QTextCursor tc(editorWidget->document()); + tc.setPosition(pos); + const QStringList fallback = CppTools::identifierWordsUnderCursor(tc); QObject::connect(m_futureWatcher.data(), &QFutureWatcherBase::finished, [this, fallback]() { diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index e8c6b5e902..1b316bea55 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -36,16 +36,27 @@ namespace ClangFormat { -static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style) +static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style, + ReplacementsToKeep replacementsToKeep) { + if (replacementsToKeep == ReplacementsToKeep::All) + return; + + style.MaxEmptyLinesToKeep = 2; + style.SortIncludes = false; + style.SortUsingDeclarations = false; + + // This is a separate pass, don't do it unless it's the full formatting. + style.FixNamespaceComments = false; + + if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) + return; + style.DisableFormat = false; style.ColumnLimit = 0; #ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED style.KeepLineBreaksForNonEmptyLines = true; #endif - style.MaxEmptyLinesToKeep = 2; - style.SortIncludes = false; - style.SortUsingDeclarations = false; } static llvm::StringRef clearExtraNewline(llvm::StringRef text) @@ -59,7 +70,6 @@ static clang::tooling::Replacements filteredReplacements( const clang::tooling::Replacements &replacements, int offset, int utf8LineLengthBeforeCursor, - int extraOffsetFromStartOfFile, int extraEmptySpaceOffset, ReplacementsToKeep replacementsToKeep) { @@ -69,14 +79,13 @@ static clang::tooling::Replacements filteredReplacements( const bool replacementDoesNotMatchRestriction = (replacementsToKeep == ReplacementsToKeep::OnlyIndent && replacementOffset != offset - 1) - || (replacementsToKeep == ReplacementsToKeep::OnlyBeforeIndent - && replacementOffset >= offset + utf8LineLengthBeforeCursor - 1); + || (replacementsToKeep == ReplacementsToKeep::IndentAndBefore + && replacementOffset > offset + utf8LineLengthBeforeCursor - 1); if (replacementDoesNotMatchRestriction) continue; if (replacementOffset >= offset - 1) replacementOffset += extraEmptySpaceOffset; - replacementOffset += extraOffsetFromStartOfFile; llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent ? clearExtraNewline(replacement.getReplacementText()) @@ -128,18 +137,6 @@ static void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock) cursor.endEditBlock(); } -static void trimCurrentBlock(const QTextBlock ¤tBlock) -{ - if (currentBlock.text().trimmed().isEmpty()) { - // Clear the block containing only spaces - QTextCursor cursor(currentBlock); - cursor.beginEditBlock(); - cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - cursor.endEditBlock(); - } -} - // Returns the total langth of previous lines with pure whitespace. static int previousEmptyLinesLength(const QTextBlock ¤tBlock) { @@ -167,7 +164,7 @@ static void modifyToIndentEmptyLines( if (firstNonWhitespace < 0 || closingParenBlock) { //This extra text works for the most cases. - QByteArray dummyText("a;"); + QByteArray dummyText("a;a;"); // Search for previous character QTextBlock prevBlock = block.previous(); @@ -198,8 +195,6 @@ static void modifyToIndentEmptyLines( } } -static const int kMaxLinesFromCurrentBlock = 200; - static Utils::LineColumn utf16LineColumn(const QTextBlock &block, int blockOffsetUtf8, const QByteArray &utf8Buffer, @@ -388,24 +383,21 @@ TextEditor::Replacements ClangFormatBaseIndenter::format( return format(cursor, cursorPositionInEditor); } -int ClangFormatBaseIndenter::indentBeforeCursor(const QTextBlock &block, - const QChar &typedChar, - int cursorPositionInEditor) +void ClangFormatBaseIndenter::indentBeforeCursor(const QTextBlock &block, + const QChar &typedChar, + int cursorPositionInEditor) { const QByteArray buffer = m_doc->toPlainText().toUtf8(); const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); - QTC_ASSERT(utf8Offset >= 0, return cursorPositionInEditor;); + QTC_ASSERT(utf8Offset >= 0, return;); const TextEditor::Replacements toReplace = replacements(buffer, utf8Offset, 0, block, cursorPositionInEditor, - ReplacementsToKeep::OnlyBeforeIndent, + ReplacementsToKeep::IndentAndBefore, typedChar); applyReplacements(block, toReplace); - for (const TextEditor::Replacement &replacement : toReplace) - cursorPositionInEditor += replacement.text.length() - replacement.length; - return cursorPositionInEditor; } static bool doNotIndentInContext(QTextDocument *doc, int pos) @@ -466,11 +458,10 @@ void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, else cursorPositionInEditor = currentBlock.position(); - cursorPositionInEditor = indentBeforeCursor(currentBlock, typedChar, cursorPositionInEditor); - currentBlock = m_doc->findBlock(cursorPositionInEditor); + indentBeforeCursor(currentBlock, typedChar, cursorPositionInEditor); + return; } - trimCurrentBlock(currentBlock); const QByteArray buffer = m_doc->toPlainText().toUtf8(); const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return;); @@ -496,7 +487,6 @@ void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, int cursorPositionInEditor) { trimFirstNonEmptyBlock(block); - trimCurrentBlock(block); const QByteArray buffer = m_doc->toPlainText().toUtf8(); const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return 0;); @@ -571,8 +561,7 @@ static int formattingRangeStart(const QTextBlock ¤tBlock, { QTextBlock prevBlock = currentBlock.previous(); while ((prevBlock.position() > 0 || prevBlock.length() > 0) - && prevBlock.revision() != documentRevision - && (currentBlock.blockNumber() - prevBlock.blockNumber() < kMaxLinesFromCurrentBlock)) { + && prevBlock.revision() != documentRevision) { // Find the first block with not matching revision. prevBlock = prevBlock.previous(); } @@ -604,41 +593,27 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer = block.text().left(cursorPositionInEditor - block.position()).toUtf8().size(); } - int extraOffsetFromStartOfFile = 0; int extraEmptySpaceOffset = 0; int rangeStart = 0; if (replacementsToKeep != ReplacementsToKeep::All) { - if (block.blockNumber() > kMaxLinesFromCurrentBlock) { - extraOffsetFromStartOfFile - = Utils::Text::utf8NthLineOffset(block.document(), - buffer, - block.blockNumber() - kMaxLinesFromCurrentBlock); - } - int endOffset = Utils::Text::utf8NthLineOffset(block.document(), - buffer, - block.blockNumber() - + kMaxLinesFromCurrentBlock); - if (endOffset == -1) - endOffset = buffer.size(); - - - if (replacementsToKeep == ReplacementsToKeep::OnlyBeforeIndent) + if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) rangeStart = formattingRangeStart(block, buffer, lastSaveRevision()); - buffer = buffer.mid(extraOffsetFromStartOfFile, endOffset - extraOffsetFromStartOfFile); - utf8Offset -= extraOffsetFromStartOfFile; - rangeStart -= extraOffsetFromStartOfFile; - extraEmptySpaceOffset = previousEmptyLinesLength(block); utf8Offset -= extraEmptySpaceOffset; buffer.remove(utf8Offset, extraEmptySpaceOffset); - if (replacementsToKeep == ReplacementsToKeep::OnlyIndent) - adjustFormatStyleForLineBreak(style); + adjustFormatStyleForLineBreak(style, replacementsToKeep); modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, block, secondTry); + + if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) { + buffer.insert(utf8Offset - 1, " //"); + extraEmptySpaceOffset -= 3; + utf8Offset += 3; + } } - if (replacementsToKeep != ReplacementsToKeep::OnlyBeforeIndent || utf8Offset < rangeStart) + if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart) rangeStart = utf8Offset; unsigned int rangeLength = static_cast<unsigned int>(utf8Offset + utf8Length - rangeStart); @@ -658,7 +633,6 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer filtered = filteredReplacements(clangReplacements, utf8Offset, utf8LineLengthBeforeCursor, - extraOffsetFromStartOfFile, extraEmptySpaceOffset, replacementsToKeep); } diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h index 1a73d9bfbc..2017f8ba64 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.h +++ b/src/plugins/clangformat/clangformatbaseindenter.h @@ -31,7 +31,7 @@ namespace ClangFormat { -enum class ReplacementsToKeep { OnlyIndent, OnlyBeforeIndent, All }; +enum class ReplacementsToKeep { OnlyIndent, IndentAndBefore, All }; class ClangFormatBaseIndenter : public TextEditor::Indenter { @@ -79,9 +79,9 @@ private: void indent(const QTextCursor &cursor, const QChar &typedChar, int cursorPositionInEditor); void indentBlock(const QTextBlock &block, const QChar &typedChar, int cursorPositionInEditor); int indentFor(const QTextBlock &block, int cursorPositionInEditor); - int indentBeforeCursor(const QTextBlock &block, - const QChar &typedChar, - int cursorPositionInEditor); + void indentBeforeCursor(const QTextBlock &block, + const QChar &typedChar, + int cursorPositionInEditor); TextEditor::Replacements replacements(QByteArray buffer, int utf8Offset, int utf8Length, diff --git a/src/plugins/clangtools/clangselectablefilesdialog.ui b/src/plugins/clangtools/clangselectablefilesdialog.ui index 316b9946fb..2fe10139a3 100644 --- a/src/plugins/clangtools/clangselectablefilesdialog.ui +++ b/src/plugins/clangtools/clangselectablefilesdialog.ui @@ -52,7 +52,7 @@ </layout> </item> <item> - <widget class="ClangTools::ClangToolsBasicSettings" name="clangToolsBasicSettings" native="true"/> + <widget class="ClangTools::Internal::ClangToolsBasicSettings" name="clangToolsBasicSettings" native="true"/> </item> </layout> </widget> @@ -87,7 +87,7 @@ </widget> <customwidgets> <customwidget> - <class>ClangTools::ClangToolsBasicSettings</class> + <class>ClangTools::Internal::ClangToolsBasicSettings</class> <extends>QWidget</extends> <header>clangtools/clangtoolsbasicsettings.h</header> </customwidget> diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index 9056b64367..0583a3aa0f 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -219,7 +219,7 @@ ClangTidyClazyTool::ClangTidyClazyTool() m_diagnosticView->sortByColumn(Debugger::DetailedErrorView::DiagnosticColumn, Qt::AscendingOrder); m_diagnosticView->setObjectName(QLatin1String("ClangTidyClazyIssuesView")); - m_diagnosticView->setWindowTitle(tr("Clang-Tidy and Clazy Issues")); + m_diagnosticView->setWindowTitle(tr("Clang-Tidy and Clazy Diagnostics")); foreach (auto * const model, QList<QAbstractItemModel *>({m_diagnosticModel, m_diagnosticFilterModel})) { @@ -281,7 +281,7 @@ ClangTidyClazyTool::ClangTidyClazyTool() // Filter line edit m_filterLineEdit = new Utils::FancyLineEdit(); m_filterLineEdit->setFiltering(true); - m_filterLineEdit->setPlaceholderText(tr("Filter Issues")); + m_filterLineEdit->setPlaceholderText(tr("Filter Diagnostics")); m_filterLineEdit->setHistoryCompleter("CppTools.ClangTidyClazyIssueFilter", true); connect(m_filterLineEdit, &Utils::FancyLineEdit::filterChanged, [this](const QString &filter) { m_diagnosticFilterModel->setFilterRegExp( @@ -309,7 +309,7 @@ ClangTidyClazyTool::ClangTidyClazyTool() ActionContainer *menu = ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER); const QString toolTip = tr("Clang-Tidy and Clazy use a customized Clang executable from the " - "Clang project to search for errors and warnings."); + "Clang project to search for diagnostics."); m_perspective.addWindow(m_diagnosticView, Perspective::SplitVertical, nullptr); @@ -450,14 +450,14 @@ void ClangTidyClazyTool::handleStateUpdate() QString message; if (m_running) { if (issuesFound) - message = tr("Running - %n issues found", nullptr, issuesFound); + message = tr("Running - %n diagnostics", nullptr, issuesFound); else - message = tr("Running - No issues found", nullptr, issuesFound); + message = tr("Running - No diagnostics", nullptr, issuesFound); } else { if (issuesFound) - message = tr("Finished - %n issues found", nullptr, issuesFound); + message = tr("Finished - %n diagnostics", nullptr, issuesFound); else - message = tr("Finished - No issues found", nullptr, issuesFound); + message = tr("Finished - No diagnostics", nullptr, issuesFound); } Debugger::showPermanentStatusMessage(message); diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index b02868cb5d..52746080c1 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -46,6 +46,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/session.h> +#include <utils/algorithm.h> #include <utils/fancymainwindow.h> #include <utils/utilsicons.h> @@ -137,7 +138,10 @@ void ClangTool::initDiagnosticView() QSet<Diagnostic> ClangTool::diagnostics() const { - return m_diagnosticModel->diagnostics(); + return Utils::filtered(m_diagnosticModel->diagnostics(), [](const Diagnostic &diagnostic) { + using CppTools::ProjectFile; + return ProjectFile::isSource(ProjectFile::classify(diagnostic.location.filePath)); + }); } void ClangTool::onNewDiagnosticsAvailable(const QList<Diagnostic> &diagnostics) diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index c92a446484..2f994c3a8f 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -267,6 +267,8 @@ void ClangToolRunControl::init() void ClangToolRunControl::start() { + TaskHub::clearTasks(Debugger::Constants::ANALYZERTASK_ID); + if (ClangToolsSettings::instance()->savedBuildBeforeAnalysis()) { QTC_ASSERT(m_projectBuilder, return;); if (!m_projectBuilder->success()) { @@ -441,7 +443,7 @@ void ClangToolRunControl::onRunnerFinishedWithFailure(const QString &errorMessag m_success = false; appendMessage(message, Utils::StdErrFormat); appendMessage(errorDetails, Utils::StdErrFormat); - TaskHub::addTask(Task::Warning, message, Debugger::Constants::ANALYZERTASK_ID); + TaskHub::addTask(Task::Error, message, Debugger::Constants::ANALYZERTASK_ID); handleFinished(); } diff --git a/src/plugins/clangtools/clangtoolsbasicsettings.cpp b/src/plugins/clangtools/clangtoolsbasicsettings.cpp index 7fe56e9193..bf1d533700 100644 --- a/src/plugins/clangtools/clangtoolsbasicsettings.cpp +++ b/src/plugins/clangtools/clangtoolsbasicsettings.cpp @@ -29,6 +29,7 @@ #include "clangtoolsutils.h" namespace ClangTools { +namespace Internal { ClangToolsBasicSettings::ClangToolsBasicSettings(QWidget *parent) : QWidget(parent) @@ -47,4 +48,5 @@ Ui::ClangToolsBasicSettings *ClangToolsBasicSettings::ui() return m_ui; } +} // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsbasicsettings.h b/src/plugins/clangtools/clangtoolsbasicsettings.h index fb896e830d..b9ecb0285e 100644 --- a/src/plugins/clangtools/clangtoolsbasicsettings.h +++ b/src/plugins/clangtools/clangtoolsbasicsettings.h @@ -28,6 +28,7 @@ #include <QWidget> namespace ClangTools { +namespace Internal { namespace Ui { class ClangToolsBasicSettings; } @@ -46,4 +47,5 @@ private: Ui::ClangToolsBasicSettings *m_ui; }; +} // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsbasicsettings.ui b/src/plugins/clangtools/clangtoolsbasicsettings.ui index 4b6f14111e..ccdba126bd 100644 --- a/src/plugins/clangtools/clangtoolsbasicsettings.ui +++ b/src/plugins/clangtools/clangtoolsbasicsettings.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> - <class>ClangTools::ClangToolsBasicSettings</class> - <widget class="QWidget" name="ClangTools::ClangToolsBasicSettings"> + <class>ClangTools::Internal::ClangToolsBasicSettings</class> + <widget class="QWidget" name="ClangTools::Internal::ClangToolsBasicSettings"> <property name="geometry"> <rect> <x>0</x> diff --git a/src/plugins/clangtools/clangtoolsconfigwidget.ui b/src/plugins/clangtools/clangtoolsconfigwidget.ui index ea8dafb92b..b2d5aa9875 100644 --- a/src/plugins/clangtools/clangtoolsconfigwidget.ui +++ b/src/plugins/clangtools/clangtoolsconfigwidget.ui @@ -21,7 +21,7 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="ClangTools::ClangToolsBasicSettings" name="clangToolsBasicSettings" native="true"/> + <widget class="ClangTools::Internal::ClangToolsBasicSettings" name="clangToolsBasicSettings" native="true"/> </item> <item> <layout class="QHBoxLayout" name="processesLayout"> @@ -77,7 +77,7 @@ </widget> <customwidgets> <customwidget> - <class>ClangTools::ClangToolsBasicSettings</class> + <class>ClangTools::Internal::ClangToolsBasicSettings</class> <extends>QWidget</extends> <header>clangtools/clangtoolsbasicsettings.h</header> </customwidget> diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index 6e16cc6eec..4225970221 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -84,7 +84,7 @@ ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) : Utils::TreeModel<>(parent) , m_filesWatcher(std::make_unique<QFileSystemWatcher>()) { - setHeader({tr("Issue"), tr("Fixit Status")}); + setHeader({tr("Diagnostic")}); connectFileWatcher(); } @@ -192,7 +192,26 @@ void ClangToolsDiagnosticModel::addWatchedPath(const QString &path) m_filesWatcher->addPath(path); } -static QString createDiagnosticToolTipString(const Diagnostic &diagnostic) +static QString fixitStatus(FixitStatus status) +{ + switch (status) { + case FixitStatus::NotAvailable: + return ClangToolsDiagnosticModel::tr("No Fixits"); + case FixitStatus::NotScheduled: + return ClangToolsDiagnosticModel::tr("Not Scheduled"); + case FixitStatus::Invalidated: + return ClangToolsDiagnosticModel::tr("Invalidated"); + case FixitStatus::Scheduled: + return ClangToolsDiagnosticModel::tr("Scheduled"); + case FixitStatus::FailedToApply: + return ClangToolsDiagnosticModel::tr("Failed to Apply"); + case FixitStatus::Applied: + return ClangToolsDiagnosticModel::tr("Applied"); + } + return QString(); +} + +static QString createDiagnosticToolTipString(const Diagnostic &diagnostic, FixitStatus fixItStatus) { using StringPair = QPair<QString, QString>; QList<StringPair> lines; @@ -226,6 +245,10 @@ static QString createDiagnosticToolTipString(const Diagnostic &diagnostic) QCoreApplication::translate("ClangTools::Diagnostic", "Location:"), createFullLocationString(diagnostic.location)); + lines << qMakePair( + QCoreApplication::translate("ClangTools::Diagnostic", "Fixit Status:"), + fixitStatus(fixItStatus)); + QString html = QLatin1String("<html>" "<head>" "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n" @@ -364,19 +387,8 @@ DiagnosticItem::~DiagnosticItem() Qt::ItemFlags DiagnosticItem::flags(int column) const { const Qt::ItemFlags itemFlags = TreeItem::flags(column); - if (column == DiagnosticView::FixItColumn) { - switch (m_fixitStatus) { - case FixitStatus::NotAvailable: - case FixitStatus::Applied: - case FixitStatus::FailedToApply: - case FixitStatus::Invalidated: - return itemFlags & ~Qt::ItemIsEnabled; - case FixitStatus::Scheduled: - case FixitStatus::NotScheduled: - return itemFlags | Qt::ItemIsUserCheckable; - } - } - + if (column == DiagnosticView::DiagnosticColumn) + return itemFlags | Qt::ItemIsUserCheckable; return itemFlags; } @@ -395,49 +407,46 @@ static QVariant iconData(const QString &type) QVariant DiagnosticItem::data(int column, int role) const { - if (column == DiagnosticView::FixItColumn) { - if (role == Qt::CheckStateRole) { + if (column == DiagnosticView::DiagnosticColumn) { + switch (role) { + case Debugger::DetailedErrorView::LocationRole: + return QVariant::fromValue(m_diagnostic.location); + case Debugger::DetailedErrorView::FullTextRole: + return fullText(m_diagnostic); + case ClangToolsDiagnosticModel::DiagnosticRole: + return QVariant::fromValue(m_diagnostic); + case ClangToolsDiagnosticModel::TextRole: + return m_diagnostic.description; + case ClangToolsDiagnosticModel::CheckBoxEnabledRole: switch (m_fixitStatus) { case FixitStatus::NotAvailable: - case FixitStatus::NotScheduled: - case FixitStatus::Invalidated: case FixitStatus::Applied: case FixitStatus::FailedToApply: - return Qt::Unchecked; + case FixitStatus::Invalidated: + return false; case FixitStatus::Scheduled: - return Qt::Checked; + case FixitStatus::NotScheduled: + return true; } - } else if (role == Qt::DisplayRole) { + break; + case Qt::CheckStateRole: { switch (m_fixitStatus) { case FixitStatus::NotAvailable: - return ClangToolsDiagnosticModel::tr("No Fixits"); - case FixitStatus::NotScheduled: - return ClangToolsDiagnosticModel::tr("Not Scheduled"); case FixitStatus::Invalidated: - return ClangToolsDiagnosticModel::tr("Invalidated"); - case FixitStatus::Scheduled: - return ClangToolsDiagnosticModel::tr("Scheduled"); - case FixitStatus::FailedToApply: - return ClangToolsDiagnosticModel::tr("Failed to Apply"); case FixitStatus::Applied: - return ClangToolsDiagnosticModel::tr("Applied"); + case FixitStatus::FailedToApply: + case FixitStatus::NotScheduled: + return Qt::Unchecked; + case FixitStatus::Scheduled: + return Qt::Checked; } + break; } - } else if (column == DiagnosticView::DiagnosticColumn) { - switch (role) { - case Debugger::DetailedErrorView::LocationRole: - return QVariant::fromValue(m_diagnostic.location); - case Debugger::DetailedErrorView::FullTextRole: - return fullText(m_diagnostic); - case ClangToolsDiagnosticModel::DiagnosticRole: - return QVariant::fromValue(m_diagnostic); - case ClangToolsDiagnosticModel::TextRole: - return m_diagnostic.description; case Qt::DisplayRole: return QString("%1: %2").arg(lineColumnString(m_diagnostic.location), m_diagnostic.description); case Qt::ToolTipRole: - return createDiagnosticToolTipString(m_diagnostic); + return createDiagnosticToolTipString(m_diagnostic, m_fixitStatus); case Qt::DecorationRole: return iconData(m_diagnostic.type); default: @@ -450,7 +459,7 @@ QVariant DiagnosticItem::data(int column, int role) const bool DiagnosticItem::setData(int column, const QVariant &data, int role) { - if (column == DiagnosticView::FixItColumn && role == Qt::CheckStateRole) { + if (column == DiagnosticView::DiagnosticColumn && role == Qt::CheckStateRole) { if (m_fixitStatus != FixitStatus::Scheduled && m_fixitStatus != FixitStatus::NotScheduled) return false; @@ -494,15 +503,6 @@ ExplainingStepItem::ExplainingStepItem(const ExplainingStep &step, int index) , m_index(index) {} -// We expect something like "note: ..." -static QVariant iconForExplainingStepMessage(const QString &message) -{ - const int index = message.indexOf(':'); - if (index == -1) - return QVariant(); - return iconData(message.mid(0, index)); -} - static QString rangeString(const QVector<Debugger::DiagnosticLocation> &ranges) { return QString("%1-%2").arg(lineColumnString(ranges[0]), lineColumnString(ranges[1])); @@ -510,9 +510,6 @@ static QString rangeString(const QVector<Debugger::DiagnosticLocation> &ranges) QVariant ExplainingStepItem::data(int column, int role) const { - if (column == DiagnosticView::FixItColumn) - return QVariant(); - if (column == DiagnosticView::DiagnosticColumn) { // DiagnosticColumn switch (role) { @@ -555,7 +552,7 @@ QVariant ExplainingStepItem::data(int column, int role) const case Qt::DecorationRole: if (m_step.isFixIt) return Utils::Icons::CODEMODEL_FIXIT.icon(); - return iconForExplainingStepMessage(m_step.message); + return Utils::Icons::INFO.icon(); default: return QVariant(); } diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h index 22d0699201..a8c4dcd011 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -112,7 +112,11 @@ public: void addDiagnostics(const QList<Diagnostic> &diagnostics); QSet<Diagnostic> diagnostics() const; - enum ItemRole { DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1, TextRole }; + enum ItemRole { + DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1, + TextRole, + CheckBoxEnabledRole + }; void clear(); void removeWatchedPath(const QString &path); diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp index 07bc7f397f..33b002ced2 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp @@ -30,11 +30,13 @@ #include "clangtoolsutils.h" #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/manhattanstyle.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> #include <QAction> +#include <QApplication> #include <QDebug> #include <QHeaderView> #include <QPainter> @@ -44,21 +46,22 @@ using namespace Debugger; namespace ClangTools { namespace Internal { -class ClickableFixItHeader : public QHeaderView +// A header view that puts a check box in front of a given column. +class HeaderWithCheckBoxInColumn : public QHeaderView { Q_OBJECT public: - ClickableFixItHeader(Qt::Orientation orientation, QWidget *parent = nullptr) + HeaderWithCheckBoxInColumn(Qt::Orientation orientation, + int column = 0, + QWidget *parent = nullptr) : QHeaderView(orientation, parent) + , m_column(column) { setDefaultAlignment(Qt::AlignLeft); } - void setState(QFlags<QStyle::StateFlag> newState) - { - state = newState; - } + void setState(QFlags<QStyle::StateFlag> newState) { state = newState; } protected: void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override @@ -66,7 +69,7 @@ protected: painter->save(); QHeaderView::paintSection(painter, rect, logicalIndex); painter->restore(); - if (logicalIndex == DiagnosticView::FixItColumn) { + if (logicalIndex == m_column) { QStyleOptionButton option; const int side = sizeHint().height(); option.rect = QRect(rect.left() + 1, 1, side - 3, side - 3); @@ -83,34 +86,111 @@ protected: void mouseReleaseEvent(QMouseEvent *event) override { const int x = event->localPos().x(); - const int fixItColumnX = sectionPosition(DiagnosticView::FixItColumn); - const bool isWithinFixitCheckBox = x > fixItColumnX - && x < fixItColumnX + sizeHint().height() - 3; - if (isWithinFixitCheckBox) { + const int columnX = sectionPosition(m_column); + const bool isWithinCheckBox = x > columnX && x < columnX + sizeHint().height() - 3; + if (isWithinCheckBox) { state = (state != QStyle::State_On) ? QStyle::State_On : QStyle::State_Off; viewport()->update(); - emit fixItColumnClicked(state == QStyle::State_On); + emit checkBoxClicked(state == QStyle::State_On); return; // Avoid changing sort order } QHeaderView::mouseReleaseEvent(event); } signals: - void fixItColumnClicked(bool checked); + void checkBoxClicked(bool checked); private: + const int m_column = 0; QFlags<QStyle::StateFlag> state = QStyle::State_Off; }; +static QString getBaseStyleName() +{ + QStyle *style = QApplication::style(); + if (auto proxyStyle = qobject_cast<QProxyStyle *>(style)) + style = proxyStyle->baseStyle(); + return style->objectName(); +} + +// Paints the check box indicator disabled if requested by client. +class DiagnosticViewStyle : public ManhattanStyle +{ +public: + DiagnosticViewStyle(const QString &baseStyleName = getBaseStyleName()) + : ManhattanStyle(baseStyleName) + {} + + void setPaintCheckBoxDisabled(bool paintDisabledCheckbox) + { + m_paintCheckBoxDisabled = paintDisabledCheckbox; + } + + void drawPrimitive(PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const final + { + if (element == QStyle::PE_IndicatorCheckBox && m_paintCheckBoxDisabled) { + if (const QStyleOptionButton *o = qstyleoption_cast<const QStyleOptionButton *>( + option)) { + QStyleOptionButton myOption = *o; + myOption.palette.setCurrentColorGroup(QPalette::Disabled); + ManhattanStyle::drawPrimitive(element, &myOption, painter, widget); + return; + } + } + ManhattanStyle::drawPrimitive(element, option, painter, widget); + } + +private: + bool m_paintCheckBoxDisabled = false; +}; + +// A delegate that allows to paint a disabled check box (indicator). +// This is useful if the rest of the item should not be disabled. +class DiagnosticViewDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + DiagnosticViewDelegate(DiagnosticViewStyle *style) + : m_style(style) + {} + + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const final + { + const bool paintDisabled = !index.data(ClangToolsDiagnosticModel::CheckBoxEnabledRole) + .toBool(); + if (paintDisabled) + m_style->setPaintCheckBoxDisabled(true); + QStyledItemDelegate::paint(painter, option, index); + if (paintDisabled) + m_style->setPaintCheckBoxDisabled(false); + } + +private: + DiagnosticViewStyle *m_style = nullptr; +}; + DiagnosticView::DiagnosticView(QWidget *parent) : Debugger::DetailedErrorView(parent) + , m_style(new DiagnosticViewStyle) + , m_delegate(new DiagnosticViewDelegate(m_style.get())) { m_suppressAction = new QAction(tr("Suppress This Diagnostic"), this); connect(m_suppressAction, &QAction::triggered, this, &DiagnosticView::suppressCurrentDiagnostic); installEventFilter(this); + + setStyle(m_style.get()); + setItemDelegate(m_delegate.get()); } +DiagnosticView::~DiagnosticView() = default; + void DiagnosticView::suppressCurrentDiagnostic() { const QModelIndexList indexes = selectionModel()->selectedRows(); @@ -199,7 +279,6 @@ bool DiagnosticView::eventFilter(QObject *watched, QEvent *event) switch (key) { case Qt::Key_Return: case Qt::Key_Enter: - case Qt::Key_Space: openEditorForCurrentIndex(); } return true; @@ -219,11 +298,11 @@ void DiagnosticView::setSelectedFixItsCount(int fixItsCount) { if (m_ignoreSetSelectedFixItsCount) return; - auto *clickableFixItHeader = static_cast<ClickableFixItHeader *>(header()); - clickableFixItHeader->setState(fixItsCount - ? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off) - : QStyle::State_Off); - clickableFixItHeader->viewport()->update(); + auto checkBoxHeader = static_cast<HeaderWithCheckBoxInColumn *>(header()); + checkBoxHeader->setState(fixItsCount + ? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off) + : QStyle::State_Off); + checkBoxHeader->viewport()->update(); } void DiagnosticView::openEditorForCurrentIndex() @@ -237,28 +316,31 @@ void DiagnosticView::openEditorForCurrentIndex() void DiagnosticView::setModel(QAbstractItemModel *theProxyModel) { const auto proxyModel = static_cast<QSortFilterProxyModel *>(theProxyModel); - const auto sourceModel = static_cast<ClangToolsDiagnosticModel *>(proxyModel->sourceModel()); - Debugger::DetailedErrorView::setModel(proxyModel); - auto *clickableFixItHeader = new ClickableFixItHeader(Qt::Horizontal, this); - connect(clickableFixItHeader, &ClickableFixItHeader::fixItColumnClicked, this, [=](bool checked) { + + auto *header = new HeaderWithCheckBoxInColumn(Qt::Horizontal, + DiagnosticView::DiagnosticColumn, + this); + connect(header, &HeaderWithCheckBoxInColumn::checkBoxClicked, this, [=](bool checked) { m_ignoreSetSelectedFixItsCount = true; - sourceModel->rootItem()->forChildrenAtLevel(2, [&](::Utils::TreeItem *item) { - auto diagnosticItem = static_cast<DiagnosticItem *>(item); - diagnosticItem->setData(FixItColumn, - checked ? Qt::Checked : Qt::Unchecked, - Qt::CheckStateRole); - }); + for (int i = 0, count = proxyModel->rowCount(); i < count; ++i) { + const QModelIndex filePathItemIndex = proxyModel->index(i, 0); + for (int j = 0, count = proxyModel->rowCount(filePathItemIndex); j < count; ++j) { + const QModelIndex proxyIndex = proxyModel->index(j, 0, filePathItemIndex); + const QModelIndex diagnosticItemIndex = proxyModel->mapToSource(proxyIndex); + auto item = static_cast<DiagnosticItem *>(diagnosticItemIndex.internalPointer()); + item->setData(DiagnosticView::DiagnosticColumn, + checked ? Qt::Checked : Qt::Unchecked, + Qt::CheckStateRole); + } + } m_ignoreSetSelectedFixItsCount = false; }); - setHeader(clickableFixItHeader); - clickableFixItHeader->setStretchLastSection(false); - clickableFixItHeader->setSectionResizeMode(0, QHeaderView::Stretch); - clickableFixItHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); - - const int fixitColumnWidth = clickableFixItHeader->sectionSizeHint(DiagnosticView::FixItColumn); - const int checkboxWidth = clickableFixItHeader->height(); - clickableFixItHeader->setMinimumSectionSize(fixitColumnWidth + 1.2 * checkboxWidth); + setHeader(header); + header->setSectionResizeMode(DiagnosticView::DiagnosticColumn, QHeaderView::Stretch); + const int columnWidth = header->sectionSizeHint(DiagnosticView::DiagnosticColumn); + const int checkboxWidth = header->height(); + header->setMinimumSectionSize(columnWidth + 1.2 * checkboxWidth); } } // namespace Internal diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.h b/src/plugins/clangtools/clangtoolsdiagnosticview.h index 7cb287ab39..6bc6135101 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.h @@ -27,19 +27,21 @@ #include <debugger/analyzer/detailederrorview.h> +#include <memory> + namespace ClangTools { namespace Internal { +class DiagnosticViewStyle; +class DiagnosticViewDelegate; + class DiagnosticView : public Debugger::DetailedErrorView { Q_OBJECT public: DiagnosticView(QWidget *parent = nullptr); - - enum ExtraColumn { - FixItColumn = DiagnosticColumn + 1, - }; + ~DiagnosticView() override; void setSelectedFixItsCount(int fixItsCount); @@ -59,6 +61,8 @@ private: void setModel(QAbstractItemModel *theProxyModel) override; QAction *m_suppressAction; + std::unique_ptr<DiagnosticViewStyle> m_style; + std::unique_ptr<DiagnosticViewDelegate> m_delegate; bool m_ignoreSetSelectedFixItsCount = false; }; diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp index d2213d3304..e8a6fbcc96 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -94,7 +94,7 @@ static ExplainingStep buildChildDiagnostic(const CXDiagnostic cxDiagnostic) const CXSourceLocation cxLocation = clang_getDiagnosticLocation(cxDiagnostic); diagnosticStep.location = diagLocationFromSourceLocation(cxLocation); - diagnosticStep.message = type + ": " + fromCXString(clang_getDiagnosticSpelling(cxDiagnostic)); + diagnosticStep.message = fromCXString(clang_getDiagnosticSpelling(cxDiagnostic)); return diagnosticStep; } diff --git a/src/plugins/coreplugin/helpitem.cpp b/src/plugins/coreplugin/helpitem.cpp index b8794481cf..d63bd883de 100644 --- a/src/plugins/coreplugin/helpitem.cpp +++ b/src/plugins/coreplugin/helpitem.cpp @@ -270,7 +270,7 @@ static const HelpItem::Links getBestLink(const HelpItem::Links &links) const HelpItem::Links HelpItem::bestLinks() const { - if (m_isFuzzyMatch) + if (isFuzzyMatch()) return getBestLinks(links()); return getBestLink(links()); } @@ -279,3 +279,10 @@ const QString HelpItem::keyword() const { return m_keyword; } + +bool HelpItem::isFuzzyMatch() const +{ + // make sure m_isFuzzyMatch is correct + links(); + return m_isFuzzyMatch; +} diff --git a/src/plugins/coreplugin/helpitem.h b/src/plugins/coreplugin/helpitem.h index 4d9ab153fb..15ff585ca8 100644 --- a/src/plugins/coreplugin/helpitem.h +++ b/src/plugins/coreplugin/helpitem.h @@ -86,6 +86,8 @@ public: const QString keyword() const; private: + bool isFuzzyMatch() const; + QUrl m_helpUrl; QStringList m_helpIds; QString m_docMark; diff --git a/src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png Binary files differindex 3dc3b0a74e..53a42c20fe 100644 --- a/src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png Binary files differindex 2adcdb5f49..d48df57e63 100644 --- a/src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png Binary files differindex 3ec47c9175..449c740a11 100644 --- a/src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png Binary files differindex 68667830c1..3a9cf292bc 100644 --- a/src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png Binary files differindex 8173281abf..87ab1ba5cb 100644 --- a/src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png Binary files differindex 9d218b36d8..43a6834a14 100644 --- a/src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png Binary files differindex 45aa2008d1..acbb5a5134 100644 --- a/src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png b/src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png Binary files differindex 778da34776..1a89d932f5 100644 --- a/src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png +++ b/src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 3f44a977c9..e5685dc5e0 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -41,6 +41,7 @@ #include <QDir> #include <QRegularExpression> +#include <QtGlobal> namespace CppTools { @@ -701,6 +702,10 @@ void CompilerOptionsBuilder::reset() // QMakeProject: -pipe -Whello -g -std=gnu++11 -Wall -W -D_REENTRANT -fPIC void CompilerOptionsBuilder::evaluateCompilerFlags() { + static QStringList userBlackList = QString::fromLocal8Bit( + qgetenv("QTC_CLANG_CMD_OPTIONS_BLACKLIST")) + .split(';', QString::SkipEmptyParts); + bool containsDriverMode = false; bool skipNext = false; for (const QString &option : m_projectPart.compilerFlags) { @@ -709,6 +714,9 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() continue; } + if (userBlackList.contains(option)) + continue; + // Ignore warning flags as these interfere with our user-configured diagnostics. // Note that once "-w" is provided, no warnings will be emitted, even if "-Wall" follows. if (m_useBuildSystemWarnings == UseBuildSystemWarnings::No diff --git a/src/plugins/cpptools/cpphoverhandler.cpp b/src/plugins/cpptools/cpphoverhandler.cpp index f65e701c03..07617a411f 100644 --- a/src/plugins/cpptools/cpphoverhandler.cpp +++ b/src/plugins/cpptools/cpphoverhandler.cpp @@ -26,6 +26,7 @@ #include "cpphoverhandler.h" #include "cppelementevaluator.h" +#include "cpptoolsreuse.h" #include <coreplugin/helpmanager.h> #include <texteditor/texteditor.h> @@ -56,14 +57,16 @@ void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, Rep tip += evaluator.diagnosis(); setPriority(Priority_Diagnostic); } + const QStringList fallback = identifierWordsUnderCursor(tc); if (evaluator.identifiedCppElement()) { const QSharedPointer<CppElement> &cppElement = evaluator.cppElement(); - QStringList candidates = cppElement->helpIdCandidates; - candidates.removeDuplicates(); - const HelpItem helpItem(candidates, cppElement->helpMark, cppElement->helpCategory); + const QStringList candidates = cppElement->helpIdCandidates; + const HelpItem helpItem(candidates + fallback, cppElement->helpMark, cppElement->helpCategory); setLastHelpItemIdentified(helpItem); // tool tip appended by decorateToolTip if (!helpItem.isValid()) tip += cppElement->tooltip; + } else { + setLastHelpItemIdentified({fallback, {}, HelpItem::Unknown}); } setToolTip(tip); } diff --git a/src/plugins/cpptools/cpptoolsreuse.cpp b/src/plugins/cpptools/cpptoolsreuse.cpp index c56bfb9160..9bcf806b7d 100644 --- a/src/plugins/cpptools/cpptoolsreuse.cpp +++ b/src/plugins/cpptools/cpptoolsreuse.cpp @@ -41,6 +41,7 @@ #include <utils/qtcassert.h> #include <QDebug> +#include <QRegularExpression> #include <QSet> #include <QStringRef> #include <QTextCursor> @@ -50,29 +51,91 @@ using namespace CPlusPlus; namespace CppTools { -static void moveCursorToStartOrEndOfIdentifier(QTextCursor *tc, - QTextCursor::MoveOperation op, - int posDiff = 0) +static int skipChars(QTextCursor *tc, + QTextCursor::MoveOperation op, + int offset, + std::function<bool(const QChar &)> skip) { - QTextDocument *doc = tc->document(); + const QTextDocument *doc = tc->document(); if (!doc) - return; - - QChar ch = doc->characterAt(tc->position() - posDiff); - while (isValidIdentifierChar(ch)) { - tc->movePosition(op); - ch = doc->characterAt(tc->position() - posDiff); + return 0; + QChar ch = doc->characterAt(tc->position() + offset); + if (ch.isNull()) + return 0; + int count = 0; + while (skip(ch)) { + if (tc->movePosition(op)) + ++count; + else + break; + ch = doc->characterAt(tc->position() + offset); } + return count; +} + +static int skipCharsForward(QTextCursor *tc, std::function<bool(const QChar &)> skip) +{ + return skipChars(tc, QTextCursor::NextCharacter, 0, skip); +} + +static int skipCharsBackward(QTextCursor *tc, std::function<bool(const QChar &)> skip) +{ + return skipChars(tc, QTextCursor::PreviousCharacter, -1, skip); +} + +QStringList identifierWordsUnderCursor(const QTextCursor &tc) +{ + const QTextDocument *document = tc.document(); + if (!document) + return {}; + const auto isSpace = [](const QChar &c) { return c.isSpace(); }; + const auto isColon = [](const QChar &c) { return c == ':'; }; + const auto isValidIdentifierCharAt = [document](const QTextCursor &tc) { + return isValidIdentifierChar(document->characterAt(tc.position())); + }; + // move to the end + QTextCursor endCursor(tc); + do { + moveCursorToEndOfIdentifier(&endCursor); + // possibly skip :: + QTextCursor temp(endCursor); + skipCharsForward(&temp, isSpace); + const int colons = skipCharsForward(&temp, isColon); + skipCharsForward(&temp, isSpace); + if (colons == 2 && isValidIdentifierCharAt(temp)) + endCursor = temp; + } while (isValidIdentifierCharAt(endCursor)); + + QStringList results; + QTextCursor startCursor(endCursor); + do { + moveCursorToStartOfIdentifier(&startCursor); + if (startCursor.position() == endCursor.position()) + break; + QTextCursor temp(endCursor); + temp.setPosition(startCursor.position(), QTextCursor::KeepAnchor); + results.append(temp.selectedText().remove(QRegularExpression("\\s"))); + // possibly skip :: + temp = startCursor; + skipCharsBackward(&temp, isSpace); + const int colons = skipCharsBackward(&temp, isColon); + skipCharsBackward(&temp, isSpace); + if (colons == 2 + && isValidIdentifierChar(document->characterAt(temp.position() - 1))) { + startCursor = temp; + } + } while (!isValidIdentifierCharAt(startCursor)); + return results; } void moveCursorToEndOfIdentifier(QTextCursor *tc) { - moveCursorToStartOrEndOfIdentifier(tc, QTextCursor::NextCharacter); + skipCharsForward(tc, isValidIdentifierChar); } void moveCursorToStartOfIdentifier(QTextCursor *tc) { - moveCursorToStartOrEndOfIdentifier(tc, QTextCursor::PreviousCharacter, 1); + skipCharsBackward(tc, isValidIdentifierChar); } static bool isOwnershipRAIIName(const QString &name) diff --git a/src/plugins/cpptools/cpptoolsreuse.h b/src/plugins/cpptools/cpptoolsreuse.h index d1a1956be3..92e1365d15 100644 --- a/src/plugins/cpptools/cpptoolsreuse.h +++ b/src/plugins/cpptools/cpptoolsreuse.h @@ -58,6 +58,7 @@ bool CPPTOOLS_EXPORT isValidFirstIdentifierChar(const QChar &ch); bool CPPTOOLS_EXPORT isValidIdentifierChar(const QChar &ch); bool CPPTOOLS_EXPORT isValidIdentifier(const QString &s); +QStringList CPPTOOLS_EXPORT identifierWordsUnderCursor(const QTextCursor &tc); QString CPPTOOLS_EXPORT identifierUnderCursor(QTextCursor *cursor); bool CPPTOOLS_EXPORT isOwnershipRAIIType(CPlusPlus::Symbol *symbol, diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index d401f452ac..dde08c6794 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -69,6 +69,7 @@ class TextMark : public TextEditor::TextMark public: TextMark(const Utils::FileName &fileName, const Diagnostic &diag) : TextEditor::TextMark(fileName, diag.range().start().line() + 1, "lspmark") + , m_diagnostic(diag) { using namespace Utils; setLineAnnotation(diag.message()); @@ -81,11 +82,17 @@ public: setIcon(isError ? Icons::CODEMODEL_ERROR.icon() : Icons::CODEMODEL_WARNING.icon()); } + + const Diagnostic &diagnostic() const { return m_diagnostic; } + +private: + const Diagnostic m_diagnostic; }; Client::Client(BaseClientInterface *clientInterface) : m_id(Core::Id::fromString(QUuid::createUuid().toString())) , m_completionProvider(this) + , m_quickFixProvider(this) , m_clientInterface(clientInterface) { m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(), @@ -101,8 +108,10 @@ Client::~Client() using namespace TextEditor; // FIXME: instead of replacing the completion provider in the text document store the // completion provider as a prioritised list in the text document - for (TextDocument *document : m_resetCompletionProvider) + for (TextDocument *document : m_resetAssistProvider) { document->setCompletionAssistProvider(nullptr); + document->setQuickFixAssistProvider(nullptr); + } for (Core::IEditor * editor : Core::DocumentModel::editorsForOpenedDocuments()) { if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) { TextEditorWidget *widget = textEditor->editorWidget(); @@ -192,10 +201,12 @@ void Client::openDocument(Core::IDocument *document) documentContentsChanged(document); }); if (textDocument) { - m_resetCompletionProvider << textDocument; + textDocument->completionAssistProvider(); + m_resetAssistProvider << textDocument; textDocument->setCompletionAssistProvider(&m_completionProvider); + textDocument->setQuickFixAssistProvider(&m_quickFixProvider); connect(textDocument, &QObject::destroyed, this, [this, textDocument]{ - m_resetCompletionProvider.remove(textDocument); + m_resetAssistProvider.remove(textDocument); }); if (BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(textDocument)) { if (QPointer<TextEditorWidget> widget = editor->editorWidget()) { @@ -543,25 +554,10 @@ void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget) void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics) { const Utils::FileName fileName = uri.toFileName(); - TextEditor::TextDocument *doc = textDocumentForFileName(fileName); + TextEditor::TextDocument *doc = TextEditor::TextDocument::textDocumentForFileName(fileName); if (!doc) return; - const QString method(CodeActionRequest::methodName); - if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - if (!registered.value()) - return; - const TextDocumentRegistrationOptions option( - m_dynamicCapabilities.option(method).toObject()); - if (option.isValid(nullptr) && !option.filterApplies(fileName)) - return; - } else { - Utils::variant<bool, CodeActionOptions> provider - = m_serverCapabilities.codeActionProvider().value_or(false); - if (!(Utils::holds_alternative<CodeActionOptions>(provider) || Utils::get<bool>(provider))) - return; - } - CodeActionParams codeActionParams; CodeActionParams::CodeActionContext context; context.setDiagnostics(diagnostics); @@ -577,6 +573,32 @@ void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> if (self) self->handleCodeActionResponse(response, uri); }); + requestCodeActions(request); +} + +void Client::requestCodeActions(const CodeActionRequest &request) +{ + if (!request.isValid(nullptr)) + return; + + const Utils::FileName fileName + = request.params().value_or(CodeActionParams()).textDocument().uri().toFileName(); + + const QString method(CodeActionRequest::methodName); + if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { + if (!registered.value()) + return; + const TextDocumentRegistrationOptions option( + m_dynamicCapabilities.option(method).toObject()); + if (option.isValid(nullptr) && !option.filterApplies(fileName)) + return; + } else { + Utils::variant<bool, CodeActionOptions> provider + = m_serverCapabilities.codeActionProvider().value_or(false); + if (!(Utils::holds_alternative<CodeActionOptions>(provider) || Utils::get<bool>(provider))) + return; + } + sendContent(request); } @@ -682,6 +704,17 @@ bool Client::needsRestart(const BaseSettings *settings) const || m_languagFilter.filePattern != settings->m_languageFilter.filePattern; } +QList<Diagnostic> Client::diagnosticsAt(const DocumentUri &uri, const Range &range) const +{ + QList<Diagnostic> diagnostics; + for (const TextMark *mark : m_diagnostics[uri]) { + const Diagnostic diagnostic = mark->diagnostic(); + if (diagnostic.range().overlaps(range)) + diagnostics << diagnostic; + } + return diagnostics; +} + bool Client::start() { return m_clientInterface->start(); @@ -778,7 +811,8 @@ void Client::showMessageBox(const ShowMessageRequestParams &message, const Messa void Client::showDiagnostics(const DocumentUri &uri) { - if (TextEditor::TextDocument *doc = textDocumentForFileName(uri.toFileName())) { + if (TextEditor::TextDocument *doc + = TextEditor::TextDocument::textDocumentForFileName(uri.toFileName())) { for (TextMark *mark : m_diagnostics.value(uri)) doc->addMark(mark); } @@ -786,7 +820,8 @@ void Client::showDiagnostics(const DocumentUri &uri) void Client::removeDiagnostics(const DocumentUri &uri) { - TextEditor::TextDocument *doc = textDocumentForFileName(uri.toFileName()); + TextEditor::TextDocument *doc + = TextEditor::TextDocument::textDocumentForFileName(uri.toFileName()); for (TextMark *mark : m_diagnostics.take(uri)) { if (doc) diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 3d691f2bd2..2d92424f42 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -26,7 +26,8 @@ #pragma once #include "dynamiccapabilities.h" -#include "languageclientcodeassist.h" +#include "languageclientcompletionassist.h" +#include "languageclientquickfix.h" #include "languageclientsettings.h" #include <coreplugin/id.h> @@ -104,6 +105,7 @@ public: void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, const QList<LanguageServerProtocol::Diagnostic> &diagnostics); + void requestCodeActions(const LanguageServerProtocol::CodeActionRequest &request); void handleCodeActionResponse(const LanguageServerProtocol::CodeActionRequest::Response &response, const LanguageServerProtocol::DocumentUri &uri); void executeCommand(const LanguageServerProtocol::Command &command); @@ -129,6 +131,10 @@ public: bool needsRestart(const BaseSettings *) const; + QList<LanguageServerProtocol::Diagnostic> diagnosticsAt( + const LanguageServerProtocol::DocumentUri &uri, + const LanguageServerProtocol::Range &range) const; + bool start(); bool reset(); @@ -184,7 +190,8 @@ private: LanguageServerProtocol::ServerCapabilities m_serverCapabilities; DynamicCapabilities m_dynamicCapabilities; LanguageClientCompletionAssistProvider m_completionProvider; - QSet<TextEditor::TextDocument *> m_resetCompletionProvider; + LanguageClientQuickFixProvider m_quickFixProvider; + QSet<TextEditor::TextDocument *> m_resetAssistProvider; QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests; int m_restartsLeft = 5; QScopedPointer<BaseClientInterface> m_clientInterface; diff --git a/src/plugins/languageclient/languageclient.pro b/src/plugins/languageclient/languageclient.pro index 59c568a75b..dbae20e109 100644 --- a/src/plugins/languageclient/languageclient.pro +++ b/src/plugins/languageclient/languageclient.pro @@ -6,11 +6,12 @@ HEADERS += \ client.h \ dynamiccapabilities.h \ languageclient_global.h \ - languageclientcodeassist.h \ + languageclientcompletionassist.h \ languageclientinterface.h \ languageclientmanager.h \ languageclientoutline.h \ languageclientplugin.h \ + languageclientquickfix.h \ languageclientsettings.h \ languageclientutils.h @@ -18,11 +19,12 @@ HEADERS += \ SOURCES += \ client.cpp \ dynamiccapabilities.cpp \ - languageclientcodeassist.cpp \ + languageclientcompletionassist.cpp \ languageclientinterface.cpp \ languageclientmanager.cpp \ languageclientoutline.cpp \ languageclientplugin.cpp \ + languageclientquickfix.cpp \ languageclientsettings.cpp \ languageclientutils.cpp diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs index 0da6a58076..0c2752ef5e 100644 --- a/src/plugins/languageclient/languageclient.qbs +++ b/src/plugins/languageclient/languageclient.qbs @@ -20,16 +20,18 @@ QtcPlugin { "dynamiccapabilities.h", "languageclient.qrc", "languageclient_global.h", - "languageclientcodeassist.cpp", - "languageclientcodeassist.h", "languageclientinterface.cpp", "languageclientinterface.h", + "languageclientcompletionassist.cpp", + "languageclientcompletionassist.h", "languageclientmanager.cpp", "languageclientmanager.h", "languageclientoutline.cpp", "languageclientoutline.h", "languageclientplugin.cpp", "languageclientplugin.h", + "languageclientquickfix.cpp", + "languageclientquickfix.h", "languageclientsettings.cpp", "languageclientsettings.h", "languageclientutils.cpp", diff --git a/src/plugins/languageclient/languageclientcodeassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp index af0e9ce892..1d9a10f6e4 100644 --- a/src/plugins/languageclient/languageclientcodeassist.cpp +++ b/src/plugins/languageclient/languageclientcompletionassist.cpp @@ -23,7 +23,7 @@ ** ****************************************************************************/ -#include "languageclientcodeassist.h" +#include "languageclientcompletionassist.h" #include "client.h" #include "languageclientutils.h" diff --git a/src/plugins/languageclient/languageclientcodeassist.h b/src/plugins/languageclient/languageclientcompletionassist.h index 1410e67f82..f0ad95bc88 100644 --- a/src/plugins/languageclient/languageclientcodeassist.h +++ b/src/plugins/languageclient/languageclientcompletionassist.h @@ -48,7 +48,7 @@ public: private: QList<QString> m_triggerChars; int m_activationCharSequenceLength = 0; - Client *m_client; + Client *m_client = nullptr; // not owned }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientquickfix.cpp b/src/plugins/languageclient/languageclientquickfix.cpp new file mode 100644 index 0000000000..def01b17d8 --- /dev/null +++ b/src/plugins/languageclient/languageclientquickfix.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#include "languageclientquickfix.h" + +#include "client.h" +#include "languageclientutils.h" + +#include <texteditor/codeassist/assistinterface.h> +#include <texteditor/codeassist/genericproposal.h> +#include <texteditor/codeassist/iassistprocessor.h> +#include <texteditor/quickfix.h> + + +using namespace LanguageServerProtocol; +using namespace TextEditor; + +namespace LanguageClient { + +class CodeActionQuickFixOperation : public QuickFixOperation +{ +public: + CodeActionQuickFixOperation(const CodeAction &action, Client *client) + : m_action(action) + , m_client(client) + { setDescription(action.title()); } + + void perform() override + { + if (Utils::optional<WorkspaceEdit> edit = m_action.edit()) { + applyWorkspaceEdit(*edit); + } else if (Utils::optional<Command> command = m_action.command()) { + if (m_client) + m_client->executeCommand(*command); + } + } + +private: + CodeAction m_action; + QPointer<Client> m_client; +}; + +class CommandQuickFixOperation : public QuickFixOperation +{ +public: + CommandQuickFixOperation(const Command &command, Client *client) + : m_command(command) + , m_client(client) + { setDescription(command.title()); } + void perform() override + { + if (m_client) + m_client->executeCommand(m_command); + } + +private: + Command m_command; + QPointer<Client> m_client; +}; + +class LanguageClientQuickFixAssistProcessor : public IAssistProcessor +{ +public: + explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {} + bool running() override { return m_running; } + IAssistProposal *perform(const AssistInterface *interface) override; + +private: + void handleCodeActionResponse(const CodeActionRequest::Response &response); + + QSharedPointer<const AssistInterface> m_assistInterface; + Client *m_client = nullptr; // not owned + bool m_running = false; +}; + +IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInterface *interface) +{ + m_assistInterface = QSharedPointer<const AssistInterface>(interface); + + CodeActionParams params; + params.setContext({}); + QTextCursor cursor(interface->textDocument()); + cursor.setPosition(interface->position()); + if (cursor.atBlockEnd() || cursor.atBlockStart()) + cursor.select(QTextCursor::LineUnderCursor); + else + cursor.select(QTextCursor::WordUnderCursor); + if (!cursor.hasSelection()) + cursor.select(QTextCursor::LineUnderCursor); + Range range(cursor); + params.setRange(range); + auto uri = DocumentUri::fromFileName(Utils::FileName::fromString(interface->fileName())); + params.setTextDocument(uri); + CodeActionParams::CodeActionContext context; + context.setDiagnostics(m_client->diagnosticsAt(uri, range)); + params.setContext(context); + + CodeActionRequest request(params); + request.setResponseCallback([this](const CodeActionRequest::Response &response){ + handleCodeActionResponse(response); + }); + + m_client->requestCodeActions(request); + m_running = true; + return nullptr; +} + +void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse( + const CodeActionRequest::Response &response) +{ + m_running = false; + if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error()) + m_client->log(*error); + QuickFixOperations ops; + if (const Utils::optional<CodeActionResult> &_result = response.result()) { + const CodeActionResult &result = _result.value(); + if (auto list = Utils::get_if<QList<Utils::variant<Command, CodeAction>>>(&result)) { + for (const Utils::variant<Command, CodeAction> &item : *list) { + if (auto action = Utils::get_if<CodeAction>(&item)) + ops << new CodeActionQuickFixOperation(*action, m_client); + else if (auto command = Utils::get_if<Command>(&item)) + ops << new CommandQuickFixOperation(*command, m_client); + } + } + } + setAsyncProposalAvailable(GenericProposal::createProposal(m_assistInterface.data(), ops)); +} + +LanguageClientQuickFixProvider::LanguageClientQuickFixProvider(Client *client) : m_client(client) +{ + QTC_CHECK(client); +} + +IAssistProvider::RunType LanguageClientQuickFixProvider::runType() const +{ + return Asynchronous; +} + +IAssistProcessor *LanguageClientQuickFixProvider::createProcessor() const +{ + return new LanguageClientQuickFixAssistProcessor(m_client); +} + +} // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientquickfix.h b/src/plugins/languageclient/languageclientquickfix.h new file mode 100644 index 0000000000..d9b322769f --- /dev/null +++ b/src/plugins/languageclient/languageclientquickfix.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** 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 <texteditor/codeassist/iassistprovider.h> + +namespace LanguageClient { + +class Client; + +class LanguageClientQuickFixProvider : public TextEditor::IAssistProvider +{ +public: + explicit LanguageClientQuickFixProvider(Client *client); + IAssistProvider::RunType runType() const override; + TextEditor::IAssistProcessor *createProcessor() const override; + +private: + Client *m_client = nullptr; // not owned +}; + +} // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index e101db412c..17ea39f80e 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -34,9 +34,8 @@ #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 <utils/jsontreeitem.h> #include <QBoxLayout> #include <QCheckBox> @@ -150,7 +149,6 @@ LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClien connect(addButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::addItem); auto deleteButton = new QPushButton(LanguageClientSettingsPage::tr("&Delete")); connect(deleteButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::deleteItem); - mainLayout->addLayout(layout); setLayout(mainLayout); layout->addWidget(m_view); @@ -505,6 +503,24 @@ BaseClientInterface *StdIOSettings::createInterface() const return new StdIOClientInterface(m_executable, m_arguments); } +static QWidget *createCapabilitiesView( + const LanguageServerProtocol::ServerCapabilities &capabilities) +{ + auto root = new Utils::JsonTreeItem("Capabilities", QJsonValue(capabilities)); + if (root->canFetchMore()) + root->fetchMore(); + + auto capabilitiesModel = new Utils::TreeModel<Utils::JsonTreeItem>(root); + capabilitiesModel->setHeader({BaseSettingsWidget::tr("Name"), + BaseSettingsWidget::tr("Value"), + BaseSettingsWidget::tr("Type")}); + auto capabilitiesView = new QTreeView(); + capabilitiesView->setModel(capabilitiesModel); + capabilitiesView->setAlternatingRowColors(true); + capabilitiesView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + return capabilitiesView; +} + BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent) : QWidget(parent) , m_name(new QLineEdit(settings->m_name, this)) @@ -528,6 +544,30 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa connect(addMimeTypeButton, &QPushButton::pressed, this, &BaseSettingsWidget::showAddMimeTypeDialog); + auto createInfoLabel = []() { + return new QLabel(tr("Available after server was initialized")); + }; + + mainLayout->addWidget(new QLabel(tr("Capabilities:")), ++row, 0, Qt::AlignTop); + if (Client *client = settings->m_client.data()) { + if (client->state() == Client::Initialized) + mainLayout->addWidget(createCapabilitiesView(client->capabilities())); + else + mainLayout->addWidget(createInfoLabel(), row, 1); + connect(client, &Client::finished, mainLayout, [mainLayout, row, createInfoLabel]() { + delete mainLayout->itemAtPosition(row, 1)->widget(); + mainLayout->addWidget(createInfoLabel(), row, 1); + }); + connect(client, &Client::initialized, mainLayout, + [mainLayout, row]( + const LanguageServerProtocol::ServerCapabilities &capabilities) { + delete mainLayout->itemAtPosition(row, 1)->widget(); + mainLayout->addWidget(createCapabilitiesView(capabilities), row, 1); + }); + } else { + mainLayout->addWidget(createInfoLabel()); + } + setLayout(mainLayout); } diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index 5f3f8e0672..f81be2427c 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -40,6 +40,7 @@ using namespace LanguageServerProtocol; using namespace Utils; +using namespace TextEditor; namespace LanguageClient { @@ -72,7 +73,7 @@ bool applyTextDocumentEdit(const TextDocumentEdit &edit) if (edits.isEmpty()) return true; const DocumentUri &uri = edit.id().uri(); - if (TextEditor::TextDocument* doc = textDocumentForFileName(uri.toFileName())) { + if (TextDocument* doc = TextDocument::textDocumentForFileName(uri.toFileName())) { LanguageClientValue<int> version = edit.id().version(); if (!version.isNull() && version.value(0) < doc->document()->revision()) return false; @@ -84,14 +85,14 @@ bool applyTextEdits(const DocumentUri &uri, const QList<TextEdit> &edits) { if (edits.isEmpty()) return true; - TextEditor::RefactoringChanges changes; - TextEditor::RefactoringFilePtr file; + RefactoringChanges changes; + RefactoringFilePtr file; file = changes.file(uri.toFileName().toString()); file->setChangeSet(editsToChangeSet(edits, file->document())); return file->apply(); } -void applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator, const TextEdit &edit) +void applyTextEdit(TextDocumentManipulatorInterface &manipulator, const TextEdit &edit) { using namespace Utils::Text; const Range range = edit.range(); @@ -118,12 +119,6 @@ bool applyWorkspaceEdit(const WorkspaceEdit &edit) return result; } -TextEditor::TextDocument *textDocumentForFileName(const FileName &fileName) -{ - return qobject_cast<TextEditor::TextDocument *>( - Core::DocumentModel::documentForFilePath(fileName.toString())); -} - QTextCursor endOfLineCursor(const QTextCursor &cursor) { QTextCursor ret = cursor; @@ -135,8 +130,7 @@ void updateCodeActionRefactoringMarker(Client *client, const CodeAction &action, const DocumentUri &uri) { - using namespace TextEditor; - TextDocument* doc = textDocumentForFileName(uri.toFileName()); + TextDocument* doc = TextDocument::textDocumentForFileName(uri.toFileName()); if (!doc) return; BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(doc); diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index 2891c2d303..f07b6e18bf 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -45,7 +45,6 @@ bool applyTextEdits(const LanguageServerProtocol::DocumentUri &uri, const QList<LanguageServerProtocol::TextEdit> &edits); void applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator, const LanguageServerProtocol::TextEdit &edit); -TextEditor::TextDocument *textDocumentForFileName(const Utils::FileName &fileName); void updateCodeActionRefactoringMarker(Client *client, const LanguageServerProtocol::CodeAction &action, const LanguageServerProtocol::DocumentUri &uri); diff --git a/src/plugins/perfprofiler/perfdatareader.cpp b/src/plugins/perfprofiler/perfdatareader.cpp index 3fec773bad..c137e87b8c 100644 --- a/src/plugins/perfprofiler/perfdatareader.cpp +++ b/src/plugins/perfprofiler/perfdatareader.cpp @@ -77,7 +77,8 @@ PerfDataReader::PerfDataReader(QObject *parent) : QMessageBox::warning(Core::ICore::mainWindow(), tr("Perf data parser failed"), tr("The perf data parser failed to process all the samples. " - "Your trace is incomplete.")); + "Your trace is incomplete. The exit code was %1.") + .arg(exitCode)); } }); @@ -120,6 +121,7 @@ PerfDataReader::PerfDataReader(QObject *parent) : qWarning() << "Cannot send data to perfparser"; break; case QProcess::Timedout: + qWarning() << "QProcess::Timedout"; default: break; } @@ -349,8 +351,10 @@ void PerfDataReader::writeChunk() "Your trace is incomplete.")); } } - } else if (m_dataFinished) { - m_input.closeWriteChannel(); + } else if (m_dataFinished && m_input.isWritable()) { + // Delay closing of the write channel. Closing the channel from within a handler + // for bytesWritten() is dangerous on windows. + QTimer::singleShot(0, &m_input, &QProcess::closeWriteChannel); } } diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp index 8e384b2036..41cbe1763b 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.cpp @@ -46,7 +46,6 @@ #include <QVariantMap> #include <qbs.h> -#include <qtprofilesetup.h> const QChar sep = QLatin1Char('.'); @@ -166,45 +165,9 @@ void QbsManager::addProfile(const QString &name, const QVariantMap &data) void QbsManager::addQtProfileFromKit(const QString &profileName, const ProjectExplorer::Kit *k) { - const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k); - if (!qt) - return; - - qbs::QtEnvironment qtEnv; - const QList<ProjectExplorer::Abi> abi = qt->qtAbis(); - if (!abi.empty()) { - qtEnv.architecture = ProjectExplorer::Abi::toString(abi.first().architecture()); - if (abi.first().wordWidth() == 64) - qtEnv.architecture.append(QLatin1String("_64")); - } - qtEnv.binaryPath = qt->binPath().toString(); - qtEnv.documentationPath = qt->docsPath().toString(); - qtEnv.includePath = qt->headerPath().toString(); - qtEnv.libraryPath = qt->libraryPath().toString(); - qtEnv.pluginPath = qt->pluginPath().toString(); - qtEnv.mkspecBasePath = qt->mkspecsPath().toString(); - qtEnv.mkspecName = qt->mkspec().toString(); - qtEnv.mkspecPath = qt->mkspecPath().toString(); - qtEnv.qtNameSpace = qt->qtNamespace(); - qtEnv.qtLibInfix = qt->qtLibInfix(); - qtEnv.qtVersion = qt->qtVersionString(); - qtEnv.qtMajorVersion = qt->qtVersion().majorVersion; - qtEnv.qtMinorVersion = qt->qtVersion().minorVersion; - qtEnv.qtPatchVersion = qt->qtVersion().patchVersion; - qtEnv.frameworkBuild = qt->isFrameworkBuild(); - qtEnv.configItems = qt->configValues(); - qtEnv.qtConfigItems = qt->qtConfigValues(); - foreach (const QString &buildVariant, - QStringList() << QLatin1String("debug") << QLatin1String("release")) { - if (qtEnv.qtConfigItems.contains(buildVariant)) - qtEnv.buildVariant << buildVariant; - } - qtEnv.qmlPath = qt->qmlPath().toString(); - qtEnv.qmlImportPath = qt->qmakeProperty("QT_INSTALL_IMPORTS"); - const qbs::ErrorInfo errorInfo = qbs::setupQtProfile(profileName, settings(), qtEnv); - if (errorInfo.hasError()) { - Core::MessageManager::write(tr("Failed to set up kit for Qbs: %1") - .arg(errorInfo.toString()), Core::MessageManager::ModeSwitch); + if (const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k)) { + qbs::Profile(profileName, settings()).setValue("moduleProviders.Qt.qmakeFilePaths", + qt->qmakeCommand().toString()); } } diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.pro b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro index d5d0b1b79a..bef7cd2a05 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.pro +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.pro @@ -5,11 +5,9 @@ isEmpty(QBS_INSTALL_DIR): QBS_INSTALL_DIR = $$(QBS_INSTALL_DIR) isEmpty(QBS_INSTALL_DIR) { QBS_SOURCE_DIR = $$PWD/../../shared/qbs include($$QBS_SOURCE_DIR/src/lib/corelib/use_corelib.pri) - include($$QBS_SOURCE_DIR/src/lib/qtprofilesetup/use_qtprofilesetup.pri) osx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../Frameworks # OS X: fix rpath for qbscore soname } else { include($${QBS_INSTALL_DIR}/include/qbs/use_installed_corelib.pri) - include($${QBS_INSTALL_DIR}/include/qbs/use_installed_qtprofilesetup.pri) } QBS_INSTALL_DIR_FWD_SLASHES = $$replace(QBS_INSTALL_DIR, \\\\, /) DEFINES += QBS_INSTALL_DIR=\\\"$$QBS_INSTALL_DIR_FWD_SLASHES\\\" diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs index 6c7793db55..45e5d1f3a4 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs @@ -20,7 +20,7 @@ QtcPlugin { if (qbs.enableDebugCode) suffix = "d"; } - libs.push("qbscore" + suffix, "qbsqtprofilesetup" + suffix); + libs.push("qbscore" + suffix); return libs } @@ -33,10 +33,6 @@ QtcPlugin { name: "qbscore" condition: product.useInternalQbsProducts } - Depends { - name: "qbsqtprofilesetup" - condition: product.useInternalQbsProducts - } Depends { name: "QmlJS" } Depends { name: "Utils" } diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 67abdece26..8b790c3351 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -322,10 +322,12 @@ protected: bool visit(AST::UiPublicMember *node) override { - if (node->memberType->name == _name){ - const ObjectValue * tVal = _context->lookupType(_doc.data(), QStringList(_name)); - if (tVal == _typeValue) - _usages.append(node->typeToken); + if (UiQualifiedId *memberType = node->memberType) { + if (memberType->name == _name) { + const ObjectValue * tVal = _context->lookupType(_doc.data(), QStringList(_name)); + if (tVal == _typeValue) + _usages.append(node->typeToken); + } } if (AST::cast<Block *>(node->statement)) { _builder.push(node); diff --git a/src/plugins/remotelinux/linuxdeviceprocess.cpp b/src/plugins/remotelinux/linuxdeviceprocess.cpp index 1dac2e4408..004ca63d2a 100644 --- a/src/plugins/remotelinux/linuxdeviceprocess.cpp +++ b/src/plugins/remotelinux/linuxdeviceprocess.cpp @@ -42,6 +42,9 @@ LinuxDeviceProcess::LinuxDeviceProcess(const QSharedPointer<const ProjectExplore : ProjectExplorer::SshDeviceProcess(device, parent), m_processId(0) { connect(this, &DeviceProcess::finished, this, [this]() { + m_processId = -1; + }); + connect(this, &DeviceProcess::started, this, [this]() { m_processId = 0; }); } @@ -70,7 +73,7 @@ QByteArray LinuxDeviceProcess::readAllStandardOutput() qint64 LinuxDeviceProcess::processId() const { - return m_processId; + return m_processId < 0 ? 0 : m_processId; } QString LinuxDeviceProcess::fullCommandLine(const Runnable &runnable) const diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 98096daee9..2bfb51439a 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -100,6 +100,7 @@ public: QTextDocument m_document; SyntaxHighlighter *m_highlighter = nullptr; CompletionAssistProvider *m_completionAssistProvider = nullptr; + IAssistProvider *m_quickFixProvider = nullptr; QScopedPointer<Indenter> m_indenter; bool m_fileIsReadOnly = false; @@ -298,6 +299,11 @@ TextDocument *TextDocument::currentTextDocument() return qobject_cast<TextDocument *>(EditorManager::currentDocument()); } +TextDocument *TextDocument::textDocumentForFileName(const Utils::FileName &fileName) +{ + return qobject_cast<TextDocument *>(DocumentModel::documentForFilePath(fileName.toString())); +} + QString TextDocument::plainText() const { return document()->toPlainText(); @@ -388,9 +394,14 @@ CompletionAssistProvider *TextDocument::completionAssistProvider() const return d->m_completionAssistProvider; } +void TextDocument::setQuickFixAssistProvider(IAssistProvider *provider) const +{ + d->m_quickFixProvider = provider; +} + IAssistProvider *TextDocument::quickFixAssistProvider() const { - return nullptr; + return d->m_quickFixProvider; } void TextDocument::applyFontSettings() diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index b7c771e748..9ea1751b11 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -70,6 +70,7 @@ public: static QMap<QString, QString> openedTextDocumentContents(); static QMap<QString, QTextCodec *> openedTextDocumentEncodings(); static TextDocument *currentTextDocument(); + static TextDocument *textDocumentForFileName(const Utils::FileName &fileName); virtual QString plainText() const; virtual QString textAt(int pos, int length) const; @@ -140,6 +141,7 @@ public: void setCompletionAssistProvider(CompletionAssistProvider *provider); virtual CompletionAssistProvider *completionAssistProvider() const; + void setQuickFixAssistProvider(IAssistProvider *provider) const; virtual IAssistProvider *quickFixAssistProvider() const; void setTabSettings(const TextEditor::TabSettings &tabSettings); diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index 8995167564..22ddbbec03 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -361,11 +361,8 @@ TextMarkRegistry::TextMarkRegistry(QObject *parent) void TextMarkRegistry::add(TextMark *mark) { instance()->m_marks[mark->fileName()].insert(mark); - auto document = qobject_cast<TextDocument *>( - DocumentModel::documentForFilePath(mark->fileName().toString())); - if (!document) - return; - document->addMark(mark); + if (TextDocument *document = TextDocument::textDocumentForFileName(mark->fileName())) + document->addMark(mark); } bool TextMarkRegistry::remove(TextMark *mark) diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp index d0c993b6e8..d10cb76be2 100644 --- a/src/plugins/welcome/introductionwidget.cpp +++ b/src/plugins/welcome/introductionwidget.cpp @@ -31,6 +31,7 @@ #include <utils/stylehelper.h> #include <QEvent> +#include <QGuiApplication> #include <QKeyEvent> #include <QLabel> #include <QPainter> @@ -349,8 +350,17 @@ void IntroductionWidget::keyPressEvent(QKeyEvent *ke) { if (ke->key() == Qt::Key_Escape) finish(); - else if (ke->modifiers() == Qt::NoModifier) - step(); + else if ((ke->modifiers() + & (Qt::ControlModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::MetaModifier)) + == Qt::NoModifier) { + const Qt::Key backKey = QGuiApplication::isLeftToRight() ? Qt::Key_Left : Qt::Key_Right; + if (ke->key() == backKey) { + if (m_step > 0) + setStep(m_step - 1); + } else { + step(); + } + } } void IntroductionWidget::mouseReleaseEvent(QMouseEvent *me) |