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

0001 /*
0002     SPDX-FileCopyrightText: 2020 Jonathan Verner <jonathan@temno.eu>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #ifndef DIFF_VIEWS_CTRL_H
0008 #define DIFF_VIEWS_CTRL_H
0009 
0010 #include "repostatusmodel.h"
0011 
0012 #include <KTextEditor/Attribute>
0013 
0014 #include <QObject>
0015 
0016 class GitPlugin;
0017 class QAction;
0018 
0019 namespace KDevelop
0020 {
0021 class IDocument;
0022 class IProject;
0023 class VcsJob;
0024 class VcsDiff;
0025 }
0026 
0027 namespace KTextEditor
0028 {
0029 class Document;
0030 class View;
0031 }
0032 
0033 /**
0034  * A class which handles displaying & updating tabs showing
0035  * staged/unstaged changes.
0036  */
0037 class DiffViewsCtrl : public QObject
0038 {
0039     Q_OBJECT
0040 public:
0041     DiffViewsCtrl(QObject* parent = nullptr);
0042     ~DiffViewsCtrl();
0043 
0044     /* Enum values to be passed to the updateDiff method */
0045     enum UpdateDiffParams { Activate, NoActivate };
0046 
0047 public Q_SLOTS:
0048 
0049     /**
0050      * Updates the diff view showing the changes to url and,
0051      * optionally, activates the view.
0052      *
0053      * @param url the url of the file to update the diff for
0054      * @param area the area (determines what type of changes are shown,
0055      * e.g. if area == Index, the diff shows the staged changes)
0056      * @param p if equal to Activate (default), activates the tab,
0057      * if equal to NoActivate, the tab is not activated (e.g. when it is
0058      * refreshed because of changes in an active document).
0059      *
0060      * @note: The tab is not opened immediately, rather a job to compute the
0061      * diff is scheduled and the tab is opened by the @method diffReady
0062      * when the job producing the diff finishes.
0063      *
0064      * @note: If p is equal to NoActivate and there is no tab already
0065      * showing the diff, the function returns without doing anything.
0066      */
0067     void updateDiff(const QUrl& url, const RepoStatusModel::Areas area, const UpdateDiffParams p = Activate);
0068 
0069     /**
0070      * Updates all diff views which are shown for a project.
0071      * The diff views which become empty are closed.
0072      *
0073      * @param proj the project to update diff views for
0074      */
0075     void updateProjectDiffs(KDevelop::IProject* proj);
0076 
0077     /**
0078      * Updates all diff views which are shown for a given url.
0079      * The diff views which become empty are closed.
0080      *
0081      * @param url the url to update diff views for
0082      */
0083     void updateUrlDiffs(const QUrl& url);
0084 
0085 
0086 private Q_SLOTS:
0087     /**
0088      * A handler called to open a document tab with a diff when
0089      * the job producing the diff finishes.
0090      *
0091      * @param diffJob the job producing the diff
0092      */
0093     void diffReady(KDevelop::VcsJob* diffJob);
0094 
0095 private:
0096     /* Describes an action on selected lines/hunk in a diff */
0097     enum ApplyAction {
0098         Stage,
0099         Unstage,
0100         Revert,
0101     };
0102 
0103     /**
0104      * A helper function which applies an action to the currently
0105      * selected lines/hunk in the active diff view.
0106      *
0107      * @param act the action to apply (stage, unstage, revert)
0108      *
0109      * @note: If the view has a non-empty selection, the action will
0110      *        be applied to the selected lines; if the selection is
0111      *        empty, it will be applied to the hunk which contains
0112      *        the current cursor position.
0113      * @note: No confirmation dialog is shown before doing the
0114      *        potentially dangerous revert action.
0115      */
0116     void applySelected(ApplyAction act);
0117 
0118     /**
0119      * Reverts the selected lines/diff in the currently active
0120      * document tab showing a diff.
0121      *
0122      * @note: This is a helper method for showing a confirmation
0123      * dialog before reverting, since reverting is an irreversible
0124      * and dangerous action. The actual work is done using the
0125      * @ref:applySelected method.
0126      */
0127     void revertSelected();
0128 
0129     /**
0130      * Opens the source document at the line corresponding
0131      * to the current line in the currently shown diff
0132      */
0133     void gotoSrcLine();
0134 
0135     /**
0136      * A helper function which sets up the actions appropriate for a diff view.
0137      *
0138      * The function also creates a context menu for the view,
0139      * adds the appropriate actions to it and sets up a
0140      * connection to update the action's texts based on whether
0141      * a hunk or lines are selected in the active
0142      * view when the menu is shown.
0143      *
0144      * @param view the view to add the actions to
0145      * @param diffType the type of diff view (staged/unstaged). Allowed
0146      *                 values are @ref RepoStatusModel::Index and
0147      *                 @ref RepoStatusModel::WorkTree
0148      */
0149     void setupDiffActions(KTextEditor::View* view, const RepoStatusModel::Areas diffType) const;
0150 
0151     QAction *m_stageSelectedAct,    /**< Action to stage selected hunk/lines */
0152             *m_unstageSelectedAct,  /**< Action to unstage selected hunk/lines */
0153             *m_revertSelectedAct,   /**< Action to revert selected hunk/lines */
0154             *m_gotoSrcLineAct;      /**< Action to goto a line in the source file */
0155 
0156     /**
0157      * A structure for holding information about a tab
0158      * showing a diff.
0159      *
0160      * @note: A valid instance **must** have non null doc, ktDoc, vcs & project
0161      * project members.
0162      */
0163     class ViewData
0164     {
0165     public:
0166         RepoStatusModel::Areas area
0167             = RepoStatusModel::None; /**< The type of diff shown (staged/unstaged changes to a file; summary of
0168                                         staged/unstaged changes for a projects) */
0169         KDevelop::IDocument* doc = nullptr; /**< The associated IDocument */
0170         KTextEditor::Document* ktDoc = nullptr; /**< The associated KTextEditor::Document */
0171         KTextEditor::View* actView
0172             = nullptr; /**< The associated KTextEditor::View, if the tab is currently active; nullptr otherwise */
0173         GitPlugin* vcs = nullptr; /**< A reference to the git plugin */
0174         KDevelop::IProject* project = nullptr; /**< A pointer to the project plugin */
0175         QUrl url; /**< The url of the source file (for which changes are shown), or of the project (if showing a summary
0176                      of changes) */
0177 
0178         /**
0179          * Returns true if the instance is valid.
0180          */
0181         bool isValid() const;
0182     };
0183 
0184     /**
0185      * A helper function to get a ViewData structure for an url.
0186      *
0187      * It first tries to find a suitable structure in m_views and, if not
0188      * found, it creates a new one, caches it in m_views and sets up a connection
0189      * to remove it from the cache when the document is closed (e.g. by the user)
0190      * and to update the shown diff when the document is saved.
0191      *
0192      * @param url the file with the changes
0193      * @param area the type of the diff (staged/unstaged)
0194      */
0195     const ViewData createView(const QUrl& url, RepoStatusModel::Areas area);
0196 
0197     /**
0198      * Returns a ViewData structure for the currently active view.
0199      *
0200      * @note: If there is no active view or it has no associated ViewData structure,
0201      * the returned structure has the actView member set to nullptr.
0202      */
0203     const ViewData activeView();
0204 
0205     /**
0206      * A helper function to construct the key into the `m_views` map.
0207      *
0208      * @param url the url of the file being diffed
0209      * @param area the type of diff (i.e. staged (Index) / unstaged (WorkTree) changes)
0210      */
0211     static const QString viewKey(const QUrl& url, RepoStatusModel::Areas area);
0212 
0213     /**
0214      * A map holding ViewData structures for opened diff windows.
0215      * A diff window is opened only once per file / diff type (staged, unstaged) and
0216      * is later reused.
0217      *
0218      * The keys are formed by concatenating the url of the document
0219      * being diffed with ':' followed by the RepoStatusModel::Areas
0220      * enum value identifying what type of diff it is (i.e. showing
0221      * staged/unstaged changes).
0222      */
0223     std::map<QString, ViewData> m_views;
0224 };
0225 
0226 #endif // DIFF_VIEWS_CTRL_H