• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdevelop API Reference
  • KDE Home
  • Contact Us
 

kdevelop/kdevplatform/outputview

  • extragear
  • kdevelop
  • kdevelop
  • kdevplatform
  • outputview
outputmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * This file is part of KDevelop *
3  * Copyright 2007 Andreas Pakulat <[email protected]> *
4  * Copyright 2010 Aleix Pol Gonzalez <[email protected]> *
5  * Copyright (C) 2012 Morten Danielsen Volden [email protected] *
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU Library General Public License as *
9  * published by the Free Software Foundation; either version 2 of the *
10  * License, or (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU Library General Public *
18  * License along with this program; if not, write to the *
19  * Free Software Foundation, Inc., *
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
21  ***************************************************************************/
22 
23 #include "outputmodel.h"
24 #include "filtereditem.h"
25 #include "outputfilteringstrategies.h"
26 #include "debug.h"
27 
28 #include <interfaces/icore.h>
29 #include <interfaces/idocumentcontroller.h>
30 #include <util/kdevstringhandler.h>
31 
32 #include <QStringList>
33 #include <QTimer>
34 #include <QThread>
35 #include <QFont>
36 #include <QFontDatabase>
37 
38 #include <functional>
39 #include <set>
40 
41 Q_DECLARE_METATYPE(QVector<KDevelop::FilteredItem>)
42 
43 namespace KDevelop
44 {
45 
51 static const int BATCH_SIZE = 50;
52 
58 static const int BATCH_AGGREGATE_TIME_DELAY = 50;
59 
60 class ParseWorker : public QObject
61 {
62  Q_OBJECT
63 public:
64  ParseWorker()
65  : QObject(nullptr)
66  , m_filter(new NoFilterStrategy)
67  , m_timer(new QTimer(this))
68  {
69  m_timer->setInterval(BATCH_AGGREGATE_TIME_DELAY);
70  m_timer->setSingleShot(true);
71  connect(m_timer, &QTimer::timeout, this, &ParseWorker::process);
72  }
73 
74 public Q_SLOTS:
75  void changeFilterStrategy( KDevelop::IFilterStrategy* newFilterStrategy )
76  {
77  m_filter = QSharedPointer<IFilterStrategy>( newFilterStrategy );
78  }
79 
80  void addLines( const QStringList& lines )
81  {
82  m_cachedLines << lines;
83 
84  if (m_cachedLines.size() >= BATCH_SIZE) {
85  // if enough lines were added, process immediately
86  m_timer->stop();
87  process();
88  } else if (!m_timer->isActive()) {
89  m_timer->start();
90  }
91  }
92 
93  void flushBuffers()
94  {
95  m_timer->stop();
96  process();
97  emit allDone();
98  }
99 
100 Q_SIGNALS:
101  void parsedBatch(const QVector<KDevelop::FilteredItem>& filteredItems);
102  void progress(const KDevelop::IFilterStrategy::Progress& progress);
103  void allDone();
104 
105 private Q_SLOTS:
109  void process()
110  {
111  QVector<KDevelop::FilteredItem> filteredItems;
112  filteredItems.reserve(qMin(BATCH_SIZE, m_cachedLines.size()));
113 
114  // apply pre-filtering functions
115  std::transform(m_cachedLines.constBegin(), m_cachedLines.constEnd(),
116  m_cachedLines.begin(), &KDevelop::stripAnsiSequences);
117 
118  // apply filtering strategy
119  for (const QString& line : qAsConst(m_cachedLines)) {
120  FilteredItem item = m_filter->errorInLine(line);
121  if( item.type == FilteredItem::InvalidItem ) {
122  item = m_filter->actionInLine(line);
123  }
124 
125  filteredItems << item;
126 
127  auto progress = m_filter->progressInLine(line);
128  if (progress.percent >= 0 && m_progress.percent != progress.percent) {
129  m_progress = progress;
130  emit this->progress(m_progress);
131  }
132 
133  if( filteredItems.size() == BATCH_SIZE ) {
134  emit parsedBatch(filteredItems);
135  filteredItems.clear();
136  filteredItems.reserve(qMin(BATCH_SIZE, m_cachedLines.size()));
137  }
138  }
139 
140  // Make sure to emit the rest as well
141  if( !filteredItems.isEmpty() ) {
142  emit parsedBatch(filteredItems);
143  }
144  m_cachedLines.clear();
145  }
146 
147 private:
148  QSharedPointer<IFilterStrategy> m_filter;
149  QStringList m_cachedLines;
150 
151  QTimer* m_timer;
152  IFilterStrategy::Progress m_progress;
153 };
154 
155 class ParsingThread
156 {
157 public:
158  ParsingThread()
159  {
160  m_thread.setObjectName(QStringLiteral("OutputFilterThread"));
161  }
162  virtual ~ParsingThread()
163  {
164  if (m_thread.isRunning()) {
165  m_thread.quit();
166  m_thread.wait();
167  }
168  }
169  void addWorker(ParseWorker* worker)
170  {
171  if (!m_thread.isRunning()) {
172  m_thread.start();
173  }
174  worker->moveToThread(&m_thread);
175  }
176 private:
177  QThread m_thread;
178 };
179 
180 Q_GLOBAL_STATIC(ParsingThread, s_parsingThread)
181 
182 class OutputModelPrivate
183 {
184 public:
185  explicit OutputModelPrivate( OutputModel* model, const QUrl& builddir = QUrl() );
186  ~OutputModelPrivate();
187  bool isValidIndex( const QModelIndex&, int currentRowCount ) const;
188 
189  OutputModel* model;
190  ParseWorker* worker;
191 
192  QVector<FilteredItem> m_filteredItems;
193  // We use std::set because that is ordered
194  std::set<int> m_errorItems; // Indices of all items that we want to move to using previous and next
195  QUrl m_buildDir;
196 
197  void linesParsed(const QVector<KDevelop::FilteredItem>& items)
198  {
199  model->beginInsertRows( QModelIndex(), model->rowCount(), model->rowCount() + items.size() - 1);
200 
201  m_filteredItems.reserve(m_filteredItems.size() + items.size());
202  for (const FilteredItem& item : items) {
203  if( item.type == FilteredItem::ErrorItem ) {
204  m_errorItems.insert(m_filteredItems.size());
205  }
206  m_filteredItems << item;
207  }
208 
209  model->endInsertRows();
210  }
211 };
212 
213 OutputModelPrivate::OutputModelPrivate( OutputModel* model_, const QUrl& builddir)
214 : model(model_)
215 , worker(new ParseWorker )
216 , m_buildDir( builddir )
217 {
218  qRegisterMetaType<QVector<KDevelop::FilteredItem> >();
219  qRegisterMetaType<KDevelop::IFilterStrategy*>();
220  qRegisterMetaType<KDevelop::IFilterStrategy::Progress>();
221 
222  s_parsingThread->addWorker(worker);
223  model->connect(worker, &ParseWorker::parsedBatch,
224  model, [=] (const QVector<KDevelop::FilteredItem>& items) { linesParsed(items); });
225  model->connect(worker, &ParseWorker::allDone,
226  model, &OutputModel::allDone);
227  model->connect(worker, &ParseWorker::progress,
228  model, &OutputModel::progress);
229 }
230 
231 bool OutputModelPrivate::isValidIndex( const QModelIndex& idx, int currentRowCount ) const
232 {
233  return ( idx.isValid() && idx.row() >= 0 && idx.row() < currentRowCount && idx.column() == 0 );
234 }
235 
236 OutputModelPrivate::~OutputModelPrivate()
237 {
238  worker->deleteLater();
239 }
240 
241 OutputModel::OutputModel( const QUrl& builddir, QObject* parent )
242 : QAbstractListModel(parent)
243 , d_ptr(new OutputModelPrivate(this, builddir))
244 {
245 }
246 
247 OutputModel::OutputModel( QObject* parent )
248  : QAbstractListModel(parent)
249  , d_ptr(new OutputModelPrivate(this))
250 {
251 }
252 
253 OutputModel::~OutputModel() = default;
254 
255 QVariant OutputModel::data(const QModelIndex& idx , int role ) const
256 {
257  Q_D(const OutputModel);
258 
259  if( d->isValidIndex(idx, rowCount()) )
260  {
261  switch( role )
262  {
263  case Qt::DisplayRole:
264  return d->m_filteredItems.at( idx.row() ).originalLine;
265  case OutputModel::OutputItemTypeRole:
266  return static_cast<int>(d->m_filteredItems.at( idx.row() ).type);
267  case Qt::FontRole:
268  return QFontDatabase::systemFont(QFontDatabase::FixedFont);
269  }
270  }
271  return QVariant();
272 }
273 
274 int OutputModel::rowCount( const QModelIndex& parent ) const
275 {
276  Q_D(const OutputModel);
277 
278  if( !parent.isValid() )
279  return d->m_filteredItems.count();
280  return 0;
281 }
282 
283 QVariant OutputModel::headerData( int, Qt::Orientation, int ) const
284 {
285  return QVariant();
286 }
287 
288 void OutputModel::activate( const QModelIndex& index )
289 {
290  Q_D(OutputModel);
291 
292  if( index.model() != this || !d->isValidIndex(index, rowCount()) )
293  {
294  return;
295  }
296  qCDebug(OUTPUTVIEW) << "Model activated" << index.row();
297 
298 
299  FilteredItem item = d->m_filteredItems.at( index.row() );
300  if( item.isActivatable )
301  {
302  qCDebug(OUTPUTVIEW) << "activating:" << item.lineNo << item.url;
303  KTextEditor::Cursor range( item.lineNo, item.columnNo );
304  KDevelop::IDocumentController *docCtrl = KDevelop::ICore::self()->documentController();
305  QUrl url = item.url;
306  if (item.url.isEmpty()) {
307  qCWarning(OUTPUTVIEW) << "trying to open empty url";
308  return;
309  }
310  if(url.isRelative()) {
311  url = d->m_buildDir.resolved(url);
312  }
313  Q_ASSERT(!url.isRelative());
314  docCtrl->openDocument( url, range );
315  } else {
316  qCDebug(OUTPUTVIEW) << "not an activateable item";
317  }
318 }
319 
320 QModelIndex OutputModel::firstHighlightIndex()
321 {
322  Q_D(OutputModel);
323 
324  if( !d->m_errorItems.empty() ) {
325  return index( *d->m_errorItems.begin(), 0, QModelIndex() );
326  }
327 
328  for( int row = 0; row < rowCount(); ++row ) {
329  if( d->m_filteredItems.at( row ).isActivatable ) {
330  return index( row, 0, QModelIndex() );
331  }
332  }
333 
334  return QModelIndex();
335 }
336 
337 QModelIndex OutputModel::nextHighlightIndex( const QModelIndex &currentIdx )
338 {
339  Q_D(OutputModel);
340 
341  int startrow = d->isValidIndex(currentIdx, rowCount()) ? currentIdx.row() + 1 : 0;
342 
343  if( !d->m_errorItems.empty() )
344  {
345  qCDebug(OUTPUTVIEW) << "searching next error";
346  // Jump to the next error item
347  auto next = d->m_errorItems.lower_bound( startrow );
348  if( next == d->m_errorItems.end() )
349  next = d->m_errorItems.begin();
350 
351  return index( *next, 0, QModelIndex() );
352  }
353 
354  for( int row = 0; row < rowCount(); ++row )
355  {
356  int currow = (startrow + row) % rowCount();
357  if( d->m_filteredItems.at( currow ).isActivatable )
358  {
359  return index( currow, 0, QModelIndex() );
360  }
361  }
362  return QModelIndex();
363 }
364 
365 QModelIndex OutputModel::previousHighlightIndex( const QModelIndex &currentIdx )
366 {
367  Q_D(OutputModel);
368 
369  //We have to ensure that startrow is >= rowCount - 1 to get a positive value from the % operation.
370  int startrow = rowCount() + (d->isValidIndex(currentIdx, rowCount()) ? currentIdx.row() : rowCount()) - 1;
371 
372  if(!d->m_errorItems.empty())
373  {
374  qCDebug(OUTPUTVIEW) << "searching previous error";
375 
376  // Jump to the previous error item
377  auto previous = d->m_errorItems.lower_bound( currentIdx.row() );
378 
379  if( previous == d->m_errorItems.begin() )
380  previous = d->m_errorItems.end();
381 
382  --previous;
383 
384  return index( *previous, 0, QModelIndex() );
385  }
386 
387  for ( int row = 0; row < rowCount(); ++row )
388  {
389  int currow = (startrow - row) % rowCount();
390  if( d->m_filteredItems.at( currow ).isActivatable )
391  {
392  return index( currow, 0, QModelIndex() );
393  }
394  }
395  return QModelIndex();
396 }
397 
398 QModelIndex OutputModel::lastHighlightIndex()
399 {
400  Q_D(OutputModel);
401 
402  if( !d->m_errorItems.empty() ) {
403  return index( *d->m_errorItems.rbegin(), 0, QModelIndex() );
404  }
405 
406  for( int row = rowCount()-1; row >=0; --row ) {
407  if( d->m_filteredItems.at( row ).isActivatable ) {
408  return index( row, 0, QModelIndex() );
409  }
410  }
411 
412  return QModelIndex();
413 }
414 
415 void OutputModel::setFilteringStrategy(const OutputFilterStrategy& currentStrategy)
416 {
417  Q_D(OutputModel);
418 
419  // TODO: Turn into factory, decouple from OutputModel
420  IFilterStrategy* filter = nullptr;
421  switch( currentStrategy )
422  {
423  case NoFilter:
424  filter = new NoFilterStrategy;
425  break;
426  case CompilerFilter:
427  filter = new CompilerFilterStrategy( d->m_buildDir );
428  break;
429  case ScriptErrorFilter:
430  filter = new ScriptErrorFilterStrategy;
431  break;
432  case NativeAppErrorFilter:
433  filter = new NativeAppErrorFilterStrategy;
434  break;
435  case StaticAnalysisFilter:
436  filter = new StaticAnalysisFilterStrategy;
437  break;
438  }
439  if (!filter) {
440  filter = new NoFilterStrategy;
441  }
442 
443  QMetaObject::invokeMethod(d->worker, "changeFilterStrategy",
444  Q_ARG(KDevelop::IFilterStrategy*, filter));
445 }
446 
447 void OutputModel::setFilteringStrategy(IFilterStrategy* filterStrategy)
448 {
449  Q_D(OutputModel);
450 
451  QMetaObject::invokeMethod(d->worker, "changeFilterStrategy",
452  Q_ARG(KDevelop::IFilterStrategy*, filterStrategy));
453 }
454 
455 void OutputModel::appendLines( const QStringList& lines )
456 {
457  Q_D(OutputModel);
458 
459  if( lines.isEmpty() )
460  return;
461 
462  QMetaObject::invokeMethod(d->worker, "addLines",
463  Q_ARG(QStringList, lines));
464 }
465 
466 void OutputModel::appendLine( const QString& line )
467 {
468  appendLines( QStringList() << line );
469 }
470 
471 void OutputModel::ensureAllDone()
472 {
473  Q_D(OutputModel);
474 
475  QMetaObject::invokeMethod(d->worker, "flushBuffers");
476 }
477 
478 void OutputModel::clear()
479 {
480  Q_D(OutputModel);
481 
482  ensureAllDone();
483  beginResetModel();
484  d->m_filteredItems.clear();
485  endResetModel();
486 }
487 
488 }
489 
490 #include "outputmodel.moc"
491 #include "moc_outputmodel.cpp"
QModelIndex
KDevelop::FilteredItem::isActivatable
bool isActivatable
Definition: filtereditem.h:59
KDevelop::OutputModel::setFilteringStrategy
void setFilteringStrategy(const OutputFilterStrategy &currentStrategy)
Definition: outputmodel.cpp:415
KDevelop::OutputModel::NoFilter
Definition: outputmodel.h:50
KDevelop::OutputModel::OutputModel
OutputModel(const QUrl &builddir, QObject *parent=nullptr)
Definition: outputmodel.cpp:241
KDevelop::FilteredItem::lineNo
int lineNo
lineNo starts with 0
Definition: filtereditem.h:62
KDevelop::IFilterStrategy
Interface class for filtering output.
Definition: ifilterstrategy.h:40
KDevelop::StaticAnalysisFilterStrategy
This filter strategy filters out errors (no actions) from Static code analysis tools (Cppcheck...
Definition: outputfilteringstrategies.h:112
KDevelop::OutputModel
Definition: outputmodel.h:37
KDevelop::OutputModel::nextHighlightIndex
QModelIndex nextHighlightIndex(const QModelIndex &current) override
Called when the user wants to see next item.
Definition: outputmodel.cpp:337
KDevelop::OutputModel::OutputItemTypeRole
Definition: outputmodel.h:45
KDevelop::OutputModel::firstHighlightIndex
QModelIndex firstHighlightIndex() override
Called when the user wants to see first item.
Definition: outputmodel.cpp:320
KDevelop::OutputModel::previousHighlightIndex
QModelIndex previousHighlightIndex(const QModelIndex &current) override
Called when the user wants to see previous item.
Definition: outputmodel.cpp:365
KDevelop::OutputModel::StaticAnalysisFilter
Definition: outputmodel.h:54
QUrl::isEmpty
bool isEmpty() const
filtereditem.h
KDevelop::OutputModel::appendLines
void appendLines(const QStringList &)
Definition: outputmodel.cpp:455
QAbstractItemModel::beginResetModel
void beginResetModel()
QTimer::timeout
void timeout()
KDevelop::CompilerFilterStrategy
This filter strategy checks if a given line contains output that is defined as an error (or an action...
Definition: outputfilteringstrategies.h:61
QVector::clear
void clear()
QModelIndex::isValid
bool isValid() const
KDevelop::FilteredItem
Holds all metadata of a given compiler/script/whatever output line.
Definition: filtereditem.h:40
QTimer
KDevelop::OutputModel::ScriptErrorFilter
Definition: outputmodel.h:52
KDevelop::OutputModel::headerData
QVariant headerData(int, Qt::Orientation, int=Qt::DisplayRole) const override
Definition: outputmodel.cpp:283
QSharedPointer
KDevelop::FilteredItem::url
QUrl url
Definition: filtereditem.h:60
KDevelop::OutputModel::activate
void activate(const QModelIndex &index) override
IOutputViewModel interfaces.
Definition: outputmodel.cpp:288
QObject
KDevelop::OutputModel::allDone
void allDone()
KDevelop::OutputModel::ensureAllDone
void ensureAllDone()
Definition: outputmodel.cpp:471
KDevelop::OutputModel::appendLine
void appendLine(const QString &)
Definition: outputmodel.cpp:466
QAbstractListModel::index
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
QAbstractListModel
QList::isEmpty
bool isEmpty() const
outputmodel.h
QModelIndex::row
int row() const
QString
KDevelop::OutputModel::progress
void progress(const KDevelop::IFilterStrategy::Progress &progress)
If the current filter strategy supports it, reports progress information.
outputfilteringstrategies.h
This file contains Concrete strategies for filtering output in KDevelop output model.
QStringList
QVector::reserve
void reserve(int size)
QUrl
KDevelop::OutputModel::OutputFilterStrategy
OutputFilterStrategy
Definition: outputmodel.h:48
KDevelop::OutputModel::lastHighlightIndex
QModelIndex lastHighlightIndex() override
Called when the user wants to see last item.
Definition: outputmodel.cpp:398
QMetaObject::invokeMethod
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
KDevelop::FilteredItem::type
FilteredOutputItemType type
Definition: filtereditem.h:58
KDevelop::NoFilterStrategy
This filter strategy is for not applying any filtering at all.
Definition: outputfilteringstrategies.h:45
KDevelop::BATCH_SIZE
static const int BATCH_SIZE
Number of lines that are processed in one go before we notify the GUI thread about the result...
Definition: outputmodel.cpp:51
KDevelop::ScriptErrorFilterStrategy
This filter strategy filters out errors (no actions) from Python and PHP scripts. ...
Definition: outputfilteringstrategies.h:82
QObject::QObject
QObject(QObject *parent)
QModelIndex::model
const QAbstractItemModel * model() const
QVector< KDevelop::FilteredItem >
KDevelop::OutputModel::NativeAppErrorFilter
Definition: outputmodel.h:53
KDevelop::FilteredItem::columnNo
int columnNo
columnNo starts with 0
Definition: filtereditem.h:64
KDevelop::OutputModel::~OutputModel
~OutputModel() override
QVector::isEmpty
bool isEmpty() const
KDevelop::OutputModel::clear
void clear()
Definition: outputmodel.cpp:478
KDevelop::NativeAppErrorFilterStrategy
This filter strategy filters out errors (no actions) from runtime debug output of native applications...
Definition: outputfilteringstrategies.h:100
QUrl::isRelative
bool isRelative() const
KDevelop::OutputModel::data
QVariant data(const QModelIndex &, int=Qt::DisplayRole) const override
QAbstractItemModel interfaces.
Definition: outputmodel.cpp:255
QUrl::resolved
QUrl resolved(const QUrl &relative) const
KDevelop::OutputModel::rowCount
int rowCount(const QModelIndex &=QModelIndex()) const override
Definition: outputmodel.cpp:274
QModelIndex::column
int column() const
KDevelop::IFilterStrategy::Progress
Definition: ifilterstrategy.h:46
KDevelop::OutputModel::CompilerFilter
Definition: outputmodel.h:51
KDevelop::BATCH_AGGREGATE_TIME_DELAY
static const int BATCH_AGGREGATE_TIME_DELAY
Time in ms that we wait in the parse worker for new incoming lines before actually processing them...
Definition: outputmodel.cpp:58
QThread
QAbstractItemModel::endResetModel
void endResetModel()
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVector::size
int size() const
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Dec 16 2019 02:15:05 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kdevelop/kdevplatform/outputview

Skip menu "kdevelop/kdevplatform/outputview"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdevelop API Reference

Skip menu "kdevelop API Reference"
  •   kdevplatform
  •     debugger
  •     documentation
  •     interfaces
  •     language
  •       assistant
  •       backgroundparser
  •       checks
  •       classmodel
  •       codecompletion
  •       codegen
  •       duchain
  •       editor
  •       highlighting
  •       interfaces
  •       util
  •     outputview
  •     project
  •     serialization
  •     shell
  •     sublime
  •     tests
  •     util
  •     vcs

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal