File indexing completed on 2025-01-05 05:19:43

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 <gitprocess.h>
0008 
0009 #include <QDebug>
0010 #include <QFileInfo>
0011 #include <QProcess>
0012 #include <QString>
0013 
0014 #include <charconv>
0015 #include <optional>
0016 #include <vector>
0017 
0018 struct ModifiedLines {
0019     int startLine;
0020     int endline;
0021 };
0022 
0023 namespace __internal
0024 {
0025 static inline auto parseRange(std::string_view sv) -> std::optional<std::pair<int, int>>
0026 {
0027     if (sv.empty()) {
0028         return {};
0029     }
0030     if (sv[0] == '+') {
0031         sv.remove_prefix(1);
0032     }
0033 
0034     const std::size_t pos = sv.find(',');
0035     if (pos == std::string_view::npos) {
0036         // if comma not found, means line count == 1
0037         int s = 0;
0038         auto res = std::from_chars(sv.data(), sv.data() + sv.size(), s);
0039         if (res.ptr != (sv.data() + sv.size())) {
0040             return {};
0041         }
0042         return std::pair<int, int>(s, s);
0043     }
0044 
0045     int s = 0;
0046     auto res = std::from_chars(sv.data(), sv.data() + pos, s);
0047     if (res.ptr != (sv.data() + pos)) {
0048         return {};
0049     }
0050     int c = 0;
0051     res = std::from_chars(sv.data() + pos + 1, sv.data() + sv.size(), c);
0052     if (res.ptr != (sv.data() + sv.size())) {
0053         return {};
0054     }
0055     return std::pair<int, int>(s, s + c);
0056 }
0057 
0058 static inline auto modifedLinesFromGitDiff(const QByteArray &out) -> std::optional<std::vector<ModifiedLines>>
0059 {
0060     int start = out.indexOf("@@ ");
0061     if (start == -1) {
0062         return std::nullopt;
0063     }
0064     start += 3; // skip @@
0065     int next = 0;
0066     std::vector<ModifiedLines> ret;
0067     while (start > -1) {
0068         next = out.indexOf(" @@", start);
0069         if (next == -1) {
0070             return std::nullopt;
0071         }
0072         int space = out.indexOf(' ', start);
0073         if (space == -1) {
0074             return std::nullopt;
0075         }
0076         space++;
0077         std::string_view sv(out.constData() + space, next - space);
0078         const auto res = parseRange(sv);
0079         if (!res.has_value()) {
0080             return std::nullopt;
0081         }
0082         const auto [startLine, endLine] = res.value();
0083         ret.push_back({startLine, endLine});
0084 
0085         start = next + 3;
0086         start = out.indexOf("@@ ", start);
0087     }
0088     return ret;
0089 }
0090 }
0091 
0092 static inline std::optional<std::vector<ModifiedLines>> getModifiedLines(const QString &filePath)
0093 {
0094     if (!QFile::exists(filePath)) {
0095         qWarning() << "Doc doesn't exist, shouldn't happen!";
0096         return std::nullopt;
0097     }
0098 
0099     QProcess git;
0100     setupGitProcess(git, QFileInfo(filePath).absolutePath(), {QStringLiteral("diff"), QStringLiteral("--no-color"), QStringLiteral("-U0"), filePath});
0101     startHostProcess(git, QProcess::ReadOnly);
0102     if (!git.waitForStarted() || !git.waitForFinished()) {
0103         return std::nullopt;
0104     }
0105     if (git.exitCode() != 0 || git.exitStatus() != QProcess::NormalExit) {
0106         return std::nullopt;
0107     }
0108     return __internal::modifedLinesFromGitDiff(git.readAllStandardOutput());
0109 }