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

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 <unordered_map>
0011 
0012 #include <QDir>
0013 #include <QFileSystemWatcher>
0014 #include <QThreadPool>
0015 
0016 #include <KTextEditor/Plugin>
0017 #include <KTextEditor/SessionConfigInterface>
0018 #include <ktexteditor/document.h>
0019 #include <ktexteditor/mainwindow.h>
0020 
0021 #include "kateprojectcompletion.h"
0022 
0023 class KateProject;
0024 
0025 enum class ClickAction : uint8_t {
0026     NoAction = 0,
0027     ShowDiff,
0028     OpenFile,
0029     StageUnstage,
0030 };
0031 
0032 class KateProjectPlugin : public KTextEditor::Plugin, public KTextEditor::SessionConfigInterface
0033 {
0034     Q_OBJECT
0035     Q_INTERFACES(KTextEditor::SessionConfigInterface)
0036     Q_PROPERTY(QList<QObject *> projects READ projectsObjects)
0037 
0038 public:
0039     explicit KateProjectPlugin(QObject *parent = nullptr, const QVariantList & = QVariantList());
0040     ~KateProjectPlugin() override;
0041 
0042     QObject *createView(KTextEditor::MainWindow *mainWindow) override;
0043 
0044     int configPages() const override;
0045     KTextEditor::ConfigPage *configPage(int number = 0, QWidget *parent = nullptr) override;
0046 
0047     /**
0048      * Create new project for given project filename.
0049      * Null pointer if no project can be opened.
0050      * @param fileName file name for the project
0051      * @return project or null if not openable
0052      */
0053     KateProject *createProjectForFileName(const QString &fileName);
0054 
0055     /**
0056      * Search and open project for given dir, if possible.
0057      * Will search upwards for .kateproject file.
0058      * Will use internally projectForFileName if project file is found.
0059      * @param dir dir to search matching project for
0060      * @param userSpecified whether user asked to open a directory as project
0061      * @return project or null if not openable
0062      */
0063     KateProject *projectForDir(QDir dir, bool userSpecified = false);
0064 
0065     /**
0066      * Try to close the given project.
0067      * Will ask if the files belonging to the project shall be closed, if not this will just do nothing.
0068      * @param project project to close
0069      */
0070     void closeProject(KateProject *project);
0071 
0072     /**
0073      * Search and open project that contains given url, if possible.
0074      * Will search upwards for .kateproject file, if the url is a local file.
0075      * Will use internally projectForDir.
0076      * @param url url to search matching project for
0077      * @return project or null if not openable
0078      */
0079     KateProject *projectForUrl(const QUrl &url);
0080 
0081     /**
0082      * get list of all current open projects
0083      * @return list of all open projects
0084      */
0085     QList<KateProject *> projects() const
0086     {
0087         return m_projects;
0088     }
0089 
0090     /**
0091      * As above, in different form for property access.
0092      */
0093     QList<QObject *> projectsObjects() const;
0094 
0095     /**
0096      * Has the given project open documents?
0097      * @param project project to check open document for
0098      * @return has the given project open documents
0099      */
0100     bool projectHasOpenDocuments(KateProject *project) const;
0101 
0102     /**
0103      * Get global code completion.
0104      * @return global completion object for KTextEditor::View
0105      */
0106     KateProjectCompletion *completion()
0107     {
0108         return &m_completion;
0109     }
0110 
0111     /**
0112      * Map current open documents to projects.
0113      * @param document document we want to know which project it belongs to
0114      * @return project or 0 if none found for this document
0115      */
0116     KateProject *projectForDocument(KTextEditor::Document *document)
0117     {
0118         const auto it = m_document2Project.find(document);
0119         return (it != m_document2Project.end()) ? it->second : nullptr;
0120     }
0121 
0122     void setAutoRepository(bool onGit, bool onSubversion, bool onMercurial, bool onFossil);
0123     bool autoGit() const;
0124     bool autoSubversion() const;
0125     bool autoMercurial() const;
0126     bool autoFossil() const;
0127 
0128     void setIndex(bool enabled, const QUrl &directory);
0129     bool getIndexEnabled() const;
0130     QUrl getIndexDirectory() const;
0131 
0132     void setMultiProject(bool completion, bool gotoSymbol);
0133     bool multiProjectCompletion() const;
0134     bool multiProjectGoto() const;
0135 
0136     void setSingleClickAction(ClickAction cb);
0137     ClickAction singleClickAcion();
0138 
0139     void setDoubleClickAction(ClickAction cb);
0140     ClickAction doubleClickAcion();
0141 
0142     void setRestoreProjectsForSession(bool enabled);
0143     bool restoreProjectsForSession() const;
0144 
0145     /**
0146      * filesystem watcher to keep track of all project files
0147      * and auto-reload
0148      */
0149     QFileSystemWatcher &fileWatcher()
0150     {
0151         return m_fileWatcher;
0152     }
0153 
0154     /**
0155      * Search for already loaded project for directory.
0156      * Avoids that we double-load stuff for same one.
0157      * @param dir director to check if we already have an open project for
0158      * @return found project to re-use or nullptr
0159      */
0160     KateProject *openProjectForDirectory(const QDir &dir);
0161 
0162     void sendMessage(const QString &text, bool error);
0163 
0164     /**
0165      * Returns project base dir for provided document
0166      */
0167     Q_INVOKABLE QString projectBaseDirForDocument(KTextEditor::Document *doc);
0168 
0169     /**
0170      * Returns project map for provided document
0171      */
0172     Q_INVOKABLE QVariantMap projectMapForDocument(KTextEditor::Document *doc);
0173 
0174 Q_SIGNALS:
0175 
0176     /**
0177      * Signal that for view to clean up
0178      * @param project to close
0179      */
0180     void pluginViewProjectClosing(KateProject *project);
0181 
0182     /**
0183      * Signal that a new project got created.
0184      * @param project new created project
0185      */
0186     void projectCreated(KateProject *project);
0187 
0188     /**
0189      * As above, but with adjusted naming and meta-object type friendly.
0190      */
0191     void projectRemoved(QObject *project);
0192     void projectAdded(QObject *project);
0193 
0194     /**
0195      * Signal that plugin configuration changed
0196      */
0197     void configUpdated();
0198 
0199 public Q_SLOTS:
0200     /**
0201      * New document got created, we need to update our connections
0202      * @param document new created document
0203      */
0204     void slotDocumentCreated(KTextEditor::Document *document);
0205 
0206     /**
0207      * Document got destroyed.
0208      * @param document deleted document
0209      */
0210     void slotDocumentDestroyed(QObject *document);
0211 
0212     /**
0213      * Url changed, to auto-load projects
0214      */
0215     void slotDocumentUrlChanged(KTextEditor::Document *document);
0216 
0217 private:
0218     KateProject *createProjectForRepository(const QString &type, const QDir &dir);
0219     KateProject *createProjectForDirectory(const QDir &dir);
0220     KateProject *createProjectForDirectory(const QDir &dir, const QVariantMap &projectMap);
0221     KateProject *detectGit(const QDir &dir);
0222     KateProject *detectSubversion(const QDir &dir);
0223     KateProject *detectMercurial(const QDir &dir);
0224     KateProject *detectFossil(const QDir &dir);
0225 
0226     void readSessionConfig(const KConfigGroup &config) override;
0227     void writeSessionConfig(KConfigGroup &config) override;
0228 
0229     void readConfig();
0230     void writeConfig();
0231 
0232     static void registerVariables();
0233     static void unregisterVariables();
0234 
0235 private:
0236     /**
0237      * open plugins, maps project base directory => project
0238      */
0239     QList<KateProject *> m_projects;
0240 
0241     /**
0242      * filesystem watcher to keep track of all project files
0243      * and auto-reload
0244      */
0245     QFileSystemWatcher m_fileWatcher;
0246 
0247     /**
0248      * Mapping document => project
0249      */
0250     std::unordered_map<KTextEditor::Document *, KateProject *> m_document2Project;
0251 
0252     // remember if we did the initial read session config
0253     bool m_initialReadSessionConfigDone = false;
0254 
0255     /**
0256      * Project completion
0257      */
0258     KateProjectCompletion m_completion;
0259 
0260     // auto discovery on per default
0261     bool m_autoGit = true;
0262     bool m_autoSubversion = true;
0263     bool m_autoMercurial = true;
0264     bool m_autoFossil = true;
0265 
0266     // restore projects on session loading?
0267     bool m_restoreProjectsForSession = true;
0268 
0269     // indexing is expensive, default off
0270     bool m_indexEnabled = false;
0271     QUrl m_indexDirectory;
0272 
0273     // some more features default off, too
0274     bool m_multiProjectCompletion = false;
0275     bool m_multiProjectGoto = false;
0276 
0277     // git features
0278     ClickAction m_singleClickAction = ClickAction::ShowDiff;
0279     ClickAction m_doubleClickAction = ClickAction::StageUnstage;
0280 
0281     /**
0282      * thread pool for our workers
0283      */
0284     QThreadPool m_threadPool;
0285 };