Clang: Use CppHoverHandler for diagnostic tooltips

We used to call QTextCharFormat::setToolTip from the ExtraSelection to
install the diagnostic tooltip. Since this allows to set only text
tooltips and we would like to introduce a custom tooltip widget for
diagnostics, make use of CppHoverHandler, which is more flexible.

Change-Id: Ia1b2c3c50810596ce4a3a025002e6e4efd8789db
Reviewed-by: Alessandro Portale <alessandro.portale@theqtcompany.com>
This commit is contained in:
Nikolai Kosjar
2016-01-27 13:37:19 +01:00
committed by Alessandro Portale
parent 593ed52c94
commit 9a4284d666
14 changed files with 475 additions and 52 deletions

View File

@@ -53,6 +53,7 @@ HEADERS += \
clangfixitoperationsextractor.h \
clangfunctionhintmodel.h \
clanghighlightingmarksreporter.h \
clangisdiagnosticrelatedtolocation.h \
clangmodelmanagersupport.h \
clangpreprocessorassistproposalitem.h \
clangtextmark.h \

View File

@@ -77,6 +77,7 @@ QtcPlugin {
"clangfunctionhintmodel.h",
"clanghighlightingmarksreporter.cpp",
"clanghighlightingmarksreporter.h",
"clangisdiagnosticrelatedtolocation.h",
"clangmodelmanagersupport.cpp",
"clangmodelmanagersupport.h",
"clangpreprocessorassistproposalitem.cpp",

View File

@@ -16,4 +16,5 @@ HEADERS += \
$$PWD/clangcompletioncontextanalyzer.h \
$$PWD/clangdiagnosticfilter.h \
$$PWD/clangfixitoperation.h \
$$PWD/clanghighlightingmarksreporter.h
$$PWD/clanghighlightingmarksreporter.h \
$$PWD/clangisdiagnosticrelatedtolocation.h

View File

@@ -25,7 +25,9 @@
#include "clangdiagnosticfilter.h"
#include "clangdiagnosticmanager.h"
#include "clangisdiagnosticrelatedtolocation.h"
#include <texteditor/convenience.h>
#include <texteditor/fontsettings.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditorsettings.h>
@@ -38,14 +40,12 @@
namespace {
QTextEdit::ExtraSelection createExtraSelections(const QTextCharFormat &mainformat,
const QTextCursor &cursor,
const QString &diagnosticText)
const QTextCursor &cursor)
{
QTextEdit::ExtraSelection extraSelection;
extraSelection.format = mainformat;
extraSelection.cursor = cursor;
extraSelection.format.setToolTip(diagnosticText);
return extraSelection;
}
@@ -61,7 +61,6 @@ int positionInText(QTextDocument *textDocument,
void addRangeSelections(const ClangBackEnd::DiagnosticContainer &diagnostic,
QTextDocument *textDocument,
const QTextCharFormat &contextFormat,
const QString &diagnosticText,
QList<QTextEdit::ExtraSelection> &extraSelections)
{
for (auto &&range : diagnostic.ranges()) {
@@ -69,7 +68,7 @@ void addRangeSelections(const ClangBackEnd::DiagnosticContainer &diagnostic,
cursor.setPosition(positionInText(textDocument, range.start()));
cursor.setPosition(positionInText(textDocument, range.end()), QTextCursor::KeepAnchor);
auto extraSelection = createExtraSelections(contextFormat, cursor, diagnosticText);
auto extraSelection = createExtraSelections(contextFormat, cursor);
extraSelections.push_back(std::move(extraSelection));
}
@@ -90,37 +89,6 @@ QTextCursor createSelectionCursor(QTextDocument *textDocument,
return cursor;
}
bool isHelpfulChildDiagnostic(const ClangBackEnd::DiagnosticContainer &parentDiagnostic,
const ClangBackEnd::DiagnosticContainer &childDiagnostic)
{
auto parentLocation = parentDiagnostic.location();
auto childLocation = childDiagnostic.location();
return parentLocation == childLocation;
}
QString diagnosticText(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
QString text = diagnostic.category().toString()
+ QStringLiteral("\n\n")
+ diagnostic.text().toString();
#ifdef QT_DEBUG
if (!diagnostic.disableOption().isEmpty()) {
text += QStringLiteral(" (disable with ")
+ diagnostic.disableOption().toString()
+ QStringLiteral(")");
}
#endif
for (auto &&childDiagnostic : diagnostic.children()) {
if (isHelpfulChildDiagnostic(diagnostic, childDiagnostic))
text += QStringLiteral("\n ") + childDiagnostic.text().toString();
}
return text;
}
void addSelections(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
QTextDocument *textDocument,
const QTextCharFormat &mainFormat,
@@ -129,11 +97,9 @@ void addSelections(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics
{
for (auto &&diagnostic : diagnostics) {
auto cursor = createSelectionCursor(textDocument, diagnostic.location());
auto extraSelection = createExtraSelections(mainFormat, cursor);
auto text = diagnosticText(diagnostic);
auto extraSelection = createExtraSelections(mainFormat, cursor, text);
addRangeSelections(diagnostic, textDocument, contextFormat, text, extraSelections);
addRangeSelections(diagnostic, textDocument, contextFormat, extraSelections);
extraSelections.push_back(std::move(extraSelection));
}
@@ -164,6 +130,69 @@ void addErrorSelections(const QVector<ClangBackEnd::DiagnosticContainer> &diagno
addSelections(diagnostics, textDocument, errorFormat, errorContextFormat, extraSelections);
}
ClangBackEnd::SourceLocationContainer toSourceLocation(QTextDocument *textDocument, int position)
{
int line, column;
if (TextEditor::Convenience::convertPosition(textDocument, position, &line, &column))
return ClangBackEnd::SourceLocationContainer(Utf8String(), line, column);
return ClangBackEnd::SourceLocationContainer();
}
ClangBackEnd::SourceRangeContainer toSourceRange(const QTextCursor &cursor)
{
using namespace ClangBackEnd;
QTextDocument *textDocument = cursor.document();
return SourceRangeContainer(toSourceLocation(textDocument, cursor.anchor()),
toSourceLocation(textDocument, cursor.position()));
}
bool isDiagnosticAtLocation(const ClangBackEnd::DiagnosticContainer &diagnostic,
uint line,
uint column,
QTextDocument *textDocument)
{
using namespace ClangCodeModel::Internal;
const ClangBackEnd::SourceLocationContainer &location = diagnostic.location();
const QTextCursor cursor = createSelectionCursor(textDocument, location);
const ClangBackEnd::SourceRangeContainer cursorRange = toSourceRange(cursor);
return isDiagnosticRelatedToLocation(diagnostic, {cursorRange}, line, column);
}
QVector<ClangBackEnd::DiagnosticContainer>
filteredDiagnosticsAtLocation(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
uint line,
uint column,
QTextDocument *textDocument)
{
QVector<ClangBackEnd::DiagnosticContainer> filteredDiagnostics;
foreach (const auto &diagnostic, diagnostics) {
if (isDiagnosticAtLocation(diagnostic, line, column, textDocument))
filteredDiagnostics.append(diagnostic);
}
return filteredDiagnostics;
}
bool editorDocumentProcessorHasDiagnosticAt(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
uint line,
uint column,
QTextDocument *textDocument)
{
foreach (const auto &diagnostic, diagnostics) {
if (isDiagnosticAtLocation(diagnostic, line, column, textDocument))
return true;
}
return false;
}
} // anonymous
namespace ClangCodeModel {
@@ -192,6 +221,26 @@ QList<QTextEdit::ExtraSelection> ClangDiagnosticManager::takeExtraSelections()
return extraSelections;
}
bool ClangDiagnosticManager::hasDiagnosticsAt(uint line, uint column) const
{
QTextDocument *textDocument = m_textDocument->document();
return editorDocumentProcessorHasDiagnosticAt(m_errorDiagnostics, line, column, textDocument)
|| editorDocumentProcessorHasDiagnosticAt(m_warningDiagnostics, line, column, textDocument);
}
QVector<ClangBackEnd::DiagnosticContainer>
ClangDiagnosticManager::diagnosticsAt(uint line, uint column) const
{
QTextDocument *textDocument = m_textDocument->document();
QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
diagnostics += filteredDiagnosticsAtLocation(m_errorDiagnostics, line, column, textDocument);
diagnostics += filteredDiagnosticsAtLocation(m_warningDiagnostics, line, column, textDocument);
return diagnostics;
}
void ClangDiagnosticManager::clearDiagnosticsWithFixIts()
{
m_fixItdiagnostics.clear();
@@ -213,8 +262,6 @@ void ClangDiagnosticManager::processNewDiagnostics(
generateTextMarks();
generateEditorSelections();
clearWarningsAndErrors();
}
const QVector<ClangBackEnd::DiagnosticContainer> &
@@ -241,12 +288,6 @@ void ClangDiagnosticManager::addClangTextMarks(
}
}
void ClangDiagnosticManager::clearWarningsAndErrors()
{
m_warningDiagnostics.clear();
m_errorDiagnostics.clear();
}
QString ClangDiagnosticManager::filePath() const
{
return m_textDocument->filePath().toString();

View File

@@ -51,6 +51,9 @@ public:
const QVector<ClangBackEnd::DiagnosticContainer> &diagnosticsWithFixIts() const;
QList<QTextEdit::ExtraSelection> takeExtraSelections();
bool hasDiagnosticsAt(uint line, uint column) const;
QVector<ClangBackEnd::DiagnosticContainer> diagnosticsAt(uint line, uint column) const;
void clearDiagnosticsWithFixIts();
private:
@@ -59,7 +62,6 @@ private:
void generateEditorSelections();
void generateTextMarks();
void addClangTextMarks(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void clearWarningsAndErrors();
private:
TextEditor::TextDocument *m_textDocument;

View File

@@ -50,6 +50,7 @@
#include <cplusplus/CppDocument.h>
#include <utils/qtcassert.h>
#include <utils/tooltip/tooltip.h>
#include <utils/QtConcurrentTools>
#include <QTextBlock>
@@ -233,6 +234,73 @@ TextEditor::QuickFixOperations ClangEditorDocumentProcessor::extraRefactoringOpe
return extractor.extract(assistInterface.fileName(), currentLine(assistInterface));
}
bool ClangEditorDocumentProcessor::hasDiagnosticsAt(uint line, uint column) const
{
return m_diagnosticManager.hasDiagnosticsAt(line, column);
}
namespace {
bool isHelpfulChildDiagnostic(const ClangBackEnd::DiagnosticContainer &parentDiagnostic,
const ClangBackEnd::DiagnosticContainer &childDiagnostic)
{
auto parentLocation = parentDiagnostic.location();
auto childLocation = childDiagnostic.location();
return parentLocation == childLocation;
}
QString diagnosticText(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
QString text = diagnostic.category().toString()
+ QStringLiteral("\n\n")
+ diagnostic.text().toString();
#ifdef QT_DEBUG
if (!diagnostic.disableOption().isEmpty()) {
text += QStringLiteral(" (disable with ")
+ diagnostic.disableOption().toString()
+ QStringLiteral(")");
}
#endif
for (auto &&childDiagnostic : diagnostic.children()) {
if (isHelpfulChildDiagnostic(diagnostic, childDiagnostic))
text += QStringLiteral("\n ") + childDiagnostic.text().toString();
}
return text;
}
QString generateTooltipText(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics)
{
QString text;
foreach (const ClangBackEnd::DiagnosticContainer &diagnostic, diagnostics) {
if (text.isEmpty())
text += diagnosticText(diagnostic);
else
text += QStringLiteral("\n\n\n") + diagnosticText(diagnostic);
}
return text;
}
} // anonymous namespace
void ClangEditorDocumentProcessor::showDiagnosticTooltip(const QPoint &point,
QWidget *parent,
uint line,
uint column) const
{
const QVector<ClangBackEnd::DiagnosticContainer> diagnostics
= m_diagnosticManager.diagnosticsAt(line, column);
const QString tooltipText = generateTooltipText(diagnostics);
::Utils::ToolTip::show(point, tooltipText, parent);
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
{
return fileContainerWithArguments(m_projectPart.data());

View File

@@ -76,6 +76,12 @@ public:
TextEditor::QuickFixOperations
extraRefactoringOperations(const TextEditor::AssistInterface &assistInterface) override;
bool hasDiagnosticsAt(uint line, uint column) const override;
void showDiagnosticTooltip(const QPoint &point,
QWidget *parent,
uint line,
uint column) const override;
ClangBackEnd::FileContainer fileContainerWithArguments() const;
void clearDiagnosticsWithFixIts();

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2016 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 <clangbackendipc/diagnosticcontainer.h>
namespace ClangCodeModel {
namespace Internal {
bool isWithinRange(const ClangBackEnd::SourceRangeContainer &range,
uint line,
uint column)
{
const ClangBackEnd::SourceLocationContainer startLocation = range.start();
const ClangBackEnd::SourceLocationContainer endLocation = range.end();
return startLocation.line() <= line
&& startLocation.column() <= column
&& line <= endLocation.line()
&& column <= endLocation.column();
}
bool isWithinOneRange(const QVector<ClangBackEnd::SourceRangeContainer> &ranges,
uint line,
uint column)
{
foreach (const ClangBackEnd::SourceRangeContainer &range, ranges) {
if (isWithinRange(range, line, column))
return true;
}
return false;
}
bool isDiagnosticRelatedToLocation(const ClangBackEnd::DiagnosticContainer &diagnostic,
const QVector<ClangBackEnd::SourceRangeContainer> &additionalRanges,
uint line,
uint column)
{
const ClangBackEnd::SourceLocationContainer location = diagnostic.location();
if (location.line() == line && location.column() == column)
return true;
if (isWithinOneRange(additionalRanges, line, column))
return true;
if (isWithinOneRange(diagnostic.ranges(), line, column))
return true;
return false;
}
} // namespace Internal
} // namespace ClangCodeModel