File indexing completed on 2024-05-05 04:40:11
0001 /* 0002 SPDX-FileCopyrightText: 2006 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "patchhighlighter.h" 0008 0009 #ifdef WITH_KOMPAREDIFF2_5_4_OR_NEWER 0010 #include <KompareDiff2/Difference> 0011 #include <KompareDiff2/DiffModel> 0012 #else 0013 #include <libkomparediff2/difference.h> 0014 #include <libkomparediff2/diffmodel.h> 0015 #endif 0016 0017 #include "patchreview.h" 0018 #include "debug.h" 0019 0020 #include <KColorScheme> 0021 #include <KIconEffect> 0022 #include <KLocalizedString> 0023 #include <KParts/MainWindow> 0024 #include <KTextEditor/View> 0025 #include <KTextEditor/Cursor> 0026 0027 #include <KTextEditor/MovingInterface> 0028 #include <KTextEditor/MarkInterface> 0029 0030 #include <interfaces/icore.h> 0031 #include <interfaces/idocument.h> 0032 #include <interfaces/iuicontroller.h> 0033 #include <language/highlighting/colorcache.h> 0034 #include <util/activetooltip.h> 0035 #include <sublime/message.h> 0036 0037 #include <QApplication> 0038 #include <QPointer> 0039 #include <QTextBrowser> 0040 #include <QTextDocument> 0041 #include <QVBoxLayout> 0042 #include <QWidget> 0043 0044 using namespace KDevelop; 0045 0046 namespace 0047 { 0048 QPointer<QWidget> currentTooltip; 0049 KTextEditor::MovingRange* currentTooltipMark; 0050 0051 0052 QSize sizeHintForHtml( const QString& html, QSize maxSize ) { 0053 QTextDocument doc; 0054 doc.setHtml( html ); 0055 0056 QSize ret; 0057 if( doc.idealWidth() > maxSize.width() ) { 0058 doc.setPageSize( QSize( maxSize.width(), 30 ) ); 0059 ret.setWidth( maxSize.width() ); 0060 }else{ 0061 ret.setWidth( doc.idealWidth() ); 0062 } 0063 ret.setHeight( doc.size().height() ); 0064 if( ret.height() > maxSize.height() ) 0065 ret.setHeight( maxSize.height() ); 0066 return ret; 0067 } 0068 0069 } 0070 0071 const unsigned int PatchHighlighter::m_allmarks = 0072 KTextEditor::MarkInterface::markType22 | KTextEditor::MarkInterface::markType23 | 0073 KTextEditor::MarkInterface::markType24 | KTextEditor::MarkInterface::markType25 | 0074 KTextEditor::MarkInterface::markType26 | KTextEditor::MarkInterface::markType27; 0075 0076 void PatchHighlighter::showToolTipForMark(const QPoint& pos, KTextEditor::MovingRange* markRange) 0077 { 0078 if( currentTooltipMark == markRange && currentTooltip ) 0079 return; 0080 delete currentTooltip; 0081 0082 //Got the difference 0083 Diff2::Difference* diff = m_ranges[markRange]; 0084 0085 QString html; 0086 #if 0 0087 if( diff->hasConflict() ) 0088 html += i18n( "<b><span style=\"color:red\">Conflict</span></b><br/>" ); 0089 #endif 0090 0091 Diff2::DifferenceStringList lines; 0092 0093 html += QLatin1String("<b>"); 0094 if( diff->applied() ) { 0095 if( !m_plugin->patch()->isAlreadyApplied() ) 0096 html += i18n( "Applied.<br/>" ); 0097 0098 if( isInsertion( diff ) ) { 0099 html += i18n( "Insertion<br/>" ); 0100 } else { 0101 if( isRemoval( diff ) ) 0102 html += i18n( "Removal<br/>" ); 0103 html += i18n( "Previous:<br/>" ); 0104 lines = diff->sourceLines(); 0105 } 0106 } else { 0107 if( m_plugin->patch()->isAlreadyApplied() ) 0108 html += i18n( "Reverted.<br/>" ); 0109 0110 if( isRemoval( diff ) ) { 0111 html += i18n( "Removal<br/>" ); 0112 } else { 0113 if( isInsertion( diff ) ) 0114 html += i18n( "Insertion<br/>" ); 0115 0116 html += i18n( "Alternative:<br/>" ); 0117 0118 lines = diff->destinationLines(); 0119 } 0120 } 0121 html += QLatin1String("</b>"); 0122 0123 for (auto* line : qAsConst(lines)) { 0124 uint currentPos = 0; 0125 const QString& string = line->string(); 0126 0127 const Diff2::MarkerList& markers = line->markerList(); 0128 0129 for (auto* marker : markers) { 0130 const QString spanText = string.mid( currentPos, marker->offset() - currentPos ).toHtmlEscaped(); 0131 if (marker->type() == Diff2::Marker::End && (currentPos != 0 || marker->offset() != static_cast<uint>( string.size()))) { 0132 html += QLatin1String("<b><span style=\"background:#FFBBBB\">") + spanText + QLatin1String("</span></b>"); 0133 }else{ 0134 html += spanText; 0135 } 0136 currentPos = marker->offset(); 0137 } 0138 0139 html += string.mid(currentPos, string.length()-currentPos).toHtmlEscaped() + QLatin1String("<br/>"); 0140 } 0141 0142 auto browser = new QTextBrowser; 0143 browser->setPalette( QApplication::palette() ); 0144 browser->setHtml( html ); 0145 0146 int maxHeight = 500; 0147 0148 browser->setMinimumSize( sizeHintForHtml( html, QSize( ( ICore::self()->uiController()->activeMainWindow()->width()*2 )/3, maxHeight ) ) ); 0149 browser->setMaximumSize( browser->minimumSize() + QSize( 10, 10 ) ); 0150 if( browser->minimumHeight() != maxHeight ) 0151 browser->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 0152 0153 auto* layout = new QVBoxLayout; 0154 layout->setContentsMargins(0, 0, 0, 0); 0155 layout->addWidget( browser ); 0156 0157 auto* tooltip = new KDevelop::ActiveToolTip( ICore::self()->uiController()->activeMainWindow(), pos + QPoint( 5, -browser->sizeHint().height() - 30 ) ); 0158 tooltip->setLayout( layout ); 0159 tooltip->resize( tooltip->sizeHint() + QSize( 10, 10 ) ); 0160 tooltip->move( pos - QPoint( 0, 20 + tooltip->height() ) ); 0161 tooltip->setHandleRect( QRect( pos - QPoint( 15, 15 ), pos + QPoint( 15, 15 ) ) ); 0162 0163 currentTooltip = tooltip; 0164 currentTooltipMark = markRange; 0165 0166 ActiveToolTip::showToolTip( tooltip ); 0167 } 0168 0169 void PatchHighlighter::markClicked( KTextEditor::Document* doc, const KTextEditor::Mark& mark, bool& handled ) { 0170 if( handled || !(mark.type & m_allmarks) ) 0171 return; 0172 0173 auto range_diff = rangeForMark(mark); 0174 m_applying = true; 0175 0176 if (range_diff.first) { 0177 handled = true; 0178 0179 KTextEditor::MovingRange *&range = range_diff.first; 0180 Diff2::Difference *&diff = range_diff.second; 0181 0182 QString currentText = doc->text( range->toRange() ); 0183 0184 removeLineMarker( range ); 0185 0186 QString sourceText; 0187 QString targetText; 0188 0189 for( int a = 0; a < diff->sourceLineCount(); ++a ) { 0190 sourceText += diff->sourceLineAt( a )->string(); 0191 if (!sourceText.endsWith(QLatin1Char('\n'))) 0192 sourceText += QLatin1Char('\n'); 0193 } 0194 0195 for( int a = 0; a < diff->destinationLineCount(); ++a ) { 0196 targetText += diff->destinationLineAt( a )->string(); 0197 if (!targetText.endsWith(QLatin1Char('\n'))) 0198 targetText += QLatin1Char('\n'); 0199 } 0200 0201 bool applied = diff->applied(); 0202 QString &replace(applied ? targetText : sourceText); 0203 QString &replaceWith(applied ? sourceText : targetText); 0204 0205 if( currentText.simplified() != replace.simplified() ) { 0206 const QString messageText = i18n("Could not apply the change: Text should be \"%1\", but is \"%2\".", replace, currentText); 0207 auto* message = new Sublime::Message(messageText, Sublime::Message::Error); 0208 ICore::self()->uiController()->postMessage(message); 0209 0210 m_applying = false; 0211 return; 0212 } 0213 0214 diff->apply(!applied); 0215 0216 KTextEditor::Cursor start = range->start().toCursor(); 0217 range->document()->replaceText( range->toRange(), replaceWith ); 0218 const uint replaceWithLines = replaceWith.count(QLatin1Char('\n')); 0219 KTextEditor::Range newRange( start, KTextEditor::Cursor(start.line() + replaceWithLines, start.column()) ); 0220 0221 range->setRange( newRange ); 0222 0223 addLineMarker( range, diff ); 0224 0225 { 0226 // After applying the change, show the tooltip again, mainly to update an old tooltip 0227 delete currentTooltip; 0228 currentTooltip = nullptr; 0229 bool h = false; 0230 markToolTipRequested( doc, mark, QCursor::pos(), h ); 0231 } 0232 } 0233 0234 m_applying = false; 0235 } 0236 0237 QPair<KTextEditor::MovingRange*, Diff2::Difference*> PatchHighlighter::rangeForMark( const KTextEditor::Mark &mark ) { 0238 if (!m_applying) { 0239 for( QMap<KTextEditor::MovingRange*, Diff2::Difference*>::const_iterator it = m_ranges.constBegin(); it != m_ranges.constEnd(); ++it ) { 0240 if (it.value() && it.key()->start().line() <= mark.line && mark.line <= it.key()->end().line()) { 0241 return qMakePair(it.key(), it.value()); 0242 } 0243 } 0244 } 0245 0246 return qMakePair(nullptr, nullptr); 0247 } 0248 0249 void PatchHighlighter::markToolTipRequested( KTextEditor::Document*, const KTextEditor::Mark& mark, QPoint pos, bool& handled ) { 0250 if( handled ) 0251 return; 0252 0253 if( mark.type & m_allmarks ) { 0254 //There is a mark in this line. Show the old text. 0255 auto range = rangeForMark(mark); 0256 if( range.first ) { 0257 showToolTipForMark( pos, range.first ); 0258 handled = true; 0259 } 0260 } 0261 } 0262 0263 bool PatchHighlighter::isInsertion( Diff2::Difference* diff ) { 0264 return diff->sourceLineCount() == 0; 0265 } 0266 0267 bool PatchHighlighter::isRemoval( Diff2::Difference* diff ) { 0268 return diff->destinationLineCount() == 0; 0269 } 0270 0271 void PatchHighlighter::performContentChange( KTextEditor::Document* doc, const QStringList& oldLines, const QStringList& newLines, int editLineNumber ) { 0272 QPair<QList<Diff2::Difference*>, QList<Diff2::Difference*> > diffChange = m_model->linesChanged( oldLines, newLines, editLineNumber ); 0273 const QList<Diff2::Difference*>& inserted = diffChange.first; 0274 const QList<Diff2::Difference*>& removed = diffChange.second; 0275 0276 for (Diff2::Difference* d : removed) { 0277 const auto sourceLines = d->sourceLines(); 0278 for (Diff2::DifferenceString* s : sourceLines) 0279 qCDebug(PLUGIN_PATCHREVIEW) << "removed source" << s->string(); 0280 const auto destinationLines = d->destinationLines(); 0281 for (Diff2::DifferenceString* s : destinationLines) 0282 qCDebug(PLUGIN_PATCHREVIEW) << "removed destination" << s->string(); 0283 } 0284 for (Diff2::Difference* d : inserted) { 0285 const auto sourceLines = d->sourceLines(); 0286 for (Diff2::DifferenceString* s : sourceLines) 0287 qCDebug(PLUGIN_PATCHREVIEW) << "inserted source" << s->string(); 0288 const auto destinationLines = d->destinationLines(); 0289 for (Diff2::DifferenceString* s : destinationLines) 0290 qCDebug(PLUGIN_PATCHREVIEW) << "inserted destination" << s->string(); 0291 } 0292 0293 // Remove all ranges that are in the same line (the line markers) 0294 for (auto it = m_ranges.begin(); it != m_ranges.end();) { 0295 if (removed.contains(it.value())) { 0296 KTextEditor::MovingRange* r = it.key(); 0297 removeLineMarker(r); // is altering m_ranges 0298 it = m_ranges.erase(it); 0299 0300 delete r; 0301 } else { 0302 ++it; 0303 } 0304 } 0305 qDeleteAll(removed); 0306 0307 auto* moving = qobject_cast<KTextEditor::MovingInterface*>(doc); 0308 if ( !moving ) 0309 return; 0310 0311 for (Diff2::Difference* diff : inserted) { 0312 int lineStart = diff->destinationLineNumber(); 0313 if ( lineStart > 0 ) { 0314 --lineStart; 0315 } 0316 int lineEnd = diff->destinationLineEnd(); 0317 if ( lineEnd > 0 ) { 0318 --lineEnd; 0319 } 0320 KTextEditor::Range newRange( lineStart, 0, lineEnd, 0 ); 0321 KTextEditor::MovingRange * r = moving->newMovingRange( newRange ); 0322 0323 m_ranges[r] = diff; 0324 addLineMarker( r, diff ); 0325 } 0326 } 0327 0328 void PatchHighlighter::textRemoved( KTextEditor::Document* doc, const KTextEditor::Range& range, const QString& oldText ) { 0329 if ( m_applying ) { // Do not interfere with patch application 0330 return; 0331 } 0332 qCDebug(PLUGIN_PATCHREVIEW) << "removal range" << range; 0333 qCDebug(PLUGIN_PATCHREVIEW) << "removed text" << oldText; 0334 0335 KTextEditor::Cursor cursor = range.start(); 0336 int startLine = cursor.line(); 0337 QStringList removedLines; 0338 QStringList remainingLines; 0339 if (startLine > 0) { 0340 QString above = doc->line(--startLine); 0341 removedLines << above; 0342 remainingLines << above; 0343 } 0344 const QString changed = doc->line(cursor.line()) + QLatin1Char('\n'); 0345 removedLines << changed.midRef(0, cursor.column()) + oldText + changed.midRef(cursor.column()); 0346 remainingLines << changed; 0347 if (doc->documentRange().end().line() > cursor.line()) { 0348 QString below = doc->line(cursor.line() + 1); 0349 removedLines << below; 0350 remainingLines << below; 0351 } 0352 0353 performContentChange(doc, removedLines, remainingLines, startLine + 1); 0354 } 0355 0356 void PatchHighlighter::newlineRemoved(KTextEditor::Document* doc, int line) { 0357 if ( m_applying ) { // Do not interfere with patch application 0358 return; 0359 } 0360 qCDebug(PLUGIN_PATCHREVIEW) << "remove newline" << line; 0361 0362 KTextEditor::Cursor cursor = m_doc->cursorPosition(); 0363 0364 int startLine = line - 1; 0365 QStringList removedLines; 0366 QStringList remainingLines; 0367 if (startLine > 0) { 0368 QString above = doc->line(--startLine); 0369 removedLines << above; 0370 remainingLines << above; 0371 } 0372 QString changed = doc->line(line - 1); 0373 if (cursor.line() == line - 1) { 0374 removedLines << changed.mid(0, cursor.column()); 0375 removedLines << changed.mid(cursor.column()); 0376 } else { 0377 removedLines << changed; 0378 removedLines << QString(); 0379 } 0380 remainingLines << changed; 0381 if (doc->documentRange().end().line() >= line) { 0382 QString below = doc->line(line); 0383 removedLines << below; 0384 remainingLines << below; 0385 } 0386 0387 performContentChange(doc, removedLines, remainingLines, startLine + 1); 0388 } 0389 0390 void PatchHighlighter::documentReloaded(KTextEditor::Document* doc) 0391 { 0392 qCDebug(PLUGIN_PATCHREVIEW) << "re-doing"; 0393 //The document was loaded / reloaded 0394 if ( !m_model->differences() ) 0395 return; 0396 auto* moving = qobject_cast<KTextEditor::MovingInterface*>(doc); 0397 if ( !moving ) 0398 return; 0399 0400 auto* markIface = qobject_cast<KTextEditor::MarkInterface*>(doc); 0401 if( !markIface ) 0402 return; 0403 0404 clear(); 0405 0406 constexpr int markPixmapSize = 32; 0407 KColorScheme scheme( QPalette::Active ); 0408 0409 QImage tintedInsertion = QIcon::fromTheme(QStringLiteral("insert-text")).pixmap(markPixmapSize, markPixmapSize).toImage(); 0410 KIconEffect::colorize( tintedInsertion, scheme.foreground( KColorScheme::NegativeText ).color(), 1.0 ); 0411 QImage tintedRemoval = QIcon::fromTheme(QStringLiteral("edit-delete")).pixmap(markPixmapSize, markPixmapSize).toImage(); 0412 KIconEffect::colorize( tintedRemoval, scheme.foreground( KColorScheme::NegativeText ).color(), 1.0 ); 0413 QImage tintedChange = QIcon::fromTheme(QStringLiteral("text-field")).pixmap(markPixmapSize, markPixmapSize).toImage(); 0414 KIconEffect::colorize( tintedChange, scheme.foreground( KColorScheme::NegativeText ).color(), 1.0 ); 0415 0416 markIface->setMarkDescription( KTextEditor::MarkInterface::markType22, i18nc("@item", "Insertion") ); 0417 markIface->setMarkPixmap( KTextEditor::MarkInterface::markType22, QPixmap::fromImage( tintedInsertion ) ); 0418 markIface->setMarkDescription( KTextEditor::MarkInterface::markType23, i18nc("@item", "Removal") ); 0419 markIface->setMarkPixmap( KTextEditor::MarkInterface::markType23, QPixmap::fromImage( tintedRemoval ) ); 0420 markIface->setMarkDescription( KTextEditor::MarkInterface::markType24, i18nc("@item", "Change") ); 0421 markIface->setMarkPixmap( KTextEditor::MarkInterface::markType24, QPixmap::fromImage( tintedChange ) ); 0422 0423 markIface->setMarkDescription( KTextEditor::MarkInterface::markType25, i18nc("@item", "Insertion" ) ); 0424 markIface->setMarkPixmap(KTextEditor::MarkInterface::markType25, QIcon::fromTheme(QStringLiteral("insert-text")).pixmap(markPixmapSize, markPixmapSize)); 0425 markIface->setMarkDescription( KTextEditor::MarkInterface::markType26, i18nc("@item", "Removal") ); 0426 markIface->setMarkPixmap(KTextEditor::MarkInterface::markType26, QIcon::fromTheme(QStringLiteral("edit-delete")).pixmap(markPixmapSize, markPixmapSize)); 0427 markIface->setMarkDescription( KTextEditor::MarkInterface::markType27, i18nc("@item", "Change") ); 0428 markIface->setMarkPixmap(KTextEditor::MarkInterface::markType27, QIcon::fromTheme(QStringLiteral("text-field")).pixmap(markPixmapSize, markPixmapSize)); 0429 0430 for (Diff2::Difference* diff : qAsConst(*m_model->differences())) { 0431 int line, lineCount; 0432 Diff2::DifferenceStringList lines; 0433 0434 if( diff->applied() ) { 0435 line = diff->destinationLineNumber(); 0436 lineCount = diff->destinationLineCount(); 0437 lines = diff->destinationLines(); 0438 } else { 0439 line = diff->sourceLineNumber(); 0440 lineCount = diff->sourceLineCount(); 0441 lines = diff->sourceLines(); 0442 } 0443 0444 if ( line > 0 ) 0445 line -= 1; 0446 0447 KTextEditor::Cursor c( line, 0 ); 0448 KTextEditor::Cursor endC( line + lineCount, 0 ); 0449 if ( doc->lines() <= c.line() ) 0450 c.setLine( doc->lines() - 1 ); 0451 if ( doc->lines() <= endC.line() ) 0452 endC.setLine( doc->lines() ); 0453 0454 if ( endC.isValid() && c.isValid() ) { 0455 KTextEditor::MovingRange * r = moving->newMovingRange( KTextEditor::Range( c, endC ) ); 0456 m_ranges[r] = diff; 0457 addLineMarker( r, diff ); 0458 } 0459 } 0460 } 0461 0462 void PatchHighlighter::textInserted(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor, const QString& text) { 0463 if ( m_applying ) { // Do not interfere with patch application 0464 return; 0465 } 0466 0467 int startLine = cursor.line(); 0468 int endColumn = cursor.column() + text.length(); 0469 0470 qCDebug(PLUGIN_PATCHREVIEW) << "insertion range" << 0471 KTextEditor::Range(cursor, KTextEditor::Cursor(startLine, endColumn)); 0472 qCDebug(PLUGIN_PATCHREVIEW) << "inserted text" << text; 0473 0474 QStringList removedLines; 0475 QStringList insertedLines; 0476 if (startLine > 0) { 0477 const QString above = doc->line(--startLine) + QLatin1Char('\n'); 0478 removedLines << above; 0479 insertedLines << above; 0480 } 0481 const QString changed = doc->line(cursor.line()) + QLatin1Char('\n'); 0482 removedLines << changed.midRef(0, cursor.column()) + changed.midRef(endColumn); 0483 insertedLines << changed; 0484 if (doc->documentRange().end().line() > cursor.line()) { 0485 const QString below = doc->line(cursor.line() + 1) + QLatin1Char('\n'); 0486 removedLines << below; 0487 insertedLines << below; 0488 } 0489 0490 performContentChange(doc, removedLines, insertedLines, startLine + 1); 0491 } 0492 0493 void PatchHighlighter::newlineInserted(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor) 0494 { 0495 if ( m_applying ) { // Do not interfere with patch application 0496 return; 0497 } 0498 qCDebug(PLUGIN_PATCHREVIEW) << "newline range" << 0499 KTextEditor::Range(cursor, KTextEditor::Cursor(cursor.line() + 1, 0)); 0500 0501 int startLine = cursor.line(); 0502 QStringList removedLines; 0503 QStringList insertedLines; 0504 if (startLine > 0) { 0505 const QString above = doc->line(--startLine) + QLatin1Char('\n'); 0506 removedLines << above; 0507 insertedLines << above; 0508 } 0509 insertedLines << QStringLiteral("\n"); 0510 if (doc->documentRange().end().line() > cursor.line()) { 0511 const QString below = doc->line(cursor.line() + 1) + QLatin1Char('\n'); 0512 removedLines << below; 0513 insertedLines << below; 0514 } 0515 0516 performContentChange(doc, removedLines, insertedLines, startLine + 1); 0517 } 0518 0519 PatchHighlighter::PatchHighlighter( Diff2::DiffModel* model, IDocument* kdoc, PatchReviewPlugin* plugin, bool updatePatchFromEdits ) 0520 : m_doc( kdoc ), m_plugin( plugin ), m_model( model ), m_applying( false ) { 0521 KTextEditor::Document* doc = kdoc->textDocument(); 0522 // connect( kdoc, SIGNAL(destroyed(QObject*)), this, SLOT(documentDestroyed()) ); 0523 if (updatePatchFromEdits) { 0524 connect(doc, &KTextEditor::Document::textInserted, this, &PatchHighlighter::textInserted); 0525 connect(doc, &KTextEditor::Document::lineWrapped, this, &PatchHighlighter::newlineInserted); 0526 connect(doc, &KTextEditor::Document::textRemoved, this, &PatchHighlighter::textRemoved); 0527 connect(doc, &KTextEditor::Document::lineUnwrapped, this, &PatchHighlighter::newlineRemoved); 0528 } 0529 connect(doc, &KTextEditor::Document::reloaded, this, &PatchHighlighter::documentReloaded); 0530 connect(doc, &KTextEditor::Document::destroyed, this, &PatchHighlighter::documentDestroyed); 0531 0532 if ( doc->lines() == 0 ) 0533 return; 0534 0535 if (qobject_cast<KTextEditor::MarkInterface*>(doc)) { 0536 //can't use new signal/slot syntax here, MarkInterface is not a QObject 0537 connect(doc, SIGNAL(markToolTipRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&)), 0538 this, SLOT(markToolTipRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&))); 0539 connect(doc, SIGNAL(markClicked(KTextEditor::Document*,KTextEditor::Mark,bool&)), 0540 this, SLOT(markClicked(KTextEditor::Document*,KTextEditor::Mark,bool&))); 0541 } 0542 if (qobject_cast<KTextEditor::MovingInterface*>(doc)) { 0543 //can't use new signal/slot syntax here, MovingInterface is not a QObject 0544 connect(doc, SIGNAL(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*)), 0545 this, SLOT(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*))); 0546 connect(doc, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), 0547 this, SLOT(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*))); 0548 } 0549 0550 documentReloaded(doc); 0551 } 0552 0553 void PatchHighlighter::removeLineMarker( KTextEditor::MovingRange* range ) { 0554 auto* moving = qobject_cast<KTextEditor::MovingInterface*>(range->document()); 0555 if ( !moving ) 0556 return; 0557 0558 auto* markIface = qobject_cast<KTextEditor::MarkInterface*>(range->document()); 0559 if( !markIface ) 0560 return; 0561 0562 for (int line = range->start().line(); line <= range->end().line(); ++line) { 0563 markIface->removeMark(line, m_allmarks); 0564 } 0565 0566 // Remove all ranges that are in the same line (the line markers) 0567 for (auto it = m_ranges.begin(); it != m_ranges.end();) { 0568 if (it.key() != range && range->overlaps(it.key()->toRange())) { 0569 delete it.key(); 0570 it = m_ranges.erase(it); 0571 } else { 0572 ++it; 0573 } 0574 } 0575 } 0576 0577 void PatchHighlighter::addLineMarker( KTextEditor::MovingRange* range, Diff2::Difference* diff ) { 0578 auto* moving = qobject_cast<KTextEditor::MovingInterface*>(range->document()); 0579 if ( !moving ) 0580 return; 0581 0582 auto* markIface = qobject_cast<KTextEditor::MarkInterface*>(range->document()); 0583 if( !markIface ) 0584 return; 0585 0586 KTextEditor::Attribute::Ptr t( new KTextEditor::Attribute() ); 0587 0588 bool isOriginalState = diff->applied() == m_plugin->patch()->isAlreadyApplied(); 0589 0590 if( isOriginalState ) { 0591 t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 0, 255, 255 ), 20 ) ) ); 0592 }else{ 0593 t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 255, 0, 255 ), 20 ) ) ); 0594 } 0595 range->setAttribute( t ); 0596 range->setZDepth( -500 ); 0597 0598 KTextEditor::MarkInterface::MarkTypes mark; 0599 0600 if( isOriginalState ) { 0601 mark = KTextEditor::MarkInterface::markType27; 0602 0603 if( isInsertion( diff ) ) 0604 mark = KTextEditor::MarkInterface::markType25; 0605 if( isRemoval( diff ) ) 0606 mark = KTextEditor::MarkInterface::markType26; 0607 }else{ 0608 mark = KTextEditor::MarkInterface::markType24; 0609 0610 if( isInsertion( diff ) ) 0611 mark = KTextEditor::MarkInterface::markType22; 0612 if( isRemoval( diff ) ) 0613 mark = KTextEditor::MarkInterface::markType23; 0614 } 0615 0616 markIface->addMark( range->start().line(), mark ); 0617 0618 Diff2::DifferenceStringList lines; 0619 if( diff->applied() ) 0620 lines = diff->destinationLines(); 0621 else 0622 lines = diff->sourceLines(); 0623 0624 for( int a = 0; a < lines.size(); ++a ) { 0625 Diff2::DifferenceString* line = lines[a]; 0626 int currentPos = 0; 0627 const uint lineLength = static_cast<uint>(line->string().size()); 0628 0629 const Diff2::MarkerList& markers = line->markerList(); 0630 0631 for (auto* marker : markers) { 0632 if (marker->type() == Diff2::Marker::End) { 0633 if (currentPos != 0 || marker->offset() != lineLength) { 0634 KTextEditor::MovingRange* r2 = moving->newMovingRange( KTextEditor::Range( KTextEditor::Cursor( a + range->start().line(), currentPos ), KTextEditor::Cursor( a + range->start().line(), marker->offset() ) ) ); 0635 m_ranges[r2] = nullptr; 0636 0637 KTextEditor::Attribute::Ptr t( new KTextEditor::Attribute() ); 0638 0639 t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 255, 0, 0 ), 70 ) ) ); 0640 r2->setAttribute( t ); 0641 r2->setZDepth( -600 ); 0642 } 0643 } 0644 currentPos = marker->offset(); 0645 } 0646 } 0647 } 0648 0649 void PatchHighlighter::clear() { 0650 if( m_ranges.empty() ) 0651 return; 0652 0653 auto* moving = qobject_cast<KTextEditor::MovingInterface*>(m_doc->textDocument()); 0654 if ( !moving ) 0655 return; 0656 0657 auto* markIface = qobject_cast<KTextEditor::MarkInterface*>(m_doc->textDocument()); 0658 if( !markIface ) 0659 return; 0660 0661 const auto lines = markIface->marks().keys(); 0662 for (int line : lines) { 0663 markIface->removeMark( line, m_allmarks ); 0664 } 0665 0666 // Diff is taking care of its own objects (except removed ones) 0667 qDeleteAll( m_ranges.keys() ); 0668 m_ranges.clear(); 0669 } 0670 0671 PatchHighlighter::~PatchHighlighter() { 0672 clear(); 0673 } 0674 0675 IDocument* PatchHighlighter::doc() { 0676 return m_doc; 0677 } 0678 0679 void PatchHighlighter::documentDestroyed() { 0680 qCDebug(PLUGIN_PATCHREVIEW) << "document destroyed"; 0681 m_ranges.clear(); 0682 } 0683 0684 void PatchHighlighter::aboutToDeleteMovingInterfaceContent( KTextEditor::Document* ) { 0685 qCDebug(PLUGIN_PATCHREVIEW) << "about to delete"; 0686 clear(); 0687 } 0688 0689 QList< KTextEditor::MovingRange* > PatchHighlighter::ranges() const 0690 { 0691 return m_ranges.keys(); 0692 } 0693 0694 #include "moc_patchhighlighter.cpp"