forked from qt-creator/qt-creator
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:
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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++");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user