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

0001 /*
0002     SPDX-FileCopyrightText: 2020 Jonathan L. Verner <jonathan.verner@matfyz.cz>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #ifndef KDEVPLATFORM_PLUGIN_REPO_STATUS_MODEL_H
0008 #define KDEVPLATFORM_PLUGIN_REPO_STATUS_MODEL_H
0009 
0010 #include "gitplugin.h"
0011 
0012 #include <project/projectchangesmodel.h>
0013 #include <vcs/interfaces/ibasicversioncontrol.h>
0014 
0015 class KJob;
0016 
0017 namespace KDevelop {
0018     class IProject;
0019     class IDocument;
0020 }
0021 
0022 /**
0023  * This implements the model for vcs status of files in a project.
0024  *
0025  * The tree structure of the model is:
0026  *
0027  *   - project item
0028  *     - index
0029  *       - file with staged changes
0030  *       - ...
0031  *     - worktree
0032  *       - file with unstaged changes
0033  *       - ...
0034  *     - conflicts
0035  *       - file with unresolved conflicts
0036  *       - ...
0037  *     - untrackded
0038  *       - untracked file
0039  *       - ...
0040  *
0041  * It is modeled on (and part of the code is copied from) the @class ProjectChangesModel
0042  * (see kdevplatform/vcs/interfaces/ibasicversioncontrol.h)
0043  *
0044  * @author Jonathan L. Verner <jonathan.verner@matfyz.cz>
0045  */
0046 class RepoStatusModel : public QStandardItemModel
0047 {
0048     Q_OBJECT
0049 
0050 public:
0051     enum ItemRoles {
0052         UrlRole = Qt::UserRole+1, /**< an url to the item */
0053         AreaRole,                 /**< the area where the item belongs (index, worktree, untracked) **/
0054         NameRole,                 /**< A human readable name of the item */
0055         BranchNameRole,           /**< the git branch on which the item is located */
0056         StatusRole,               /**< the git status of the item (GitPlugin::ExtendedState) */
0057         ReadableStatusRole,       /**< a human-readable git status of the item */
0058         ProjectUrlRole,          /**< the url of the project */
0059     };
0060 
0061     /**
0062      * The areas into which files are sorted. A file may be in
0063      * more than one area (e.g. if it has both staged and unstaged
0064      * changes, it will be in Index and WorkTree). The special
0065      * areas ProjectRoot, IndexRoot, WorkTreeRoot, UntrackedRoot and ConflictsRoot
0066      * correspond to the root items.
0067      */
0068     enum Areas {
0069         ProjectRoot,    /**< The root item of the project */
0070         IndexRoot,      /**< The root item of the staged changes */
0071         WorkTreeRoot,   /**< The root item of the unstaged changes */
0072         UntrackedRoot,  /**< The root item of the untracked files */
0073         ConflictRoot,   /**< The root item of the files with unresolved conflicts */
0074         Index,          /**< An item with staged changes */
0075         WorkTree,       /**< An item with unstaged changes */
0076         Untracked,      /**< An untracked item */
0077         Conflicts,      /**< An item with unresolved conflicts */
0078         None,           /**< None of the above */
0079     };
0080 
0081     /**
0082      * A structure containing the collection of top-level items (i.e. the ones not containing file items)
0083      * corresponding to a project.
0084      */
0085     struct ProjectItem {
0086         QStandardItem *project, *index, *worktree, *conflicts, *untracked;
0087         bool isValid() const {return project;}
0088     };
0089 
0090     explicit RepoStatusModel(QObject* parent);
0091     ~RepoStatusModel() override;
0092 
0093     /**
0094      * Updates the items described by `status`, i.e. moves the
0095      * item(s) corresponding to status.url to the respective areas and
0096      * sets their area role data.
0097      *
0098      * @param pItem the model item for the project that the items belongs to
0099      * @param status the status data
0100      *
0101      * @note the @p pItem must be valid!
0102      */
0103     void updateState(const ProjectItem& pItem, const KDevelop::VcsStatusInfo& status);
0104 
0105     /**
0106      * Runs a job to fetches the statuses of elements (given by `urls`)
0107      * of project `project`.
0108      *
0109      * @param project the project the elements belong to
0110      * @param urls the urls to fetch the status for
0111      * @param mode whether to recurse into subdirectories, if an url is a directory
0112      */
0113     void fetchStatusesForUrls(KDevelop::IProject* project, const QList<QUrl>& urls,
0114                               KDevelop::IBasicVersionControl::RecursionMode mode);
0115 
0116     /**
0117      * Returns a list of items corresponding to project roots.
0118      *
0119      * @note we return a const so that the result can be used in for-range
0120      * loops without detaching the container.
0121      */
0122     const QList<QStandardItem*> projectRoots() const;
0123 
0124     /**
0125      * Retrieves the items in a given area of a project.
0126      *
0127      * @param project a project root
0128      * @param area    the area to get the items from
0129      *
0130      * @returns a list of items in the given area of the project
0131      *
0132      * @note we return a const so that the result can be used in for-range
0133      * loops without detaching the container.
0134      */
0135     const QList<QStandardItem*> items(const QStandardItem* project, Areas area) const;
0136 
0137     /**
0138      * Returns the ProjectItem structure (containing the project, index, worktree,
0139      * conflicts & untracked items) for project `p`.
0140      *
0141      * @note: Returns a default-constructed ProjectItem{} if @p p is absent from the model.
0142      *        This includes the case when @p p is null.
0143      *
0144      * @param p the project
0145      */
0146     ProjectItem projectItem(const KDevelop::IProject* p) const;
0147 
0148     /**
0149      * Returns the ProjectItem structure (containing the project, index, worktree,
0150      * conflicts & untracked items) for the project whose ProjectRoot
0151      * item is `p_item`
0152      *
0153      * @param p_item the ProjectRoot item
0154      */
0155     ProjectItem projectItem(QStandardItem* p_item) const;
0156 
0157 public Q_SLOTS:
0158 
0159     /**
0160      * Updates all open projects and removes projects which are not open anymore.
0161      */
0162     void reloadAll();
0163 
0164     /**
0165      * Updates all items belonging to the projects `p` (and removes the ones
0166      * no longer in the projects).
0167      *
0168      * @param projects the projects to update
0169      */
0170     void reload(const QList<KDevelop::IProject*>& projects);
0171 
0172     /**
0173      * Updates the projects given byl urls `p`.
0174      *
0175      * @param projects the urls of projects to update
0176      */
0177     void reload(const QList<QUrl>& projects);
0178 
0179     /**
0180      * Adds a new project to the model (e.g. when a new project is opened).
0181      *
0182      * @param p the project to add.
0183      */
0184     void addProject(const KDevelop::IProject* p);
0185 
0186     /**
0187      * Removes a project from the model (e.g. when a project is closed).
0188      *
0189      * @param p the project to remove
0190      */
0191     void removeProject(const KDevelop::IProject* p);
0192 
0193     /**
0194      * Processes the result of a job started by `fetchStatusesForUrls`
0195      *
0196      * @param job the KJob to whose results are processed.
0197      */
0198     void statusReady(KJob* job);
0199 
0200     /**
0201      * A handler called to update the status of the item(s) in the model
0202      * when a document is saved
0203      *
0204      * @param doc the document (the item(s) corresponding to this will be
0205      * updated)
0206      */
0207     void documentSaved(const KDevelop::IDocument* doc);
0208 
0209     /**
0210      * A handler called when items are added to a project
0211      */
0212     void itemsAdded(const QModelIndex& idx, int start, int end);
0213 
0214     /**
0215      * A handler called when a job is unregistered.
0216      *
0217      * This is used to monitor when jobs run by the VCS
0218      * (e.g. git add, git commit, etc.) finish so that
0219      * we can schedule an update of the corresponding projects.
0220      */
0221     void jobUnregistered(KJob*);
0222 
0223     /**
0224      * A handler called when the branch is switched in a repo
0225      * to schedule the update of the name of the project.
0226      */
0227     void repositoryBranchChanged(const QUrl& url);
0228 
0229     /**
0230      * A handler for processing the result of a job getting
0231      * the current branch name of a repo. (this job is scheduled
0232      * by the `repositoryBranchChanged` method)
0233      */
0234     void branchNameReady(KDevelop::VcsJob* job);
0235 
0236 private:
0237     /* Functions determining which area(s) a file should be listed in
0238      * depending on its status */
0239     static bool showInIndex(const GitPlugin::ExtendedState state);
0240     static bool showInWorkTree(const GitPlugin::ExtendedState state);
0241     static bool showInConflicts(const GitPlugin::ExtendedState state);
0242     static bool showInUntracked(const GitPlugin::ExtendedState state);
0243 
0244     /**
0245      * Removes all items with url `url` from the model.
0246      *
0247      * @param url the url to remove
0248      * @param parent the parent whose children will be processed (if it is
0249      * null all elements in the model will be processed)
0250      */
0251     void removeUrl(const QUrl& url, const QStandardItem* parent = nullptr);
0252 
0253     /**
0254      * Return the list of all urls for items in project `project`
0255      *
0256      * @param project the project
0257      */
0258     QList<QUrl> childUrls(const ProjectItem& project) const;
0259 
0260     /**
0261      * A helper function to recursively compute all the descendants of
0262      * `parent` and return them as a list. If `parent` is null, all items
0263      * (except the invisible root) are returned
0264      *
0265      * @param parent the node whose children will be returned (if it is
0266      * null all non-root nodes will be returned)
0267      *
0268      * @note we return a const so that the result can be used in for-range
0269      * loops without detaching the container.
0270      */
0271     const QList<QStandardItem*> allItems(const QStandardItem* parent = nullptr) const;
0272 
0273     /**
0274      * A helper function to find the model item corresponding to the project
0275      * @p project
0276      *
0277      * Returns a nullptr if the project is not in the model (e.g. it does not use git)
0278      */
0279     QStandardItem* findProject(const KDevelop::IProject* project) const;
0280 };
0281 
0282 #endif