Add cross-directory filesystem move support

- enable efsw cross-directory move reporting in ecode
  - transfer FileSystemModel nodes between materialized parents
  - preserve selections, descendant paths, and lazy destination branches
  - update open document paths when parent directories move
  - fix persistent indexes across insert, delete, and move operations
  - clear stale tree-view metadata before model nodes are deleted
  - safely handle getRealPath() failures for stale paths
  - add model operation and filesystem move regression tests
  - update efsw and SOIL2 submodules
This commit is contained in:
Martín Lucas Golini
2026-08-17 22:42:52 -03:00
parent ffabb19d60
commit 1534253e54
17 changed files with 626 additions and 215 deletions
+2 -1
View File
@@ -3572,7 +3572,8 @@ void App::loadDirTree( const std::string& path ) {
mFolderWatches.insert( { dirTree.getPath(), 0 } );
}
mFolderWatches[dirTree.getPath()] =
mFileWatcher->addWatch( dirTree.getPath(), mFileSystemListener, true );
mFileWatcher->addWatch( dirTree.getPath(), mFileSystemListener, true,
{ { efsw::Options::ReportCrossDirectoryMoves, 1 } } );
}
mFileSystemListener->setDirTree( mDirTree );
},
+18 -10
View File
@@ -62,14 +62,12 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir
if ( file.isLink() )
file = FileInfo( file.linksTo() );
if ( isFileOpen( oldFile ) )
notifyMove( oldFile, file );
notifyMove( oldFile, file );
if ( oldFile.isLink() ) {
oldFile = FileInfo( oldFile.linksTo() );
if ( isFileOpen( oldFile ) )
notifyMove( oldFile, file );
notifyMove( oldFile, file );
}
} else if ( action == efsw::Actions::Delete ) {
notifyDelete( file );
@@ -83,8 +81,7 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir
Lock l( mCbsMutex );
if ( !mCbs.empty() ) {
auto cbs = mCbs;
for ( const auto& cb : cbs )
for ( const auto& cb : mCbs )
cb.second( event, file );
}
@@ -98,9 +95,8 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir
Lock l( mCbsMutex );
if ( !mCbs.empty() ) {
auto cbs = mCbs;
FileEvent event( (FileSystemEventType)action, dir, filename, oldFilename );
for ( const auto& cb : cbs )
for ( const auto& cb : mCbs )
cb.second( event, file );
}
}
@@ -162,9 +158,21 @@ void FileSystemListener::notifyChange( const FileInfo& file ) {
}
void FileSystemListener::notifyMove( const FileInfo& oldFile, const FileInfo& newFile ) {
std::string oldPath( oldFile.getFilepath() );
std::string newPath( newFile.getFilepath() );
const bool directoryMoved = newFile.isDirectory();
if ( directoryMoved ) {
FileSystem::dirAddSlashAtEnd( oldPath );
FileSystem::dirAddSlashAtEnd( newPath );
}
mSplitter->forEachDoc( [&]( TextDocument& doc ) {
if ( oldFile.getFilepath() == doc.getFileInfo().getFilepath() )
doc.notifyDocumentMoved( newFile.getFilepath() );
const std::string& documentPath = doc.getFileInfo().getFilepath();
if ( oldPath == documentPath ) {
doc.notifyDocumentMoved( newPath );
} else if ( directoryMoved && String::startsWith( documentPath, oldPath ) ) {
doc.notifyDocumentMoved( newPath + documentPath.substr( oldPath.size() ) );
}
} );
}