Added global project search support to ecode.

This commit is contained in:
Martín Lucas Golini
2020-08-04 01:10:38 -03:00
parent 6f09fc79c9
commit 93bc53f379
21 changed files with 544 additions and 34 deletions
-4
View File
@@ -33,10 +33,6 @@ Detect errors and log them.
* Display number of results in search and number of replacements.
* Add support for global/project search.
* Add support for find and open file.
## UI Editor
* Integrate the `UICodeEditor` into the editor in order to be able to edit the layouts and CSS in app.
+1
View File
@@ -141,6 +141,7 @@ class EE_API Console : protected LogReaderInterface {
const bool& isFading() const;
protected:
Mutex mMutex;
std::map<String, ConsoleCallback> mCallbacks;
std::deque<String> mCmdLog;
std::deque<String> mLastCommands;
+2 -1
View File
@@ -44,7 +44,7 @@ class EE_API TextDocument {
virtual void onDocumentClosed( TextDocument* ) {}
};
TextDocument();
TextDocument( bool verbose = true );
~TextDocument();
@@ -337,6 +337,7 @@ class EE_API TextDocument {
bool mAutoDetectIndentType{true};
bool mForceNewLineAtEndOfFile{false};
bool mTrimTrailingWhitespaces{false};
bool mVerbose{false};
Uint32 mIndentWidth{4};
IndentType mIndentType{IndentType::IndentTabs};
Clock mTimer;
+3 -7
View File
@@ -27,12 +27,7 @@ class EE_API Model {
InvalidateAllIndexes = 1 << 0,
};
enum class Role {
Display,
Icon,
Sort,
Custom
};
enum class Role { Display, Icon, Sort, Custom };
virtual ~Model(){};
@@ -91,7 +86,8 @@ class EE_API Model {
void onModelUpdate( unsigned flags = UpdateFlag::InvalidateAllIndexes );
ModelIndex createIndex( int row, int column, const void* data = nullptr ) const;
ModelIndex createIndex( int row, int column, const void* data = nullptr,
const Int64& internalId = 0 ) const;
private:
std::unordered_set<UIAbstractView*> mViews;
+12 -3
View File
@@ -2,6 +2,7 @@
#define EE_UI_MODEL_MODELINDEX_HPP
#include <eepp/config.hpp>
#include <limits>
namespace EE { namespace UI { namespace Models {
@@ -19,6 +20,8 @@ class EE_API ModelIndex {
void* data() const { return mData; }
Int64 internalId() const { return mInternalId; }
ModelIndex parent() const;
bool hasParent() const { return parent().isValid(); }
@@ -40,11 +43,17 @@ class EE_API ModelIndex {
Int64 mRow{-1};
Int64 mColumn{-1};
void* mData{nullptr};
Int64 mInternalId{0};
ModelIndex( const Model& model, int row, int column, void* internalData ) :
mModel( &model ), mRow( row ), mColumn( column ), mData( internalData ) {}
ModelIndex( const Model& model, int row, int column, void* internalData,
const Int64& internalId = 0 ) :
mModel( &model ),
mRow( row ),
mColumn( column ),
mData( internalData ),
mInternalId( internalId ) {}
};
}}} // namespace EE::UI::Model
}}} // namespace EE::UI::Models
#endif // EE_UI_MODEL_MODELINDEX_HPP
+10
View File
@@ -32,14 +32,22 @@ class EE_API UITreeView : public UIAbstractTableView {
bool isExpanded( const ModelIndex& index ) const;
void expandAll( const ModelIndex& index = {} );
void contractAll( const ModelIndex& index = {} );
Drawable* getExpandIcon() const;
void setExpandedIcon( Drawable* expandIcon );
void setExpandedIcon( const std::string& expandIcon );
Drawable* getContractIcon() const;
void setContractedIcon( Drawable* contractIcon );
void setContractedIcon( const std::string& contractIcon );
bool getExpandersAsIcons() const;
void setExpandersAsIcons( bool expandersAsIcons );
@@ -87,6 +95,8 @@ class EE_API UITreeView : public UIAbstractTableView {
virtual void onOpenTreeModelIndex( const ModelIndex& index, bool open );
void updateContentSize();
void setAllExpanded( const ModelIndex& index = {}, bool expanded = true );
};
}} // namespace EE::UI
+2
View File
@@ -1013,6 +1013,8 @@
../../src/tools/codeeditor/ignorematcher.hpp
../../src/tools/codeeditor/projectdirectorytree.cpp
../../src/tools/codeeditor/projectdirectorytree.hpp
../../src/tools/codeeditor/projectsearch.cpp
../../src/tools/codeeditor/projectsearch.hpp
../../src/tools/codeeditor/uicodeeditorsplitter.cpp
../../src/tools/codeeditor/uicodeeditorsplitter.hpp
../../src/tools/mapeditor/mapeditor.cpp
+9 -1
View File
@@ -5,6 +5,7 @@
#include <eepp/graphics/pixeldensity.hpp>
#include <eepp/graphics/renderer/renderer.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/lock.hpp>
#include <eepp/window/cursormanager.hpp>
#include <eepp/window/engine.hpp>
#include <eepp/window/input.hpp>
@@ -217,6 +218,7 @@ void Console::addCommand( const String& Command, ConsoleCallback CB ) {
}
void Console::draw( const Time& elapsedTime ) {
Lock l( mMutex );
if ( mEnabled && NULL != mFontStyleConfig.Font ) {
fade( elapsedTime == Time::Zero ? mWindow->getElapsed() : elapsedTime );
@@ -418,6 +420,7 @@ void Console::processLine() {
}
void Console::privPushText( const String& str ) {
Lock l( mMutex );
mCmdLog.push_back( str );
if ( mCmdLog.size() >= mMaxLogLines )
@@ -726,7 +729,12 @@ void Console::privInputCallback( InputEvent* Event ) {
}
if ( KeyCode == KEY_HOME ) {
if ( static_cast<Int32>( mCmdLog.size() ) > linesOnScreen() )
size_t size;
{
Lock l( mMutex );
size = mCmdLog.size();
}
if ( static_cast<Int32>( size ) > linesOnScreen() )
mCon.ConModif = mCon.ConMin;
}
-1
View File
@@ -9,7 +9,6 @@ IOStreamFile* IOStreamFile::New( const std::string& path, const char* modes ) {
IOStreamFile::IOStreamFile( const std::string& path, const char* modes ) : mFS( NULL ), mSize( 0 ) {
mFS = std::fopen( path.c_str(), modes );
;
}
IOStreamFile::~IOStreamFile() {
+6
View File
@@ -53,7 +53,9 @@ void Log::write( std::string Text, const bool& newLine ) {
Text += '\n';
}
lock();
mData += Text;
unlock();
writeToReaders( Text );
@@ -115,7 +117,9 @@ void Log::writef( const char* format, ... ) {
tstr.resize( n );
tstr += '\n';
lock();
mData += tstr;
unlock();
writeToReaders( tstr );
@@ -169,8 +173,10 @@ void Log::setConsoleOutput( const bool& output ) {
mConsoleOutput = output;
if ( !OldOutput && output ) {
lock();
std::string data( mData );
mData = "";
unlock();
write( data, false );
}
}
+4 -3
View File
@@ -22,7 +22,7 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) const {
return mNonWordChars.find_first_of( ch ) != String::InvalidPos;
}
TextDocument::TextDocument() :
TextDocument::TextDocument( bool verbose ) :
mUndoStack( this ),
mDefaultFileName( "untitled" ),
mCleanChangeId( 0 ),
@@ -149,8 +149,9 @@ bool TextDocument::loadFromStream( IOStream& file, std::string path ) {
notifyTextChanged();
eePRINTL( "Document \"%s\" loaded in %.2fms.", path.c_str(),
clock.getElapsedTime().asMilliseconds() );
if ( mVerbose )
eePRINTL( "Document \"%s\" loaded in %.2fms.", path.c_str(),
clock.getElapsedTime().asMilliseconds() );
return true;
}
+3 -2
View File
@@ -26,8 +26,9 @@ void Model::registerView( UIAbstractView* view ) {
mViews.insert( view );
}
ModelIndex Model::createIndex( int row, int column, const void* data ) const {
return ModelIndex( *this, row, column, const_cast<void*>( data ) );
ModelIndex Model::createIndex( int row, int column, const void* data,
const Int64& internalId ) const {
return ModelIndex( *this, row, column, const_cast<void*>( data ), internalId );
}
void Model::setOnUpdate( const std::function<void()>& onUpdate ) {
+1
View File
@@ -639,6 +639,7 @@ Uint32 UICodeEditor::onFocusLoss() {
mMouseDown = false;
mCursorVisible = false;
getSceneNode()->getWindow()->stopTextInput();
getUISceneNode()->setCursor( Cursor::Arrow );
if ( mDoc->getActiveClient() == this )
mDoc->setActiveClient( nullptr );
for ( auto& module : mModules )
+35
View File
@@ -383,6 +383,33 @@ bool UITreeView::isExpanded( const ModelIndex& index ) const {
return getIndexMetadata( index ).open;
}
void UITreeView::setAllExpanded( const ModelIndex& index, bool expanded ) {
Model& model = *getModel();
size_t count = model.rowCount( index );
for ( size_t i = 0; i < count; i++ ) {
auto curIndex = model.index( i, model.treeColumn(), index );
getIndexMetadata( curIndex ).open = expanded;
if ( model.rowCount( curIndex ) > 0 )
setAllExpanded( curIndex, expanded );
}
createOrUpdateColumns();
}
void UITreeView::expandAll( const ModelIndex& index ) {
if ( !getModel() )
return;
setAllExpanded( index, true );
createOrUpdateColumns();
}
void UITreeView::contractAll( const ModelIndex& index ) {
if ( !getModel() )
return;
setAllExpanded( index, false );
createOrUpdateColumns();
}
Drawable* UITreeView::getExpandIcon() const {
return mExpandIcon;
}
@@ -394,6 +421,10 @@ void UITreeView::setExpandedIcon( Drawable* expandIcon ) {
}
}
void UITreeView::setExpandedIcon( const std::string& expandIcon ) {
setExpandedIcon( mUISceneNode->findIcon( expandIcon ) );
}
Drawable* UITreeView::getContractIcon() const {
return mContractIcon;
}
@@ -405,6 +436,10 @@ void UITreeView::setContractedIcon( Drawable* contractIcon ) {
}
}
void UITreeView::setContractedIcon( const std::string& contractIcon ) {
setContractedIcon( mUISceneNode->findIcon( contractIcon ) );
}
bool UITreeView::getExpandersAsIcons() const {
return mExpandersAsIcons;
}
+2 -2
View File
@@ -192,8 +192,8 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
Clock clock;
auto model = FileSystemModel::New( "." ); // std::make_shared<TestModel>();
//UITreeView* view = UITreeView::New();
UITableView* view = UITableView::New();
UITreeView* view = UITreeView::New();
//UITableView* view = UITableView::New();
view->setId( "treeview" );
/*view->setExpandedIcon( open );
view->setContractedIcon( closed );*/
+205 -8
View File
@@ -1,5 +1,6 @@
#include "codeeditor.hpp"
#include "autocompletemodule.hpp"
#include "projectsearch.hpp"
#include <algorithm>
#include <args/args.hxx>
@@ -390,6 +391,11 @@ std::string App::getKeybind( const std::string& command ) {
static int LOCATEBAR_MAX_VISIBLE_ITEMS = 18;
static int LOCATEBAR_MAX_RESULTS = 100;
void App::hideLocateBar() {
mLocateBarLayout->setVisible( false );
mLocateTable->setVisible( false );
}
void App::updateLocateTable() {
if ( !mLocateInput->getText().empty() ) {
mLocateTable->setModel(
@@ -430,13 +436,17 @@ void App::initLocateBar() {
mLocateTable->forceKeyDown( *keyEvent );
} );
mLocateBarLayout->addCommand( "close-locatebar", [&] {
mLocateBarLayout->setVisible( false );
mLocateTable->setVisible( false );
hideLocateBar();
mEditorSplitter->getCurEditor()->setFocus();
} );
mLocateBarLayout->getKeyBindings().addKeybindsString( {
{"escape", "close-locatebar"},
} );
mLocateTable->addEventListener( Event::KeyDown, [&]( const Event* event ) {
const KeyEvent* keyEvent = reinterpret_cast<const KeyEvent*>( event );
if ( keyEvent->getKeyCode() == KEY_ESCAPE )
mLocateBarLayout->execute( "close-locatebar" );
} );
addClickListener( mLocateBarLayout->find<UIWidget>( "locatebar_close" ), "close-locatebar" );
mLocateTable->addEventListener( Event::OnModelEvent, [&]( const Event* event ) {
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
@@ -474,7 +484,14 @@ void App::updateLocateBar() {
} );
}
void App::hideSearchBar() {
mSearchBarLayout->setEnabled( false )->setVisible( false );
}
void App::showLocateBar() {
hideGlobalSearchBar();
hideSearchBar();
mLocateBarLayout->setVisible( true );
mLocateInput->setFocus();
mLocateTable->setVisible( true );
@@ -522,8 +539,9 @@ void App::initSearchBar() {
}
} );
mSearchBarLayout->addCommand( "close-searchbar", [&] {
mSearchBarLayout->setEnabled( false )->setVisible( false );
mEditorSplitter->getCurEditor()->setFocus();
hideSearchBar();
if ( mEditorSplitter->getCurEditor() )
mEditorSplitter->getCurEditor()->setFocus();
if ( mSearchState.editor ) {
if ( mEditorSplitter->editorExists( mSearchState.editor ) ) {
mSearchState.editor->setHighlightWord( "" );
@@ -566,7 +584,154 @@ void App::initSearchBar() {
[findInput]( const Event* ) { findInput->setFocus(); } );
}
void App::showGlobalSearch() {
hideLocateBar();
hideSearchBar();
mGlobalSearchBarLayout->setVisible( true )->setEnabled( true );
mGlobalSearchInput->setFocus();
mGlobalSearchTree->setVisible( true );
updateGlobalSearchBar();
}
void App::updateGlobalSearchBar() {
mGlobalSearchBarLayout->runOnMainThread( [&] {
Float width = eeceil( mGlobalSearchInput->getPixelsSize().getWidth() );
Float rowHeight = mGlobalSearchTree->getRowHeight() * LOCATEBAR_MAX_VISIBLE_ITEMS;
mGlobalSearchTree->setPixelsSize( width, rowHeight );
width -= mGlobalSearchTree->getVerticalScrollBar()->getPixelsSize().getWidth();
mGlobalSearchTree->setColumnWidth( 0, eeceil( width ) );
Vector2f pos( mGlobalSearchInput->convertToWorldSpace( {0, 0} ) );
pos.y -= mGlobalSearchTree->getPixelsSize().getHeight();
mGlobalSearchTree->setPixelsPosition( pos );
} );
}
void App::hideGlobalSearchBar() {
mGlobalSearchBarLayout->setEnabled( false )->setVisible( false );
mGlobalSearchTree->setVisible( false );
}
void App::initGlobalSearchBar() {
auto addClickListener = [&]( UIWidget* widget, std::string cmd ) {
widget->addEventListener( Event::MouseClick, [this, cmd]( const Event* event ) {
const MouseEvent* mouseEvent = static_cast<const MouseEvent*>( event );
if ( mouseEvent->getFlags() & EE_BUTTON_LMASK )
mGlobalSearchBarLayout->execute( cmd );
} );
};
UIPushButton* searchButton = mGlobalSearchBarLayout->find<UIPushButton>( "search" );
UICheckBox* caseSensitiveBox = mGlobalSearchBarLayout->find<UICheckBox>( "case_sensitive" );
UIWidget* searchBarClose = mGlobalSearchBarLayout->find<UIWidget>( "global_searchbar_close" );
mGlobalSearchInput = mGlobalSearchBarLayout->find<UITextInput>( "global_search_find" );
mGlobalSearchInput->addEventListener( Event::OnPressEnter, [this]( const Event* ) {
mGlobalSearchBarLayout->execute( "search-in-files" );
} );
mGlobalSearchBarLayout->addCommand( "search-in-files", [&, caseSensitiveBox] {
if ( mDirTree && mDirTree->getFilesCount() > 0 && !mGlobalSearchInput->getText().empty() ) {
UILoader* loader = UILoader::New();
loader->setId( "loader " );
loader->setRadius( 48 );
loader->setOutlineThickness( 6 );
loader->setFillColor( Color::Red );
loader->setParent( mGlobalSearchTree->getParent() );
loader->setPosition( mGlobalSearchTree->getPosition() +
mGlobalSearchTree->getSize() * 0.5f - loader->getSize() * 0.5f );
Clock* clock = eeNew( Clock, () );
std::string search( mGlobalSearchInput->getText().toUtf8() );
ProjectSearch::find(
mDirTree->getFiles(), search,
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
mThreadPool,
#endif
[&, clock, search, loader]( const ProjectSearch::Result& res ) {
eePRINTL( "Global search for \"%s\" took %.2fms", search.c_str(),
clock->getElapsedTime().asMilliseconds() );
eeDelete( clock );
mUISceneNode->runOnMainThread( [&, loader, res] {
updateGlobalSearchBar();
mGlobalSearchTree->setModel( ProjectSearch::asModel( res ) );
if ( mGlobalSearchTree->getModel()->rowCount() < 50 )
mGlobalSearchTree->expandAll();
loader->setVisible( false );
loader->close();
} );
},
caseSensitiveBox->isChecked() );
}
} );
mGlobalSearchBarLayout->addCommand( "close-global-searchbar", [&] {
hideGlobalSearchBar();
if ( mEditorSplitter->getCurEditor() )
mEditorSplitter->getCurEditor()->setFocus();
} );
mGlobalSearchBarLayout->getKeyBindings().addKeybindsString( {
{"escape", "close-global-searchbar"},
} );
mGlobalSearchInput->addEventListener( Event::OnPressEnter, [&]( const Event* ) {
KeyEvent keyEvent( mGlobalSearchTree, Event::KeyDown, KEY_RETURN, 0, 0 );
mGlobalSearchTree->forceKeyDown( keyEvent );
} );
mGlobalSearchInput->addEventListener( Event::KeyDown, [&]( const Event* event ) {
const KeyEvent* keyEvent = reinterpret_cast<const KeyEvent*>( event );
mGlobalSearchTree->forceKeyDown( *keyEvent );
} );
addClickListener( searchButton, "search-in-files" );
addClickListener( searchBarClose, "close-global-searchbar" );
mGlobalSearchTree = UITreeView::New();
mGlobalSearchTree->setId( "search_tree" );
mGlobalSearchTree->setParent( mUISceneNode->getRoot() );
mGlobalSearchTree->setHeadersVisible( false );
mGlobalSearchTree->setVisible( false );
mGlobalSearchTree->setColumnsHidden(
{ProjectSearch::ResultModel::Line, ProjectSearch::ResultModel::ColumnPosition}, true );
mGlobalSearchTree->addEventListener( Event::KeyDown, [&]( const Event* event ) {
const KeyEvent* keyEvent = reinterpret_cast<const KeyEvent*>( event );
if ( keyEvent->getKeyCode() == KEY_ESCAPE )
mGlobalSearchBarLayout->execute( "close-global-searchbar" );
} );
mGlobalSearchTree->addEventListener( Event::OnModelEvent, [&]( const Event* event ) {
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
if ( modelEvent->getModelEventType() == ModelEventType::Open ) {
const Model* model = modelEvent->getModel();
if ( !model )
return;
Variant vPath(
model->data( model->index( modelEvent->getModelIndex().internalId(),
ProjectSearch::ResultModel::FileOrPosition ) ) );
if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) {
std::string path( vPath.asCStr() );
UITab* tab = mEditorSplitter->isDocumentOpen( path );
if ( !tab ) {
FileInfo fileInfo( path );
if ( fileInfo.exists() && fileInfo.isRegularFile() )
mEditorSplitter->loadFileFromPathInNewTab( path );
} else {
tab->getTabWidget()->setTabSelected( tab );
}
Variant lineNum(
model->data( model->index( modelEvent->getModelIndex().row(),
ProjectSearch::ResultModel::FileOrPosition,
modelEvent->getModelIndex().parent() ),
Model::Role::Custom ) );
Variant colNum(
model->data( model->index( modelEvent->getModelIndex().row(),
ProjectSearch::ResultModel::ColumnPosition,
modelEvent->getModelIndex().parent() ),
Model::Role::Custom ) );
if ( mEditorSplitter->getCurEditor() && lineNum.isValid() && colNum.isValid() &&
lineNum.is( Variant::Type::Int64 ) && colNum.is( Variant::Type::Int64 ) ) {
mEditorSplitter->getCurEditor()->getDocument().setSelection(
{lineNum.asInt64(), colNum.asInt64()} );
}
}
}
} );
}
void App::showFindView() {
hideLocateBar();
hideGlobalSearchBar();
UICodeEditor* editor = mEditorSplitter->getCurEditor();
if ( !editor )
return;
@@ -653,7 +818,7 @@ void App::onTextDropped( String text ) {
}
}
App::App() : mThreadPool( ThreadPool::createShared( eemin<int>( 4, Sys::getCPUCount() ) ) ) {}
App::App() : mThreadPool( ThreadPool::createShared( eemax<int>( 2, Sys::getCPUCount() ) ) ) {}
App::~App() {
saveConfig();
@@ -1286,6 +1451,7 @@ std::map<KeyBindings::Shortcut, std::string> App::getLocalKeybindings() {
{{KEY_F7, KEYMOD_NONE}, "debug-draw-boxes-toggle"},
{{KEY_F8, KEYMOD_NONE}, "debug-draw-debug-data"},
{{KEY_K, KEYMOD_CTRL}, "open-locatebar"},
{{KEY_F, KEYMOD_CTRL | KEYMOD_SHIFT}, "global-find"},
};
}
@@ -1321,6 +1487,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) {
doc.setCommand( "save-doc", [&] { saveDoc(); } );
doc.setCommand( "save-as-doc", [&] { saveFileDialog(); } );
doc.setCommand( "find-replace", [&] { showFindView(); } );
doc.setCommand( "global-find", [&] { showGlobalSearch(); } );
doc.setCommand( "open-locatebar", [&] { showLocateBar(); } );
doc.setCommand( "repeat-find", [&] { findNextText( mSearchState ); } );
doc.setCommand( "close-app", [&] { closeApp(); } );
@@ -1409,12 +1576,15 @@ bool App::setAutoComplete( bool enable ) {
UIPopUpMenu* App::createToolsMenu() {
mToolsMenu = UIPopUpMenu::New();
mToolsMenu->add( "Locate...", findIcon( "search" ), getKeybind( "open-locatebar" ) );
mToolsMenu->add( "Project Find...", findIcon( "search" ), getKeybind( "global-find" ) );
mToolsMenu->addEventListener( Event::OnItemClicked, [&]( const Event* event ) {
if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) )
return;
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
if ( item->getText() == "Locate..." ) {
showLocateBar();
} else if ( item->getText() == "Project Find..." ) {
showGlobalSearch();
}
} );
return mToolsMenu;
@@ -1565,6 +1735,8 @@ void App::initProjectTreeView( const std::string& path ) {
FileSystemModel::Inode, FileSystemModel::Owner, FileSystemModel::SymlinkTarget,
FileSystemModel::Permissions, FileSystemModel::ModificationTime, FileSystemModel::Path},
true );
mProjectTreeView->setExpandedIcon( "folder-open" );
mProjectTreeView->setContractedIcon( "folder" );
mProjectTreeView->setHeadersVisible( false );
mProjectTreeView->setExpandersAsIcons( true );
mProjectTreeView->addEventListener( Event::OnModelEvent, [&]( const Event* event ) {
@@ -1717,11 +1889,13 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
<style>
TextInput#search_find,
TextInput#search_replace,
TextInput#locate_find {
TextInput#locate_find,
TextInput#global_search_find {
padding-top: 0;
padding-bottom: 0;
}
#search_bar,
#global_search_bar,
#locate_bar {
padding-left: 4dp;
padding-right: 4dp;
@@ -1773,6 +1947,9 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
TableView#locate_bar_table > table::row:selected > table::cell:nth-child(2) {
color: var(--font);
}
#search_tree treeview::cell {
font-family: monospace;
}
</style>
<RelativeLayout id="main_layout" layout_width="match_parent" layout_height="match_parent">
<Splitter id="project_splitter" layout_width="match_parent" layout_height="match_parent">
@@ -1816,6 +1993,20 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
<TextInput id="locate_find" layout_width="0" layout_weight="1" layout_height="18dp" padding="0" margin-bottom="2dp" margin-right="4dp" hint="Type to locate..." />
<Widget id="locatebar_close" class="close_button" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="center_vertical|right"/>
</locatebar>
<globalsearchbar id="global_search_bar" layout_width="match_parent" layout_height="wrap_content">
<hbox layout_width="match_parent" layout_height="wrap_content">
<TextView layout_width="wrap_content" layout_height="wrap_content" text="Search for:" margin-right="4dp" />
<vbox layout_width="0" layout_weight="1" layout_height="wrap_content">
<TextInput id="global_search_find" layout_width="match_parent" layout_height="wrap_content" layout_height="18dp" padding="0" margin-bottom="2dp" />
<hbox layout_width="match_parent" layout_height="wrap_content">
<CheckBox id="case_sensitive" layout_width="wrap_content" layout_height="wrap_content" text="Case sensitive" selected="true" />
<Widget layout_width="0" layout_weight="1" layout_height="match_parent" />
<PushButton id="search" layout_width="wrap_content" layout_height="18dp" text="Search" />
</hbox>
</vbox>
<Widget id="global_searchbar_close" class="close_button" layout_width="wrap_content" layout_height="wrap_content" layout_gravity="top|right" margin-left="4dp" margin-top="4dp" />
</hbox>
</globalsearchbar>
</vbox>
</Splitter>
<TextView id="settings" layout_width="wrap_content" layout_height="wrap_content" text="&#xf0e9;" layout_gravity="top|right" />
@@ -1862,17 +2053,20 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
addIcon( "cancel", 0xeb98, buttonIconSize );
addIcon( "color-picker", 0xf13d, buttonIconSize );
addIcon( "pixel-density", 0xed8c, buttonIconSize );
addIcon( "tree-expanded", 0xed70, menuIconSize );
addIcon( "tree-contracted", 0xed54, menuIconSize );
addIcon( "tree-expanded", 0xea50, PixelDensity::dpToPx( 24 ) );
addIcon( "tree-contracted", 0xea54, PixelDensity::dpToPx( 24 ) );
addIcon( "search", 0xf0d1, menuIconSize );
mUISceneNode->getUIIconThemeManager()->setCurrentTheme( iconTheme );
UIWidgetCreator::registerWidget( "searchbar", [] { return UISearchBar::New(); } );
UIWidgetCreator::registerWidget( "locatebar", [] { return UILocateBar::New(); } );
UIWidgetCreator::registerWidget( "globalsearchbar",
[] { return UIGlobalSearchBar::New(); } );
mUISceneNode->loadLayoutFromString( baseUI );
mUISceneNode->bind( "main_layout", mMainLayout );
mUISceneNode->bind( "code_container", mBaseLayout );
mUISceneNode->bind( "search_bar", mSearchBarLayout );
mUISceneNode->bind( "global_search_bar", mGlobalSearchBarLayout );
mUISceneNode->bind( "locate_bar", mLocateBarLayout );
mUISceneNode->bind( "doc_info", mDocInfo );
mUISceneNode->bind( "doc_info_text", mDocInfoText );
@@ -1881,6 +2075,7 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
mUISceneNode->bind( "locate_find", mLocateInput );
mDocInfo->setVisible( mConfig.editor.showDocInfo );
mSearchBarLayout->setVisible( false )->setEnabled( false );
mGlobalSearchBarLayout->setVisible( false )->setEnabled( false );
mProjectSplitter->setSplitPartition( StyleSheetLength( mConfig.window.panelPartition ) );
if ( !mConfig.ui.showSidePanel )
@@ -1893,6 +2088,8 @@ void App::init( const std::string& file, const Float& pidelDensity ) {
initSearchBar();
initGlobalSearchBar();
initLocateBar();
createSettingsMenu();
+47
View File
@@ -68,6 +68,38 @@ class UILocateBar : public UILinearLayout {
}
};
class UIGlobalSearchBar : public UILinearLayout {
public:
typedef std::function<void()> CommandCallback;
static UIGlobalSearchBar* New() { return eeNew( UIGlobalSearchBar, () ); }
UIGlobalSearchBar() :
UILinearLayout( "globalsearchbar", UIOrientation::Vertical ),
mKeyBindings( getUISceneNode()->getWindow()->getInput() ) {}
void addCommand( const std::string& name, const CommandCallback& cb ) { mCommands[name] = cb; }
void execute( const std::string& command ) {
auto cmdIt = mCommands.find( command );
if ( cmdIt != mCommands.end() )
cmdIt->second();
}
KeyBindings& getKeyBindings() { return mKeyBindings; }
protected:
KeyBindings mKeyBindings;
std::unordered_map<std::string, std::function<void()>> mCommands;
Uint32 onKeyDown( const KeyEvent& event ) {
std::string cmd =
mKeyBindings.getCommandFromKeyBind( {event.getKeyCode(), event.getMod()} );
if ( !cmd.empty() ) {
auto cmdIt = mCommands.find( cmd );
if ( cmdIt != mCommands.end() ) {
cmdIt->second();
return 0;
}
}
return 1;
}
};
struct UIConfig {
StyleSheetLength fontSize{12, StyleSheetLength::Dp};
bool showSidePanel{true};
@@ -151,6 +183,8 @@ class App : public UICodeEditorSplitter::Client {
void showFindView();
void showGlobalSearch();
void showLocateBar();
bool replaceSelection( SearchState& search, const String& replacement );
@@ -177,6 +211,7 @@ class App : public UICodeEditorSplitter::Client {
UILayout* mBaseLayout{nullptr};
UISearchBar* mSearchBarLayout{nullptr};
UILocateBar* mLocateBarLayout{nullptr};
UILocateBar* mGlobalSearchBarLayout{nullptr};
UIPopUpMenu* mSettingsMenu{nullptr};
UITextView* mSettingsButton{nullptr};
UIPopUpMenu* mColorSchemeMenu{nullptr};
@@ -209,6 +244,8 @@ class App : public UICodeEditorSplitter::Client {
UITreeView* mProjectTreeView{nullptr};
UITableView* mLocateTable{nullptr};
UITextInput* mLocateInput{nullptr};
UITreeView* mGlobalSearchTree{nullptr};
UITextInput* mGlobalSearchInput;
bool mDirTreeReady{false};
void initLocateBar();
@@ -233,6 +270,8 @@ class App : public UICodeEditorSplitter::Client {
void initSearchBar();
void initGlobalSearchBar();
void addRemainingTabWidgets( Node* widget );
void createSettingsMenu();
@@ -301,7 +340,15 @@ class App : public UICodeEditorSplitter::Client {
void updateLocateTable();
void updateGlobalSearchBar();
UIPopUpMenu* createToolsMenu();
void hideGlobalSearchBar();
void hideSearchBar();
void hideLocateBar();
};
#endif // EE_TOOLS_CODEEDITOR_HPP
@@ -14,7 +14,7 @@ void ProjectDirectoryTree::scan( const ProjectDirectoryTree::ScanCompleteEvent&
const bool& ignoreHidden ) {
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
mPool->run(
[&, acceptedPattern] {
[&, acceptedPattern, ignoreHidden] {
#endif
Lock l( mFilesMutex );
if ( !acceptedPattern.empty() ) {
@@ -112,6 +112,10 @@ size_t ProjectDirectoryTree::getFilesCount() const {
return mFiles.size();
}
const std::vector<std::string>& ProjectDirectoryTree::getFiles() const {
return mFiles;
}
void ProjectDirectoryTree::getDirectoryFiles( std::vector<std::string>& files,
std::vector<std::string>& names,
std::string directory,
@@ -29,7 +29,7 @@ class FileListModel : public Model {
}
virtual Variant data( const ModelIndex& index, Role role = Role::Display ) const {
if ( role == Role::Display ) {
if ( role == Role::Display && index.row() < (Int64)mFiles.size() ) {
return Variant( index.column() == 0 ? mNames[index.row()].c_str()
: mFiles[index.row()].c_str() );
}
@@ -62,6 +62,8 @@ class ProjectDirectoryTree {
size_t getFilesCount() const;
const std::vector<std::string>& getFiles() const;
protected:
std::string mPath;
std::shared_ptr<ThreadPool> mPool;
+68
View File
@@ -0,0 +1,68 @@
#include "projectsearch.hpp"
static std::vector<ProjectSearch::ResultData::Result>
searchInFile( const std::string& file, const std::string& text, const bool& caseSensitive ) {
std::vector<ProjectSearch::ResultData::Result> res;
TextDocument doc( false );
TextPosition pos{0, 0};
String searchText( text );
if ( doc.loadFromFile( file ) ) {
do {
pos = doc.find( searchText, pos, caseSensitive );
if ( pos.isValid() ) {
const auto& l = doc.line( pos.line() ).getText();
res.push_back( {l.substr( 0, l.size() - 1 ).toUtf8(), pos} );
pos = doc.positionOffset( pos, searchText.size() );
}
} while ( pos.isValid() );
}
return res;
}
void ProjectSearch::find( const std::vector<std::string> files, const std::string& string,
ResultCb result, bool caseSensitive ) {
Result res;
for ( auto& file : files ) {
auto fileRes = searchInFile( file, string, caseSensitive );
if ( !fileRes.empty() )
res.push_back( {file, fileRes} );
}
result( res );
}
struct FindData {
Mutex resMutex;
Mutex countMutex;
int resCount{0};
ProjectSearch::Result res;
};
void ProjectSearch::find( const std::vector<std::string> files, const std::string& string,
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive ) {
if ( files.empty() )
result( {} );
FindData* findData = eeNew( FindData, () );
findData->resCount = files.size();
for ( auto& file : files ) {
pool->run(
[findData, file, string, caseSensitive] {
auto fileRes = searchInFile( file, string, caseSensitive );
if ( !fileRes.empty() ) {
Lock l( findData->resMutex );
findData->res.push_back( {file, fileRes} );
}
},
[result, findData] {
int count;
{
Lock l( findData->countMutex );
findData->resCount--;
count = findData->resCount;
}
if ( count == 0 ) {
result( findData->res );
eeDelete( findData );
}
} );
}
}
+126
View File
@@ -0,0 +1,126 @@
#ifndef PROJECTSEARCH_HPP
#define PROJECTSEARCH_HPP
#include <eepp/core/string.hpp>
#include <eepp/system/threadpool.hpp>
#include <eepp/ui/doc/textdocument.hpp>
#include <eepp/ui/models/model.hpp>
#include <functional>
#include <memory>
#include <string>
using namespace EE;
using namespace EE::System;
using namespace EE::UI::Doc;
using namespace EE::UI::Models;
class ProjectSearch {
public:
struct ResultData {
struct Result {
Result( const String& line, const TextPosition& pos ) : line( line ), position( pos ) {}
std::string line;
TextPosition position;
};
std::string file;
std::vector<Result> results;
};
typedef std::vector<ResultData> Result;
typedef std::function<void( const Result& )> ResultCb;
class ResultModel : public Model {
public:
enum Column { FileOrPosition, Line, ColumnPosition };
ResultModel( const Result& result ) : mResult( result ) {}
virtual size_t treeColumn() const { return Column::FileOrPosition; }
ModelIndex parentIndex( const ModelIndex& index ) const {
if ( !index.isValid() || index.internalId() == -1 )
return {};
return createIndex( index.internalId(), index.column(), &mResult[index.internalId()],
-1 );
}
ModelIndex index( int row, int column, const ModelIndex& parent ) const {
if ( row < 0 || column < 0 )
return {};
if ( !parent.isValid() )
return createIndex( row, column, &mResult[row], -1 );
if ( parent.data() )
return createIndex( row, column, &mResult[parent.row()].results[row],
parent.row() );
return {};
}
size_t rowCount( const ModelIndex& index ) const {
if ( !index.isValid() )
return mResult.size();
if ( index.internalId() == -1 )
return mResult[index.row()].results.size();
return 0;
}
size_t columnCount( const ModelIndex& ) const { return 2; }
std::string columnName( const size_t& colIndex ) const {
return colIndex == 0 ? "File" : "Line";
}
Variant data( const ModelIndex& index, Role role = Role::Display ) const {
static const char* EMPTY = "";
if ( role == Role::Display ) {
if ( index.internalId() == -1 ) {
if ( index.column() == FileOrPosition ) {
return Variant( String::format( "%s (%zu)",
mResult[index.row()].file.c_str(),
mResult[index.row()].results.size() ) );
}
} else {
switch ( index.column() ) {
case FileOrPosition:
return Variant( String::format(
"%6lld %s",
mResult[index.internalId()].results[index.row()].position.line(),
mResult[index.internalId()].results[index.row()].line.c_str() ) );
}
}
} else if ( role == Role::Custom ) {
if ( index.internalId() != -1 ) {
switch ( index.column() ) {
case FileOrPosition:
return Variant(
mResult[index.internalId()].results[index.row()].position.line() );
case Line:
return Variant(
mResult[index.internalId()].results[index.row()].line.c_str() );
case ColumnPosition:
return Variant( mResult[index.internalId()]
.results[index.row()]
.position.column() );
}
}
}
return Variant( EMPTY );
}
virtual void update() { onModelUpdate(); }
protected:
Result mResult;
};
static std::shared_ptr<ResultModel> asModel( const Result& result ) {
return std::make_shared<ResultModel>( result );
}
static void find( const std::vector<std::string> files, const std::string& string,
ResultCb result, bool caseSensitive );
static void find( const std::vector<std::string> files, const std::string& string,
std::shared_ptr<ThreadPool> pool, ResultCb result, bool caseSensitive );
};
#endif // PROJECTSEARCH_HPP