From 20b84171e9afa0717349e556b68bb6c6ba5481c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 25 Mar 2022 19:26:04 -0300 Subject: [PATCH] eepp: Added support to load documents asynchronously. Added FileSystem::isRelativePath. Added TextPosition::fromString and TextRange::fromString. UICodeEditor: don't show currently loaded content until document load is complete. Added UICodeEditorSplitter::loadAsyncFileFromPath and UICodeEditorSplitter::loadAsyncFileFromPathInNewTab. ecode: ecode now load documents async. Fix: detect when an open document is open and has been moved in the file system, update the new location and name. Remember document position and last focused document on project load. --- include/eepp/scene/event.hpp | 1 + include/eepp/system/filesystem.hpp | 3 + include/eepp/ui/doc/textdocument.hpp | 10 +++ include/eepp/ui/doc/textposition.hpp | 28 ++++++--- include/eepp/ui/doc/textrange.hpp | 11 +++- .../eepp/ui/tools/uicodeeditorsplitter.hpp | 12 ++++ include/eepp/ui/uicodeeditor.hpp | 12 +++- include/eepp/ui/uitextinput.hpp | 2 + src/eepp/system/filesystem.cpp | 12 ++++ src/eepp/ui/doc/textdocument.cpp | 26 ++++++++ src/eepp/ui/tools/uicodeeditorsplitter.cpp | 55 ++++++++++++++++- src/eepp/ui/uicodeeditor.cpp | 61 +++++++++++++++---- src/eepp/ui/uitextinput.cpp | 2 + src/tools/codeeditor/appconfig.cpp | 61 +++++++++++++++++-- src/tools/codeeditor/appconfig.hpp | 2 +- src/tools/codeeditor/codeeditor.cpp | 35 ++++++----- src/tools/codeeditor/filesystemlistener.cpp | 24 ++++++++ src/tools/codeeditor/filesystemlistener.hpp | 2 + 18 files changed, 312 insertions(+), 47 deletions(-) diff --git a/include/eepp/scene/event.hpp b/include/eepp/scene/event.hpp index fe7594a64..56b57ea10 100644 --- a/include/eepp/scene/event.hpp +++ b/include/eepp/scene/event.hpp @@ -79,6 +79,7 @@ class EE_API Event { OnActiveWidgetChange, OnWindowReady, OnCreateContextMenu, + OnDocumentMoved, UserEvent, NoEvent = eeINDEX_NOT_FOUND }; diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index 1633a44bb..76f7bda0b 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -150,6 +150,9 @@ class EE_API FileSystem { const std::string& fileName, const std::string& separator = ".", const std::string& fileExtension = "" ); + + /** @returns True if the path provided is relative. */ + static bool isRelativePath( const std::string& path ); }; }} // namespace EE::System diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index d34b83254..f528740dc 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ class EE_API TextDocument { virtual void onDocumentSaved( TextDocument* ) = 0; virtual void onDocumentClosed( TextDocument* ) = 0; virtual void onDocumentDirtyOnFileSystem( TextDocument* ) = 0; + virtual void onDocumentMoved( TextDocument* ) = 0; }; TextDocument( bool verbose = true ); @@ -67,6 +69,10 @@ class EE_API TextDocument { bool loadFromFile( const std::string& path ); + bool loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function onLoaded = + std::function() ); + bool loadFromMemory( const Uint8* data, const Uint32& size ); bool loadFromPack( Pack* pack, std::string filePackPath ); @@ -404,6 +410,8 @@ class EE_API TextDocument { bool hasSyntaxDefinition() const; + void notifyDocumentMoved( const std::string& newPath ); + protected: friend class UndoStack; UndoStack mUndoStack; @@ -457,6 +465,8 @@ class EE_API TextDocument { void notifyDirtyOnFileSystem(); + void notifyDocumentMoved(); + void insertAtStartOfSelectedLines( const String& text, bool skipEmpty ); void removeFromStartOfSelectedLines( const String& text, bool skipEmpty ); diff --git a/include/eepp/ui/doc/textposition.hpp b/include/eepp/ui/doc/textposition.hpp index c0da6071a..eeb456f09 100644 --- a/include/eepp/ui/doc/textposition.hpp +++ b/include/eepp/ui/doc/textposition.hpp @@ -48,26 +48,40 @@ class EE_API TextPosition { } TextPosition operator+( const TextPosition& other ) const { - return {mLine + other.line(), mColumn + other.column()}; + return { mLine + other.line(), mColumn + other.column() }; } TextPosition operator+=( const TextPosition& other ) const { - return {mLine + other.line(), mColumn + other.column()}; + return { mLine + other.line(), mColumn + other.column() }; } TextPosition operator-( const TextPosition& other ) const { - return {mLine - other.line(), mColumn - other.column()}; + return { mLine - other.line(), mColumn - other.column() }; } TextPosition operator-=( const TextPosition& other ) const { - return {mLine - other.line(), mColumn - other.column()}; + return { mLine - other.line(), mColumn - other.column() }; } - std::string toString() { return String::format( "L%lld,C%lld", mLine, mColumn ); } + std::string toString() const { return String::format( "L%lld,C%lld", mLine, mColumn ); } + + static TextPosition fromString( const std::string& pos ) { + auto split = String::split( pos, ',' ); + if ( split.size() == 2 && !split[0].empty() && !split[1].empty() ) { + if ( split[0][0] == 'L' || split[0][0] == 'l' ) + split[0] = split[0].substr( 1 ); + if ( split[1][0] == 'C' || split[0][0] == 'c' ) + split[1] = split[1].substr( 1 ); + Int64 l, c; + if ( String::fromString( l, split[0] ) && String::fromString( c, split[1] ) ) + return TextPosition( l, c ); + } + return {}; + } private: - Int64 mLine{0xffffffff}; - Int64 mColumn{0xffffffff}; + Int64 mLine{ 0xffffffff }; + Int64 mColumn{ 0xffffffff }; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/textrange.hpp b/include/eepp/ui/doc/textrange.hpp index cc27d62a3..334b7f8f8 100644 --- a/include/eepp/ui/doc/textrange.hpp +++ b/include/eepp/ui/doc/textrange.hpp @@ -61,10 +61,19 @@ class EE_API TextRange { bool inSameLine() const { return isValid() && mStart.line() == mEnd.line(); } - std::string toString() { + std::string toString() const { return String::format( "%s - %s", mStart.toString().c_str(), mEnd.toString().c_str() ); } + static TextRange fromString( const std::string& range ) { + auto split = String::split( range, "-" ); + if ( split.size() == 2 ) { + return { TextPosition::fromString( String::trim( split[0] ) ), + TextPosition::fromString( String::trim( split[1] ) ) }; + } + return {}; + } + private: TextPosition mStart; TextPosition mEnd; diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 6f5c33b51..08056c039 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -75,8 +75,18 @@ class EE_API UICodeEditorSplitter { bool loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = nullptr ); + void loadAsyncFileFromPath( const std::string& path, std::shared_ptr pool, + UICodeEditor* codeEditor = nullptr, + std::function onLoaded = + std::function() ); + void loadFileFromPathInNewTab( const std::string& path ); + void loadAsyncFileFromPathInNewTab( + const std::string& path, std::shared_ptr pool, + std::function onLoaded = + std::function() ); + void removeUnusedTab( UITabWidget* tabWidget ); UITabWidget* createEditorWithTabWidget( Node* parent ); @@ -129,6 +139,8 @@ class EE_API UICodeEditorSplitter { void setHideTabBarOnSingleTab( bool hideTabBarOnSingleTab ); + const std::vector& getTabWidgets() const; + protected: UISceneNode* mUISceneNode{ nullptr }; UICodeEditor* mCurEditor{ nullptr }; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 26190ddd6..20c78b44f 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -114,6 +114,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool loadFromFile( const std::string& path ); + bool loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function, bool )> onLoaded = + std::function, bool )>() ); + bool loadFromURL( const std::string& url, const EE::Network::Http::Request::FieldTable& headers = Http::Request::FieldTable() ); @@ -417,6 +421,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void copyFilePath(); + void scrollToCursor( bool centered = true ); + + void scrollToMakeVisible( const TextPosition& position, bool centered = false ); + protected: struct LastXOffset { TextPosition position; @@ -547,6 +555,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { virtual void onDocumentSaved( TextDocument* ); + virtual void onDocumentMoved( TextDocument* ); + void onDocumentClosed( TextDocument* doc ); virtual void onDocumentDirtyOnFileSystem( TextDocument* doc ); @@ -555,8 +565,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { int getVisibleLinesCount(); - void scrollToMakeVisible( const TextPosition& position, bool centered = false ); - void setScrollX( const Float& val, bool emmitEvent = true ); void setScrollY( const Float& val, bool emmitEvent = true ); diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index f7ea05d73..6e4904cf9 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -177,6 +177,8 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { virtual void onDocumentSaved( TextDocument* ); + virtual void onDocumentMoved( TextDocument* ); + void onDocumentClosed( TextDocument* ){}; void onDocumentDirtyOnFileSystem( TextDocument* ){}; diff --git a/src/eepp/system/filesystem.cpp b/src/eepp/system/filesystem.cpp index 1e0f87847..f4474ae7a 100644 --- a/src/eepp/system/filesystem.cpp +++ b/src/eepp/system/filesystem.cpp @@ -727,4 +727,16 @@ std::string FileSystem::fileGetNumberedFileNameFromPath( std::string directoryPa return ""; } +bool FileSystem::isRelativePath( const std::string& path ) { + if ( !path.empty() ) { + if ( path[0] == '/' ) + return false; +#if EE_PLATFORM == EE_PLATFORM_WIN + if ( path.size() >= 2 && String::isLetter( path[0] ) && path[1] == ':' ) + return false; +#endif + } + return true; +} + }} // namespace EE::System diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 0d8492e3a..e1ea2dd92 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -266,6 +266,13 @@ bool TextDocument::getBOM() const { return mIsBOM; } +void TextDocument::notifyDocumentMoved( const std::string& path ) { + mFilePath = path; + mFileRealPath = FileInfo::isLink( mFilePath ) ? FileInfo( FileInfo( mFilePath ).linksTo() ) + : FileInfo( mFilePath ); + notifyDocumentMoved(); +} + bool TextDocument::loadFromFile( const std::string& path ) { mLoading = true; if ( !FileSystem::fileExists( path ) && PackManager::instance()->isFallbackToPacksActive() ) { @@ -288,6 +295,19 @@ bool TextDocument::loadFromFile( const std::string& path ) { return ret; } +bool TextDocument::loadAsyncFromFile( const std::string& path, std::shared_ptr pool, + std::function onLoaded ) { + mLoading = true; + pool->run( + [&, path, onLoaded] { + bool loaded = loadFromFile( path ); + if ( onLoaded ) + onLoaded( this, loaded ); + }, + [] {} ); + return true; +} + bool TextDocument::loadFromMemory( const Uint8* data, const Uint32& size ) { IOStreamMemory stream( (const char*)data, size ); return loadFromStream( stream, mFilePath, true ); @@ -1881,6 +1901,12 @@ void TextDocument::notifyDirtyOnFileSystem() { } } +void TextDocument::notifyDocumentMoved() { + for ( auto& client : mClients ) { + client->onDocumentMoved( this ); + } +} + void TextDocument::initializeCommands() { mCommands["reset"] = [&] { reset(); }; mCommands["save"] = [&] { save(); }; diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 836c783f4..c41029fb0 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -319,6 +319,41 @@ bool UICodeEditorSplitter::loadFileFromPath( const std::string& path, UICodeEdit return ret; } +void UICodeEditorSplitter::loadAsyncFileFromPath( + const std::string& path, std::shared_ptr pool, UICodeEditor* codeEditor, + std::function onLoaded ) { +#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) + if ( FileSystem::isDirectory( path ) ) + return; + if ( nullptr == codeEditor ) + codeEditor = mCurEditor; + codeEditor->setColorScheme( mColorSchemes[mCurrentColorScheme] ); + bool isUrl = String::startsWith( path, "https://" ) || String::startsWith( path, "http://" ); + if ( isUrl ) { + codeEditor->loadAsyncFromURL( + path, Http::Request::FieldTable(), + [&, codeEditor, path, onLoaded]( std::shared_ptr, bool ) { + mClient->onDocumentLoaded( codeEditor, path ); + if ( onLoaded ) + onLoaded( codeEditor, path ); + } ); + } else { + codeEditor->loadAsyncFromFile( + path, pool, [&, codeEditor, path, onLoaded]( std::shared_ptr, bool ) { + mClient->onDocumentLoaded( codeEditor, path ); + if ( onLoaded ) + onLoaded( codeEditor, path ); + } ); + } +#else + loadFileFromPath( path, codeEditor ); + if ( nullptr == codeEditor ) + codeEditor = mCurEditor; + if ( onLoaded ) + onLoaded( codeEditor, path ); +#endif +} + void UICodeEditorSplitter::loadFileFromPathInNewTab( const std::string& path ) { auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); UITabWidget* tabWidget = d.first->getTabWidget(); @@ -327,6 +362,16 @@ void UICodeEditorSplitter::loadFileFromPathInNewTab( const std::string& path ) { tabWidget->setTabSelected( addedTab ); } +void UICodeEditorSplitter::loadAsyncFileFromPathInNewTab( + const std::string& path, std::shared_ptr pool, + std::function onLoaded ) { + auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); + UITabWidget* tabWidget = d.first->getTabWidget(); + UITab* addedTab = d.first; + loadAsyncFileFromPath( path, pool, d.second, onLoaded ); + tabWidget->setTabSelected( addedTab ); +} + void UICodeEditorSplitter::setCurrentEditor( UICodeEditor* editor ) { bool isNew = mCurEditor != editor; mCurEditor = editor; @@ -351,7 +396,7 @@ UICodeEditorSplitter::createCodeEditorInTabWidget( UITabWidget* tabWidget ) { } void UICodeEditorSplitter::removeUnusedTab( UITabWidget* tabWidget ) { - if ( tabWidget && tabWidget->getTabCount() == 2 && + if ( tabWidget && tabWidget->getTabCount() >= 2 && tabWidget->getTab( 0 ) ->getOwnedWidget() ->asType() @@ -475,6 +520,10 @@ void UICodeEditorSplitter::setHideTabBarOnSingleTab( bool hideTabBarOnSingleTab } } +const std::vector& UICodeEditorSplitter::getTabWidgets() const { + return mTabWidgets; +} + std::vector UICodeEditorSplitter::getAllEditors() { std::vector editors; forEachEditor( [&]( UICodeEditor* editor ) { editors.push_back( editor ); } ); @@ -762,7 +811,9 @@ void UICodeEditorSplitter::onTabClosed( const TabEvent* tabEvent ) { auto d = createCodeEditorInTabWidget( tabWidget ); d.first->getTabWidget()->setTabSelected( d.first ); } else { - tabWidget->setTabSelected( eemin( tabWidget->getTabCount() - 1, tabEvent->getTabIndex() ) ); + if ( tabWidget->getTabSelectedIndex() >= tabWidget->getTabCount() ) + tabWidget->setTabSelected( + eemin( tabWidget->getTabCount() - 1, tabEvent->getTabIndex() ) ); } if ( tabEvent->getTab()->getOwnedWidget() == mCurEditor ) setCurrentEditor( nullptr ); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 3f46ee9d6..fb37e9a44 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -188,6 +188,19 @@ void UICodeEditor::draw() { if ( mFont == NULL ) return; + if ( mDisplayLoaderIfDocumentLoading && mDoc->isLoading() ) { + UILoader* loader = getLoader(); + loader->setParent( this ); + loader->setVisible( true ); + loader->setEnabled( false ); + loader->setPixelsSize( getPixelsSize() ); + } else if ( mLoader != nullptr && !mDoc->isLoading() && mLoader->isVisible() ) { + mLoader->setVisible( false ); + } + + if ( mDoc->isLoading() ) + return; + if ( mDirtyEditor ) updateEditor(); @@ -273,16 +286,6 @@ void UICodeEditor::draw() { for ( auto& module : mModules ) module->postDraw( this, startScroll, lineHeight, cursor ); - - if ( mDisplayLoaderIfDocumentLoading && mDoc->isLoading() ) { - UILoader* loader = getLoader(); - loader->setParent( this ); - loader->setVisible( true ); - loader->setEnabled( false ); - loader->setPixelsSize( getPixelsSize() ); - } else if ( mLoader != nullptr && !mDoc->isLoading() && mLoader->isVisible() ) { - mLoader->setVisible( false ); - } } void UICodeEditor::scheduledUpdate( const Time& ) { @@ -346,6 +349,31 @@ bool UICodeEditor::loadFromFile( const std::string& path ) { return ret; } +bool UICodeEditor::loadAsyncFromFile( + const std::string& path, std::shared_ptr pool, + std::function, bool )> onLoaded ) { + bool wasLocked = isLocked(); + if ( !wasLocked ) + setLocked( true ); + bool ret = mDoc->loadAsyncFromFile( path, pool, + [this, onLoaded, wasLocked]( TextDocument*, bool success ) { + runOnMainThread( [&, onLoaded, wasLocked] { + invalidateEditor(); + updateLongestLineWidth(); + mHighlighter.changeDoc( mDoc.get() ); + invalidateDraw(); + if ( !wasLocked ) + setLocked( false ); + onDocumentLoaded(); + if ( onLoaded ) + onLoaded( mDoc, success ); + } ); + } ); + if ( !ret && !wasLocked ) + setLocked( false ); + return ret; +} + bool UICodeEditor::loadFromURL( const std::string& url, const Http::Request::FieldTable& headers ) { bool ret = mDoc->loadFromURL( url, headers ); if ( ret ) { @@ -367,14 +395,14 @@ bool UICodeEditor::loadAsyncFromURL( bool ret = mDoc->loadAsyncFromURL( url, headers, [this, onLoaded, wasLocked]( TextDocument*, bool success ) { - runOnMainThread( [&, onLoaded] { + runOnMainThread( [&, onLoaded, wasLocked] { invalidateEditor(); updateLongestLineWidth(); mHighlighter.changeDoc( mDoc.get() ); invalidateDraw(); - onDocumentLoaded(); if ( !wasLocked ) setLocked( false ); + onDocumentLoaded(); if ( onLoaded ) onLoaded( mDoc, success ); } ); @@ -1173,6 +1201,10 @@ void UICodeEditor::copyFilePath() { getUISceneNode()->getWindow()->getClipboard()->setText( mDoc->getFilePath() ); } +void UICodeEditor::scrollToCursor( bool centered ) { + scrollToMakeVisible( mDoc->getSelection().start(), centered ); +} + void UICodeEditor::updateEditor() { mDoc->setPageSize( getVisibleLinesCount() ); if ( mDoc->getActiveClient() == this ) @@ -1219,6 +1251,11 @@ void UICodeEditor::onDocumentSaved( TextDocument* doc ) { sendEvent( &event ); } +void UICodeEditor::onDocumentMoved( TextDocument* doc ) { + DocEvent event( this, doc, Event::OnDocumentMoved ); + sendEvent( &event ); +} + void UICodeEditor::onDocumentClosed( TextDocument* doc ) { DocEvent event( this, doc, Event::OnDocumentClosed ); sendEvent( &event ); diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index 2458b49a3..ce63796a8 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -430,6 +430,8 @@ void UITextInput::onDocumentUndoRedo( const TextDocument::UndoRedo& ) { void UITextInput::onDocumentSaved( TextDocument* ) {} +void UITextInput::onDocumentMoved( TextDocument* ) {} + UITextInput* UITextInput::setMaxLength( const Uint32& maxLength ) { mMaxLength = maxLength; return this; diff --git a/src/tools/codeeditor/appconfig.cpp b/src/tools/codeeditor/appconfig.cpp index dc1f4dfbe..803b3ccc7 100644 --- a/src/tools/codeeditor/appconfig.cpp +++ b/src/tools/codeeditor/appconfig.cpp @@ -159,14 +159,38 @@ void AppConfig::save( const std::vector& recentFiles, iniState.writeFile(); } +struct ProjectPath { + std::string path; + TextRange selection{ { 0, 0 }, { 0, 0 } }; + ProjectPath() {} + ProjectPath( const std::string& path, const TextRange& selection ) : + path( path ), selection( selection ) {} + + std::string toString() { return URI::encode( path ) + ";" + selection.toString(); } + + static ProjectPath fromString( const std::string& str ) { + auto split = String::split( str, ';' ); + if ( !split.empty() ) { + ProjectPath pp; + pp.path = URI::decode( split[0] ); + pp.selection = split.size() >= 2 + ? TextRange::fromString( split[1] ) + : TextRange( TextPosition( 0, 0 ), TextPosition( 0, 0 ) ); + return pp; + } + return {}; + } +}; + void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, const std::string& configPath ) { FileSystem::dirAddSlashAtEnd( projectFolder ); std::vector editors = editorSplitter->getAllEditors(); - std::vector paths; + std::vector paths; for ( auto editor : editors ) if ( editor->getDocument().hasFilepath() ) - paths.emplace_back( editor->getDocument().getFilePath() ); + paths.emplace_back( ProjectPath{ editor->getDocument().getFilePath(), + editor->getDocument().getSelection() } ); std::string projectsPath( configPath + "projects" + FileSystem::getOSSlash() ); if ( !FileSystem::fileExists( projectsPath ) ) FileSystem::makeDir( projectsPath ); @@ -175,12 +199,16 @@ void AppConfig::saveProject( std::string projectFolder, UICodeEditorSplitter* ed IniFile ini( projectCfgPath, false ); ini.setValue( "path", "folder_path", projectFolder ); for ( size_t i = 0; i < paths.size(); i++ ) - ini.setValue( "files", String::format( "file_name_%lu", i ), paths[i] ); + ini.setValue( "files", String::format( "file_name_%lu", i ), paths[i].toString() ); + ini.setValueI( "files", "current_page", + !editorSplitter->getTabWidgets().empty() + ? editorSplitter->getTabWidgets()[0]->getTabSelectedIndex() + : 0 ); ini.writeFile(); } void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath ) { + const std::string& configPath, std::shared_ptr pool ) { FileSystem::dirAddSlashAtEnd( projectFolder ); std::string projectsPath( configPath + "projects" + FileSystem::getOSSlash() ); MD5::Result hash = MD5::fromString( projectFolder ); @@ -190,11 +218,32 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed IniFile ini( projectCfgPath ); bool found; size_t i = 0; + std::vector paths; do { std::string val( ini.getValue( "files", String::format( "file_name_%lu", i ) ) ); found = !val.empty(); - if ( found && FileSystem::fileExists( val ) ) - editorSplitter->loadFileFromPathInNewTab( val ); + if ( found ) { + auto pp = ProjectPath::fromString( val ); + if ( FileSystem::fileExists( pp.path ) ) + paths.emplace_back( pp ); + } i++; } while ( found ); + + Int64 currentPage = ini.getValueI( "files", "current_page" ); + size_t totalToLoad = paths.size(); + + for ( auto& pp : paths ) { + editorSplitter->loadAsyncFileFromPathInNewTab( + pp.path, pool, + [pp, editorSplitter, totalToLoad, currentPage]( UICodeEditor* editor, + const std::string& ) { + editor->getDocument().setSelection( pp.selection ); + editor->scrollToCursor(); + + if ( !editorSplitter->getTabWidgets().empty() && + editorSplitter->getTabWidgets()[0]->getTabCount() == totalToLoad ) + editorSplitter->switchToTab( currentPage ); + } ); + } } diff --git a/src/tools/codeeditor/appconfig.hpp b/src/tools/codeeditor/appconfig.hpp index 8578aa904..3cfc389c5 100644 --- a/src/tools/codeeditor/appconfig.hpp +++ b/src/tools/codeeditor/appconfig.hpp @@ -86,7 +86,7 @@ struct AppConfig { const std::string& configPath ); void loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter, - const std::string& configPath ); + const std::string& configPath, std::shared_ptr pool ); }; #endif // APPCONFIG_HPP diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 438b3cbac..813feca84 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -7,18 +7,6 @@ App* appInstance = nullptr; -static bool isRelativePath( const std::string& path ) { - if ( !path.empty() ) { - if ( path[0] == '/' ) - return false; -#if EE_PLATFORM == EE_PLATFORM_WIN - if ( path.size() >= 2 && String::isLetter( path[0] ) && path[1] == ':' ) - return false; -#endif - } - return true; -} - void appLoop() { appInstance->mainLoop(); } @@ -187,7 +175,7 @@ void App::openFolderDialog() { void App::openFontDialog( std::string& fontPath ) { std::string absoluteFontPath( fontPath ); - if ( isRelativePath( absoluteFontPath ) ) + if ( FileSystem::isRelativePath( absoluteFontPath ) ) absoluteFontPath = mResPath + fontPath; UIFileDialog* dialog = UIFileDialog::New( UIFileDialog::DefaultFlags, "*.ttf; *.otf; *.wolff; *.otb", @@ -1302,11 +1290,19 @@ void App::loadFileFromPath( const std::string& path, bool inNewTab, UICodeEditor #endif } } else { +#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) + if ( inNewTab ) { + mEditorSplitter->loadAsyncFileFromPathInNewTab( path, mThreadPool ); + } else { + mEditorSplitter->loadAsyncFileFromPath( path, mThreadPool, codeEditor ); + } +#else if ( inNewTab ) { mEditorSplitter->loadFileFromPathInNewTab( path ); } else { mEditorSplitter->loadFileFromPath( path, codeEditor ); } +#endif } } @@ -1499,6 +1495,13 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { } } ); + editor->addEventListener( Event::OnDocumentMoved, [&]( const Event* event ) { + if ( !appInstance ) + return; + UICodeEditor* editor = event->getNode()->asType(); + updateEditorTabTitle( editor ); + } ); + if ( config.autoComplete && !mAutoCompleteModule ) setAutoComplete( config.autoComplete ); @@ -2039,7 +2042,7 @@ void App::loadFolder( const std::string& path ) { mCurrentProject = rpath; loadDirTree( rpath ); - mConfig.loadProject( rpath, mEditorSplitter, mConfigPath ); + mConfig.loadProject( rpath, mEditorSplitter, mConfigPath, mThreadPool ); mFileSystemModel = FileSystemModel::New( rpath, FileSystemModel::Mode::FilesAndDirectories, { true, true, true } ); @@ -2064,7 +2067,7 @@ void App::loadFolder( const std::string& path ) { FontTrueType* App::loadFont( const std::string& name, std::string fontPath, const std::string& fallback ) { - if ( isRelativePath( fontPath ) ) + if ( FileSystem::isRelativePath( fontPath ) ) fontPath = mResPath + fontPath; if ( fontPath.empty() || !FileSystem::fileExists( fontPath ) ) fontPath = fallback; @@ -2103,7 +2106,7 @@ void App::init( const std::string& file, const Float& pidelDensity, winSettings.Height = mConfig.window.size.getHeight(); if ( winSettings.Icon.empty() ) { winSettings.Icon = mConfig.window.winIcon; - if ( isRelativePath( winSettings.Icon ) ) + if ( FileSystem::isRelativePath( winSettings.Icon ) ) winSettings.Icon = mResPath + winSettings.Icon; } ContextSettings contextSettings = engine->createContextSettings( &mConfig.ini, "window" ); diff --git a/src/tools/codeeditor/filesystemlistener.cpp b/src/tools/codeeditor/filesystemlistener.cpp index 61d8a79d4..54b49e069 100644 --- a/src/tools/codeeditor/filesystemlistener.cpp +++ b/src/tools/codeeditor/filesystemlistener.cpp @@ -18,6 +18,23 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir if ( mDirTree ) mDirTree.get()->onChange( (ProjectDirectoryTree::Action)action, file, oldFilename ); + + if ( action == efsw::Actions::Moved ) { + FileInfo oldFile( FileSystem::isRelativePath( oldFilename ) ? dir + oldFilename + : oldFilename ); + if ( file.isLink() ) + file = FileInfo( file.linksTo() ); + + if ( isFileOpen( oldFile ) ) + notifyMove( oldFile, file ); + + if ( oldFile.isLink() ) { + oldFile = FileInfo( oldFile.linksTo() ); + + if ( isFileOpen( oldFile ) ) + notifyMove( oldFile, file ); + } + } } case efsw::Actions::Modified: { if ( file.isLink() ) @@ -52,3 +69,10 @@ void FileSystemListener::notifyChange( const FileInfo& file ) { doc.setDirtyOnFileSystem( true ); } ); } + +void FileSystemListener::notifyMove( const FileInfo& oldFile, const FileInfo& newFile ) { + mSplitter->forEachDoc( [&]( TextDocument& doc ) { + if ( oldFile.getFilepath() == doc.getFileInfo().getFilepath() ) + doc.notifyDocumentMoved( newFile.getFilepath() ); + } ); +} diff --git a/src/tools/codeeditor/filesystemlistener.hpp b/src/tools/codeeditor/filesystemlistener.hpp index a7135fe3c..2a4308e32 100644 --- a/src/tools/codeeditor/filesystemlistener.hpp +++ b/src/tools/codeeditor/filesystemlistener.hpp @@ -35,6 +35,8 @@ class FileSystemListener : public efsw::FileWatchListener { bool isFileOpen( const FileInfo& file ); void notifyChange( const FileInfo& file ); + + void notifyMove( const FileInfo& oldFile, const FileInfo& newFile ); }; #endif // FILESYSTEMLISTENER_HPP