mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-11 17:17:42 +03:00
ecode: detect file system changes in the project directory tree.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user