ecode: detect file system changes in the project directory tree.

This commit is contained in:
Martín Lucas Golini
2021-01-24 01:06:45 -03:00
parent 239c5e00c6
commit 5b33d8ed5e
8 changed files with 236 additions and 12 deletions

View File

@@ -54,6 +54,10 @@ class EE_API FileInfo {
const std::string& getFilepath() const;
std::string getFileName() const;
std::string getDirectoryPath() const;
const Uint64& getModificationTime() const;
const Uint64& getSize() const;

View File

@@ -148,6 +148,21 @@ const std::string& FileInfo::getFilepath() const {
return mFilepath;
}
std::string FileInfo::getFileName() const {
if ( !mFilepath.empty() && mFilepath[mFilepath.size() - 1] != '\\' &&
mFilepath[mFilepath.size() - 1] != '/' )
return FileSystem::fileNameFromPath( mFilepath );
std::string path( mFilepath );
FileSystem::dirRemoveSlashAtEnd( mFilepath );
return FileSystem::fileNameFromPath( path );
}
std::string FileInfo::getDirectoryPath() const {
if ( isDirectory() )
return mFilepath;
return FileSystem::fileRemoveFileName( mFilepath );
}
const Uint64& FileInfo::getModificationTime() const {
return mModificationTime;
}

View File

@@ -1126,6 +1126,9 @@ void App::closeEditors() {
tabWidget->removeTab( (UITab*)editor->getData() );
}
mCurrentProject = "";
mDirTree = nullptr;
if ( mFileSystemListener )
mFileSystemListener->setDirTree( mDirTree );
}
void App::closeFolder() {
@@ -1253,7 +1256,7 @@ void App::hideLocateBar() {
}
bool App::isDirTreeReady() const {
return mDirTreeReady;
return mDirTreeReady && mDirTree != nullptr;
}
NotificationCenter* App::getNotificationCenter() const {
@@ -1650,7 +1653,8 @@ void App::removeFolderWatches() {
void App::loadDirTree( const std::string& path ) {
Clock* clock = eeNew( Clock, () );
mDirTree = std::make_unique<ProjectDirectoryTree>( path, mThreadPool );
mDirTreeReady = false;
mDirTree = std::make_shared<ProjectDirectoryTree>( path, mThreadPool );
Log::info( "Loading DirTree: %s", path.c_str() );
mDirTree->scan(
[&, clock]( ProjectDirectoryTree& dirTree ) {
@@ -1664,6 +1668,7 @@ void App::loadDirTree( const std::string& path ) {
auto newDirs = dirTree.getDirectories();
for ( const auto& dir : newDirs )
mFolderWatches.insert( mFileWatcher->addWatch( dir, mFileSystemListener ) );
mFileSystemListener->setDirTree( mDirTree );
}
},
SyntaxDefinitionManager::instance()->getExtensionsPatternsSupported() );

View File

@@ -104,7 +104,7 @@ class App : public UICodeEditorSplitter::Client {
LinterModule* mLinterModule{ nullptr };
FormatterModule* mFormatterModule{ nullptr };
std::shared_ptr<ThreadPool> mThreadPool;
std::unique_ptr<ProjectDirectoryTree> mDirTree;
std::shared_ptr<ProjectDirectoryTree> mDirTree;
UITreeView* mProjectTreeView{ nullptr };
std::shared_ptr<FileSystemModel> mFileSystemModel;
size_t mMenuIconSize;

View File

@@ -6,7 +6,7 @@ FileSystemListener::FileSystemListener( UICodeEditorSplitter* splitter,
void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir,
const std::string& filename, efsw::Action action,
std::string ) {
std::string oldFilename ) {
FileInfo file( dir + filename );
switch ( action ) {
@@ -18,6 +18,9 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir
node->invalidate();
mFileSystemModel.get()->invalidate();
}
if ( mDirTree )
mDirTree.get()->onChange( (ProjectDirectoryTree::Action)action, file, oldFilename );
}
case efsw::Actions::Modified: {
if ( file.isLink() )
@@ -28,6 +31,10 @@ void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir
}
}
void FileSystemListener::setDirTree( const std::shared_ptr<ProjectDirectoryTree>& dirTree ) {
mDirTree = dirTree;
}
bool FileSystemListener::isFileOpen( const FileInfo& file ) {
bool found = false;
mSplitter->forEachDocStoppable( [&]( TextDocument& doc ) {

View File

@@ -1,6 +1,7 @@
#ifndef FILESYSTEMLISTENER_HPP
#define FILESYSTEMLISTENER_HPP
#include "projectdirectorytree.hpp"
#include <eepp/system/fileinfo.hpp>
#include <eepp/ui/models/filesystemmodel.hpp>
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
@@ -20,13 +21,16 @@ class FileSystemListener : public efsw::FileWatchListener {
virtual ~FileSystemListener() {}
void handleFileAction( efsw::WatchID, const std::string& dir, const std::string& filename,
efsw::Action action, std::string );
efsw::Action action, std::string oldFilename );
void setFileSystemModel( std::shared_ptr<FileSystemModel> model ) { mFileSystemModel = model; }
void setDirTree( const std::shared_ptr<ProjectDirectoryTree>& dirTree );
protected:
UICodeEditorSplitter* mSplitter;
std::shared_ptr<FileSystemModel> mFileSystemModel;
std::shared_ptr<ProjectDirectoryTree> mDirTree;
bool isFileOpen( const FileInfo& file );

View File

@@ -1,7 +1,6 @@
#include "projectdirectorytree.hpp"
#include <algorithm>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/luapattern.hpp>
ProjectDirectoryTree::ProjectDirectoryTree( const std::string& path,
std::shared_ptr<ThreadPool> threadPool ) :
@@ -10,20 +9,22 @@ ProjectDirectoryTree::ProjectDirectoryTree( const std::string& path,
}
void ProjectDirectoryTree::scan( const ProjectDirectoryTree::ScanCompleteEvent& scanComplete,
const std::vector<std::string>& acceptedPattern,
const std::vector<std::string>& acceptedPatterns,
const bool& ignoreHidden ) {
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ )
mPool->run(
[&, acceptedPattern, ignoreHidden] {
[&, acceptedPatterns, ignoreHidden] {
#endif
Lock l( mFilesMutex );
mIgnoreHidden = ignoreHidden;
mDirectories.push_back( mPath );
if ( !acceptedPattern.empty() ) {
if ( !acceptedPatterns.empty() ) {
std::vector<std::string> files;
std::vector<std::string> names;
std::vector<LuaPattern> patterns;
for ( auto& strPattern : acceptedPattern )
for ( auto& strPattern : acceptedPatterns )
patterns.emplace_back( LuaPattern( strPattern ) );
mAcceptedPatterns = patterns;
std::set<std::string> info;
getDirectoryFiles( files, names, mPath, info, ignoreHidden, mIgnoreMatcher );
size_t namesCount = names.size();
@@ -134,7 +135,7 @@ bool ProjectDirectoryTree::isFileInTree( const std::string& filePath ) const {
bool ProjectDirectoryTree::isDirInTree( const std::string& dirTree ) const {
std::string dir( FileSystem::fileRemoveFileName( dirTree ) );
FileSystem::dirAddSlashAtEnd( dir );
return std::find( mDirectories.begin(), mDirectories.end(), dirTree ) != mDirectories.end();
return std::find( mDirectories.begin(), mDirectories.end(), dir ) != mDirectories.end();
}
void ProjectDirectoryTree::getDirectoryFiles( std::vector<std::string>& files,
@@ -174,3 +175,164 @@ void ProjectDirectoryTree::getDirectoryFiles( std::vector<std::string>& files,
}
}
}
void ProjectDirectoryTree::onChange( const ProjectDirectoryTree::Action& action,
const FileInfo& file, const std::string& oldFilename ) {
if ( !file.isDirectory() && !isDirInTree( file.getFilepath() ) )
return;
switch ( action ) {
case ProjectDirectoryTree::Action::Add:
addFile( file );
break;
case ProjectDirectoryTree::Action::Delete:
removeFile( file );
break;
case ProjectDirectoryTree::Action::Moved:
moveFile( file, oldFilename );
break;
case ProjectDirectoryTree::Action::Modified:
break;
}
}
void ProjectDirectoryTree::addFile( const FileInfo& file ) {
if ( file.isDirectory() ) {
if ( !String::startsWith( file.getFilepath(), mPath ) || isDirInTree( file.getFilepath() ) )
return;
Lock l( mFilesMutex );
std::vector<std::string> files;
std::vector<std::string> names;
std::vector<LuaPattern> patterns;
std::set<std::string> info;
if ( !mAcceptedPatterns.empty() ) {
getDirectoryFiles( files, names, mPath, info, mIgnoreHidden, mIgnoreMatcher );
size_t namesCount = names.size();
bool found;
for ( size_t i = 0; i < namesCount; i++ ) {
found = false;
for ( auto& pattern : patterns ) {
if ( pattern.matches( names[i] ) ) {
found = true;
break;
}
}
if ( found ) {
mFiles.emplace_back( std::move( files[i] ) );
mNames.emplace_back( std::move( names[i] ) );
}
}
} else {
getDirectoryFiles( mFiles, mNames, mPath, info, mIgnoreHidden, mIgnoreMatcher );
}
} else {
IgnoreMatcherManager matcher( getIgnoreMatcherFromPath( file.getFilepath() ) );
if ( !matcher.foundMatch() ||
( matcher.foundMatch() && !matcher.match( file.getFilepath() ) ) ) {
bool foundPattern = mAcceptedPatterns.empty();
for ( auto& pattern : mAcceptedPatterns ) {
if ( pattern.matches( file.getFilepath() ) ) {
foundPattern = true;
break;
}
}
if ( foundPattern ) {
Lock l( mFilesMutex );
mFiles.emplace_back( file.getFilepath() );
mNames.emplace_back( file.getFileName() );
}
}
}
}
void ProjectDirectoryTree::moveFile( const FileInfo& file, const std::string& oldFilename ) {
Lock l( mFilesMutex );
if ( file.isDirectory() ) {
std::string dir( file.getDirectoryPath() );
FileSystem::dirRemoveSlashAtEnd( dir );
std::string parentDir( FileSystem::fileRemoveFileName( dir ) );
FileSystem::dirAddSlashAtEnd( parentDir );
std::string oldDir( parentDir + oldFilename );
FileSystem::dirAddSlashAtEnd( dir );
FileSystem::dirAddSlashAtEnd( oldDir );
std::vector<std::string> files;
std::vector<std::string> names;
for ( size_t i = 0; i < mFiles.size(); i++ ) {
if ( !String::startsWith( mFiles[i], oldDir ) ) {
files.emplace_back( mFiles[i] );
names.emplace_back( mNames[i] );
} else {
std::string newDir( dir + mFiles[i].substr( oldDir.size() ) );
files.emplace_back( newDir );
names.emplace_back( FileSystem::fileNameFromPath( newDir ) );
}
}
mFiles = files;
mNames = names;
auto wasDirIt = std::find( mDirectories.begin(), mDirectories.end(), oldDir );
if ( wasDirIt != mDirectories.end() )
mDirectories.erase( wasDirIt );
mDirectories.emplace_back( std::move( dir ) );
} else {
std::string dir( file.getDirectoryPath() );
FileSystem::dirAddSlashAtEnd( dir );
size_t index = findFileIndex( dir + oldFilename );
if ( index != std::string::npos ) {
mFiles[index] = file.getFilepath();
mNames[index] = file.getFileName();
}
}
}
void ProjectDirectoryTree::removeFile( const FileInfo& file ) {
Lock l( mFilesMutex );
std::string removedDir( file.getFilepath() );
FileSystem::dirAddSlashAtEnd( removedDir );
auto wasDirIt = std::find( mDirectories.begin(), mDirectories.end(), removedDir );
if ( wasDirIt != mDirectories.end() ) {
std::vector<std::string> files;
std::vector<std::string> names;
for ( size_t i = 0; i < mFiles.size(); i++ ) {
if ( !String::startsWith( mFiles[i], removedDir ) ) {
files.emplace_back( mFiles[i] );
names.emplace_back( mNames[i] );
}
}
mFiles = files;
mNames = names;
mDirectories.erase( wasDirIt );
} else {
size_t index = findFileIndex( file.getFilepath() );
if ( index != std::string::npos ) {
mFiles.erase( mFiles.begin() + index );
mNames.erase( mNames.begin() + index );
}
}
}
IgnoreMatcherManager ProjectDirectoryTree::getIgnoreMatcherFromPath( const std::string& path ) {
std::string dir( FileSystem::fileRemoveFileName( path ) );
std::string ldir;
FileSystem::dirAddSlashAtEnd( dir );
IgnoreMatcherManager dirMatcher( dir );
while ( !dirMatcher.foundMatch() ) {
dirMatcher = IgnoreMatcherManager( dir );
if ( !dirMatcher.foundMatch() ) {
if ( dir.empty() || dir.find_first_of( "/\\" ) == std::string::npos || dir == mPath ||
dir == ldir )
break;
ldir = dir;
FileSystem::dirRemoveSlashAtEnd( dir );
dir = FileSystem::fileRemoveFileName( dir );
FileSystem::dirAddSlashAtEnd( dir );
}
}
return dirMatcher;
}
size_t ProjectDirectoryTree::findFileIndex( const std::string& path ) {
for ( size_t i = 0; i < mFiles.size(); i++ ) {
if ( mFiles[i] == path )
return i;
}
return std::string::npos;
}

View File

@@ -2,6 +2,7 @@
#define EE_TOOLS_PROJECTDIRECTORYTREE_HPP
#include "ignorematcher.hpp"
#include <eepp/system/luapattern.hpp>
#include <eepp/system/mutex.hpp>
#include <eepp/system/threadpool.hpp>
#include <eepp/ui/models/model.hpp>
@@ -45,13 +46,24 @@ class FileListModel : public Model {
class ProjectDirectoryTree {
public:
enum Action {
/// Sent when a file is created or renamed
Add = 1,
/// Sent when a file is deleted or renamed
Delete = 2,
/// Sent when a file is modified
Modified = 3,
/// Sent when a file is moved
Moved = 4
};
typedef std::function<void( ProjectDirectoryTree& dirTree )> ScanCompleteEvent;
typedef std::function<void( std::shared_ptr<FileListModel> )> MatchResultCb;
ProjectDirectoryTree( const std::string& path, std::shared_ptr<ThreadPool> threadPool );
void scan( const ScanCompleteEvent& scanComplete,
const std::vector<std::string>& acceptedPattern = {},
const std::vector<std::string>& acceptedPatterns = {},
const bool& ignoreHidden = true );
std::shared_ptr<FileListModel> fuzzyMatchTree( const std::string& match,
@@ -76,19 +88,34 @@ class ProjectDirectoryTree {
bool isDirInTree( const std::string& dirTree ) const;
void onChange( const Action& action, const FileInfo& file, const std::string& oldFilename );
protected:
std::string mPath;
std::shared_ptr<ThreadPool> mPool;
std::vector<std::string> mFiles;
std::vector<std::string> mNames;
std::vector<std::string> mDirectories;
std::vector<LuaPattern> mAcceptedPatterns;
bool mIsReady;
bool mIgnoreHidden;
Mutex mFilesMutex;
IgnoreMatcherManager mIgnoreMatcher;
void getDirectoryFiles( std::vector<std::string>& files, std::vector<std::string>& names,
std::string directory, std::set<std::string> currentDirs,
const bool& ignoreHidden, const IgnoreMatcherManager& ignoreMatcher );
void addFile( const FileInfo& file );
void moveFile( const FileInfo& file, const std::string& oldFilename );
void removeFile( const FileInfo& file );
IgnoreMatcherManager getIgnoreMatcherFromPath( const std::string& path );
size_t findFileIndex( const std::string& path );
};
#endif // EE_TOOLS_PROJECTDIRECTORYTREE_HPP