mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
248 lines
8.7 KiB
C++
248 lines
8.7 KiB
C++
![]() |
/*
|
||
|
* KDevelop Problem Reporter
|
||
|
*
|
||
|
* Copyright 2008 Hamish Rodda <rodda@kde.org>
|
||
|
* Copyright 2008-2009 David Nolden <david.nolden.kdevelop@art-master.de>
|
||
|
*
|
||
|
* 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 "problemhighlighter.h"
|
||
|
|
||
|
#include <KTextEditor/Document>
|
||
|
#include <KTextEditor/MarkInterface>
|
||
|
#include <ktexteditor/texthintinterface.h>
|
||
|
#include <ktexteditor/view.h>
|
||
|
#include <ktexteditor/movinginterface.h>
|
||
|
|
||
|
#include <language/duchain/indexedstring.h>
|
||
|
#include <language/duchain/navigation/abstractnavigationwidget.h>
|
||
|
#include <language/duchain/navigation/problemnavigationcontext.h>
|
||
|
#include <language/util/navigationtooltip.h>
|
||
|
#include <interfaces/icore.h>
|
||
|
#include <interfaces/ilanguagecontroller.h>
|
||
|
#include <interfaces/icompletionsettings.h>
|
||
|
#include <language/duchain/duchainlock.h>
|
||
|
#include <language/duchain/duchainutils.h>
|
||
|
#include <language/duchain/topducontext.h>
|
||
|
|
||
|
#include <kcolorscheme.h>
|
||
|
|
||
|
using namespace KTextEditor;
|
||
|
using namespace KDevelop;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
QColor colorForSeverity(ProblemData::Severity severity)
|
||
|
{
|
||
|
KColorScheme scheme(QPalette::Active);
|
||
|
switch (severity) {
|
||
|
case ProblemData::Error:
|
||
|
return scheme.foreground(KColorScheme::NegativeText).color();
|
||
|
case ProblemData::Warning:
|
||
|
return scheme.foreground(KColorScheme::NeutralText).color();
|
||
|
case ProblemData::Hint:
|
||
|
default:
|
||
|
return scheme.foreground(KColorScheme::PositiveText).color();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
ProblemHighlighter::ProblemHighlighter(KTextEditor::Document* document)
|
||
|
: m_document(document)
|
||
|
{
|
||
|
Q_ASSERT(m_document);
|
||
|
|
||
|
foreach(KTextEditor::View* view, m_document->views())
|
||
|
viewCreated(document, view);
|
||
|
|
||
|
connect(m_document, SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)), this, SLOT(viewCreated(KTextEditor::Document*,KTextEditor::View*)));
|
||
|
connect(ICore::self()->languageController()->completionSettings(), SIGNAL(settingsChanged(ICompletionSettings*)), this, SLOT(settingsChanged()));
|
||
|
connect(m_document, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)),
|
||
|
this, SLOT(aboutToInvalidateMovingInterfaceContent()));
|
||
|
connect(m_document, SIGNAL(aboutToRemoveText(KTextEditor::Range)),
|
||
|
this, SLOT(aboutToRemoveText(KTextEditor::Range)));
|
||
|
connect(m_document, SIGNAL(reloaded(KTextEditor::Document*)),
|
||
|
this, SLOT(documentReloaded()));
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::settingsChanged()
|
||
|
{
|
||
|
//Re-highlight
|
||
|
setProblems(m_problems);
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::viewCreated(Document* , View* view)
|
||
|
{
|
||
|
KTextEditor::TextHintInterface* iface = dynamic_cast<KTextEditor::TextHintInterface*>(view);
|
||
|
if( !iface )
|
||
|
return;
|
||
|
|
||
|
connect(view, SIGNAL(needTextHint(KTextEditor::Cursor,QString&)), this, SLOT(textHintRequested(KTextEditor::Cursor,QString&)));
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::textHintRequested(const KTextEditor::Cursor& pos, QString& )
|
||
|
{
|
||
|
KTextEditor::View* view = qobject_cast<KTextEditor::View*>(sender());
|
||
|
Q_ASSERT(view);
|
||
|
|
||
|
KTextEditor::MovingInterface* moving = dynamic_cast<KTextEditor::MovingInterface*>(m_document.data());
|
||
|
if(moving) {
|
||
|
///@todo Sort the ranges when writing them, and do binary search instead of linear
|
||
|
foreach(MovingRange* range, m_topHLRanges) {
|
||
|
if(m_problemsForRanges.contains(range) && range->contains(pos))
|
||
|
{
|
||
|
//There is a problem which's range contains the cursor
|
||
|
ProblemPointer problem = m_problemsForRanges[range];
|
||
|
if (problem->source() == ProblemData::ToDo) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
KDevelop::AbstractNavigationWidget* widget = new KDevelop::AbstractNavigationWidget;
|
||
|
widget->setContext(NavigationContextPointer(new ProblemNavigationContext(problem)));
|
||
|
|
||
|
KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, QCursor::pos() + QPoint(20, 40), widget);
|
||
|
|
||
|
tooltip->resize( widget->sizeHint() + QSize(10, 10) );
|
||
|
ActiveToolTip::showToolTip(tooltip, 99, "problem-tooltip");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ProblemHighlighter::~ProblemHighlighter()
|
||
|
{
|
||
|
if(m_topHLRanges.isEmpty() || !m_document)
|
||
|
return;
|
||
|
|
||
|
qDeleteAll(m_topHLRanges);
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::setProblems(const QList<KDevelop::ProblemPointer>& problems)
|
||
|
{
|
||
|
if(!m_document)
|
||
|
return;
|
||
|
|
||
|
KTextEditor::MovingInterface* iface = dynamic_cast<KTextEditor::MovingInterface*>(m_document.data());
|
||
|
Q_ASSERT(iface);
|
||
|
|
||
|
const bool hadProblems = !m_problems.isEmpty();
|
||
|
m_problems = problems;
|
||
|
|
||
|
qDeleteAll(m_topHLRanges);
|
||
|
m_topHLRanges.clear();
|
||
|
m_problemsForRanges.clear();
|
||
|
|
||
|
IndexedString url( m_document->url() );
|
||
|
|
||
|
///TODO: create a better MarkInterface that makes it possible to add the marks to the scrollbar
|
||
|
/// but having no background.
|
||
|
/// also make it nicer together with other plugins, this would currently fail with
|
||
|
/// this method...
|
||
|
const uint errorMarkType = KTextEditor::MarkInterface::Error;
|
||
|
const uint warningMarkType = KTextEditor::MarkInterface::Warning;
|
||
|
KTextEditor::MarkInterface* markIface = dynamic_cast<KTextEditor::MarkInterface*>(m_document.data());
|
||
|
if (markIface && hadProblems) {
|
||
|
// clear previously added marks
|
||
|
foreach(KTextEditor::Mark* mark, markIface->marks().values()) {
|
||
|
if (mark->type == errorMarkType || mark->type == warningMarkType) {
|
||
|
markIface->removeMark(mark->line, mark->type);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DUChainReadLocker lock;
|
||
|
|
||
|
TopDUContext* top = DUChainUtils::standardContextForUrl(m_document->url());
|
||
|
|
||
|
foreach (const KDevelop::ProblemPointer& problem, problems) {
|
||
|
if (problem->finalLocation().document != url || !problem->finalLocation().isValid())
|
||
|
continue;
|
||
|
|
||
|
SimpleRange range;
|
||
|
if(top)
|
||
|
range = top->transformFromLocalRevision(RangeInRevision::castFromSimpleRange(problem->finalLocation()));
|
||
|
else
|
||
|
range = problem->finalLocation();
|
||
|
|
||
|
if(range.end.line >= m_document->lines())
|
||
|
range.end = SimpleCursor(m_document->endOfLine(m_document->lines()-1));
|
||
|
|
||
|
if(range.isEmpty())
|
||
|
range.end.column += 1;
|
||
|
|
||
|
KTextEditor::MovingRange* problemRange = iface->newMovingRange(range.textRange());
|
||
|
|
||
|
m_problemsForRanges.insert(problemRange, problem);
|
||
|
m_topHLRanges.append(problemRange);
|
||
|
|
||
|
if(problem->source() != ProblemData::ToDo && (problem->severity() != ProblemData::Hint
|
||
|
|| ICore::self()->languageController()->completionSettings()->highlightSemanticProblems()))
|
||
|
{
|
||
|
KTextEditor::Attribute::Ptr attribute(new KTextEditor::Attribute());
|
||
|
attribute->setUnderlineStyle(QTextCharFormat::WaveUnderline);
|
||
|
attribute->setUnderlineColor(colorForSeverity(problem->severity()));
|
||
|
problemRange->setAttribute(attribute);
|
||
|
}
|
||
|
|
||
|
if (markIface && ICore::self()->languageController()->completionSettings()->highlightProblematicLines()) {
|
||
|
uint mark;
|
||
|
if (problem->severity() == ProblemData::Error) {
|
||
|
mark = errorMarkType;
|
||
|
} else if (problem->severity() == ProblemData::Warning) {
|
||
|
mark = warningMarkType;
|
||
|
} else {
|
||
|
continue;
|
||
|
}
|
||
|
markIface->addMark(problem->finalLocation().start.line, mark);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::aboutToInvalidateMovingInterfaceContent()
|
||
|
{
|
||
|
qDeleteAll(m_topHLRanges);
|
||
|
m_topHLRanges.clear();
|
||
|
m_problemsForRanges.clear();
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::aboutToRemoveText( const KTextEditor::Range& range )
|
||
|
{
|
||
|
if (range.onSingleLine()) { // no need to optimize this
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QList< MovingRange* >::iterator it = m_topHLRanges.begin();
|
||
|
while(it != m_topHLRanges.end()) {
|
||
|
if (range.contains((*it)->toRange())) {
|
||
|
m_problemsForRanges.remove(*it);
|
||
|
delete (*it);
|
||
|
it = m_topHLRanges.erase(it);
|
||
|
} else {
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ProblemHighlighter::documentReloaded()
|
||
|
{
|
||
|
setProblems(m_problems);
|
||
|
}
|
||
|
|
||
|
#include "moc_problemhighlighter.cpp"
|