File indexing completed on 2024-05-26 05:51:57
0001 /* 0002 SPDX-FileCopyrightText: 2022 Waqar Ahmed <waqar.17a@gmail.com> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 #pragma once 0006 0007 #include "hostprocess.h" 0008 #include <KLocalizedString> 0009 #include <KTextEditor/Document> 0010 #include <KTextEditor/MovingCursor> 0011 #include <QFileInfo> 0012 #include <QIcon> 0013 #include <QRegularExpression> 0014 #include <QTemporaryFile> 0015 #include <gitprocess.h> 0016 #include <ktexteditor_utils.h> 0017 0018 [[maybe_unused]] static QString diff(KTextEditor::Document *doc, const QByteArray &formatted) 0019 { 0020 QTemporaryFile f; 0021 if (!f.open()) { 0022 Utils::showMessage(i18n("Failed to write a temp file"), {}, i18n("Format"), MessageType::Warning); 0023 return {}; 0024 } 0025 f.write(formatted); 0026 f.close(); 0027 0028 QProcess p; 0029 QStringList args = {QStringLiteral("diff"), QStringLiteral("--no-color"), QStringLiteral("--no-index")}; 0030 args << doc->url().toString(QUrl::PreferLocalFile); 0031 args << f.fileName(); 0032 setupGitProcess(p, QFileInfo(doc->url().toString(QUrl::PreferLocalFile)).absolutePath(), args); 0033 startHostProcess(p); 0034 if (!p.waitForStarted() || !p.waitForFinished()) { 0035 Utils::showMessage(i18n("Failed to run git diff: %1", p.errorString()), {}, i18n("Format"), MessageType::Warning); 0036 return {}; 0037 } 0038 0039 return QString::fromUtf8(p.readAllStandardOutput()); 0040 } 0041 0042 struct PatchLine { 0043 KTextEditor::MovingCursor *pos = nullptr; 0044 KTextEditor::Cursor inPos; 0045 enum { Remove, Add } type; 0046 QString text; 0047 }; 0048 Q_DECLARE_METATYPE(PatchLine) 0049 Q_DECLARE_METATYPE(std::vector<PatchLine>) 0050 0051 [[maybe_unused]] static std::pair<uint, uint> parseRange(const QString &range) 0052 { 0053 int commaPos = range.indexOf(QLatin1Char(',')); 0054 if (commaPos > -1) { 0055 return {QStringView(range).sliced(0, commaPos).toInt(), QStringView(range).sliced(commaPos + 1).toInt()}; 0056 } 0057 return {range.toInt(), 1}; 0058 } 0059 0060 [[maybe_unused]] static std::vector<PatchLine> parseDiff(KTextEditor::Document *doc, const QString &diff) 0061 { 0062 static const QRegularExpression HUNK_HEADER_RE(QStringLiteral("^@@ -([0-9,]+) \\+([0-9,]+) @@(.*)")); 0063 0064 std::vector<PatchLine> lines; 0065 const QStringList d = diff.split(QStringLiteral("\n")); 0066 for (int i = 0; i < d.size(); ++i) { 0067 const QString &l = d.at(i); 0068 const auto match = HUNK_HEADER_RE.match(l); 0069 if (!match.hasMatch()) { 0070 continue; 0071 } 0072 0073 const std::pair<int, int> src = parseRange(match.captured(1)); 0074 const std::pair<int, int> tgt = parseRange(match.captured(2)); 0075 0076 // unroll into the hunk 0077 int srcline = src.first - 1; 0078 int tgtline = tgt.first - 1; 0079 // qDebug() << "NEW HUNK: " << l << "------------" << srcline << tgtline; 0080 for (int j = i + 1; j < d.size(); ++j) { 0081 const QString &hl = d.at(j); 0082 if (hl.startsWith(QLatin1Char(' '))) { 0083 srcline++; 0084 tgtline++; 0085 } else if (hl.startsWith(QLatin1Char('+'))) { 0086 PatchLine p; 0087 p.type = PatchLine::Add; 0088 p.text = hl.mid(1); 0089 p.inPos = KTextEditor::Cursor(tgtline, 0); 0090 // p.pos = iface->newMovingCursor(KTextEditor::Cursor(tgtline, 0)); 0091 lines.push_back(p); 0092 // qDebug() << "insert line" << tgtline << p.text << p.inPos.line(); 0093 tgtline++; 0094 } else if (hl.startsWith(QLatin1Char('-'))) { 0095 PatchLine p; 0096 p.type = PatchLine::Remove; 0097 p.pos = doc->newMovingCursor(KTextEditor::Cursor(srcline, 0)); 0098 // qDebug() << "remove line" << srcline << hl.mid(1) << p.pos->line(); 0099 lines.push_back(p); 0100 srcline++; 0101 } else if (hl.startsWith(QStringLiteral("@@ "))) { 0102 i = j - 1; // advance i to next hunk 0103 break; 0104 } 0105 } 0106 } 0107 // qDebug() << "================"; 0108 0109 return lines; 0110 } 0111 0112 [[maybe_unused]] static void applyPatch(KTextEditor::Document *doc, const std::vector<PatchLine> &edits) 0113 { 0114 // EditingTransaction scope 0115 KTextEditor::Document::EditingTransaction t(doc); 0116 for (const auto &p : edits) { 0117 if (p.type == PatchLine::Add) { 0118 // qDebug() << "insert at " << p.inPos.line() << "text: " << p.text; 0119 doc->insertLine(p.inPos.line(), p.text /*+ QStringLiteral("\n")*/); 0120 } else if (p.type == PatchLine::Remove) { 0121 // qDebug() << "remove line" << p.pos->line() << doc->line(p.pos->line()); 0122 doc->removeLine(p.pos->line()); 0123 } 0124 } 0125 for (const auto &p : edits) { 0126 delete p.pos; 0127 } 0128 }