#include "plugin.hpp" #include "pluginmanager.hpp" #include namespace ecode { Plugin::Plugin( PluginManager* manager ) : mManager( manager ), mThreadPool( manager->getThreadPool() ), mReady( false ), // All plugins will start as "not ready" until proven the contrary mLoading( true ) // All plugins will start as "loading" until the load is complete, this is to // avoid concurrency issues {} bool Plugin::isReady() const { return mReady; } bool Plugin::isLoading() const { return mLoading; } bool Plugin::isShuttingDown() const { return mShuttingDown; } bool Plugin::hasFileConfig() { return !mConfigPath.empty(); } void Plugin::subscribeFileSystemListener() { mConfigFileInfo = FileInfo( mConfigPath ); mManager->subscribeFileSystemListener( this ); } void Plugin::unsubscribeFileSystemListener() { mManager->unsubscribeFileSystemListener( this ); } std::string Plugin::getFileConfigPath() { return mConfigPath; } PluginManager* Plugin::getManager() const { return mManager; } UISceneNode* Plugin::getUISceneNode() const { return mManager->getUISceneNode(); } String Plugin::i18n( const std::string& key, const String& def ) const { return getManager()->getUISceneNode()->i18n( key, def ); } UIIcon* Plugin::findIcon( const std::string& iconName ) { return getManager()->getUISceneNode()->findIcon( iconName ); } Drawable* Plugin::iconDrawable( const std::string& iconName, Float dpSize ) { UIIcon* icon = findIcon( iconName ); return icon ? icon->getSize( PixelDensity::dpToPx( dpSize ) ) : nullptr; } void Plugin::showMessage( LSPMessageType type, const std::string& message, const std::string& title ) { if ( !mManager ) return; LSPShowMessageParams msgReq{ type, message, { { title } } }; mManager->sendBroadcast( PluginMessageType::ShowMessage, PluginMessageFormat::ShowMessage, &msgReq ); } Plugin::~Plugin() { while ( mLoading ) Sys::sleep( Milliseconds( 1 ) ); } void Plugin::onFileSystemEvent( const FileEvent& ev, const FileInfo& file ) { if ( ev.type != FileSystemEventType::Modified || mShuttingDown || isLoading() ) return; if ( file.getFilepath() != mConfigPath || file.getModificationTime() == mConfigFileInfo.getModificationTime() ) return; std::string fileContents; FileSystem::fileGet( file.getFilepath(), fileContents ); if ( getConfigFileHash() != String::hash( fileContents ) ) { if ( mManager->isPluginReloadEnabled() && !isLoading() && isReady() ) { mConfigFileInfo = file; unsubscribeFileSystemListener(); mManager->reload( getId() ); } else { Log::debug( "Plugin %s: Configuration file has been modified: %s. But " "plugin reload is not enabled.", getTitle().c_str(), mConfigPath.c_str() ); } } else { Log::debug( "Plugin %s: Configuration file has been modified: %s. But contents " "are the same.", getTitle().c_str(), mConfigPath.c_str() ); } } void Plugin::setReady() { if ( mReady ) { Log::info( "Plugin: %s loaded and ready from process %u", getTitle().c_str(), Sys::getProcessID() ); } } PluginBase::~PluginBase() { mShuttingDown = true; unsubscribeFileSystemListener(); for ( auto editor : mEditors ) { onBeforeUnregister( editor.first ); for ( auto listener : editor.second ) editor.first->removeEventListener( listener ); editor.first->unregisterPlugin( this ); } } void PluginBase::onRegister( UICodeEditor* editor ) { Lock l( mMutex ); std::vector listeners; listeners.push_back( editor->addEventListener( Event::OnDocumentLoaded, [this, editor]( const Event* event ) { Lock l( mMutex ); const DocEvent* docEvent = static_cast( event ); mDocs.insert( docEvent->getDoc() ); mEditorDocs[editor] = docEvent->getDoc(); onDocumentLoaded( docEvent->getDoc() ); } ) ); listeners.push_back( editor->addEventListener( Event::OnDocumentClosed, [this]( const Event* event ) { { Lock l( mMutex ); const DocEvent* docEvent = static_cast( event ); TextDocument* doc = docEvent->getDoc(); onDocumentClosed( doc ); onUnregisterDocument( doc ); mDocs.erase( doc ); } } ) ); listeners.push_back( editor->addEventListener( Event::OnDocumentChanged, [this, editor]( const Event* ) { TextDocument* oldDoc = mEditorDocs[editor]; TextDocument* newDoc = editor->getDocumentRef().get(); Lock l( mMutex ); mDocs.erase( oldDoc ); mDocs.insert( newDoc ); mEditorDocs[editor] = newDoc; onDocumentChanged( editor, oldDoc ); } ) ); onRegisterListeners( editor, listeners ); mEditors.insert( { editor, listeners } ); if ( mDocs.count( editor->getDocumentRef().get() ) == 0 ) { mDocs.insert( editor->getDocumentRef().get() ); onRegisterDocument( editor->getDocumentRef().get() ); } mEditorDocs[editor] = editor->getDocumentRef().get(); } void PluginBase::onUnregister( UICodeEditor* editor ) { onBeforeUnregister( editor ); if ( mShuttingDown ) return; Lock l( mMutex ); TextDocument* doc = mEditorDocs[editor]; auto cbs = mEditors[editor]; for ( auto listener : cbs ) editor->removeEventListener( listener ); onUnregisterEditor( editor ); mEditors.erase( editor ); mEditorDocs.erase( editor ); for ( auto editorIt : mEditorDocs ) if ( editorIt.second == doc ) return; onUnregisterDocument( doc ); mDocs.erase( doc ); } } // namespace ecode