diff --git a/kdevplatform/interfaces/icompletionsettings.h b/kdevplatform/interfaces/icompletionsettings.h index 612179e13e..c8506682d2 100644 --- a/kdevplatform/interfaces/icompletionsettings.h +++ b/kdevplatform/interfaces/icompletionsettings.h @@ -1,65 +1,73 @@ /*************************************************************************** * Copyright 2008 David Nolden * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_ICOMPLETIONSETTINGS_H #define KDEVPLATFORM_ICOMPLETIONSETTINGS_H #include #include "interfacesexport.h" namespace KDevelop { class KDEVPLATFORMINTERFACES_EXPORT ICompletionSettings : public QObject { Q_OBJECT public: ~ICompletionSettings() override; enum CompletionLevel { Minimal, MinimalWhenAutomatic, AlwaysFull, LAST_LEVEL }; + enum ProblemInlineNotesLevel { + NoProblemsInlineNotesLevel, + ErrorsProblemInlineNotesLevel, + WarningsAndErrorsProblemInlineNotesLevel, + AllProblemsInlineNotesLevel + }; + virtual int minFilesForSimplifiedParsing() const = 0; virtual CompletionLevel completionLevel() const = 0; virtual bool automaticCompletionEnabled() const = 0; virtual int localColorizationLevel() const = 0; virtual int globalColorizationLevel() const = 0; virtual bool highlightSemanticProblems() const = 0; virtual bool highlightProblematicLines() const = 0; + virtual ProblemInlineNotesLevel problemInlineNotesLevel() const = 0; virtual bool boldDeclarations() const = 0; virtual bool showMultiLineSelectionInformation() const = 0; virtual QStringList todoMarkerWords() const = 0; Q_SIGNALS: void settingsChanged(ICompletionSettings*); }; } #endif diff --git a/kdevplatform/shell/completionsettings.cpp b/kdevplatform/shell/completionsettings.cpp index 68f2d2d697..b013ab2e0e 100644 --- a/kdevplatform/shell/completionsettings.cpp +++ b/kdevplatform/shell/completionsettings.cpp @@ -1,106 +1,111 @@ /*************************************************************************** * Copyright 2008 David Nolden * * Copyright 2013 Vlas Puhov * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "completionsettings.h" - +#include "languageconfig.h" #include #include namespace KDevelop { static QString completionLevelToString(ICompletionSettings::CompletionLevel l) { if (l < 0 || l >= ICompletionSettings::LAST_LEVEL) { return QString(); } const static QString levels[ICompletionSettings::LAST_LEVEL] = {QStringLiteral("Minimal"), QStringLiteral("MinimalWhenAutomatic"), QStringLiteral("AlwaysFull")}; return levels[l]; } CompletionSettings& CompletionSettings::self() { static CompletionSettings settings; return settings; } QStringList CompletionSettings::todoMarkerWords() const { const QString markers = m_languageGroup.readEntry("todoMarkerWords", m_todoMarkerWords); return KShell::splitArgs(markers); } int CompletionSettings::minFilesForSimplifiedParsing() const { return m_languageGroup.readEntry("minFilesForSimplifiedParsing", m_minFilesForSimplifiedParsing); } bool CompletionSettings::showMultiLineSelectionInformation() const { return m_languageGroup.readEntry("showMultiLineSelectionInformation", m_showMultiLineInformation); } bool CompletionSettings::highlightProblematicLines() const { return m_languageGroup.readEntry("highlightProblematicLines", m_highlightProblematicLines); } bool CompletionSettings::highlightSemanticProblems() const { return m_languageGroup.readEntry("highlightSemanticProblems", m_highlightSemanticProblems); } +ICompletionSettings::ProblemInlineNotesLevel CompletionSettings::problemInlineNotesLevel() const +{ + return LanguageConfig::problemInlineNotesLevel(); +} + bool CompletionSettings::boldDeclarations() const { return m_languageGroup.readEntry("boldDeclarations", m_boldDeclarations); } int CompletionSettings::globalColorizationLevel() const { return m_languageGroup.readEntry("globalColorization", m_globalColorizationLevel); } int CompletionSettings::localColorizationLevel() const { return m_languageGroup.readEntry("localColorization", m_localColorizationLevel); } bool CompletionSettings::automaticCompletionEnabled() const { return m_languageGroup.readEntry("Automatic Invocation", m_automatic); } ICompletionSettings::CompletionLevel CompletionSettings::completionLevel() const { const QString level = m_languageGroup.readEntry("completionDetail", completionLevelToString(m_level)); for (int i = 0; i < ICompletionSettings::LAST_LEVEL; i++) { if (completionLevelToString(static_cast(i)) == level) { return static_cast(i); } } return m_level; } CompletionSettings::CompletionSettings() : m_todoMarkerWords(QStringLiteral("TODO FIXME")) , m_languageGroup(KSharedConfig::openConfig(), "Language Support"){} } diff --git a/kdevplatform/shell/completionsettings.h b/kdevplatform/shell/completionsettings.h index e9ce1b5c9f..b6d2a1cf9d 100644 --- a/kdevplatform/shell/completionsettings.h +++ b/kdevplatform/shell/completionsettings.h @@ -1,74 +1,76 @@ /*************************************************************************** * Copyright 2008 David Nolden * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KDEVPLATFORM_COMPLETIONSETTINGS_H #define KDEVPLATFORM_COMPLETIONSETTINGS_H #include #include namespace KDevelop { class CompletionSettings : public KDevelop::ICompletionSettings { Q_OBJECT public: CompletionLevel completionLevel() const override; bool automaticCompletionEnabled() const override; void emitChanged() { emit settingsChanged(this); } int localColorizationLevel() const override; int globalColorizationLevel() const override; bool highlightSemanticProblems() const override; bool highlightProblematicLines() const override; + ProblemInlineNotesLevel problemInlineNotesLevel() const override; + bool boldDeclarations() const override; bool showMultiLineSelectionInformation() const override; int minFilesForSimplifiedParsing() const override; QStringList todoMarkerWords() const override; static CompletionSettings& self(); private: CompletionSettings(); const CompletionLevel m_level = MinimalWhenAutomatic; const bool m_automatic = true; const bool m_highlightSemanticProblems = true; const bool m_highlightProblematicLines = false; const bool m_showMultiLineInformation = false; const bool m_boldDeclarations = true; const int m_localColorizationLevel = 170; const int m_globalColorizationLevel = 255; const int m_minFilesForSimplifiedParsing = 100000; const QString m_todoMarkerWords; const KConfigGroup m_languageGroup; }; } #endif diff --git a/kdevplatform/shell/settings/languageconfig.kcfg b/kdevplatform/shell/settings/languageconfig.kcfg index 624d8da975..e09b056819 100644 --- a/kdevplatform/shell/settings/languageconfig.kcfg +++ b/kdevplatform/shell/settings/languageconfig.kcfg @@ -1,44 +1,49 @@ + "interfaces/icompletionsettings.h" true true false true false 170 255 100000 TODO FIXME + MinimalWhenAutomatic + + + KDevelop::ICompletionSettings::WarningsAndErrorsProblemInlineNotesLevel diff --git a/kdevplatform/shell/settings/languagepreferences.ui b/kdevplatform/shell/settings/languagepreferences.ui index 474e0c4081..f7ad834fd4 100644 --- a/kdevplatform/shell/settings/languagepreferences.ui +++ b/kdevplatform/shell/settings/languagepreferences.ui @@ -1,328 +1,370 @@ LanguagePreferences 0 0 599 592 - + + 0 + + + 0 + + + 0 + + 0 Code Completion If disabled, the code completion widget will never show automatically. Choose whether to display additional information for the currently selected code completion item. <p>The code completion UI has a minimal mode and a detailed mode.<br>Choose in what cases full code completion will be displayed.</p> 1 Never When Invoked Manually Always <p>The code completion UI has a minimal mode and a detailed mode.<br>Choose in what cases full code completion will be displayed.</p> Detailed completion: If disabled, the code completion widget will never show automatically. Enable automatic invocation: Choose whether to display additional information for the currently selected codecompletion item. Additional information for current item: Semantic Code Highlighting QFormLayout::ExpandingFieldsGrow <p>This setting decides about the intensity of colors for local variables, for example function arguments, variables and the like.</p> Local colorization intensity: kcfg_localColorization <p>This setting decides about the intensity of colors for local variables, for example function arguments, variables and the like.</p> 255 3 25 170 Qt::Horizontal <p>This settings lets you change the intensity of globally accessible types, for example classes, methods, functions etc.</p> Global colorization intensity: kcfg_localColorization <p>This settings lets you change the intensity of globally accessible types, for example classes, methods, functions etc.</p> 255 3 25 255 Qt::Horizontal <p>Highlight semantic problems, such as non-existent or inaccessible declarations.</p> Highlight semantic problems: <p>Highlight semantic problems, such as non-existent or inaccessible declarations.</p> <p>When enabled, lines with errors will get additionally highlighted and their positions get marked in the scrollbar.</p> Highlight problematic lines: <p>When enabled, lines with errors will get additionally highlighted and their positions get marked in the scrollbar.</p> - + Space-separated list of words that make a comment represent a TODO item TODO marker words: - + Space-separated list of words that make a comment represent a TODO item. <html><head/><body><p>Use bold font for declarations such as classes, functions, local variables, etc.</p></body></html> Bold font for declarations: + + + + + None + + + + + Errors + + + + + Warnings and Errors + + + + + Hints, Warnings and Errors + + + + + + + + Problems shown as inline notes: + + + + kcfg_problemInlineNotesLevel kcfg_localColorization label_4 kcfg_highlightSemanticProblems label_5 kcfg_highlightProblematicLines label_3 kcfg_todoMarkerWords kcfg_globalColorization globalColorizationLabel localColorizationLabel boldDeclarationsLabel kcfg_boldDeclarations + label_8 Project Parsing QFormLayout::ExpandingFieldsGrow <p>When a project contains more files than this number, the project will be parsed in simplified mode, increasing the efficiency by gathering less information. Global code navigation and quickopen capabilities will be reduced.</p> Minimum project size for simplified parsing: 0 0 <p>When a project contains more files than this number, the project will be parsed in simplified mode, increasing the efficiency by gathering less information. Global code navigation and quickopen capabilities will be reduced.</p> 99999999 1000 Qt::Vertical 20 40 diff --git a/plugins/problemreporter/CMakeLists.txt b/plugins/problemreporter/CMakeLists.txt index 343c5df491..45e9a812e1 100644 --- a/plugins/problemreporter/CMakeLists.txt +++ b/plugins/problemreporter/CMakeLists.txt @@ -1,25 +1,26 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevproblemreporter\") ########### next target ############### set(kdevproblemreporter_PART_SRCS problemreporterplugin.cpp problemtreeview.cpp problemhighlighter.cpp problemsview.cpp #problemnavigationcontext.cpp problemreportermodel.cpp + probleminlinenoteprovider.cpp ) declare_qt_logging_category(kdevproblemreporter_PART_SRCS TYPE PLUGIN IDENTIFIER PLUGIN_PROBLEMREPORTER CATEGORY_BASENAME "problemreporter" ) qt5_add_resources(kdevproblemreporter_PART_SRCS kdevproblemreporter.qrc) kdevplatform_add_plugin(kdevproblemreporter JSON kdevproblemreporter.json SOURCES ${kdevproblemreporter_PART_SRCS}) target_link_libraries(kdevproblemreporter KF5::TextEditor KF5::Parts KDev::Language KDev::Interfaces KDev::Util KDev::Project KDev::Shell) if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/plugins/problemreporter/probleminlinenoteprovider.cpp b/plugins/problemreporter/probleminlinenoteprovider.cpp new file mode 100644 index 0000000000..be24198a46 --- /dev/null +++ b/plugins/problemreporter/probleminlinenoteprovider.cpp @@ -0,0 +1,243 @@ +/* + * InlineNoteProvider + * + * Copyright 2020 David Redondo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "probleminlinenoteprovider.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace KDevelop; + +static constexpr int marginColumns = 2; + +struct SeverityColors +{ + QColor foreground; + QColor background; +}; + +static SeverityColors severityColors(IProblem::Severity severity) +{ + const KColorScheme::ForegroundRole foregroundRole = + (severity == IProblem::Error) ? KColorScheme::NegativeText : + (severity == IProblem::Warning) ? KColorScheme::NeutralText : + /* IProblem::Hint, default */ KColorScheme::PositiveText; + const KColorScheme::BackgroundRole backgroundRole = + (severity == IProblem::Error) ? KColorScheme::NegativeBackground : + (severity == IProblem::Warning) ? KColorScheme::NeutralBackground : + /* IProblem::Hint, default */ KColorScheme::PositiveBackground; + + KColorScheme scheme(QPalette::Active); + return { + scheme.foreground(foregroundRole).color(), + scheme.background(backgroundRole).color() + }; +} + +ProblemInlineNoteProvider::ProblemInlineNoteProvider(KTextEditor::Document* document) + : m_document{document} +{ + auto registerProvider = [this] (KTextEditor::Document*, KTextEditor::View* view) { + auto* inlineNoteIface = qobject_cast(view); + if(inlineNoteIface) { + inlineNoteIface->registerInlineNoteProvider(this); + } + }; + for (auto* view : m_document->views()) { + registerProvider(m_document, view); + } + connect(m_document, &KTextEditor::Document::viewCreated, this, registerProvider); + connect(ICore::self()->languageController()->completionSettings(), &ICompletionSettings::settingsChanged, + this, &ProblemInlineNoteProvider::completionSettingsChanged); +} + +void ProblemInlineNoteProvider::completionSettingsChanged() +{ + if (m_currentLevel == ICore::self()->languageController()->completionSettings()->problemInlineNotesLevel()) { + return; + } + setProblems(m_problems); +} + +void ProblemInlineNoteProvider::setProblems(const QVector& problems) +{ + if (!m_document) { + return; + } + m_problemForLine.clear(); + m_problems = problems; + if (problems.isEmpty()) { + emit inlineNotesReset(); + return; + } + m_currentLevel = ICore::self()->languageController()->completionSettings()->problemInlineNotesLevel() ; + if (m_currentLevel == ICompletionSettings::NoProblemsInlineNotesLevel) { + return; + } + for (const IProblem::Ptr& problem : problems) { + if (problem->finalLocation().document.toUrl() != m_document->url() || !problem->finalLocation().isValid()) { + continue; + } + switch (problem->severity()) { + case IProblem::NoSeverity: + case IProblem::Hint: + if (m_currentLevel != ICompletionSettings::AllProblemsInlineNotesLevel) { + continue; + } + break; + case IProblem::Warning: + if (m_currentLevel == ICompletionSettings::ErrorsProblemInlineNotesLevel) { + continue; + } + break; + case IProblem::Error: + break; + } + const int line = problem->finalLocation().start().line(); + // Only render the problem with the highest severity in each line. + if (m_problemForLine.contains(line)) { + const IProblem::Ptr currentProblem = m_problemForLine.value(line); + if (problem->severity() == currentProblem->severity()) { + if (problem->finalLocation().start().column() < currentProblem->finalLocation().start().column()) { + m_problemForLine[line] = problem; + } + // No Severity has the lowest value + } else if (problem->severity() < currentProblem->severity() && problem->severity() != IProblem::NoSeverity) { + m_problemForLine[line] = problem; + } else if (currentProblem->severity() == IProblem::NoSeverity) { + m_problemForLine[line] = problem; + } + } else { + m_problemForLine[line] = problem; + } + } + emit inlineNotesReset(); +} + +QVector ProblemInlineNoteProvider::inlineNotes(int line) const +{ + return m_problemForLine.contains(line) ? QVector(1, m_document->endOfLine(line).column() + marginColumns) + : QVector(); +} + +// matching logic of KateIconBorder's icon height calculation for a line +static constexpr int iconTopBottomMargin = 1; + +static int iconHeight(const KTextEditor::InlineNote& note) +{ + QFontMetrics fontMetrics(note.font()); + + return qMin(fontMetrics.height(), note.lineHeight()) - 2 * iconTopBottomMargin; +} + +static constexpr int noteBorderWidth = 2; + +struct InlineNoteLayout +{ + int iconSize; + int iconX; + int descriptionX; + int rightMargin; +}; + + +// Design of note: +// following basically the message widget design, but reduced to only a colored border on the left side +// +// |_O_description_ +// +static void doInlineNoteLayout(const KTextEditor::InlineNote& note, + InlineNoteLayout* layout) +{ + const auto* view = note.view(); + const auto* style = view->style(); + const int leftMargin = style->pixelMetric(QStyle::PM_LayoutLeftMargin, nullptr, view); + layout->rightMargin = style->pixelMetric(QStyle::PM_LayoutRightMargin, nullptr, view); + const int noteSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, nullptr, view); + + layout->iconSize = iconHeight(note); + + layout->iconX = noteBorderWidth + leftMargin; + layout->descriptionX = layout->iconX + layout->iconSize + noteSpacing; +} + +QSize ProblemInlineNoteProvider::inlineNoteSize(const KTextEditor::InlineNote& note) const +{ + InlineNoteLayout layout; + doInlineNoteLayout(note, &layout); + + const auto prob = m_problemForLine[note.position().line()]; + QFont font = note.font(); + font.setItalic(true); + const QFontMetrics metric(font); + const QRect boundingRect = metric.boundingRect(prob->description()); + return {layout.descriptionX + boundingRect.width() + layout.rightMargin, note.lineHeight()}; +} + +void ProblemInlineNoteProvider::paintInlineNote(const KTextEditor::InlineNote& note, QPainter& painter) const +{ + InlineNoteLayout layout; + doInlineNoteLayout(note, &layout); + + const int line = note.position().line(); + const auto prob = m_problemForLine[note.position().line()]; + QFont font = note.font(); + font.setItalic(true); + painter.setFont(font); + const KTextEditor::View* view = note.view(); + // NOTE cursorToCoordinate is relative to (0,0) of the view widget so we have to subtract the x + // value of the start of the line from it. However it returns also -1 for cursors that have no + // actual text so we subtract the width of the margin column(s) from the available width + const int viewWidth = view->textAreaRect().width(); + const int textAreaStart = view->cursorToCoordinate(KTextEditor::Cursor(line, 0)).x(); + const int marginWidth = view->cursorToCoordinate(KTextEditor::Cursor(line, marginColumns)).x() - textAreaStart; + const int nonTextSize = layout.descriptionX + layout.rightMargin; + const int lineEnd = view->cursorToCoordinate(KTextEditor::Cursor(line, note.position().column() - marginColumns)).x() - textAreaStart; + const int availableTextWidth = viewWidth - marginWidth - lineEnd - nonTextSize; + QString text = painter.fontMetrics().elidedText(prob->description(), Qt::ElideRight, availableTextWidth); + QIcon icon = IProblem::iconForSeverity(prob->severity()); + // QFontMetrics doesn't provide results that are good enough for painting so we use QPainter::boundingRect here + QRect boundingRect = painter.boundingRect(QRect(layout.descriptionX, 0, 0, 0), Qt::AlignLeft, text); + const auto colors = severityColors(prob->severity()); + // background + painter.setBrush(colors.background); + painter.setPen(Qt::NoPen); + painter.drawRect(boundingRect.adjusted(-(layout.descriptionX), 0, layout.rightMargin, 0)); + // borderline + painter.setBrush(colors.foreground); + painter.drawRect(QRect(0, 0, noteBorderWidth, note.lineHeight())); + // icpn + icon.paint(&painter, layout.iconX, iconTopBottomMargin, layout.iconSize, layout.iconSize, Qt::AlignCenter); + // text + painter.setPen(colors.foreground); + painter.drawText(boundingRect, Qt::AlignLeft, text); +} diff --git a/plugins/problemreporter/probleminlinenoteprovider.h b/plugins/problemreporter/probleminlinenoteprovider.h new file mode 100644 index 0000000000..6b67805d0e --- /dev/null +++ b/plugins/problemreporter/probleminlinenoteprovider.h @@ -0,0 +1,57 @@ +/* + * InlineNoteProvider + * + * Copyright 2020 David Redondo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KDEVPLATFORM_PLUGIN_PROBLEMINLINENOTEPROVIDER_H +#define KDEVPLATFORM_PLUGIN_PROBLEMINLINENOTEPROVIDER_H + +#include +#include + +#include + +#include +#include +#include + +namespace KTextEditor { +class Document; +} + +class ProblemInlineNoteProvider : public KTextEditor::InlineNoteProvider +{ + Q_OBJECT +public: + explicit ProblemInlineNoteProvider(KTextEditor::Document* document); + + void setProblems(const QVector& problems); + + QVector inlineNotes(int line) const override; + QSize inlineNoteSize(const KTextEditor::InlineNote& note) const override; + void paintInlineNote(const KTextEditor::InlineNote& note, QPainter& painter) const override; +private: + void completionSettingsChanged(); + + QPointer m_document; + QVector m_problems; + QHash m_problemForLine; + KDevelop::ICompletionSettings::ProblemInlineNotesLevel m_currentLevel; +}; +#endif diff --git a/plugins/problemreporter/problemreporterplugin.cpp b/plugins/problemreporter/problemreporterplugin.cpp index 89413b8582..d8e7992a18 100644 --- a/plugins/problemreporter/problemreporterplugin.cpp +++ b/plugins/problemreporter/problemreporterplugin.cpp @@ -1,257 +1,263 @@ /* * KDevelop Problem Reporter * * Copyright 2006 Adam Treat * Copyright 2006-2007 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemreporterplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include "problemhighlighter.h" +#include "probleminlinenoteprovider.h" #include "problemreportermodel.h" #include "language/assistant/staticassistantsmanager.h" #include #include #include #include #include #include #include "shell/problemmodelset.h" #include "problemsview.h" #include #include K_PLUGIN_FACTORY_WITH_JSON(KDevProblemReporterFactory, "kdevproblemreporter.json", registerPlugin();) using namespace KDevelop; class ProblemReporterFactory : public KDevelop::IToolViewFactory { public: QWidget* create(QWidget* parent = nullptr) override { Q_UNUSED(parent); auto* v = new ProblemsView(); v->load(); return v; } Qt::DockWidgetArea defaultPosition() const override { return Qt::BottomDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.ProblemReporterView"); } }; ProblemReporterPlugin::ProblemReporterPlugin(QObject* parent, const QVariantList&) : KDevelop::IPlugin(QStringLiteral("kdevproblemreporter"), parent) , m_factory(new ProblemReporterFactory) , m_model(new ProblemReporterModel(this)) { KDevelop::ProblemModelSet* pms = core()->languageController()->problemModelSet(); pms->addModel(QStringLiteral("Parser"), i18n("Parser"), m_model); core()->uiController()->addToolView(i18n("Problems"), m_factory); setXMLFile(QStringLiteral("kdevproblemreporter.rc")); connect(ICore::self()->documentController(), &IDocumentController::documentClosed, this, &ProblemReporterPlugin::documentClosed); connect(ICore::self()->documentController(), &IDocumentController::textDocumentCreated, this, &ProblemReporterPlugin::textDocumentCreated); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ProblemReporterPlugin::documentActivated); connect(DUChain::self(), &DUChain::updateReady, this, &ProblemReporterPlugin::updateReady); connect(ICore::self()->languageController()->staticAssistantsManager(), &StaticAssistantsManager::problemsChanged, this, &ProblemReporterPlugin::updateHighlight); connect(pms, &ProblemModelSet::showRequested, this, &ProblemReporterPlugin::showModel); connect(pms, &ProblemModelSet::problemsChanged, this, &ProblemReporterPlugin::updateOpenedDocumentsHighlight); } ProblemReporterPlugin::~ProblemReporterPlugin() { qDeleteAll(m_highlighters); + qDeleteAll(m_inlineNoteProviders); } ProblemReporterModel* ProblemReporterPlugin::model() const { return m_model; } void ProblemReporterPlugin::unload() { KDevelop::ProblemModelSet* pms = KDevelop::ICore::self()->languageController()->problemModelSet(); pms->removeModel(QStringLiteral("Parser")); core()->uiController()->removeToolView(m_factory); } void ProblemReporterPlugin::documentClosed(IDocument* doc) { if (!doc->textDocument()) return; IndexedString url(doc->url()); delete m_highlighters.take(url); + delete m_inlineNoteProviders.take(url); m_reHighlightNeeded.remove(url); } void ProblemReporterPlugin::textDocumentCreated(KDevelop::IDocument* document) { Q_ASSERT(document->textDocument()); - m_highlighters.insert(IndexedString(document->url()), new ProblemHighlighter(document->textDocument())); - DUChain::self()->updateContextForUrl(IndexedString(document->url()), + IndexedString documentUrl(document->url()); + m_highlighters.insert(documentUrl, new ProblemHighlighter(document->textDocument())); + m_inlineNoteProviders.insert(documentUrl, new ProblemInlineNoteProvider(document->textDocument())); + DUChain::self()->updateContextForUrl(documentUrl, KDevelop::TopDUContext::AllDeclarationsContextsAndUses, this); } void ProblemReporterPlugin::documentActivated(KDevelop::IDocument* document) { IndexedString documentUrl(document->url()); const auto neededIt = m_reHighlightNeeded.find(documentUrl); if (neededIt != m_reHighlightNeeded.end()) { m_reHighlightNeeded.erase(neededIt); updateHighlight(documentUrl); } } void ProblemReporterPlugin::updateReady(const IndexedString& url, const KDevelop::ReferencedTopDUContext&) { m_model->problemsUpdated(url); updateHighlight(url); } void ProblemReporterPlugin::updateHighlight(const KDevelop::IndexedString& url) { ProblemHighlighter* ph = m_highlighters.value(url); if (!ph) return; KDevelop::ProblemModelSet* pms(core()->languageController()->problemModelSet()); QVector documentProblems; const auto models = pms->models(); for (const ModelData& modelData : models) { documentProblems += modelData.model->problems({url}); } ph->setProblems(documentProblems); + m_inlineNoteProviders.value(url)->setProblems(documentProblems); } void ProblemReporterPlugin::showModel(const QString& id) { auto w = qobject_cast(core()->uiController()->findToolView(i18n("Problems"), m_factory)); if (w) w->showModel(id); } KDevelop::ContextMenuExtension ProblemReporterPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { KDevelop::ContextMenuExtension extension; auto* editorContext = dynamic_cast(context); if (editorContext) { DUChainReadLocker lock(DUChain::lock(), 1000); if (!lock.locked()) { qCDebug(PLUGIN_PROBLEMREPORTER) << "failed to lock duchain in time"; return extension; } QString title; QList actions; TopDUContext* top = DUChainUtils::standardContextForUrl(editorContext->url()); if (top) { const auto problems = top->problems(); for (auto& problem : problems) { if (problem->range().contains( top->transformToLocalRevision(KTextEditor::Cursor(editorContext->position())))) { KDevelop::IAssistant::Ptr solution = problem->solutionAssistant(); if (solution) { title = solution->title(); const auto solutionActions = solution->actions(); for (auto& action : solutionActions) { actions << action->toQAction(parent); } } } } } if (!actions.isEmpty()) { QString text; if (title.isEmpty()) text = i18n("Solve Problem"); else { text = i18n("Solve: %1", KDevelop::htmlToPlainText(title)); } auto* menu = new QMenu(text, parent); for (QAction* action : qAsConst(actions)) { menu->addAction(action); } extension.addAction(ContextMenuExtension::ExtensionGroup, menu->menuAction()); } } return extension; } void ProblemReporterPlugin::updateOpenedDocumentsHighlight() { const auto openDocuments = core()->documentController()->openDocuments(); for (auto* document : openDocuments) { // Skip non-text documents. // This also fixes crash caused by calling updateOpenedDocumentsHighlight() method without // any opened documents. In this case documentController()->openDocuments() returns single // (non-text) document with url like file:///tmp/kdevelop_QW2530.patch which has fatal bug: // if we call isActive() method from this document the crash will happens. if (!document->isTextDocument()) continue; IndexedString documentUrl(document->url()); if (document->isActive()) updateHighlight(documentUrl); else m_reHighlightNeeded.insert(documentUrl); } } #include "problemreporterplugin.moc" diff --git a/plugins/problemreporter/problemreporterplugin.h b/plugins/problemreporter/problemreporterplugin.h index caee82534c..eb99ba61a4 100644 --- a/plugins/problemreporter/problemreporterplugin.h +++ b/plugins/problemreporter/problemreporterplugin.h @@ -1,78 +1,80 @@ /* * KDevelop Problem Reporter * * Copyright 2006 Adam Treat * Copyright 2006-2007 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H #define KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H #include #include #include #include namespace KTextEditor { class Document; } namespace KDevelop { class IDocument; } class ProblemHighlighter; +class ProblemInlineNoteProvider; class ProblemReporterModel; class ProblemReporterPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit ProblemReporterPlugin(QObject* parent, const QVariantList& = QVariantList()); ~ProblemReporterPlugin() override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context, QWidget* parent) override; // KDevelop::Plugin methods void unload() override; ProblemReporterModel* model() const; private Q_SLOTS: void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext&); void updateHighlight(const KDevelop::IndexedString& url); void textDocumentCreated(KDevelop::IDocument* document); void documentActivated(KDevelop::IDocument* document); void showModel(const QString& id); private: void updateOpenedDocumentsHighlight(); class ProblemReporterFactory* m_factory; ProblemReporterModel* m_model; QHash m_highlighters; + QHash m_inlineNoteProviders; QSet m_reHighlightNeeded; public Q_SLOTS: void documentClosed(KDevelop::IDocument*); }; #endif // KDEVPLATFORM_PLUGIN_PROBLEMREPORTERPLUGIN_H