Clang: Indicate available "fix its" with a light bulb

...at the end of the line, just like for the "Apply Function Signature
Changes" refactor action.

* Hovering the light bulb shows the tooltip "Inspect available fixits".
* Clicking the light bulb leads to the refactoring menu, as if the user
  hit Alt+Return.

Change-Id: Iaf7b3734c43e21fc28e6b0658f517d98858c0e0c
Reviewed-by: Alessandro Portale <alessandro.portale@theqtcompany.com>
This commit is contained in:
Nikolai Kosjar
2016-02-05 15:16:02 +01:00
parent cd94f66af0
commit 89199b519b
9 changed files with 132 additions and 7 deletions

View File

@@ -27,12 +27,18 @@
#include "clangdiagnosticmanager.h" #include "clangdiagnosticmanager.h"
#include "clangisdiagnosticrelatedtolocation.h" #include "clangisdiagnosticrelatedtolocation.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <cpptools/cpptoolsconstants.h>
#include <texteditor/convenience.h> #include <texteditor/convenience.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/proxyaction.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QTextBlock> #include <QTextBlock>
@@ -193,6 +199,40 @@ bool editorDocumentProcessorHasDiagnosticAt(
return false; return false;
} }
QTextCursor cursorAtLastPositionOfLine(QTextDocument *textDocument, int lineNumber)
{
const QTextBlock textBlock = textDocument->findBlockByNumber(lineNumber - 1);
QTC_ASSERT(textBlock.isValid(), return QTextCursor());
const int lastPositionOfLine = textBlock.position() + textBlock.length() - 1;
QTextCursor textCursor(textDocument);
textCursor.setPosition(lastPositionOfLine);
return textCursor;
}
QString tooltipForFixItAvailableMarker()
{
QString text = QObject::tr("Inspect available fixits");
Core::Command *command = Core::ActionManager::command(TextEditor::Constants::QUICKFIX_THIS);
if (command)
text = Utils::ProxyAction::stringWithAppendedShortcut(text, command->keySequence());
return text;
}
TextEditor::RefactorMarker createFixItAvailableMarker(QTextDocument *textDocument, int lineNumber)
{
TextEditor::RefactorMarker marker;
marker.tooltip = tooltipForFixItAvailableMarker();
marker.cursor = cursorAtLastPositionOfLine(textDocument, lineNumber);
marker.data = QLatin1String(CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID);
return marker;
}
} // anonymous } // anonymous
namespace ClangCodeModel { namespace ClangCodeModel {
@@ -212,6 +252,15 @@ void ClangDiagnosticManager::generateTextMarks()
addClangTextMarks(m_errorDiagnostics); addClangTextMarks(m_errorDiagnostics);
} }
void ClangDiagnosticManager::generateFixItAvailableMarkers()
{
m_fixItAvailableMarkers.clear();
QSet<int> lineNumbersWithFixItMarker;
addFixItAvailableMarker(m_warningDiagnostics, lineNumbersWithFixItMarker);
addFixItAvailableMarker(m_errorDiagnostics, lineNumbersWithFixItMarker);
}
QList<QTextEdit::ExtraSelection> ClangDiagnosticManager::takeExtraSelections() QList<QTextEdit::ExtraSelection> ClangDiagnosticManager::takeExtraSelections()
{ {
auto extraSelections = m_extraSelections; auto extraSelections = m_extraSelections;
@@ -221,6 +270,15 @@ QList<QTextEdit::ExtraSelection> ClangDiagnosticManager::takeExtraSelections()
return extraSelections; return extraSelections;
} }
TextEditor::RefactorMarkers ClangDiagnosticManager::takeFixItAvailableMarkers()
{
TextEditor::RefactorMarkers fixItAvailableMarkers = m_fixItAvailableMarkers;
m_fixItAvailableMarkers.clear();
return fixItAvailableMarkers;
}
bool ClangDiagnosticManager::hasDiagnosticsAt(uint line, uint column) const bool ClangDiagnosticManager::hasDiagnosticsAt(uint line, uint column) const
{ {
QTextDocument *textDocument = m_textDocument->document(); QTextDocument *textDocument = m_textDocument->document();
@@ -262,6 +320,7 @@ void ClangDiagnosticManager::processNewDiagnostics(
generateTextMarks(); generateTextMarks();
generateEditorSelections(); generateEditorSelections();
generateFixItAvailableMarkers();
} }
const QVector<ClangBackEnd::DiagnosticContainer> & const QVector<ClangBackEnd::DiagnosticContainer> &
@@ -288,6 +347,24 @@ void ClangDiagnosticManager::addClangTextMarks(
} }
} }
void ClangDiagnosticManager::addFixItAvailableMarker(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
QSet<int> &lineNumbersWithFixItMarker)
{
for (auto &&diagnostic : diagnostics) {
const int line = int(diagnostic.location().line());
if (!diagnostic.fixIts().isEmpty() && !lineNumbersWithFixItMarker.contains(line)) {
const TextEditor::RefactorMarker marker
= createFixItAvailableMarker(m_textDocument->document(), line);
lineNumbersWithFixItMarker.insert(line);
m_fixItAvailableMarkers.append(marker);
}
addFixItAvailableMarker(diagnostic.children(), lineNumbersWithFixItMarker);
}
}
QString ClangDiagnosticManager::filePath() const QString ClangDiagnosticManager::filePath() const
{ {
return m_textDocument->filePath().toString(); return m_textDocument->filePath().toString();

View File

@@ -28,9 +28,12 @@
#include "clangtextmark.h" #include "clangtextmark.h"
#include <texteditor/refactoroverlay.h>
#include <clangbackendipc/diagnosticcontainer.h> #include <clangbackendipc/diagnosticcontainer.h>
#include <QList> #include <QList>
#include <QSet>
#include <QTextEdit> #include <QTextEdit>
#include <QVector> #include <QVector>
@@ -50,6 +53,7 @@ public:
const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticsWithFixIts() const; const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticsWithFixIts() const;
QList<QTextEdit::ExtraSelection> takeExtraSelections(); QList<QTextEdit::ExtraSelection> takeExtraSelections();
TextEditor::RefactorMarkers takeFixItAvailableMarkers();
bool hasDiagnosticsAt(uint line, uint column) const; bool hasDiagnosticsAt(uint line, uint column) const;
QVector<ClangBackEnd::DiagnosticContainer> diagnosticsAt(uint line, uint column) const; QVector<ClangBackEnd::DiagnosticContainer> diagnosticsAt(uint line, uint column) const;
@@ -61,7 +65,10 @@ private:
void filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics); void filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void generateEditorSelections(); void generateEditorSelections();
void generateTextMarks(); void generateTextMarks();
void generateFixItAvailableMarkers();
void addClangTextMarks(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics); void addClangTextMarks(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void addFixItAvailableMarker(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
QSet<int> &lineNumbersWithFixItMarker);
private: private:
TextEditor::TextDocument *m_textDocument; TextEditor::TextDocument *m_textDocument;
@@ -70,6 +77,7 @@ private:
QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_errorDiagnostics;
QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics; QVector<ClangBackEnd::DiagnosticContainer> m_fixItdiagnostics;
QList<QTextEdit::ExtraSelection> m_extraSelections; QList<QTextEdit::ExtraSelection> m_extraSelections;
TextEditor::RefactorMarkers m_fixItAvailableMarkers;
std::vector<ClangTextMark> m_clangTextMarks; std::vector<ClangTextMark> m_clangTextMarks;
}; };

View File

@@ -164,7 +164,8 @@ void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd
if (documentRevision == revision()) { if (documentRevision == revision()) {
m_diagnosticManager.processNewDiagnostics(diagnostics); m_diagnosticManager.processNewDiagnostics(diagnostics);
const auto codeWarnings = m_diagnosticManager.takeExtraSelections(); const auto codeWarnings = m_diagnosticManager.takeExtraSelections();
emit codeWarningsUpdated(revision(), codeWarnings); const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers();
emit codeWarningsUpdated(revision(), codeWarnings, fixitAvailableMarkers);
} }
} }
namespace { namespace {

View File

@@ -56,6 +56,7 @@
#include <cpptools/symbolfinder.h> #include <cpptools/symbolfinder.h>
#include <texteditor/completionsettings.h> #include <texteditor/completionsettings.h>
#include <texteditor/convenience.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h> #include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
@@ -271,11 +272,14 @@ void CppEditorWidget::onCppDocumentUpdated()
} }
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections) const QList<QTextEdit::ExtraSelection> selections,
const TextEditor::RefactorMarkers &refactorMarkers)
{ {
if (revision != documentRevision()) if (revision != documentRevision())
return; return;
setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
} }
void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
@@ -428,6 +432,26 @@ unsigned CppEditorWidget::documentRevision() const
return document()->revision(); return document()->revision();
} }
static bool isClangFixItAvailableMarker(const RefactorMarker &marker)
{
return marker.data.toString()
== QLatin1String(CppTools::Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID);
}
RefactorMarkers CppEditorWidget::refactorMarkersWithoutClangMarkers() const
{
RefactorMarkers clearedRefactorMarkers;
foreach (const RefactorMarker &marker, refactorMarkers()) {
if (isClangFixItAvailableMarker(marker))
continue;
clearedRefactorMarkers.append(marker);
}
return clearedRefactorMarkers;
}
bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const bool CppEditorWidget::isSemanticInfoValidExceptLocalUses() const
{ {
return d->m_lastSemanticInfo.doc return d->m_lastSemanticInfo.doc
@@ -631,8 +655,15 @@ QSharedPointer<FunctionDeclDefLink> CppEditorWidget::declDefLink() const
void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker) void CppEditorWidget::onRefactorMarkerClicked(const RefactorMarker &marker)
{ {
if (marker.data.canConvert<FunctionDeclDefLink::Marker>()) if (marker.data.canConvert<FunctionDeclDefLink::Marker>()) {
applyDeclDefLinkChanges(true); applyDeclDefLinkChanges(true);
} else if (isClangFixItAvailableMarker(marker)) {
int line, column;
if (Convenience::convertPosition(document(), marker.cursor.position(), &line, &column)) {
setTextCursor(marker.cursor);
invokeAssist(TextEditor::QuickFix);
}
}
} }
void CppEditorWidget::updateFunctionDeclDefLink() void CppEditorWidget::updateFunctionDeclDefLink()

View File

@@ -115,7 +115,8 @@ private slots:
void onCppDocumentUpdated(); void onCppDocumentUpdated();
void onCodeWarningsUpdated(unsigned revision, void onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections); const QList<QTextEdit::ExtraSelection> selections,
const TextEditor::RefactorMarkers &refactorMarkers);
void onIfdefedOutBlocksUpdated(unsigned revision, void onIfdefedOutBlocksUpdated(unsigned revision,
const QList<TextEditor::BlockRange> ifdefedOutBlocks); const QList<TextEditor::BlockRange> ifdefedOutBlocks);
@@ -133,6 +134,8 @@ private:
unsigned documentRevision() const; unsigned documentRevision() const;
TextEditor::RefactorMarkers refactorMarkersWithoutClangMarkers() const;
private: private:
QScopedPointer<CppEditorWidgetPrivate> d; QScopedPointer<CppEditorWidgetPrivate> d;
}; };

View File

@@ -60,7 +60,8 @@ public:
signals: signals:
void codeWarningsUpdated(unsigned contentsRevision, void codeWarningsUpdated(unsigned contentsRevision,
const QList<QTextEdit::ExtraSelection> selections); const QList<QTextEdit::ExtraSelection> selections,
const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned contentsRevision, void ifdefedOutBlocksUpdated(unsigned contentsRevision,
const QList<TextEditor::BlockRange> ifdefedOutBlocks); const QList<TextEditor::BlockRange> ifdefedOutBlocks);

View File

@@ -74,7 +74,8 @@ public:
signals: signals:
// Signal interface to implement // Signal interface to implement
void codeWarningsUpdated(unsigned revision, void codeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections); const QList<QTextEdit::ExtraSelection> selections,
const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned revision, void ifdefedOutBlocksUpdated(unsigned revision,
const QList<TextEditor::BlockRange> ifdefedOutBlocks); const QList<TextEditor::BlockRange> ifdefedOutBlocks);

View File

@@ -34,6 +34,7 @@
#include <texteditor/convenience.h> #include <texteditor/convenience.h>
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <texteditor/refactoroverlay.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <cplusplus/CppDocument.h> #include <cplusplus/CppDocument.h>
@@ -307,7 +308,7 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument()); m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument());
m_codeWarningsUpdated = true; m_codeWarningsUpdated = true;
emit codeWarningsUpdated(revision(), m_codeWarnings); emit codeWarningsUpdated(revision(), m_codeWarnings, TextEditor::RefactorMarkers());
} }
SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const

View File

@@ -61,6 +61,8 @@ const char CPP_SETTINGS_CATEGORY[] = "I.C++";
const char CPP_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("CppTools", "C++"); const char CPP_SETTINGS_TR_CATEGORY[] = QT_TRANSLATE_NOOP("CppTools", "C++");
const char SETTINGS_CATEGORY_CPP_ICON[] = ":/cpptools/images/category_cpp.png"; const char SETTINGS_CATEGORY_CPP_ICON[] = ":/cpptools/images/category_cpp.png";
const char CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID[] = "ClangFixItAvailableMarker";
const char CPP_SETTINGS_ID[] = "Cpp"; const char CPP_SETTINGS_ID[] = "Cpp";
const char CPP_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppTools", "C++"); const char CPP_SETTINGS_NAME[] = QT_TRANSLATE_NOOP("CppTools", "C++");