#ifndef ECODE_PLUGINMANAGER_HPP #define ECODE_PLUGINMANAGER_HPP #include "../projectsearch.hpp" #include "lsp/lspprotocol.hpp" #include #include #include #include #include #include #include #include #include using namespace EE; using namespace EE::System; using namespace EE::UI; using namespace EE::UI::Models; using namespace EE::UI::Tools; namespace ecode { class PluginManager; typedef std::function PluginCreatorFn; #ifdef minor #undef minor #endif #ifdef major #undef major #endif struct PluginVersion { PluginVersion() {} PluginVersion( Uint8 major, Uint8 minor, Uint8 patch ) : major( major ), minor( minor ), patch( patch ), string( String::format( "%d.%d.%d", major, minor, patch ) ) {} Uint8 major; /**< major version */ Uint8 minor; /**< minor version */ Uint8 patch; /**< update version */ std::string string; Uint32 getVersion() const { return major * 1000 + minor * 100 + patch; } const std::string& getVersionString() const { return string; } }; struct PluginDefinition { std::string id; std::string name; std::string description; PluginCreatorFn creatorFn; PluginVersion version; PluginCreatorFn creatorSyncFn{ nullptr }; }; enum class PluginMessageType { WorkspaceFolderChanged, // Broadcast the workspace folder from the application to the plugins Diagnostics, // Broadcast a document diagnostics from the LSP Client CodeCompletion, // Request the LSP Server to start a code completion in the requested document // and position LanguageServerCapabilities, // Request the language server capabilities of a language if there // is any available, it will be returned as a broadcast SignatureHelp, // Request the LSP Server to provide function/method signature help CancelRequest, // Cancel a request ID FindAndOpenClosestURI, // Request a component to find and open the closest path from an URI DocumentFormatting, // Request the LSP Server to format a document SymbolReference, // Request the LSP Server to find a symbol reference in the project ShowMessage, // The LSP server sends a request to the client to show a message on screen ShowDocument, // The LSP server sends a request to the client to show a document WorkspaceSymbol, // Request to the LSP server to query workspace symbols TextDocumentSymbol, // Request to the LSP server the document symbols TextDocumentFlattenSymbol, // Request to the LSP server the document symbols flattened DiagnosticsCodeAction, // Request a code action to anyone that can handle it Undefined }; enum class PluginMessageFormat { JSON, Diagnostics, CodeCompletion, LanguageServerCapabilities, SignatureHelp, ProjectSearchResult, ShowMessage, ShowDocument, SymbolInformation, DiagnosticsCodeAction }; class PluginIDType { public: enum class Type { Integer, String, Invalid }; PluginIDType() {} PluginIDType( Int64 val ) : mType( Type::Integer ), mInt( val ) {} PluginIDType( const std::string& val ) : mType( Type::String ), mString( val ) {} bool operator==( const PluginIDType& right ) { return mType == right.mType && ( ( mType == Type::Integer && mInt == right.mInt ) || ( mType == Type::String && mString == right.mString ) ); } bool operator!=( const PluginIDType& right ) { return !( *this == right ); } bool is( const Type& type ) const { return type == mType; } bool isString() const { return Type::String == mType; } bool isInteger() const { return Type::Integer == mType; } bool isValid() const { return mType != Type::Invalid; } std::string toString() { if ( mType == Type::Integer ) return String::toString( mInt ); return mString; } operator Int64() const { return mInt; } operator std::string() { return mString; } const Int64& asInt() const { return mInt; } const std::string& asString() const { return mString; } protected: Type mType{ Type::Invalid }; Int64 mInt{ std::numeric_limits::max() }; std::string mString; }; class LSPClientPlugin; struct PluginMessage { PluginMessageType type; PluginMessageFormat format; const void* data; PluginIDType responseID{ 0 }; // 0 if it's not a response; const nlohmann::json& asJSON() const { return *static_cast( data ); } bool isJSON() const { return format == PluginMessageFormat::JSON; } const LSPPublishDiagnosticsParams& asDiagnostics() const { return *static_cast( data ); } const LSPCompletionList& asCodeCompletion() const { return *static_cast( data ); } const LSPServerCapabilities& asLanguageServerCapabilities() const { return *static_cast( data ); } const LSPSignatureHelp& asSignatureHelp() const { return *static_cast( data ); } const ProjectSearch::Result& asProjectSearchResult() const { return *static_cast( data ); } const LSPShowMessageParams& asShowMessage() const { return *static_cast( data ); } const LSPShowDocumentParams& asShowDocument() const { return *static_cast( data ); } const LSPSymbolInformationList& asSymbolInformation() const { return *static_cast( data ); } const LSPDiagnosticsCodeAction& asDiasnosticsCodeAction() const { return *static_cast( data ); } const PluginIDType& asPluginID() const { return *static_cast( data ); } bool isResponse() const { return -1 != responseID && 0 != responseID; } bool isRequest() const { return -1 != responseID && 0 == responseID; } bool isBroadcast() const { return -1 == responseID; } }; class PluginRequestHandle { public: static PluginRequestHandle broadcast() { return PluginRequestHandle( -1 ); } static PluginRequestHandle empty() { return PluginRequestHandle(); } PluginRequestHandle() {} PluginRequestHandle( PluginIDType id ) : mId( id ) {} virtual const PluginIDType& id() const { return mId; } virtual void cancel() {} bool isEmpty() const { return mId == 0; } bool isBroadcast() const { return mId == -1; } protected: PluginIDType mId{ 0 }; }; class PluginManager { public: static constexpr int versionNumber( int major, int minor, int patch ) { return ( (major)*1000 + (minor)*100 + ( patch ) ); } static std::string versionString( int major, int minor, int patch ) { return String::format( "%d.%d.%.d", major, minor, patch ); } PluginManager( const std::string& resourcesPath, const std::string& pluginsPath, std::shared_ptr pool ); ~PluginManager(); void registerPlugin( const PluginDefinition& def ); UICodeEditorPlugin* get( const std::string& id ); bool setEnabled( const std::string& id, bool enable, bool sync = false ); bool isEnabled( const std::string& id ) const; const std::string& getResourcesPath() const; const std::string& getPluginsPath() const; const std::map& getPluginsEnabled() const; void onNewEditor( UICodeEditor* editor ); void setPluginsEnabled( const std::map& pluginsEnabled, bool sync ); const std::shared_ptr& getThreadPool() const; std::function onPluginEnabled; const std::map& getDefinitions() const; const PluginDefinition* getDefinitionIndex( const Int64& index ) const; UICodeEditorSplitter* getSplitter() const; UISceneNode* getUISceneNode() const; const std::string& getWorkspaceFolder() const; void setWorkspaceFolder( const std::string& workspaceFolder ); PluginRequestHandle sendRequest( PluginMessageType type, PluginMessageFormat format, const void* data ); PluginRequestHandle sendRequest( UICodeEditorPlugin* pluginWho, PluginMessageType type, PluginMessageFormat format, const void* data ); void sendResponse( UICodeEditorPlugin* pluginWho, PluginMessageType type, PluginMessageFormat format, const void* data, const PluginIDType& responseID ); void sendBroadcast( UICodeEditorPlugin* pluginWho, PluginMessageType, PluginMessageFormat, const void* data ); void sendBroadcast( const PluginMessageType& notification, const PluginMessageFormat& format, void* data ); void subscribeMessages( UICodeEditorPlugin* plugin, std::function cb ); void unsubscribeMessages( UICodeEditorPlugin* plugin ); void subscribeMessages( const std::string& uniqueComponentId, std::function cb ); void unsubscribeMessages( const std::string& uniqueComponentId ); protected: using SubscribedPlugins = std::map>; friend class App; std::string mResourcesPath; std::string mPluginsPath; std::string mWorkspaceFolder; std::map mPlugins; std::map mPluginsEnabled; std::map mDefinitions; std::shared_ptr mThreadPool; UICodeEditorSplitter* mSplitter{ nullptr }; Mutex mSubscribedPluginsMutex; SubscribedPlugins mSubscribedPlugins; bool mClosing{ false }; bool hasDefinition( const std::string& id ); void setSplitter( UICodeEditorSplitter* splitter ); }; class PluginsModel : public Model { public: enum Columns { Id, Title, Enabled, Description, Version }; static std::shared_ptr New( PluginManager* manager ); PluginsModel( PluginManager* manager ) : mManager( manager ) {} virtual ~PluginsModel() {} virtual size_t rowCount( const ModelIndex& ) const; virtual size_t columnCount( const ModelIndex& ) const { return mColumnNames.size(); } virtual std::string columnName( const size_t& col ) const; virtual void setColumnName( const size_t& index, const std::string& name ) { eeASSERT( index <= Columns::Version ); mColumnNames[index] = name; } virtual Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const; virtual void update() { onModelUpdate(); } PluginManager* getManager() const; protected: PluginManager* mManager; std::vector mColumnNames{ "Id", "Title", "Enabled", "Description", "Version" }; }; class UIPluginManager { public: static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager, std::function loadFileCb ); }; } // namespace ecode #endif // ECODE_PLUGINMANAGER_HPP