diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 2dc098b57..078fd3cfa 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -718,11 +718,13 @@ void App::initGlobalSearchBar() { UIPushButton* searchButton = mGlobalSearchBarLayout->find( "global_search" ); UICheckBox* caseSensitiveChk = mGlobalSearchBarLayout->find( "case_sensitive" ); UICheckBox* wholeWordChk = mGlobalSearchBarLayout->find( "whole_word" ); + UICheckBox* luaPatternChk = mGlobalSearchBarLayout->find( "lua_pattern" ); UIWidget* searchBarClose = mGlobalSearchBarLayout->find( "global_searchbar_close" ); mGlobalSearchInput = mGlobalSearchBarLayout->find( "global_search_find" ); mGlobalSearchHistoryList = mGlobalSearchBarLayout->find( "global_search_history" ); - mGlobalSearchBarLayout->addCommand( "search-in-files", [&, caseSensitiveChk, wholeWordChk] { + mGlobalSearchBarLayout->addCommand( "search-in-files", [&, caseSensitiveChk, wholeWordChk, + luaPatternChk] { if ( mDirTree && mDirTree->getFilesCount() > 0 && !mGlobalSearchInput->getText().empty() ) { UILoader* loader = UILoader::New(); loader->setId( "loader" ); @@ -777,7 +779,9 @@ void App::initGlobalSearchBar() { loader->close(); } ); }, - caseSensitiveChk->isChecked(), wholeWordChk->isChecked() ); + caseSensitiveChk->isChecked(), wholeWordChk->isChecked(), + luaPatternChk->isChecked() ? TextDocument::FindReplaceType::LuaPattern + : TextDocument::FindReplaceType::Normal ); } } ); mGlobalSearchBarLayout->addCommand( "close-global-searchbar", [&] { @@ -789,6 +793,7 @@ void App::initGlobalSearchBar() { { "escape", "close-global-searchbar" }, { "ctrl+s", "change-case" }, { "ctrl+w", "change-whole-word" }, + { "ctrl+l", "toggle-lua-pattern" }, } ); mGlobalSearchBarLayout->addCommand( "change-case", [&, caseSensitiveChk] { caseSensitiveChk->setChecked( !caseSensitiveChk->isChecked() ); @@ -796,6 +801,9 @@ void App::initGlobalSearchBar() { mGlobalSearchBarLayout->addCommand( "change-whole-word", [&, wholeWordChk] { wholeWordChk->setChecked( !wholeWordChk->isChecked() ); } ); + mGlobalSearchBarLayout->addCommand( "toggle-lua-pattern", [&, luaPatternChk] { + luaPatternChk->setChecked( !luaPatternChk->isChecked() ); + } ); mGlobalSearchInput->addEventListener( Event::OnPressEnter, [&]( const Event* ) { if ( mGlobalSearchInput->hasFocus() ) { mGlobalSearchBarLayout->execute( "search-in-files" ); @@ -822,7 +830,7 @@ void App::initGlobalSearchBar() { mGlobalSearchTree->setHeadersVisible( false ); mGlobalSearchTree->setVisible( false ); mGlobalSearchTree->setColumnsHidden( - { ProjectSearch::ResultModel::Line, ProjectSearch::ResultModel::ColumnPosition }, true ); + { ProjectSearch::ResultModel::Line, ProjectSearch::ResultModel::ColumnStart }, true ); mGlobalSearchTree->addEventListener( Event::KeyDown, [&]( const Event* event ) { const KeyEvent* keyEvent = static_cast( event ); if ( keyEvent->getKeyCode() == KEY_ESCAPE ) @@ -852,11 +860,10 @@ void App::initGlobalSearchBar() { 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 ) ); + Variant colNum( model->data( model->index( modelEvent->getModelIndex().row(), + ProjectSearch::ResultModel::ColumnStart, + modelEvent->getModelIndex().parent() ), + Model::Role::Custom ) ); if ( mEditorSplitter->getCurEditor() && lineNum.isValid() && colNum.isValid() && lineNum.is( Variant::Type::Int64 ) && colNum.is( Variant::Type::Int64 ) ) { TextPosition pos{ lineNum.asInt64(), colNum.asInt64() }; @@ -2490,6 +2497,7 @@ void App::init( const std::string& file, const Float& pidelDensity ) { + diff --git a/src/tools/codeeditor/projectsearch.cpp b/src/tools/codeeditor/projectsearch.cpp index cdefc677b..45dff9d93 100644 --- a/src/tools/codeeditor/projectsearch.cpp +++ b/src/tools/codeeditor/projectsearch.cpp @@ -1,5 +1,6 @@ #include "projectsearch.hpp" #include +#include static int countNewLines( const std::string& text, const size_t& start, const size_t& end ) { const char* startPtr = text.c_str() + start; @@ -21,10 +22,13 @@ static String textLine( const std::string& fileText, const size_t& fromPos, size const char* stringStartPtr = fileText.c_str(); const char* startPtr = fileText.c_str() + fromPos; const char* ptr = startPtr; - while ( stringStartPtr != ptr && *--ptr != '\n' ) { + const char* nlStartPtr = stringStartPtr; + if ( stringStartPtr != ptr ) { + while ( stringStartPtr != ptr && *--ptr != '\n' ) { + } + nlStartPtr = ptr + 1; + start = ptr - stringStartPtr + 1; } - const char* nlStartPtr = ptr + 1; - start = ptr - stringStartPtr + 1; ptr = startPtr; while ( ++ptr && *ptr != '\0' && *ptr != '\n' ) { } @@ -53,13 +57,12 @@ searchInFileHorspool( const std::string& file, const std::string& text, const bo searchRes += text.size(); continue; } - TextPosition pos; size_t relCol; totNl += countNewLines( fileText, lSearchRes, searchRes ); String str( textLine( fileTextOriginal, searchRes, relCol ) ); - pos.setLine( totNl ); - pos.setColumn( relCol ); - res.push_back( { str, pos } ); + res.push_back( { str, + { { (Int64)totNl, (Int64)relCol }, + { (Int64)totNl, ( Int64 )( relCol + text.size() ) } } } ); lSearchRes = searchRes; searchRes += text.size(); } @@ -73,13 +76,12 @@ searchInFileHorspool( const std::string& file, const std::string& text, const bo searchRes += text.size(); continue; } - TextPosition pos; size_t relCol; totNl += countNewLines( fileText, lSearchRes, searchRes ); - std::string str = textLine( fileText, searchRes, relCol ); - pos.setLine( totNl ); - pos.setColumn( relCol ); - res.push_back( { str, pos } ); + String str( textLine( fileText, searchRes, relCol ) ); + res.push_back( { str, + { { (Int64)totNl, (Int64)relCol }, + { (Int64)totNl, ( Int64 )( relCol + text.size() ) } } } ); lSearchRes = searchRes; searchRes += text.size(); } @@ -88,13 +90,70 @@ searchInFileHorspool( const std::string& file, const std::string& text, const bo return res; } +static std::vector +searchInFileLuaPattern( const std::string& file, const std::string& text, const bool& caseSensitive, + const bool& wholeWord ) { + std::string fileText; + FileSystem::fileGet( file, fileText ); + LuaPattern pattern( text ); + std::vector res; + size_t totNl = 0; + bool matched = false; + Int64 searchRes = 0; + if ( !caseSensitive ) { + std::string fileTextOriginal( fileText ); + String::toLowerInPlace( fileText ); + do { + int start, end = 0; + if ( ( matched = pattern.find( fileText, start, end, searchRes ) ) ) { + if ( wholeWord && !String::isWholeWord( + fileText, fileText.substr( start, end - start ), start ) ) { + searchRes = end; + continue; + } + size_t relCol; + totNl += countNewLines( fileText, searchRes, end ); + String str( textLine( fileTextOriginal, start, relCol ) ); + int len = end - start; + res.push_back( { str, + { { (Int64)totNl, (Int64)relCol }, + { (Int64)totNl, ( Int64 )( relCol + len ) } } } ); + searchRes = end; + } + } while ( matched ); + } else { + do { + int start, end = 0; + if ( ( matched = pattern.find( fileText, start, end, searchRes ) ) ) { + if ( wholeWord && !String::isWholeWord( + fileText, fileText.substr( start, end - start ), start ) ) { + searchRes = end; + continue; + } + size_t relCol; + totNl += countNewLines( fileText, searchRes, end ); + String str( textLine( fileText, start, relCol ) ); + int len = end - start; + res.push_back( { str, + { { (Int64)totNl, (Int64)relCol }, + { (Int64)totNl, ( Int64 )( relCol + len ) } } } ); + searchRes = end; + } + } while ( matched ); + } + return res; +} + void ProjectSearch::find( const std::vector files, const std::string& string, - ResultCb result, bool caseSensitive, bool wholeWord ) { + ResultCb result, bool caseSensitive, bool wholeWord, + const TextDocument::FindReplaceType& type ) { Result res; const auto occ = String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() ); for ( auto& file : files ) { - auto fileRes = searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ); + auto fileRes = type == TextDocument::FindReplaceType::Normal + ? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ) + : searchInFileLuaPattern( file, string, caseSensitive, wholeWord ); if ( !fileRes.empty() ) res.push_back( { file, fileRes } ); } @@ -110,7 +169,7 @@ struct FindData { void ProjectSearch::find( const std::vector files, std::string string, std::shared_ptr pool, ResultCb result, bool caseSensitive, - bool wholeWord ) { + bool wholeWord, const TextDocument::FindReplaceType& type ) { if ( files.empty() ) result( {} ); FindData* findData = eeNew( FindData, () ); @@ -121,8 +180,11 @@ void ProjectSearch::find( const std::vector files, std::string stri String::BMH::createOccTable( (const unsigned char*)string.c_str(), string.size() ); for ( auto& file : files ) { pool->run( - [findData, file, string, caseSensitive, wholeWord, occ] { - auto fileRes = searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ); + [findData, file, string, caseSensitive, wholeWord, occ, type] { + auto fileRes = + type == TextDocument::FindReplaceType::Normal + ? searchInFileHorspool( file, string, caseSensitive, wholeWord, occ ) + : searchInFileLuaPattern( file, string, caseSensitive, wholeWord ); if ( !fileRes.empty() ) { Lock l( findData->resMutex ); findData->res.push_back( { file, fileRes } ); diff --git a/src/tools/codeeditor/projectsearch.hpp b/src/tools/codeeditor/projectsearch.hpp index 791201f3c..da70eb27d 100644 --- a/src/tools/codeeditor/projectsearch.hpp +++ b/src/tools/codeeditor/projectsearch.hpp @@ -18,9 +18,9 @@ class ProjectSearch { public: struct ResultData { struct Result { - Result( const String& line, const TextPosition& pos ) : line( line ), position( pos ) {} + Result( const String& line, const TextRange& pos ) : line( line ), position( pos ) {} String line; - TextPosition position; + TextRange position; }; std::string file; std::vector results; @@ -31,7 +31,7 @@ class ProjectSearch { class ResultModel : public Model { public: - enum Column { FileOrPosition, Line, ColumnPosition }; + enum Column { FileOrPosition, Line, LineEnd, ColumnStart, ColumnEnd }; ResultModel( const Result& result ) : mResult( result ) {} @@ -81,29 +81,39 @@ class ProjectSearch { } else { switch ( index.column() ) { case FileOrPosition: - return Variant( String::format( - "%6lld %s", - mResult[index.internalId()].results[index.row()].position.line() + - 1, - mResult[index.internalId()] - .results[index.row()] - .line.toUtf8() - .c_str() ) ); + return Variant( String::format( "%6lld %s", + mResult[index.internalId()] + .results[index.row()] + .position.start() + .line() + + 1, + mResult[index.internalId()] + .results[index.row()] + .line.toUtf8() + .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() ); + return Variant( mResult[index.internalId()] + .results[index.row()] + .position.start() + .line() ); case Line: return Variant( mResult[index.internalId()].results[index.row()].line.c_str() ); - case ColumnPosition: + case ColumnStart: return Variant( mResult[index.internalId()] .results[index.row()] - .position.column() ); + .position.start() + .column() ); + case ColumnEnd: + return Variant( mResult[index.internalId()] + .results[index.row()] + .position.end() + .column() ); } } else { switch ( index.column() ) { @@ -125,12 +135,16 @@ class ProjectSearch { return std::make_shared( result ); } - static void find( const std::vector files, const std::string& string, - ResultCb result, bool caseSensitive, bool wholeWord = false ); + static void + find( const std::vector files, const std::string& string, ResultCb result, + bool caseSensitive, bool wholeWord = false, + const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal ); - static void find( const std::vector files, std::string string, - std::shared_ptr pool, ResultCb result, bool caseSensitive, - bool wholeWord = false ); + static void + find( const std::vector files, std::string string, + std::shared_ptr pool, ResultCb result, bool caseSensitive, + bool wholeWord = false, + const TextDocument::FindReplaceType& type = TextDocument::FindReplaceType::Normal ); }; #endif // PROJECTSEARCH_HPP diff --git a/src/tools/codeeditor/uitreeviewglobalsearch.cpp b/src/tools/codeeditor/uitreeviewglobalsearch.cpp index 0d3b226f0..ba59334d9 100644 --- a/src/tools/codeeditor/uitreeviewglobalsearch.cpp +++ b/src/tools/codeeditor/uitreeviewglobalsearch.cpp @@ -43,15 +43,27 @@ UIPushButton* UITreeViewCellGlobalSearch::updateText( const std::string& text ) mTextBox->setFontFillColor( pp->getLineNumColor(), from, to ); } - ModelIndex curIndex = getCurIndex(); - ModelIndex index = pp->getModel()->index( - curIndex.row(), ProjectSearch::ResultModel::ColumnPosition, curIndex.parent() ); - Variant variant = pp->getModel()->data( index, Model::Role::Custom ); Int64 iniPos = 0; - if ( variant.is( Variant::Type::Int64 ) ) - iniPos = variant.asInt64(); - String txt( text ); - mSearchStrPos = { txt.find( pp->getSearchStr(), iniPos ), pp->getSearchStr().size() }; + Int64 endPos = 0; + Model* model = pp->getModel(); + ModelIndex curIndex = getCurIndex(); + ModelIndex indexStart = model->index( + curIndex.row(), ProjectSearch::ResultModel::ColumnStart, curIndex.parent() ); + ModelIndex indexEnd = model->index( curIndex.row(), ProjectSearch::ResultModel::ColumnEnd, + curIndex.parent() ); + Variant variantStart = model->data( indexStart, Model::Role::Custom ); + Variant variantEnd = model->data( indexEnd, Model::Role::Custom ); + iniPos = variantStart.asInt64(); + endPos = variantEnd.asInt64(); + mSearchStrPos = { iniPos + 12, endPos + 12 }; + const String& txt = mTextBox->getText(); + + if ( mSearchStrPos.second < txt.size() ) { + mResultStr = + txt.substr( mSearchStrPos.first, mSearchStrPos.second - mSearchStrPos.first ); + } else { + mResultStr = ""; + } auto tokens = SyntaxTokenizer::tokenize( styleDef, text, SYNTAX_TOKENIZER_STATE_NONE, to ).first; @@ -95,14 +107,13 @@ void UITreeViewCellGlobalSearch::draw() { mTextBox->getFont()->getGlyph( L' ', mTextBox->getPixelsFontSize(), false ).advance; Primitives p; p.setColor( pp->getColorScheme().getEditorSyntaxStyle( "selection" ).color ); - p.drawRectangle( - Rectf( { mScreenPos.x + mTextBox->getPixelsPosition().x + - getXOffsetCol( hspace, mTextBox->getTextCache()->getTabWidth(), - mTextBox->getText(), mSearchStrPos.first ), - mScreenPos.y + mTextBox->getPixelsPosition().y }, - Sizef( getTextWidth( hspace, mTextBox->getTextCache()->getTabWidth(), - pp->getSearchStr() ), - mTextBox->getPixelsSize().getHeight() ) ) ); + p.drawRectangle( Rectf( + { mScreenPos.x + mTextBox->getPixelsPosition().x + + getXOffsetCol( hspace, mTextBox->getTextCache()->getTabWidth(), + mTextBox->getText(), mSearchStrPos.first ), + mScreenPos.y + mTextBox->getPixelsPosition().y }, + Sizef( getTextWidth( hspace, mTextBox->getTextCache()->getTabWidth(), mResultStr ), + mTextBox->getPixelsSize().getHeight() ) ) ); } } } diff --git a/src/tools/codeeditor/uitreeviewglobalsearch.hpp b/src/tools/codeeditor/uitreeviewglobalsearch.hpp index e0eb6da7b..26ce8d4b4 100644 --- a/src/tools/codeeditor/uitreeviewglobalsearch.hpp +++ b/src/tools/codeeditor/uitreeviewglobalsearch.hpp @@ -19,8 +19,10 @@ class UITreeViewCellGlobalSearch : public UITreeViewCell { UIPushButton* updateText( const std::string& text ); virtual void draw(); + protected: std::pair mSearchStrPos; + String mResultStr; }; class UITreeViewGlobalSearch : public UITreeView {