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

0001 /*  This file is part of the Kate project.
0002  *
0003  *  SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #pragma once
0009 
0010 #include <KTextEditor/Document>
0011 
0012 #include <QHash>
0013 #include <QPointer>
0014 #include <QStandardItemModel>
0015 #include <memory>
0016 
0017 class QTextDocument;
0018 class KateProjectItem;
0019 class KateProjectIndex;
0020 
0021 class KateProjectModel : public QStandardItemModel
0022 {
0023     Q_OBJECT
0024 public:
0025     using QStandardItemModel::QStandardItemModel;
0026 
0027     Qt::DropActions supportedDropActions() const override
0028     {
0029         return Qt::CopyAction;
0030     }
0031 
0032     bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
0033     bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
0034 
0035 private:
0036     friend class KateProject;
0037     QPointer<class KateProject> m_project;
0038 };
0039 
0040 /**
0041  * Shared pointer data types.
0042  * Used to pass pointers over queued connected slots
0043  */
0044 typedef std::shared_ptr<QStandardItem> KateProjectSharedQStandardItem;
0045 Q_DECLARE_METATYPE(KateProjectSharedQStandardItem)
0046 
0047 typedef std::shared_ptr<QHash<QString, KateProjectItem *>> KateProjectSharedQHashStringItem;
0048 Q_DECLARE_METATYPE(KateProjectSharedQHashStringItem)
0049 
0050 typedef std::shared_ptr<KateProjectIndex> KateProjectSharedProjectIndex;
0051 Q_DECLARE_METATYPE(KateProjectSharedProjectIndex)
0052 
0053 class KateProjectPlugin;
0054 class QThreadPool;
0055 
0056 /**
0057  * Class representing a project.
0058  * Holds project properties like name, groups, contained files, ...
0059  */
0060 class KateProject : public QObject
0061 {
0062     Q_OBJECT
0063 
0064     Q_PROPERTY(QString baseDir READ baseDir)
0065     Q_PROPERTY(QString name READ name)
0066 
0067 public:
0068     /**
0069      * Construct project by reading from given file.
0070      * Success can be checked later by using isValid().
0071      *
0072      * @param threadPool thread pool to be used by worker threads
0073      * @param plugin our plugin instance, for config & file system watcher
0074      * @param fileName fileName to load the project from
0075      */
0076     KateProject(QThreadPool &threadPool, KateProjectPlugin *plugin, const QString &fileName);
0077 
0078     /**
0079      * Construct project from given data for given base directory
0080      * Success can be checked later by using isValid().
0081      *
0082      * @param threadPool thread pool to be used by worker threads
0083      * @param plugin our plugin instance, for config & file system watcher
0084      * @param globalProject globalProject object content
0085      * @param directory project base directory
0086      */
0087     KateProject(QThreadPool &threadPool, KateProjectPlugin *plugin, const QVariantMap &globalProject, const QString &directory);
0088 
0089     /**
0090      * deconstruct project
0091      */
0092     ~KateProject() override;
0093 
0094     /**
0095      * Is this project valid?
0096      * @return project valid? we are valid, if we have some name set
0097      */
0098     bool isValid() const
0099     {
0100         return !name().isEmpty();
0101     }
0102 
0103     /**
0104      * Is this a file backed project or just generated from e.g. opening a directory or VCS?
0105      * @return file backed project? e.g. was this read from a .kateproject file?
0106      */
0107     bool isFileBacked() const
0108     {
0109         return m_fileBacked;
0110     }
0111 
0112     /**
0113      * Try to reload a project.
0114      * If the reload fails, e.g. because the file is not readable or corrupt, nothing will happen!
0115      * @param force will enforce the worker to update files list and co even if the content of the file was not changed!
0116      * @return success
0117      */
0118     bool reload(bool force = false);
0119 
0120     /**
0121      * Accessor to file name.
0122      * Even for projects generated from version control or by open directory we will create a fake name,
0123      * as the project file name is used in many places as unique identifier for the project.
0124      * @return file name
0125      */
0126     const QString &fileName() const
0127     {
0128         return m_fileName;
0129     }
0130 
0131     /**
0132      * Return the base directory of this project.
0133      * @return base directory of project, might not be the directory of the fileName!
0134      */
0135     const QString &baseDir() const
0136     {
0137         return m_baseDir;
0138     }
0139 
0140     /**
0141      * Accessor to project map containing the whole project info.
0142      * @return project info
0143      */
0144     const QVariantMap &projectMap() const
0145     {
0146         return m_projectMap;
0147     }
0148 
0149     /**
0150      * Accessor to project name.
0151      * @return project name
0152      */
0153     QString name() const
0154     {
0155         return m_projectMap[QStringLiteral("name")].toString();
0156     }
0157 
0158     /**
0159      * Accessor for the model.
0160      * @return model of this project
0161      */
0162     QStandardItemModel *model()
0163     {
0164         return &m_model;
0165     }
0166 
0167     /**
0168      * Flat list of all files in the project
0169      * @return list of files in project
0170      */
0171     QStringList files()
0172     {
0173         return m_file2Item ? m_file2Item->keys() : QStringList();
0174     }
0175 
0176     /**
0177      * get item for file
0178      * @param file file to get item for
0179      * @return item for given file or 0
0180      */
0181     KateProjectItem *itemForFile(const QString &file)
0182     {
0183         return m_file2Item ? m_file2Item->value(file) : nullptr;
0184     }
0185 
0186     /**
0187      * add a new file to the project
0188      */
0189     void addFile(const QString &file, KateProjectItem *item)
0190     {
0191         if (m_file2Item && item) {
0192             (*m_file2Item)[file] = item;
0193         }
0194     }
0195 
0196     /**
0197      * rename a file
0198      */
0199     void renameFile(const QString &newName, const QString &oldName);
0200 
0201     /**
0202      * remove a file
0203      */
0204     void removeFile(const QString &file);
0205 
0206     /**
0207      * Access to project index.
0208      * May be null.
0209      * Don't store this pointer, might change.
0210      * @return project index
0211      */
0212     KateProjectIndex *projectIndex()
0213     {
0214         return m_projectIndex.get();
0215     }
0216 
0217     KateProjectPlugin *plugin()
0218     {
0219         return m_plugin;
0220     }
0221 
0222     /**
0223      * Computes a suitable file name for the given suffix.
0224      * If you e.g. want to store a "notes" file, you could pass "notes" and get
0225      * the full path to projectbasedir/.kateproject.notes
0226      * @param suffix suffix for the file
0227      * @return full path for project local file, on error => empty string
0228      */
0229     QString projectLocalFileName(const QString &suffix) const;
0230 
0231     /**
0232      * Document with project local notes.
0233      * Will be stored in a projectLocalFile "notes.txt".
0234      * @return notes document
0235      */
0236     QTextDocument *notesDocument();
0237 
0238     /**
0239      * Save the notes document to "notes.txt" if any document around.
0240      */
0241     void saveNotesDocument();
0242 
0243     /**
0244      * Register a document for this project.
0245      * @param document document to register
0246      */
0247     void registerDocument(KTextEditor::Document *document);
0248 
0249     /**
0250      * Unregister a document for this project.
0251      * @param document document to unregister
0252      */
0253     void unregisterDocument(KTextEditor::Document *document);
0254 
0255 private Q_SLOTS:
0256     bool load(const QVariantMap &globalProject, bool force = false);
0257 
0258     /**
0259      * Used for worker to send back the results of project loading
0260      * @param topLevel new toplevel element for model
0261      * @param file2Item new file => item mapping
0262      */
0263     void loadProjectDone(const KateProjectSharedQStandardItem &topLevel, KateProjectSharedQHashStringItem file2Item);
0264 
0265     /**
0266      * Used for worker to send back the results of index loading
0267      * @param projectIndex new project index
0268      */
0269     void loadIndexDone(KateProjectSharedProjectIndex projectIndex);
0270 
0271     void slotModifiedChanged(KTextEditor::Document *);
0272 
0273     void slotModifiedOnDisk(KTextEditor::Document *document, bool isModified, KTextEditor::Document::ModifiedOnDiskReason reason);
0274 
0275     /**
0276      * did some project file change?
0277      * @param file name of file that did change
0278      */
0279     void slotFileChanged(const QString &file);
0280 
0281 Q_SIGNALS:
0282     /**
0283      * Emitted on project map changes.
0284      * This includes the name!
0285      */
0286     void projectMapChanged();
0287 
0288     /**
0289      * Emitted on model changes.
0290      * This includes the files list, itemForFile mapping!
0291      */
0292     void modelChanged();
0293 
0294     /**
0295      * Emitted when the index creation is finished.
0296      * This includes the ctags index.
0297      */
0298     void indexChanged();
0299 
0300 private:
0301     void registerUntrackedDocument(KTextEditor::Document *document);
0302     void unregisterUntrackedItem(const KateProjectItem *item);
0303     QVariantMap readProjectFile() const;
0304     /**
0305      * Read a JSON document from file.
0306      *
0307      * In case of an error, the returned object verifies isNull() is true.
0308      */
0309     QJsonDocument readJSONFile(const QString &fileName) const;
0310 
0311 private:
0312     /**
0313      * thread pool used for project worker
0314      */
0315     QThreadPool &m_threadPool;
0316 
0317     /**
0318      * Project plugin (configuration)
0319      */
0320     KateProjectPlugin *const m_plugin;
0321 
0322     /**
0323      * file backed project? e.g. was this read from a .kateproject file?
0324      */
0325     const bool m_fileBacked;
0326 
0327     /**
0328      * project file name, will stay constant
0329      */
0330     const QString m_fileName;
0331 
0332     /**
0333      * base directory of the project
0334      */
0335     QString m_baseDir;
0336 
0337     /**
0338      * project name
0339      */
0340     QString m_name;
0341 
0342     /**
0343      * variant map representing the project
0344      */
0345     QVariantMap m_projectMap;
0346 
0347     /**
0348      * standard item model with content of this project
0349      */
0350     KateProjectModel m_model;
0351 
0352     /**
0353      * mapping files => items
0354      */
0355     KateProjectSharedQHashStringItem m_file2Item;
0356 
0357     /**
0358      * project index, if any
0359      */
0360     KateProjectSharedProjectIndex m_projectIndex;
0361 
0362     /**
0363      * notes buffer for project local notes
0364      */
0365     QTextDocument *m_notesDocument = nullptr;
0366 
0367     /**
0368      * Set of existing documents for this project.
0369      */
0370     QHash<KTextEditor::Document *, QString> m_documents;
0371 
0372     /**
0373      * Parent item for existing documents that are not in the project tree
0374      */
0375     QStandardItem *m_untrackedDocumentsRoot = nullptr;
0376 
0377     /**
0378      * project configuration (read from file or injected)
0379      */
0380     QVariantMap m_globalProject;
0381 };