Sys added Sys::epochToString.

ecode: Update file system tree view on changes.
Added a couple of linters.
Allow to select global results in UITreeViewGlobalSearch (not used but implemented, it will be used for global search & replace).
This commit is contained in:
Martín Lucas Golini
2020-12-27 02:37:58 -03:00
parent 652867c202
commit 4bc9b7a644
29 changed files with 455 additions and 106 deletions

View File

@@ -823,7 +823,8 @@ void App::initGlobalSearchBar() {
} );
addClickListener( searchButton, "search-in-files" );
addClickListener( searchBarClose, "close-global-searchbar" );
mGlobalSearchTree = UITreeViewGlobalSearch::New( mEditorSplitter->getCurrentColorScheme() );
mGlobalSearchTree =
UITreeViewGlobalSearch::New( mEditorSplitter->getCurrentColorScheme(), false );
mGlobalSearchTree->setId( "search_tree" );
mGlobalSearchTree->setParent( mUISceneNode->getRoot() );
mGlobalSearchTree->setExpanderIconSize( PixelDensity::dpToPx( 20 ) );
@@ -1971,8 +1972,10 @@ bool App::setAutoComplete( bool enable ) {
bool App::setLinter( bool enable ) {
mConfig.editor.linter = enable;
if ( enable && !mLinterModule ) {
mLinterModule =
eeNew( LinterModule, ( mResPath + "assets/linters/linters.json", mThreadPool ) );
std::string path( mResPath + "assets/linters/linters.json" );
if ( FileSystem::fileExists( mConfigPath + "linters.json" ) )
path = mConfigPath + "linters.json";
mLinterModule = eeNew( LinterModule, ( path, mThreadPool ) );
mEditorSplitter->forEachEditor(
[&]( UICodeEditor* editor ) { editor->registerModule( mLinterModule ); } );
return true;
@@ -2234,9 +2237,14 @@ void App::initProjectTreeView( const std::string& path ) {
} else {
std::string rpath( FileSystem::getRealPath( path ) );
mProjectTreeView->setModel( FileSystemModel::New(
FileSystem::fileRemoveFileName( rpath ), FileSystemModel::Mode::FilesAndDirectories,
{ true, true, true } ) );
mFileSystemModel = FileSystemModel::New( FileSystem::fileRemoveFileName( rpath ),
FileSystemModel::Mode::FilesAndDirectories,
{ true, true, true } );
mProjectTreeView->setModel( mFileSystemModel );
if ( mFileSystemListener )
mFileSystemListener->setFileSystemModel( mFileSystemModel );
mEditorSplitter->loadFileFromPath( rpath );
}
@@ -2257,8 +2265,13 @@ void App::loadFolder( const std::string& path ) {
mConfig.loadProject( rpath, mEditorSplitter, mConfigPath );
mProjectTreeView->setModel( FileSystemModel::New(
rpath, FileSystemModel::Mode::FilesAndDirectories, { true, true, true } ) );
mFileSystemModel = FileSystemModel::New( rpath, FileSystemModel::Mode::FilesAndDirectories,
{ true, true, true } );
mProjectTreeView->setModel( mFileSystemModel );
if ( mFileSystemListener )
mFileSystemListener->setFileSystemModel( mFileSystemModel );
auto found = std::find( mRecentFolders.begin(), mRecentFolders.end(), rpath );
if ( found != mRecentFolders.end() )
@@ -2592,7 +2605,7 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
mFileWatcher = new efsw::FileWatcher();
mFileSystemListener = new FileSystemListener( mEditorSplitter );
mFileSystemListener = new FileSystemListener( mEditorSplitter, mFileSystemModel );
mFileWatcher->watch();
#endif

View File

@@ -188,6 +188,7 @@ class App : public UICodeEditorSplitter::Client {
std::shared_ptr<ThreadPool> mThreadPool;
std::unique_ptr<ProjectDirectoryTree> mDirTree;
UITreeView* mProjectTreeView{ nullptr };
std::shared_ptr<FileSystemModel> mFileSystemModel;
UITableView* mLocateTable{ nullptr };
UITextInput* mLocateInput{ nullptr };
UITreeViewGlobalSearch* mGlobalSearchTree{ nullptr };

View File

@@ -1,16 +1,30 @@
#include "filesystemlistener.hpp"
FileSystemListener::FileSystemListener( UICodeEditorSplitter* splitter ) : mSplitter( splitter ) {}
FileSystemListener::FileSystemListener( UICodeEditorSplitter* splitter,
std::shared_ptr<FileSystemModel> fileSystemModel ) :
mSplitter( splitter ), mFileSystemModel( fileSystemModel ) {}
void FileSystemListener::handleFileAction( efsw::WatchID, const std::string& dir,
const std::string& filename, efsw::Action action,
std::string ) {
if ( action == efsw::Actions::Modified ) {
FileInfo file( dir + filename );
if ( file.isLink() )
file = FileInfo( file.linksTo() );
if ( isFileOpen( file ) )
notifyChange( file );
FileInfo file( dir + filename );
switch ( action ) {
case efsw::Actions::Add:
case efsw::Actions::Delete:
case efsw::Actions::Moved: {
auto* node = mFileSystemModel.get()->getNodeFromPath( file.getFilepath(), true, false );
if ( node ) {
node->invalidate();
mFileSystemModel.get()->invalidate();
}
}
case efsw::Actions::Modified: {
if ( file.isLink() )
file = FileInfo( file.linksTo() );
if ( isFileOpen( file ) )
notifyChange( file );
}
}
}

View File

@@ -2,25 +2,31 @@
#define FILESYSTEMLISTENER_HPP
#include <eepp/system/fileinfo.hpp>
#include <eepp/ui/models/filesystemmodel.hpp>
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <efsw/efsw.hpp>
using namespace EE::System;
using namespace EE::UI;
using namespace EE::UI::Models;
using namespace EE::UI::Tools;
class FileSystemListener : public efsw::FileWatchListener {
public:
FileSystemListener( UICodeEditorSplitter* codeSplitter );
FileSystemListener( UICodeEditorSplitter* codeSplitter,
std::shared_ptr<FileSystemModel> fileSystemModel );
virtual ~FileSystemListener() {}
void handleFileAction( efsw::WatchID, const std::string& dir, const std::string& filename,
efsw::Action action, std::string );
void setFileSystemModel( std::shared_ptr<FileSystemModel> model ) { mFileSystemModel = model; }
protected:
UICodeEditorSplitter* mSplitter;
std::shared_ptr<FileSystemModel> mFileSystemModel;
bool isFileOpen( const FileInfo& file );

View File

@@ -50,7 +50,15 @@ void LinterModule::load( const std::string& lintersPath ) {
for ( auto& pattern : fp )
linter.files.push_back( pattern.get<std::string>() );
linter.warningPattern = obj["warning_pattern"].get<std::string>();
auto wp = obj["warning_pattern"];
if ( wp.is_array() ) {
for ( auto& warningPattern : wp )
linter.warningPattern.push_back( warningPattern.get<std::string>() );
} else {
linter.warningPattern = { wp.get<std::string>() };
}
linter.command = obj["command"].get<std::string>();
if ( obj.contains( "warning_pattern_order" ) ) {
@@ -65,6 +73,9 @@ void LinterModule::load( const std::string& lintersPath ) {
linter.warningPatternOrder.type = wpo["type"].get<int>();
}
if ( obj.contains( "column_starts_at_zero" ) )
linter.columnsStartAtZero = obj["column_starts_at_zero"].get<bool>();
mLinters.emplace_back( std::move( linter ) );
}
@@ -220,35 +231,41 @@ void LinterModule::runLinter( TextDocument* doc, const Linter& linter, const std
// Log::info( "Linter result:\n%s", data.c_str() );
LuaPattern pattern( linter.warningPattern );
std::map<Int64, LinterMatch> matches;
for ( auto& match : pattern.gmatch( data ) ) {
LinterMatch linterMatch;
std::string lineStr = match.group( linter.warningPatternOrder.line );
std::string colStr = linter.warningPatternOrder.col >= 0
? match.group( linter.warningPatternOrder.col )
: "";
linterMatch.text = match.group( linter.warningPatternOrder.message );
if ( linter.warningPatternOrder.type >= 0 ) {
std::string type( match.group( linter.warningPatternOrder.type ) );
String::toLowerInPlace( type );
if ( String::startsWith( type, "warn" ) ) {
linterMatch.type = LinterType::Warning;
} else if ( String::startsWith( type, "notice" ) ) {
linterMatch.type = LinterType::Notice;
for ( auto& warningPatterm : linter.warningPattern ) {
LuaPattern pattern( warningPatterm );
for ( auto& match : pattern.gmatch( data ) ) {
LinterMatch linterMatch;
std::string lineStr = match.group( linter.warningPatternOrder.line );
std::string colStr = linter.warningPatternOrder.col >= 0
? match.group( linter.warningPatternOrder.col )
: "";
linterMatch.text = match.group( linter.warningPatternOrder.message );
if ( linter.warningPatternOrder.type >= 0 ) {
std::string type( match.group( linter.warningPatternOrder.type ) );
String::toLowerInPlace( type );
if ( String::startsWith( type, "warn" ) ) {
linterMatch.type = LinterType::Warning;
} else if ( String::startsWith( type, "notice" ) ) {
linterMatch.type = LinterType::Notice;
}
}
}
Int64 line;
Int64 col = 1;
if ( !linterMatch.text.empty() && !lineStr.empty() &&
String::fromString( line, lineStr ) ) {
if ( !colStr.empty() )
String::fromString( col, colStr );
linterMatch.pos = { line - 1, col > 0 ? col - 1 : 0 };
linterMatch.lineCache = doc->line( line - 1 ).getHash();
matches.insert( { line - 1, std::move( linterMatch ) } );
Int64 line;
Int64 col = 1;
if ( !linterMatch.text.empty() && !lineStr.empty() &&
String::fromString( line, lineStr ) ) {
if ( !colStr.empty() ) {
String::fromString( col, colStr );
if ( linter.columnsStartAtZero )
col++;
}
linterMatch.pos = { line - 1, col > 0 ? col - 1 : 0 };
linterMatch.lineCache = doc->line( line - 1 ).getHash();
matches.insert( { line - 1, std::move( linterMatch ) } );
}
}
}

View File

@@ -10,15 +10,12 @@ using namespace EE;
using namespace EE::System;
using namespace EE::UI;
enum class LinterType {
Notice,
Warning,
Error
};
enum class LinterType { Notice, Warning, Error };
struct Linter {
std::vector<std::string> files;
std::string warningPattern;
std::vector<std::string> warningPattern;
bool columnsStartAtZero{ false };
struct {
int line{ 1 };
int col{ 2 };
@@ -87,7 +84,6 @@ class LinterModule : public UICodeEditorModule {
void setDocDirty( UICodeEditor* editor );
void invalidateEditors( TextDocument* doc );
};
#endif // EE_TOOLS_LINTER_HPP

View File

@@ -21,9 +21,33 @@ class ProjectSearch {
Result( const String& line, const TextRange& pos ) : line( line ), position( pos ) {}
String line;
TextRange position;
bool selected{ false };
};
std::string file;
std::vector<Result> results;
bool selected{ false };
void setResultsSelected( bool selected ) {
this->selected = selected;
for ( auto& res : results )
res.selected = selected;
}
bool allResultsSelected() {
for ( const auto& res : results ) {
if ( !res.selected )
return false;
}
return true;
}
bool noneResultsSelected() {
for ( const auto& res : results ) {
if ( res.selected )
return false;
}
return true;
}
};
typedef std::vector<ResultData> Result;
@@ -31,7 +55,7 @@ class ProjectSearch {
class ResultModel : public Model {
public:
enum Column { FileOrPosition, Line, LineEnd, ColumnStart, ColumnEnd };
enum Column { FileOrPosition, Line, LineEnd, ColumnStart, ColumnEnd, Selected, Data };
ResultModel( const Result& result ) : mResult( result ) {}
@@ -114,11 +138,19 @@ class ProjectSearch {
.results[index.row()]
.position.end()
.column() );
case Selected:
return Variant(
mResult[index.internalId()].results[index.row()].selected );
case Data:
return Variant(
(void*)&mResult[index.internalId()].results[index.row()] );
}
} else {
switch ( index.column() ) {
case FileOrPosition:
return Variant( mResult[index.row()].file.c_str() );
case Data:
return Variant( (void*)&mResult[index.row()] );
}
}
}

View File

@@ -1,23 +1,94 @@
#include "uitreeviewglobalsearch.hpp"
#include "projectsearch.hpp"
#include <eepp/graphics/primitives.hpp>
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
#include <eepp/ui/doc/syntaxtokenizer.hpp>
#include <eepp/ui/uicheckbox.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uistyle.hpp>
UITreeViewGlobalSearch::UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme ) :
UITreeView(), mColorScheme( colorScheme ) {
UITreeViewGlobalSearch::UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme,
bool searchReplace ) :
UITreeView(), mColorScheme( colorScheme ), mSearchReplace( searchReplace ) {
mLineNumColor = Color::fromString(
mUISceneNode->getRoot()->getUIStyle()->getVariable( "--font-hint" ).getValue() );
}
UIWidget* UITreeViewGlobalSearch::createCell( UIWidget* rowWidget, const ModelIndex& index ) {
UITableCell* widget = index.column() == (Int64)getModel()->treeColumn()
? UITreeViewCellGlobalSearch::New()
? UITreeViewCellGlobalSearch::New( mSearchReplace )
: UITableCell::New();
return setupCell( widget, rowWidget, index );
}
std::function<UITextView*()> UITreeViewCellGlobalSearch::getCheckBoxFn() {
return [&]() -> UITextView* {
UICheckBox* chk = UICheckBox::New();
addEventListener( Event::MouseClick, [&, chk]( const Event* event ) {
const MouseEvent* mouseEvent = static_cast<const MouseEvent*>( event );
Vector2f pos = convertToNodeSpace( mouseEvent->getPosition().asFloat() );
Rectf box( { convertToNodeSpace( chk->getCurrentButton()->convertToWorldSpace(
chk->getCurrentButton()->getPixelsPosition() ) ),
chk->getCurrentButton()->getPixelsSize() } );
if ( box.contains( pos ) ) {
if ( getCurIndex().internalId() != -1 ) {
ProjectSearch::ResultData::Result* result = getResultPtr();
if ( result ) {
result->selected = !result->selected;
chk->setChecked( result->selected );
ProjectSearch::ResultData* parentData =
(ProjectSearch::ResultData*)getDataPtr( getCurIndex().parent() );
if ( parentData )
parentData->selected = parentData->allResultsSelected();
}
} else {
auto* result = getResultDataPtr();
result->setResultsSelected( !result->selected );
chk->setChecked( result->selected );
}
}
return 1;
} );
return chk;
};
}
void* UITreeViewCellGlobalSearch::getDataPtr( const ModelIndex& modelIndex ) {
const ProjectSearch::ResultModel* model =
static_cast<const ProjectSearch::ResultModel*>( modelIndex.model() );
ModelIndex index =
model->index( modelIndex.row(), ProjectSearch::ResultModel::Data, modelIndex.parent() );
Variant var( model->data( index, Model::Role::Custom ) );
if ( var.is( Variant::Type::DataPtr ) )
return var.asDataPtr();
return nullptr;
}
ProjectSearch::ResultData::Result* UITreeViewCellGlobalSearch::getResultPtr() {
if ( getCurIndex().internalId() == -1 )
return nullptr;
void* ptr = getDataPtr( getCurIndex() );
if ( ptr )
return (ProjectSearch::ResultData::Result*)ptr;
return nullptr;
}
ProjectSearch::ResultData* UITreeViewCellGlobalSearch::getResultDataPtr() {
if ( getCurIndex().internalId() != -1 )
return nullptr;
void* ptr = getDataPtr( getCurIndex() );
if ( ptr )
return (ProjectSearch::ResultData*)ptr;
return nullptr;
}
UITreeViewCellGlobalSearch::UITreeViewCellGlobalSearch( bool selectionEnabled ) :
UITreeViewCell( selectionEnabled ? getCheckBoxFn() : nullptr ) {}
UIPushButton* UITreeViewCellGlobalSearch::setText( const String& text ) {
if ( text != mTextBox->getText() ) {
mTextBox->setVisible( !text.empty() );
@@ -107,13 +178,30 @@ void UITreeViewCellGlobalSearch::draw() {
mTextBox->getFont()->getGlyph( L' ', mTextBox->getPixelsFontSize(), false ).advance;
Primitives p;
p.setColor( pp->getColorScheme().getEditorSyntaxStyle( "selection" ).color );
Vector2f screenPos( mScreenPos );
if ( mTextBox->isType( UI_TYPE_CHECKBOX ) ) {
UICheckBox* chk = mTextBox->asType<UICheckBox>();
screenPos.x += chk->getRealAlignOffset().x;
}
p.drawRectangle( Rectf(
{ mScreenPos.x + mTextBox->getPixelsPosition().x +
{ screenPos.x + mTextBox->getPixelsPosition().x +
getXOffsetCol( hspace, mTextBox->getTextCache()->getTabWidth(),
mTextBox->getText(), mSearchStrPos.first ),
mScreenPos.y + mTextBox->getPixelsPosition().y },
screenPos.y + mTextBox->getPixelsPosition().y },
Sizef( getTextWidth( hspace, mTextBox->getTextCache()->getTabWidth(), mResultStr ),
mTextBox->getPixelsSize().getHeight() ) ) );
}
}
}
void UITreeViewCellGlobalSearch::updateCell( Model* ) {
if ( mTextBox->isType( UI_TYPE_CHECKBOX ) ) {
UICheckBox* chk = mTextBox->asType<UICheckBox>();
auto* result = getResultPtr();
if ( result ) {
chk->setChecked( result->selected );
} else if ( auto* dataResult = getResultDataPtr() ) {
chk->setChecked( dataResult->selected );
}
}
}

View File

@@ -10,9 +10,11 @@ using namespace EE::UI::Doc;
class UITreeViewCellGlobalSearch : public UITreeViewCell {
public:
static UITreeViewCellGlobalSearch* New() { return eeNew( UITreeViewCellGlobalSearch, () ); }
static UITreeViewCellGlobalSearch* New( bool selectionEnabled ) {
return eeNew( UITreeViewCellGlobalSearch, ( selectionEnabled ) );
}
UITreeViewCellGlobalSearch() : UITreeViewCell() {}
UITreeViewCellGlobalSearch( bool selectionEnabled );
UIPushButton* setText( const String& text );
@@ -20,18 +22,28 @@ class UITreeViewCellGlobalSearch : public UITreeViewCell {
virtual void draw();
virtual void updateCell( Model* model );
protected:
std::pair<size_t, size_t> mSearchStrPos;
String mResultStr;
std::function<UITextView*()> getCheckBoxFn();
void* getDataPtr( const ModelIndex& modelIndex );
ProjectSearch::ResultData::Result* getResultPtr();
ProjectSearch::ResultData* getResultDataPtr();
};
class UITreeViewGlobalSearch : public UITreeView {
public:
static UITreeViewGlobalSearch* New( const SyntaxColorScheme& colorScheme ) {
return eeNew( UITreeViewGlobalSearch, ( colorScheme ) );
static UITreeViewGlobalSearch* New( const SyntaxColorScheme& colorScheme, bool searchReplace ) {
return eeNew( UITreeViewGlobalSearch, ( colorScheme, searchReplace ) );
}
UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme );
UITreeViewGlobalSearch( const SyntaxColorScheme& colorScheme, bool searchReplace );
UIWidget* createCell( UIWidget* rowWidget, const ModelIndex& index );
@@ -49,6 +61,7 @@ class UITreeViewGlobalSearch : public UITreeView {
Color mLineNumColor;
SyntaxColorScheme mColorScheme;
String mSearchStr;
bool mSearchReplace{ false };
};
#endif // UITREEVIEWGLOBALSEARCH_HPP