Added Sys::execute and Sys::getProcessFilePath (not tested on macOS, could be broken).
ecode:
Optimized document search (now is async).
Improved auto-reload of plugins, should solve some issues.
Added "New Window" option on Settings Menu.
This commit is contained in:
Martín Lucas Golini
2023-10-28 01:09:00 -03:00
parent 7d2fbc4678
commit b7bdfd33ba
16 changed files with 313 additions and 150 deletions

View File

@@ -35,9 +35,12 @@ class EE_API Sys {
/** Wait the time defined before returning. */
static void sleep( const Time& time );
/** @return The application path ( the executable path ) */
/** @return The application path ( the executable path without the executable ) */
static std::string getProcessPath();
/** @return The process path ( the executable file path ) */
static std::string getProcessFilePath();
/** @return The System Time */
static double getSystemTime();
@@ -94,6 +97,9 @@ class EE_API Sys {
* Other platforms will do nothing.
*/
static bool windowAttachConsole();
/** Executes a command */
static void execute( const std::string& cmd );
};
}} // namespace EE::System

View File

@@ -361,28 +361,26 @@ class EE_API TextDocument {
bool removeCommand( const std::string& command );
TextRange find( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
bool wholeWord = false, const FindReplaceType& type = FindReplaceType::Normal,
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
TextRange findLast( const String& text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
bool wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
TextRanges findAll( const String& text, bool caseSensitive = true, bool wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange(), size_t maxResults = 0 );
int replaceAll( const String& text, const String& replace, const bool& caseSensitive = true,
const bool& wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
const bool& wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
TextPosition replaceSelection( const String& replace );
TextPosition replace( String search, const String& replace, TextPosition from = { 0, 0 },
const bool& caseSensitive = true, const bool& wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
String getIndentString();
@@ -699,13 +697,11 @@ class EE_API TextDocument {
LoadStatus loadFromStream( IOStream& file, std::string path, bool callReset );
TextRange findText( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
bool wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
TextRange findTextLast( String text, TextPosition from = { 0, 0 }, bool caseSensitive = true,
bool wholeWord = false,
const FindReplaceType& type = FindReplaceType::Normal,
bool wholeWord = false, FindReplaceType type = FindReplaceType::Normal,
TextRange restrictRange = TextRange() );
};

View File

@@ -20,9 +20,9 @@
#endif
#if EE_PLATFORM == EE_PLATFORM_MACOS
#include <CoreFoundation/CoreFoundation.h>
#include <libproc.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#elif EE_PLATFORM == EE_PLATFORM_WIN
#ifndef NOMINMAX
#define NOMINMAX
@@ -48,6 +48,10 @@
#include <stdlib.h>
#endif
#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS
#include <mach-o/dyld.h>
#endif
#if EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_BSD || \
EE_PLATFORM == EE_PLATFORM_IOS
#include <sys/mount.h>
@@ -57,6 +61,7 @@
#if EE_PLATFORM == EE_PLATFORM_WIN
#include <direct.h>
#include <sys/utime.h>
#include <tchar.h>
#else
#include <sys/time.h>
#endif
@@ -488,7 +493,7 @@ static std::string sGetProcessPath() {
#if EE_PLATFORM == EE_PLATFORM_MACOS
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
pid_t pid = getpid();
int ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
int ret = proc_pidpath( pid, pathbuf, sizeof( pathbuf ) );
if ( ret >= 0 )
return FileSystem::fileRemoveFileName( std::string( pathbuf ) );
@@ -1143,4 +1148,61 @@ bool Sys::windowAttachConsole() {
return true;
}
#if EE_PLATFORM == EE_PLATFORM_WIN
static void windowsSystem( const std::string& programPath ) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof( si ) );
si.cb = sizeof( si );
ZeroMemory( &pi, sizeof( pi ) );
if ( CreateProcess( NULL, const_cast<char*>( programPath.c_str() ), NULL, NULL, FALSE, 0, NULL,
NULL, &si, &pi ) ) {
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
}
#endif
void Sys::execute( const std::string& cmd ) {
#if EE_PLATFORM == EE_PLATFORM_WIN
windowsSystem( cmd );
#else
std::system( cmd.c_str() );
#endif
}
std::string Sys::getProcessFilePath() {
char exename[PATH_MAX];
#if EE_PLATFORM == EE_PLATFORM_WIN
int len = GetModuleFileName( NULL, exename, PATH_MAX - 1 );
exename[len] = '\0';
#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID
char path[] = "/proc/self/exe";
ssize_t len = readlink( path, exename, PATH_MAX - 1 );
if ( len > 0 )
exename[len] = '\0';
#elif EE_PLATFORM == EE_PLATFORM_MACOS || EE_PLATFORM == EE_PLATFORM_IOS
/* use realpath to resolve a symlink if the process was launched from one.
** This happens when Homebrew installs a cack and creates a symlink in
** /usr/loca/bin for launching the executable from the command line. */
unsigned size = PATH_MAX;
char exepath[size];
_NSGetExecutablePath( exepath, &size );
realpath( exepath, exename );
#elif EE_PLATFORM == EE_PLATFORM_BSD
size_t len = PATH_MAX;
const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
sysctl( mib, 4, exename, &len, NULL, 0 );
#elif EE_PLATFORM == EE_PLATFORM_HAIKU
image_info info;
get_image_info( 0, &info );
strncpy( exename, info.name, sizeof( exename ) );
#else
*exename = 0;
#endif
return std::string( exename );
}
}} // namespace EE::System

View File

@@ -2160,8 +2160,7 @@ static std::pair<size_t, size_t> findLastType( const String& str, const String&
}
TextRange TextDocument::findText( String text, TextPosition from, bool caseSensitive,
bool wholeWord, const FindReplaceType& type,
TextRange restrictRange ) {
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
if ( text.empty() )
return TextRange();
from = sanitizePosition( from );
@@ -2213,7 +2212,7 @@ TextRange TextDocument::findText( String text, TextPosition from, bool caseSensi
}
TextRange TextDocument::findTextLast( String text, TextPosition from, bool caseSensitive,
bool wholeWord, const FindReplaceType& type,
bool wholeWord, FindReplaceType type,
TextRange restrictRange ) {
if ( text.empty() )
return TextRange();
@@ -2268,8 +2267,7 @@ TextRange TextDocument::findTextLast( String text, TextPosition from, bool caseS
}
TextRange TextDocument::find( const String& text, TextPosition from, bool caseSensitive,
bool wholeWord, const FindReplaceType& type,
TextRange restrictRange ) {
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
std::vector<String> textLines = text.split( '\n', true, true );
if ( textLines.empty() || textLines.size() > mLines.size() )
@@ -2349,8 +2347,7 @@ TextRange TextDocument::find( const String& text, TextPosition from, bool caseSe
}
TextRange TextDocument::findLast( const String& text, TextPosition from, bool caseSensitive,
bool wholeWord, const FindReplaceType& type,
TextRange restrictRange ) {
bool wholeWord, FindReplaceType type, TextRange restrictRange ) {
std::vector<String> textLines = text.split( '\n', true, true );
if ( textLines.empty() || textLines.size() > mLines.size() )
@@ -2439,7 +2436,7 @@ bool TextDocument::isInsertingText() const {
}
TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool wholeWord,
const FindReplaceType& type, TextRange restrictRange,
FindReplaceType type, TextRange restrictRange,
size_t maxResults ) {
TextRanges all;
TextRange found;
@@ -2475,7 +2472,7 @@ TextRanges TextDocument::findAll( const String& text, bool caseSensitive, bool w
}
int TextDocument::replaceAll( const String& text, const String& replace, const bool& caseSensitive,
const bool& wholeWord, const FindReplaceType& type,
const bool& wholeWord, FindReplaceType type,
TextRange restrictRange ) {
if ( text.empty() )
return 0;
@@ -2537,7 +2534,7 @@ void TextDocument::selectAllMatches() {
TextPosition TextDocument::replace( String search, const String& replace, TextPosition from,
const bool& caseSensitive, const bool& wholeWord,
const FindReplaceType& type, TextRange restrictRange ) {
FindReplaceType type, TextRange restrictRange ) {
TextRange found( findText( search, from, caseSensitive, wholeWord, type, restrictRange ) );
if ( found.isValid() ) {
setSelection( found );

View File

@@ -6,17 +6,18 @@ namespace ecode {
DocSearchController::DocSearchController( UICodeEditorSplitter* editorSplitter, App* app ) :
mSplitter( editorSplitter ), mApp( app ) {}
DocSearchController::~DocSearchController() {
Clock clock;
while ( mSearchingCount && clock.getElapsedTime() < Seconds( 1 ) )
Sys::sleep( Milliseconds( 10 ) );
}
void DocSearchController::refreshHighlight() {
if ( mSearchState.editor && mSplitter->editorExists( mSearchState.editor ) ) {
mSearchState.text = mFindInput->getText();
mSearchState.editor->setHighlightWord( mSearchState.toTextSearchParams() );
if ( !mSearchState.text.empty() ) {
mSearchState.editor->getDocument().setSelection( { 0, 0 } );
if ( !findNextText( mSearchState ) ) {
mFindInput->addClass( "error" );
} else {
mFindInput->removeClass( "error" );
}
findNextText( mSearchState, true );
} else {
mFindInput->removeClass( "error" );
mSearchState.editor->getDocument().setSelection(
@@ -201,86 +202,113 @@ void DocSearchController::showFindView() {
editor->getDocument().setActiveClient( editor );
}
bool DocSearchController::findPrevText( SearchState& search ) {
void DocSearchController::findPrevText( SearchState& search ) {
if ( search.text.empty() )
search.text = mLastSearch;
if ( !search.editor || !mSplitter->editorExists( search.editor ) || search.text.empty() )
return false;
return;
search.editor->getDocument().setActiveClient( search.editor );
mLastSearch = search.text;
TextDocument& doc = search.editor->getDocument();
TextRange range = doc.getDocRange();
TextPosition from = doc.getSelection( true ).start();
if ( search.range.isValid() ) {
range = doc.sanitizeRange( search.range ).normalized();
from = from < range.start() ? range.start() : from;
}
String txt( search.text );
if ( search.escapeSequences )
txt.unescape();
Uint64 tag = String::hash( "DocSearchController::findPrevText-" +
search.editor->getDocument().getHashHexString() );
mApp->getThreadPool()->removeWithTag( tag );
mApp->getThreadPool()->run( [this, search] {
ScopedOp op( [this] { mSearchingCount++; }, [this] { mSearchingCount--; } );
TextDocument& doc = search.editor->getDocument();
TextRange range = doc.getDocRange();
TextPosition from = doc.getSelection( true ).start();
if ( search.range.isValid() ) {
range = doc.sanitizeRange( search.range ).normalized();
from = from < range.start() ? range.start() : from;
}
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord, search.type,
search.range );
if ( found.isValid() ) {
doc.setSelection( found );
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
return true;
} else {
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord, search.type,
range );
String txt( search.text );
if ( search.escapeSequences )
txt.unescape();
TextRange found = doc.findLast( txt, from, search.caseSensitive, search.wholeWord,
search.type, search.range );
if ( found.isValid() ) {
doc.setSelection( found );
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
return true;
mFindInput->runOnMainThread( [this, search] {
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
} );
return;
} else {
found = doc.findLast( txt, range.end(), search.caseSensitive, search.wholeWord,
search.type, range );
if ( found.isValid() ) {
doc.setSelection( found );
mFindInput->runOnMainThread( [this, search] {
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
} );
return;
}
}
}
mFindInput->addClass( "error" );
return false;
mFindInput->runOnMainThread( [this] { mFindInput->addClass( "error" ); } );
} );
}
bool DocSearchController::findNextText( SearchState& search ) {
void DocSearchController::findNextText( SearchState& search, bool resetSelection ) {
if ( search.text.empty() )
search.text = mLastSearch;
if ( !search.editor || !mSplitter->editorExists( search.editor ) || search.text.empty() )
return false;
return;
search.editor->getDocument().setActiveClient( search.editor );
mLastSearch = search.text;
TextDocument& doc = search.editor->getDocument();
TextRange range = doc.getDocRange();
TextPosition from = doc.getSelection( true ).end();
if ( search.range.isValid() ) {
range = doc.sanitizeRange( search.range ).normalized();
from = from < range.start() ? range.start() : from;
}
String txt( search.text );
if ( search.escapeSequences )
txt.unescape();
Uint64 tag = String::hash( "DocSearchController::findNextText-" +
search.editor->getDocument().getHashHexString() );
TextRange found =
doc.find( txt, from, search.caseSensitive, search.wholeWord, search.type, range );
if ( found.isValid() ) {
doc.setSelection( found.reversed() );
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
return true;
} else {
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord, search.type,
range );
if ( found.isValid() ) {
doc.setSelection( found.reversed() );
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
return true;
}
}
mFindInput->addClass( "error" );
return false;
mApp->getThreadPool()->removeWithTag( tag );
mApp->getThreadPool()->run(
[this, search, resetSelection] {
ScopedOp op( [this] { mSearchingCount++; }, [this] { mSearchingCount--; } );
TextDocument& doc = search.editor->getDocument();
TextRange range = doc.getDocRange();
TextPosition from =
resetSelection ? TextPosition( 0, 0 ) : doc.getSelection( true ).end();
if ( search.range.isValid() ) {
range = doc.sanitizeRange( search.range ).normalized();
from = from < range.start() ? range.start() : from;
}
String txt( search.text );
if ( search.escapeSequences )
txt.unescape();
TextRange found =
doc.find( txt, from, search.caseSensitive, search.wholeWord, search.type, range );
if ( found.isValid() ) {
doc.setSelection( found.reversed() );
mFindInput->runOnMainThread( [this, search] {
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
} );
return;
} else {
found = doc.find( txt, range.start(), search.caseSensitive, search.wholeWord,
search.type, range );
if ( found.isValid() ) {
doc.setSelection( found.reversed() );
mFindInput->runOnMainThread( [this, search] {
mSplitter->addEditorPositionToNavigationHistory( search.editor );
mFindInput->removeClass( "error" );
} );
return;
}
}
mFindInput->runOnMainThread( [this] { mFindInput->addClass( "error" ); } );
},
{}, tag );
}
bool DocSearchController::replaceSelection( SearchState& search, const String& replacement ) {
@@ -333,13 +361,13 @@ int DocSearchController::replaceAll( SearchState& search, const String& replace
return count;
}
bool DocSearchController::findAndReplace( SearchState& search, const String& replace ) {
void DocSearchController::findAndReplace( SearchState& search, const String& replace ) {
if ( !search.editor || !mSplitter->editorExists( search.editor ) )
return false;
return;
if ( search.text.empty() )
search.text = mLastSearch;
if ( search.text.empty() )
return false;
return;
search.editor->getDocument().setActiveClient( search.editor );
mLastSearch = search.text;
TextDocument& doc = search.editor->getDocument();
@@ -352,9 +380,9 @@ bool DocSearchController::findAndReplace( SearchState& search, const String& rep
}
if ( doc.hasSelection() && doc.getSelectedText() == txt ) {
return replaceSelection( search, repl );
replaceSelection( search, repl );
} else {
return findNextText( search );
findNextText( search );
}
}

View File

@@ -46,21 +46,23 @@ class DocSearchController {
DocSearchController( UICodeEditorSplitter*, App* app );
~DocSearchController();
void initSearchBar(
UISearchBar* searchBar, const SearchBarConfig& searchBarConfig,
std::unordered_map<std::string, std::string> keybindings = getDefaultKeybindings() );
void showFindView();
bool findPrevText( SearchState& search );
void findPrevText( SearchState& search );
bool findNextText( SearchState& search );
void findNextText( SearchState& search, bool resetSelection = false );
bool replaceSelection( SearchState& search, const String& replacement );
int replaceAll( SearchState& search, const String& replace );
bool findAndReplace( SearchState& search, const String& replace );
void findAndReplace( SearchState& search, const String& replace );
void hideSearchBar();
@@ -73,6 +75,7 @@ class DocSearchController {
void selectAll( SearchState& search );
void refreshHighlight();
protected:
UICodeEditorSplitter* mSplitter{ nullptr };
UITextInput* mFindInput{ nullptr };
@@ -85,6 +88,7 @@ class DocSearchController {
App* mApp{ nullptr };
SearchState mSearchState;
String mLastSearch;
size_t mSearchingCount{ 0 };
};
} // namespace ecode

View File

@@ -552,6 +552,7 @@ void App::onReady() {
// Plugin reload is only available right after we render the first frame and the editor is ready
// to run.
mPluginManager->setPluginReloadEnabled( true );
Log::info( "App Ready" );
}
void App::mainLoop() {
@@ -1875,6 +1876,7 @@ std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
"open-workspace-symbol-search" },
{ { KEY_P, KeyMod::getDefaultModifier() | KEYMOD_SHIFT },
"open-document-symbol-search" },
{ { KEY_N, KEYMOD_SHIFT | KEYMOD_LALT }, "create-new-window" },
};
}
@@ -1950,7 +1952,8 @@ std::vector<std::string> App::getUnlockedCommands() {
"open-workspace-symbol-search",
"open-document-symbol-search",
"show-folder-treeview-tab",
"show-build-tab" };
"show-build-tab",
"create-new-window" };
}
bool App::isUnlockedCommand( const std::string& command ) {

View File

@@ -293,6 +293,13 @@ class App : public UICodeEditorSplitter::Client {
mTerminalManager->configureTerminalScrollback();
} );
t.setCommand( "check-for-updates", [this] { checkForUpdates( false ); } );
t.setCommand( "create-new-window", [this] {
std::string processPath = Sys::getProcessFilePath();
if ( !processPath.empty() ) {
std::string cmd( processPath + " -x" );
Sys::execute( cmd );
}
} );
mSplitter->registerSplitterCommands( t );
}

View File

@@ -73,7 +73,8 @@ void IconManager::init( UISceneNode* sceneNode, FontTrueType* iconFont, FontTrue
{ "add", 0xea12 },
{ "hammer", 0xedee },
{ "eraser", 0xec9e },
{ "file-search", 0xed05 } };
{ "file-search", 0xed05 },
{ "window", 0xf2c4 } };
for ( const auto& icon : icons )
iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) );

View File

@@ -203,9 +203,11 @@ void FormatterPlugin::loadFormatterConfig( const std::string& path, bool updateC
j["keybindings"]["format-doc"] = mKeyBindings["format-doc"];
if ( updateConfigFile ) {
data = j.dump( 2 );
FileSystem::fileWrite( path, data );
mConfigHash = String::hash( data );
std::string newData = j.dump( 2 );
if ( newData != data ) {
FileSystem::fileWrite( path, newData );
mConfigHash = String::hash( data );
}
}
if ( !j.contains( "formatters" ) )

View File

@@ -183,7 +183,11 @@ void LinterPlugin::loadLinterConfig( const std::string& path, bool updateConfigF
}
if ( updateConfigFile ) {
FileSystem::fileWrite( path, j.dump( 2 ) );
std::string newData( j.dump( 2 ) );
if ( newData != data ) {
FileSystem::fileWrite( path, newData );
mConfigHash = String::hash( newData );
}
}
if ( !j.contains( "linters" ) )

View File

@@ -908,7 +908,11 @@ void LSPClientPlugin::loadLSPConfig( std::vector<LSPDefinition>& lsps, const std
}
if ( updateConfigFile ) {
FileSystem::fileWrite( path, j.dump( 2 ) );
std::string newData( j.dump( 2 ) );
if ( newData != data ) {
FileSystem::fileWrite( path, newData );
mConfigHash = String::hash( newData );
}
}
if ( !j.contains( "servers" ) )

View File

@@ -22,6 +22,7 @@ PluginManager::~PluginManager() {
Log::debug( "PluginManager: unloading plugin %s", plugin.second->getTitle().c_str() );
eeDelete( plugin.second );
}
unsubscribeFileSystemListener();
}
void PluginManager::registerPlugin( const PluginDefinition& def ) {
@@ -65,12 +66,18 @@ bool PluginManager::isEnabled( const std::string& id ) const {
}
bool PluginManager::reload( const std::string& id ) {
if ( !isPluginReloadEnabled() ) {
Log::warning( "PluginManager: tried to reload a plugin but plugin reload is not enabled." );
return false;
}
if ( isEnabled( id ) ) {
Log::warning( "PluginManager: reloading plugin %s", id.c_str() );
Log::warning( "PluginManager: reloading plugin %s from process %u", id.c_str(),
Sys::getProcessID() );
setEnabled( id, false );
setEnabled( id, true );
return true;
}
Log::warning( "PluginManager: tried to reload a plugin but plugin is not enabled." );
return false;
}
@@ -254,6 +261,31 @@ void PluginManager::setFileSystemListener( FileSystemListener* listener ) {
mFileSystemListener = listener;
sendBroadcast( PluginMessageType::FileSystemListenerReady, PluginMessageFormat::Empty,
nullptr );
subscribeFileSystemListener();
}
void PluginManager::subscribeFileSystemListener( Plugin* plugin ) {
mPluginsFSSubs.insert( plugin );
}
void PluginManager::unsubscribeFileSystemListener( Plugin* plugin ) {
mPluginsFSSubs.erase( plugin );
}
void PluginManager::subscribeFileSystemListener() {
if ( mFileSystemListenerCb != 0 || mFileSystemListener == nullptr )
return;
mFileSystemListenerCb =
mFileSystemListener->addListener( [this]( const FileEvent& ev, const FileInfo& file ) {
for ( Plugin* plugin : mPluginsFSSubs )
plugin->onFileSystemEvent( ev, file );
} );
}
void PluginManager::unsubscribeFileSystemListener() {
if ( mFileSystemListenerCb != 0 && mFileSystemListener )
mFileSystemListener->removeListener( mFileSystemListenerCb );
}
void PluginManager::sendBroadcast( const PluginMessageType& notification,
@@ -456,45 +488,6 @@ Plugin::Plugin( PluginManager* manager ) :
// avoid concurrency issues
{}
void Plugin::subscribeFileSystemListener() {
if ( mFileSystemListenerCb != 0 || mManager->getFileSystemListener() == nullptr )
return;
mConfigFileInfo = FileInfo( mConfigPath );
mFileSystemListenerCb = mManager->getFileSystemListener()->addListener(
[this]( const FileEvent& ev, const FileInfo& file ) {
if ( ev.type != FileSystemEventType::Modified )
return;
if ( !mShuttingDown && !isLoading() && file.getFilepath() == mConfigPath &&
file.getModificationTime() != mConfigFileInfo.getModificationTime() ) {
std::string fileContents;
FileSystem::fileGet( file.getFilepath(), fileContents );
if ( getConfigFileHash() != String::hash( fileContents ) ) {
if ( mManager->isPluginReloadEnabled() && !isLoading() && isReady() ) {
mConfigFileInfo = file;
mManager->getFileSystemListener()->removeListener( mFileSystemListenerCb );
mFileSystemListenerCb = 0;
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::unsubscribeFileSystemListener() {
if ( mFileSystemListenerCb != 0 && mManager->getFileSystemListener() )
mManager->getFileSystemListener()->removeListener( mFileSystemListenerCb );
}
bool Plugin::isReady() const {
return mReady;
}
@@ -511,6 +504,15 @@ 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;
}
@@ -519,9 +521,38 @@ PluginManager* Plugin::getManager() const {
return mManager;
}
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.", getTitle().c_str() );
if ( mReady ) {
Log::info( "Plugin: %s loaded and ready from process %u", getTitle().c_str(),
Sys::getProcessID() );
}
}
PluginBase::~PluginBase() {

View File

@@ -23,6 +23,7 @@ using namespace EE::UI::Tools;
namespace ecode {
class PluginManager;
class Plugin;
class FileSystemListener;
typedef std::function<UICodeEditorPlugin*( PluginManager* pluginManager )> PluginCreatorFn;
@@ -332,6 +333,10 @@ class PluginManager {
void setPluginReloadEnabled( bool pluginReloadEnabled );
void subscribeFileSystemListener( Plugin* plugin );
void unsubscribeFileSystemListener( Plugin* plugin );
protected:
using SubscribedPlugins =
std::map<std::string, std::function<PluginRequestHandle( const PluginMessage& )>>;
@@ -348,6 +353,8 @@ class PluginManager {
Mutex mSubscribedPluginsMutex;
SubscribedPlugins mSubscribedPlugins;
OnLoadFileCb mLoadFileFn;
Uint64 mFileSystemListenerCb{ 0 };
UnorderedSet<Plugin*> mPluginsFSSubs;
bool mClosing{ false };
bool mPluginReloadEnabled{ false };
@@ -356,6 +363,10 @@ class PluginManager {
void setSplitter( UICodeEditorSplitter* splitter );
void setFileSystemListener( FileSystemListener* listener );
void subscribeFileSystemListener();
void unsubscribeFileSystemListener();
};
class PluginsModel : public Model {
@@ -418,10 +429,11 @@ class Plugin : public UICodeEditorPlugin {
virtual String::HashType getConfigFileHash() { return 0; }
virtual void onFileSystemEvent( const FileEvent& ev, const FileInfo& file );
protected:
PluginManager* mManager{ nullptr };
std::shared_ptr<ThreadPool> mThreadPool;
Uint64 mFileSystemListenerCb{ 0 };
std::string mConfigPath;
FileInfo mConfigFileInfo;

View File

@@ -93,9 +93,11 @@ void XMLToolsPlugin::load( PluginManager* pluginManager ) {
}
if ( updateConfigFile ) {
data = j.dump( 2 );
FileSystem::fileWrite( path, data );
mConfigHash = String::hash( data );
std::string newData = j.dump( 2 );
if ( newData != data ) {
FileSystem::fileWrite( path, newData );
mConfigHash = String::hash( newData );
}
}
subscribeFileSystemListener();

View File

@@ -36,6 +36,10 @@ void SettingsMenu::createSettingsMenu( App* app ) {
->add( i18n( "new_terminal", "New Terminal" ), findIcon( "terminal" ),
getKeybind( "create-new-terminal" ) )
->setId( "create-new-terminal" );
mSettingsMenu
->add( i18n( "new_window", "New Window" ), findIcon( "window" ),
getKeybind( "create-new-window" ) )
->setId( "create-new-window" );
mSettingsMenu
->add( i18n( "open_file", "Open File..." ), findIcon( "document-open" ),
getKeybind( "open-file" ) )