File indexing completed on 2024-04-28 05:49:05

0001 /*
0002     SPDX-FileCopyrightText: 2023 Waqar Ahmed <waqar.17a@gmail.com>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 #include "currentgitbranchbutton.h"
0006 
0007 #include "gitprocess.h"
0008 #include "hostprocess.h"
0009 
0010 #include <KAcceleratorManager>
0011 #include <KLocalizedString>
0012 #include <KTextEditor/Document>
0013 #include <KTextEditor/MainWindow>
0014 #include <KTextEditor/View>
0015 #include <QPointer>
0016 
0017 #include <QFileInfo>
0018 #include <QProcess>
0019 #include <QtConcurrentRun>
0020 
0021 static CurrentGitBranchButton::BranchResult getCurrentBranchName(const QString &workingDir)
0022 {
0023     const QStringList argsList[3] = {
0024         {QStringLiteral("symbolic-ref"), QStringLiteral("--short"), QStringLiteral("HEAD")},
0025         {QStringLiteral("rev-parse"), QStringLiteral("--short"), QStringLiteral("HEAD")},
0026         {QStringLiteral("describe"), QStringLiteral("--exact-match"), QStringLiteral("HEAD")},
0027     };
0028 
0029     for (int i = 0; i < 3; ++i) {
0030         QProcess git;
0031         if (!setupGitProcess(git, workingDir, argsList[i])) {
0032             return {};
0033         }
0034 
0035         startHostProcess(git, QProcess::ReadOnly);
0036         if (git.waitForStarted() && git.waitForFinished(-1)) {
0037             if (git.exitStatus() == QProcess::NormalExit && git.exitCode() == 0) {
0038                 return {
0039                     QString::fromUtf8(git.readAllStandardOutput().trimmed()),
0040                     static_cast<CurrentGitBranchButton::BranchType>(i),
0041                 };
0042             }
0043         }
0044     }
0045 
0046     // give up
0047     return {};
0048 }
0049 
0050 CurrentGitBranchButton::CurrentGitBranchButton(KTextEditor::MainWindow *mainWindow, QWidget *parent)
0051     : QToolButton(parent)
0052 {
0053     setVisible(false);
0054     setAutoRaise(true);
0055     setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
0056     m_viewChangedTimer.setSingleShot(true);
0057     m_viewChangedTimer.setInterval(1000);
0058     KAcceleratorManager::setNoAccel(this);
0059 
0060     auto mw = QPointer<KTextEditor::MainWindow>(mainWindow);
0061     connect(mainWindow, &KTextEditor::MainWindow::viewChanged, this, [this](KTextEditor::View *v) {
0062         if (!v || v->document()->url().toLocalFile().isEmpty()) {
0063             hideButton();
0064             m_viewChangedTimer.stop();
0065             return;
0066         }
0067         m_viewChangedTimer.start();
0068     });
0069     m_viewChangedTimer.callOnTimeout(this, [this, mw] {
0070         if (mw) {
0071             onViewChanged(mw->activeView());
0072         }
0073     });
0074 
0075     connect(&m_watcher, &QFutureWatcher<QString>::finished, this, &CurrentGitBranchButton::onBranchFetched);
0076     onViewChanged(mainWindow->activeView());
0077 }
0078 
0079 CurrentGitBranchButton::~CurrentGitBranchButton()
0080 {
0081     m_viewChangedTimer.stop();
0082     if (m_watcher.isRunning()) {
0083         disconnect(&m_watcher, &QFutureWatcher<BranchResult>::finished, this, &CurrentGitBranchButton::onBranchFetched);
0084         m_watcher.cancel();
0085         m_watcher.waitForFinished();
0086     }
0087 }
0088 
0089 void CurrentGitBranchButton::hideButton()
0090 {
0091     setText({});
0092     setVisible(false);
0093 }
0094 
0095 void CurrentGitBranchButton::refresh()
0096 {
0097     m_viewChangedTimer.start();
0098 }
0099 
0100 void CurrentGitBranchButton::onViewChanged(KTextEditor::View *v)
0101 {
0102     if (!v || v->document()->url().toLocalFile().isEmpty()) {
0103         hideButton();
0104         return;
0105     }
0106 
0107     const auto fi = QFileInfo(v->document()->url().toLocalFile());
0108     const auto workingDir = fi.absolutePath();
0109     auto future = QtConcurrent::run(&getCurrentBranchName, workingDir);
0110     m_watcher.setFuture(future);
0111 }
0112 
0113 void CurrentGitBranchButton::onBranchFetched()
0114 {
0115     const BranchResult branch = m_watcher.result();
0116     if (branch.branch.isEmpty()) {
0117         hideButton();
0118         return;
0119     }
0120 
0121     setText(branch.branch);
0122 
0123     if (branch.type == Commit) {
0124         setToolTip(i18nc("Tooltip text, describing that '%1' commit is checked out", "HEAD at commit %1", branch.branch));
0125     } else if (branch.type == Tag) {
0126         setToolTip(i18nc("Tooltip text, describing that '%1' tag is checked out", "HEAD is at this tag %1", branch.branch));
0127     } else if (branch.type == Branch) {
0128         setToolTip(i18nc("Tooltip text, describing that '%1' branch is checked out", "Active branch: %1", branch.branch));
0129     }
0130 
0131     if (!isVisible()) {
0132         setVisible(true);
0133     }
0134 }
0135 
0136 #include "moc_currentgitbranchbutton.cpp"